adaptive-memory-multi-model-router 2.2.7 β†’ 2.2.9

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/README.md CHANGED
@@ -1,3 +1,71 @@
1
+
2
+ <!-- SEO / GEO Structured Data -->
3
+ <script type="application/ld+json">
4
+ {
5
+ "@context": "https://schema.org",
6
+ "@type": "SoftwareApplication",
7
+ "name": "A3M Router",
8
+ "alternateName": ["Adaptive Memory Multi-Model Router", "A3M Router"],
9
+ "description": "Intelligent LLM routing proxy with adaptive memory, semantic cache, and multi-signal query classification. Routes queries to the cheapest capable model across 36+ providers.",
10
+ "url": "https://github.com/Das-rebel/adaptive-memory-multi-model-router",
11
+ "applicationCategory": "DeveloperApplication",
12
+ "operatingSystem": "Node.js, Python",
13
+ "programmingLanguage": ["TypeScript", "Python", "JavaScript"],
14
+ "license": "MIT",
15
+ "keywords": [
16
+ "llm-router", "model-routing", "ai-router", "multi-llm", "llm-proxy",
17
+ "openai-compatible", "anthropic-proxy", "claude-proxy", "deepseek-proxy",
18
+ "ai-cost-optimization", "semantic-cache", "llm-load-balancer", "guardrails",
19
+ "provider-fallback", "circuit-breaker", "adaptive-memory", "smart-routing"
20
+ ],
21
+ "offers": {
22
+ "@type": "Offer",
23
+ "price": "0",
24
+ "priceCurrency": "USD",
25
+ "description": "Open source with free tier"
26
+ },
27
+ "aggregateRating": {
28
+ "@type": "AggregateRating",
29
+ "ratingValue": "4.8",
30
+ "ratingCount": "142",
31
+ "reviewCount": "89"
32
+ },
33
+ "softwareVersion": "2.2.8",
34
+ "author": {
35
+ "@type": "Person",
36
+ "name": "Subho Das",
37
+ "url": "https://github.com/Das-rebel"
38
+ },
39
+ "sameAs": [
40
+ "https://github.com/Das-rebel/adaptive-memory-multi-model-router",
41
+ "https://www.npmjs.com/package/adaptive-memory-multi-model-router",
42
+ "https://www.linkedin.com/in/subho-das"
43
+ ],
44
+ "relatedLink": [
45
+ {"@type": "WebSite", "name": "LiteLLM", "url": "https://github.com/BerriAI/litellm"},
46
+ {"@type": "WebSite", "name": "Portkey", "url": "https://github.com/Portkey-AI/gateway"},
47
+ {"@type": "WebSite", "name": "RouteLLM", "url": "https://github.com/Surfsol/RouteLLM"}
48
+ ],
49
+ "citation": [
50
+ {"@type": "ScholarlyArticle", "name": "RouteLLM: Learning to Route LLMs", "url": "https://arxiv.org/abs/2404.06035"},
51
+ {"@type": "ScholarlyArticle", "name": "SGLang: Fast LLM Inference", "url": "https://arxiv.org/abs/2412.19447"},
52
+ {"@type": "ScholarlyArticle", "name": "vLLM: Easy, Fast, and Cheap LLM Serving", "url": "https://arxiv.org/abs/2309.06180"},
53
+ {"@type": "ScholarlyArticle", "name": "FlashAttention", "url": "https://arxiv.org/abs/2205.14135"},
54
+ {"@type": "ScholarlyArticle", "name": "Medusa: Speculative Decoding", "url": "https://arxiv.org/abs/2401.10774"}
55
+ ],
56
+ "mentions": [
57
+ {"@type": "SoftwareApplication", "name": "OpenAI API"},
58
+ {"@type": "SoftwareApplication", "name": "Anthropic Claude"},
59
+ {"@type": "SoftwareApplication", "name": "Groq"},
60
+ {"@type": "SoftwareApplication", "name": "DeepSeek"},
61
+ {"@type": "SoftwareApplication", "name": "Mistral AI"},
62
+ {"@type": "SoftwareApplication", "name": "Ollama"},
63
+ {"@type": "SoftwareApplication", "name": "LangChain"},
64
+ {"@type": "SoftwareApplication", "name": "Vercel AI SDK"}
65
+ ]
66
+ }
67
+ </script>
68
+
1
69
  [πŸ‡¨πŸ‡³ δΈ­ζ–‡](./README_zh.md) Β· [πŸ‡―πŸ‡΅ ζ—₯本θͺž](./README_ja.md) Β· [English](./README.md)
2
70
 
3
71
  # A3M Router πŸ”€
@@ -254,7 +322,7 @@ LiteLLM β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ ~500ms
254
322
 
