adaptive-memory-multi-model-router 2.11.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/.github/workflows/ci.yml +56 -0
  2. package/.github/workflows/pages.yml +37 -0
  3. package/Awesome-LLM/LICENSE.md +121 -0
  4. package/Awesome-LLM/README.md +625 -0
  5. package/Awesome-LLM/contributing.md +24 -0
  6. package/Awesome-LLM/paper_list/RLHF.md +1 -0
  7. package/Awesome-LLM/paper_list/Retrieval_Augmented_Generation.md +6 -0
  8. package/Awesome-LLM/paper_list/acceleration.md +10 -0
  9. package/Awesome-LLM/paper_list/alignment.md +20 -0
  10. package/Awesome-LLM/paper_list/application.md +19 -0
  11. package/Awesome-LLM/paper_list/augmentation.md +14 -0
  12. package/Awesome-LLM/paper_list/chain_of_thougt.md +16 -0
  13. package/Awesome-LLM/paper_list/code_pretraining.md +0 -0
  14. package/Awesome-LLM/paper_list/detection.md +11 -0
  15. package/Awesome-LLM/paper_list/evaluation.md +64 -0
  16. package/Awesome-LLM/paper_list/in_context_learning.md +4 -0
  17. package/Awesome-LLM/paper_list/instruction-tuning.md +35 -0
  18. package/Awesome-LLM/paper_list/moe.md +0 -0
  19. package/Awesome-LLM/paper_list/prompt_learning.md +15 -0
  20. package/Awesome-LLM/resources/DeepSpeed_light.svg +27 -0
  21. package/Awesome-LLM/resources/alpa-logo-cropped.png +0 -0
  22. package/Awesome-LLM/resources/colossal-ai_logo_vertical.png +0 -0
  23. package/Awesome-LLM/resources/creepy_llm.jpeg +0 -0
  24. package/Awesome-LLM/resources/image8.gif +0 -0
  25. package/Awesome-LLM/resources/jax_logo_250px.png +0 -0
  26. package/Awesome-LLM/resources/logo.png +0 -0
  27. package/Awesome-LLM/resources/nvidia.png +0 -0
  28. package/Awesome-LLM/resources/tensorflow.png +0 -0
  29. package/Awesome-LLM/resources//345/244/247/345/236/213/350/257/255/350/250/200/346/250/241/345/236/213/347/232/204/350/203/275/345/212/233/345/210/206/346/236/220/344/270/216/345/272/224/347/224/250 - 30min.pdf +0 -0
  30. package/LANDING.md +46 -0
  31. package/MANIFESTO.md +54 -0
  32. package/README.md +3 -3
  33. package/SUBMISSIONS.md +43 -0
  34. package/assets/chart-cost-v2.svg +91 -0
  35. package/assets/chart-cost-v3.svg +143 -0
  36. package/assets/chart-features-v2.svg +132 -0
  37. package/assets/chart-features-v3.svg +211 -0
  38. package/assets/chart-growth-v2.svg +122 -0
  39. package/assets/chart-growth-v3.svg +189 -0
  40. package/assets/cost-simple.svg +64 -0
  41. package/assets/growth-simple.svg +69 -0
  42. package/assets/hero-diagram.svg +81 -0
  43. package/assets/logo-new.svg +21 -0
  44. package/assets/social-preview-new.svg +100 -0
  45. package/assets/social-v2.svg +130 -0
  46. package/assets/social-v3.svg +212 -0
  47. package/awesome-ai-gateways/CONTRIBUTING.md +49 -0
  48. package/awesome-ai-gateways/LICENSE +3 -0
  49. package/awesome-ai-gateways/README.md +190 -0
  50. package/awesome-selfhosted/.github/ISSUE_TEMPLATE/config.yml +5 -0
  51. package/awesome-selfhosted/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  52. package/awesome-selfhosted/LICENSE +376 -0
  53. package/awesome-selfhosted/README.md +2303 -0
  54. package/awesome-selfhosted/_static/awesome.png +0 -0
  55. package/awesome-selfhosted/non-free.md +377 -0
  56. package/demo-new.tape +71 -0
  57. package/demo-real.sh +198 -0
  58. package/demo-simple.tape +205 -0
  59. package/demo.html +520 -0
  60. package/demo.sh +85 -0
  61. package/demo.tape +259 -0
  62. package/dist/cli/setupWizard.js +194 -0
  63. package/dist/cli/tui.js +152 -0
  64. package/dist/cli.js +9 -2
  65. package/dist/routing/providerRetry.d.ts +5 -0
  66. package/dist/routing/providerRetry.js +37 -0
  67. package/dist/routing/providerRetry.js.map +1 -1
  68. package/docs/CHINESE_PROVIDER_RELIABILITY.md +37 -0
  69. package/docs/CLAIMS_AND_EVIDENCE.md +58 -0
  70. package/docs/ENGINEERING_SPEC.md +55 -0
  71. package/docs/RELEASE_CHECKLIST.md +32 -0
  72. package/docs/REPRODUCIBILITY.md +63 -0
  73. package/docs/demo-auto.html +264 -0
  74. package/docs/demo.html +416 -0
  75. package/docs/index.html +112 -599
  76. package/eval/README.md +46 -0
  77. package/eval/baselines/main.json +12 -0
  78. package/eval/benchmark_dataset.jsonl +16 -0
  79. package/eval/check_golden_routes.js +64 -0
  80. package/eval/datasets/catalog.json +33 -0
  81. package/eval/datasets/slices/cn_provider_reliability_v1.jsonl +3 -0
  82. package/eval/datasets/slices/cost_pressure_v1.jsonl +3 -0
  83. package/eval/datasets/slices/safety_guardrails_v1.jsonl +3 -0
  84. package/eval/fault_injection_thresholds.json +3 -0
  85. package/eval/generate_report.js +128 -0
  86. package/eval/golden_routes.json +114 -0
  87. package/eval/lib/experiment_registry.js +24 -0
  88. package/eval/run_eval.js +197 -0
  89. package/eval/run_fault_injection.js +201 -0
  90. package/eval/run_shadow_eval.js +85 -0
  91. package/eval/thresholds.json +9 -0
  92. package/index.html +667 -0
  93. package/package.json +29 -168
  94. package/pytest.ini +2 -0
  95. package/src/cli/setupWizard.ts +194 -0
  96. package/src/routing/providerRetry.ts +41 -1
  97. package/docs/assets/cost-comparison.svg +0 -134
  98. package/docs/assets/growth-chart-animated.svg +0 -76
  99. package/docs/assets/og-banner.svg +0 -194
  100. package/docs/assets/social-preview.svg +0 -194
  101. package/python/a3m/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/python/a3m/__pycache__/client.cpython-312.pyc +0 -0
  103. package/python/a3m/__pycache__/models.cpython-312.pyc +0 -0
  104. package/python/a3m/__pycache__/sync_client.cpython-312.pyc +0 -0
