UNPKG

@autobe/agent

Version:

AI backend server code generator

229 lines (217 loc) 7.43 kB
import { IAgenticaController } from "@agentica/core"; import { AutoBeDatabaseComponent, AutoBeDatabaseComponentTableDesign, AutoBeDatabaseSchemaEvent, AutoBeEventSource, AutoBeProgressEventBase, } from "@autobe/interface"; 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 { transformDatabaseSchemaHistory } from "./histories/transformDatabaseSchemaHistory"; import { AutoBeDatabaseSchemaProgrammer } from "./programmers/AutoBeDatabaseSchemaProgrammer"; import { IAutoBeDatabaseSchemaApplication } from "./structures/IAutoBeDatabaseSchemaApplication"; export async function orchestrateDatabaseSchema( ctx: AutoBeContext, props: { instruction: string; components: AutoBeDatabaseComponent[]; written: Set<string>; failed: Map<string, number>; progress: AutoBeProgressEventBase; }, ): Promise<AutoBeDatabaseSchemaEvent[]> { const start: Date = new Date(); const total: number = props.components .map( (c) => c.tables.filter((n) => props.written.has(n.name) === false).length, ) .reduce((x, y) => x + y, 0); props.progress.total += total; // Flatten component list into individual table tasks const designPairs: Array<{ component: AutoBeDatabaseComponent; design: AutoBeDatabaseComponentTableDesign; }> = props.components.flatMap((component) => component.tables .filter((table) => props.written.has(table.name) === false) .map((table) => ({ component, design: table, })), ); const events: Array<AutoBeDatabaseSchemaEvent | null> = await executeCachedBatch( ctx, designPairs.map((task) => async (promptCacheKey) => { const counter = new Singleton(() => ++props.progress.completed); try { const otherComponents: AutoBeDatabaseComponent[] = props.components.filter((c) => c !== task.component); const event: AutoBeDatabaseSchemaEvent = await process(ctx, { instruction: props.instruction, progress: props.progress, counter, component: task.component, design: task.design, otherComponents, start, promptCacheKey, }); ctx.dispatch(event); return event; } catch (error) { counter.get(); console.log("database schema error", task.design.name, error); const count: number | undefined = props.failed.get(task.design.name); if (count === undefined) props.failed.set(task.design.name, 1); else if (count < 3) props.failed.set(task.design.name, count + 1); else throw error; return null; } }), ); return events.filter((e) => e !== null); } async function process( ctx: AutoBeContext, props: { instruction: string; progress: AutoBeProgressEventBase; counter: Singleton<number>; component: AutoBeDatabaseComponent; design: AutoBeDatabaseComponentTableDesign; otherComponents: AutoBeDatabaseComponent[]; start: Date; promptCacheKey: string; }, ): Promise<AutoBeDatabaseSchemaEvent> { const preliminary: AutoBePreliminaryController< | "analysisSections" | "previousAnalysisSections" | "previousDatabaseSchemas" | "complete" > = new AutoBePreliminaryController({ dispatch: (e) => ctx.dispatch(e), application: typia.json.application<IAutoBeDatabaseSchemaApplication>(), source: SOURCE, kinds: [ "analysisSections", "previousAnalysisSections", "previousDatabaseSchemas", "complete", ], state: ctx.state(), config: { database: "ast", }, }); return await preliminary.orchestrate(ctx, async (out) => { const pointer: IPointer<IAutoBeDatabaseSchemaApplication.IWrite | null> = { value: null, }; const result: AutoBeContext.IResult = await ctx.conversate({ source: SOURCE, controller: createController({ preliminary, targetComponent: props.component, otherComponents: props.otherComponents, design: props.design, build: (next) => { pointer.value = next; }, dispatch: ctx.dispatch, }), enforceFunctionCall: true, promptCacheKey: props.promptCacheKey, ...transformDatabaseSchemaHistory({ component: props.component, design: props.design, otherComponents: props.otherComponents, instruction: props.instruction, preliminary, }), }); if (pointer.value === null) return out(result)(null); return out(result)({ type: SOURCE, id: v7(), created_at: props.start.toISOString(), plan: pointer.value.plan, namespace: props.component.namespace, definition: pointer.value.definition, acquisition: preliminary.getAcquisition(), metric: result.metric, tokenUsage: result.tokenUsage, completed: props.counter.get(), total: props.progress.total, step: ctx.state().analyze?.step ?? 0, } satisfies AutoBeDatabaseSchemaEvent); }); } function createController(props: { preliminary: AutoBePreliminaryController< | "analysisSections" | "previousAnalysisSections" | "previousDatabaseSchemas" | "complete" >; targetComponent: AutoBeDatabaseComponent; otherComponents: AutoBeDatabaseComponent[]; design: AutoBeDatabaseComponentTableDesign; build: (next: IAutoBeDatabaseSchemaApplication.IWrite) => void; dispatch: AutoBeContext["dispatch"]; }): IAgenticaController.IClass { const validate: Validator = (input) => { const result: IValidation<IAutoBeDatabaseSchemaApplication.IProps> = typia.validate<IAutoBeDatabaseSchemaApplication.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[] = []; AutoBeDatabaseSchemaProgrammer.validate({ path: "$input.request.definition", errors, targetTable: props.design.name, otherTables: [props.targetComponent, ...props.otherComponents] .flatMap((c) => c.tables.map((t) => t.name)) .filter((s) => s !== props.design.name), definition: result.data.request.definition, }); if (errors.length !== 0) return { success: false, data: result.data, errors, }; return result; }; const application: ILlmApplication = props.preliminary.fixApplication( typia.llm.application<IAutoBeDatabaseSchemaApplication>({ validate: { process: validate, }, }), ); return { protocol: "class", name: SOURCE, application, execute: { process: (next) => { if (next.request.type === "write") props.build(next.request); }, } satisfies IAutoBeDatabaseSchemaApplication, }; } type Validator = ( input: unknown, ) => IValidation<IAutoBeDatabaseSchemaApplication.IProps>; const SOURCE = "databaseSchema" satisfies AutoBeEventSource;