@autobe/agent
Version:
AI backend server code generator
253 lines (238 loc) • 8.06 kB
text/typescript
import { IAgenticaController } from "@agentica/core";
import {
AutoBeEventSource,
AutoBeOpenApi,
AutoBeProgressEventBase,
} from "@autobe/interface";
import { AutoBeInterfacePrerequisiteEvent } from "@autobe/interface/src/events/AutoBeInterfacePrerequisiteEvent";
import { HashMap, IPointer, Singleton } from "tstl";
import typia, { ILlmApplication, IValidation } from "typia";
import { v7 } from "uuid";
import { AutoBeContext } from "../../context/AutoBeContext";
import { buildAnalysisContextSections } from "../../utils/RAGRetrieval";
import { executeCachedBatch } from "../../utils/executeCachedBatch";
import { getEmbedder } from "../../utils/getEmbedder";
import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController";
import { convertToSectionEntries } from "../common/internal/convertToSectionEntries";
import { IAnalysisSectionEntry } from "../common/structures/IAnalysisSectionEntry";
import { transformInterfacePrerequisiteHistory } from "./histories/transformInterfacePrerequisiteHistory";
import { AutoBeInterfacePrerequisiteProgrammer } from "./programmers/AutoBeInterfacePrerequisiteProgrammer";
import { IAutoBeInterfacePrerequisiteApplication } from "./structures/IAutoBeInterfacePrerequisiteApplication";
export async function orchestrateInterfacePrerequisite(
ctx: AutoBeContext,
document: AutoBeOpenApi.IDocument,
): Promise<AutoBeInterfacePrerequisiteEvent[]> {
const dict: HashMap<AutoBeOpenApi.IEndpoint, AutoBeOpenApi.IOperation> =
AutoBeInterfacePrerequisiteProgrammer.associate(document.operations);
const candidates: AutoBeOpenApi.IOperation[] = document.operations.filter(
AutoBeInterfacePrerequisiteProgrammer.isCandidate,
);
const progress: AutoBeProgressEventBase = {
total: candidates.length,
completed: 0,
};
const result: Array<AutoBeInterfacePrerequisiteEvent | null> =
await executeCachedBatch(
ctx,
candidates.map((it) => async (promptCacheKey) => {
const counter = new Singleton(() => ++progress.completed);
try {
return await process(ctx, {
dict,
document,
operation: it,
progress,
promptCacheKey,
counter,
});
} catch {
counter.get();
return null;
}
}),
);
return result.filter((r) => r !== null);
}
async function process(
ctx: AutoBeContext,
props: {
dict: HashMap<AutoBeOpenApi.IEndpoint, AutoBeOpenApi.IOperation>;
document: AutoBeOpenApi.IDocument;
operation: AutoBeOpenApi.IOperation;
progress: AutoBeProgressEventBase;
promptCacheKey: string;
counter: Singleton<number>;
},
): Promise<AutoBeInterfacePrerequisiteEvent | null> {
const allSections: IAnalysisSectionEntry[] = convertToSectionEntries(
ctx.state().analyze?.files ?? [],
);
const domains = Array.from(
new Set(
props.operation.path
.split("/")
.filter((p) => p && !p.startsWith(":") && !p.startsWith("{")),
),
).join(", ");
const paths = props.operation.path;
const queryText: string = `
Domains: ${domains}
Task: ${paths}
`.trim();
const ragSections: IAnalysisSectionEntry[] =
await buildAnalysisContextSections(
getEmbedder(),
allSections,
queryText,
"TOPK",
{ log: false, logPrefix: "interfacePrerequisite" },
);
const preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "interfaceOperations"
| "interfaceSchemas"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceOperations"
| "previousInterfaceSchemas"
| "complete"
> = new AutoBePreliminaryController({
dispatch: (e) => ctx.dispatch(e),
state: ctx.state(),
application:
typia.json.application<IAutoBeInterfacePrerequisiteApplication>(),
source: SOURCE,
kinds: [
"analysisSections",
"databaseSchemas",
"interfaceOperations",
"interfaceSchemas",
"previousAnalysisSections",
"previousDatabaseSchemas",
"previousInterfaceOperations",
"previousInterfaceSchemas",
"complete",
],
all: {
interfaceOperations: props.document.operations,
interfaceSchemas: props.document.components.schemas,
},
local: {
analysisSections: ragSections,
interfaceOperations: [props.operation],
},
});
const event: AutoBeInterfacePrerequisiteEvent = await preliminary.orchestrate(
ctx,
async (out) => {
const pointer: IPointer<IAutoBeInterfacePrerequisiteApplication.IWrite | null> =
{
value: null,
};
const result: AutoBeContext.IResult = await ctx.conversate({
source: SOURCE,
controller: createController({
dict: props.dict,
document: props.document,
operation: props.operation,
preliminary,
build: (next) => {
pointer.value = next;
},
}),
enforceFunctionCall: true,
promptCacheKey: props.promptCacheKey,
...transformInterfacePrerequisiteHistory({
document: props.document,
operation: props.operation,
preliminary,
}),
});
if (pointer.value === null) return out(result)(null);
const event: AutoBeInterfacePrerequisiteEvent = {
type: SOURCE,
id: v7(),
endpoint: {
path: props.operation.path,
method: props.operation.method,
},
analysis: pointer.value.analysis,
rationale: pointer.value.rationale,
prerequisites: pointer.value.prerequisites,
acquisition: preliminary.getAcquisition(),
metric: result.metric,
tokenUsage: result.tokenUsage,
total: props.progress.total,
completed: props.counter.get(),
step: ctx.state().database?.step ?? 0,
created_at: new Date().toISOString(),
};
return out(result)(event);
},
);
ctx.dispatch(event);
return event;
}
function createController(props: {
dict: HashMap<AutoBeOpenApi.IEndpoint, AutoBeOpenApi.IOperation>;
document: AutoBeOpenApi.IDocument;
operation: AutoBeOpenApi.IOperation;
preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "interfaceOperations"
| "interfaceSchemas"
| "previousInterfaceOperations"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceSchemas"
| "complete"
>;
build: (next: IAutoBeInterfacePrerequisiteApplication.IWrite) => void;
}): IAgenticaController.IClass {
const validate = (
next: unknown,
): IValidation<IAutoBeInterfacePrerequisiteApplication.IProps> => {
const result: IValidation<IAutoBeInterfacePrerequisiteApplication.IProps> =
typia.validate<IAutoBeInterfacePrerequisiteApplication.IProps>(next);
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[] =
AutoBeInterfacePrerequisiteProgrammer.validate({
dict: props.dict,
document: props.document,
operation: props.operation,
complete: result.data.request,
});
return errors.length === 0
? result
: {
success: false,
errors,
data: result.data,
};
};
const application: ILlmApplication = props.preliminary.fixApplication(
typia.llm.application<IAutoBeInterfacePrerequisiteApplication>({
validate: {
process: validate,
},
}),
);
return {
protocol: "class",
name: SOURCE,
application,
execute: {
process: (next) => {
if (next.request.type === "write") props.build(next.request);
},
} satisfies IAutoBeInterfacePrerequisiteApplication,
};
}
const SOURCE = "interfacePrerequisite" satisfies AutoBeEventSource;