@autobe/agent
Version:
AI backend server code generator
242 lines (228 loc) • 7.52 kB
text/typescript
import {
AutoBeAnalyze,
AutoBeDatabase,
AutoBeEventSource,
AutoBeInterfaceEndpointDesign,
AutoBeInterfaceEndpointEvent,
AutoBeInterfaceGroup,
AutoBeProgressEventBase,
} from "@autobe/interface";
import { AutoBeOpenApiEndpointComparator } from "@autobe/utils";
import { HashMap, IPointer, Pair, Singleton } from "tstl";
import typia, { ILlmApplication, ILlmController, IValidation } from "typia";
import { v7 } from "uuid";
import { AutoBeContext } from "../../context/AutoBeContext";
import { IAutoBeOrchestrateHistory } from "../../structures/IAutoBeOrchestrateHistory";
import { buildAnalysisContextSections } from "../../utils/RAGRetrieval";
import { getEmbedder } from "../../utils/getEmbedder";
import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController";
import { convertToSectionEntries } from "../common/internal/convertToSectionEntries";
import { IAnalysisSectionEntry } from "../common/structures/IAnalysisSectionEntry";
import { AutoBeInterfaceEndpointProgrammer } from "./programmers/AutoBeInterfaceEndpointProgrammer";
import { IAutoBeInterfaceEndpointWriteApplication } from "./structures/IAutoBeInterfaceEndpointWriteApplication";
interface IProgrammer {
kind: AutoBeInterfaceEndpointEvent["kind"];
history(next: {
group: AutoBeInterfaceGroup;
preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceOperations"
| "complete"
>;
}): IAutoBeOrchestrateHistory;
}
export const orchestrateInterfaceEndpointWrite = async (
ctx: AutoBeContext,
props: {
programmer: IProgrammer;
group: AutoBeInterfaceGroup;
progress: AutoBeProgressEventBase;
counter: Singleton<number>;
promptCacheKey: string;
},
): Promise<AutoBeInterfaceEndpointDesign[]> => {
const start: Date = new Date();
const allSections: IAnalysisSectionEntry[] = convertToSectionEntries(
ctx.state().analyze?.files ?? [],
);
const queryText: string = [
"interface",
"endpoint",
props.group.name,
...props.group.databaseSchemas,
].join(" ");
const ragSections: IAnalysisSectionEntry[] =
await buildAnalysisContextSections(
getEmbedder(),
allSections,
queryText,
"TOPK",
{ log: false, logPrefix: "interfaceEndpointWrite" },
);
const databaseSchemas: Map<string, AutoBeDatabase.IModel> = new Map(
ctx
.state()
.database!.result.data.files.flatMap((f) => f.models)
.map((m) => [m.name, m]),
);
const preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceOperations"
| "complete"
> = new AutoBePreliminaryController({
dispatch: (e) => ctx.dispatch(e),
state: ctx.state(),
application:
typia.json.application<IAutoBeInterfaceEndpointWriteApplication>(),
kinds: [
"analysisSections",
"databaseSchemas",
"previousAnalysisSections",
"previousDatabaseSchemas",
"previousInterfaceOperations",
"complete",
],
source: SOURCE,
local: {
analysisSections: ragSections,
databaseSchemas: props.group.databaseSchemas
.map((key) => databaseSchemas.get(key))
.filter((m) => m !== undefined),
},
});
const event: AutoBeInterfaceEndpointEvent = await preliminary.orchestrate(
ctx,
async (out) => {
const pointer: IPointer<IAutoBeInterfaceEndpointWriteApplication.IWrite | null> =
{
value: null,
};
const result: AutoBeContext.IResult = await ctx.conversate({
source: SOURCE,
controller: createController({
actors: ctx.state().analyze?.actors ?? [],
preliminary,
build: (next) => {
pointer.value = next;
},
}),
enforceFunctionCall: true,
promptCacheKey: props.promptCacheKey,
...props.programmer.history({
group: props.group,
preliminary,
}),
});
if (pointer.value === null) return out(result)(null);
const actors: AutoBeAnalyze.IActor[] = ctx.state().analyze?.actors ?? [];
const designs: AutoBeInterfaceEndpointDesign[] = new HashMap(
pointer.value.designs.map((c) => new Pair(c.endpoint, c)),
AutoBeOpenApiEndpointComparator.hashCode,
AutoBeOpenApiEndpointComparator.equals,
)
.toJSON()
.map((it) =>
AutoBeInterfaceEndpointProgrammer.fixDesign({
actors,
design: it.second,
}),
)
.filter((design) =>
AutoBeInterfaceEndpointProgrammer.filter({
kind: props.programmer.kind,
design,
actors,
}),
);
return out(result)({
id: v7(),
type: SOURCE,
kind: props.programmer.kind,
group: props.group.name,
analysis: pointer.value.analysis,
rationale: pointer.value.rationale,
designs,
acquisition: preliminary.getAcquisition(),
metric: result.metric,
tokenUsage: result.tokenUsage,
created_at: start.toISOString(),
step: ctx.state().analyze?.step ?? 0,
completed: props.counter.get(),
total: props.progress.total,
} satisfies AutoBeInterfaceEndpointEvent);
},
);
ctx.dispatch(event);
return event.designs;
};
const createController = (props: {
actors: AutoBeAnalyze.IActor[];
preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceOperations"
| "complete"
>;
build: (next: IAutoBeInterfaceEndpointWriteApplication.IWrite) => void;
}): ILlmController => {
const validate = (
input: unknown,
): IValidation<IAutoBeInterfaceEndpointWriteApplication.IProps> => {
const result: IValidation<IAutoBeInterfaceEndpointWriteApplication.IProps> =
typia.validate<IAutoBeInterfaceEndpointWriteApplication.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 designs: AutoBeInterfaceEndpointDesign[] =
result.data.request.designs;
const errors: IValidation.IError[] = [];
designs.forEach((d, i) => {
AutoBeInterfaceEndpointProgrammer.validateDesign({
actors: props.actors,
design: d,
errors,
path: `$input.request.designs[${i}]`,
});
});
if (errors.length !== 0)
return {
success: false,
errors,
data: input,
};
return result;
};
const application: ILlmApplication = props.preliminary.fixApplication(
typia.llm.application<IAutoBeInterfaceEndpointWriteApplication>({
validate: {
process: validate,
},
}),
);
AutoBeInterfaceEndpointProgrammer.fixApplication({
application,
actors: props.actors,
});
return {
protocol: "class",
name: SOURCE,
application,
execute: {
process: (next) => {
if (next.request.type === "write") props.build(next.request);
},
} satisfies IAutoBeInterfaceEndpointWriteApplication,
};
};
const SOURCE = "interfaceEndpoint" satisfies AutoBeEventSource;