@autobe/agent
Version:
AI backend server code generator
162 lines (152 loc) • 5.41 kB
text/typescript
import { IAgenticaController, MicroAgentica } from "@agentica/core";
import {
AutoBeTestScenario,
AutoBeTestWriteEvent,
IAutoBeCompiler,
} from "@autobe/interface";
import { ILlmApplication, ILlmSchema } from "@samchon/openapi";
import { IPointer } from "tstl";
import typia from "typia";
import { AutoBeContext } from "../../context/AutoBeContext";
import { assertSchemaModel } from "../../context/assertSchemaModel";
import { enforceToolCall } from "../../utils/enforceToolCall";
import { forceRetry } from "../../utils/forceRetry";
import { completeTestCode } from "./compile/completeTestCode";
import { getTestScenarioArtifacts } from "./compile/getTestScenarioArtifacts";
import { IAutoBeTestScenarioArtifacts } from "./structures/IAutoBeTestScenarioArtifacts";
import { IAutoBeTestWriteApplication } from "./structures/IAutoBeTestWriteApplication";
import { IAutoBeTestWriteResult } from "./structures/IAutoBeTestWriteResult";
import { transformTestWriteHistories } from "./transformTestWriteHistories";
export async function orchestrateTestWrite<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
scenarios: AutoBeTestScenario[],
): Promise<IAutoBeTestWriteResult[]> {
const start: Date = new Date();
let complete: number = 0;
const result: Array<IAutoBeTestWriteResult | null> = await Promise.all(
/**
* Generate test code for each scenario. Maps through plans array to create
* individual test code implementations. Each scenario is processed to
* generate corresponding test code and progress events.
*/
scenarios.map(async (scenario) => {
try {
const r = await forceRetry(async () => {
const artifacts: IAutoBeTestScenarioArtifacts =
await getTestScenarioArtifacts(ctx, scenario);
const result: IAutoBeTestWriteApplication.IProps = await process(
ctx,
scenario,
artifacts,
);
const event: AutoBeTestWriteEvent = {
type: "testWrite",
created_at: start.toISOString(),
location: `test/features/api/${result.domain}/${scenario.functionName}.ts`,
...result,
completed: ++complete,
total: scenarios.length,
step: ctx.state().interface?.step ?? 0,
};
ctx.dispatch(event);
return {
scenario,
artifacts,
event,
};
});
return r;
} catch {
return null;
}
}),
);
return result.filter((r) => r !== null);
}
/**
* Process function that generates test code for each individual scenario. Takes
* the AutoBeContext and scenario information as input and uses MicroAgentica to
* generate appropriate test code through LLM interaction.
*
* @param ctx - The AutoBeContext containing model, vendor and configuration
* @param scenario - The test scenario information to generate code for
* @param artifacts - The artifacts containing the reference files and schemas
* @returns Promise resolving to IAutoBeTestWriteApplication.IProps containing
* the generated test code
*/
async function process<Model extends ILlmSchema.Model>(
ctx: AutoBeContext<Model>,
scenario: AutoBeTestScenario,
artifacts: IAutoBeTestScenarioArtifacts,
): Promise<IAutoBeTestWriteApplication.IProps> {
const pointer: IPointer<IAutoBeTestWriteApplication.IProps | null> = {
value: null,
};
const agentica: MicroAgentica<Model> = new MicroAgentica({
model: ctx.model,
vendor: ctx.vendor,
config: {
...(ctx.config ?? {}),
},
histories: transformTestWriteHistories(scenario, artifacts),
controllers: [
createApplication({
model: ctx.model,
artifacts,
build: (next) => {
pointer.value = next;
},
}),
],
});
enforceToolCall(agentica);
await agentica.conversate("Create e2e test functions.").finally(() => {
const tokenUsage = agentica.getTokenUsage();
ctx.usage().record(tokenUsage, ["test"]);
});
if (pointer.value === null) throw new Error("Failed to create test code.");
const compiler: IAutoBeCompiler = await ctx.compiler();
pointer.value.final = await compiler.typescript.beautify(pointer.value.final);
return pointer.value;
}
function createApplication<Model extends ILlmSchema.Model>(props: {
model: Model;
artifacts: IAutoBeTestScenarioArtifacts;
build: (next: IAutoBeTestWriteApplication.IProps) => void;
}): IAgenticaController.IClass<Model> {
assertSchemaModel(props.model);
const application: ILlmApplication<Model> = collection[
props.model
] as unknown as ILlmApplication<Model>;
return {
protocol: "class",
name: "Create Test Code",
application,
execute: {
write: (next) => {
next.draft = completeTestCode(props.artifacts, next.draft);
next.final = completeTestCode(props.artifacts, next.final);
props.build(next);
},
} satisfies IAutoBeTestWriteApplication,
};
}
const claude = typia.llm.application<
IAutoBeTestWriteApplication,
"claude",
{
reference: true;
}
>();
const collection = {
chatgpt: typia.llm.application<
IAutoBeTestWriteApplication,
"chatgpt",
{ reference: true }
>(),
claude,
llama: claude,
deepseek: claude,
"3.1": claude,
"3.0": typia.llm.application<IAutoBeTestWriteApplication, "3.0">(),
};