adaptive-memory-multi-model-router 2.13.18 → 2.13.22

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 (91) hide show
  1. package/.dockerignore +82 -0
  2. package/.env.example +303 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +83 -12
  4. package/.github/ISSUE_TEMPLATE/config.yml +12 -6
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +61 -10
  6. package/.github/PULL_REQUEST_TEMPLATE.md +53 -26
  7. package/.github/dependabot.yml +9 -0
  8. package/.github/workflows/codeql.yml +38 -0
  9. package/.github/workflows/npm-publish.yml +20 -0
  10. package/.github/workflows/stale.yml +56 -0
  11. package/ARCHITECTURE.md +346 -0
  12. package/AUDIT_REPORT.md +28 -0
  13. package/CHANGELOG.md +386 -22
  14. package/CONTRIBUTORS.md +20 -0
  15. package/Dockerfile +53 -0
  16. package/Dockerfile.proxy +33 -0
  17. package/PR_STATUS_REPORT.md +148 -0
  18. package/README.md +22 -0
  19. package/RUNKIT.md +83 -0
  20. package/_schema.html +61 -15
  21. package/articles/AI_AGENT_LLM_ROUTING.md +150 -0
  22. package/articles/FROM_ZERO_TO_10K.md +107 -0
  23. package/articles/LLM_BENCHMARK_DEEP_DIVE.md +153 -0
  24. package/articles/TWEETS_10K_DOWNLOADS.md +47 -0
  25. package/articles/TWEETS_BENCHMARK_FIRST.md +46 -0
  26. package/articles/TWEETS_MCP_PLAY.md +51 -0
  27. package/articles/TWEETS_SEQUENTIAL_BROKEN.md +49 -0
  28. package/articles/TWEETS_WHY_BUILD.md +54 -0
  29. package/benchmark-results.json +26 -45
  30. package/cli/a3m +840 -0
  31. package/demo/package.json +13 -0
  32. package/demo/public/index.html +762 -0
  33. package/demo/server.js +405 -0
  34. package/dist/cli.js +4 -0
  35. package/docker-compose.yml +74 -0
  36. package/docs/.nojekyll +0 -0
  37. package/docs/BENCHMARK.md +96 -22
  38. package/docs/_config.yml +49 -0
  39. package/docs/api.html +513 -0
  40. package/docs/benchmark.html +387 -0
  41. package/docs/cli-cheatsheet.md +339 -0
  42. package/docs/comparison.md +108 -0
  43. package/docs/curl-examples.md +247 -0
  44. package/docs/index.html +390 -99
  45. package/docs/openapi.yaml +1318 -0
  46. package/docs/quick-start.html +366 -0
  47. package/docs/robots.txt +1 -1
  48. package/docs/sitemap.xml +23 -5
  49. package/docs/styles.css +682 -0
  50. package/examples/README.md +61 -0
  51. package/examples/a3m-sdk.js +124 -0
  52. package/examples/basic-route.js +54 -0
  53. package/examples/chat-loop.js +202 -0
  54. package/examples/classify-then-route.js +102 -0
  55. package/examples/cost-compare.js +120 -0
  56. package/examples/ensemble.js +160 -0
  57. package/integrations/langchain/README.md +216 -0
  58. package/integrations/langchain/a3m_langchain.ts +1360 -0
  59. package/integrations/langchain/example.ts +287 -0
  60. package/integrations/vercel-ai-sdk/README.md +49 -0
  61. package/integrations/vercel-ai-sdk/a3m_provider.ts +78 -0
  62. package/integrations/vercel-ai-sdk/example.ts +25 -0
  63. package/llms-full.txt +43 -0
  64. package/llms.txt +9 -0
  65. package/mcp-server/README.md +188 -0
  66. package/mcp-server/package.json +29 -0
  67. package/mcp-server/src/index.ts +744 -0
  68. package/mcp-server/tsconfig.json +19 -0
  69. package/package.json +3 -3
  70. package/proxy/README.md +227 -0
  71. package/proxy/package-lock.json +831 -0
  72. package/proxy/package.json +17 -0
  73. package/proxy/rate-limit.js +145 -0
  74. package/proxy/rate-limit.test.js +311 -0
  75. package/proxy/server.js +970 -0
  76. package/scripts/banner.js +29 -0
  77. package/scripts/compare-providers.sh +230 -0
  78. package/scripts/cross_post.py +443 -0
  79. package/scripts/publish_fcc.py +106 -0
  80. package/scripts/push-to-gitee.sh +52 -0
  81. package/src/tui/dashboard.ts +13 -0
  82. package/tests/__mocks__/tokenUtils.ts +22 -0
  83. package/tests/memory/episodicMemory.test.ts +227 -0
  84. package/tests/package-lock.json +1628 -0
  85. package/tests/package.json +18 -0
  86. package/tests/routing/ensembleVoting.test.ts +236 -0
  87. package/tests/routing/providerRetry.test.ts +360 -0
  88. package/tests/routing/queryTypePresets.test.ts +206 -0
  89. package/tests/tsconfig.json +21 -0
  90. package/tests/vitest.config.ts +18 -0
  91. package/.env +0 -2
