UNPKG

@autobe/agent

Version:

AI backend server code generator

300 lines (286 loc) 9.52 kB
import { IAgenticaController } from "@agentica/core"; import { AutoBeEventSource, AutoBeOpenApi, AutoBeProgressEventBase, } from "@autobe/interface"; import { AutoBeInterfaceSchemaReviewEvent } from "@autobe/interface/src/events/AutoBeInterfaceSchemaReviewEvent"; import { ILlmApplication, ILlmSchema, IValidation } from "@samchon/openapi"; import { OpenApiV3_1Emender } from "@samchon/openapi/lib/converters/OpenApiV3_1Emender"; import { IPointer } from "tstl"; import typia from "typia"; import { v7 } from "uuid"; import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant"; import { AutoBeContext } from "../../context/AutoBeContext"; import { assertSchemaModel } from "../../context/assertSchemaModel"; import { divideArray } from "../../utils/divideArray"; import { executeCachedBatch } from "../../utils/executeCachedBatch"; import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController"; import { transformInterfaceSchemaReviewHistory } from "./histories/transformInterfaceSchemaReviewHistory"; import { IAutoBeInterfaceSchemaContentReviewApplication } from "./structures/IAutoBeInterfaceSchemaContentReviewApplication"; import { JsonSchemaFactory } from "./utils/JsonSchemaFactory"; import { JsonSchemaNamingConvention } from "./utils/JsonSchemaNamingConvention"; import { JsonSchemaValidator } from "./utils/JsonSchemaValidator"; import { fulfillJsonSchemaErrorMessages } from "./utils/fulfillJsonSchemaErrorMessages"; interface IConfig { kind: AutoBeInterfaceSchemaReviewEvent["kind"]; systemPrompt: string; } export async function orchestrateInterfaceSchemaReview< Model extends ILlmSchema.Model, >( ctx: AutoBeContext<Model>, config: IConfig, props: { document: AutoBeOpenApi.IDocument; schemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>; instruction: string; progress: AutoBeProgressEventBase; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>> { const typeNames: string[] = Object.keys(props.schemas); const matrix: string[][] = divideArray({ array: typeNames, capacity: AutoBeConfigConstant.INTERFACE_CAPACITY, }); const x: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> = {}; for (const y of await executeCachedBatch( ctx, matrix.map((it) => async (promptCacheKey) => { const reviewOperations: AutoBeOpenApi.IOperation[] = props.document.operations.filter( (op) => (op.requestBody && it.includes(op.requestBody.typeName)) || (op.responseBody && it.includes(op.responseBody.typeName)), ); const row: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> = await divideAndConquer(ctx, config, { instruction: props.instruction, document: props.document, reviewOperations, reviewSchemas: it.reduce( (acc, cur) => { acc[cur] = props.schemas[cur]; return acc; }, {} as Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>, ), progress: props.progress, promptCacheKey, }); return row; }), )) { JsonSchemaNamingConvention.schemas(props.document.operations, x, y); Object.assign(x, y); } return x; } async function divideAndConquer<Model extends ILlmSchema.Model>( ctx: AutoBeContext<Model>, config: IConfig, props: { instruction: string; document: AutoBeOpenApi.IDocument; reviewOperations: AutoBeOpenApi.IOperation[]; reviewSchemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>; progress: AutoBeProgressEventBase; promptCacheKey: string; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>> { try { return await process(ctx, config, props); } catch { ++props.progress.completed; return {}; } } async function process<Model extends ILlmSchema.Model>( ctx: AutoBeContext<Model>, config: IConfig, props: { instruction: string; document: AutoBeOpenApi.IDocument; reviewOperations: AutoBeOpenApi.IOperation[]; reviewSchemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>; progress: AutoBeProgressEventBase; promptCacheKey: string; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>> { const preliminary: AutoBePreliminaryController< | "analysisFiles" | "prismaSchemas" | "interfaceOperations" | "interfaceSchemas" > = new AutoBePreliminaryController({ application: typia.json.application<IAutoBeInterfaceSchemaContentReviewApplication>(), source: SOURCE, kinds: [ "analysisFiles", "prismaSchemas", "interfaceOperations", "interfaceSchemas", ], state: ctx.state(), all: { interfaceOperations: props.document.operations, interfaceSchemas: props.document.components.schemas, }, local: { interfaceOperations: props.reviewOperations, interfaceSchemas: props.reviewSchemas, }, }); return await preliminary.orchestrate(ctx, async (out) => { const pointer: IPointer<IAutoBeInterfaceSchemaContentReviewApplication.IComplete | null> = { value: null, }; const result: AutoBeContext.IResult<Model> = await ctx.conversate({ source: SOURCE, controller: createController({ preliminary, pointer, model: ctx.model, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...transformInterfaceSchemaReviewHistory({ state: ctx.state(), systemPrompt: config.systemPrompt, instruction: props.instruction, reviewOperations: props.reviewOperations, reviewSchemas: props.reviewSchemas, preliminary, }), }); if (pointer.value !== null) { const content: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> = (( OpenApiV3_1Emender.convertComponents({ schemas: pointer.value.content, }) as AutoBeOpenApi.IComponents ).schemas ?? {}) as Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>; ctx.dispatch({ type: SOURCE, kind: config.kind, id: v7(), schemas: props.reviewSchemas, review: pointer.value.think.review, plan: pointer.value.think.plan, content, metric: result.metric, tokenUsage: result.tokenUsage, step: ctx.state().analyze?.step ?? 0, total: props.progress.total, completed: ++props.progress.completed, created_at: new Date().toISOString(), }); return out(result)(content); } return out(result)(null); }); } function createController<Model extends ILlmSchema.Model>(props: { model: Model; pointer: IPointer<IAutoBeInterfaceSchemaContentReviewApplication.IComplete | null>; preliminary: AutoBePreliminaryController< | "analysisFiles" | "prismaSchemas" | "interfaceOperations" | "interfaceSchemas" >; }): IAgenticaController.IClass<Model> { assertSchemaModel(props.model); const validate = ( next: unknown, ): IValidation<IAutoBeInterfaceSchemaContentReviewApplication.IProps> => { if ( typia.is<{ request: { type: "complete"; schemas: object; }; }>(next) ) JsonSchemaFactory.fixPage("content", next.request); const result: IValidation<IAutoBeInterfaceSchemaContentReviewApplication.IProps> = typia.validate<IAutoBeInterfaceSchemaContentReviewApplication.IProps>( next, ); if (result.success === false) { fulfillJsonSchemaErrorMessages(result.errors); return result; } else if (result.data.request.type !== "complete") return props.preliminary.validate({ thinking: result.data.thinking, request: result.data.request, }); const errors: IValidation.IError[] = []; JsonSchemaValidator.validateSchemas({ errors, schemas: result.data.request.content, path: "$input.request.content", }); if (errors.length !== 0) return { success: false, errors, data: next, }; return result; }; const application: ILlmApplication<Model> = collection[ props.model === "chatgpt" ? "chatgpt" : props.model === "gemini" ? "gemini" : "claude" ]( validate, ) satisfies ILlmApplication<any> as unknown as ILlmApplication<Model>; return { protocol: "class", name: SOURCE, application, execute: { process: (input) => { if (input.request.type === "complete") props.pointer.value = input.request; }, } satisfies IAutoBeInterfaceSchemaContentReviewApplication, }; } const collection = { chatgpt: (validate: Validator) => typia.llm.application< IAutoBeInterfaceSchemaContentReviewApplication, "chatgpt" >({ validate: { process: validate, }, }), claude: (validate: Validator) => typia.llm.application< IAutoBeInterfaceSchemaContentReviewApplication, "claude" >({ validate: { process: validate, }, }), gemini: (validate: Validator) => typia.llm.application< IAutoBeInterfaceSchemaContentReviewApplication, "gemini" >({ validate: { process: validate, }, }), }; type Validator = ( input: unknown, ) => IValidation<IAutoBeInterfaceSchemaContentReviewApplication.IProps>; const SOURCE = "interfaceSchemaReview" satisfies AutoBeEventSource;