UNPKG

@autobe/agent

Version:

AI backend server code generator

209 lines 9.03 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AutoBeInterfaceSchemaDecoupleProgrammer = void 0; const utils_1 = require("@autobe/utils"); const utils_2 = require("@typia/utils"); const typia_1 = __importDefault(require("typia")); var AutoBeInterfaceSchemaDecoupleProgrammer; (function (AutoBeInterfaceSchemaDecoupleProgrammer) { /** * Detect cross-type circular references in the schema graph. * * Builds a directed graph of `$ref` relationships between types, then finds * strongly connected components (SCCs) using Tarjan's algorithm. * Self-references (A → A) are excluded — they represent legitimate tree * structures. */ AutoBeInterfaceSchemaDecoupleProgrammer.detectCycles = (schemas) => { const graph = buildGraph(schemas); const sccs = findSCCs(graph); return sccs.map((scc) => { var _a; const sccSet = new Set(scc); const edges = []; for (const type of scc) for (const edge of (_a = graph.get(type)) !== null && _a !== void 0 ? _a : []) if (sccSet.has(edge.targetType)) edges.push(edge); return { types: scc, edges }; }); }; /** * Fix LLM application schema by injecting valid edge pairs into the removal * object's description. * * Listing valid `typeName.propertyName` pairs on the removal object guides * the LLM to choose a correct cycle edge without the independent- enum * problem (where `typeName` and `propertyName` enums are checked separately, * allowing invalid cross-combinations). */ AutoBeInterfaceSchemaDecoupleProgrammer.fixApplication = (props) => { var _a; const $defs = (_a = props.application.functions[0]) === null || _a === void 0 ? void 0 : _a.parameters.$defs; if ($defs === undefined) return; const removal = $defs["AutoBeInterfaceSchemaDecoupleRemoval"]; if (removal === undefined || utils_2.LlmTypeChecker.isObject(removal) === false) return; const pairs = props.cycle.edges .map((e) => `${e.sourceType}.${e.propertyName}`) .join(", "); removal.description = `Valid edges for this cycle (typeName.propertyName): ${pairs}`; }; /** * Execute one property removal and apply inline documentation updates. * * Step 1: Delete the named property from its schema. Step 2: Apply * description/specification updates from the removal if provided (non-null * fields on the removal object itself). */ AutoBeInterfaceSchemaDecoupleProgrammer.execute = (props) => { const schema = props.schemas[props.removal.typeName]; if (schema === undefined || utils_1.AutoBeOpenApiTypeChecker.isObject(schema) === false) return; delete schema.properties[props.removal.propertyName]; if (schema.required) schema.required = schema.required.filter((r) => r !== props.removal.propertyName); if (props.removal.description !== null) schema.description = props.removal.description; if (props.removal.specification !== null) schema["x-autobe-specification"] = props.removal.specification; }; /** * Validate that the LLM's removal decision is correct. * * Checks: * * 1. The removal references a valid typeName + propertyName * 2. The removal corresponds to an actual edge in this cycle */ AutoBeInterfaceSchemaDecoupleProgrammer.validate = (props) => { const { removal } = props; const schema = props.schemas[removal.typeName]; if (!schema) { props.errors.push({ path: `${props.path}.removal.typeName`, expected: `one of the existing schema type names`, value: removal.typeName, }); return; } if (!utils_1.AutoBeOpenApiTypeChecker.isObject(schema)) { props.errors.push({ path: `${props.path}.removal.typeName`, expected: "an object schema type name", value: removal.typeName, }); return; } if (!(removal.propertyName in schema.properties)) { const validProps = Object.keys(schema.properties).join(", "); props.errors.push({ path: `${props.path}.removal.propertyName`, expected: `one of [${validProps}]`, value: removal.propertyName, }); return; } // Check the removal corresponds to an actual edge in this cycle const isEdge = props.cycle.edges.some((edge) => edge.sourceType === removal.typeName && edge.propertyName === removal.propertyName); if (!isEdge) props.errors.push({ path: `${props.path}.removal`, expected: "a removal that matches a cycle edge (sourceType.propertyName)", value: `${removal.typeName}.${removal.propertyName}`, }); }; // --------------------------------------------------------------- // INTERNAL: Graph Construction // --------------------------------------------------------------- const buildGraph = (schemas) => { const graph = new Map(); for (const [typeName, schema] of Object.entries(schemas)) { if (!utils_1.AutoBeOpenApiTypeChecker.isObject(schema)) continue; const edges = []; for (const [propName, propSchema] of Object.entries(schema.properties)) collectRefs(propSchema, typeName, propName, edges); // collectRefs already excludes self-references if (edges.length > 0) graph.set(typeName, edges); } return graph; }; /** * Recursively collect $ref targets from a property schema. Handles direct * references, arrays of references, and nullable references. */ const collectRefs = (schema, sourceType, propertyName, edges) => { if (utils_1.AutoBeOpenApiTypeChecker.isReference(schema)) { const targetType = schema.$ref.split("/").pop(); if (targetType !== sourceType) edges.push({ sourceType, propertyName, targetType }); } else if (utils_1.AutoBeOpenApiTypeChecker.isArray(schema)) { collectRefs(schema.items, sourceType, propertyName, edges); } else if (utils_1.AutoBeOpenApiTypeChecker.isOneOf(schema)) { for (const sub of schema.oneOf) collectRefs(sub, sourceType, propertyName, edges); } }; // --------------------------------------------------------------- // INTERNAL: Tarjan's SCC Algorithm // --------------------------------------------------------------- const findSCCs = (graph) => { // Collect all nodes reachable in the graph const allNodes = new Set(); for (const [node, edges] of graph) { allNodes.add(node); for (const edge of edges) allNodes.add(edge.targetType); } let index = 0; const stack = []; const onStack = new Set(); const indices = new Map(); const lowlinks = new Map(); const sccs = []; const strongconnect = (v) => { var _a; indices.set(v, index); lowlinks.set(v, index); index++; stack.push(v); onStack.add(v); for (const edge of (_a = graph.get(v)) !== null && _a !== void 0 ? _a : []) { const w = edge.targetType; if (!indices.has(w)) { strongconnect(w); lowlinks.set(v, Math.min(lowlinks.get(v), lowlinks.get(w))); } else if (onStack.has(w)) { lowlinks.set(v, Math.min(lowlinks.get(v), indices.get(w))); } } if (lowlinks.get(v) === indices.get(v)) { const scc = []; let w; do { w = stack.pop(); onStack.delete(w); scc.push(w); } while (w !== v); sccs.push(scc); } }; for (const node of allNodes) if (!indices.has(node)) strongconnect(node); // Only return SCCs with 2+ nodes (actual cross-type cycles) return sccs.filter((scc) => scc.length > 1); }; })(AutoBeInterfaceSchemaDecoupleProgrammer || (exports.AutoBeInterfaceSchemaDecoupleProgrammer = AutoBeInterfaceSchemaDecoupleProgrammer = {})); //# sourceMappingURL=AutoBeInterfaceSchemaDecoupleProgrammer.js.map