feat(data-recipes, recipe-studio): refactor and enhance recipe templates with updated model configurations, structure changes, and added validation logic
This commit is contained in:
parent
015e93491f
commit
59995d8447
5 changed files with 297 additions and 231 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,45 +68,51 @@ function findNonOverlappingPosition(
|
|||
return preferred;
|
||||
}
|
||||
|
||||
function isProviderToConfigEdge(edge: Edge, configs: Record<string, NodeConfig>): boolean {
|
||||
function isProviderToConfigEdge(
|
||||
edge: Edge,
|
||||
configs: Record<string, NodeConfig>,
|
||||
): 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<string, NodeConfig>): boolean {
|
||||
function isConfigToLlmEdge(
|
||||
edge: Edge,
|
||||
configs: Record<string, NodeConfig>,
|
||||
): 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<string, NodeConfig>): 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<string, number>, nodeId: string, handleId: string): void {
|
||||
function incrementUsage(
|
||||
map: Map<string, number>,
|
||||
nodeId: string,
|
||||
handleId: string,
|
||||
): void {
|
||||
const key = usageKey(nodeId, handleId);
|
||||
map.set(key, (map.get(key) ?? 0) + 1);
|
||||
}
|
||||
|
||||
function decrementUsage(map: Map<string, number>, nodeId: string, handleId: string): void {
|
||||
function decrementUsage(
|
||||
map: Map<string, number>,
|
||||
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<string, number>, nodeId: string, handleId: string): number {
|
||||
function getUsage(
|
||||
map: Map<string, number>,
|
||||
nodeId: string,
|
||||
handleId: string,
|
||||
): number {
|
||||
return map.get(usageKey(nodeId, handleId)) ?? 0;
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +121,9 @@ function pickHandleByUsage(
|
|||
nodeId: string,
|
||||
usageMap: Map<string, number>,
|
||||
): 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<string, RecipeNode>): Bounds | null {
|
||||
function collectBounds(
|
||||
ids: string[],
|
||||
nodesById: Map<string, RecipeNode>,
|
||||
): 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 => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue