diff --git a/studio/frontend/src/features/data-recipes/learning-recipes/pdf-grounded-qa.json b/studio/frontend/src/features/data-recipes/learning-recipes/pdf-grounded-qa.json index 13dfe69f96..bd999b9779 100644 --- a/studio/frontend/src/features/data-recipes/learning-recipes/pdf-grounded-qa.json +++ b/studio/frontend/src/features/data-recipes/learning-recipes/pdf-grounded-qa.json @@ -106,6 +106,12 @@ "note_color": "#F3E8FF", "note_opacity": "35" }, + { + "id": "seed", + "x": 484.36210245413577, + "y": 1059.99180558796, + "width": 400 + }, { "id": "provider_1", "x": 960, @@ -123,12 +129,6 @@ "x": 960, "y": 1077, "width": 400 - }, - { - "id": "seed_1", - "x": 478.48685020808557, - "y": 1060.0000000000002, - "width": 400 } ], "edges": [ @@ -148,7 +148,7 @@ }, { "from": "llm_structured_1", - "to": "seed_1", + "to": "seed", "type": "canvas", "source_handle": "data-out-left", "target_handle": "data-in-right" diff --git a/studio/frontend/src/features/data-recipes/learning-recipes/structured-outputs-jinja.json b/studio/frontend/src/features/data-recipes/learning-recipes/structured-outputs-jinja.json index f6f11bbc71..d7ccabaca3 100644 --- a/studio/frontend/src/features/data-recipes/learning-recipes/structured-outputs-jinja.json +++ b/studio/frontend/src/features/data-recipes/learning-recipes/structured-outputs-jinja.json @@ -3,7 +3,7 @@ "model_providers": [ { "name": "provider_column", - "endpoint": "https://openrouter.ai/api/v1", + "endpoint": "", "provider_type": "openai", "extra_headers": {}, "extra_body": {} @@ -13,11 +13,10 @@ "model_configs": [ { "alias": "ministral", - "model": "mistralai/ministral-8b-2512", + "model": "", "provider": "provider_column", "inference_parameters": { - "temperature": 0.7, - "max_tokens": 256 + "temperature": 0.7 } } ], @@ -76,6 +75,8 @@ "drop": false, "model_alias": "ministral", "prompt": "Create a realistic support ticket from {{ user_full_name }} using the {{ platform }} platform. Impact scope is {{ impact_scope }}.\n", + "with_trace": "none", + "extract_reasoning_content": false, "output_format": { "type": "object", "additionalProperties": false, @@ -129,6 +130,8 @@ "drop": false, "model_alias": "ministral", "prompt": "Write a concise support reply for ticket '{{ ticket.issue_title }}'. Category: {{ ticket.category }}. Priority: {{ ticket.priority }}. SLA target: {{ sla_target }}. {% if ticket.priority == 'P1' %}Tone must be urgent and action-first.{% else %}Tone must be calm and instructional.{% endif %}", + "with_trace": "none", + "extract_reasoning_content": false, "output_format": { "type": "object", "additionalProperties": false, @@ -168,8 +171,8 @@ "nodes": [ { "id": "note_1", - "x": 1084.767431711644, - "y": -293.4482850247655, + "x": 990.3973509933774, + "y": 1487.5768211920529, "width": 782, "node_type": "markdown_note", "name": "note_1", @@ -179,8 +182,8 @@ }, { "id": "note_2", - "x": 1944, - "y": 760.9999999999999, + "x": 3217.6543046357615, + "y": 2081.596026490066, "width": 400, "node_type": "markdown_note", "name": "note_2", @@ -190,8 +193,8 @@ }, { "id": "note_3", - "x": 2381.178207301403, - "y": 790.2196835690842, + "x": 2294.4516556291387, + "y": 2399.6099337748346, "width": 638, "node_type": "markdown_note", "name": "note_3", @@ -201,8 +204,8 @@ }, { "id": "note_4", - "x": 2405.796928768747, - "y": -362.26583299682716, + "x": 2544.684105960265, + "y": 1126.5490066225163, "width": 399, "node_type": "markdown_note", "name": "note_4", @@ -212,62 +215,62 @@ }, { "id": "provider_column", - "x": 1947.2039072039072, - "y": 32.08363858363858, + "x": 2542, + "y": 1696, "width": 400 }, { "id": "ministral", - "x": 1947.0573870573871, - "y": 271.94139194139194, + "x": 2542, + "y": 1890, "width": 400 }, { "id": "user", - "x": 0, - "y": 656.5, + "x": 191, + "y": 2423, "width": 400 }, { "id": "platform", - "x": 480, - "y": 656.5, + "x": 858.0384105960266, + "y": 2286.5, "width": 400 }, { "id": "impact_scope", - "x": 960, - "y": 656.5, + "x": 1342, + "y": 2286.5, "width": 400 }, { "id": "user_first_name", - "x": 1440, - "y": 895, + "x": 1822, + "y": 2505, "width": 400 }, { "id": "user_full_name", - "x": 1440, - "y": 269, + "x": 1822, + "y": 1959, "width": 400 }, { "id": "ticket", - "x": 1946.9108669108673, - "y": 657.2161172161173, + "x": 2302, + "y": 2286.5, "width": 400 }, { "id": "sla_target", - "x": 1440, - "y": 582, + "x": 1822, + "y": 2232, "width": 400 }, { "id": "agent_reply", - "x": 2384.5665445665445, - "y": 657.449938949939, + "x": 2782, + "y": 2151, "width": 400 } ], @@ -353,10 +356,10 @@ "from": "ministral", "to": "agent_reply", "type": "semantic", - "source_handle": "semantic-out", + "source_handle": "semantic-out-bottom", "target_handle": "data-in-top" } ], "layout_direction": "LR" } -} +} \ No newline at end of file diff --git a/studio/frontend/src/features/data-recipes/learning-recipes/text-to-python.json b/studio/frontend/src/features/data-recipes/learning-recipes/text-to-python.json index 1208e4a959..cd8118baa6 100644 --- a/studio/frontend/src/features/data-recipes/learning-recipes/text-to-python.json +++ b/studio/frontend/src/features/data-recipes/learning-recipes/text-to-python.json @@ -2,8 +2,8 @@ "recipe": { "model_providers": [ { - "name": "provider_1", - "endpoint": "https://openrouter.ai/api/v1", + "name": "openai-compatible", + "endpoint": "", "provider_type": "openai", "extra_headers": {}, "extra_body": {} @@ -12,12 +12,11 @@ "mcp_providers": [], "model_configs": [ { - "alias": "model_1", - "model": "mistralai/ministral-8b-2512", - "provider": "provider_1", + "alias": "coding-model", + "model": "", + "provider": "openai-compatible", "inference_parameters": { - "temperature": 0.7, - "max_tokens": 2048 + "temperature": 0.7 } } ], @@ -66,24 +65,29 @@ "column_type": "llm-text", "name": "instruction", "drop": false, - "model_alias": "model_1", + "model_alias": "coding-model", "prompt": "Write one clear Python coding instruction.\nDomain: {{ domain }}\nTask type: {{ task_type }}\n\nKeep it practical and specific.\nReturn only the instruction without any code.", - "with_trace": "none" + "with_trace": "none", + "extract_reasoning_content": false }, { "column_type": "llm-code", "name": "code_implementation", "drop": false, - "model_alias": "model_1", + "model_alias": "coding-model", "prompt": "Write Python code for:\n{{ instruction }}\n\nRequirements:\n- runnable script or function\n- include needed imports\n- short comments only where useful\n- no markdown fences", + "with_trace": "none", + "extract_reasoning_content": false, "code_lang": "python" }, { "column_type": "llm-judge", "name": "code_judge_result", "drop": false, - "model_alias": "model_1", + "model_alias": "coding-model", "prompt": "Evaluate generated Python code against the instruction.\n\nInstruction:\n{{ instruction }}\n\nCode:\n{{ code_implementation }}", + "with_trace": "none", + "extract_reasoning_content": false, "scores": [ { "name": "Correctness", @@ -109,52 +113,10 @@ }, "ui": { "nodes": [ - { - "id": "provider_1", - "x": 1032.6798211423347, - "y": -450.4885376732656, - "width": 400 - }, - { - "id": "model_1", - "x": 1538.0273166472973, - "y": -483.2003290046642, - "width": 400 - }, - { - "id": "domain", - "x": 0, - "y": 24, - "width": 400 - }, - { - "id": "task_type", - "x": 480, - "y": 24, - "width": 400 - }, - { - "id": "instruction", - "x": 958.8989453654599, - "y": -9.971266983459952, - "width": 400 - }, - { - "id": "code_implementation", - "x": 1538.788058529745, - "y": -45.56493974435071, - "width": 400 - }, - { - "id": "code_judge_result", - "x": 2040.9251520522098, - "y": -13.362336454344792, - "width": 400 - }, { "id": "note_1", - "x": 1482.1328175027095, - "y": 242.4370179053253, + "x": 1526, + "y": 1790.75, "width": 568, "node_type": "markdown_note", "name": "note_1", @@ -164,14 +126,56 @@ }, { "id": "note_2", - "x": 2513.2527820497985, - "y": -235.2544980991115, + "x": 2597.376821192053, + "y": 1233.2039735099338, "width": 471, "node_type": "markdown_note", "name": "note_2", "markdown": "The **LLM Judge** block evaluates generated outputs with rubric-style scores.\n\n##### Important:\n\n- A judge can have **one or many scores**\n- Each score has:\n - a name (for example: `Correctness`)\n - a description\n - options (value + meaning)\n\n##### Example multi-score setup:\n\n- Correctness\n- Readability\n- Efficiency\n\n##### Why use multiple scores:\n\n- You get richer quality signals than a single pass/fail\n- Easier filtering and weighting later in training data prep\n\n##### Practical pattern:\n\n1. Generate code with LLM Code\n2. Judge with 2-4 focused scores\n3. Keep high-quality rows based on score thresholds\n", "note_color": "#FEF3C7", "note_opacity": "35" + }, + { + "id": "openai-compatible", + "x": 1627.1046357615896, + "y": 921.0301324503313, + "width": 400 + }, + { + "id": "coding-model", + "x": 1627.1046357615894, + "y": 1138.910927152318, + "width": 400 + }, + { + "id": "domain", + "x": 84, + "y": 1600.5, + "width": 400 + }, + { + "id": "task_type", + "x": 648, + "y": 1600.5, + "width": 400 + }, + { + "id": "instruction", + "x": 1128, + "y": 1567, + "width": 400 + }, + { + "id": "code_implementation", + "x": 1627.1046357615894, + "y": 1531.4728476821192, + "width": 400 + }, + { + "id": "code_judge_result", + "x": 2124.617218543046, + "y": 1567.076490066225, + "width": 400 } ], "edges": [ @@ -190,11 +194,11 @@ "target_handle": "data-in" }, { - "from": "provider_1", - "to": "model_1", + "from": "openai-compatible", + "to": "coding-model", "type": "semantic", - "source_handle": "semantic-out", - "target_handle": "semantic-in" + "source_handle": "semantic-out-bottom", + "target_handle": "semantic-in-top" }, { "from": "instruction", @@ -204,14 +208,14 @@ "target_handle": "data-in" }, { - "from": "model_1", + "from": "coding-model", "to": "instruction", "type": "semantic", "source_handle": "semantic-out-bottom", "target_handle": "data-in-top" }, { - "from": "model_1", + "from": "coding-model", "to": "code_implementation", "type": "semantic", "source_handle": "semantic-out-bottom", @@ -225,7 +229,7 @@ "target_handle": "data-in" }, { - "from": "model_1", + "from": "coding-model", "to": "code_judge_result", "type": "semantic", "source_handle": "semantic-out-bottom", @@ -234,4 +238,4 @@ ], "layout_direction": "LR" } -} +} \ No newline at end of file diff --git a/studio/frontend/src/features/data-recipes/learning-recipes/text-to-sql.json b/studio/frontend/src/features/data-recipes/learning-recipes/text-to-sql.json index 60957bcf4a..d0776d3ead 100644 --- a/studio/frontend/src/features/data-recipes/learning-recipes/text-to-sql.json +++ b/studio/frontend/src/features/data-recipes/learning-recipes/text-to-sql.json @@ -2,8 +2,8 @@ "recipe": { "model_providers": [ { - "name": "provider_1", - "endpoint": "https://openrouter.ai/api/v1", + "name": "vllm", + "endpoint": "", "provider_type": "openai", "extra_headers": {}, "extra_body": {} @@ -12,12 +12,11 @@ "mcp_providers": [], "model_configs": [ { - "alias": "model_1", - "model": "mistralai/ministral-8b-2512", - "provider": "provider_1", + "alias": "sql-pro", + "model": "", + "provider": "vllm", "inference_parameters": { - "temperature": 0.7, - "max_tokens": 2048 + "temperature": 0.7 } } ], @@ -93,19 +92,35 @@ "column_type": "llm-text", "name": "sql_prompt", "drop": false, - "model_alias": "model_1", + "model_alias": "sql-pro", "prompt": "Generate one natural-language SQL task.\nContext:\n- Domain: {{ domain }}\n- Topic: {{ topic }}\n- Task type: {{ sql_task_type }}\nRules:\n- Must start exactly with: \"{{ instruction_phrase }}\"\n- Make it specific and practical.\n- Mention expected business outcome.\n- Keep it 1-2 sentences.\n- Do not include SQL code.\n- Output only the instruction text.", "system_prompt": "You create clear, realistic business SQL tasks for training data.\n", - "with_trace": "none" + "with_trace": "none", + "extract_reasoning_content": false }, { "column_type": "llm-code", "name": "sql", "drop": false, - "model_alias": "model_1", + "model_alias": "sql-pro", "prompt": "Write SQL for this instruction:\n{{ sql_prompt }}\nReturn ONE SQL script with this exact structure:\n-- SCHEMA\n[CREATE TABLE statements]\n[INSERT statements with sample rows]\n-- QUERY\n[final SELECT query solving the instruction]\nRules:\n- Use 2-3 tables max.\n- Use realistic snake_case names.\n- Include 5-8 rows of sample data per table.\n- Query must match task type \"{{ sql_task_type }}\".\n- Use only tables/columns you created.\n- No markdown fences.\n- No explanation text outside SQL comments shown above.", "system_prompt": "You are an expert SQL engineer. Produce correct, runnable SQL only.\n", + "with_trace": "none", + "extract_reasoning_content": false, "code_lang": "sql:ansi" + }, + { + "column_type": "validation", + "name": "sql-validator", + "drop": false, + "target_columns": [ + "sql" + ], + "validator_type": "code", + "validator_params": { + "code_lang": "sql:ansi" + }, + "batch_size": 10 } ], "processors": [] @@ -119,58 +134,10 @@ }, "ui": { "nodes": [ - { - "id": "provider_1", - "x": -1092.2003193114556, - "y": 715.157165665104, - "width": 400 - }, - { - "id": "model_1", - "x": -546.1001596557278, - "y": 681.8114012018752, - "width": 400 - }, - { - "id": "domain", - "x": -18.379173679952572, - "y": 137.70260329000595, - "width": 400 - }, - { - "id": "topic", - "x": -18.6022437080035, - "y": 373.0271253737222, - "width": 400 - }, - { - "id": "sql_task_type", - "x": 477.85851567876665, - "y": 137.4202046707293, - "width": 400 - }, - { - "id": "instruction_phrase", - "x": -477.8585156787667, - "y": 138.42770371608808, - "width": 400 - }, - { - "id": "sql_prompt", - "x": -18.188598798124787, - "y": 701.5157165665104, - "width": 400 - }, - { - "id": "sql", - "x": -18.188598798124758, - "y": 950.647309355259, - "width": 400 - }, { "id": "note_1", - "x": -103.00586025666547, - "y": -332.088439142397, + "x": 338, + "y": 1020, "width": 600, "node_type": "markdown_note", "name": "note_1", @@ -180,8 +147,8 @@ }, { "id": "note_2", - "x": 517.0372102151987, - "y": 600.4949327304814, + "x": 1672, + "y": 1577, "width": 400, "node_type": "markdown_note", "name": "note_2", @@ -191,25 +158,79 @@ }, { "id": "note_3", - "x": 12.635681904967385, - "y": 1224.7626182706356, + "x": 2126, + "y": 1485, "width": 400, "node_type": "markdown_note", "name": "note_3", - "markdown": "The **LLM Code** block (`sql`) generates SQL script from `{{ sql_prompt }}`.\n\n##### In this recipe it returns:\n\n- schema section (`CREATE TABLE`)\n- sample seed rows (`INSERT`)\n- final query (`SELECT`)\n\n##### Current status:\n\n- SQL validation block is **not** included yet in this learning recipe\n- We will add SQL validation later", + "markdown": "The **LLM Code** block (`sql`) generates SQL script from `{{ sql_prompt }}`.\n\n##### In this recipe it returns:\n\n- schema section (`CREATE TABLE`)\n- sample seed rows (`INSERT`)\n- final query (`SELECT`)\n", "note_color": "#DBEAFE", "note_opacity": "35" }, { "id": "note_4", - "x": -1044, - "y": 108.64730935525904, + "x": 1264, + "y": 1037, "width": 400, "node_type": "markdown_note", "name": "note_4", "markdown": "Sampler columns are useful during generation, but often noisy in final output.\n\nSet helper columns to **drop=true** (like in this recipe), keep only output columns you want to export.\n\n#### Final keep we have set here:\n\n- `sql_prompt`\n- `sql`\n\n", "note_color": "#DBEAFE", "note_opacity": "35" + }, + { + "id": "vllm", + "x": 1880, + "y": 781.25, + "width": 400 + }, + { + "id": "sql-pro", + "x": 1880, + "y": 975.25, + "width": 400 + }, + { + "id": "domain", + "x": 680, + "y": 1495, + "width": 400 + }, + { + "id": "topic", + "x": 1160, + "y": 1413, + "width": 400 + }, + { + "id": "sql_task_type", + "x": 1160, + "y": 1577, + "width": 400 + }, + { + "id": "instruction_phrase", + "x": 100, + "y": 1495, + "width": 400 + }, + { + "id": "sql_prompt", + "x": 1666.887417218543, + "y": 1378.9437086092717, + "width": 400 + }, + { + "id": "sql", + "x": 2120, + "y": 1236.25, + "width": 400 + }, + { + "id": "sql-validator", + "x": 2600, + "y": 1304.75, + "width": 400 } ], "edges": [ @@ -217,8 +238,8 @@ "from": "domain", "to": "topic", "type": "canvas", - "source_handle": "data-out-bottom", - "target_handle": "data-in-top" + "source_handle": "data-out", + "target_handle": "data-in" }, { "from": "domain", @@ -238,38 +259,52 @@ "from": "topic", "to": "sql_prompt", "type": "canvas", - "source_handle": "data-out-bottom", - "target_handle": "data-in-top" + "source_handle": "data-out", + "target_handle": "data-in" }, { "from": "sql_prompt", "to": "sql", "type": "canvas", - "source_handle": "data-out-bottom", - "target_handle": "data-in-top" - }, - { - "from": "provider_1", - "to": "model_1", - "type": "semantic", - "source_handle": "semantic-out", - "target_handle": "semantic-in" - }, - { - "from": "model_1", - "to": "sql_prompt", - "type": "semantic", - "source_handle": "semantic-out", + "source_handle": "data-out", "target_handle": "data-in" }, { - "from": "model_1", + "from": "vllm", + "to": "sql-pro", + "type": "semantic", + "source_handle": "semantic-out-bottom", + "target_handle": "semantic-in-top" + }, + { + "from": "sql-pro", "to": "sql", "type": "semantic", "source_handle": "semantic-out-bottom", + "target_handle": "data-in-top" + }, + { + "from": "sql", + "to": "sql-validator", + "type": "semantic", + "source_handle": "data-out", "target_handle": "data-in" + }, + { + "from": "sql-pro", + "to": "sql_prompt", + "type": "semantic", + "source_handle": "semantic-out-bottom", + "target_handle": "data-in-top" + }, + { + "from": "sql_prompt", + "to": "sql_task_type", + "type": "canvas", + "source_handle": "data-out-left", + "target_handle": "data-in-right" } ], "layout_direction": "LR" } -} +} \ No newline at end of file diff --git a/studio/frontend/src/features/recipe-studio/stores/helpers/model-infra-layout.ts b/studio/frontend/src/features/recipe-studio/stores/helpers/model-infra-layout.ts index ac30da9278..ce09c46513 100644 --- a/studio/frontend/src/features/recipe-studio/stores/helpers/model-infra-layout.ts +++ b/studio/frontend/src/features/recipe-studio/stores/helpers/model-infra-layout.ts @@ -68,45 +68,51 @@ function findNonOverlappingPosition( return preferred; } -function isProviderToConfigEdge(edge: Edge, configs: Record): boolean { +function isProviderToConfigEdge( + edge: Edge, + configs: Record, +): boolean { const source = configs[edge.source]; const target = configs[edge.target]; return source?.kind === "model_provider" && target?.kind === "model_config"; } -function isConfigToLlmEdge(edge: Edge, configs: Record): boolean { +function isConfigToLlmEdge( + edge: Edge, + configs: Record, +): boolean { const source = configs[edge.source]; const target = configs[edge.target]; return source?.kind === "model_config" && target?.kind === "llm"; } -function isDataToLlmEdge(edge: Edge, configs: Record): boolean { - const source = configs[edge.source]; - const target = configs[edge.target]; - return Boolean( - source && - target && - source.kind !== "model_config" && - target.kind === "llm" && - edge.type !== "semantic", - ); -} - function usageKey(nodeId: string, handleId: string): string { return `${nodeId}::${handleId}`; } -function incrementUsage(map: Map, nodeId: string, handleId: string): void { +function incrementUsage( + map: Map, + nodeId: string, + handleId: string, +): void { const key = usageKey(nodeId, handleId); map.set(key, (map.get(key) ?? 0) + 1); } -function decrementUsage(map: Map, nodeId: string, handleId: string): void { +function decrementUsage( + map: Map, + nodeId: string, + handleId: string, +): void { const key = usageKey(nodeId, handleId); map.set(key, Math.max(0, (map.get(key) ?? 0) - 1)); } -function getUsage(map: Map, nodeId: string, handleId: string): number { +function getUsage( + map: Map, + nodeId: string, + handleId: string, +): number { return map.get(usageKey(nodeId, handleId)) ?? 0; } @@ -115,7 +121,9 @@ function pickHandleByUsage( nodeId: string, usageMap: Map, ): string { - const free = candidates.filter((handleId) => getUsage(usageMap, nodeId, handleId) === 0); + const free = candidates.filter( + (handleId) => getUsage(usageMap, nodeId, handleId) === 0, + ); if (free.length > 0) { return free[0]; } @@ -152,7 +160,10 @@ function getNodeCenter(node: RecipeNode): { x: number; y: number } { }; } -function collectBounds(ids: string[], nodesById: Map): Bounds | null { +function collectBounds( + ids: string[], + nodesById: Map, +): Bounds | null { const rects = ids .map((id) => nodesById.get(id)) .flatMap((node) => (node ? [toRect(node)] : [])); @@ -198,20 +209,26 @@ function sortPreferredLlmTargetHandles( return [...verticalFirst, HANDLE_IDS.dataIn, HANDLE_IDS.dataInRight]; } -function getProviderSourceHandleCandidates(direction: LayoutDirection): string[] { +function getProviderSourceHandleCandidates( + direction: LayoutDirection, +): string[] { return direction === "TB" ? [HANDLE_IDS.semanticOut, HANDLE_IDS.semanticOutBottom] : [HANDLE_IDS.semanticOutBottom, HANDLE_IDS.semanticOut]; } -function getProviderTargetHandleCandidates(direction: LayoutDirection): string[] { +function getProviderTargetHandleCandidates( + direction: LayoutDirection, +): string[] { return direction === "TB" ? [HANDLE_IDS.semanticIn, HANDLE_IDS.semanticInTop] : [HANDLE_IDS.semanticInTop, HANDLE_IDS.semanticIn]; } function getConfigSourceHandleCandidates(direction: LayoutDirection): string[] { - return direction === "TB" ? [HANDLE_IDS.semanticOut] : [HANDLE_IDS.semanticOutBottom]; + return direction === "TB" + ? [HANDLE_IDS.semanticOut] + : [HANDLE_IDS.semanticOutBottom]; } export function optimizeModelInfraEdgeHandles( @@ -248,28 +265,31 @@ export function optimizeModelInfraEdgeHandles( const targetHandleBefore = normalizeRecipeHandleId(edge.targetHandle); const isModelSemantic = isProviderToConfigEdge(edge, configs) || isConfigToLlmEdge(edge, configs); - const isLlmDataTarget = isDataToLlmEdge(edge, configs); - if (!isModelSemantic && !isLlmDataTarget) { + if (!isModelSemantic) { nextEdges.push(edge); continue; } - if (isModelSemantic) { - if (sourceHandleBefore) { - decrementUsage(sourceUsage, edge.source, sourceHandleBefore); - } - if (targetHandleBefore) { - decrementUsage(targetUsage, edge.target, targetHandleBefore); - } - } else if (targetHandleBefore) { + if (sourceHandleBefore) { + decrementUsage(sourceUsage, edge.source, sourceHandleBefore); + } + if (targetHandleBefore) { decrementUsage(targetUsage, edge.target, targetHandleBefore); } if (isProviderToConfigEdge(edge, configs)) { const sourceCandidates = getProviderSourceHandleCandidates(direction); const targetCandidates = getProviderTargetHandleCandidates(direction); - const sourceHandle = pickHandleByUsage(sourceCandidates, edge.source, sourceUsage); - const targetHandle = pickHandleByUsage(targetCandidates, edge.target, targetUsage); + const sourceHandle = pickHandleByUsage( + sourceCandidates, + edge.source, + sourceUsage, + ); + const targetHandle = pickHandleByUsage( + targetCandidates, + edge.target, + targetUsage, + ); nextEdges.push( applyEdgeWithHandles( edge, @@ -287,19 +307,17 @@ export function optimizeModelInfraEdgeHandles( nodesById.get(edge.source), nodesById.get(edge.target), ); - if (isLlmDataTarget) { - const targetHandle = pickHandleByUsage(targetCandidates, edge.target, targetUsage); - incrementUsage(targetUsage, edge.target, targetHandle); - nextEdges.push({ - ...edge, - targetHandle, - }); - continue; - } - const sourceCandidates = getConfigSourceHandleCandidates(direction); - const sourceHandle = pickHandleByUsage(sourceCandidates, edge.source, sourceUsage); - const targetHandle = pickHandleByUsage(targetCandidates, edge.target, targetUsage); + const sourceHandle = pickHandleByUsage( + sourceCandidates, + edge.source, + sourceUsage, + ); + const targetHandle = pickHandleByUsage( + targetCandidates, + edge.target, + targetUsage, + ); nextEdges.push( applyEdgeWithHandles( edge, @@ -343,13 +361,19 @@ export function centerModelInfraNodes( } const modelConfigIds = Object.values(configs) - .filter((config) => config.kind === "model_config" && nodesById.has(config.id)) + .filter( + (config) => config.kind === "model_config" && nodesById.has(config.id), + ) .map((config) => config.id); const modelProviderIds = Object.values(configs) - .filter((config) => config.kind === "model_provider" && nodesById.has(config.id)) + .filter( + (config) => config.kind === "model_provider" && nodesById.has(config.id), + ) .map((config) => config.id); - const occupiedById = new Map(nodes.map((node) => [node.id, toRect(node)] as const)); + const occupiedById = new Map( + nodes.map((node) => [node.id, toRect(node)] as const), + ); const clusterGap = 72; const placeNode = (nodeId: string, preferred: XYPosition): void => {