UNPKG

@autobe/agent

Version:

AI backend server code generator

284 lines (268 loc) 9.45 kB
import { IAgenticaController } from "@agentica/core"; import { AutoBeDatabase, AutoBeEventSource, AutoBeInterfaceSchemaCastingEvent, 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 { transformInterfaceSchemaCastingHistory } from "./histories/transformInterfaceSchemaCastingHistory"; import { AutoBeInterfaceSchemaProgrammer } from "./programmers/AutoBeInterfaceSchemaProgrammer"; import { IAutoBeInterfaceSchemaCastingApplication } from "./structures/IAutoBeInterfaceSchemaCastingApplication"; import { AutoBeJsonSchemaFactory } from "./utils/AutoBeJsonSchemaFactory"; import { AutoBeJsonSchemaValidator } from "./utils/AutoBeJsonSchemaValidator"; import { fulfillJsonSchemaErrorMessages } from "./utils/fulfillJsonSchemaErrorMessages"; export async function orchestrateInterfaceSchemaCasting( ctx: AutoBeContext, props: { document: AutoBeOpenApi.IDocument; schemas: Record<string, AutoBeOpenApi.IJsonSchema>; instruction: string; progress: AutoBeProgressEventBase; }, ): Promise<Record<string, AutoBeOpenApi.IJsonSchema>> { // Filter to only process non-object type schemas (potential degenerate primitives) const typeNames: string[] = Object.keys(props.schemas).filter( (k) => props.schemas[k] !== undefined && AutoBeJsonSchemaValidator.isPreset(k) === false && AutoBeOpenApiTypeChecker.isObject(props.schemas[k]) === false, ); props.progress.total += typeNames.length; const x: Record<string, AutoBeOpenApi.IJsonSchema> = {}; await executeCachedBatch( ctx, typeNames.map((it) => async (promptCacheKey) => { const predicate = (key: string) => key === it || (AutoBeJsonSchemaValidator.isPage(key) && AutoBeJsonSchemaFactory.getPageName(key) === it); const refineOperations: AutoBeOpenApi.IOperation[] = props.document.operations.filter( (op) => (op.requestBody && predicate(op.requestBody.typeName)) || (op.responseBody && predicate(op.responseBody.typeName)), ); const originalSchema: AutoBeOpenApi.IJsonSchema = props.schemas[it]; const counter = new Singleton(() => ++props.progress.completed); try { const refined: AutoBeOpenApi.IJsonSchema | null = await process(ctx, { instruction: props.instruction, document: props.document, typeName: it, refineOperations, originalSchema, progress: props.progress, counter, promptCacheKey, }); if (refined !== null) x[it] = refined; } catch (error) { counter.get(); throw error; } }), ); return x; } async function process( ctx: AutoBeContext, props: { instruction: string; document: AutoBeOpenApi.IDocument; typeName: string; refineOperations: AutoBeOpenApi.IOperation[]; originalSchema: AutoBeOpenApi.IJsonSchema; progress: AutoBeProgressEventBase; counter: Singleton<number>; promptCacheKey: string; }, ): Promise<AutoBeOpenApi.IJsonSchema | null> { const preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "interfaceSchemas" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" > = new AutoBePreliminaryController({ dispatch: (e) => ctx.dispatch(e), state: ctx.state(), application: typia.json.application<IAutoBeInterfaceSchemaCastingApplication>(), source: SOURCE, kinds: [ "analysisSections", "previousAnalysisSections", "databaseSchemas", "previousDatabaseSchemas", "interfaceOperations", "previousInterfaceOperations", "interfaceSchemas", "previousInterfaceSchemas", "complete", ], config: { database: "text", databaseProperty: true, }, all: { interfaceOperations: props.document.operations, interfaceSchemas: props.document.components.schemas, }, local: { interfaceOperations: props.refineOperations, interfaceSchemas: { // actually not "AutoBeOpenApi.IJsonSchemaDescriptive" type [props.typeName]: props.originalSchema as AutoBeOpenApi.IJsonSchemaDescriptive, }, databaseSchemas: AutoBeInterfaceSchemaProgrammer.getNeighborDatabaseSchemas({ typeName: props.typeName, application: ctx.state().database!.result.data, }), }, }); const event: AutoBeInterfaceSchemaCastingEvent = await preliminary.orchestrate(ctx, async (out) => { const pointer: IPointer<IAutoBeInterfaceSchemaCastingApplication.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.originalSchema, preliminary, pointer, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...transformInterfaceSchemaCastingHistory({ state: ctx.state(), instruction: props.instruction, typeName: props.typeName, refineOperations: props.refineOperations, originalSchema: props.originalSchema, preliminary, }), }); if (pointer.value === null) return out(result)(null); // Fix schema if refined const refinedSchema: AutoBeOpenApi.IJsonSchemaDescriptive.IObject | null = pointer.value.casting !== null ? (AutoBeJsonSchemaFactory.fixDesign( pointer.value.casting, ) as AutoBeOpenApi.IJsonSchemaDescriptive.IObject) : null; return out(result)({ type: SOURCE, id: v7(), typeName: props.typeName, original: props.originalSchema, observation: pointer.value.observation, reasoning: pointer.value.reasoning, verdict: pointer.value.verdict, refined: refinedSchema, 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 AutoBeInterfaceSchemaCastingEvent); }); ctx.dispatch(event); return event.refined || null; } function createController( ctx: AutoBeContext, props: { typeName: string; schema: AutoBeOpenApi.IJsonSchema; operations: AutoBeOpenApi.IOperation[]; pointer: IPointer<IAutoBeInterfaceSchemaCastingApplication.IWrite | null>; preliminary: AutoBePreliminaryController< | "analysisSections" | "databaseSchemas" | "interfaceOperations" | "interfaceSchemas" | "previousAnalysisSections" | "previousDatabaseSchemas" | "previousInterfaceOperations" | "previousInterfaceSchemas" | "complete" >; }, ): IAgenticaController.IClass { const everyModels: AutoBeDatabase.IModel[] = ctx.state().database?.result.data.files.flatMap((f) => f.models) ?? []; const validate: Validator = (next) => { const result: IValidation<IAutoBeInterfaceSchemaCastingApplication.IProps> = typia.validate<IAutoBeInterfaceSchemaCastingApplication.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[] = []; if (result.data.request.casting !== null) AutoBeInterfaceSchemaProgrammer.validate({ path: "$input.request.design", errors, everyModels, operations: props.operations, typeName: props.typeName, design: result.data.request.casting, }); if (errors.length !== 0) return { success: false, errors, data: next, }; return result; }; const application: ILlmApplication = props.preliminary.fixApplication( typia.llm.application<IAutoBeInterfaceSchemaCastingApplication>({ validate: { process: validate, }, }), ); AutoBeInterfaceSchemaProgrammer.fixApplication({ application, everyModels, }); return { protocol: "class", name: SOURCE, application, execute: { process: (input) => { if (input.request.type === "write") props.pointer.value = input.request; }, } satisfies IAutoBeInterfaceSchemaCastingApplication, }; } type Validator = ( input: unknown, ) => IValidation<IAutoBeInterfaceSchemaCastingApplication.IProps>; const SOURCE = "interfaceSchemaCasting" satisfies AutoBeEventSource;