adaptive-memory-multi-model-router 2.14.16 → 2.14.18

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 (69) hide show
  1. package/.a3m-vault.json +23 -0
  2. package/.github/workflows/ci.yml +253 -5
  3. package/.publish-tick +1 -1
  4. package/AGENT_COUNCIL_FINDINGS.md +142 -0
  5. package/LAUNCH_CHECKLIST.md +141 -0
  6. package/README.md +15 -17
  7. package/README.md.bak +836 -0
  8. package/articles/CHINESE_SUBMISSIONS_READY.md +322 -0
  9. package/articles/DEVTO_READY.md +255 -0
  10. package/articles/HN_POST_READY.md +137 -0
  11. package/articles/INDIEHACKERS_READY.md +120 -0
  12. package/articles/NEWSLETTER_SEND_NOW.md +259 -0
  13. package/articles/PRODUCTHUNT_READY.md +106 -0
  14. package/articles/REDDIT_SUBMISSION_READY.md +348 -0
  15. package/articles/TWEET_STORM_READY.md +165 -0
  16. package/benchmark-results.json +45 -43
  17. package/council-votes/architecture-vote.md +121 -0
  18. package/council-votes/coverage-vote.md +93 -0
  19. package/dist/cost/costTracker.d.ts +109 -44
  20. package/dist/cost/costTracker.js +321 -98
  21. package/dist/cost/costTracker.js.map +1 -1
  22. package/dist/ensemble.d.ts +21 -0
  23. package/dist/ensemble.js +85 -0
  24. package/dist/index.d.ts +9 -5
  25. package/dist/index.js +12 -4
  26. package/dist/routing/advancedRouter.d.ts +38 -43
  27. package/dist/routing/advancedRouter.js +394 -408
  28. package/dist/routing/advancedRouter.js.map +1 -1
  29. package/dist/routing/providers/providerConfig.d.ts +49 -0
  30. package/dist/routing/providers/providerConfig.js +883 -0
  31. package/dist/routing/routing/advancedRouter.d.ts +62 -0
  32. package/dist/routing/routing/advancedRouter.js +447 -0
  33. package/dist/routing/utils/tokenUtils.d.ts +52 -0
  34. package/dist/routing/utils/tokenUtils.js +129 -0
  35. package/dist/server/proxyServer.d.ts +1 -1
  36. package/dist/tui/dashboard.js +66 -2
  37. package/dist/tui/dashboard.js.map +1 -1
  38. package/dist/utils/tokenUtils.d.ts +48 -1
  39. package/dist/utils/tokenUtils.js +117 -4
  40. package/dist/utils/tokenUtils.js.map +1 -1
  41. package/docs/CITATIONS.md +2 -2
  42. package/docs/GEO_STATUS.md +43 -157
  43. package/docs/ai-plugin.json +4 -4
  44. package/docs/llms.txt +21 -27
  45. package/docs/sitemap.xml +14 -20
  46. package/package.json +2 -2
  47. package/research-log.md +49 -0
  48. package/sitemap.xml +57 -0
  49. package/src/cost/costTracker.ts +576 -0
  50. package/src/ensemble.ts +103 -0
  51. package/src/index.ts +13 -3
  52. package/src/routing/advancedRouter.ts +536 -0
  53. package/src/tui/dashboard.ts +76 -3
  54. package/src/utils/tokenUtils.ts +142 -4
  55. package/test-council/1-structure-tests.test.js +353 -0
  56. package/test-council/1-structure-tests.test.ts +353 -0
  57. package/test-council/2-edge-case-tests.test.ts +361 -0
  58. package/test-council/3-performance-tests.test.ts +669 -0
  59. package/test-council/4-integration-tests.test.ts +391 -0
  60. package/test-council/5-agent-council-eval.test.ts +413 -0
  61. package/test-council/AGENT_COUNCIL_ARCHITECTURE.md +349 -0
  62. package/test-council/TEST_COUNCIL_REPORT.md +201 -0
  63. package/test-council/agents/edge-case-agent.ts +363 -0
  64. package/test-council/agents/performance-agent.ts +426 -0
  65. package/test-council/agents/structure-agent.ts +227 -0
  66. package/test-council/council.md +183 -0
  67. package/tests/security/guardrailEngine.test.ts +700 -0
  68. package/docs/.well-known/ai-plugin.json +0 -16
  69. package/research/PUBLISH_LOG.md +0 -3
