@autobe/agent
Version:
AI backend server code generator
191 lines (157 loc) • 6.63 kB
text/typescript
import { AutoBeDatabase, AutoBeOpenApi } from "@autobe/interface";
import { StringUtil } from "@autobe/utils";
import { v7 } from "uuid";
import { AutoBeSystemPromptConstant } from "../../../constants/AutoBeSystemPromptConstant";
import { AutoBeState } from "../../../context/AutoBeState";
import { IAutoBeOrchestrateHistory } from "../../../structures/IAutoBeOrchestrateHistory";
import { AutoBePreliminaryController } from "../../common/AutoBePreliminaryController";
import { transformInterfaceOperationParameterHistory } from "./transformInterfaceOperationParameterHistory";
export const transformInterfaceSchemaRefineHistory = (props: {
state: AutoBeState;
instruction: string;
preliminary: AutoBePreliminaryController<
| "analysisSections"
| "databaseSchemas"
| "interfaceOperations"
| "interfaceSchemas"
| "previousAnalysisSections"
| "previousDatabaseSchemas"
| "previousInterfaceOperations"
| "previousInterfaceSchemas"
| "complete"
>;
typeName: string;
operations: AutoBeOpenApi.IOperation[];
schema: AutoBeOpenApi.IJsonSchema.IObject;
}): IAutoBeOrchestrateHistory => {
const everyModels: AutoBeDatabase.IModel[] =
props.state.database?.result.data.files.flatMap((f) => f.models) ?? [];
const model: AutoBeDatabase.IModel | undefined = props.schema[
"x-autobe-database-schema"
]
? everyModels.find(
(m) => m.name === props.schema["x-autobe-database-schema"],
)
: undefined;
const result: IAutoBeOrchestrateHistory = {
histories: [
{
type: "systemMessage",
id: v7(),
created_at: new Date().toISOString(),
text: AutoBeSystemPromptConstant.INTERFACE_SCHEMA_REFINE,
},
...props.preliminary.getHistories(),
{
type: "assistantMessage",
id: v7(),
created_at: new Date().toISOString(),
text: StringUtil.trim`
## API Design Instructions
The following API-specific instructions were extracted from
the user's requirements. These focus on API interface design aspects
relevant to the refinement task.
Follow these instructions when enriching schemas with documentation
and metadata.
Carefully distinguish between:
- Suggestions or recommendations (consider these as guidance)
- Direct specifications or explicit commands (these must be followed exactly)
When instructions contain direct specifications or explicit design decisions,
follow them precisely even if you believe you have better alternatives.
${props.instruction}
`,
},
{
id: v7(),
type: "assistantMessage",
created_at: new Date().toISOString(),
text: StringUtil.trim`
## Operations (Filtered for Target Schema)
Here are the API operations that directly use the schema to refine.
These operations reference the target schema "${props.typeName}" via
requestBody.typeName or responseBody.typeName.
This FILTERED list helps you understand the exact usage context for
the schema you're enriching:
\`\`\`json
${JSON.stringify(props.operations)}
\`\`\`
${transformInterfaceOperationParameterHistory({
typeName: props.typeName,
operations: props.operations,
})}
## DTO type to refine
Here is the SPECIFIC schema that needs refinement for type "${props.typeName}":
\`\`\`json
${JSON.stringify(props.schema)}
\`\`\`
Also, here is the list of properties currently defined in this schema,
so you have to enrich them one by one. Note that, only this schema needs
refinement. Other schemas in the complete schema set are provided for
reference only.
${Object.keys(props.schema.properties)
.map((k) => `- ${k}`)
.join("\n")}
${transformDatabaseSchemaProperties({
everyModels,
model,
})}
`,
},
],
userMessage: StringUtil.trim`
Refine ${JSON.stringify(props.typeName)} schema by enriching each property
with \`databaseSchemaProperty\`, \`specification\`, and \`description\`.
**Object-level**: \`databaseSchema\` (nullable), \`specification\` (MANDATORY),
\`description\` (MANDATORY).
**Property-level**: Use \`depict\`, \`create\`, \`update\`, or \`erase\` for each
property in \`revises\`. DB properties not in this DTO go in \`excludes\`.
When \`databaseSchemaProperty\` or \`databaseSchema\` is \`null\`, the
\`specification\` becomes the ONLY source of truth for downstream agents.
You MUST provide a refinement for every single property without exception:
${Object.keys(props.schema.properties)
.map((k) => `- ${k}`)
.join("\n")}
${transformDatabaseSchemaProperties({
everyModels,
model,
})}
`,
};
return result;
};
function transformDatabaseSchemaProperties(props: {
everyModels: AutoBeDatabase.IModel[];
model: AutoBeDatabase.IModel | undefined;
}): string {
if (props.model === undefined) return "";
// Columns: primary key, plain fields, FK columns
const columns: string[] = [
props.model.primaryField.name,
...props.model.plainFields.map((f) => f.name),
...props.model.foreignFields.map((f) => f.name),
];
// Belongs-to relations (from FK)
const belongsTo: string[] = props.model.foreignFields.map(
(f) => f.relation.name,
);
// Has-many/has-one relations (opposite side)
const hasRelations: string[] = props.everyModels
.flatMap((m) =>
m.foreignFields
.filter((f) => f.relation.targetModel === props.model!.name)
.map((f) => f.relation.oppositeName),
)
.filter((name): name is string => name !== undefined);
return StringUtil.trim`
## Database Schema Properties for \`${props.model.name}\`
If you keep \`databaseSchema\` as \`${JSON.stringify(props.model.name)}\`,
every DB property below must be explicitly handled: either mapped to a DTO
property in \`revises\`, or declared in \`excludes\` with a reason.
**Columns** (scalar fields):
${columns.map((c) => `- \`${c}\``).join("\n")}
**Belongs-to Relations** (FK → object):
${belongsTo.length > 0 ? belongsTo.map((r) => `- \`${r}\``).join("\n") : "- (none)"}
**Has-many/Has-one Relations** (reverse side):
${hasRelations.length > 0 ? hasRelations.map((r) => `- \`${r}\``).join("\n") : "- (none)"}
`;
}