package/demo/server.js ADDED
@@ -0,0 +1,405 @@
1
+ /**
2
+ * A3M Router Demo Server
3
+ *
4
+ * Interactive web demo showing parallel multi-LLM routing.
5
+ * Works without API keys — falls back to mock results.
6
+ */
7
+
8
+ import express from "express";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+
14
+ // ============================================================
15
+ // Lazy-load A3M Router (fail gracefully if not installed locally)
16
+ // ============================================================
17
+ let a3m;
18
+ try {
19
+ a3m = await import("adaptive-memory-multi-model-router");
20
+ } catch {
21
+ console.warn("[demo] A3M Router not installed locally — using file:// import");
22
+ // Try relative path for development
23
+ try {
24
+ a3m = await import("../dist/index.js");
25
+ } catch {
26
+ a3m = null;
27
+ }
28
+ }
29
+
30
+ // ============================================================
31
+ // Fallback responses for when no real API keys are configured
32
+ // ============================================================
33
+ const MOCK_RESPONSES = {
34
+ "Write a haiku about Kubernetes": {
35
+ content: `Pods drift through the cloud\nOrchestrator hums softly\nContainers at rest`,
36
+ model: "groq/llama-3.3-70b-versatile",
37
+ provider: "Groq",
38
+ },
39
+ "Explain quantum computing to a 5-year-old": {
40
+ content: `Imagine you have a magical coin. Normal coins are either heads OR tails. But a magical quantum coin can be heads AND tails at the same time! That's what quantum computers use — they have special "magic coins" called qubits (say "kyoo-bits").
41
+
42
+ While a regular computer checks one answer at a time (like trying every key on a keychain), a quantum computer can check ALL the answers at once. That makes it super fast at solving certain puzzles, like finding the right key instantly!`,
43
+ model: "anthropic/claude-sonnet-4-20250514",
44
+ provider: "Anthropic",
45
+ },
46
+ "Write a React component for a search bar": {
47
+ content: `\`\`\`jsx
48
+ import React, { useState, useCallback } from 'react';
49
+
50
+ function SearchBar({ onSearch, placeholder = "Search..." }) {
51
+ const [query, setQuery] = useState("");
52
+ const [isFocused, setIsFocused] = useState(false);
53
+
54
+ const handleChange = useCallback((e) => {
55
+ setQuery(e.target.value);
56
+ }, []);
57
+
58
+ const handleSubmit = useCallback((e) => {
59
+ e.preventDefault();
60
+ if (query.trim()) onSearch(query.trim());
61
+ }, [query, onSearch]);
62
+
63
+ const handleClear = useCallback(() => {
64
+ setQuery("");
65
+ }, []);
66
+
67
+ return (
68
+ <form
69
+ onSubmit={handleSubmit}
70
+ style={{
71
+
72
+ display: "flex",
73
+ alignItems: "center",
74
+ background: isFocused ? "#fff" : "#f5f5f5",
75
+ border: \`2px solid \${isFocused ? "#6366f1" : "#e5e7eb"}\`,
76
+ borderRadius: "12px",
77
+ padding: "8px 16px",
78
+ transition: "all 0.2s ease",
79
+ maxWidth: "500px",
80
+ }}
81
+ >
82
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" style={{ marginRight: 8 }}>
83
+ <path d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" fill="#9ca3af"/>
84
+ </svg>
85
+ <input
86
+ type="text"
87
+ value={query}
88
+ onChange={handleChange}
89
+ onFocus={() => setIsFocused(true)}
90
+ onBlur={() => setIsFocused(false)}
91
+ placeholder={placeholder}
92
+ style={{
93
+ flex: 1,
94
+ border: "none",
95
+ outline: "none",
96
+ background: "transparent",
97
+ fontSize: "16px",
98
+ padding: "4px 0",
99
+ color: "#1f2937",
100
+ }}
101
+ />
102
+ {query && (
103
+ <button
104
+ type="button"
105
+ onClick={handleClear}
106
+ style={{
107
+ background: "none",
108
+ border: "none",
109
+ cursor: "pointer",
110
+ padding: 4,
111
+ color: "#9ca3af",
112
+ }}
113
+ aria-label="Clear search"
114
+ >
115
+
116
+ </button>
117
+ )}
118
+ <button
119
+ type="submit"
120
+ style={{
121
+ background: "#6366f1",
122
+ color: "#fff",
123
+ border: "none",
124
+ borderRadius: "8px",
125
+ padding: "8px 16px",
126
+ marginLeft: 8,
127
+ cursor: "pointer",
128
+ fontWeight: 600,
129
+ fontSize: "14px",
130
+ }}
131
+ >
132
+ Search
133
+ </button>
134
+ </form>
135
+ );
136
+ }
137
+
138
+ export default SearchBar;
139
+ \`\`\``,
140
+ model: "openai/gpt-4o",
141
+ provider: "OpenAI",
142
+ },
143
+ };
144
+
145
+ const DEFAULT_MOCK = {
146
+ content:
147
+ "A3M Router selected the optimal model for your query. Configure API keys to get real LLM responses — the routing engine analyzed your query's complexity, domain, and requirements to find the best provider.",
148
+ model: "a3m-routing/auto",
149
+ provider: "A3M Routing Engine",
150
+ };
151
+
152
+ // ============================================================
153
+ // Cost calculator (based on A3M Router token costs)
154
+ // ============================================================
155
+ const MODEL_COSTS = {
156
+ "groq/llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
157
+ "anthropic/claude-sonnet-4-20250514": { input: 3.0, output: 15.0 },
158
+ "openai/gpt-4o": { input: 2.5, output: 10.0 },
159
+ };
160
+
161
+ function estimateCost(model, content) {
162
+ const tokens = Math.ceil(content.length / 4); // rough estimate
163
+ const costs = MODEL_COSTS[model] || { input: 0.5, output: 0.5 };
164
+ return ((tokens / 1_000_000) * (costs.input + costs.output)).toFixed(6);
165
+ }
166
+
167
+ // ============================================================
168
+ // Express App
169
+ // ============================================================
170
+ const app = express();
171
+ app.use(express.json());
172
+ app.use(express.static(path.join(__dirname, "public")));
173
+
174
+ // ── GET /api/providers ───────────────────────────────────
175
+ app.get("/api/providers", (req, res) => {
176
+ let providers = {};
177
+ let hasRealKeys = false;
178
+
179
+ if (a3m) {
180
+ try {
181
+ providers = a3m.getAvailableProviders();
182
+ hasRealKeys = Object.keys(providers).length > 0;
183
+ } catch {
184
+ providers = {};
185
+ }
186
+ }
187
+
188
+ // Build a clean list of what's available
189
+ const list = Object.entries(providers).map(([id, p]) => ({
190
+ id,
191
+ name: p.name,
192
+ tier: p.tier,
193
+ models: p.models?.length || 0,
194
+ type: p.type,
195
+ costPerM: p.costPerK
196
+ ? { input: p.costPerK.input, output: p.costPerK.output }
197
+ : { input: 0, output: 0 },
198
+ }));
199
+
200
+ res.json({
201
+ count: list.length,
202
+ hasApiKeys: hasRealKeys,
203
+ providers: list,
204
+ note: hasRealKeys
205
+ ? "API keys detected — real LLM responses enabled"
206
+ : "No API keys configured — showing simulated responses",
207
+ });
208
+ });
209
+
210
+ // ── POST /api/analyze ───────────────────────────────────
211
+ app.post("/api/analyze", (req, res) => {
212
+ const { prompt } = req.body;
213
+ if (!prompt || typeof prompt !== "string") {
214
+ return res.status(400).json({ error: "prompt is required" });
215
+ }
216
+
217
+ let features = null;
218
+ let routing = null;
219
+
220
+ if (a3m) {
221
+ try {
222
+ features = a3m.extractQueryFeatures(prompt);
223
+ routing = a3m.routeQuery(prompt);
224
+ } catch {
225
+ // fallback below
226
+ }
227
+ }
228
+
229
+ if (!features) {
230
+ const complexity = Math.min(1, 0.15 + prompt.length / 500);
231
+ features = {
232
+ complexity,
233
+ length: prompt.split(/\s+/).length,
234
+ has_code: /function|class |def |import|<[a-z]+|const |let |var /i.test(prompt),
235
+ has_math: /\d+[\+\-\*\/=]|calculate|sum|equation/i.test(prompt),
236
+ requires_reasoning: /why|explain|analyze|compare|reason/i.test(prompt),
237
+ is_creative: /write|story|poem|create|imagine/i.test(prompt),
238
+ detected_domain: "general",
239
+ };
240
+ }
241
+
242
+ if (!routing) {
243
+ routing = {
244
+ primary_model: "a3m/auto-routed",
245
+ confidence: 0.85,
246
+ reasoning: "Intelligent routing based on query complexity and domain",
247
+ estimated_cost: "0.000001",
248
+ estimated_latency_ms: 420,
249
+ };
250
+ }
251
+
252
+ res.json({ features, routing });
253
+ });
254
+
255
+ // ── POST /api/route ────────────────────────────────────
256
+ app.post("/api/route", async (req, res) => {
257
+ const { prompt, mode } = req.body;
258
+ if (!prompt || typeof prompt !== "string") {
259
+ return res.status(400).json({ error: "prompt is required" });
260
+ }
261
+
262
+ const startTime = Date.now();
263
+ const isEnsemble = mode === "ensemble";
264
+
265
+ try {
266
+ // 1) Route the query using A3M Router
267
+ let routing = null;
268
+ let features = null;
269
+
270
+ if (a3m) {
271
+ try {
272
+ routing = a3m.routeQuery(prompt);
273
+ features = a3m.extractQueryFeatures(prompt);
274
+ } catch {
275
+ // fallback
276
+ }
277
+ }
278
+
279
+ // 2) Try real LLM execution via proxy server
280
+ let content = null;
281
+ let provider = null;
282
+ let model = null;
283
+ let usedRealProvider = false;
284
+
285
+ // Check if we have any API keys configured
286
+ let availableProviders = {};
287
+ if (a3m) {
288
+ try {
289
+ availableProviders = a3m.getAvailableProviders();
290
+ } catch {}
291
+ }
292
+
293
+ const hasRealKeys = Object.keys(availableProviders).length > 0;
294
+
295
+ if (hasRealKeys && a3m) {
296
+ try {
297
+ // Try routing via proxy
298
+ const modelName = "auto";
299
+ const messages = [{ role: "user", content: prompt }];
300
+
301
+ const modelMapper = await import("../dist/server/modelMapper.js");
302
+ const mapping = modelMapper.resolveModel(modelName, prompt);
303
+ if (mapping && mapping.apiKey) {
304
+ const baseUrl = mapping.baseUrl;
305
+ const body = {
306
+ model: mapping.model,
307
+ messages,
308
+ max_tokens: 512,
309
+ };
310
+ const headers = { "Content-Type": "application/json" };
311
+ if (mapping.apiKey) headers["Authorization"] = `Bearer ${mapping.apiKey}`;
312
+
313
+ const resp = await fetch(baseUrl, {
314
+ method: "POST",
315
+ headers,
316
+ body: JSON.stringify(body),
317
+ });
318
+ const data = await resp.json();
319
+ if (data.choices?.[0]?.message?.content) {
320
+ content = data.choices[0].message.content;
321
+ provider = mapping.providerId;
322
+ model = mapping.model;
323
+ usedRealProvider = true;
324
+ }
325
+ }
326
+ } catch (e) {
327
+ console.warn("[demo] Real provider call failed:", e.message);
328
+ }
329
+ }
330
+
331
+ // 3) Fallback to mock if no real response
332
+ if (!content) {
333
+ const mockKey = Object.keys(MOCK_RESPONSES).find((k) => prompt.includes(k));
334
+ const mock = mockKey ? MOCK_RESPONSES[mockKey] : DEFAULT_MOCK;
335
+ content = mock.content;
336
+ provider = mock.provider;
337
+ model = mock.model;
338
+ }
339
+
340
+ // 4) Ensemble mode: return multiple responses
341
+ let responses = null;
342
+ if (isEnsemble) {
343
+ const providers = ["Groq", "Anthropic", "OpenAI"];
344
+ responses = providers.map((p) => ({
345
+ provider: p,
346
+ model: p === "Groq"
347
+ ? "llama-3.3-70b-versatile"
348
+ : p === "Anthropic"
349
+ ? "claude-sonnet-4-20250514"
350
+ : "gpt-4o",
351
+ content: `[Simulated response from ${p}] A3M Router selected ${p} for optimal performance on your query. Configure API keys to get real responses from all providers simultaneously.`,
352
+ latency: Math.floor(Math.random() * 400 + 200),
353
+ cost: (Math.random() * 0.002).toFixed(6),
354
+ }));
355
+ }
356
+
357
+ const latency = Date.now() - startTime;
358
+ const cost = usedRealProvider
359
+ ? estimateCost(model, content)
360
+ : "0.000001";
361
+
362
+ res.json({
363
+ result: content,
364
+ provider,
365
+ model,
366
+ mode: isEnsemble ? "ensemble" : "single",
367
+ latency,
368
+ cost,
369
+ usedMock: !usedRealProvider,
370
+ features,
371
+ routing,
372
+ responses,
373
+ hasRealKeys,
374
+ });
375
+ } catch (err) {
376
+ console.error("[demo] Route error:", err);
377
+ res.status(500).json({
378
+ error: err.message,
379
+ result: "A3M Router encountered an error processing your query.",
380
+ provider: "A3M Router",
381
+ model: "error",
382
+ latency: Date.now() - startTime,
383
+ cost: "0",
384
+ usedMock: true,
385
+ });
386
+ }
387
+ });
388
+
389
+ // ── Serve index.html for all other routes ──────────────
390
+ app.get("*", (req, res) => {
391
+ res.sendFile(path.join(__dirname, "public", "index.html"));
392
+ });
393
+
394
+ // ── Start ──────────────────────────────────────────────
395
+ const PORT = process.env.PORT || 3000;
396
+ app.listen(PORT, () => {
397
+ console.log(`
398
+ A3M Router Demo Server
399
+ ─────────────────────────
400
+ Running: http://localhost:${PORT}
401
+ API Key: ${process.env.OPENAI_API_KEY ? "Yes" : "No (mock mode)"}
402
+ Providers: ${a3m ? "A3M SDK loaded" : "A3M SDK not available"}
403
+ Mode: ${process.env.OPENAI_API_KEY || process.env.GROQ_API_KEY ? "Live" : "Demo (mock responses)"}
404
+ `);
405
+ });
package/dist/cli.js CHANGED
@@ -154,6 +154,10 @@ async function callProvider(providerId, model, prompt, maxTokens) {
154
154
  // ============================================================
155
155
 
156
156
  async function main() {
157
+ // Show banner
158
+ const banner = require('./scripts/banner.js');
159
+ process.stdout.write(banner);
160
+
157
161
  const router = createA3MRouter({ memory: { maxSize: 1000 } });
158
162
 
159
163
  switch (command) {
@@ -0,0 +1,74 @@
1
+ # =============================================================================
2
+ # A3M Router — Docker Compose
3
+ # Runs the main router service + OpenAI-compatible proxy side-by-side
4
+ # =============================================================================
5
+
6
+ version: "3.9"
7
+
8
+ services:
9
+
10
+ # ---------------------------------------------------------------------------
11
+ # A3M Router — Main API & health endpoint
12
+ # ---------------------------------------------------------------------------
13
+ a3m-router:
14
+ build:
15
+ context: .
16
+ dockerfile: Dockerfile
17
+ container_name: a3m-router
18
+ restart: unless-stopped
19
+ ports:
20
+ - "3000:3000"
21
+ environment:
22
+ - NODE_ENV=production
23
+ - PORT=3000
24
+ # Pass through any .env variables needed at runtime
25
+ - NVIDIA_API_KEY=${NVIDIA_API_KEY}
26
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
27
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
28
+ - GEMINI_API_KEY=${GEMINI_API_KEY}
29
+ - GROQ_API_KEY=${GROQ_API_KEY}
30
+ - CEREBRAS_API_KEY=${CEREBRAS_API_KEY}
31
+ # Add additional provider keys as needed
32
+ env_file:
33
+ - .env
34
+ healthcheck:
35
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
36
+ interval: 30s
37
+ timeout: 5s
38
+ start_period: 10s
39
+ retries: 3
40
+ logging:
41
+ driver: "json-file"
42
+ options:
43
+ max-size: "10m"
44
+ max-file: "3"
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # A3M Proxy — OpenAI-compatible proxy server (port 8787)
48
+ # ---------------------------------------------------------------------------
49
+ a3m-proxy:
50
+ build:
51
+ context: .
52
+ dockerfile: Dockerfile.proxy
53
+ container_name: a3m-proxy
54
+ restart: unless-stopped
55
+ ports:
56
+ - "8787:8787"
57
+ environment:
58
+ - NODE_ENV=production
59
+ - PORT=8787
60
+ - ROUTER_BASE_URL=http://a3m-router:3000
61
+ depends_on:
62
+ a3m-router:
63
+ condition: service_healthy
64
+ healthcheck:
65
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8787/health"]
66
+ interval: 30s
67
+ timeout: 5s
68
+ start_period: 10s
69
+ retries: 3
70
+ logging:
71
+ driver: "json-file"
72
+ options:
73
+ max-size: "10m"
74
+ max-file: "3"
package/docs/.nojekyll ADDED
File without changes
package/docs/BENCHMARK.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # A3M Router — Independent Benchmark
2
2
 
3
+ A3M Router is benchmarked on two independent dimensions:
4
+
5
+ 1. **Latency** — How much overhead does the gateway add? (real API calls)
6
+ 2. **Routing Accuracy** — How well does the complexity classifier sort queries into tiers? (offline, 200 queries)
7
+
8
+ Both benchmarks are reproducible — scripts live in `scripts/`.
9
+
10
+ ---
11
+
12
+ ## 1. Latency Benchmark
13
+
3
14
  **The question everyone asks:** *"How much latency does a gateway add?"*
4
15
 
5
16
  **The answer:** +96ms for passthrough, +236ms for full intelligent routing — on a 138ms baseline.
@@ -8,9 +19,7 @@
8
19
 
9
20
  *Left: latency comparison. Right: cost savings projection. Dark theme.*
10
21
 
11
- ---
12
-
13
- ## The TL;DR
22
+ ### The TL;DR
14
23
 
15
24
  ```
16
25
  Direct call to Groq: ──▸ 138ms (baseline)
@@ -25,9 +34,7 @@ Through A3M auto (routed): ──▸ 374ms (+140ms = routing decision)
25
34
 
26
35
  **Total overhead: 236ms.** Less than the time it takes to blink.
27
36
 
28
- ---
29
-
30
- ## The Details
37
+ ### The Details
31
38
 
32
39
  | Scenario | Time | What's happening |
33
40
  |:---------|:----:|:-----------------|
@@ -35,11 +42,9 @@ Through A3M auto (routed): ──▸ 374ms (+140ms = routing decision)
35
42
  | **Through A3M (forced route)** | **234ms** | Request hits A3M proxy. Guardrails scan for prompt injection (17 patterns) and PII. Cache checks for semantic duplicates. Cost tracker logs the call. Request forwarded to Groq. Response logged. |
36
43
  | **Through A3M (auto route)** | **374ms** | Everything above, plus: A3M's router extracts 12 signals from the query text — domain, task type, complexity, verb intensity, multi-step structure. Scores it. Assigns a tier. Selects the cheapest capable model. Forwards the request. |
37
44
 
38
- **The extra 140ms for auto-routing is the intelligence.** It's the difference between "throw every query at GPT-4o" and "route simple questions to free tier, code questions to DeepSeek, expert questions to premium."
39
-
40
- ---
45
+ **The extra 140ms for auto-routing is the intelligence.**
41
46
 
42
- ## The Trade-Off
47
+ ### The Trade-Off
43
48
 
44
49
  ```text
45
50
  Without A3M With A3M
@@ -54,9 +59,7 @@ Cost visibility: End-of-month surprise Per-query tracking + budget
54
59
 
55
60
  **236ms of overhead saves you $2,604/year.** That's about $11 per millisecond.
56
61
 
57
- ---
58
-
59
- ## Why Most Gateways Don't Publish This
62
+ ### Why Most Gateways Don't Publish This
60
63
 
61
64
  Every gateway adds latency. Most don't publish their numbers because they're either:
62
65
 
@@ -66,23 +69,15 @@ Every gateway adds latency. Most don't publish their numbers because they're eit
66
69
 
67
70
  A3M publishes this because the numbers are honest and the trade-off is clear: **pay 236ms, save 62%, get production-grade security.**
68
71
 
69
- ---
70
-
71
- ## Reproduce This Yourself
72
+ ### Reproduce This
72
73
 
73
74
  ```bash
74
- # Install the benchmark tool
75
75
  pip install llm-gateway-bench
76
-
77
- # Start A3M proxy
78
76
  npx a3m-router serve
79
-
80
- # Run comparison
81
77
  python3 -m llm_gateway_bench.cli run groq \
82
78
  --model llama-3.3-70b-versatile \
83
79
  --prompt "What is the capital of France?" \
84
80
  --requests 10
85
-
86
81
  python3 -m llm_gateway_bench.cli run custom \
87
82
  --model auto \
88
83
  --base-url http://localhost:8787/v1 \
@@ -94,3 +89,82 @@ python3 -m llm_gateway_bench.cli run custom \
94
89
  **Run date:** 2026-05-26
95
90
  **Provider:** Groq (llama-3.3-70b-versatile)
96
91
  **Methodology:** 3 prompts × 5 requests = 15 calls per scenario, real API calls
92
+
93
+ ---
94
+
95
+ ## 2. Routing Accuracy Benchmark
96
+
97
+ **The question everyone asks:** *"Does the complexity classifier actually pick the right tier?"*
98
+
99
+ **The answer:** **99.5% ±1 tier accuracy** across 200 diverse queries — no ML training needed.
100
+
101
+ Benchmark script: `scripts/routing-benchmark-v2.js`
102
+ Methodology: RouteLLM-inspired (arXiv:2404.06035), 4-tier classification
103
+
104
+ ### Results (2026-05-28)
105
+
106
+ | Metric | Score | What It Means |
107
+ |:-------|:-----:|:--------------|
108
+ | **±1 Tier Accuracy** | **99.5%** | Only 1 in 200 queries is misrouted by >1 tier |
109
+ | Exact Tier Match | 64.5% | ~2 in 3 queries hit the *exact* right tier |
110
+ | Free Tier Recall | 92.0% | Simple queries correctly routed to $0 models |
111
+ | Cheap Tier Recall | 78.3% | Standard code/translation routed to cheap |
112
+ | Mid Tier Recall | 36.0% | Complex reasoning often routed cheaper (fallback-safe) |
113
+ | Premium Tier Recall | 45.0% | Expert queries routed to premium |
114
+ | Over-routing (waste) | 7.0% | Sent to a stronger but costlier model than needed |
115
+ | Under-routing (risk) | 28.5% | Sent weak first; auto-fallback in <2s |
116
+ | Cost Savings vs All-Premium | **61.6%** | At 100K queries/mo: **save $77.04/mo** |
117
+
118
+ ### Confusion Matrix
119
+
120
+ ```
121
+ Expected \\ Routed free cheap mid premium
122
+ ──────────────────────────────────────────────────
123
+ free 46✓ 4 0 0
124
+ cheap 11 47✓ 2 0
125
+ mid 0 24 18✓ 8
126
+ premium 0 1 21 18✓
127
+ ```
128
+
129
+ ### Complexity Score Distribution
130
+
131
+ ```
132
+ free avg=0.125 range=[0.100, 0.270]
133
+ cheap avg=0.275 range=[0.100, 0.575]
134
+ mid avg=0.477 range=[0.230, 0.710]
135
+ premium avg=0.690 range=[0.430, 1.000]
136
+ ```
137
+
138
+ ### Test Set
139
+
140
+ - **50 simple** — trivia, basic math, yes/no (target: free)
141
+ - **60 medium** — code snippets, summarization, translation (target: cheap)
142
+ - **50 complex** — reasoning, analysis, system design (target: mid)
143
+ - **40 expert** — legal, medical, security, finance (target: premium)
144
+
145
+ ### Third-Party Cross-Validation
146
+
147
+ A3M's tier assignments align with **MMLU accuracy rankings**:
148
+
149
+ ```
150
+ Provider MMLU A3M Tier Source
151
+ ────────────────────────────────────────────────
152
+ gpt-4o 88.7% premium MMLU Leaderboard
153
+ claude-3.5-sonnet 88.4% premium MMLU Leaderboard
154
+ gemini-1.5-pro 85.7% premium MMLU Leaderboard
155
+ mistral-large 84.2% mid MMLU Leaderboard
156
+ llama-3.3-70b 82.5% mid MMLU Leaderboard
157
+ deepseek-v2 78.3% mid MMLU Leaderboard
158
+ llama-3.1-8b 68.3% cheap MMLU Leaderboard
159
+ ```
160
+
161
+ **References:** [MMLU Leaderboard](https://paperswithcode.com/sota/multi-task-language-understanding-on-mmlu), [RouteLLM arXiv:2404.06035](https://arxiv.org/abs/2404.06035)
162
+
163
+ ### Reproduce This
164
+
165
+ ```bash
166
+ cd /path/to/a3m-router
167
+ node scripts/routing-benchmark-v2.js
168
+ ```
169
+
170
+ Outputs `benchmark-results.json` with full breakdown.