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,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A3M Router Setup Wizard
|
|
3
|
+
* Interactive configuration wizard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const readline = require('readline');
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.config', 'a3m-router');
|
|
11
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'providers.json');
|
|
12
|
+
|
|
13
|
+
// API key environment variable mappings
|
|
14
|
+
const API_KEY_ENV_MAP = {
|
|
15
|
+
'GROQ_API_KEY': 'groq',
|
|
16
|
+
'OPENAI_API_KEY': 'openai',
|
|
17
|
+
'ANTHROPIC_API_KEY': 'anthropic',
|
|
18
|
+
'DEEPSEEK_API_KEY': 'deepseek',
|
|
19
|
+
'MISTRAL_API_KEY': 'mistral',
|
|
20
|
+
'GOOGLE_API_KEY': 'google',
|
|
21
|
+
'CEREBRAS_API_KEY': 'cerebras',
|
|
22
|
+
'TOGETHER_API_KEY': 'together',
|
|
23
|
+
'AI21_API_KEY': 'ai21',
|
|
24
|
+
'COHERE_API_KEY': 'cohere',
|
|
25
|
+
'MINIMAX_API_KEY': 'minimax',
|
|
26
|
+
'KIMI_API_KEY': 'kimi',
|
|
27
|
+
'MOONSHOT_API_KEY': 'moonshot',
|
|
28
|
+
'QWEN_API_KEY': 'qwen',
|
|
29
|
+
'ZHIPU_API_KEY': 'zhipu',
|
|
30
|
+
'YI_API_KEY': 'yi',
|
|
31
|
+
'BAICHUAN_API_KEY': 'baichuan',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Provider metadata
|
|
35
|
+
const PROVIDER_INFO = {
|
|
36
|
+
groq: { name: 'Groq', models: 'llama-3.3-70b-versatile', tier: 'free', strength: 'Fast, free tier' },
|
|
37
|
+
openai: { name: 'OpenAI', models: 'gpt-4o-mini', tier: 'paid', strength: 'GPT-4, most capable' },
|
|
38
|
+
anthropic: { name: 'Anthropic', models: 'claude-3.5-haiku', tier: 'paid', strength: 'Claude, best reasoning' },
|
|
39
|
+
deepseek: { name: 'DeepSeek', models: 'deepseek-chat-v3', tier: 'cheap', strength: 'Cheap, good code' },
|
|
40
|
+
mistral: { name: 'Mistral', models: 'mistral-small-latest', tier: 'cheap', strength: 'European, balanced' },
|
|
41
|
+
google: { name: 'Google AI', models: 'gemini-1.5-flash', tier: 'free', strength: 'Gemini, multimodal' },
|
|
42
|
+
cerebras: { name: 'Cerebras', models: 'llama-3.3-70b', tier: 'free', strength: 'Fastest inference' },
|
|
43
|
+
together: { name: 'Together AI', models: 'Llama-3.3-70B-Instruct', tier: 'cheap', strength: 'Managed, reliable' },
|
|
44
|
+
ai21: { name: 'AI21', models: 'jamba-1.5-medium', tier: 'paid', strength: 'Jamba, long context' },
|
|
45
|
+
cohere: { name: 'Cohere', models: 'command-r7b', tier: 'cheap', strength: 'Command series, fast' },
|
|
46
|
+
minimax: { name: 'MiniMax', models: 'abab6.5s-chat', tier: 'cheap', strength: 'Chinese, cheap' },
|
|
47
|
+
kimi: { name: 'Kimi/Moonshot', models: 'moonshot-v1-8k', tier: 'cheap', strength: 'Chinese, 128k context' },
|
|
48
|
+
moonshot: { name: 'Moonshot', models: 'moonshot-v1-8k', tier: 'cheap', strength: 'Chinese, good' },
|
|
49
|
+
qwen: { name: 'Qwen', models: 'qwen-turbo', tier: 'cheap', strength: 'Alibaba, multilingual' },
|
|
50
|
+
zhipu: { name: 'Zhipu GLM', models: 'glm-4', tier: 'cheap', strength: 'Chinese, smart' },
|
|
51
|
+
yi: { name: 'Yi', models: 'yi-large', tier: 'cheap', strength: 'Chinese, good reasoning' },
|
|
52
|
+
baichuan: { name: 'Baichuan', models: 'baichuan-4', tier: 'cheap', strength: 'Chinese, balanced' },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function createInterface() {
|
|
56
|
+
return readline.createInterface({
|
|
57
|
+
input: process.stdin,
|
|
58
|
+
output: process.stdout
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function question(rl, text) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
rl.question(text, (answer) => resolve(answer));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function detectApiKeys() {
|
|
69
|
+
const detected = [];
|
|
70
|
+
for (const [envVar, providerId] of Object.entries(API_KEY_ENV_MAP)) {
|
|
71
|
+
if (process.env[envVar]) {
|
|
72
|
+
detected.push({ envVar, providerId, info: PROVIDER_INFO[providerId] });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return detected;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function runWizard() {
|
|
79
|
+
console.log('\n🔧 A3M Router Setup Wizard');
|
|
80
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
81
|
+
|
|
82
|
+
const rl = createInterface();
|
|
83
|
+
|
|
84
|
+
// Ensure config directory exists
|
|
85
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
86
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check for existing config
|
|
90
|
+
let existingConfig = {};
|
|
91
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
92
|
+
try {
|
|
93
|
+
existingConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
94
|
+
console.log('✓ Found existing config at', CONFIG_FILE);
|
|
95
|
+
console.log(' Providers:', Object.keys(existingConfig.providers || {}).join(', '));
|
|
96
|
+
console.log('');
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.log('⚠ Could not read existing config, starting fresh\n');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Auto-detect API keys
|
|
103
|
+
console.log('🔍 Scanning for API keys in environment...');
|
|
104
|
+
const detected = await detectApiKeys();
|
|
105
|
+
|
|
106
|
+
if (detected.length === 0) {
|
|
107
|
+
console.log('⚠ No API keys detected in environment.');
|
|
108
|
+
console.log(' Set any of: GROQ_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, etc.\n');
|
|
109
|
+
} else {
|
|
110
|
+
console.log('✓ Found', detected.length, 'API key(s):');
|
|
111
|
+
detected.forEach(({ envVar, providerId, info }) => {
|
|
112
|
+
console.log(' ✓', envVar, '→', info?.name || providerId);
|
|
113
|
+
});
|
|
114
|
+
console.log('');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Provider selection
|
|
118
|
+
const allProviders = Object.keys(PROVIDER_INFO);
|
|
119
|
+
const selected = new Set();
|
|
120
|
+
|
|
121
|
+
// Pre-select providers with detected keys
|
|
122
|
+
detected.forEach(({ providerId }) => selected.add(providerId));
|
|
123
|
+
|
|
124
|
+
console.log('📡 Select providers to configure (comma-separated numbers, or "all"):');
|
|
125
|
+
console.log('');
|
|
126
|
+
|
|
127
|
+
const numbered = allProviders.map((id, i) => ({ id, i }));
|
|
128
|
+
numbered.forEach(({ id, i }) => {
|
|
129
|
+
const info = PROVIDER_INFO[id];
|
|
130
|
+
const selected_mark = selected.has(id) ? '[x]' : '[ ]';
|
|
131
|
+
const tier_mark = info?.tier === 'free' ? '(FREE)' : info?.tier === 'cheap' ? '(cheap)' : '(paid)';
|
|
132
|
+
console.log(` ${String(i + 1).padStart(2)}. ${selected_mark} ${id.padEnd(12)} ${tier_mark} - ${info?.strength || ''}`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log('');
|
|
136
|
+
const answer = await question(rl, ' Enter numbers or "all" [all with keys detected]: ');
|
|
137
|
+
|
|
138
|
+
if (answer.toLowerCase().trim() === 'all') {
|
|
139
|
+
allProviders.forEach(id => selected.add(id));
|
|
140
|
+
} else if (answer.trim()) {
|
|
141
|
+
const nums = answer.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
|
|
142
|
+
nums.forEach(n => {
|
|
143
|
+
const idx = n - 1;
|
|
144
|
+
if (idx >= 0 && idx < allProviders.length) {
|
|
145
|
+
selected.add(allProviders[idx]);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log('\n✓ Selected providers:', Array.from(selected).join(', '));
|
|
151
|
+
|
|
152
|
+
// Build config
|
|
153
|
+
const config = {
|
|
154
|
+
version: '1.0',
|
|
155
|
+
providers: {}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
selected.forEach(providerId => {
|
|
159
|
+
const info = PROVIDER_INFO[providerId];
|
|
160
|
+
const envKey = Object.entries(API_KEY_ENV_MAP).find(([k, v]) => v === providerId)?.[0];
|
|
161
|
+
|
|
162
|
+
config.providers[providerId] = {
|
|
163
|
+
name: info?.name || providerId,
|
|
164
|
+
apiKey: envKey ? process.env[envKey] : '',
|
|
165
|
+
models: [info?.models || 'default'],
|
|
166
|
+
type: 'api',
|
|
167
|
+
enabled: true
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Save config
|
|
172
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
173
|
+
console.log('\n✓ Config saved to', CONFIG_FILE);
|
|
174
|
+
|
|
175
|
+
// Test connections
|
|
176
|
+
console.log('\n🧪 Testing connections...');
|
|
177
|
+
console.log(' (Skipped in wizard mode - run "npx a3m-router test" to verify)\n');
|
|
178
|
+
|
|
179
|
+
// Ready message
|
|
180
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
181
|
+
console.log('✅ A3M Router is ready!');
|
|
182
|
+
console.log('');
|
|
183
|
+
console.log(' Next steps:');
|
|
184
|
+
console.log(' 1. npx a3m-router serve # Start proxy server');
|
|
185
|
+
console.log(' 2. npx a3m-router test # Test provider connections');
|
|
186
|
+
console.log(' 3. npx a3m-router route "hi" # Try routing a query');
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log(' Docs: https://github.com/Das-rebel/adaptive-memory-multi-model-router');
|
|
189
|
+
console.log('');
|
|
190
|
+
|
|
191
|
+
rl.close();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = { runWizard };
|
|
@@ -357,6 +357,11 @@ export class ProviderRetryHandler {
|
|
|
357
357
|
isRetryableError(error: any): boolean {
|
|
358
358
|
if (!error) return false;
|
|
359
359
|
|
|
360
|
+
// Known non-retryable account/quota/policy states seen in Chinese provider APIs.
|
|
361
|
+
if (this.isPermanentProviderStateError(error)) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
|
|
360
365
|
const config = this.configs.get('default')!.retry;
|
|
361
366
|
const retryableErrors = config.retryableErrors || DEFAULT_RETRY_CONFIG.retryableErrors!;
|
|
362
367
|
|
|
@@ -382,6 +387,41 @@ export class ProviderRetryHandler {
|
|
|
382
387
|
return false;
|
|
383
388
|
}
|
|
384
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Detect hard non-retryable account/policy states.
|
|
392
|
+
* These should fail fast instead of wasting retries.
|
|
393
|
+
*/
|
|
394
|
+
private isPermanentProviderStateError(error: any): boolean {
|
|
395
|
+
const status = Number(error?.status ?? error?.statusCode ?? 0);
|
|
396
|
+
const message = String(error?.message || '').toLowerCase();
|
|
397
|
+
const code = String(error?.code || '').toLowerCase();
|
|
398
|
+
|
|
399
|
+
// Payment/balance/account states are generally non-retryable without human action.
|
|
400
|
+
if (status === 402) return true;
|
|
401
|
+
if (status === 401 || status === 403) return true;
|
|
402
|
+
|
|
403
|
+
const hardPatterns = [
|
|
404
|
+
'insufficient',
|
|
405
|
+
'balance',
|
|
406
|
+
'invalid api key',
|
|
407
|
+
'incorrect api key',
|
|
408
|
+
'access terminated',
|
|
409
|
+
'account abnormal',
|
|
410
|
+
'违规',
|
|
411
|
+
'quota exhausted',
|
|
412
|
+
'hour allocated quota exceeded',
|
|
413
|
+
'week allocated quota exceeded',
|
|
414
|
+
'month allocated quota exceeded',
|
|
415
|
+
'tpd rate limit'
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
for (const p of hardPatterns) {
|
|
419
|
+
if (message.includes(p) || code.includes(p)) return true;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
|
|
385
425
|
/**
|
|
386
426
|
* Check if error is a rate limit (429)
|
|
387
427
|
*/
|
|
@@ -575,4 +615,4 @@ export function getDefaultRetryHandler(): ProviderRetryHandler {
|
|
|
575
615
|
export {
|
|
576
616
|
DEFAULT_RETRY_CONFIG,
|
|
577
617
|
PROVIDER_CONTEXT_LIMITS,
|
|
578
|
-
};
|
|
618
|
+
};
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="780" height="520" viewBox="0 0 780 520">
|
|
2
|
-
<defs>
|
|
3
|
-
<linearGradient id="gpt4bar" x1="0" y1="0" x2="1" y2="0">
|
|
4
|
-
<stop offset="0%" stop-color="#f85149"/>
|
|
5
|
-
<stop offset="100%" stop-color="#da3633"/>
|
|
6
|
-
</linearGradient>
|
|
7
|
-
<linearGradient id="a3mbar" x1="0" y1="0" x2="1" y2="0">
|
|
8
|
-
<stop offset="0%" stop-color="#3fb950"/>
|
|
9
|
-
<stop offset="100%" stop-color="#238636"/>
|
|
10
|
-
</linearGradient>
|
|
11
|
-
<linearGradient id="header" x1="0" y1="0" x2="0" y2="1">
|
|
12
|
-
<stop offset="0%" stop-color="#161b22"/>
|
|
13
|
-
<stop offset="100%" stop-color="#0d1117"/>
|
|
14
|
-
</linearGradient>
|
|
15
|
-
</defs>
|
|
16
|
-
|
|
17
|
-
<!-- Background -->
|
|
18
|
-
<rect width="780" height="520" rx="12" fill="#0d1117" stroke="#30363d" stroke-width="1"/>
|
|
19
|
-
|
|
20
|
-
<!-- Header -->
|
|
21
|
-
<rect width="780" height="56" rx="12" fill="url(#header)"/>
|
|
22
|
-
<rect y="44" width="780" height="12" fill="url(#header)"/>
|
|
23
|
-
<text x="24" y="28" font-family="-apple-system,system-ui,sans-serif" font-size="18" fill="#c9d1d9" font-weight="700">💰 Cost Comparison</text>
|
|
24
|
-
<text x="24" y="46" font-family="-apple-system,system-ui,sans-serif" font-size="12" fill="#8b949e">10,000 queries/month · Real provider pricing · Mixed query complexity</text>
|
|
25
|
-
|
|
26
|
-
<!-- Legend -->
|
|
27
|
-
<rect x="580" y="16" width="14" height="14" rx="3" fill="url(#gpt4bar)"/>
|
|
28
|
-
<text x="600" y="27" font-family="-apple-system,system-ui,sans-serif" font-size="12" fill="#f85149">All → GPT-4o</text>
|
|
29
|
-
<rect x="680" y="16" width="14" height="14" rx="3" fill="url(#a3mbar)"/>
|
|
30
|
-
<text x="700" y="27" font-family="-apple-system,system-ui,sans-serif" font-size="12" fill="#3fb950">A3M Routed</text>
|
|
31
|
-
|
|
32
|
-
<!-- Chart rows -->
|
|
33
|
-
<g font-family="'SF Mono','Fira Code',monospace" font-size="12">
|
|
34
|
-
|
|
35
|
-
<!-- Row 1: Simple Q&A -->
|
|
36
|
-
<text x="24" y="82" fill="#8b949e">Simple Q&A</text>
|
|
37
|
-
<text x="24" y="96" fill="#484f58" font-size="10">47% of traffic · "What is 2+2?"</text>
|
|
38
|
-
<rect x="140" y="78" width="200" height="20" rx="4" fill="url(#gpt4bar)" opacity="0.9">
|
|
39
|
-
<animate attributeName="width" from="0" to="200" dur="0.6s" fill="freeze"/>
|
|
40
|
-
</rect>
|
|
41
|
-
<text x="346" y="93" fill="#f85149" font-size="11">$4.94</text>
|
|
42
|
-
<rect x="140" y="100" width="2" height="14" rx="2" fill="url(#a3mbar)">
|
|
43
|
-
<animate attributeName="width" from="0" to="2" dur="0.4s" fill="freeze"/>
|
|
44
|
-
</rect>
|
|
45
|
-
<text x="148" y="112" fill="#3fb950" font-size="11">$0.00 → CommandCode (FREE)</text>
|
|
46
|
-
<text x="720" y="96" fill="#3fb950" font-weight="bold" font-size="13">100% ✓</text>
|
|
47
|
-
|
|
48
|
-
<!-- Row 2: Code generation -->
|
|
49
|
-
<text x="24" y="148" fill="#8b949e">Code generation</text>
|
|
50
|
-
<text x="24" y="162" fill="#484f58" font-size="10">15% of traffic · "Write Python sort"</text>
|
|
51
|
-
<rect x="140" y="144" width="198" height="20" rx="4" fill="url(#gpt4bar)" opacity="0.9">
|
|
52
|
-
<animate attributeName="width" from="0" to="198" dur="0.6s" begin="0.15s" fill="freeze"/>
|
|
53
|
-
</rect>
|
|
54
|
-
<text x="344" y="159" fill="#f85149" font-size="11">$4.88</text>
|
|
55
|
-
<rect x="140" y="166" width="7" height="14" rx="2" fill="url(#a3mbar)">
|
|
56
|
-
<animate attributeName="width" from="0" to="7" dur="0.4s" begin="0.15s" fill="freeze"/>
|
|
57
|
-
</rect>
|
|
58
|
-
<text x="153" y="178" fill="#3fb950" font-size="11">$0.17 → DeepSeek v3</text>
|
|
59
|
-
<text x="720" y="162" fill="#3fb950" font-weight="bold" font-size="13">97% ✓</text>
|
|
60
|
-
|
|
61
|
-
<!-- Row 3: Summarization -->
|
|
62
|
-
<text x="24" y="214" fill="#8b949e">Summarization</text>
|
|
63
|
-
<text x="24" y="228" fill="#484f58" font-size="10">18% of traffic · "Summarize this doc"</text>
|
|
64
|
-
<rect x="140" y="210" width="292" height="20" rx="4" fill="url(#gpt4bar)" opacity="0.9">
|
|
65
|
-
<animate attributeName="width" from="0" to="292" dur="0.6s" begin="0.3s" fill="freeze"/>
|
|
66
|
-
</rect>
|
|
67
|
-
<text x="438" y="225" fill="#f85149" font-size="11">$7.20</text>
|
|
68
|
-
<rect x="140" y="232" width="17" height="14" rx="2" fill="url(#a3mbar)">
|
|
69
|
-
<animate attributeName="width" from="0" to="17" dur="0.4s" begin="0.3s" fill="freeze"/>
|
|
70
|
-
</rect>
|
|
71
|
-
<text x="163" y="244" fill="#3fb950" font-size="11">$0.43 → GPT-4o-mini</text>
|
|
72
|
-
<text x="720" y="228" fill="#3fb950" font-weight="bold" font-size="13">94% ✓</text>
|
|
73
|
-
|
|
74
|
-
<!-- Row 4: Complex reasoning -->
|
|
75
|
-
<text x="24" y="280" fill="#8b949e">Complex reasoning</text>
|
|
76
|
-
<text x="24" y="294" fill="#484f58" font-size="10">12% of traffic · "Analyze economics of..."</text>
|
|
77
|
-
<rect x="140" y="276" width="352" height="20" rx="4" fill="url(#gpt4bar)" opacity="0.9">
|
|
78
|
-
<animate attributeName="width" from="0" to="352" dur="0.6s" begin="0.45s" fill="freeze"/>
|
|
79
|
-
</rect>
|
|
80
|
-
<text x="498" y="291" fill="#f85149" font-size="11">$8.70</text>
|
|
81
|
-
<rect x="140" y="298" width="136" height="14" rx="2" fill="url(#a3mbar)">
|
|
82
|
-
<animate attributeName="width" from="0" to="136" dur="0.4s" begin="0.45s" fill="freeze"/>
|
|
83
|
-
</rect>
|
|
84
|
-
<text x="282" y="310" fill="#3fb950" font-size="11">$3.36 → Claude Haiku</text>
|
|
85
|
-
<text x="720" y="294" fill="#ffa657" font-weight="bold" font-size="13">61% ✓</text>
|
|
86
|
-
|
|
87
|
-
<!-- Row 5: Expert analysis -->
|
|
88
|
-
<text x="24" y="346" fill="#8b949e">Expert analysis</text>
|
|
89
|
-
<text x="24" y="360" fill="#484f58" font-size="10">8% of traffic · "Legal contract review"</text>
|
|
90
|
-
<rect x="140" y="342" width="340" height="20" rx="4" fill="url(#gpt4bar)" opacity="0.9">
|
|
91
|
-
<animate attributeName="width" from="0" to="340" dur="0.6s" begin="0.6s" fill="freeze"/>
|
|
92
|
-
</rect>
|
|
93
|
-
<text x="486" y="357" fill="#f85149" font-size="11">$8.40</text>
|
|
94
|
-
<rect x="140" y="364" width="340" height="14" rx="2" fill="url(#a3mbar)" opacity="0.5">
|
|
95
|
-
<animate attributeName="width" from="0" to="340" dur="0.4s" begin="0.6s" fill="freeze"/>
|
|
96
|
-
</rect>
|
|
97
|
-
<text x="486" y="376" fill="#8b949e" font-size="11">$8.40 → GPT-4o (no shortcut)</text>
|
|
98
|
-
<text x="720" y="360" fill="#8b949e" font-weight="bold" font-size="13">0%</text>
|
|
99
|
-
</g>
|
|
100
|
-
|
|
101
|
-
<!-- Divider -->
|
|
102
|
-
<line x1="24" y1="396" x2="756" y2="396" stroke="#30363d" stroke-width="1"/>
|
|
103
|
-
|
|
104
|
-
<!-- Total -->
|
|
105
|
-
<g font-family="-apple-system,system-ui,sans-serif">
|
|
106
|
-
<text x="24" y="418" fill="#c9d1d9" font-size="16" font-weight="700">Total: 10,000 queries</text>
|
|
107
|
-
<text x="380" y="418" fill="#f85149" font-size="16" font-weight="700">$34.11/mo</text>
|
|
108
|
-
<text x="470" y="418" fill="#484f58" font-size="16">→</text>
|
|
109
|
-
<text x="498" y="418" fill="#3fb950" font-size="16" font-weight="700">$12.36/mo</text>
|
|
110
|
-
<text x="600" y="418" fill="#3fb950" font-size="20" font-weight="800">Save 64%</text>
|
|
111
|
-
</g>
|
|
112
|
-
|
|
113
|
-
<!-- Scale projections -->
|
|
114
|
-
<g font-family="'SF Mono','Fira Code',monospace" font-size="11">
|
|
115
|
-
<text x="24" y="448" fill="#8b949e">At scale:</text>
|
|
116
|
-
<text x="24" y="468" fill="#484f58"> 100K queries/mo</text>
|
|
117
|
-
<text x="200" y="468" fill="#f85149">$341</text>
|
|
118
|
-
<text x="240" y="468" fill="#484f58">→</text>
|
|
119
|
-
<text x="260" y="468" fill="#3fb950">$124</text>
|
|
120
|
-
<text x="310" y="468" fill="#3fb950">save $218/mo</text>
|
|
121
|
-
|
|
122
|
-
<text x="420" y="468" fill="#484f58"> 1M queries/mo</text>
|
|
123
|
-
<text x="580" y="468" fill="#f85149">$3,411</text>
|
|
124
|
-
<text x="630" y="468" fill="#484f58">→</text>
|
|
125
|
-
<text x="650" y="468" fill="#3fb950">$1,236</text>
|
|
126
|
-
<text x="700" y="468" fill="#3fb950">save $2,175/mo</text>
|
|
127
|
-
</g>
|
|
128
|
-
|
|
129
|
-
<!-- Bottom note -->
|
|
130
|
-
<rect x="16" y="488" width="748" height="24" rx="4" fill="#161b22"/>
|
|
131
|
-
<text x="390" y="504" text-anchor="middle" font-family="-apple-system,system-ui,sans-serif" font-size="11" fill="#8b949e">
|
|
132
|
-
Based on real provider pricing from providerConfig.ts · RouteLLM query distribution (47% simple) · Savings vary by usage
|
|
133
|
-
</text>
|
|
134
|
-
</svg>
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="280" viewBox="0 0 600 280">
|
|
2
|
-
<style>
|
|
3
|
-
.label { font-family: 'SF Mono', monospace; font-size: 12px; fill: #8b949e; }
|
|
4
|
-
.value { font-family: 'SF Mono', monospace; font-size: 14px; font-weight: bold; }
|
|
5
|
-
.title { font-family: -apple-system, system-ui, sans-serif; font-size: 16px; fill: #c9d1d9; font-weight: 600; }
|
|
6
|
-
.subtitle { font-family: -apple-system, system-ui, sans-serif; font-size: 12px; fill: #8b949e; }
|
|
7
|
-
</style>
|
|
8
|
-
|
|
9
|
-
<!-- Background -->
|
|
10
|
-
<rect width="600" height="280" rx="12" fill="#0d1117" stroke="#30363d" stroke-width="1"/>
|
|
11
|
-
|
|
12
|
-
<!-- Title -->
|
|
13
|
-
<text x="300" y="30" text-anchor="middle" class="title">📈 Downloads Over 3 Days</text>
|
|
14
|
-
<text x="300" y="48" text-anchor="middle" class="subtitle">Zero marketing budget. Pure npm keyword discovery + word-of-mouth.</text>
|
|
15
|
-
|
|
16
|
-
<!-- Chart area -->
|
|
17
|
-
<g transform="translate(80, 70)">
|
|
18
|
-
<!-- Grid lines -->
|
|
19
|
-
<line x1="0" y1="0" x2="440" y2="0" stroke="#21262d" stroke-width="1"/>
|
|
20
|
-
<line x1="0" y1="40" x2="440" y2="40" stroke="#21262d" stroke-width="1" stroke-dasharray="4"/>
|
|
21
|
-
<line x1="0" y1="80" x2="440" y2="80" stroke="#21262d" stroke-width="1" stroke-dasharray="4"/>
|
|
22
|
-
<line x1="0" y1="120" x2="440" y2="120" stroke="#21262d" stroke-width="1" stroke-dasharray="4"/>
|
|
23
|
-
<line x1="0" y1="160" x2="440" y2="160" stroke="#21262d" stroke-width="1"/>
|
|
24
|
-
|
|
25
|
-
<!-- Y-axis labels -->
|
|
26
|
-
<text x="-10" y="4" text-anchor="end" class="label">2000</text>
|
|
27
|
-
<text x="-10" y="44" text-anchor="end" class="label">1500</text>
|
|
28
|
-
<text x="-10" y="84" text-anchor="end" class="label">1000</text>
|
|
29
|
-
<text x="-10" y="124" text-anchor="end" class="label">500</text>
|
|
30
|
-
<text x="-10" y="164" text-anchor="end" class="label">0</text>
|
|
31
|
-
|
|
32
|
-
<!-- Area fill -->
|
|
33
|
-
<polygon points="73,116 220,134 367,8 367,160 73,160" fill="#238636" fill-opacity="0.15">
|
|
34
|
-
<animate attributeName="fill-opacity" from="0" to="0.15" dur="1s" fill="freeze"/>
|
|
35
|
-
</polygon>
|
|
36
|
-
|
|
37
|
-
<!-- Line -->
|
|
38
|
-
<polyline points="73,116 220,134 367,8" fill="none" stroke="#3fb950" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
|
|
39
|
-
<animate attributeName="stroke-dashoffset" from="400" to="0" dur="1.5s" fill="freeze"/>
|
|
40
|
-
<set attributeName="stroke-dasharray" to="400"/>
|
|
41
|
-
</polyline>
|
|
42
|
-
|
|
43
|
-
<!-- Data points -->
|
|
44
|
-
<circle cx="73" cy="116" r="6" fill="#0d1117" stroke="#3fb950" stroke-width="3">
|
|
45
|
-
<animate attributeName="r" from="0" to="6" dur="0.3s" begin="0.5s" fill="freeze"/>
|
|
46
|
-
</circle>
|
|
47
|
-
<circle cx="220" cy="134" r="6" fill="#0d1117" stroke="#f0883e" stroke-width="3">
|
|
48
|
-
<animate attributeName="r" from="0" to="6" dur="0.3s" begin="1s" fill="freeze"/>
|
|
49
|
-
</circle>
|
|
50
|
-
<circle cx="367" cy="8" r="6" fill="#0d1117" stroke="#3fb950" stroke-width="3">
|
|
51
|
-
<animate attributeName="r" from="0" to="6" dur="0.3s" begin="1.5s" fill="freeze"/>
|
|
52
|
-
</circle>
|
|
53
|
-
|
|
54
|
-
<!-- Value labels -->
|
|
55
|
-
<text x="73" y="106" text-anchor="middle" class="value" fill="#c9d1d9">552</text>
|
|
56
|
-
<text x="220" y="152" text-anchor="middle" class="value" fill="#f0883e">320</text>
|
|
57
|
-
<text x="367" y="-4" text-anchor="middle" class="value" fill="#3fb950">1,903</text>
|
|
58
|
-
|
|
59
|
-
<!-- X-axis labels -->
|
|
60
|
-
<text x="73" y="180" text-anchor="middle" class="label">Day 1</text>
|
|
61
|
-
<text x="73" y="194" text-anchor="middle" class="subtitle">May 15</text>
|
|
62
|
-
<text x="220" y="180" text-anchor="middle" class="label">Day 2</text>
|
|
63
|
-
<text x="220" y="194" text-anchor="middle" class="subtitle">May 16</text>
|
|
64
|
-
<text x="367" y="180" text-anchor="middle" class="label">Day 3</text>
|
|
65
|
-
<text x="367" y="194" text-anchor="middle" class="subtitle">May 17</text>
|
|
66
|
-
|
|
67
|
-
<!-- Annotations -->
|
|
68
|
-
<text x="147" y="105" text-anchor="middle" class="subtitle" fill="#f0883e">-42%</text>
|
|
69
|
-
<text x="293" y="60" text-anchor="middle" class="subtitle" fill="#3fb950">+495%</text>
|
|
70
|
-
</g>
|
|
71
|
-
|
|
72
|
-
<!-- Bottom stats -->
|
|
73
|
-
<text x="150" y="270" text-anchor="middle" class="value" fill="#3fb950">2,775 total</text>
|
|
74
|
-
<text x="300" y="270" text-anchor="middle" class="value" fill="#79c0ff">245% growth</text>
|
|
75
|
-
<text x="450" y="270" text-anchor="middle" class="value" fill="#d2a8ff">$0 budget</text>
|
|
76
|
-
</svg>
|