@autobe/agent
Version:
AI backend server code generator
87 lines (80 loc) • 8.91 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformInterfaceSchemaDecoupleHistory = void 0;
const utils_1 = require("@autobe/utils");
const uuid_1 = require("uuid");
const transformInterfaceSchemaDecoupleHistory = (props) => {
// filter schemas
const schemas = {};
for (const key of props.cycle.types)
utils_1.AutoBeOpenApiTypeChecker.visit({
components: { schemas: props.schemas, authorizations: [] },
schema: { $ref: `#/components/schemas/${key}` },
closure: (next) => {
if (utils_1.AutoBeOpenApiTypeChecker.isReference(next) === false)
return;
const name = next.$ref.split("/").at(-1);
const found = props.schemas[name];
if (found)
schemas[name] = found;
},
});
// filter operations
const operations = [];
for (const op of props.operations) {
const predicate = (key) => {
let matched = false;
utils_1.AutoBeOpenApiTypeChecker.visit({
components: { schemas: props.schemas, authorizations: [] },
schema: { $ref: `#/components/schemas/${key}` },
closure: (next) => {
if (utils_1.AutoBeOpenApiTypeChecker.isReference(next) === false)
return;
else if (schemas[next.$ref.split("/").at(-1)] !== undefined)
matched || (matched = true);
},
});
return matched;
};
if ((op.requestBody && predicate(op.requestBody.typeName)) ||
(op.responseBody && predicate(op.responseBody.typeName)))
operations.push(op);
}
return {
histories: [
{
type: "systemMessage",
id: (0, uuid_1.v7)(),
created_at: new Date().toISOString(),
text: "<!--\nfilename: INTERFACE_SCHEMA_DECOUPLE.md\n-->\n# Schema Decouple Agent\n\nYou resolve **one cross-type circular reference cycle** in OpenAPI DTO schema definitions.\n\n**Function calling is MANDATORY** \u2014 call `process` immediately without asking.\n\n## 1. Task\n\nCross-type circular references (A \u2192 B \u2192 A, or A \u2192 B \u2192 C \u2192 A) make code generation impossible. You receive **one programmatically detected cycle** \u2014 along with the full JSON schemas of all involved types and the API operations that reference them \u2014 and decide which property reference to remove to break it.\n\n**Self-references (A \u2192 A) are NOT your concern** \u2014 they represent legitimate tree structures (categories, org charts) and are handled separately.\n\n## 2. Decision Criteria\n\nChoose which edge to remove by considering:\n\n### 2.1. Semantic Essentiality\n\nKeep the reference that is core to the type's purpose.\n\n- A shopping cart SHOULD contain items \u2192 keep `cart.items`\n- An item does NOT need to reference its cart \u2192 remove `item.cart`\n- An order SHOULD contain order items \u2192 keep `order.orderItems`\n- An order item does NOT need the full order \u2192 remove `orderItem.order`\n\n### 2.2. Reference Direction\n\nPrefer removing back-references (child \u2192 parent) over forward-references (parent \u2192 child).\n\n- Parent \u2192 children (forward): usually essential for API responses\n- Child \u2192 parent (back): often redundant \u2014 the client already knows the parent context\n\n### 2.3. Multiplicity\n\nA 1-to-many (array) reference is often MORE important than a 1-to-1 reference, because it represents a collection that defines the parent entity.\n\n- `IOrder.items: IOrderItem[]` \u2014 essential, defines what the order contains\n- `IOrderItem.order: IOrder` \u2014 redundant back-reference\n\n### 2.4. DTO Purpose\n\nSummary DTOs (`ISummary`, `IBrief`, `IPreview`) should have fewer outgoing references. If one side of the cycle is a summary type, prefer removing its outgoing reference.\n\n### 2.5. API Operations\n\nExamine the provided operations to understand how each type surfaces in the API:\n\n- A type that appears as a **direct response body** has client-visible structure \u2014 its outgoing references tend to be essential.\n- A type referenced only as embedded data inside another response rarely needs a back-reference to its parent.\n- If the client already receives the parent from a dedicated endpoint, the child's back-reference is redundant.\n\n## 3. Rules\n\n- Remove **exactly one** property \u2014 one removal always suffices to break the cycle\n- The property MUST correspond to an edge in the detected cycle\n- Provide a clear `reason` explaining why that specific edge was chosen\n\n## 4. Description Consistency\n\nAfter deciding which property to remove, check the owning schema's `description` and `x-autobe-specification`. If either field mentions the removed property, provide corrected text directly on the removal object.\n\n- `description`: corrected text if the original mentions the removed property, otherwise `null`\n- `specification`: corrected text if the original mentions the removed property, otherwise `null`\n\n**Use `null` when no change is needed** \u2014 do not redundantly restate the original text.\n\n## 5. Function Calling\n\nFill fields in order \u2014 each builds on the previous.\n\n```typescript\nprocess({\n thinking: string, // Analyze the cycle: which edge, semantic direction, doc impact\n\n draft: {\n reason: string, // WHY first \u2014 rationale for choosing this edge\n typeName: string, // Schema owning the property to remove\n propertyName: string, // Property name to delete\n description: string | null, // Updated description for typeName schema, or null if unchanged\n specification: string | null, // Updated x-autobe-specification, or null if unchanged\n },\n\n review: string, // Critically re-examine: correct edge? doc updates right?\n\n final: { // Refined removal after review, or null if draft was already correct\n reason: string,\n typeName: string,\n propertyName: string,\n description: string | null,\n specification: string | null,\n } | null,\n})\n```\n\nThe **effective removal** applied is `final ?? draft`.\n\n## 6. Example\n\n```typescript\n// Cycle: IOrder \u2192 IOrderItem \u2192 IOrder\nprocess({\n thinking: \"IOrder.items is an array defining what the order contains \u2014 semantically essential. IOrderItem.order is a back-reference to the parent \u2014 redundant since the client already has the order context.\",\n draft: {\n reason: \"IOrderItem.order is a child\u2192parent back-reference; the client always knows the parent IOrder.\",\n typeName: \"IOrderItem\",\n propertyName: \"order\",\n description: null,\n specification: null,\n },\n review: \"Confirmed: removing IOrderItem.order breaks the cycle. IOrder.items (parent\u2192children array) is preserved. Neither description nor specification mentions the removed property.\",\n final: null,\n})\n```\n\n## 7. Checklist\n\n- [ ] `draft.typeName` is a source type in the detected cycle\n- [ ] `draft.propertyName` is an actual edge property in the detected cycle\n- [ ] `draft.reason` written before `typeName`/`propertyName` (commit to WHY first)\n- [ ] `draft.description`/`specification` are `null` if the original text does not reference the removed property\n- [ ] `review` evaluates the draft critically \u2014 is the edge semantically correct? are doc updates right?\n- [ ] `final` is `null` if draft required no change; otherwise provides the corrected removal" /* AutoBeSystemPromptConstant.INTERFACE_SCHEMA_DECOUPLE */,
},
{
type: "assistantMessage",
id: (0, uuid_1.v7)(),
created_at: new Date().toISOString(),
text: utils_1.StringUtil.trim `
## Detected Circular Reference Cycle
**Cycle**: ${props.cycle.types.join(" → ")} → ${props.cycle.types[0]}
**Edges (candidates for removal):**
${props.cycle.edges
.map((e) => `- \`${e.sourceType}.${e.propertyName}\` → \`${e.targetType}\``)
.join("\n")}
## Schemas Involved
\`\`\`json
${JSON.stringify(schemas)}
\`\`\`
## Operations Involved
\`\`\`json
${JSON.stringify(operations)}
\`\`\`
`,
},
],
userMessage: utils_1.StringUtil.trim `
Resolve this cross-type circular reference cycle by choosing
exactly one property reference to remove.
`,
};
};
exports.transformInterfaceSchemaDecoupleHistory = transformInterfaceSchemaDecoupleHistory;
//# sourceMappingURL=transformInterfaceSchemaDecoupleHistory.js.map