@@ -0,0 +1,883 @@
1
+ "use strict";
2
+ /**
3
+ * A3M Router - Generic Provider Configuration System
4
+ *
5
+ * Users can configure their available LLM providers via:
6
+ * 1. Environment variables (*_API_KEY patterns)
7
+ * 2. Config file at ~/.config/a3m-router/providers.json
8
+ * 3. Runtime registration via registerProvider()
9
+ *
10
+ * 40+ providers across free, cheap, mid-tier, premium, and enterprise tiers.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.DEFAULT_PROVIDERS = void 0;
47
+ exports.loadConfig = loadConfig;
48
+ exports.getAvailableProviders = getAvailableProviders;
49
+ exports.registerProvider = registerProvider;
50
+ exports.deregisterProvider = deregisterProvider;
51
+ exports.updateProvider = updateProvider;
52
+ exports.healthCheck = healthCheck;
53
+ exports.checkAllProviders = checkAllProviders;
54
+ exports.findCheapestAvailableProvider = findCheapestAvailableProvider;
55
+ exports.findFastestAvailableProvider = findFastestAvailableProvider;
56
+ exports.saveConfig = saveConfig;
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ // ============================================================
60
+ // DEFAULT PROVIDER DEFINITIONS
61
+ // ============================================================
62
+ exports.DEFAULT_PROVIDERS = {
63
+ // ========================================================================
64
+ // TIER: FREE / LOCAL
65
+ // ========================================================================
66
+ ollama: {
67
+ id: 'ollama',
68
+ name: 'Ollama',
69
+ baseUrl: 'http://127.0.0.1:11434/v1/chat/completions',
70
+ apiKeyEnv: '',
71
+ models: ['llama3', 'mistral', 'qwen2', 'codellama', 'phi3', 'gemma2'],
72
+ costPerK: { input: 0, output: 0 },
73
+ tier: 'free',
74
+ format: 'openai',
75
+ type: 'local',
76
+ priority: 1,
77
+ maxTokens: 8192,
78
+ },
79
+ lmstudio: {
80
+ id: 'lmstudio',
81
+ name: 'LM Studio',
82
+ baseUrl: 'http://127.0.0.1:1234/v1/chat/completions',
83
+ apiKeyEnv: '',
84
+ models: [],
85
+ costPerK: { input: 0, output: 0 },
86
+ tier: 'free',
87
+ format: 'openai',
88
+ type: 'local',
89
+ priority: 2,
90
+ maxTokens: 8192,
91
+ },
92
+ vllm: {
93
+ id: 'vllm',
94
+ name: 'vLLM',
95
+ baseUrl: 'http://127.0.0.1:8000/v1/chat/completions',
96
+ apiKeyEnv: '',
97
+ models: [],
98
+ costPerK: { input: 0, output: 0 },
99
+ tier: 'free',
100
+ format: 'openai',
101
+ type: 'local',
102
+ priority: 3,
103
+ maxTokens: 8192,
104
+ },
105
+ google: {
106
+ id: 'google',
107
+ name: 'Google AI',
108
+ baseUrl: 'https://generativelanguage.googleapis.com/v1beta/models',
109
+ apiKeyEnv: 'GOOGLE_API_KEY',
110
+ models: [
111
+ 'gemini-2.5-flash',
112
+ 'gemini-2.5-pro',
113
+ 'gemini-2.0-flash',
114
+ 'gemini-1.5-flash',
115
+ 'gemini-1.5-pro',
116
+ 'gemma-3-27b-it',
117
+ ],
118
+ costPerK: { input: 0, output: 0 }, // Free tier available
119
+ tier: 'free',
120
+ format: 'google',
121
+ type: 'api',
122
+ priority: 4,
123
+ maxTokens: 8192,
124
+ },
125
+ // ========================================================================
126
+ // TIER: CHEAP / FAST (inference-optimized)
127
+ // ========================================================================
128
+ groq: {
129
+ id: 'groq',
130
+ name: 'Groq',
131
+ baseUrl: 'https://api.groq.com/openai/v1/chat/completions',
132
+ apiKeyEnv: 'GROQ_API_KEY',
133
+ models: [
134
+ 'llama-3.3-70b-versatile',
135
+ 'llama-3.1-8b-instant',
136
+ 'openai/gpt-oss-120b',
137
+ 'openai/gpt-oss-20b',
138
+ 'qwen/qwen3-32b',
139
+ 'meta-llama/llama-4-scout-17b-16e-instruct',
140
+ ],
141
+ costPerK: { input: 0.59, output: 0.79 },
142
+ tier: 'cheap',
143
+ format: 'openai',
144
+ type: 'api',
145
+ priority: 5,
146
+ maxTokens: 8192,
147
+ },
148
+ cerebras: {
149
+ id: 'cerebras',
150
+ name: 'Cerebras',
151
+ baseUrl: 'https://api.cerebras.ai/v1/chat/completions',
152
+ apiKeyEnv: 'CEREBRAS_API_KEY',
153
+ models: [
154
+ 'llama3.1-8b',
155
+ 'llama-3.3-70b',
156
+ 'qwen-3-235b-a22b-instruct-2507',
157
+ 'gpt-oss-120b',
158
+ 'zai-glm-4.7',
159
+ ],
160
+ costPerK: { input: 0.6, output: 0.6 },
161
+ tier: 'cheap',
162
+ format: 'openai',
163
+ type: 'api',
164
+ priority: 6,
165
+ maxTokens: 8192,
166
+ },
167
+ deepinfra: {
168
+ id: 'deepinfra',
169
+ name: 'DeepInfra',
170
+ baseUrl: 'https://api.deepinfra.com/v1/openai/chat/completions',
171
+ apiKeyEnv: 'DEEPINFRA_API_KEY',
172
+ models: [
173
+ 'meta-llama/Meta-Llama-3.1-8B-Instruct',
174
+ 'meta-llama/Meta-Llama-3.1-70B-Instruct',
175
+ 'mistralai/Mixtral-8x7B-Instruct-v0.1',
176
+ 'Qwen/Qwen2.5-72B-Instruct',
177
+ 'google/gemma-2-27b-it',
178
+ ],
179
+ costPerK: { input: 0.05, output: 0.05 },
180
+ tier: 'cheap',
181
+ format: 'openai',
182
+ type: 'api',
183
+ priority: 7,
184
+ maxTokens: 8192,
185
+ },
186
+ together: {
187
+ id: 'together',
188
+ name: 'Together AI',
189
+ baseUrl: 'https://api.together.xyz/v1/chat/completions',
190
+ apiKeyEnv: 'TOGETHER_API_KEY',
191
+ models: [
192
+ 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo',
193
+ 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo',
194
+ 'mistralai/Mixtral-8x7B-Instruct-v0.1',
195
+ 'Qwen/Qwen2.5-72B-Instruct-Turbo',
196
+ 'google/gemma-2-27b-it',
197
+ ],
198
+ costPerK: { input: 0.18, output: 0.18 },
199
+ tier: 'cheap',
200
+ format: 'openai',
201
+ type: 'api',
202
+ priority: 8,
203
+ maxTokens: 8192,
204
+ },
205
+ fireworks: {
206
+ id: 'fireworks',
207
+ name: 'Fireworks AI',
208
+ baseUrl: 'https://api.fireworks.ai/inference/v1/chat/completions',
209
+ apiKeyEnv: 'FIREWORKS_API_KEY',
210
+ models: [
211
+ 'accounts/fireworks/models/llama-v3p1-8b-instruct',
212
+ 'accounts/fireworks/models/llama-v3p1-70b-instruct',
213
+ 'accounts/fireworks/models/mixtral-8x7b-instruct',
214
+ 'accounts/fireworks/models/qwen2p5-72b-instruct',
215
+ ],
216
+ costPerK: { input: 0.2, output: 0.2 },
217
+ tier: 'cheap',
218
+ format: 'openai',
219
+ type: 'api',
220
+ priority: 9,
221
+ maxTokens: 8192,
222
+ },
223
+ novita: {
224
+ id: 'novita',
225
+ name: 'Novita AI',
226
+ baseUrl: 'https://api.novita.ai/v3/openai/chat/completions',
227
+ apiKeyEnv: 'NOVITA_API_KEY',
228
+ models: [
229
+ 'meta-llama/llama-3.1-8b-instruct',
230
+ 'meta-llama/llama-3.1-70b-instruct',
231
+ 'Qwen/Qwen2.5-72B-Instruct',
232
+ ],
233
+ costPerK: { input: 0.06, output: 0.06 },
234
+ tier: 'cheap',
235
+ format: 'openai',
236
+ type: 'api',
237
+ priority: 10,
238
+ maxTokens: 8192,
239
+ },
240
+ sambanova: {
241
+ id: 'sambanova',
242
+ name: 'SambaNova',
243
+ baseUrl: 'https://api.sambanova.ai/v1/chat/completions',
244
+ apiKeyEnv: 'SAMBANOVA_API_KEY',
245
+ models: [
246
+ 'Meta-Llama-3.1-8B-Instruct',
247
+ 'Meta-Llama-3.1-70B-Instruct',
248
+ ],
249
+ costPerK: { input: 0.1, output: 0.1 },
250
+ tier: 'cheap',
251
+ format: 'openai',
252
+ type: 'api',
253
+ priority: 11,
254
+ maxTokens: 8192,
255
+ },
256
+ anyscale: {
257
+ id: 'anyscale',
258
+ name: 'Anyscale',
259
+ baseUrl: 'https://api.endpoints.anyscale.com/v1/chat/completions',
260
+ apiKeyEnv: 'ANYSCALE_API_KEY',
261
+ models: [
262
+ 'meta-llama/Meta-Llama-3.1-8B-Instruct',
263
+ 'meta-llama/Meta-Llama-3.1-70B-Instruct',
264
+ 'mistralai/Mixtral-8x7B-Instruct-v0.1',
265
+ ],
266
+ costPerK: { input: 0.15, output: 0.15 },
267
+ tier: 'cheap',
268
+ format: 'openai',
269
+ type: 'api',
270
+ priority: 12,
271
+ maxTokens: 8192,
272
+ },
273
+ replicate: {
274
+ id: 'replicate',
275
+ name: 'Replicate',
276
+ baseUrl: 'https://api.replicate.com/v1/chat/completions',
277
+ apiKeyEnv: 'REPLICATE_API_KEY',
278
+ models: [
279
+ 'meta/llama-2-70b-chat',
280
+ 'mistralai/mixtral-8x7b-instruct-v0.1',
281
+ ],
282
+ costPerK: { input: 0.2, output: 0.2 },
283
+ tier: 'cheap',
284
+ format: 'openai',
285
+ type: 'api',
286
+ priority: 13,
287
+ maxTokens: 8192,
288
+ },
289
+ // ========================================================================
290
+ // TIER: MID (good quality/price ratio)
291
+ // ========================================================================
292
+ deepseek: {
293
+ id: 'deepseek',
294
+ name: 'DeepSeek',
295
+ baseUrl: 'https://api.deepseek.com/v1/chat/completions',
296
+ apiKeyEnv: 'DEEPSEEK_API_KEY',
297
+ models: ['deepseek-v4-flash', 'deepseek-v4-pro'],
298
+ costPerK: { input: 0.14, output: 0.28 },
299
+ tier: 'mid',
300
+ format: 'openai',
301
+ type: 'api',
302
+ priority: 14,
303
+ maxTokens: 8192,
304
+ },
305
+ mistral: {
306
+ id: 'mistral',
307
+ name: 'Mistral',
308
+ baseUrl: 'https://api.mistral.ai/v1/chat/completions',
309
+ apiKeyEnv: 'MISTRAL_API_KEY',
310
+ models: [
311
+ 'mistral-small-latest',
312
+ 'mistral-medium-latest',
313
+ 'mistral-large-latest',
314
+ 'mistral-small-2506',
315
+ 'devstral-small-2507',
316
+ 'ministral-3b-latest',
317
+ 'ministral-8b-latest',
318
+ 'codestral-latest',
319
+ 'open-mistral-nemo',
320
+ ],
321
+ costPerK: { input: 0.2, output: 0.6 },
322
+ tier: 'mid',
323
+ format: 'openai',
324
+ type: 'api',
325
+ priority: 15,
326
+ maxTokens: 8192,
327
+ },
328
+ perplexity: {
329
+ id: 'perplexity',
330
+ name: 'Perplexity',
331
+ baseUrl: 'https://api.perplexity.ai/chat/completions',
332
+ apiKeyEnv: 'PERPLEXITY_API_KEY',
333
+ models: [
334
+ 'llama-3.1-sonar-small-128k-online',
335
+ 'llama-3.1-sonar-large-128k-online',
336
+ 'sonar-pro',
337
+ ],
338
+ costPerK: { input: 1.0, output: 1.0 },
339
+ tier: 'mid',
340
+ format: 'openai',
341
+ type: 'api',
342
+ priority: 16,
343
+ maxTokens: 8192,
344
+ },
345
+ cohere: {
346
+ id: 'cohere',
347
+ name: 'Cohere',
348
+ baseUrl: 'https://api.cohere.ai/v1/chat/completions',
349
+ apiKeyEnv: 'COHERE_API_KEY',
350
+ models: ['command-r-plus', 'command-r', 'command-a-03-2025'],
351
+ costPerK: { input: 2.5, output: 10 },
352
+ tier: 'mid',
353
+ format: 'cohere',
354
+ type: 'api',
355
+ priority: 17,
356
+ maxTokens: 8192,
357
+ },
358
+ ai21: {
359
+ id: 'ai21',
360
+ name: 'AI21 Labs',
361
+ baseUrl: 'https://api.ai21.com/studio/v1/chat/completions',
362
+ apiKeyEnv: 'AI21_API_KEY',
363
+ models: ['jamba-1.5-mini', 'jamba-1.5-large'],
364
+ costPerK: { input: 0.2, output: 0.4 },
365
+ tier: 'mid',
366
+ format: 'openai',
367
+ type: 'api',
368
+ priority: 18,
369
+ maxTokens: 8192,
370
+ },
371
+ // ========================================================================
372
+ // TIER: PREMIUM (high-quality frontier models)
373
+ // ========================================================================
374
+ openai: {
375
+ id: 'openai',
376
+ name: 'OpenAI',
377
+ baseUrl: 'https://api.openai.com/v1/chat/completions',
378
+ apiKeyEnv: 'OPENAI_API_KEY',
379
+ models: [
380
+ 'gpt-4o',
381
+ 'gpt-4o-mini',
382
+ 'gpt-4-turbo',
383
+ 'gpt-4',
384
+ 'gpt-3.5-turbo',
385
+ 'o1-preview',
386
+ 'o1-mini',
387
+ ],
388
+ costPerK: { input: 2.5, output: 10 },
389
+ tier: 'premium',
390
+ format: 'openai',
391
+ type: 'api',
392
+ priority: 19,
393
+ maxTokens: 8192,
394
+ },
395
+ anthropic: {
396
+ id: 'anthropic',
397
+ name: 'Anthropic',
398
+ baseUrl: 'https://api.anthropic.com/v1/messages',
399
+ apiKeyEnv: 'ANTHROPIC_API_KEY',
400
+ models: [
401
+ 'claude-sonnet-4-20250514',
402
+ 'claude-3.5-sonnet',
403
+ 'claude-3-opus',
404
+ 'claude-3-haiku',
405
+ ],
406
+ costPerK: { input: 3, output: 15 },
407
+ tier: 'premium',
408
+ format: 'anthropic',
409
+ type: 'api',
410
+ priority: 20,
411
+ maxTokens: 8192,
412
+ },
413
+ xai: {
414
+ id: 'xai',
415
+ name: 'xAI',
416
+ baseUrl: 'https://api.x.ai/v1/chat/completions',
417
+ apiKeyEnv: 'XAI_API_KEY',
418
+ models: ['grok-3', 'grok-3-mini', 'grok-2', 'grok-2-mini'],
419
+ costPerK: { input: 3.0, output: 15.0 },
420
+ tier: 'premium',
421
+ format: 'openai',
422
+ type: 'api',
423
+ priority: 21,
424
+ maxTokens: 8192,
425
+ },
426
+ // ========================================================================
427
+ // TIER: ENTERPRISE (cloud-managed models)
428
+ // ========================================================================
429
+ azure_openai: {
430
+ id: 'azure_openai',
431
+ name: 'Azure OpenAI',
432
+ baseUrl: 'https://{resource}.openai.azure.com/openai/deployments/{deployment}/chat/completions',
433
+ apiKeyEnv: 'AZURE_OPENAI_API_KEY',
434
+ models: ['gpt-4', 'gpt-35-turbo', 'gpt-4o'],
435
+ costPerK: { input: 3.0, output: 12.0 },
436
+ tier: 'enterprise',
437
+ format: 'openai',
438
+ type: 'api',
439
+ priority: 22,
440
+ maxTokens: 8192,
441
+ },
442
+ bedrock: {
443
+ id: 'bedrock',
444
+ name: 'AWS Bedrock',
445
+ baseUrl: '',
446
+ apiKeyEnv: 'AWS_ACCESS_KEY_ID',
447
+ models: [
448
+ 'anthropic.claude-3-sonnet',
449
+ 'anthropic.claude-3-haiku',
450
+ 'meta.llama3-1-8b',
451
+ 'meta.llama3-1-70b',
452
+ 'mistral.mixtral-8x7b',
453
+ ],
454
+ costPerK: { input: 3.0, output: 15.0 },
455
+ tier: 'enterprise',
456
+ format: 'aws-bedrock',
457
+ type: 'api',
458
+ priority: 23,
459
+ maxTokens: 8192,
460
+ },
461
+ vertex: {
462
+ id: 'vertex',
463
+ name: 'Google Vertex AI',
464
+ baseUrl: '',
465
+ apiKeyEnv: 'GOOGLE_APPLICATION_CREDENTIALS',
466
+ models: [
467
+ 'gemini-1.5-flash',
468
+ 'gemini-1.5-pro',
469
+ 'claude-3-sonnet',
470
+ 'claude-3-haiku',
471
+ ],
472
+ costPerK: { input: 1.25, output: 5.0 },
473
+ tier: 'enterprise',
474
+ format: 'google-vertex',
475
+ type: 'api',
476
+ priority: 24,
477
+ maxTokens: 8192,
478
+ },
479
+ // ========================================================================
480
+ // ASIAN PROVIDERS
481
+ // ========================================================================
482
+ zhipu: {
483
+ id: 'zhipu',
484
+ name: 'Zhipu AI (GLM)',
485
+ baseUrl: 'https://open.bigmodel.cn/api/paas/v4/chat/completions',
486
+ apiKeyEnv: 'ZHIPU_API_KEY',
487
+ models: ['glm-4-flash', 'glm-4-plus', 'glm-4-air', 'glm-4-long'],
488
+ costPerK: { input: 0.1, output: 0.1 },
489
+ tier: 'cheap',
490
+ format: 'openai',
491
+ type: 'api',
492
+ priority: 25,
493
+ maxTokens: 8192,
494
+ },
495
+ moonshot: {
496
+ id: 'moonshot',
497
+ name: 'Moonshot (Kimi)',
498
+ baseUrl: 'https://api.moonshot.cn/v1/chat/completions',
499
+ apiKeyEnv: 'MOONSHOT_API_KEY',
500
+ models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],
501
+ costPerK: { input: 0.14, output: 0.14 },
502
+ tier: 'cheap',
503
+ format: 'openai',
504
+ type: 'api',
505
+ priority: 26,
506
+ maxTokens: 8192,
507
+ },
508
+ qwen: {
509
+ id: 'qwen',
510
+ name: 'Alibaba Qwen (DashScope)',
511
+ baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
512
+ apiKeyEnv: 'DASHSCOPE_API_KEY',
513
+ models: ['qwen-turbo', 'qwen-plus', 'qwen-max', 'qwen-long'],
514
+ costPerK: { input: 0.4, output: 1.2 },
515
+ tier: 'mid',
516
+ format: 'openai',
517
+ type: 'api',
518
+ priority: 27,
519
+ maxTokens: 8192,
520
+ },
521
+ yi: {
522
+ id: 'yi',
523
+ name: 'Yi (01.AI)',
524
+ baseUrl: 'https://api.lingyiwanwu.com/v1/chat/completions',
525
+ apiKeyEnv: 'YI_API_KEY',
526
+ models: ['yi-lightning', 'yi-large', 'yi-medium', 'yi-spark'],
527
+ costPerK: { input: 0.2, output: 0.2 },
528
+ tier: 'cheap',
529
+ format: 'openai',
530
+ type: 'api',
531
+ priority: 28,
532
+ maxTokens: 8192,
533
+ },
534
+ baichuan: {
535
+ id: 'baichuan',
536
+ name: 'Baichuan',
537
+ baseUrl: 'https://api.baichuan-ai.com/v1/chat/completions',
538
+ apiKeyEnv: 'BAICHUAN_API_KEY',
539
+ models: ['Baichuan4', 'Baichuan3-Turbo', 'Baichuan3-Turbo-128k'],
540
+ costPerK: { input: 0.2, output: 0.2 },
541
+ tier: 'cheap',
542
+ format: 'openai',
543
+ type: 'api',
544
+ priority: 29,
545
+ maxTokens: 8192,
546
+ },
547
+ minimax: {
548
+ id: 'minimax',
549
+ name: 'MiniMax',
550
+ baseUrl: 'https://api.minimax.chat/v1/text/chatcompletion_v2',
551
+ apiKeyEnv: 'MINIMAX_API_KEY',
552
+ models: ['MiniMax-Text-01', 'abab6.5s-chat'],
553
+ costPerK: { input: 0.1, output: 0.1 },
554
+ tier: 'cheap',
555
+ format: 'openai',
556
+ type: 'api',
557
+ priority: 30,
558
+ maxTokens: 8192,
559
+ },
560
+ stepfun: {
561
+ id: 'stepfun',
562
+ name: 'StepFun',
563
+ baseUrl: 'https://api.stepfun.com/v1/chat/completions',
564
+ apiKeyEnv: 'STEPFUN_API_KEY',
565
+ models: ['step-1-8k', 'step-1-32k', 'step-2-16k'],
566
+ costPerK: { input: 0.2, output: 0.2 },
567
+ tier: 'mid',
568
+ format: 'openai',
569
+ type: 'api',
570
+ priority: 31,
571
+ maxTokens: 8192,
572
+ },
573
+ // ========================================================================
574
+ // EUROPEAN PROVIDERS
575
+ // ========================================================================
576
+ alephalpha: {
577
+ id: 'alephalpha',
578
+ name: 'Aleph Alpha',
579
+ baseUrl: 'https://api.aleph-alpha.com/v1/chat/completions',
580
+ apiKeyEnv: 'ALEPH_ALPHA_API_KEY',
581
+ models: ['luminous-base', 'luminous-extended', 'luminous-supreme'],
582
+ costPerK: { input: 2.0, output: 2.0 },
583
+ tier: 'mid',
584
+ format: 'openai',
585
+ type: 'api',
586
+ priority: 32,
587
+ maxTokens: 8192,
588
+ },
589
+ deepset: {
590
+ id: 'deepset',
591
+ name: 'Deepset',
592
+ baseUrl: 'https://api.deepset.ai/v1/chat/completions',
593
+ apiKeyEnv: 'DEEPSET_API_KEY',
594
+ models: ['gpt-4', 'claude-3-sonnet'],
595
+ costPerK: { input: 3.0, output: 12.0 },
596
+ tier: 'mid',
597
+ format: 'openai',
598
+ type: 'api',
599
+ priority: 33,
600
+ maxTokens: 8192,
601
+ },
602
+ // ========================================================================
603
+ // OPEN ROUTER / AGGREGATORS
604
+ // ========================================================================
605
+ openrouter: {
606
+ id: 'openrouter',
607
+ name: 'OpenRouter',
608
+ baseUrl: 'https://openrouter.ai/api/v1/chat/completions',
609
+ apiKeyEnv: 'OPENROUTER_API_KEY',
610
+ models: [
611
+ 'openai/gpt-4o',
612
+ 'anthropic/claude-3.5-sonnet',
613
+ 'google/gemini-pro-1.5',
614
+ 'meta-llama/llama-3.1-70b-instruct',
615
+ 'mistralai/mistral-large',
616
+ ],
617
+ costPerK: { input: 0, output: 0 }, // Passthrough pricing
618
+ tier: 'cheap',
619
+ format: 'openai',
620
+ type: 'api',
621
+ priority: 34,
622
+ maxTokens: 8192,
623
+ },
624
+ // ========================================================================
625
+ // CLI PROVIDERS (local tools)
626
+ // ========================================================================
627
+ opencode: {
628
+ id: 'opencode',
629
+ name: 'OpenCode',
630
+ baseUrl: '',
631
+ apiKeyEnv: '',
632
+ cliCommand: 'opencode',
633
+ models: [],
634
+ costPerK: { input: 0, output: 0 },
635
+ tier: 'free',
636
+ format: 'openai',
637
+ type: 'cli',
638
+ priority: 35,
639
+ maxTokens: 8192,
640
+ },
641
+ commandcode: {
642
+ id: 'commandcode',
643
+ name: 'CommandCode',
644
+ baseUrl: 'https://api.commandcode.ai/v1/chat/completions',
645
+ apiKeyEnv: 'COMMANDCODE_API_KEY',
646
+ models: ['taste-1'],
647
+ costPerK: { input: 0, output: 0 },
648
+ tier: 'free',
649
+ format: 'openai',
650
+ type: 'cli',
651
+ cliCommand: 'commandcode',
652
+ priority: 36,
653
+ maxTokens: 8192,
654
+ },
655
+ // ========================================================================
656
+ // NVIDIA NIM (free tier via NVIDIA API key)
657
+ // ========================================================================
658
+ nvidia: {
659
+ id: 'nvidia',
660
+ name: 'NVIDIA NIM',
661
+ baseUrl: 'https://integrate.api.nvidia.com/v1/chat/completions',
662
+ apiKeyEnv: 'NVIDIA_API_KEY',
663
+ models: [
664
+ 'meta/llama-3.1-8b-instruct',
665
+ 'meta/llama-3.3-70b-instruct',
666
+ 'meta/llama-4-maverick-17b-128e-instruct',
667
+ 'nvidia/nemotron-mini-4b-instruct',
668
+ 'nvidia/nemotron-3-super-120b-a12b',
669
+ 'google/gemma-4-31b-it',
670
+ 'qwen/qwen3.5-397b-a17b',
671
+ 'minimaxai/minimax-m2.7',
672
+ 'mistralai/mistral-large-3-675b-instruct-2512',
673
+ 'z-ai/glm-5.1',
674
+ ],
675
+ costPerK: { input: 0, output: 0 },
676
+ tier: 'free',
677
+ format: 'openai',
678
+ type: 'api',
679
+ priority: 4,
680
+ maxTokens: 8192,
681
+ },
682
+ };
683
+ // ============================================================
684
+ // RUNTIME STATE
685
+ // ============================================================
686
+ let _registeredProviders = { ...exports.DEFAULT_PROVIDERS };
687
+ let _configLoaded = false;
688
+ // ============================================================
689
+ // CONFIGURATION LOADING
690
+ // ============================================================
691
+ function loadConfig(configPath) {
692
+ const paths = [];
693
+ if (configPath && fs.existsSync(configPath)) {
694
+ paths.push(configPath);
695
+ }
696
+ const userConfig = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.config', 'a3m-router', 'providers.json');
697
+ if (fs.existsSync(userConfig)) {
698
+ paths.push(userConfig);
699
+ }
700
+ const projectConfig = path.join(process.cwd(), 'a3m-providers.json');
701
+ if (fs.existsSync(projectConfig)) {
702
+ paths.push(projectConfig);
703
+ }
704
+ for (const p of paths) {
705
+ try {
706
+ const raw = fs.readFileSync(p, 'utf-8');
707
+ const config = JSON.parse(raw);
708
+ if (config.providers) {
709
+ for (const [id, provider] of Object.entries(config.providers)) {
710
+ const prov = provider;
711
+ if (_registeredProviders[id]) {
712
+ _registeredProviders[id] = { ..._registeredProviders[id], ...prov };
713
+ }
714
+ else {
715
+ _registeredProviders[id] = {
716
+ id,
717
+ type: 'api',
718
+ priority: 50,
719
+ maxTokens: 8192,
720
+ costPerK: { input: 0, output: 0 },
721
+ tier: 'mid',
722
+ format: 'openai',
723
+ models: [],
724
+ name: id,
725
+ baseUrl: '',
726
+ apiKeyEnv: '',
727
+ ...prov,
728
+ };
729
+ }
730
+ }
731
+ }
732
+ _configLoaded = true;
733
+ break;
734
+ }
735
+ catch {
736
+ // Skip invalid config files
737
+ }
738
+ }
739
+ // Load API keys from environment
740
+ for (const provider of Object.values(_registeredProviders)) {
741
+ if (provider.apiKeyEnv) {
742
+ provider.apiKey = process.env[provider.apiKeyEnv] || null;
743
+ }
744
+ }
745
+ return _registeredProviders;
746
+ }
747
+ function getAvailableProviders() {
748
+ if (!_configLoaded) {
749
+ loadConfig();
750
+ }
751
+ const available = {};
752
+ for (const [id, provider] of Object.entries(_registeredProviders)) {
753
+ if (provider.type === 'api') {
754
+ if (provider.apiKey) {
755
+ available[id] = provider;
756
+ }
757
+ }
758
+ else {
759
+ available[id] = provider;
760
+ }
761
+ }
762
+ return Object.entries(available)
763
+ .sort(([, a], [, b]) => a.priority - b.priority)
764
+ .reduce((acc, [k, v]) => { acc[k] = v; return acc; }, {});
765
+ }
766
+ // ============================================================
767
+ // RUNTIME REGISTRATION
768
+ // ============================================================
769
+ function registerProvider(id, config) {
770
+ _registeredProviders[id] = {
771
+ id,
772
+ name: config.name || id,
773
+ baseUrl: config.baseUrl || '',
774
+ apiKeyEnv: config.apiKeyEnv || '',
775
+ type: 'api',
776
+ priority: 50,
777
+ maxTokens: 8192,
778
+ costPerK: { input: 0, output: 0 },
779
+ tier: 'mid',
780
+ format: 'openai',
781
+ models: [],
782
+ ...config,
783
+ };
784
+ return _registeredProviders[id];
785
+ }
786
+ function deregisterProvider(id) {
787
+ delete _registeredProviders[id];
788
+ }
789
+ function updateProvider(id, updates) {
790
+ if (_registeredProviders[id]) {
791
+ _registeredProviders[id] = { ..._registeredProviders[id], ...updates };
792
+ return _registeredProviders[id];
793
+ }
794
+ return null;
795
+ }
796
+ // ============================================================
797
+ // HEALTH CHECK
798
+ // ============================================================
799
+ async function healthCheck(providerId) {
800
+ const provider = _registeredProviders[providerId];
801
+ if (!provider) {
802
+ return { healthy: false, error: 'Provider not found: ' + providerId };
803
+ }
804
+ if (provider.type === 'cli') {
805
+ const { execSync } = require('child_process');
806
+ try {
807
+ execSync(`which ${provider.cliCommand || provider.id}`, { stdio: 'pipe' });
808
+ return { healthy: true, latency: 0, type: 'cli' };
809
+ }
810
+ catch {
811
+ return { healthy: false, error: 'Command not found: ' + (provider.cliCommand || provider.id) };
812
+ }
813
+ }
814
+ if (provider.type === 'api') {
815
+ if (!provider.apiKey) {
816
+ return { healthy: false, error: 'No API key for ' + provider.name };
817
+ }
818
+ const startTime = Date.now();
819
+ try {
820
+ const model = provider.models[0];
821
+ const resp = await fetch(provider.baseUrl, {
822
+ method: 'POST',
823
+ headers: {
824
+ 'Authorization': 'Bearer ' + provider.apiKey,
825
+ 'Content-Type': 'application/json',
826
+ },
827
+ body: JSON.stringify({
828
+ model,
829
+ messages: [{ role: 'user', content: 'test' }],
830
+ max_tokens: 5,
831
+ }),
832
+ });
833
+ const latency = Date.now() - startTime;
834
+ const data = await resp.json();
835
+ if (data.error) {
836
+ return { healthy: false, error: data.error.message, latency };
837
+ }
838
+ return { healthy: true, latency, model: data.model || model };
839
+ }
840
+ catch (e) {
841
+ return { healthy: false, error: e.message, latency: Date.now() - startTime };
842
+ }
843
+ }
844
+ return { healthy: false, error: 'Unknown provider type: ' + provider.type };
845
+ }
846
+ async function checkAllProviders() {
847
+ const results = {};
848
+ const available = getAvailableProviders();
849
+ for (const id of Object.keys(available)) {
850
+ results[id] = await healthCheck(id);
851
+ }
852
+ return results;
853
+ }
854
+ function findCheapestAvailableProvider(model) {
855
+ const available = getAvailableProviders();
856
+ const sorted = Object.values(available).sort((a, b) => (a.costPerK.input + a.costPerK.output) - (b.costPerK.input + b.costPerK.output));
857
+ if (model) {
858
+ return sorted.find(p => p.models.includes(model)) || null;
859
+ }
860
+ return sorted[0] || null;
861
+ }
862
+ function findFastestAvailableProvider() {
863
+ const available = getAvailableProviders();
864
+ const cheapTier = Object.values(available).filter(p => p.tier === 'cheap');
865
+ return cheapTier[0] || Object.values(available)[0] || null;
866
+ }
867
+ // ============================================================
868
+ // SAVE CONFIG
869
+ // ============================================================
870
+ function saveConfig(configPath) {
871
+ const target = configPath || path.join(process.env.HOME || '.', '.config', 'a3m-router', 'providers.json');
872
+ const dir = path.dirname(target);
873
+ if (!fs.existsSync(dir)) {
874
+ fs.mkdirSync(dir, { recursive: true });
875
+ }
876
+ const safeConfig = {};
877
+ for (const [id, provider] of Object.entries(_registeredProviders)) {
878
+ safeConfig[id] = { ...provider };
879
+ delete safeConfig[id].apiKey;
880
+ }
881
+ fs.writeFileSync(target, JSON.stringify({ providers: safeConfig }, null, 2));
882
+ return target;
883
+ }