UNPKG

@autobe/agent

Version:

AI backend server code generator

299 lines (287 loc) 10 kB
import { IAgenticaController } from "@agentica/core"; import { AutoBeDatabase, AutoBeEventSource, AutoBeInterfaceSchemaRefineEvent, AutoBeOpenApi, AutoBeProgressEventBase, } from "@autobe/interface"; import { AutoBeOpenApiTypeChecker } from "@autobe/utils"; import { IPointer, Singleton } from "tstl"; import typia, { ILlmApplication, IValidation } from "typia"; import { v7 } from "uuid"; import { AutoBeContext } from "../../context/AutoBeContext"; import { executeCachedBatch } from "../../utils/executeCachedBatch"; import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController"; import { AutoBeDatabaseModelProgrammer } from "../database/programmers/AutoBeDatabaseModelProgrammer"; import { transformInterfaceSchemaRefineHistory } from "./histories/transformInterfaceSchemaRefineHistory"; import { AutoBeInterfaceSchemaProgrammer } from "./programmers/AutoBeInterfaceSchemaProgrammer"; import { AutoBeInterfaceSchemaRefineProgrammer } from "./programmers/AutoBeInterfaceSchemaRefineProgrammer"; import { IAutoBeInterfaceSchemaRefineApplication } from "./structures/IAutoBeInterfaceSchemaRefineApplication"; import { AutoBeJsonSchemaValidator } from "./utils/AutoBeJsonSchemaValidator"; import { fulfillJsonSchemaErrorMessages } from "./utils/fulfillJsonSchemaErrorMessages"; export async function orchestrateInterfaceSchemaRefine( ctx: AutoBeContext, props: { document: AutoBeOpenApi.IDocument; schemas: Record<string, AutoBeOpenApi.IJsonSchema>; instruction: string; progress: AutoBeProgressEventBase; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>> { // Filter to only process object-type schemas (non-preset and object type) const typeNames: string[] = Object.entries(props.schemas) .filter( ([k, v]) => AutoBeJsonSchemaValidator.isPreset(k) === false && AutoBeOpenApiTypeChecker.isObject(v), ) .map(([k]) => k); props.progress.total += typeNames.length; const x: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> = {}; await executeCachedBatch( ctx, typeNames.map((it) => async (promptCacheKey) => { const counter = new Singleton(() => ++props.progress.completed); const predicate = (key: string) => key === it; const operations: AutoBeOpenApi.IOperation[] = props.document.operations.filter( (op) => (op.requestBody && predicate(op.requestBody.typeName)) || (op.responseBody && predicate(op.responseBody.typeName)), ); try { const schema: AutoBeOpenApi.IJsonSchema = props.schemas[it]; if (AutoBeOpenApiTypeChecker.isObject(schema) === false) { counter.get(); return; } const refined: AutoBeOpenApi.IJsonSchemaDescriptive.IObject = await process(ctx, { instruction: props.instruction, document: props.document, typeName: it, operations, schema, progress: props.progress, promptCacheKey, counter, }); x[it] = refined; } catch (error) { console.log("interfaceSchemaRefine failure", it, error); counter.get(); } }), ); return x; } async function process( ctx: AutoBeContext, props: { instruction: string; document: AutoBeOpenApi.IDocument; typeName: string; operations: AutoBeOpenApi.IOperation[]; schema: AutoBeOpenApi.IJsonSchema.IObject; progress: AutoBeProgressEventBase; promptCacheKey: string; counter: Singleton<number>; }, ): Promise<AutoBeOpenApi.IJsonSchemaDescriptive.IObject> { const preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "interfaceSchemas" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" > = new AutoBePreliminaryController({ dispatch: (e) => ctx.dispatch(e), application: typia.json.application<IAutoBeInterfaceSchemaRefineApplication>(), source: SOURCE, kinds: [ "analysisSections", "previousAnalysisSections", "databaseSchemas", "previousDatabaseSchemas", "interfaceOperations", "previousInterfaceOperations", "interfaceSchemas", "previousInterfaceSchemas", "complete", ], config: { database: "text", databaseProperty: true, }, state: ctx.state(), all: { interfaceOperations: props.document.operations, interfaceSchemas: props.document.components.schemas, }, local: { interfaceOperations: props.operations, interfaceSchemas: { [props.typeName]: props.schema as AutoBeOpenApi.IJsonSchemaDescriptive, }, databaseSchemas: (() => { const expected: string = props.schema["x-autobe-database-schema"] ?? AutoBeInterfaceSchemaProgrammer.getDatabaseSchemaName(props.typeName); const model: AutoBeDatabase.IModel | undefined = ctx .state() .database?.result.data.files.flatMap((f) => f.models) .find((m) => m.name === expected); if (model === undefined) return []; return AutoBeDatabaseModelProgrammer.getNeighbors({ application: ctx.state().database!.result.data, model, }); })(), }, }); const event: AutoBeInterfaceSchemaRefineEvent = await preliminary.orchestrate( ctx, async (out) => { const pointer: IPointer<IAutoBeInterfaceSchemaRefineApplication.IWrite | null> = { value: null, }; const result: AutoBeContext.IResult = await ctx.conversate({ source: SOURCE, controller: createController(ctx, { typeName: props.typeName, operations: props.document.operations, schema: props.schema, preliminary, pointer, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...transformInterfaceSchemaRefineHistory({ state: ctx.state(), instruction: props.instruction, typeName: props.typeName, operations: props.operations, schema: props.schema, preliminary, }), }); if (pointer.value === null) return out(result)(null); return out(result)({ type: SOURCE, id: v7(), typeName: props.typeName, schema: props.schema, review: pointer.value.review, databaseSchema: pointer.value.databaseSchema, specification: pointer.value.specification, description: pointer.value.description, excludes: pointer.value.excludes, revises: pointer.value.revises, acquisition: preliminary.getAcquisition(), metric: result.metric, tokenUsage: result.tokenUsage, step: ctx.state().analyze?.step ?? 0, total: props.progress.total, completed: props.counter.get(), created_at: new Date().toISOString(), } satisfies AutoBeInterfaceSchemaRefineEvent); }, ); ctx.dispatch(event); return AutoBeInterfaceSchemaRefineProgrammer.execute({ schema: props.schema, databaseSchema: event.databaseSchema, specification: event.specification, description: event.description, revises: event.revises, }); } function createController( ctx: AutoBeContext, props: { typeName: string; schema: AutoBeOpenApi.IJsonSchema.IObject; operations: AutoBeOpenApi.IOperation[]; pointer: IPointer< IAutoBeInterfaceSchemaRefineApplication.IWrite | null | false >; preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "interfaceSchemas" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" >; }, ): IAgenticaController.IClass { const validate: Validator = (next) => { const result: IValidation<IAutoBeInterfaceSchemaRefineApplication.IProps> = typia.validate<IAutoBeInterfaceSchemaRefineApplication.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, }); const errors: IValidation.IError[] = []; AutoBeInterfaceSchemaRefineProgrammer.validate({ typeName: props.typeName, schema: props.schema, everyModels: ctx.state().database?.result.data.files.flatMap((f) => f.models) ?? [], databaseSchema: result.data.request.databaseSchema, excludes: result.data.request.excludes, revises: result.data.request.revises, errors, path: `$input.request`, }); return errors.length ? { success: false, errors, data: result.data, } : result; }; const application: ILlmApplication = props.preliminary.fixApplication( typia.llm.application<IAutoBeInterfaceSchemaRefineApplication>({ validate: { process: validate, }, }), ); AutoBeInterfaceSchemaRefineProgrammer.fixApplication({ everyModels: ctx.state().database?.result.data.files.flatMap((f) => f.models) ?? [], application, typeName: props.typeName, schema: props.schema, }); return { protocol: "class", name: SOURCE, application, execute: { process: (input) => { if (input.request.type === "write") props.pointer.value = input.request; }, } satisfies IAutoBeInterfaceSchemaRefineApplication, }; } type Validator = ( input: unknown, ) => IValidation<IAutoBeInterfaceSchemaRefineApplication.IProps>; const SOURCE = "interfaceSchemaRefine" satisfies AutoBeEventSource;