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.
- package/.github/workflows/ci.yml +56 -0
- package/.github/workflows/pages.yml +37 -0
- package/Awesome-LLM/LICENSE.md +121 -0
- package/Awesome-LLM/README.md +625 -0
- package/Awesome-LLM/contributing.md +24 -0
- package/Awesome-LLM/paper_list/RLHF.md +1 -0
- package/Awesome-LLM/paper_list/Retrieval_Augmented_Generation.md +6 -0
- package/Awesome-LLM/paper_list/acceleration.md +10 -0
- package/Awesome-LLM/paper_list/alignment.md +20 -0
- package/Awesome-LLM/paper_list/application.md +19 -0
- package/Awesome-LLM/paper_list/augmentation.md +14 -0
- package/Awesome-LLM/paper_list/chain_of_thougt.md +16 -0
- package/Awesome-LLM/paper_list/code_pretraining.md +0 -0
- package/Awesome-LLM/paper_list/detection.md +11 -0
- package/Awesome-LLM/paper_list/evaluation.md +64 -0
- package/Awesome-LLM/paper_list/in_context_learning.md +4 -0
- package/Awesome-LLM/paper_list/instruction-tuning.md +35 -0
- package/Awesome-LLM/paper_list/moe.md +0 -0
- package/Awesome-LLM/paper_list/prompt_learning.md +15 -0
- package/Awesome-LLM/resources/DeepSpeed_light.svg +27 -0
- package/Awesome-LLM/resources/alpa-logo-cropped.png +0 -0
- package/Awesome-LLM/resources/colossal-ai_logo_vertical.png +0 -0
- package/Awesome-LLM/resources/creepy_llm.jpeg +0 -0
- package/Awesome-LLM/resources/image8.gif +0 -0
- package/Awesome-LLM/resources/jax_logo_250px.png +0 -0
- package/Awesome-LLM/resources/logo.png +0 -0
- package/Awesome-LLM/resources/nvidia.png +0 -0
- package/Awesome-LLM/resources/tensorflow.png +0 -0
- 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
- package/LANDING.md +46 -0
- package/MANIFESTO.md +54 -0
- package/README.md +3 -3
- package/SUBMISSIONS.md +43 -0
- package/assets/chart-cost-v2.svg +91 -0
- package/assets/chart-cost-v3.svg +143 -0
- package/assets/chart-features-v2.svg +132 -0
- package/assets/chart-features-v3.svg +211 -0
- package/assets/chart-growth-v2.svg +122 -0
- package/assets/chart-growth-v3.svg +189 -0
- package/assets/cost-simple.svg +64 -0
- package/assets/growth-simple.svg +69 -0
- package/assets/hero-diagram.svg +81 -0
- package/assets/logo-new.svg +21 -0
- package/assets/social-preview-new.svg +100 -0
- package/assets/social-v2.svg +130 -0
- package/assets/social-v3.svg +212 -0
- package/awesome-ai-gateways/CONTRIBUTING.md +49 -0
- package/awesome-ai-gateways/LICENSE +3 -0
- package/awesome-ai-gateways/README.md +190 -0
- package/awesome-selfhosted/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/awesome-selfhosted/.github/PULL_REQUEST_TEMPLATE.md +1 -0
- package/awesome-selfhosted/LICENSE +376 -0
- package/awesome-selfhosted/README.md +2303 -0
- package/awesome-selfhosted/_static/awesome.png +0 -0
- package/awesome-selfhosted/non-free.md +377 -0
- package/demo-new.tape +71 -0
- package/demo-real.sh +198 -0
- package/demo-simple.tape +205 -0
- package/demo.html +520 -0
- package/demo.sh +85 -0
- package/demo.tape +259 -0
- package/dist/cli/setupWizard.js +194 -0
- package/dist/cli/tui.js +152 -0
- package/dist/cli.js +9 -2
- package/dist/routing/providerRetry.d.ts +5 -0
- package/dist/routing/providerRetry.js +37 -0
- package/dist/routing/providerRetry.js.map +1 -1
- package/docs/CHINESE_PROVIDER_RELIABILITY.md +37 -0
- package/docs/CLAIMS_AND_EVIDENCE.md +58 -0
- package/docs/ENGINEERING_SPEC.md +55 -0
- package/docs/RELEASE_CHECKLIST.md +32 -0
- package/docs/REPRODUCIBILITY.md +63 -0
- package/docs/demo-auto.html +264 -0
- package/docs/demo.html +416 -0
- package/docs/index.html +112 -599
- package/eval/README.md +46 -0
- package/eval/baselines/main.json +12 -0
- package/eval/benchmark_dataset.jsonl +16 -0
- package/eval/check_golden_routes.js +64 -0
- package/eval/datasets/catalog.json +33 -0
- package/eval/datasets/slices/cn_provider_reliability_v1.jsonl +3 -0
- package/eval/datasets/slices/cost_pressure_v1.jsonl +3 -0
- package/eval/datasets/slices/safety_guardrails_v1.jsonl +3 -0
- package/eval/fault_injection_thresholds.json +3 -0
- package/eval/generate_report.js +128 -0
- package/eval/golden_routes.json +114 -0
- package/eval/lib/experiment_registry.js +24 -0
- package/eval/run_eval.js +197 -0
- package/eval/run_fault_injection.js +201 -0
- package/eval/run_shadow_eval.js +85 -0
- package/eval/thresholds.json +9 -0
- package/index.html +667 -0
- package/package.json +29 -168
- package/pytest.ini +2 -0
- package/src/cli/setupWizard.ts +194 -0
- package/src/routing/providerRetry.ts +41 -1
- package/docs/assets/cost-comparison.svg +0 -134
- package/docs/assets/growth-chart-animated.svg +0 -76
- package/docs/assets/og-banner.svg +0 -194
- package/docs/assets/social-preview.svg +0 -194
- package/python/a3m/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/a3m/__pycache__/client.cpython-312.pyc +0 -0
- package/python/a3m/__pycache__/models.cpython-312.pyc +0 -0
- 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,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
|
+
};
|
package/eval/run_eval.js
ADDED
|
@@ -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();
|