@autobe/agent
Version:
AI backend server code generator
305 lines (291 loc) • 10.1 kB
text/typescript
import {
AutoBeEventSource,
AutoBeOpenApi,
AutoBeProgressEventBase,
AutoBeRealizeAuthorization,
AutoBeRealizeCollectorFunction,
AutoBeRealizeOperationFunction,
AutoBeRealizeTransformerFunction,
AutoBeRealizeWriteEvent,
} from "@autobe/interface";
import { IPointer, Singleton } from "tstl";
import typia, { ILlmApplication, ILlmController, IValidation } from "typia";
import { v7 } from "uuid";
import { AutoBeContext } from "../../context/AutoBeContext";
import { buildAnalysisContextSections } from "../../utils/RAGRetrieval";
import { executeCachedBatch } from "../../utils/executeCachedBatch";
import { forceRetry } from "../../utils/forceRetry";
import { getEmbedder } from "../../utils/getEmbedder";
import { validateEmptyCode } from "../../utils/validateEmptyCode";
import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController";
import { convertToSectionEntries } from "../common/internal/convertToSectionEntries";
import { IAnalysisSectionEntry } from "../common/structures/IAnalysisSectionEntry";
import { transformRealizeOperationWriteHistory } from "./histories/transformRealizeOperationWriteHistory";
import { AutoBeRealizeOperationProgrammer } from "./programmers/AutoBeRealizeOperationProgrammer";
import { IAutoBeRealizeOperationWriteApplication } from "./structures/IAutoBeRealizeOperationWriteApplication";
import { IAutoBeRealizeScenarioResult } from "./structures/IAutoBeRealizeScenarioResult";
export async function orchestrateRealizeOperationWrite(
ctx: AutoBeContext,
props: {
authorizations: AutoBeRealizeAuthorization[];
collectors: AutoBeRealizeCollectorFunction[];
transformers: AutoBeRealizeTransformerFunction[];
progress: AutoBeProgressEventBase;
targetEndpoints?: AutoBeOpenApi.IEndpoint[];
},
): Promise<AutoBeRealizeOperationFunction[]> {
const document: AutoBeOpenApi.IDocument = ctx.state().interface!.document;
const allScenarios: IAutoBeRealizeScenarioResult[] = document.operations.map(
(operation) =>
AutoBeRealizeOperationProgrammer.getScenario({
authorizations: props.authorizations,
operation,
}),
);
const scenarios: IAutoBeRealizeScenarioResult[] = props.targetEndpoints
? allScenarios.filter((s) =>
props.targetEndpoints!.some(
(e) => e.method === s.operation.method && e.path === s.operation.path,
),
)
: allScenarios;
return await executeCachedBatch(
ctx,
scenarios.map((s) => {
const counter = new Singleton(() => ++props.progress.completed);
return async (promptCacheKey: string) => {
try {
return await forceRetry(() =>
process(ctx, {
document,
totalAuthorizations: props.authorizations,
collectors: props.collectors,
transformers: props.transformers,
authorization: s.decoratorEvent ?? null,
scenario: s,
progress: props.progress,
counter,
promptCacheKey,
}),
);
} catch (error) {
counter.get();
throw error;
}
};
}),
);
}
async function process(
ctx: AutoBeContext,
props: {
document: AutoBeOpenApi.IDocument;
authorization: AutoBeRealizeAuthorization | null;
collectors: AutoBeRealizeCollectorFunction[];
totalAuthorizations: AutoBeRealizeAuthorization[];
scenario: IAutoBeRealizeScenarioResult;
transformers: AutoBeRealizeTransformerFunction[];
progress: AutoBeProgressEventBase;
counter: Singleton<number>;
promptCacheKey: string;
},
): Promise<AutoBeRealizeOperationFunction> {
const allSections: IAnalysisSectionEntry[] = convertToSectionEntries(
ctx.state().analyze?.files ?? [],
);
const pathSegments = props.scenario.operation.path
.split("/")
.filter((p) => p && !p.startsWith(":") && !p.startsWith("{"));
const queryText: string = [
"operation",
"write",
props.scenario.operation.method,
...pathSegments,
props.scenario.functionName,
].join(" ");
const ragSections: IAnalysisSectionEntry[] =
await buildAnalysisContextSections(
getEmbedder(),
allSections,
queryText,
"TOPK",
{ log: false, logPrefix: "realizeOperationWrite" },
);
const preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "realizeCollectors"
| "realizeTransformers"
| "complete"
> = new AutoBePreliminaryController({
source: SOURCE,
application:
typia.json.application<IAutoBeRealizeOperationWriteApplication>(),
kinds: [
"analysisSections",
"databaseSchemas",
"realizeCollectors",
"realizeTransformers",
"complete",
],
dispatch: (e) => ctx.dispatch(e),
state: ctx.state(),
all: {
realizeCollectors: props.collectors,
realizeTransformers: props.transformers,
},
local: {
realizeCollectors: props.collectors.filter(
(c) =>
c.plan.dtoTypeName === props.scenario.operation.requestBody?.typeName,
),
realizeTransformers:
AutoBeRealizeOperationProgrammer.getLocalTransformers({
operation: props.scenario.operation,
schemas: props.document.components.schemas,
transformers: props.transformers,
}),
analysisSections: ragSections,
},
});
const event: AutoBeRealizeWriteEvent = await preliminary.orchestrate(
ctx,
async (out) => {
const pointer: IPointer<IAutoBeRealizeOperationWriteApplication.IWrite | null> =
{
value: null,
};
const dto: Record<string, string> =
await AutoBeRealizeOperationProgrammer.writeStructures(
ctx,
props.scenario.operation,
);
const result: AutoBeContext.IResult = await ctx.conversate({
source: "realizeWrite",
controller: createController({
functionName: props.scenario.functionName,
build: (next) => {
pointer.value = next;
},
preliminary,
}),
enforceFunctionCall: true,
promptCacheKey: props.promptCacheKey,
...transformRealizeOperationWriteHistory({
state: ctx.state(),
scenario: props.scenario,
authorization: props.authorization,
totalAuthorizations: props.totalAuthorizations,
collectors: props.collectors,
transformers: props.transformers,
dto,
preliminary,
}),
});
if (pointer.value === null) return out(result)(null);
const template: string = AutoBeRealizeOperationProgrammer.writeTemplate({
authorizations: props.totalAuthorizations,
schemas: props.document.components.schemas,
operation: props.scenario.operation,
collectors: props.collectors,
transformers: props.transformers,
});
const functor: AutoBeRealizeOperationFunction = {
type: "operation",
endpoint: {
method: props.scenario.operation.method,
path: props.scenario.operation.path,
},
location: props.scenario.location,
name: props.scenario.functionName,
content: await AutoBeRealizeOperationProgrammer.replaceImportStatements(
ctx,
{
operation: props.scenario.operation,
schemas: props.document.components.schemas,
code: pointer.value.revise.final ?? pointer.value.draft,
payload: props.authorization?.payload.name,
},
),
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 AutoBeRealizeOperationFunction;
}
function createController(props: {
functionName: string;
build: (next: IAutoBeRealizeOperationWriteApplication.IWrite) => void;
preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "realizeCollectors"
| "realizeTransformers"
| "complete"
>;
}): ILlmController {
const validate: Validator = (input) => {
const result: IValidation<IAutoBeRealizeOperationWriteApplication.IProps> =
typia.validate<IAutoBeRealizeOperationWriteApplication.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[] = [
...validateEmptyCode({
name: props.functionName,
draft: result.data.request.draft,
revise: result.data.request.revise,
path: "$input.request",
asynchronous: true,
}),
...AutoBeRealizeOperationProgrammer.validateSelectTransformContract({
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<IAutoBeRealizeOperationWriteApplication>({
validate: {
process: validate,
},
}),
);
return {
protocol: "class",
name: SOURCE,
application,
execute: {
process: (next) => {
if (next.request.type === "write") props.build(next.request);
},
} satisfies IAutoBeRealizeOperationWriteApplication,
};
}
type Validator = (
input: unknown,
) => IValidation<IAutoBeRealizeOperationWriteApplication.IProps>;
const SOURCE = "realizeWrite" satisfies AutoBeEventSource;