@autobe/agent
Version:
AI backend server code generator
264 lines (248 loc) • 8.77 kB
text/typescript
import {
AutoBeAssistantMessageHistory,
AutoBeInterfaceAuthorization,
AutoBeInterfaceCompleteEvent,
AutoBeInterfaceGroupEvent,
AutoBeInterfaceHistory,
AutoBeOpenApi,
AutoBeProgressEventBase,
} from "@autobe/interface";
import { AutoBeInterfacePrerequisite } from "@autobe/interface/src/histories/contents/AutoBeInterfacePrerequisite";
import {
AutoBeOpenApiEndpointComparator,
missedOpenApiSchemas,
revertOpenApiAccessor,
} from "@autobe/utils";
import { ILlmSchema } from "@samchon/openapi";
import { HashMap, Pair } from "tstl";
import { v7 } from "uuid";
import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant";
import { AutoBeSystemPromptConstant } from "../../constants/AutoBeSystemPromptConstant";
import { AutoBeContext } from "../../context/AutoBeContext";
import { predicateStateMessage } from "../../utils/predicateStateMessage";
import { IAutoBeFacadeApplicationProps } from "../facade/histories/IAutoBeFacadeApplicationProps";
import { orchestrateInterfaceAuthorization } from "./orchestrateInterfaceAuthorization";
import { orchestrateInterfaceComplement } from "./orchestrateInterfaceComplement";
import { orchestrateInterfaceEndpoint } from "./orchestrateInterfaceEndpoint";
import { orchestrateInterfaceGroup } from "./orchestrateInterfaceGroup";
import { orchestrateInterfaceOperation } from "./orchestrateInterfaceOperation";
import { orchestrateInterfacePrerequisite } from "./orchestrateInterfacePrerequisite";
import { orchestrateInterfaceSchema } from "./orchestrateInterfaceSchema";
import { orchestrateInterfaceSchemaRename } from "./orchestrateInterfaceSchemaRename";
import { orchestrateInterfaceSchemaReview } from "./orchestrateInterfaceSchemaReview";
import { JsonSchemaFactory } from "./utils/JsonSchemaFactory";
import { JsonSchemaNamingConvention } from "./utils/JsonSchemaNamingConvention";
export const orchestrateInterface =
<Model extends ILlmSchema.Model>(ctx: AutoBeContext<Model>) =>
async (
props: IAutoBeFacadeApplicationProps,
): Promise<AutoBeAssistantMessageHistory | AutoBeInterfaceHistory> => {
// PREDICATION
const start: Date = new Date();
const predicate: string | null = predicateStateMessage(
ctx.state(),
"interface",
);
if (predicate !== null)
return ctx.assistantMessage({
type: "assistantMessage",
id: v7(),
created_at: start.toISOString(),
text: predicate,
completed_at: new Date().toISOString(),
});
ctx.dispatch({
type: "interfaceStart",
id: v7(),
created_at: start.toISOString(),
reason: props.instruction,
step: ctx.state().analyze?.step ?? 0,
});
//------------------------------------------------
// OPERATIONS
//------------------------------------------------
// ENDPOINTS
const init: AutoBeInterfaceGroupEvent = await orchestrateInterfaceGroup(
ctx,
{
instruction: props.instruction,
},
);
ctx.dispatch(init);
// AUTHORIZATION
const authorizations: AutoBeInterfaceAuthorization[] =
await orchestrateInterfaceAuthorization(ctx, {
instruction: props.instruction,
});
const authOperations: AutoBeOpenApi.IOperation[] = authorizations
.map((authorization) => authorization.operations)
.flat();
// ENDPOINTS & OPERATIONS
const endpoints: AutoBeOpenApi.IEndpoint[] =
await orchestrateInterfaceEndpoint(ctx, {
groups: init.groups,
authorizations: authOperations,
instruction: props.instruction,
});
const firstOperations: AutoBeOpenApi.IOperation[] =
await orchestrateInterfaceOperation(ctx, {
endpoints,
instruction: props.instruction,
});
const operations: AutoBeOpenApi.IOperation[] = new HashMap<
AutoBeOpenApi.IEndpoint,
AutoBeOpenApi.IOperation
>(
[...authOperations, ...firstOperations].map(
(o) =>
new Pair(
{
path: o.path,
method: o.method,
},
o, // early inserted be kept
),
),
AutoBeOpenApiEndpointComparator.hashCode,
AutoBeOpenApiEndpointComparator.equals,
)
.toJSON()
.map((it) => it.second);
// THE DOCUMENT
const document: AutoBeOpenApi.IDocument = {
operations,
components: {
authorizations: ctx.state().analyze?.actors ?? [],
schemas: {},
},
};
//------------------------------------------------
// DTO SCHEMAS
//------------------------------------------------
const assign = (
schemas: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive>,
) => {
Object.assign(document.components.schemas, schemas);
JsonSchemaFactory.authorize(document.components.schemas);
Object.assign(
document.components.schemas,
JsonSchemaFactory.presets(
new Set(Object.keys(document.components.schemas)),
),
);
JsonSchemaNamingConvention.schemas(
document.operations,
document.components.schemas,
);
JsonSchemaFactory.finalize({
document,
application: ctx.state().prisma!.result.data,
});
};
// INITIAL SCHEMAS
assign(
await orchestrateInterfaceSchema(ctx, {
instruction: props.instruction,
operations,
}),
);
// REVIEW GENERATED
const reviewProgress: AutoBeProgressEventBase = {
completed: 0,
total: 0,
};
for (const config of REVIEWERS) {
reviewProgress.total = Math.ceil(
(Object.keys(document.components.schemas).length * REVIEWERS.length) /
AutoBeConfigConstant.INTERFACE_CAPACITY,
);
assign(
await orchestrateInterfaceSchemaReview(ctx, config, {
instruction: props.instruction,
document,
schemas: document.components.schemas,
progress: reviewProgress,
}),
);
}
// COMPLEMENTATION
const complementProgress: AutoBeProgressEventBase = {
completed: 0,
total: 0,
};
while (missedOpenApiSchemas(document).length !== 0) {
// COMPLEMENT OMITTED
const oldbie: Set<string> = new Set(
Object.keys(document.components.schemas),
);
const complemented: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> =
await orchestrateInterfaceComplement(ctx, {
instruction: props.instruction,
progress: complementProgress,
document,
});
const newbie: Record<string, AutoBeOpenApi.IJsonSchemaDescriptive> =
Object.fromEntries(
Object.keys(complemented)
.filter((key) => oldbie.has(key) === false)
.map((key) => [key, complemented[key]]),
);
assign(complemented);
// REVIEW COMPLEMENTED
for (const config of REVIEWERS) {
reviewProgress.total = Math.ceil(
(Object.keys(document.components.schemas).length * REVIEWERS.length) /
AutoBeConfigConstant.INTERFACE_CAPACITY,
);
assign(
await orchestrateInterfaceSchemaReview(ctx, config, {
instruction: props.instruction,
document,
schemas: newbie,
progress: reviewProgress,
}),
);
}
}
await orchestrateInterfaceSchemaRename(ctx, document);
//------------------------------------------------
// FINALIZATION
//------------------------------------------------
// CONNECT PREREQUISITES
const prerequisites: AutoBeInterfacePrerequisite[] =
await orchestrateInterfacePrerequisite(ctx, document);
document.operations.forEach((op) => {
op.prerequisites =
prerequisites.find(
(p) => p.endpoint.method === op.method && p.endpoint.path === op.path,
)?.prerequisites ?? [];
});
// NORMALIZE ACCESSORS
revertOpenApiAccessor(document);
// DO COMPILE
return ctx.dispatch({
type: "interfaceComplete",
id: v7(),
document,
missed: missedOpenApiSchemas(document),
authorizations,
aggregates: ctx.getCurrentAggregates("interface"),
step: ctx.state().analyze?.step ?? 0,
elapsed: new Date().getTime() - start.getTime(),
created_at: new Date().toISOString(),
} satisfies AutoBeInterfaceCompleteEvent);
};
const REVIEWERS = [
{
kind: "relation" as const,
systemPrompt: AutoBeSystemPromptConstant.INTERFACE_SCHEMA_RELATION_REVIEW,
},
{
kind: "content" as const,
systemPrompt: AutoBeSystemPromptConstant.INTERFACE_SCHEMA_CONTENT_REVIEW,
},
{
kind: "security" as const,
systemPrompt: AutoBeSystemPromptConstant.INTERFACE_SCHEMA_SECURITY_REVIEW,
},
];