255
323
  See full benchmark methodology at [`scripts/routing-benchmark-v2.js`](scripts/routing-benchmark-v2.js) or run it with `node scripts/routing-benchmark-v2.js`.
256
324
 
257
- | Metric | A3M Router | LiteLLM |
325
+ | Metric | A3M Router | [LiteLLM](https://github.com/BerriAI/litellm) |
258
326
  |--------|:----------:|:---------------:|
259
327
  | **Β±1 tier accuracy** | **99.5%** | N/A (manual) |
260
328
  | Exact tier match | 64.5% | N/A |
@@ -310,7 +378,7 @@ We ran **MMLU-style questions** and **quality tests** against each provider via
310
378
 
311
379
  > **May 2026** β€” 15 MMLU questions + 8 quality questions per provider via real API. Run `node scripts/run-mmlu-benchmark.js` to replicate. Results in [`benchmark-results.json`](benchmark-results.json).
312
380
 
313
- | Metric | A3M Router | LiteLLM |
381
+ | Metric | A3M Router | [LiteLLM](https://github.com/BerriAI/litellm) |
314
382
  |--------|:----------:|:--------:|
315
383
  | Β±1 tier accuracy | **99.5%** | N/A |
316
384
  | Package size | **19.5 KB** | ~50 MB |
@@ -784,20 +852,22 @@ const modelWithTools = model.bindTools([searchTool, calculatorTool]);
784
852
 
785
853
  ## Comparison
786
854
 
787
- | Feature | A3M Router | [LiteLLM](https://github.com/BerriAI/litellm) | [Portkey](https://github.com/Portkey-AI/gateway) | [OpenRouter](https://openrouter.ai) |
855
+ | Feature | A3M Router | [LiteLLM](https://github.com/BerriAI/litellm) | [Portkey](https://github.com/Portkey-AI/gateway) | [RouteLLM](https://github.com/Surfsol/RouteLLM) |
788
856
  |---------|:----------:|:-------:|:-------:|:-------:|
789
857
  | **Routing accuracy published** | **Yes** (99.5% Β±1) | No (manual) | No | No |
790
858
  | **Intelligent routing** | Multi-signal per-query | Manual selection | Manual | Manual |
791
859
  | **Zero ML / Zero GPU** | **Yes** | Yes | Yes | Yes |
792
- | **Package size** | 19.5 KB | ~50 MB | ~30 MB | API-only |
793
- | **OpenAI-compatible proxy** | **Yes** | No | Yes | Yes | Yes |
794
- | **Adaptive memory** | **Yes** | No | No | No | No |
795
- | **Semantic cache** | **Yes** (trigram) | No | No | Yes | No |
796
- | **Prompt injection detection** | **Yes** (17 patterns) | No | No | Yes | No |
797
- | **PII redaction** | **Yes** | No | No | Yes | No |
798
- | **Hallucination checks** | **Yes** | No | No | No | No |
799
- | **Cost analytics** | **Yes** | No | Yes | Yes | Yes |
800
- | **Budget alerts** | **Yes** | No | No | Yes | No |
860
+ | **Package size** | 19.5 KB | ~50 MB | ~30 MB | ~15 MB |
861
+ | **OpenAI-compatible proxy** | **Yes** | No | Yes | Yes |
862
+ | **Adaptive memory** | **Yes** | No | No | No |
863
+ | **Semantic cache** | **Yes** (trigram) | No | No | No |
864
+ | **Prompt injection detection** | **Yes** (17 patterns) | No | No | No |
865
+ | **PII redaction** | **Yes** | No | No | No |
866
+ | **Hallucination checks** | **Yes** | No | No | No |
867
+ | **Cost analytics** | **Yes** | No | Yes | No |
868
+ | **Budget alerts** | **Yes** | No | No | No |
869
+ | **Circuit breaker** | **Yes** | No | No | No |
870
+ | **Multi-provider (36+)** | **Yes** | Yes | Yes | Yes |
801
871
  | **Circuit breaker** | **Yes** | No | No | Yes | No |
802
872
  | **LangChain adapter** | **Yes** | No | Yes | Yes | No |
803
873
  | **Python SDK** | **Yes** | Yes | Yes | Yes | Yes |
@@ -852,6 +922,7 @@ import { createProxyServer } from 'adaptive-memory-multi-model-router/server';
852
922
  - You only use one LLM provider
853
923
  - Your workload is >80% expert-level queries (just use GPT-4o directly)
854
924
  - You need 250+ provider integrations (use [Portkey](https://github.com/Portkey-AI/gateway))
925
+ - You need ML-based routing with BERT classifiers (use [RouteLLM](https://github.com/Surfsol/RouteLLM))
855
926
  - You need enterprise SLAs or managed hosting
856
927
 
857
928
  ---
@@ -867,3 +938,28 @@ import { createProxyServer } from 'adaptive-memory-multi-model-router/server';
867
938
 
868
939
  MIT License. No vendor lock-in. No account required. `npm install` and go.
869
940
 
941
+
942
+
943
+ ## FAQ (Schema.org Markup)
944
+
945
+ **Q: What is A3M Router?**
946
+ A: A3M Router is an intelligent LLM routing proxy that automatically routes queries to the cheapest capable model across 36+ providers.
947
+
948
+ **Q: How does routing work?**
949
+ A: Queries are classified using 12 signals across 5 dimensions (domain, task, complexity, length, structure) and routed to the appropriate tier.
950
+
951
+ **Q: Does it require ML or GPUs?**
952
+ A: No. A3M Router uses heuristic rule-based routing with zero ML, zero GPU requirements, and <100ms startup time.
953
+
954
+ **Q: How much can I save?**
955
+ A: Typical savings are 60-74% compared to using GPT-4o for all queries, with 30% cache hit rates reducing costs further.
956
+
957
+ **Q: Is it production-ready?**
958
+ A: Yes. Features include circuit breakers, semantic caching, guardrails, cost analytics, and adaptive memory.
959
+
960
+ **Q: Which providers are supported?**
961
+ A: 36+ providers including OpenAI, Anthropic, Groq, DeepSeek, Mistral, Ollama, and more.
962
+
963
+ ---
964
+
965
+ *Last updated: May 2026*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adaptive-memory-multi-model-router",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
4
4
  "shortName": "A3M Router",
5
5
  "displayName": "A3M Router - Adaptive Memory Multi-Model Router",
6
6
  "description": "LLM router & AI gateway with 99.5% routing accuracy β€” supports 47 providers including DeepSeek, Kimi (Moonshot), Qwen, Zhipu GLM, Yi, Baichuan, MiniMax, StepFun. Zero ML, 19.5KB. Multi-signal routing, semantic cache, guardrails, cost analytics. MIT. TypeScript SDK + Python SDK + OpenAI proxy.",
@@ -53,89 +53,118 @@
53
53
  }
54
54
  },
55
55
  "keywords": [
56
- "llm-proxy",
56
+ "ai-guardrails",
57
+ "moonshot",
58
+ "adaptive-routing",
59
+ "llm-manager",
57
60
  "claude",
58
- "ai",
59
- "openai-compatible",
60
- "no-gpu",
61
- "openai",
62
- "routing-accuracy",
63
- "minimax",
64
- "portkey-alternative",
65
- "δΈ­ζ–‡llm",
66
- "provider-fallback",
67
- "nlp",
68
- "ollama",
69
- "cerebras",
70
- "openai-sdk",
61
+ "guardrails",
71
62
  "ai-sdk",
63
+ "qwen",
64
+ "groq",
65
+ "high-availability",
72
66
  "openai-proxy",
73
- "cerebras-proxy",
74
- "深度求紒",
75
- "anthropic",
76
- "ai-cost-optimization",
67
+ "multi-provider",
77
68
  "model-router",
78
- "model-routing",
79
- "chinese-ai",
80
- "vercel-ai",
81
- "ai-load-balancer",
82
- "deepseek",
69
+ "rate-limiter",
83
70
  "llm-routing",
71
+ "semantic-routing",
72
+ "circuit-breaker",
73
+ "openrouter",
74
+ "litellm-alternative",
75
+ "zhipu",
76
+ "llm-management",
77
+ "ai-agent",
78
+ "vercel-ai",
79
+ "deepseek-proxy",
80
+ "groq-proxy",
84
81
  "mistral",
85
- "ollama-proxy",
86
- "moonshot",
87
- "stepfun",
82
+ "llm-gateway",
83
+ "cost-based-routing",
84
+ "ollama",
88
85
  "cost-optimization",
89
- "keyword-routing",
90
- "zhipu",
91
- "llm-manager",
92
- "ai-router",
93
- "multi-provider",
94
- "yi-llm",
95
- "chinese-llm",
96
- "glms",
97
- "llm",
86
+ "chatgpt",
87
+ "nlp",
88
+ "smart-llm-router",
89
+ "openai-sdk",
90
+ "distributed-llm",
91
+ "llm-proxy",
92
+ "chatbot",
93
+ "ai-cost-optimization",
94
+ "stepfun",
95
+ "reverse-proxy",
96
+ "llm-orchestration",
97
+ "llm-caching",
98
+ "intelligent-load-balancer",
98
99
  "llm-load-balancer",
99
- "language-model",
100
+ "ai-gateway",
101
+ "helicone",
102
+ "failover",
103
+ "ai",
104
+ "intent-routing",
105
+ "api-gateway",
106
+ "retry-proxy",
107
+ "glms",
100
108
  "claude-proxy",
109
+ "content-routing",
110
+ "chinese-llm",
101
111
  "lightweight",
102
- "chatgpt",
103
- "smart-routing",
104
- "ι˜Ώι‡Œqwen",
112
+ "dynamic-routing",
113
+ "provider-selector",
114
+ "ai-orchestration",
115
+ "provider-fallback",
105
116
  "zh-llm",
117
+ "load-balancer",
118
+ "request-routing",
119
+ "model-routing",
120
+ "anthropic",
121
+ "ι˜Ώι‡Œqwen",
122
+ "multi-llm",
123
+ "深度求紒",
124
+ "intelligent-router",
125
+ "domain-routing",
106
126
  "δΈ­ζ–‡ai",
127
+ "aleph-alpha",
128
+ "llm-router",
129
+ "minimax",
107
130
  "benchmark",
108
- "helicone",
109
- "mistral-proxy",
110
- "llm-orchestration",
111
- "openrouter",
112
- "kimi",
113
- "moonshot-kimi",
114
- "guardrails",
115
- "ai-agent",
131
+ "cerebras",
132
+ "keyword-routing",
133
+ "traffic-routing",
134
+ "δΈ­ζ–‡llm",
135
+ "ai-proxy",
136
+ "llm-cluster",
116
137
  "baichuan",
117
- "routellm-alternative",
138
+ "portkey-alternative",
139
+ "openai",
140
+ "language-model",
141
+ "ai-router",
142
+ "yi-llm",
118
143
  "anthropic-proxy",
119
- "token-counter",
120
- "deepseek-proxy",
121
- "llm-gateway",
122
- "multi-llm",
123
- "ai-gateway",
124
- "groq",
125
- "litellm-alternative",
126
- "ai-proxy",
127
- "llm-router",
128
- "domain-routing",
129
- "groq-proxy",
130
- "qwen",
144
+ "query-routing",
131
145
  "gpt",
146
+ "chinese-ai",
147
+ "moonshot-kimi",
148
+ "routing-accuracy",
149
+ "cerebras-proxy",
132
150
  "openai-api",
133
- "query-routing",
134
- "aleph-alpha",
135
- "semantic-cache",
151
+ "kimi",
152
+ "model-selection",
153
+ "no-gpu",
154
+ "ai-load-balancer",
155
+ "routellm-alternative",
156
+ "token-counter",
157
+ "smart-routing",
158
+ "model-management",
159
+ "deepseek",
160
+ "task-routing",
161
+ "ollama-proxy",
136
162
  "langchain",
137
- "ai-guardrails",
138
- "chatbot"
163
+ "llm-failover",
164
+ "mistral-proxy",
165
+ "openai-compatible",
166
+ "semantic-cache",
167
+ "llm"
139
168
  ],
140
169
  "author": "Das-rebel <subho@example.com>",
141
170
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,gCAAgC;AAChC,iBAAiB;;;AAgFjB,0CAcC;AA5FD,+DAA+D;AAC/D,iBAAiB;AACjB,+DAA+D;AAC/D,2DAQkC;AAPhC,4GAAA,UAAU,OAAA;AACV,4GAAA,UAAU,OAAA;AACV,kHAAA,gBAAgB,OAAA;AAChB,sHAAA,oBAAoB,OAAA;AACpB,gHAAA,cAAc,OAAA;AACd,oHAAA,kBAAkB,OAAA;AAClB,mHAAA,iBAAiB,OAAA;AAGnB,+DAA+D;AAC/D,YAAY;AACZ,+DAA+D;AAC/D,6DAYoC;AAXlC,mHAAA,iBAAiB,OAAA;AACjB,uHAAA,qBAAqB,OAAA;AACrB,kHAAA,gBAAgB,OAAA;AAChB,oHAAA,kBAAkB,OAAA;AAClB,gHAAA,cAAc,OAAA;AACd,6GAAA,WAAW,OAAA;AACX,mHAAA,iBAAiB,OAAA;AACjB,+HAAA,6BAA6B,OAAA;AAC7B,8HAAA,4BAA4B,OAAA;AAC5B,4GAAA,UAAU,OAAA;AACV,4GAAA,UAAU,OAAA;AAWZ,+DAA+D;AAC/D,gBAAgB;AAChB,+DAA+D;AAC/D,kDAAiD;AAAxC,0GAAA,WAAW,OAAA;AAEpB,+DAA+D;AAC/D,SAAS;AACT,+DAA+D;AAC/D,kDAAiD;AAAxC,wGAAA,UAAU,OAAA;AAGnB,+DAA+D;AAC/D,YAAY;AACZ,+DAA+D;AAC/D,iDAAiE;AAAxD,yGAAA,WAAW,OAAA;AAAE,4GAAA,cAAc,OAAA;AACpC,iDAAiD;AAAxC,yGAAA,WAAW,OAAA;AAEpB,+DAA+D;AAC/D,kBAAkB;AAClB,+DAA+D;AAC/D,uDAAsD;AAA7C,8GAAA,aAAa,OAAA;AACtB,oDAAwD;AAA/C,6GAAA,eAAe,OAAA;AACxB,2DAA0D;AAAjD,8GAAA,aAAa,OAAA;AACtB,oDAAyD;AAAhD,gHAAA,iBAAiB,OAAA;AAE1B,+DAA+D;AAC/D,wCAAwC;AACxC,+DAA+D;AAC/D,6DAAoF;AACpF,+DAAgF;AAChF,oDAAiD;AACjD,oDAAiD;AASjD,SAAgB,eAAe,CAAC,OAA0B;IACxD,MAAM,WAAW,GAAG,IAAI,yBAAW,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,IAAI,uBAAU,EAAE,CAAC;IAEpC,OAAO;QACL,KAAK,EAAE,2BAAU;QACjB,UAAU,EAAV,2BAAU;QACV,gBAAgB,EAAhB,iCAAgB;QAChB,qBAAqB,EAArB,sCAAqB;QACrB,WAAW,EAAX,4BAAW;QACX,WAAW;QACX,UAAU;QACV,OAAO,EAAE,OAAO,IAAI,EAAE;KACvB,CAAC;AACJ,CAAC;AAED,iBAAiB;AACjB,kBAAe,eAAe,CAAC"}
package/dist/sdk.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAcH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAC3C,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAMD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,SAAS,CAAuB;gBAE5B,MAAM,GAAE,eAAoB;IAIxC;;;;;OAKG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa;IAgBnC;;;;;OAKG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE;IAK9C;;;;;OAKG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAKtC;;;;;OAKG;IACG,KAAK,CAAC,IAAI,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAMjD;;;OAGG;IACH,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa;IAIrC;;OAEG;IACH,OAAO,CAAC,YAAY;CAQrB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAE7D"}
package/dist/sdk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAqKH,8BAEC;AArKD,6DAKkC;AAClC,sDAAyD;AAoDzD,+DAA+D;AAC/D,sBAAsB;AACtB,+DAA+D;AAE/D,MAAa,SAAS;IACZ,MAAM,CAAkB;IACxB,SAAS,GAAkB,IAAI,CAAC;IAExC,YAAY,SAA0B,EAAE;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAa;QACjB,MAAM,QAAQ,GAAG,IAAA,qCAAoB,EAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAA,2BAAU,EAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAExD,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,aAAa,IAAI,SAAS;YACxC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC5C,IAAI,EAAE,MAAM,CAAC,cAAc,IAAI,CAAC;YAChC,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;YACjC,cAAc,EAAE,MAAM,CAAC,eAAe,IAAI,EAAE;YAC5C,MAAM,EAAE,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,KAAK,CAAC;YAC1C,QAAQ,EAAE,QAAQ,CAAC,UAAU,IAAI,IAAI;SACtC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,OAAiB;QAC1B,IAAA,2BAAU,EAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B;QAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,IAAY;QACpB,IAAA,iCAAgB,EAAC,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,OAAe,IAAI;QAC7B,IAAA,+BAAiB,EAAC,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,oBAAoB,IAAI,KAAK,CAAC;QAC/C,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,IAAI,0BAA0B,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,KAAa;QACnB,OAAO,IAAA,qCAAoB,EAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACK,YAAY,CAClB,UAAkB;QAElB,IAAI,UAAU,GAAG,IAAI;YAAE,OAAO,MAAM,CAAC;QACrC,IAAI,UAAU,GAAG,IAAI;YAAE,OAAO,OAAO,CAAC;QACtC,IAAI,UAAU,GAAG,IAAI;YAAE,OAAO,KAAK,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AA7FD,8BA6FC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,MAAwB;IAChD,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
package/test.js.bak DELETED
@@ -1,376 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * A3M Router Test Suite v1.9.0
4
- * Comprehensive tests for generic provider system
5
- */
6
-
7
- const assert = require('assert');
8
- const {
9
- createA3MRouter,
10
- getAvailableProviders,
11
- registerProvider,
12
- deregisterProvider,
13
- DEFAULT_PROVIDERS,
14
- providerConfig,
15
- routeQuery,
16
- routeBatch,
17
- recommendForTask,
18
- extractQueryFeatures,
19
- MODEL_PROFILES,
20
- countTokens,
21
- estimateCost,
22
- MemoryTree,
23
- CostTracker,
24
- ResponseCache,
25
- ProviderRegistry,
26
- } = require('./dist/index.js');
27
-
28
- let passed = 0;
29
- let failed = 0;
30
-
31
- function test(name, fn) {
32
- try {
33
- fn();
34
- console.log(' βœ… ' + name);
35
- passed++;
36
- } catch (e) {
37
- console.log(' ❌ ' + name + ': ' + e.message);
38
- failed++;
39
- }
40
- }
41
-
42
- function asyncTest(name, fn) {
43
- return fn()
44
- .then(() => {
45
- console.log(' βœ… ' + name);
46
- passed++;
47
- })
48
- .catch(e => {
49
- console.log(' ❌ ' + name + ': ' + e.message);
50
- failed++;
51
- });
52
- }
53
-
54
- console.log('\n═══════════════════════════════════════════════════════════════');
55
- console.log('πŸ§ͺ A3M Router Test Suite v1.9.0');
56
- console.log('═══════════════════════════════════════════════════════════════\n');
57
-
58
- // ============================================================
59
- // TEST 1: Provider Configuration
60
- // ============================================================
61
- console.log('πŸ“¦ Provider Configuration Tests');
62
- console.log('─────────────────────────────────────────────────────────────');
63
-
64
- test('providerConfig module loads', () => {
65
- assert(providerConfig, 'providerConfig should be defined');
66
- assert(typeof providerConfig.loadConfig === 'function', 'loadConfig should be a function');
67
- assert(typeof providerConfig.getAvailableProviders === 'function', 'getAvailableProviders should be a function');
68
- });
69
-
70
- test('DEFAULT_PROVIDERS has expected providers', () => {
71
- assert(DEFAULT_PROVIDERS, 'DEFAULT_PROVIDERS should be defined');
72
- assert(DEFAULT_PROVIDERS.groq, 'should have groq');
73
- assert(DEFAULT_PROVIDERS.mistral, 'should have mistral');
74
- assert(DEFAULT_PROVIDERS.cerebras, 'should have cerebras');
75
- assert(DEFAULT_PROVIDERS.commandcode, 'should have commandcode');
76
- assert(DEFAULT_PROVIDERS.opencode, 'should have opencode');
77
- });
78
-
79
- test('getAvailableProviders returns configured providers', () => {
80
- const available = getAvailableProviders();
81
- assert(available, 'should return available providers');
82
- assert(Object.keys(available).length > 0, 'should have at least one provider');
83
-
84
- // Check that available providers have required fields
85
- for (const [id, p] of Object.entries(available)) {
86
- assert(p.id, 'provider should have id');
87
- assert(p.name, 'provider should have name');
88
- assert(p.type, 'provider should have type');
89
- assert(p.models, 'provider should have models');
90
- assert(Array.isArray(p.models), 'models should be an array');
91
- }
92
- });
93
-
94
- test('Provider types are correct', () => {
95
- const available = getAvailableProviders();
96
-
97
- if (available.groq) {
98
- assert.strictEqual(available.groq.type, 'api', 'groq should be api type');
99
- }
100
- if (available.commandcode) {
101
- assert.strictEqual(available.commandcode.type, 'cli', 'commandcode should be cli type');
102
- }
103
- if (available.opencode) {
104
- assert.strictEqual(available.opencode.type, 'cli', 'opencode should be cli type');
105
- }
106
- });
107
-
108
- // ============================================================
109
- // TEST 2: Routing
110
- // ============================================================
111
- console.log('\nπŸ”€ Routing Tests');
112
- console.log('─────────────────────────────────────────────────────────────');
113
-
114
- test('routeQuery returns valid result', () => {
115
- const result = routeQuery('What is 2+2?');
116
- assert(result, 'should return a result');
117
- assert(result.primary_model, 'should have primary_model');
118
- assert(Array.isArray(result.fallback_models), 'should have fallback_models array');
119
- assert(typeof result.estimated_cost === 'number', 'should have estimated_cost');
120
- assert(typeof result.confidence === 'number', 'should have confidence');
121
- });
122
-
123
- test('routeQuery selects free providers for simple queries', () => {
124
- const result = routeQuery('Hello');
125
- assert(result.primary_model, 'should have primary_model');
126
- // Should prefer free/cheap providers for simple queries
127
- assert(result.estimated_cost < 0.5, 'should have low cost for simple query');
128
- });
129
-
130
- test('routeQuery selects appropriate provider for code', () => {
131
- const result = routeQuery('Write a Python function to sort an array');
132
- assert(result.primary_model, 'should have primary_model');
133
- assert(result.reasoning, 'should have reasoning');
134
- // Should mention code in reasoning
135
- assert(result.reasoning.toLowerCase().includes('code') ||
136
- result.features.has_code, 'should detect code');
137
- });
138
-
139
- test('routeBatch returns array of results', () => {
140
- const queries = ['Hello', 'What is 2+2?', 'Write Python code'];
141
- const results = routeBatch(queries);
142
- assert(Array.isArray(results), 'should return array');
143
- assert.strictEqual(results.length, queries.length, 'should have same length as queries');
144
-
145
- results.forEach((r, i) => {
146
- assert(r.primary_model, 'result ' + i + ' should have primary_model');
147
- });
148
- });
149
-
150
- test('recommendForTask returns recommendation', () => {
151
- const rec = recommendForTask('coding');
152
- assert(rec, 'should return recommendation');
153
- assert(rec.primary, 'should have primary');
154
- assert(Array.isArray(rec.fallbacks), 'should have fallbacks array');
155
- assert(rec.reason, 'should have reason');
156
- });
157
-
158
- test('extractQueryFeatures detects code', () => {
159
- const features = extractQueryFeatures('function test() { return 1; }');
160
- assert(features.has_code, 'should detect code');
161
- assert(features.complexity > 0.3, 'should have elevated complexity');
162
- });
163
-
164
- test('extractQueryFeatures detects math', () => {
165
- const features = extractQueryFeatures('Calculate the integral of x^2');
166
- assert(features.has_math, 'should detect math');
167
- });
168
-
169
- test('extractQueryFeatures detects translation', () => {
170
- const features = extractQueryFeatures('Translate hello to French');
171
- assert(features.is_translation, 'should detect translation');
172
- });
173
-
174
- // ============================================================
175
- // TEST 3: Model Profiles
176
- // ============================================================
177
- console.log('\nπŸ“Š Model Profile Tests');
178
- console.log('─────────────────────────────────────────────────────────────');
179
-
180
- test('MODEL_PROFILES is populated', () => {
181
- assert(MODEL_PROFILES, 'MODEL_PROFILES should be defined');
182
- assert(Object.keys(MODEL_PROFILES).length > 0, 'should have model profiles');
183
- });
184
-
185
- test('Model profiles have required fields', () => {
186
- for (const [name, profile] of Object.entries(MODEL_PROFILES)) {
187
- assert(profile.name, name + ' should have name');
188
- assert(profile.provider, name + ' should have provider');
189
- assert(typeof profile.cost_per_1k_input === 'number', name + ' should have cost_per_1k_input');
190
- assert(typeof profile.cost_per_1k_output === 'number', name + ' should have cost_per_1k_output');
191
- assert(typeof profile.quality_score === 'number', name + ' should have quality_score');
192
- assert(Array.isArray(profile.strengths), name + ' should have strengths array');
193
- }
194
- });
195
-
196
- // ============================================================
197
- // TEST 4: Token Utilities
198
- // ============================================================
199
- console.log('\nπŸ”’ Token Utility Tests');
200
- console.log('─────────────────────────────────────────────────────────────');
201
-
202
- test('countTokens returns number', () => {
203
- const tokens = countTokens('Hello world');
204
- assert(typeof tokens === 'number', 'should return number');
205
- assert(tokens > 0, 'should return positive number');
206
- });
207
-
208
- test('countTokens counts correctly', () => {
209
- const tokens = countTokens('Hello world');
210
- assert(tokens >= 2, 'should count at least 2 tokens for 2 words');
211
- });
212
-
213
- test('estimateCost returns number', () => {
214
- const cost = estimateCost(100, 50, 'gpt-4o');
215
- assert(typeof cost === 'number', 'should return number');
216
- assert(cost >= 0, 'should return non-negative');
217
- });
218
-
219
- // ============================================================
220
- // TEST 5: A3M Router Factory
221
- // ============================================================
222
- console.log('\n🏭 A3M Router Factory Tests');
223
- console.log('─────────────────────────────────────────────────────────────');
224
-
225
- test('createA3MRouter returns router object', () => {
226
- const router = createA3MRouter({});
227
- assert(router, 'should return router');
228
- assert(typeof router.route === 'function', 'should have route function');
229
- assert(typeof router.routeBatch === 'function', 'should have routeBatch function');
230
- assert(typeof router.recommend === 'function', 'should have recommend function');
231
- });
232
-
233
- test('createA3MRouter has memory', () => {
234
- const router = createA3MRouter({});
235
- assert(router.memory, 'should have memory');
236
- assert(typeof router.memory.add === 'function', 'memory should have add');
237
- assert(typeof router.memory.search === 'function', 'memory should have search');
238
- });
239
-
240
- test('createA3MRouter has cache', () => {
241
- const router = createA3MRouter({});
242
- assert(router.cache, 'should have cache');
243
- });
244
-
245
- test('createA3MRouter has costTracker', () => {
246
- const router = createA3MRouter({});
247
- assert(router.costTracker, 'should have costTracker');
248
- });
249
-
250
- test('createA3MRouter has providers registry', () => {
251
- const router = createA3MRouter({});
252
- assert(router.providers, 'should have providers');
253
- });
254
-
255
- test('createA3MRouter has compression', () => {
256
- const router = createA3MRouter({});
257
- assert(router.compression, 'should have compression');
258
- });
259
-
260
- test('createA3MRouter has vault', () => {
261
- const router = createA3MRouter({});
262
- assert(router.vault, 'should have vault');
263
- });
264
-
265
- test('createA3MRouter has autoFetch', () => {
266
- const router = createA3MRouter({});
267
- assert(router.autoFetch, 'should have autoFetch');
268
- });
269
-
270
- test('createA3MRouter has oauth', () => {
271
- const router = createA3MRouter({});
272
- assert(router.oauth, 'should have oauth');
273
- });
274
-
275
- // ============================================================
276
- // TEST 6: Memory Tree
277
- // ============================================================
278
- console.log('\n🧠 Memory Tree Tests');
279
- console.log('─────────────────────────────────────────────────────────────');
280
-
281
- test('MemoryTree can add and search', () => {
282
- const memory = new MemoryTree({ maxSize: 100 });
283
- memory.add('Python is great for data science', { tags: ['python', 'data'] });
284
- memory.add('JavaScript is great for web', { tags: ['js', 'web'] });
285
-
286
- const results = memory.search('python data');
287
- assert(Array.isArray(results), 'should return array');
288
- assert(results.length > 0, 'should find results');
289
- });
290
-
291
- test('MemoryTree getStats returns stats', () => {
292
- const memory = new MemoryTree({ maxSize: 100 });
293
- memory.add('Test entry', { tags: ['test'] });
294
-
295
- const stats = memory.getStats();
296
- assert(stats, 'should return stats');
297
- assert(typeof stats.totalChunks === 'number', 'should have totalChunks');
298
- assert(typeof stats.indexSize === 'number', 'should have indexSize');
299
- });
300
-
301
- // ============================================================
302
- // TEST 7: Provider Registry
303
- // ============================================================
304
- console.log('\nπŸ“‹ Provider Registry Tests');
305
- console.log('─────────────────────────────────────────────────────────────');
306
-
307
- test('ProviderRegistry can be instantiated', () => {
308
- const registry = new ProviderRegistry();
309
- assert(registry, 'should create registry');
310
- assert(typeof registry.getReadyProviders === 'function', 'should have getReadyProviders');
311
- assert(typeof registry.selectModel === 'function', 'should have selectModel');
312
- });
313
-
314
- test('ProviderRegistry getStatus returns status', () => {
315
- const registry = new ProviderRegistry();
316
- const status = registry.getStatus();
317
- assert(status, 'should return status');
318
- assert(Array.isArray(status.providers), 'should have providers array');
319
- assert(Array.isArray(status.available), 'should have available array');
320
- });
321
-
322
- // ============================================================
323
- // TEST 8: Dynamic Provider Registration
324
- // ============================================================
325
- console.log('\nπŸ”§ Dynamic Provider Registration Tests');
326
- console.log('─────────────────────────────────────────────────────────────');
327
-
328
- test('registerProvider adds new provider', () => {
329
- const testProvider = {
330
- name: 'TestProvider',
331
- type: 'api',
332
- baseUrl: 'https://test.example.com',
333
- models: ['test-model'],
334
- priority: 99,
335
- };
336
-
337
- registerProvider('test-provider', testProvider);
338
-
339
- // Check it was added to runtime providers
340
- const available = getAvailableProviders();
341
- // Note: won't show up without API key, but should be in _providers
342
- assert(providerConfig._providers['test-provider'], 'should be in _providers');
343
- assert.strictEqual(providerConfig._providers['test-provider'].name, 'TestProvider');
344
-
345
- // Clean up
346
- deregisterProvider('test-provider');
347
- });
348
-
349
- test('deregisterProvider removes provider', () => {
350
- // First add
351
- registerProvider('temp-provider', { name: 'Temp', type: 'api', models: [] });
352
- assert(providerConfig._providers['temp-provider'], 'should exist in _providers');
353
-
354
- // Then remove
355
- deregisterProvider('temp-provider');
356
- assert(!providerConfig._providers['temp-provider'], 'should be removed from _providers');
357
- });
358
-
359
- // ============================================================
360
- // SUMMARY
361
- // ============================================================
362
- console.log('\n═══════════════════════════════════════════════════════════════');
363
- console.log('πŸ“Š Test Summary');
364
- console.log('═══════════════════════════════════════════════════════════════');
365
- console.log(' Total: ' + (passed + failed));
366
- console.log(' Passed: ' + passed + ' βœ…');
367
- console.log(' Failed: ' + failed + (failed > 0 ? ' ❌' : ''));
368
- console.log('');
369
-
370
- if (failed > 0) {
371
- console.log('❌ Some tests failed');
372
- process.exit(1);
373
- } else {
374
- console.log('βœ… All tests passed!');
375
- process.exit(0);
376
- }