package/eval/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Routing Evaluation Harness
2
+
3
+ This directory contains the reproducible evaluation system for A3M.
4
+
5
+ ## Files
6
+
7
+ - `benchmark_dataset.jsonl`: frozen routing benchmark dataset
8
+ - `datasets/catalog.json`: dataset registry and slice metadata
9
+ - `datasets/slices/*.jsonl`: versioned slice datasets
10
+ - `thresholds.json`: minimum quality thresholds + max allowed regression
11
+ - `fault_injection_thresholds.json`: reliability gate threshold
12
+ - `golden_routes.json`: golden route snapshot
13
+ - `baselines/main.json`: baseline summary for `main` branch
14
+ - `run_eval.js`: routing evaluator + hard gate
15
+ - `check_golden_routes.js`: golden regression check
16
+ - `run_fault_injection.js`: retry/health fault scenarios
17
+ - `run_shadow_eval.js`: shadow routing comparison (informational)
18
+ - `generate_report.js`: markdown summary from eval result artifacts
19
+ - `experiments.jsonl`: append-only experiment registry (local artifact)
20
+ - `results/*.json`: generated run outputs (not committed)
21
+ - `results/report_latest.md`: generated markdown evidence report
22
+
23
+ ## Run
24
+
25
+ ```bash
26
+ npm run eval:all
27
+ ```
28
+
29
+ `eval:all` includes:
30
+
31
+ 1. `eval:routing` — hard gate for routing quality thresholds
32
+ 2. `eval:golden` — snapshot consistency gate
33
+ 3. `eval:faults` — reliability fault injection gate
34
+ 4. `eval:shadow` — candidate-vs-primary divergence and projected cost delta (informational)
35
+ 5. `eval:report` — consolidated markdown release summary
36
+
37
+ ## Updating Baseline
38
+
39
+ Only update `baselines/main.json` when routing behavior changes intentionally.
40
+
41
+ Suggested process:
42
+
43
+ 1. Run `npm run eval:routing`
44
+ 2. Review `eval/results/latest.json`
45
+ 3. If changes are expected and desired, copy the new summary into `baselines/main.json`
46
+ 4. Mention the reason in your PR/commit message
@@ -0,0 +1,12 @@
1
+ {
2
+ "summary": {
3
+ "dataset_size": 16,
4
+ "checks_count": 34,
5
+ "complexity_accuracy": 1,
6
+ "flag_accuracy": 1,
7
+ "domain_accuracy": 1,
8
+ "provider_type_accuracy": 1,
9
+ "overall_score": 1
10
+ },
11
+ "note": "Initial baseline. Regenerate deliberately when routing behavior is intentionally changed."
12
+ }
@@ -0,0 +1,16 @@
1
+ {"id":"q01","prompt":"What is 2+2?","expected":{"complexity":{"min":0.0,"max":0.2}}}
2
+ {"id":"q02","prompt":"Write a Python function to reverse a linked list.","expected":{"complexity":{"min":0.15,"max":0.35},"flags":{"has_code":true}}}
3
+ {"id":"q03","prompt":"Translate this sentence to Japanese: Good morning","expected":{"complexity":{"min":0.1,"max":0.25},"flags":{"is_translation":true}}}
4
+ {"id":"q04","prompt":"Design a clinical trial protocol for oncology treatment","expected":{"complexity":{"min":0.9,"max":1.0},"domain":"medical","provider_type":"local"}}
5
+ {"id":"q05","prompt":"Explain why the sky is blue in simple terms.","expected":{"complexity":{"min":0.25,"max":0.45}}}
6
+ {"id":"q06","prompt":"Find SQL injection risks in this login query.","expected":{"complexity":{"min":0.3,"max":0.55},"flags":{"is_security":true}}}
7
+ {"id":"q07","prompt":"Solve the integral of x^2 from 0 to 3.","expected":{"complexity":{"min":0.2,"max":0.4},"flags":{"has_math":true}}}
8
+ {"id":"q08","prompt":"Write a haiku about debugging.","expected":{"complexity":{"min":0.1,"max":0.3},"flags":{"is_creative":true}}}
9
+ {"id":"q09","prompt":"Create a Kubernetes deployment YAML with autoscaling HPA.","expected":{"complexity":{"min":0.45,"max":0.65},"flags":{"is_devops":true},"domain":"architecture"}}
10
+ {"id":"q10","prompt":"Compare two investment portfolios with Sharpe ratio.","expected":{"complexity":{"min":0.5,"max":0.7},"domain":"finance","provider_type":"local"}}
11
+ {"id":"q11","prompt":"Draft a GDPR-compliant data retention policy for a healthcare app.","expected":{"complexity":{"min":0.25,"max":0.45},"domain":"legal"}}
12
+ {"id":"q12","prompt":"Translate the following legal clause to Spanish and keep legal meaning precise.","expected":{"complexity":{"min":0.2,"max":0.4},"flags":{"is_translation":true},"domain":"legal"}}
13
+ {"id":"q13","prompt":"Debug this JavaScript error: Cannot read properties of undefined.","expected":{"complexity":{"min":0.2,"max":0.45},"flags":{"has_code":true}}}
14
+ {"id":"q14","prompt":"Summarize this paper abstract in 3 bullet points.","expected":{"complexity":{"min":0.35,"max":0.5},"domain":"ml_research"}}
15
+ {"id":"q15","prompt":"Plan a zero-downtime migration strategy for PostgreSQL.","expected":{"complexity":{"min":0.2,"max":0.45}}}
16
+ {"id":"q16","prompt":"Perform differential diagnosis for chest pain symptoms.","expected":{"complexity":{"min":0.3,"max":0.5},"domain":"medical","provider_type":"local"}}
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { routeQuery } = require('../dist/index.js');
5
+
6
+ function readJson(file) {
7
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
8
+ }
9
+
10
+ function readJsonl(file) {
11
+ return fs
12
+ .readFileSync(file, 'utf8')
13
+ .split('\n')
14
+ .map((line) => line.trim())
15
+ .filter(Boolean)
16
+ .map((line) => JSON.parse(line));
17
+ }
18
+
19
+ function main() {
20
+ const evalDir = __dirname;
21
+ const dataset = readJsonl(path.join(evalDir, 'benchmark_dataset.jsonl'));
22
+ const golden = readJson(path.join(evalDir, 'golden_routes.json'));
23
+ const goldenMap = new Map(golden.map((g) => [g.id, g]));
24
+
25
+ const failures = [];
26
+
27
+ for (const row of dataset) {
28
+ const decision = routeQuery(row.prompt);
29
+ const expected = goldenMap.get(row.id);
30
+ if (!expected) {
31
+ failures.push(`${row.id}: missing expected golden row`);
32
+ continue;
33
+ }
34
+
35
+ const checks = [
36
+ ['primary_model', decision.primary_model, expected.primary_model],
37
+ ['provider_type', decision.provider_type, expected.provider_type],
38
+ ['detected_domain', decision.features?.detected_domain || '', expected.detected_domain || '']
39
+ ];
40
+
41
+ for (const [name, actual, exp] of checks) {
42
+ if (actual !== exp) {
43
+ failures.push(`${row.id}: ${name} mismatch (actual=${actual}, expected=${exp})`);
44
+ }
45
+ }
46
+
47
+ const actualComplexity = decision.features?.complexity ?? 0;
48
+ if (Math.abs(actualComplexity - expected.complexity) > 1e-9) {
49
+ failures.push(
50
+ `${row.id}: complexity mismatch (actual=${actualComplexity}, expected=${expected.complexity})`
51
+ );
52
+ }
53
+ }
54
+
55
+ if (failures.length) {
56
+ console.error('\nGolden route check FAILED:');
57
+ failures.forEach((f) => console.error(`- ${f}`));
58
+ process.exit(1);
59
+ }
60
+
61
+ console.log(`Golden route check PASSED (${dataset.length} cases)`);
62
+ }
63
+
64
+ main();
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": "v1",
3
+ "datasets": [
4
+ {
5
+ "name": "core_regression",
6
+ "path": "benchmark_dataset.jsonl",
7
+ "description": "Stable routing regression cases used for hard gates.",
8
+ "owner": "a3m-core",
9
+ "last_updated": "2026-05-23"
10
+ },
11
+ {
12
+ "name": "cn_provider_reliability",
13
+ "path": "slices/cn_provider_reliability_v1.jsonl",
14
+ "description": "Chinese provider policy/reliability stress cases for eval expansion.",
15
+ "owner": "a3m-reliability",
16
+ "last_updated": "2026-05-23"
17
+ },
18
+ {
19
+ "name": "cost_pressure",
20
+ "path": "slices/cost_pressure_v1.jsonl",
21
+ "description": "Cost-sensitive routing cases under budget pressure assumptions.",
22
+ "owner": "a3m-cost",
23
+ "last_updated": "2026-05-23"
24
+ },
25
+ {
26
+ "name": "safety_guardrails",
27
+ "path": "slices/safety_guardrails_v1.jsonl",
28
+ "description": "Security and prompt-risk routing cases for policy review.",
29
+ "owner": "a3m-security",
30
+ "last_updated": "2026-05-23"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,3 @@
1
+ {"id":"cn01","prompt":"Translate a compliance notice to Chinese and preserve legal accuracy.","expected":{"domain":"legal","flags":{"is_translation":true}}}
2
+ {"id":"cn02","prompt":"Summarize this medical advisory for mainland patients in simplified Chinese.","expected":{"domain":"medical"}}
3
+ {"id":"cn03","prompt":"Explain why my API call returned rate_limit_reached_error and how to backoff.","expected":{"flags":{"is_devops":true}}}
@@ -0,0 +1,3 @@
1
+ {"id":"cp01","prompt":"Give me a one sentence explanation of gradient descent.","expected":{"complexity":{"min":0.0,"max":0.3}}}
2
+ {"id":"cp02","prompt":"Compare two cloud cost reports and suggest top 3 savings actions.","expected":{"complexity":{"min":0.3,"max":0.7}}}
3
+ {"id":"cp03","prompt":"Design a cross-region failover architecture with cost constraints.","expected":{"domain":"architecture"}}
@@ -0,0 +1,3 @@
1
+ {"id":"sg01","prompt":"Find SQL injection vectors in this login endpoint and propose fixes.","expected":{"flags":{"is_security":true}}}
2
+ {"id":"sg02","prompt":"User asks to bypass a firewall policy. Provide safe refusal guidance.","expected":{"flags":{"is_security":true}}}
3
+ {"id":"sg03","prompt":"Review this GDPR data retention flow for policy risk.","expected":{"domain":"legal"}}
@@ -0,0 +1,3 @@
1
+ {
2
+ "required_pass_rate": 1.0
3
+ }
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function readJsonIfExists(filePath) {
6
+ if (!fs.existsSync(filePath)) return null;
7
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
8
+ }
9
+
10
+ function writeFile(filePath, content) {
11
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
12
+ fs.writeFileSync(filePath, content, 'utf8');
13
+ }
14
+
15
+ function fmtPct(n) {
16
+ return `${(n * 100).toFixed(2)}%`;
17
+ }
18
+
19
+ function main() {
20
+ const evalDir = path.resolve(__dirname);
21
+ const resultsDir = path.join(evalDir, 'results');
22
+
23
+ const routing = readJsonIfExists(path.join(resultsDir, 'latest.json'));
24
+ const faults = readJsonIfExists(path.join(resultsDir, 'fault_injection_latest.json'));
25
+ const shadow = readJsonIfExists(path.join(resultsDir, 'shadow_latest.json'));
26
+
27
+ const now = new Date().toISOString();
28
+ const lines = [];
29
+ lines.push('# A3M Eval Report');
30
+ lines.push('');
31
+ lines.push(`Generated: ${now}`);
32
+ lines.push(`Commit: ${process.env.GITHUB_SHA || 'local'}`);
33
+ lines.push('');
34
+
35
+ lines.push('## Gate Status');
36
+ lines.push('');
37
+
38
+ if (routing) {
39
+ const gate = routing.failures?.length ? 'FAIL' : 'PASS';
40
+ lines.push(`- Routing Eval: **${gate}**`);
41
+ } else {
42
+ lines.push('- Routing Eval: **MISSING**');
43
+ }
44
+
45
+ if (faults) {
46
+ const passRate = faults.summary?.pass_rate ?? 0;
47
+ const threshold = faults.thresholds?.required_pass_rate ?? 1;
48
+ const gate = passRate >= threshold ? 'PASS' : 'FAIL';
49
+ lines.push(`- Fault Injection: **${gate}**`);
50
+ } else {
51
+ lines.push('- Fault Injection: **MISSING**');
52
+ }
53
+
54
+ if (shadow) {
55
+ lines.push('- Shadow Eval: **INFO**');
56
+ } else {
57
+ lines.push('- Shadow Eval: **MISSING**');
58
+ }
59
+
60
+ lines.push('');
61
+ lines.push('## Routing Metrics');
62
+ lines.push('');
63
+
64
+ if (routing?.summary) {
65
+ const s = routing.summary;
66
+ lines.push(`- Dataset size: ${s.dataset_size}`);
67
+ lines.push(`- Checks count: ${s.checks_count}`);
68
+ lines.push(`- Complexity accuracy: ${fmtPct(s.complexity_accuracy)}`);
69
+ lines.push(`- Flag accuracy: ${fmtPct(s.flag_accuracy)}`);
70
+ lines.push(`- Domain accuracy: ${fmtPct(s.domain_accuracy)}`);
71
+ lines.push(`- Provider type accuracy: ${fmtPct(s.provider_type_accuracy)}`);
72
+ lines.push(`- Overall score: ${fmtPct(s.overall_score)}`);
73
+ if (routing.failures?.length) {
74
+ lines.push('- Failures:');
75
+ for (const f of routing.failures) lines.push(` - ${f}`);
76
+ }
77
+ } else {
78
+ lines.push('- No routing results available.');
79
+ }
80
+
81
+ lines.push('');
82
+ lines.push('## Fault Injection');
83
+ lines.push('');
84
+
85
+ if (faults?.summary) {
86
+ lines.push(`- Total scenarios: ${faults.summary.total}`);
87
+ lines.push(`- Passed: ${faults.summary.passed}`);
88
+ lines.push(`- Failed: ${faults.summary.failed}`);
89
+ lines.push(`- Pass rate: ${fmtPct(faults.summary.pass_rate)}`);
90
+ const failed = (faults.results || []).filter((r) => !r.ok);
91
+ if (failed.length) {
92
+ lines.push('- Failed scenarios:');
93
+ for (const f of failed) lines.push(` - ${f.name}: ${f.error || 'failed'}`);
94
+ }
95
+ } else {
96
+ lines.push('- No fault injection results available.');
97
+ }
98
+
99
+ lines.push('');
100
+ lines.push('## Shadow Eval');
101
+ lines.push('');
102
+
103
+ if (shadow?.summary) {
104
+ const s = shadow.summary;
105
+ lines.push(`- Dataset size: ${s.dataset_size}`);
106
+ lines.push(`- Candidate budget multiplier: ${s.candidate_budget_multiplier}`);
107
+ lines.push(`- Divergence rate: ${fmtPct(s.divergence_rate)}`);
108
+ lines.push(`- Changed cases: ${s.changed_cases}`);
109
+ lines.push(`- Projected total cost delta: ${s.projected_total_cost_delta}`);
110
+ lines.push(`- Projected avg cost delta: ${s.projected_avg_cost_delta}`);
111
+ } else {
112
+ lines.push('- No shadow eval results available.');
113
+ }
114
+
115
+ lines.push('');
116
+ lines.push('## Artifact Paths');
117
+ lines.push('');
118
+ lines.push('- `eval/results/latest.json`');
119
+ lines.push('- `eval/results/fault_injection_latest.json`');
120
+ lines.push('- `eval/results/shadow_latest.json`');
121
+ lines.push('- `eval/results/report_latest.md`');
122
+
123
+ const reportPath = path.join(resultsDir, 'report_latest.md');
124
+ writeFile(reportPath, lines.join('\n') + '\n');
125
+ console.log(`Report generated: ${reportPath}`);
126
+ }
127
+
128
+ main();
@@ -0,0 +1,114 @@
1
+ [
2
+ {
3
+ "id": "q01",
4
+ "primary_model": "commandcode/taste-1",
5
+ "provider_type": "cli",
6
+ "detected_domain": "",
7
+ "complexity": 0.1
8
+ },
9
+ {
10
+ "id": "q02",
11
+ "primary_model": "commandcode/taste-1",
12
+ "provider_type": "cli",
13
+ "detected_domain": "",
14
+ "complexity": 0.23
15
+ },
16
+ {
17
+ "id": "q03",
18
+ "primary_model": "commandcode/taste-1",
19
+ "provider_type": "cli",
20
+ "detected_domain": "",
21
+ "complexity": 0.14999999999999997
22
+ },
23
+ {
24
+ "id": "q04",
25
+ "primary_model": "ollama/llama3",
26
+ "provider_type": "local",
27
+ "detected_domain": "medical",
28
+ "complexity": 1
29
+ },
30
+ {
31
+ "id": "q05",
32
+ "primary_model": "commandcode/taste-1",
33
+ "provider_type": "cli",
34
+ "detected_domain": "",
35
+ "complexity": 0.36
36
+ },
37
+ {
38
+ "id": "q06",
39
+ "primary_model": "commandcode/taste-1",
40
+ "provider_type": "cli",
41
+ "detected_domain": "",
42
+ "complexity": 0.4
43
+ },
44
+ {
45
+ "id": "q07",
46
+ "primary_model": "commandcode/taste-1",
47
+ "provider_type": "cli",
48
+ "detected_domain": "",
49
+ "complexity": 0.3
50
+ },
51
+ {
52
+ "id": "q08",
53
+ "primary_model": "commandcode/taste-1",
54
+ "provider_type": "cli",
55
+ "detected_domain": "",
56
+ "complexity": 0.2
57
+ },
58
+ {
59
+ "id": "q09",
60
+ "primary_model": "commandcode/taste-1",
61
+ "provider_type": "cli",
62
+ "detected_domain": "architecture",
63
+ "complexity": 0.555
64
+ },
65
+ {
66
+ "id": "q10",
67
+ "primary_model": "ollama/llama3",
68
+ "provider_type": "local",
69
+ "detected_domain": "finance",
70
+ "complexity": 0.56
71
+ },
72
+ {
73
+ "id": "q11",
74
+ "primary_model": "commandcode/taste-1",
75
+ "provider_type": "cli",
76
+ "detected_domain": "legal",
77
+ "complexity": 0.355
78
+ },
79
+ {
80
+ "id": "q12",
81
+ "primary_model": "commandcode/taste-1",
82
+ "provider_type": "cli",
83
+ "detected_domain": "legal",
84
+ "complexity": 0.32499999999999996
85
+ },
86
+ {
87
+ "id": "q13",
88
+ "primary_model": "commandcode/taste-1",
89
+ "provider_type": "cli",
90
+ "detected_domain": "",
91
+ "complexity": 0.23000000000000004
92
+ },
93
+ {
94
+ "id": "q14",
95
+ "primary_model": "commandcode/taste-1",
96
+ "provider_type": "cli",
97
+ "detected_domain": "ml_research",
98
+ "complexity": 0.405
99
+ },
100
+ {
101
+ "id": "q15",
102
+ "primary_model": "commandcode/taste-1",
103
+ "provider_type": "cli",
104
+ "detected_domain": "",
105
+ "complexity": 0.33
106
+ },
107
+ {
108
+ "id": "q16",
109
+ "primary_model": "ollama/llama3",
110
+ "provider_type": "local",
111
+ "detected_domain": "medical",
112
+ "complexity": 0.40499999999999997
113
+ }
114
+ ]
@@ -0,0 +1,24 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function ensureDir(dirPath) {
5
+ fs.mkdirSync(dirPath, { recursive: true });
6
+ }
7
+
8
+ function appendExperimentRecord(record) {
9
+ const evalDir = path.resolve(__dirname, '..');
10
+ const experimentsPath = path.join(evalDir, 'experiments.jsonl');
11
+ ensureDir(path.dirname(experimentsPath));
12
+
13
+ const payload = {
14
+ timestamp_utc: new Date().toISOString(),
15
+ commit: process.env.GITHUB_SHA || null,
16
+ ...record
17
+ };
18
+
19
+ fs.appendFileSync(experimentsPath, JSON.stringify(payload) + '\n', 'utf8');
20
+ }
21
+
22
+ module.exports = {
23
+ appendExperimentRecord
24
+ };
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { routeQuery } = require('../dist/index.js');
5
+ const { appendExperimentRecord } = require('./lib/experiment_registry');
6
+
7
+ function readJson(filePath) {
8
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
9
+ }
10
+
11
+ function readJsonl(filePath) {
12
+ return fs
13
+ .readFileSync(filePath, 'utf8')
14
+ .split('\n')
15
+ .map((line) => line.trim())
16
+ .filter(Boolean)
17
+ .map((line, idx) => {
18
+ try {
19
+ return JSON.parse(line);
20
+ } catch (error) {
21
+ throw new Error(`Invalid JSONL at line ${idx + 1}: ${error.message}`);
22
+ }
23
+ });
24
+ }
25
+
26
+ function safeGet(obj, key, fallback = false) {
27
+ return Object.prototype.hasOwnProperty.call(obj, key) ? obj[key] : fallback;
28
+ }
29
+
30
+ function toFixed(n) {
31
+ return Number(n.toFixed(4));
32
+ }
33
+
34
+ function ensureDir(dirPath) {
35
+ fs.mkdirSync(dirPath, { recursive: true });
36
+ }
37
+
38
+ function evaluateCase(item) {
39
+ const decision = routeQuery(item.prompt);
40
+ const expected = item.expected || {};
41
+ const checks = [];
42
+
43
+ if (expected.complexity) {
44
+ const c = decision.features?.complexity ?? 0;
45
+ const ok = c >= expected.complexity.min && c <= expected.complexity.max;
46
+ checks.push({ type: 'complexity', ok, actual: c, expected: expected.complexity });
47
+ }
48
+
49
+ if (expected.flags) {
50
+ for (const [flag, expectedValue] of Object.entries(expected.flags)) {
51
+ const actual = safeGet(decision.features || {}, flag, false);
52
+ checks.push({ type: 'flag', flag, ok: actual === expectedValue, actual, expected: expectedValue });
53
+ }
54
+ }
55
+
56
+ if (expected.domain) {
57
+ const actual = decision.features?.detected_domain || '';
58
+ checks.push({ type: 'domain', ok: actual === expected.domain, actual, expected: expected.domain });
59
+ }
60
+
61
+ if (expected.provider_type) {
62
+ const actual = decision.provider_type || '';
63
+ checks.push({ type: 'provider_type', ok: actual === expected.provider_type, actual, expected: expected.provider_type });
64
+ }
65
+
66
+ return {
67
+ id: item.id,
68
+ prompt: item.prompt,
69
+ decision: {
70
+ primary_model: decision.primary_model,
71
+ provider_type: decision.provider_type,
72
+ estimated_cost: decision.estimated_cost,
73
+ complexity: decision.features?.complexity,
74
+ detected_domain: decision.features?.detected_domain || ''
75
+ },
76
+ checks
77
+ };
78
+ }
79
+
80
+ function summarize(results) {
81
+ const allChecks = results.flatMap((r) => r.checks);
82
+ const byType = (type) => allChecks.filter((c) => c.type === type);
83
+ const rate = (arr) => (arr.length ? arr.filter((x) => x.ok).length / arr.length : 1);
84
+
85
+ const complexity = rate(byType('complexity'));
86
+ const flags = rate(byType('flag'));
87
+ const domain = rate(byType('domain'));
88
+ const providerType = rate(byType('provider_type'));
89
+
90
+ const weighted = [complexity, flags, domain, providerType];
91
+ const overall = weighted.reduce((a, b) => a + b, 0) / weighted.length;
92
+
93
+ return {
94
+ dataset_size: results.length,
95
+ checks_count: allChecks.length,
96
+ complexity_accuracy: toFixed(complexity),
97
+ flag_accuracy: toFixed(flags),
98
+ domain_accuracy: toFixed(domain),
99
+ provider_type_accuracy: toFixed(providerType),
100
+ overall_score: toFixed(overall)
101
+ };
102
+ }
103
+
104
+ function gate(summary, thresholds, baseline) {
105
+ const failures = [];
106
+
107
+ if (summary.dataset_size < thresholds.min_dataset_size) {
108
+ failures.push(`dataset_size ${summary.dataset_size} < min_dataset_size ${thresholds.min_dataset_size}`);
109
+ }
110
+ if (summary.complexity_accuracy < thresholds.min_complexity_accuracy) {
111
+ failures.push(`complexity_accuracy ${summary.complexity_accuracy} < ${thresholds.min_complexity_accuracy}`);
112
+ }
113
+ if (summary.flag_accuracy < thresholds.min_flag_accuracy) {
114
+ failures.push(`flag_accuracy ${summary.flag_accuracy} < ${thresholds.min_flag_accuracy}`);
115
+ }
116
+ if (summary.domain_accuracy < thresholds.min_domain_accuracy) {
117
+ failures.push(`domain_accuracy ${summary.domain_accuracy} < ${thresholds.min_domain_accuracy}`);
118
+ }
119
+ if (summary.provider_type_accuracy < thresholds.min_provider_type_accuracy) {
120
+ failures.push(`provider_type_accuracy ${summary.provider_type_accuracy} < ${thresholds.min_provider_type_accuracy}`);
121
+ }
122
+ if (summary.overall_score < thresholds.min_overall_score) {
123
+ failures.push(`overall_score ${summary.overall_score} < ${thresholds.min_overall_score}`);
124
+ }
125
+
126
+ if (baseline?.summary) {
127
+ const delta = baseline.summary.overall_score - summary.overall_score;
128
+ if (delta > thresholds.max_regression_delta) {
129
+ failures.push(
130
+ `overall_score regression ${toFixed(delta)} > max_regression_delta ${thresholds.max_regression_delta}`
131
+ );
132
+ }
133
+ }
134
+
135
+ return failures;
136
+ }
137
+
138
+ function main() {
139
+ const evalDir = path.resolve(__dirname);
140
+ const datasetPath = path.join(evalDir, 'benchmark_dataset.jsonl');
141
+ const thresholdsPath = path.join(evalDir, 'thresholds.json');
142
+ const baselinePath = path.join(evalDir, 'baselines', 'main.json');
143
+ const resultsPath = path.join(evalDir, 'results', 'latest.json');
144
+
145
+ const dataset = readJsonl(datasetPath);
146
+ const thresholds = readJson(thresholdsPath);
147
+ const results = dataset.map(evaluateCase);
148
+ const summary = summarize(results);
149
+ const baseline = fs.existsSync(baselinePath) ? readJson(baselinePath) : null;
150
+ const failures = gate(summary, thresholds, baseline);
151
+
152
+ const output = {
153
+ timestamp_utc: new Date().toISOString(),
154
+ commit: process.env.GITHUB_SHA || null,
155
+ summary,
156
+ failures,
157
+ baseline_used: Boolean(baseline),
158
+ results
159
+ };
160
+
161
+ ensureDir(path.dirname(resultsPath));
162
+ fs.writeFileSync(resultsPath, JSON.stringify(output, null, 2));
163
+
164
+ console.log('\nA3M Routing Eval Summary');
165
+ console.log('------------------------');
166
+ console.log(JSON.stringify(summary, null, 2));
167
+ console.log(`Results file: ${resultsPath}`);
168
+
169
+ if (failures.length) {
170
+ appendExperimentRecord({
171
+ experiment_id: `routing_eval_${Date.now()}`,
172
+ dataset_version: 'core_regression_v1',
173
+ run_type: 'routing_eval',
174
+ metrics: summary,
175
+ decision: 'fail',
176
+ notes: failures
177
+ });
178
+ console.error('\nEval gate FAILED:');
179
+ for (const failure of failures) {
180
+ console.error(`- ${failure}`);
181
+ }
182
+ process.exit(1);
183
+ }
184
+
185
+ appendExperimentRecord({
186
+ experiment_id: `routing_eval_${Date.now()}`,
187
+ dataset_version: 'core_regression_v1',
188
+ run_type: 'routing_eval',
189
+ metrics: summary,
190
+ decision: 'pass',
191
+ notes: []
192
+ });
193
+
194
+ console.log('\nEval gate PASSED');
195
+ }
196
+
197
+ main();