@autobe/agent
Version:
AI backend server code generator
244 lines (233 loc) • 7.09 kB
text/typescript
import { IAgenticaController, MicroAgentica } from "@agentica/core";
import { AutoBeOpenApi } from "@autobe/interface";
import {
ILlmApplication,
ILlmSchema,
OpenApiTypeChecker,
} from "@samchon/openapi";
import { OpenApiV3_1Emender } from "@samchon/openapi/lib/converters/OpenApiV3_1Emender";
import { IPointer } from "tstl";
import typia from "typia";
import { v4 } from "uuid";
import { AutoBeSystemPromptConstant } from "../../constants/AutoBeSystemPromptConstant";
import { AutoBeContext } from "../../context/AutoBeContext";
import { assertSchemaModel } from "../../context/assertSchemaModel";
import { enforceToolCall } from "../../utils/enforceToolCall";
import { transformInterfaceHistories } from "./transformInterfaceHistories";
export function orchestrateInterfaceComplement<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
document: AutoBeOpenApi.IDocument,
life: number = 8,
): Promise<AutoBeOpenApi.IComponents> {
return step(ctx, document, life);
}
async function step<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
document: AutoBeOpenApi.IDocument,
retry: number,
): Promise<AutoBeOpenApi.IComponents> {
const missed: string[] = getMissed(document);
if (missed.length === 0 || retry <= 0) return document.components;
const pointer: IPointer<Record<
string,
AutoBeOpenApi.IJsonSchemaDescriptive
> | null> = {
value: null,
};
const agentica: MicroAgentica<Model> = new MicroAgentica({
model: ctx.model,
vendor: ctx.vendor,
config: {
...(ctx.config ?? {}),
executor: {
describe: null,
},
},
histories: [
...transformInterfaceHistories(
ctx.state(),
AutoBeSystemPromptConstant.INTERFACE_COMPLEMENT,
),
{
id: v4(),
created_at: new Date().toISOString(),
type: "assistantMessage",
text: [
"Here is the OpenAPI document what you've made:",
"",
"```json",
JSON.stringify(document),
"```",
].join("\n"),
},
{
id: v4(),
created_at: new Date().toISOString(),
type: "assistantMessage",
text: [
"You have missed below schema types in the document.components.schemas:",
"",
...missed.map((s) => `- ${s}`),
].join("\n"),
},
],
tokenUsage: ctx.usage(),
controllers: [
createApplication({
model: ctx.model,
build: (next) => {
pointer.value ??= {};
Object.assign(
pointer.value,
(OpenApiV3_1Emender.convertComponents({
schemas: next,
}).schemas ?? {}) as Record<
string,
AutoBeOpenApi.IJsonSchemaDescriptive
>,
);
},
}),
],
});
enforceToolCall(agentica);
await agentica.conversate("Fill missing schema types please");
if (pointer.value === null) {
// unreachable
throw new Error(
"Failed to fill missing schema types. No response from agentica.",
);
}
ctx.dispatch({
type: "interfaceComplement",
missed,
schemas: pointer.value,
step: ctx.state().analyze?.step ?? 0,
created_at: new Date().toISOString(),
});
const newComponents: AutoBeOpenApi.IComponents = {
schemas: {
...pointer.value,
...document.components.schemas,
},
authorization: document.components.authorization,
};
return step(
ctx,
{
...document,
components: newComponents,
},
retry - 1,
);
}
const getMissed = (document: AutoBeOpenApi.IDocument): string[] => {
const missed: Set<string> = new Set();
const check = (name: string) => {
if (document.components.schemas[name] === undefined) missed.add(name);
};
for (const op of document.operations) {
if (op.requestBody !== null) check(op.requestBody.typeName);
if (op.responseBody !== null) check(op.responseBody.typeName);
}
for (const value of Object.values(document.components.schemas))
OpenApiTypeChecker.visit({
components: document.components,
schema: value,
closure: (next) => {
if (OpenApiTypeChecker.isReference(next))
check(next.$ref.split("/").pop()!);
},
});
return Array.from(missed);
};
function createApplication<Model extends ILlmSchema.Model>(props: {
model: Model;
build: (
schemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>,
) => void;
}): IAgenticaController.IClass<Model> {
assertSchemaModel(props.model);
const application: ILlmApplication<Model> = collection[
props.model
] as unknown as ILlmApplication<Model>;
return {
protocol: "class",
name: "interface",
application,
execute: {
complementComponents: (next) => {
props.build(next.schemas);
},
} satisfies IApplication,
};
}
const claude = typia.llm.application<
IApplication,
"claude",
{ reference: true }
>();
const collection = {
chatgpt: typia.llm.application<
IApplication,
"chatgpt",
{ reference: true }
>(),
claude,
llama: claude,
deepseek: claude,
"3.1": claude,
"3.0": typia.llm.application<IApplication, "3.0">(),
};
interface IApplication {
/**
* Complements missing schema types
*
* This method fills in schema definitions that are referenced via $ref but
* not yet defined in the `components.schemas` section. For example, if an API
* operation references `{ "$ref": "#/components/schemas/UserProfile" }` but
* `UserProfile` type is not defined in `components.schemas`, this method will
* add the missing schema definition.
*
* This function is designed to be called via AI function calling mechanism to
* ensure the OpenAPI document is complete and all referenced schemas are
* properly defined.
*/
complementComponents(props: IComplementComponentsProps): void;
}
interface IComplementComponentsProps {
/**
* A collection of missing schema definitions that need to be added to the
* OpenAPI document's `components.schemas` section.
*
* This object contains schema definitions for types that are referenced but
* not yet defined:
*
* - Key: Schema name (`string`): The name of the schema type that will be
* referenced in $ref statements
* - Value: `AutoBeOpenApi.IJsonSchema` - The complete JSON Schema definition
* for that type
*
* Example structure:
*
* ```typescript
* {
* "UserProfile": {
* "type": "object",
* "properties": {
* "id": { "type": "string" },
* "name": { "type": "string" },
* "email": { "type": "string", "format": "email" }
* },
* "required": ["id", "name", "email"]
* }
* }
* ```
*
* Each schema definition follows the JSON Schema specification and will be
* directly inserted into the OpenAPI document's components.schemas section,
* making them available for $ref references throughout the API
* specification.
*/
schemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>;
}