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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { ProviderRetryHandler } = require('../dist/routing/providerRetry.js');
|
|
5
|
+
const { ProviderHealthManager } = require('../dist/routing/providerHealth.js');
|
|
6
|
+
const { appendExperimentRecord } = require('./lib/experiment_registry');
|
|
7
|
+
|
|
8
|
+
function readJson(filePath) {
|
|
9
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function scenarioRetryTransientThenSuccess() {
|
|
13
|
+
const handler = new ProviderRetryHandler({
|
|
14
|
+
openai: {
|
|
15
|
+
timeout: 3000,
|
|
16
|
+
retry: {
|
|
17
|
+
maxRetries: 3,
|
|
18
|
+
initialDelayMs: 10,
|
|
19
|
+
maxDelayMs: 100,
|
|
20
|
+
backoffMultiplier: 2,
|
|
21
|
+
retryableErrors: ['ETIMEDOUT', '503']
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
let callCount = 0;
|
|
27
|
+
const result = await handler.executeWithRetry('openai', async () => {
|
|
28
|
+
callCount += 1;
|
|
29
|
+
if (callCount < 3) {
|
|
30
|
+
const err = new Error('temporary timeout');
|
|
31
|
+
err.code = 'ETIMEDOUT';
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
return 'ok';
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return result === 'ok' && callCount === 3;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function scenarioNoRetryOnBadRequest() {
|
|
41
|
+
const handler = new ProviderRetryHandler({
|
|
42
|
+
openai: {
|
|
43
|
+
timeout: 3000,
|
|
44
|
+
retry: {
|
|
45
|
+
maxRetries: 5,
|
|
46
|
+
initialDelayMs: 10,
|
|
47
|
+
maxDelayMs: 100,
|
|
48
|
+
backoffMultiplier: 2,
|
|
49
|
+
retryableErrors: ['ETIMEDOUT', '503']
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let callCount = 0;
|
|
55
|
+
try {
|
|
56
|
+
await handler.executeWithRetry('openai', async () => {
|
|
57
|
+
callCount += 1;
|
|
58
|
+
const err = new Error('bad request');
|
|
59
|
+
err.status = 400;
|
|
60
|
+
throw err;
|
|
61
|
+
});
|
|
62
|
+
return false;
|
|
63
|
+
} catch {
|
|
64
|
+
return callCount === 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function scenarioNoRetryOnChineseQuotaAccountErrors() {
|
|
69
|
+
const handler = new ProviderRetryHandler({
|
|
70
|
+
moonshot: {
|
|
71
|
+
timeout: 3000,
|
|
72
|
+
retry: {
|
|
73
|
+
maxRetries: 5,
|
|
74
|
+
initialDelayMs: 10,
|
|
75
|
+
maxDelayMs: 100,
|
|
76
|
+
backoffMultiplier: 2,
|
|
77
|
+
retryableErrors: ['429', '503', 'ETIMEDOUT']
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
let callCount = 0;
|
|
83
|
+
try {
|
|
84
|
+
await handler.executeWithRetry('moonshot', async () => {
|
|
85
|
+
callCount += 1;
|
|
86
|
+
const err = new Error('request reached organization TPD rate limit, current: 1501880, limit: 1500000');
|
|
87
|
+
err.status = 429;
|
|
88
|
+
err.code = 'rate_limit_reached_error';
|
|
89
|
+
throw err;
|
|
90
|
+
});
|
|
91
|
+
return false;
|
|
92
|
+
} catch {
|
|
93
|
+
return callCount === 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function scenarioCircuitBreakerOpens() {
|
|
98
|
+
const health = new ProviderHealthManager({
|
|
99
|
+
circuitBreakerThreshold: 3,
|
|
100
|
+
cooldownMs: 30000,
|
|
101
|
+
windowSize: 10
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const provider = 'mock/provider';
|
|
105
|
+
health.recordFailure(provider, 'e1');
|
|
106
|
+
health.recordFailure(provider, 'e2');
|
|
107
|
+
health.recordFailure(provider, 'e3');
|
|
108
|
+
|
|
109
|
+
const state = health.getHealth(provider);
|
|
110
|
+
return state.isHealthy === false && state.consecutiveErrors >= 3 && state.cooldownUntil > Date.now();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function scenarioFallbackOrdering() {
|
|
114
|
+
const health = new ProviderHealthManager({ windowSize: 10 });
|
|
115
|
+
|
|
116
|
+
health.recordSuccess('fast', 80);
|
|
117
|
+
health.recordSuccess('fast', 90);
|
|
118
|
+
|
|
119
|
+
health.recordSuccess('slow', 500);
|
|
120
|
+
health.recordSuccess('slow', 600);
|
|
121
|
+
|
|
122
|
+
health.recordFailure('broken', 'x1');
|
|
123
|
+
health.recordFailure('broken', 'x2');
|
|
124
|
+
health.recordFailure('broken', 'x3');
|
|
125
|
+
|
|
126
|
+
const chain = health.getFallbackChain(['broken', 'slow', 'fast']);
|
|
127
|
+
return chain[0] === 'fast' && chain[chain.length - 1] === 'broken';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
const thresholds = readJson(path.join(__dirname, 'fault_injection_thresholds.json'));
|
|
132
|
+
const scenarios = [
|
|
133
|
+
['retry_transient_then_success', scenarioRetryTransientThenSuccess],
|
|
134
|
+
['no_retry_on_bad_request', scenarioNoRetryOnBadRequest],
|
|
135
|
+
['no_retry_on_chinese_quota_account_errors', scenarioNoRetryOnChineseQuotaAccountErrors],
|
|
136
|
+
['circuit_breaker_opens', scenarioCircuitBreakerOpens],
|
|
137
|
+
['fallback_ordering', scenarioFallbackOrdering]
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const results = [];
|
|
141
|
+
for (const [name, fn] of scenarios) {
|
|
142
|
+
try {
|
|
143
|
+
const ok = await fn();
|
|
144
|
+
results.push({ name, ok, error: null });
|
|
145
|
+
} catch (error) {
|
|
146
|
+
results.push({ name, ok: false, error: String(error) });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const passed = results.filter((r) => r.ok).length;
|
|
151
|
+
const passRate = results.length ? passed / results.length : 0;
|
|
152
|
+
const summary = {
|
|
153
|
+
total: results.length,
|
|
154
|
+
passed,
|
|
155
|
+
failed: results.length - passed,
|
|
156
|
+
pass_rate: Number(passRate.toFixed(4))
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const output = {
|
|
160
|
+
timestamp_utc: new Date().toISOString(),
|
|
161
|
+
summary,
|
|
162
|
+
thresholds,
|
|
163
|
+
results
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const outDir = path.join(__dirname, 'results');
|
|
167
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
168
|
+
fs.writeFileSync(path.join(outDir, 'fault_injection_latest.json'), JSON.stringify(output, null, 2));
|
|
169
|
+
|
|
170
|
+
console.log('\nFault Injection Summary');
|
|
171
|
+
console.log('-----------------------');
|
|
172
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
173
|
+
|
|
174
|
+
if (passRate < thresholds.required_pass_rate) {
|
|
175
|
+
appendExperimentRecord({
|
|
176
|
+
experiment_id: `fault_injection_${Date.now()}`,
|
|
177
|
+
dataset_version: 'fault_scenarios_v1',
|
|
178
|
+
run_type: 'fault_injection',
|
|
179
|
+
metrics: summary,
|
|
180
|
+
decision: 'fail',
|
|
181
|
+
notes: results.filter((r) => !r.ok).map((r) => `${r.name}: ${r.error || 'failed'}`)
|
|
182
|
+
});
|
|
183
|
+
console.error(
|
|
184
|
+
`\nFault injection gate FAILED: pass_rate ${summary.pass_rate} < ${thresholds.required_pass_rate}`
|
|
185
|
+
);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
appendExperimentRecord({
|
|
190
|
+
experiment_id: `fault_injection_${Date.now()}`,
|
|
191
|
+
dataset_version: 'fault_scenarios_v1',
|
|
192
|
+
run_type: 'fault_injection',
|
|
193
|
+
metrics: summary,
|
|
194
|
+
decision: 'pass',
|
|
195
|
+
notes: []
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
console.log('\nFault injection gate PASSED');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
main();
|
|
@@ -0,0 +1,85 @@
|
|
|
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 readJsonl(filePath) {
|
|
8
|
+
return fs
|
|
9
|
+
.readFileSync(filePath, 'utf8')
|
|
10
|
+
.split('\n')
|
|
11
|
+
.map((line) => line.trim())
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.map((line) => JSON.parse(line));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function toFixed(n) {
|
|
17
|
+
return Number(n.toFixed(6));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
const evalDir = path.resolve(__dirname);
|
|
22
|
+
const dataset = readJsonl(path.join(evalDir, 'benchmark_dataset.jsonl'));
|
|
23
|
+
const candidateBudgetMultiplier = Number(process.env.A3M_SHADOW_BUDGET_MULTIPLIER || '0.85');
|
|
24
|
+
|
|
25
|
+
let divergence = 0;
|
|
26
|
+
let projectedCostDelta = 0;
|
|
27
|
+
const comparisons = [];
|
|
28
|
+
|
|
29
|
+
for (const row of dataset) {
|
|
30
|
+
const primary = routeQuery(row.prompt);
|
|
31
|
+
const shadow = routeQuery(row.prompt, undefined, candidateBudgetMultiplier);
|
|
32
|
+
|
|
33
|
+
const changed = primary.primary_model !== shadow.primary_model;
|
|
34
|
+
if (changed) divergence += 1;
|
|
35
|
+
|
|
36
|
+
const costDelta = Number((shadow.estimated_cost || 0) - (primary.estimated_cost || 0));
|
|
37
|
+
projectedCostDelta += costDelta;
|
|
38
|
+
|
|
39
|
+
comparisons.push({
|
|
40
|
+
id: row.id,
|
|
41
|
+
primary_model: primary.primary_model,
|
|
42
|
+
shadow_model: shadow.primary_model,
|
|
43
|
+
changed,
|
|
44
|
+
primary_cost: primary.estimated_cost,
|
|
45
|
+
shadow_cost: shadow.estimated_cost,
|
|
46
|
+
cost_delta: toFixed(costDelta)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const summary = {
|
|
51
|
+
dataset_size: dataset.length,
|
|
52
|
+
candidate_budget_multiplier: candidateBudgetMultiplier,
|
|
53
|
+
divergence_rate: toFixed(divergence / dataset.length),
|
|
54
|
+
changed_cases: divergence,
|
|
55
|
+
projected_total_cost_delta: toFixed(projectedCostDelta),
|
|
56
|
+
projected_avg_cost_delta: toFixed(projectedCostDelta / dataset.length)
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const out = {
|
|
60
|
+
timestamp_utc: new Date().toISOString(),
|
|
61
|
+
summary,
|
|
62
|
+
comparisons
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const outDir = path.join(evalDir, 'results');
|
|
66
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
67
|
+
const outPath = path.join(outDir, 'shadow_latest.json');
|
|
68
|
+
fs.writeFileSync(outPath, JSON.stringify(out, null, 2));
|
|
69
|
+
|
|
70
|
+
appendExperimentRecord({
|
|
71
|
+
experiment_id: `shadow_eval_${Date.now()}`,
|
|
72
|
+
dataset_version: 'core_regression_v1',
|
|
73
|
+
run_type: 'shadow_eval',
|
|
74
|
+
metrics: summary,
|
|
75
|
+
decision: 'informational',
|
|
76
|
+
notes: []
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
console.log('\nShadow Eval Summary');
|
|
80
|
+
console.log('-------------------');
|
|
81
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
82
|
+
console.log(`Shadow output: ${outPath}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|