UNPKG

@autobe/agent

Version:

AI backend server code generator

249 lines (237 loc) 8.32 kB
import { AutoBeDatabase, AutoBeEventSource, AutoBeInterfaceHistory, AutoBeOpenApi, AutoBeProgressEventBase, AutoBeRealizeCollectorFunction, AutoBeRealizeCollectorPlan, AutoBeRealizeWriteEvent, } from "@autobe/interface"; import { AutoBeOpenApiTypeChecker } from "@autobe/utils"; import { IPointer, Singleton } from "tstl"; import typia, { ILlmApplication, ILlmController, IValidation } from "typia"; import { v7 } from "uuid"; import { AutoBeContext } from "../../context/AutoBeContext"; import { executeCachedBatch } from "../../utils/executeCachedBatch"; import { forceRetry } from "../../utils/forceRetry"; import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController"; import { transformRealizeCollectorWriteHistory } from "./histories/transformRealizeCollectorWriteHistory"; import { AutoBeRealizeCollectorProgrammer } from "./programmers/AutoBeRealizeCollectorProgrammer"; import { IAutoBeRealizeCollectorWriteApplication } from "./structures/IAutoBeRealizeCollectorWriteApplication"; export async function orchestrateRealizeCollectorWrite( ctx: AutoBeContext, props: { plans: AutoBeRealizeCollectorPlan[]; progress: AutoBeProgressEventBase; }, ): Promise<AutoBeRealizeCollectorFunction[]> { const history: AutoBeInterfaceHistory | null = ctx.state().interface; if (history === null) throw new Error("Cannot realize collector write without interface."); const document: AutoBeOpenApi.IDocument = history.document; const getNeighbors = ( plan: AutoBeRealizeCollectorPlan, ): AutoBeRealizeCollectorPlan[] => { const visited: Set<string> = new Set(); AutoBeOpenApiTypeChecker.visit({ components: document.components, schema: { $ref: `#/components/schemas/${plan.dtoTypeName}` }, closure: (next) => { if (AutoBeOpenApiTypeChecker.isReference(next)) { const key: string = next.$ref.split("/").pop()!; visited.add(key); } }, }); return props.plans.filter( (p) => p.dtoTypeName !== plan.dtoTypeName && visited.has(p.dtoTypeName), ); }; props.progress.total += props.plans.length; const result: AutoBeRealizeCollectorFunction[] = await executeCachedBatch( ctx, props.plans.map((x) => { const counter = new Singleton(() => ++props.progress.completed); return (promptCacheKey: string) => forceRetry(() => process(ctx, { document: history.document, progress: props.progress, counter, neighbors: getNeighbors(x), plan: x, promptCacheKey, }), ); }), ); return result; } async function process( ctx: AutoBeContext, props: { document: AutoBeOpenApi.IDocument; plan: AutoBeRealizeCollectorPlan; neighbors: AutoBeRealizeCollectorPlan[]; promptCacheKey: string; progress: AutoBeProgressEventBase; counter: Singleton<number>; }, ): Promise<AutoBeRealizeCollectorFunction> { const models: AutoBeDatabase.IModel[] = ctx .state() .database!.result.data.files.map((f) => f.models) .flat(); const dtoTypeName: string = props.plan.dtoTypeName; const location: string = `src/collectors/${AutoBeRealizeCollectorProgrammer.getName(dtoTypeName)}.ts`; const preliminary: AutoBePreliminaryController< "databaseSchemas" | "complete" > = new AutoBePreliminaryController({ dispatch: (e) => ctx.dispatch(e), state: ctx.state(), source: SOURCE, application: typia.json.application<IAutoBeRealizeCollectorWriteApplication>(), kinds: ["databaseSchemas", "complete"], local: { databaseSchemas: models.filter( (m) => m.name === props.plan.databaseSchemaName, ), }, }); const event: AutoBeRealizeWriteEvent = await preliminary.orchestrate( ctx, async (out) => { const pointer: IPointer<IAutoBeRealizeCollectorWriteApplication.IWrite | null> = { value: null, }; const result: AutoBeContext.IResult = await ctx.conversate({ source: "realizeWrite", controller: createController(ctx, { plan: props.plan, neighbors: props.neighbors, build: (next) => { pointer.value = next; }, preliminary, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...(await transformRealizeCollectorWriteHistory(ctx, { plan: props.plan, neighbors: props.neighbors, preliminary, })), }); if (pointer.value === null) return out(result)(null); const content: string = await AutoBeRealizeCollectorProgrammer.replaceImportStatements(ctx, { dtoTypeName, schemas: props.document.components.schemas, code: pointer.value.revise.final ?? pointer.value.draft, }); const model: AutoBeDatabase.IModel | undefined = models.find( (m) => m.name === props.plan.databaseSchemaName, ); const body: AutoBeOpenApi.IJsonSchema = props.document.components.schemas[props.plan.dtoTypeName]; const template: string | undefined = model ? AutoBeRealizeCollectorProgrammer.writeTemplate({ plan: props.plan, body, model, application: ctx.state().database!.result.data, }) : undefined; const functor: AutoBeRealizeCollectorFunction = { type: "collector", plan: props.plan, neighbors: AutoBeRealizeCollectorProgrammer.getNeighbors(content), location, content, template, }; return out(result)({ id: v7(), type: "realizeWrite", function: functor, acquisition: preliminary.getAcquisition(), metric: result.metric, tokenUsage: result.tokenUsage, completed: props.counter.get(), total: props.progress.total, step: ctx.state().analyze?.step ?? 0, created_at: new Date().toISOString(), } satisfies AutoBeRealizeWriteEvent); }, ); ctx.dispatch(event); return event.function as AutoBeRealizeCollectorFunction; } function createController( ctx: AutoBeContext, props: { plan: AutoBeRealizeCollectorPlan; neighbors: AutoBeRealizeCollectorPlan[]; build: (next: IAutoBeRealizeCollectorWriteApplication.IWrite) => void; preliminary: AutoBePreliminaryController<"databaseSchemas" | "complete">; }, ): ILlmController { const validate = ( input: unknown, ): IValidation<IAutoBeRealizeCollectorWriteApplication.IProps> => { const result: IValidation<IAutoBeRealizeCollectorWriteApplication.IProps> = typia.validate<IAutoBeRealizeCollectorWriteApplication.IProps>(input); if (result.success === false) 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[] = AutoBeRealizeCollectorProgrammer.validate({ application: ctx.state().database!.result.data, mappings: result.data.request.mappings, plan: props.plan, neighbors: props.neighbors, draft: result.data.request.draft, revise: result.data.request.revise, }); return errors.length ? { success: false, errors, data: result.data, } : result; }; const application: ILlmApplication = props.preliminary.fixApplication( typia.llm.application<IAutoBeRealizeCollectorWriteApplication>({ validate: { process: validate, }, }), ); AutoBeRealizeCollectorProgrammer.fixApplication({ definition: application, application: ctx.state().database!.result.data, model: ctx .state() .database!.result.data.files.map((f) => f.models) .flat() .find((m) => m.name === props.plan.databaseSchemaName)!, }); return { protocol: "class", name: SOURCE, application, execute: { process: (next) => { if (next.request.type === "write") props.build(next.request); }, } satisfies IAutoBeRealizeCollectorWriteApplication, }; } const SOURCE = "realizeWrite" satisfies AutoBeEventSource;