@autobe/agent
Version:
AI backend server code generator
350 lines (316 loc) • 12.2 kB
text/typescript
import {
AutoBeDatabase,
AutoBeOpenApi,
AutoBePreliminaryKind,
} from "@autobe/interface";
import { AutoBeOpenApiEndpointComparator, StringUtil } from "@autobe/utils";
import { LlmTypeChecker } from "@typia/utils";
import typia, { ILlmApplication, ILlmFunction, ILlmSchema } from "typia";
import { AutoBeState } from "../../../context/AutoBeState";
import { AutoBePreliminaryController } from "../AutoBePreliminaryController";
import { IAutoBePreliminaryRequest } from "../structures/AutoBePreliminaryRequest";
import { IAnalysisSectionEntry } from "../structures/IAnalysisSectionEntry";
import { IAutoBePreliminaryGetAnalysisSections } from "../structures/IAutoBePreliminaryGetAnalysisSections";
import { IAutoBePreliminaryGetDatabaseSchemas } from "../structures/IAutoBePreliminaryGetDatabaseSchemas";
import { IAutoBePreliminaryGetInterfaceOperations } from "../structures/IAutoBePreliminaryGetInterfaceOperations";
import { IAutoBePreliminaryGetInterfaceSchemas } from "../structures/IAutoBePreliminaryGetInterfaceSchemas";
import { IAutoBePreliminaryGetPreviousAnalysisSections } from "../structures/IAutoBePreliminaryGetPreviousAnalysisSections";
import { IAutoBePreliminaryGetPreviousDatabaseSchemas } from "../structures/IAutoBePreliminaryGetPreviousDatabaseSchemas";
import { IAutoBePreliminaryGetPreviousInterfaceOperations } from "../structures/IAutoBePreliminaryGetPreviousInterfaceOperations";
import { IAutoBePreliminaryGetPreviousInterfaceSchemas } from "../structures/IAutoBePreliminaryGetPreviousInterfaceSchemas";
import { IAutoBePreliminaryGetRealizeCollectors } from "../structures/IAutoBePreliminaryGetRealizeCollectors";
import { IAutoBePreliminaryGetRealizeTransformers } from "../structures/IAutoBePreliminaryGetRealizeTransformers";
export const fixPreliminaryApplication = <
Kind extends AutoBePreliminaryKind,
>(props: {
state: AutoBeState;
preliminary: AutoBePreliminaryController<Kind>;
application: ILlmApplication;
enumerable: boolean;
}): void => {
if (
props.preliminary.getKinds().some((k) => k.includes("previous")) === false
)
return;
const func: ILlmFunction | undefined = props.application.functions.find(
(f) => f.name === "process",
);
if (func === undefined) return;
const request: ILlmSchema | undefined = func.parameters.properties.request;
if (request === undefined) return;
const eraseKind = (kind: AutoBePreliminaryKind) => {
props.preliminary.getKinds().splice(
props.preliminary.getKinds().indexOf(
// biome-ignore lint: intended
kind as any,
),
1,
);
// biome-ignore lint: intended
delete (props.preliminary.getAll() as any)[kind];
// biome-ignore lint: intended
delete (props.preliminary.getLocal() as any)[kind];
};
const eraseMetadata = getUnionErasure({
$defs: func.parameters.$defs,
request,
});
if (eraseMetadata === null) return;
for (const kind of props.preliminary.getKinds().slice())
if (kind === "previousAnalysisSections") {
if (props.state.previousAnalyze === null) {
eraseMetadata("getPreviousAnalysisSections");
eraseKind(kind);
}
} else if (kind === "previousDatabaseSchemas") {
if (props.state.previousDatabase === null) {
eraseMetadata("getPreviousDatabaseSchemas");
eraseKind(kind);
}
} else if (kind === "previousInterfaceOperations") {
if (props.state.previousInterface === null) {
eraseMetadata("getPreviousInterfaceOperations");
eraseKind(kind);
}
} else if (kind === "previousInterfaceSchemas") {
if (props.state.previousInterface === null) {
eraseMetadata("getPreviousInterfaceSchemas");
eraseKind(kind);
}
}
for (const kind of props.preliminary.getKinds()) {
const accessor: Exclude<AutoBePreliminaryKind, `previous${string}`> = (
kind.startsWith("previous")
? (() => {
const value = kind.replace("previous", "");
return value[0].toLowerCase() + value.substring(1);
})()
: kind
) as Exclude<AutoBePreliminaryKind, `previous${string}`>;
if (
props.enumerable === false &&
accessor !== "analysisSections" &&
accessor !== "databaseSchemas"
)
continue;
ApplicationFixer[accessor]({
$defs: func.parameters.$defs,
// biome-ignore lint: intended
controller: props.preliminary as any,
previous: kind.startsWith("previous"),
});
}
};
const getUnionErasure = (props: {
$defs: Record<string, ILlmSchema>;
request: ILlmSchema;
}) => {
if (LlmTypeChecker.isAnyOf(props.request) === false) return null;
else if (
props.request.anyOf.some((s) => LlmTypeChecker.isReference(s) === false)
)
return null;
const children: ILlmSchema.IReference[] = props.request
.anyOf as ILlmSchema.IReference[];
const mapping: Record<string, string> =
props.request["x-discriminator"]?.mapping ?? {};
return (
key: IAutoBePreliminaryRequest<
Extract<AutoBePreliminaryKind, `previous${string}`>
>["request"]["type"],
): void => {
const type: string = `IAutoBePreliminary${key[0].toUpperCase()}${key.substring(1)}`;
const index: number = children.findIndex((c) =>
c.$ref.endsWith(`/${type}`),
);
if (index !== -1) children.splice(index, 1);
delete props.$defs[key];
delete mapping[key];
};
};
namespace ApplicationFixer {
export const analysisSections = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<
"analysisSections" | "previousAnalysisSections"
>;
previous: boolean;
}): void => {
const sections: IAnalysisSectionEntry[] =
props.controller.getAll()[
props.previous ? "previousAnalysisSections" : "analysisSections"
];
if (sections.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
props.previous
? typia.reflect.name<IAutoBePreliminaryGetPreviousAnalysisSections>()
: typia.reflect.name<IAutoBePreliminaryGetAnalysisSections>()
] as ILlmSchema.IObject;
if (type === undefined) return;
const array: ILlmSchema | undefined = type.properties.sectionIds;
if (array === undefined) return;
else if (LlmTypeChecker.isArray(array) === false) return;
// describe(
// array,
// StringUtil.trim`
// Here is the catalog of analysis sections available for retrieval:
// ID | File | Unit | Section | Keywords
// ---|------|------|---------|----------
// ${sections
// .map(
// (s) =>
// `${s.id} | ${s.filename} | ${s.unitTitle} | ${s.sectionTitle} | ${s.keywords.join(", ")}`,
// )
// .join("\n")}
// `,
// );
const items: ILlmSchema = array.items;
if (LlmTypeChecker.isInteger(items) === false) return;
items.minimum = Math.min(...sections.map((s) => s.id));
items.maximum = Math.max(...sections.map((s) => s.id));
};
export const databaseSchemas = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<
"databaseSchemas" | "previousDatabaseSchemas"
>;
previous: boolean;
}): void => {
const schemas: AutoBeDatabase.IModel[] =
props.controller.getAll()[
props.previous ? "previousDatabaseSchemas" : "databaseSchemas"
];
if (schemas.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
props.previous
? typia.reflect.name<IAutoBePreliminaryGetPreviousDatabaseSchemas>()
: typia.reflect.name<IAutoBePreliminaryGetDatabaseSchemas>()
] as ILlmSchema.IObject;
if (type === undefined) return;
describe(
type.properties.schemaNames,
StringUtil.trim`
Here is the list of database schemas available for retrieval:
${schemas
.slice()
.sort((a, b) => a.name.localeCompare(b.name))
.map((s) => `- ${s.name}`)
.join("\n")}
`,
);
};
export const interfaceOperations = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<
"interfaceOperations" | "previousInterfaceOperations"
>;
previous: boolean;
}): void => {
const operations: AutoBeOpenApi.IOperation[] =
props.controller.getAll()[
props.previous ? "previousInterfaceOperations" : "interfaceOperations"
];
if (operations.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
props.previous
? typia.reflect.name<IAutoBePreliminaryGetPreviousInterfaceOperations>()
: typia.reflect.name<IAutoBePreliminaryGetInterfaceOperations>()
] as ILlmSchema.IObject;
if (type === undefined) return;
describe(
type.properties.endpoints,
StringUtil.trim`
Here is the list of interface operations available for retrieval:
path | method
---- | ------
${operations
.slice()
.sort(AutoBeOpenApiEndpointComparator.compare)
.map((o) => ` ${o.path} | ${o.method}`)
.join("\n")}
`,
);
};
export const interfaceSchemas = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<
"interfaceSchemas" | "previousInterfaceSchemas"
>;
previous: boolean;
}): void => {
const dtoTypeNames: string[] = Object.keys(
props.controller.getAll()[
props.previous ? "previousInterfaceSchemas" : "interfaceSchemas"
] satisfies Record<string, AutoBeOpenApi.IJsonSchema>,
);
if (dtoTypeNames.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
props.previous
? typia.reflect.name<IAutoBePreliminaryGetPreviousInterfaceSchemas>()
: typia.reflect.name<IAutoBePreliminaryGetInterfaceSchemas>()
] as ILlmSchema.IObject;
if (type === undefined) return;
describe(
type.properties.typeNames,
StringUtil.trim`
Here is the list of interface schemas available for retrieval:
${dtoTypeNames
.slice()
.sort((a, b) => a.localeCompare(b))
.map((name) => `- ${name}`)
.join("\n")}
`,
);
};
export const realizeCollectors = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<"realizeCollectors">;
}): void => {
if (props.controller.getAll().realizeCollectors.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
typia.reflect.name<IAutoBePreliminaryGetRealizeCollectors>()
] as ILlmSchema.IObject;
if (type === undefined) return;
describe(
type.properties.dtoTypeNames,
StringUtil.trim`
Here is the list of DTO types available for realize collectors:
${props.controller
.getAll()
.realizeCollectors.slice()
.sort((a, b) => a.plan.dtoTypeName.localeCompare(b.plan.dtoTypeName))
.map((x) => `- ${x.plan.dtoTypeName}`)
.join("\n")}
`,
);
};
// biome-ignore lint: no-op for control signal kind
export const complete = (_props: any): void => {};
export const realizeTransformers = (props: {
$defs: Record<string, ILlmSchema>;
controller: AutoBePreliminaryController<"realizeTransformers">;
}): void => {
if (props.controller.getAll().realizeTransformers.length === 0) return;
const type: ILlmSchema.IObject = props.$defs[
typia.reflect.name<IAutoBePreliminaryGetRealizeTransformers>()
] as ILlmSchema.IObject;
if (type === undefined) return;
describe(
type.properties.dtoTypeNames,
StringUtil.trim`
Here is the list of DTO types available for realize transformers:
${props.controller
.getAll()
.realizeTransformers.slice()
.sort((a, b) => a.plan.dtoTypeName.localeCompare(b.plan.dtoTypeName))
.map((x) => `- ${x.plan.dtoTypeName}`)
.join("\n")}
`,
);
};
}
const describe = (schema: ILlmSchema, content: string): void => {
schema.description ??= "";
if (schema.description.length > 0) schema.description += "\n\n";
schema.description += content;
};