UNPKG

@autobe/agent

Version:

AI backend server code generator

294 lines (282 loc) 9.46 kB
import { IAgenticaController } from "@agentica/core"; import { AutoBeDatabase, AutoBeEventSource, AutoBeInterfaceSchemaDesign, AutoBeInterfaceSchemaEvent, AutoBeOpenApi, AutoBeProgressEventBase, } from "@autobe/interface"; import { IPointer, Singleton } from "tstl"; import typia, { ILlmApplication, ILlmSchema, IValidation } from "typia"; import { v7 } from "uuid"; import { AutoBeContext } from "../../context/AutoBeContext"; import { executeCachedBatch } from "../../utils/executeCachedBatch"; import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController"; import { transformInterfaceSchemaWriteHistory } from "./histories/transformInterfaceSchemaWriteHistory"; import { AutoBeInterfaceSchemaProgrammer } from "./programmers/AutoBeInterfaceSchemaProgrammer"; import { IAutoBeInterfaceSchemaApplication } from "./structures/IAutoBeInterfaceSchemaApplication"; import { AutoBeJsonSchemaFactory } from "./utils/AutoBeJsonSchemaFactory"; import { AutoBeJsonSchemaValidator } from "./utils/AutoBeJsonSchemaValidator"; import { fulfillJsonSchemaErrorMessages } from "./utils/fulfillJsonSchemaErrorMessages"; export async function orchestrateInterfaceSchemaWrite( ctx: AutoBeContext, props: { operations: AutoBeOpenApi.IOperation[]; instruction: string; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchema>> { // gather type names const collection: Set<string> = new Set(); const gather = (key: string): void => { if (AutoBeJsonSchemaValidator.isPage(key)) collection.add(AutoBeJsonSchemaFactory.getPageName(key)); collection.add(key); }; for (const op of props.operations) { if (op.requestBody !== null) gather(op.requestBody.typeName); if (op.responseBody !== null) gather(op.responseBody.typeName); } const presets: Record<string, AutoBeOpenApi.IJsonSchema> = AutoBeJsonSchemaFactory.presets(collection); // divide and conquer const typeNames: string[] = Array.from(collection).filter( (k) => AutoBeJsonSchemaValidator.isPreset(k) === false, ); const progress: AutoBeProgressEventBase = { total: typeNames.length, completed: 0, }; const x: Record<string, AutoBeOpenApi.IJsonSchema> = { ...presets, }; await executeCachedBatch( ctx, typeNames.map((it) => async (promptCacheKey) => { const counter = new Singleton(() => ++progress.completed); const predicate = (key: string) => key === it || (AutoBeJsonSchemaValidator.isPage(key) && AutoBeJsonSchemaFactory.getPageName(key) === it); const operations: AutoBeOpenApi.IOperation[] = props.operations.filter( (op) => (op.requestBody && predicate(op.requestBody.typeName)) || (op.responseBody && predicate(op.responseBody.typeName)), ); try { const row: AutoBeOpenApi.IJsonSchema = await process(ctx, { operations, progress, otherTypeNames: typeNames.filter((k) => k !== it), promptCacheKey, typeName: it, instruction: props.instruction, counter, }); x[it] = row; } catch (error) { console.log("interfaceSchema failure", it, error); counter.get(); } }), ); return x; } async function process( ctx: AutoBeContext, props: { operations: AutoBeOpenApi.IOperation[]; typeName: string; otherTypeNames: string[]; progress: AutoBeProgressEventBase; promptCacheKey: string; instruction: string; counter: Singleton<number>; }, ): Promise<AutoBeOpenApi.IJsonSchema> { const preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" > = new AutoBePreliminaryController({ dispatch: (e) => ctx.dispatch(e), application: typia.json.application<IAutoBeInterfaceSchemaApplication>(), source: SOURCE, kinds: [ "analysisSections", "databaseSchemas", "interfaceOperations", "previousAnalysisSections", "previousDatabaseSchemas", "previousInterfaceOperations", "previousInterfaceSchemas", "complete", ], config: { database: "text", databaseProperty: true, }, state: ctx.state(), all: { interfaceOperations: props.operations, }, local: { interfaceOperations: props.operations.filter((o) => { const predicate = (key: string) => key === props.typeName || (AutoBeJsonSchemaValidator.isPage(key) && AutoBeJsonSchemaFactory.getPageName(key) === props.typeName); return ( (o.requestBody && predicate(o.requestBody.typeName)) || (o.responseBody && predicate(o.responseBody.typeName)) ); }), databaseSchemas: AutoBeInterfaceSchemaProgrammer.getNeighborDatabaseSchemas({ typeName: props.typeName, application: ctx.state().database!.result.data, }), }, }); const event: AutoBeInterfaceSchemaEvent = await preliminary.orchestrate( ctx, async (out) => { const pointer: IPointer<IAutoBeInterfaceSchemaApplication.IWrite | null> = { value: null, }; const result: AutoBeContext.IResult = await ctx.conversate({ source: SOURCE, controller: createController(ctx, { build: async (next) => { pointer.value = next; }, preliminary, typeName: props.typeName, operations: props.operations, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...transformInterfaceSchemaWriteHistory({ preliminary, operations: props.operations, instruction: props.instruction, typeName: props.typeName, otherTypeNames: props.otherTypeNames, }), }); if (pointer.value === null) return out(result)(null); const schema: AutoBeOpenApi.IJsonSchema = AutoBeJsonSchemaFactory.fixDesign(pointer.value.design); return out(result)({ type: SOURCE, id: v7(), typeName: props.typeName, analysis: pointer.value.analysis, rationale: pointer.value.rationale, schema, acquisition: preliminary.getAcquisition(), metric: result.metric, tokenUsage: result.tokenUsage, completed: props.counter.get(), total: props.progress.total, step: ctx.state().database?.step ?? 0, created_at: new Date().toISOString(), } satisfies AutoBeInterfaceSchemaEvent); }, ); ctx.dispatch(event); return event.schema; } function createController( ctx: AutoBeContext, props: { build: (next: IAutoBeInterfaceSchemaApplication.IWrite) => Promise<void>; preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" >; operations: AutoBeOpenApi.IOperation[]; typeName: string; }, ): IAgenticaController.IClass { const everyModels: AutoBeDatabase.IModel[] = ctx.state().database?.result.data.files.flatMap((f) => f.models) ?? []; const validate = ( next: unknown, ): IValidation<IAutoBeInterfaceSchemaApplication.IProps> => { const result: IValidation<IAutoBeInterfaceSchemaApplication.IProps> = typia.validate<IAutoBeInterfaceSchemaApplication.IProps>(next); if (result.success === false) { fulfillJsonSchemaErrorMessages(result.errors); return result; } else if (result.data.request.type !== "write") return props.preliminary.validate({ thinking: result.data.thinking, request: result.data.request, }); // Check all IAuthorized types const errors: IValidation.IError[] = []; AutoBeInterfaceSchemaProgrammer.validate({ path: "$input.request.design", errors, operations: props.operations, everyModels, typeName: props.typeName, design: result.data.request.design, }); if (errors.length !== 0) return { success: false, errors, data: next, }; return result; }; const application: ILlmApplication = props.preliminary.fixApplication( typia.llm.application<IAutoBeInterfaceSchemaApplication>({ validate: { process: validate, }, }), ); if ( AutoBeJsonSchemaValidator.isObjectType({ operations: props.operations, typeName: props.typeName, }) === true ) ( ( application.functions[0].parameters.$defs[ typia.reflect.name<AutoBeInterfaceSchemaDesign>() ] as ILlmSchema.IObject ).properties.schema as ILlmSchema.IReference ).$ref = "AutoBeOpenApi.IJsonSchema.IObject"; AutoBeInterfaceSchemaProgrammer.fixApplication({ application, everyModels, }); return { protocol: "class", name: SOURCE, application, execute: { process: async (next) => { if (next.request.type === "write") await props.build(next.request); }, } satisfies IAutoBeInterfaceSchemaApplication, }; } const SOURCE = "interfaceSchema" satisfies AutoBeEventSource;