@autobe/agent
Version:
AI backend server code generator
268 lines (252 loc) • 8.41 kB
text/typescript
import { IAgenticaController } from "@agentica/core";
import {
AutoBeEventSource,
AutoBePrisma,
AutoBePrismaCorrectEvent,
IAutoBeCompiler,
IAutoBePrismaValidation,
} from "@autobe/interface";
import { ILlmApplication, ILlmSchema, IValidation } from "@samchon/openapi";
import { IPointer } from "tstl";
import typia from "typia";
import { v7 } from "uuid";
import { AutoBeContext } from "../../context/AutoBeContext";
import { assertSchemaModel } from "../../context/assertSchemaModel";
import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController";
import { transformPrismaCorrectHistory } from "./histories/transformPrismaCorrectHistory";
import { IAutoBePrismaCorrectApplication } from "./structures/IAutoBePrismaCorrectApplication";
export function orchestratePrismaCorrect<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
application: AutoBePrisma.IApplication,
): Promise<IAutoBePrismaValidation> {
const unique: Set<string> = new Set();
for (const file of application.files)
file.models = file.models.filter((model) => {
if (unique.has(model.name)) return false;
unique.add(model.name);
return true;
});
application.files = application.files.filter((f) => f.models.length !== 0);
return iterate(ctx, application, Math.max(ctx.retry, 8));
}
async function iterate<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
application: AutoBePrisma.IApplication,
life: number,
): Promise<IAutoBePrismaValidation> {
const compiler: IAutoBeCompiler = await ctx.compiler();
const result: IAutoBePrismaValidation =
await compiler.prisma.validate(application);
if (result.success)
return result; // SUCCESS
else if (life < 0) return result; // FAILURE
// VALIDATION FAILED
const schemas: Record<string, string> = await compiler.prisma.write(
application,
"postgres",
);
ctx.dispatch({
type: "prismaValidate",
id: v7(),
result,
schemas,
compiled: await compiler.prisma.compile({
files: schemas,
}),
step: ctx.state().analyze?.step ?? 0,
created_at: new Date().toISOString(),
});
const next: IExecutionResult = await process(ctx, result);
return iterate(ctx, next.correction, life - 1);
}
async function process<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
failure: IAutoBePrismaValidation.IFailure,
capacity: number = 8,
): Promise<IExecutionResult> {
const count: number = getTableCount(failure);
if (count <= capacity) return execute(ctx, failure);
let correction: AutoBePrisma.IApplication = failure.data;
const volume: number = Math.ceil(count / capacity);
const plannings: string[] = [];
const models: Record<string, AutoBePrisma.IModel> = {};
let i: number = 0;
while (i++ < volume && failure.errors.length !== 0) {
const next: IExecutionResult = await execute(ctx, {
...failure,
errors: (() => {
const unique: Set<string | null> = new Set();
const errors: IAutoBePrismaValidation.IError[] = [];
for (const err of failure.errors) {
unique.add(err.table ?? null);
if (unique.size > capacity) break;
else errors.push(err);
}
return errors;
})(),
});
plannings.push(next.planning);
for (const m of next.models) models[m.name] = m;
const compiler: IAutoBeCompiler = await ctx.compiler();
const result: IAutoBePrismaValidation = await compiler.prisma.validate(
next.correction,
);
correction = next.correction;
if (result.success === true) break;
else failure = result;
}
return {
type: "complete",
planning: plannings.join("\n\n"),
models: Object.values(models),
correction,
};
}
async function execute<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
failure: IAutoBePrismaValidation.IFailure,
): Promise<IExecutionResult> {
const preliminary: AutoBePreliminaryController<
"analysisFiles" | "prismaSchemas"
> = new AutoBePreliminaryController({
application: typia.json.application<IAutoBePrismaCorrectApplication>(),
source: SOURCE,
kinds: ["analysisFiles", "prismaSchemas"],
state: ctx.state(),
all: {
prismaSchemas: failure.data.files.map((f) => f.models).flat(),
},
local: {
prismaSchemas: Array.from(
new Set(failure.errors.map((e) => e.table).filter((t) => t !== null)),
)
.map((table: string): AutoBePrisma.IModel | undefined =>
failure.data.files
.map((f) => f.models)
.flat()
.find((m) => m.name === table),
)
.filter((m) => m !== undefined),
},
});
return await preliminary.orchestrate(ctx, async (out) => {
const pointer: IPointer<IAutoBePrismaCorrectApplication.IComplete | null> =
{
value: null,
};
const result: AutoBeContext.IResult<Model> = await ctx.conversate({
source: SOURCE,
controller: createController({
preliminary,
model: ctx.model,
build: (next) => {
pointer.value = next;
},
}),
enforceFunctionCall: true,
...transformPrismaCorrectHistory({
preliminary,
result: failure,
}),
});
if (pointer.value !== null) {
const correction: AutoBePrisma.IApplication = {
files: failure.data.files.map((file) => ({
filename: file.filename,
namespace: file.namespace,
models: file.models.map((model) => {
const newbie = pointer.value?.models.find(
(m) => m.name === model.name,
);
return newbie ?? model;
}),
})),
};
ctx.dispatch({
type: SOURCE,
id: v7(),
failure,
planning: pointer.value.planning,
correction: correction,
metric: result.metric,
tokenUsage: result.tokenUsage,
step: ctx.state().analyze?.step ?? 0,
created_at: new Date().toISOString(),
} satisfies AutoBePrismaCorrectEvent);
return out(result)({
...pointer.value,
correction,
});
}
return out(result)(null);
});
}
interface IExecutionResult extends IAutoBePrismaCorrectApplication.IComplete {
correction: AutoBePrisma.IApplication;
}
const getTableCount = (failure: IAutoBePrismaValidation.IFailure): number => {
const unique: Set<string | null> = new Set(
failure.errors.map((error) => error.table ?? null),
);
return unique.size;
};
function createController<Model extends ILlmSchema.Model>(props: {
model: Model;
preliminary: AutoBePreliminaryController<"analysisFiles" | "prismaSchemas">;
build: (next: IAutoBePrismaCorrectApplication.IComplete) => void;
}): IAgenticaController.IClass<Model> {
assertSchemaModel(props.model);
const validate: Validator = (input) => {
const result =
typia.validate<IAutoBePrismaCorrectApplication.IProps>(input);
if (result.success === false || result.data.request.type === "complete")
return result;
return props.preliminary.validate({
thinking: result.data.thinking,
request: result.data.request,
});
};
const application: ILlmApplication<Model> = collection[
props.model === "chatgpt"
? "chatgpt"
: props.model === "gemini"
? "gemini"
: "claude"
](
validate,
) satisfies ILlmApplication<any> as unknown as ILlmApplication<Model>;
return {
protocol: "class",
name: SOURCE satisfies AutoBeEventSource,
application,
execute: {
process: (next) => {
if (next.request.type === "complete") props.build(next.request);
},
} satisfies IAutoBePrismaCorrectApplication,
};
}
const collection = {
chatgpt: (validate: Validator) =>
typia.llm.application<IAutoBePrismaCorrectApplication, "chatgpt">({
validate: {
process: validate,
},
}),
claude: (validate: Validator) =>
typia.llm.application<IAutoBePrismaCorrectApplication, "claude">({
validate: {
process: validate,
},
}),
gemini: (validate: Validator) =>
typia.llm.application<IAutoBePrismaCorrectApplication, "gemini">({
validate: {
process: validate,
},
}),
};
type Validator = (
input: unknown,
) => IValidation<IAutoBePrismaCorrectApplication.IProps>;
const SOURCE = "prismaCorrect" satisfies AutoBeEventSource;