UNPKG

@autobe/agent

Version:

AI backend server code generator

242 lines (231 loc) 7.74 kB
import { IAgenticaController, MicroAgentica } from "@agentica/core"; import { AutoBeOpenApi } from "@autobe/interface"; import { ILlmApplication, ILlmSchema, IValidation } from "@samchon/openapi"; import { HashMap, HashSet, IPointer } from "tstl"; import typia from "typia"; import { AutoBeSystemPromptConstant } from "../../constants/AutoBeSystemPromptConstant"; import { AutoBeContext } from "../../context/AutoBeContext"; import { assertSchemaModel } from "../../context/assertSchemaModel"; import { divideArray } from "../../utils/divideArray"; import { enforceToolCall } from "../../utils/enforceToolCall"; import { OpenApiEndpointComparator } from "./OpenApiEndpointComparator"; import { transformInterfaceHistories } from "./transformInterfaceHistories"; export async function orchestrateInterfaceOperations< Model extends ILlmSchema.Model, >( ctx: AutoBeContext<Model>, endpoints: AutoBeOpenApi.IEndpoint[], capacity: number = 12, ): Promise<AutoBeOpenApi.IOperation[]> { const matrix: AutoBeOpenApi.IEndpoint[][] = divideArray({ array: endpoints, capacity, }); let completed: number = 0; const operations: AutoBeOpenApi.IOperation[][] = await Promise.all( matrix.map(async (it) => { const row: AutoBeOpenApi.IOperation[] = await divideAndConquer( ctx, it, 3, (count) => { completed += count; }, ); ctx.dispatch({ type: "interfaceOperations", operations: row, completed, total: endpoints.length, step: ctx.state().analyze?.step ?? 0, created_at: new Date().toISOString(), }); return row; }), ); return operations.flat(); } async function divideAndConquer<Model extends ILlmSchema.Model>( ctx: AutoBeContext<Model>, endpoints: AutoBeOpenApi.IEndpoint[], retry: number, progress: (completed: number) => void, ): Promise<AutoBeOpenApi.IOperation[]> { const remained: HashSet<AutoBeOpenApi.IEndpoint> = new HashSet( endpoints, OpenApiEndpointComparator.hashCode, OpenApiEndpointComparator.equals, ); const operations: HashMap<AutoBeOpenApi.IEndpoint, AutoBeOpenApi.IOperation> = new HashMap( OpenApiEndpointComparator.hashCode, OpenApiEndpointComparator.equals, ); for (let i: number = 0; i < retry; ++i) { if (remained.empty() === true || operations.size() >= endpoints.length) break; const before: number = operations.size(); const newbie: AutoBeOpenApi.IOperation[] = await process( ctx, Array.from(remained), ); for (const item of newbie) { operations.set(item, item); remained.erase(item); } if (operations.size() - before !== 0) progress(operations.size() - before); } return operations.toJSON().map((it) => it.second); } async function process<Model extends ILlmSchema.Model>( ctx: AutoBeContext<Model>, endpoints: AutoBeOpenApi.IEndpoint[], ): Promise<AutoBeOpenApi.IOperation[]> { const pointer: IPointer<AutoBeOpenApi.IOperation[] | 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_ENDPOINT, ), tokenUsage: ctx.usage(), controllers: [ createApplication({ model: ctx.model, build: (endpoints) => { pointer.value ??= []; pointer.value.push(...endpoints); }, }), ], }); enforceToolCall(agentica); await agentica.conversate( [ "Make API operations for below endpoints:", "", "```json", JSON.stringify(Array.from(endpoints), null, 2), "```", ].join("\n"), ); if (pointer.value === null) throw new Error("Failed to create operations."); // never be happened return pointer.value; } function createApplication<Model extends ILlmSchema.Model>(props: { model: Model; build: (operations: AutoBeOpenApi.IOperation[]) => void; }): IAgenticaController.IClass<Model> { assertSchemaModel(props.model); const application: ILlmApplication<Model> = collection[ props.model ] as unknown as ILlmApplication<Model>; application.functions[0].validate = (next: unknown): IValidation => { const result: IValidation<IMakeOperationProps> = typia.validate<IMakeOperationProps>(next); if (result.success === false) return result; const errors: IValidation.IError[] = []; result.data.operations.forEach((op, i) => { if (op.method === "get" && op.requestBody !== null) errors.push({ path: `operations[${i}].requestBody`, expected: "GET method should not have request body. Change method, or re-design the operation.", value: op.requestBody, }); }); if (errors.length !== 0) return { success: false, errors, data: next, }; return result; }; return { protocol: "class", name: "interface", application, execute: { makeOperations: (next) => { props.build(next.operations); }, } 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 { /** * Generate detailed API operations from path/method combinations. * * This function creates complete API operations following REST principles and * quality standards. Each generated operation includes specification, path, * method, detailed multi-paragraph description, concise summary, parameters, * and appropriate request/response bodies. * * The function processes as many operations as possible in a single call, * with progress tracking to ensure iterative completion of all required * endpoints. * * @param props Properties containing the operations to generate. */ makeOperations(props: IMakeOperationProps): void; } interface IMakeOperationProps { /** * Array of API operations to generate. * * Each operation in this array must include: * * - Specification: Detailed API specification with clear purpose and * functionality * - Path: Resource-centric URL path (e.g., "/resources/{resourceId}") * - Method: HTTP method (get, post, put, delete, patch) * - Description: Extremely detailed multi-paragraph description referencing * Prisma schema comments * - Summary: Concise one-sentence summary of the endpoint * - Parameters: Array of all necessary parameters with descriptions and schema * definitions * - RequestBody: For POST/PUT/PATCH methods, with typeName referencing * components.schemas * - ResponseBody: With typeName referencing appropriate response type * * All operations must follow strict quality standards: * * 1. Detailed descriptions referencing Prisma schema comments * 2. Accurate parameter definitions matching path parameters * 3. Appropriate request/response body type references * 4. Consistent patterns for CRUD operations * * For list retrievals (typically PATCH), include pagination, search, and * sorting. For detail retrieval (GET), return a single resource. For creation * (POST), use .ICreate request body. For modification (PUT), use .IUpdate * request body. */ operations: AutoBeOpenApi.IOperation[]; }