adaptive-memory-multi-model-router 2.16.0 → 2.16.2

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 (248) hide show
  1. package/.github/workflows/adapters-ci.yml +142 -0
  2. package/.github/workflows/auto-submit-sitemap.yml +41 -0
  3. package/.github/workflows/ci.yml +2 -5
  4. package/.github/workflows/mcp-pypi-publish.yml +34 -0
  5. package/.github/workflows/pypi-publish.yml +146 -0
  6. package/.github/workflows/tmlpd-publish.yml +23 -0
  7. package/README.md +245 -148
  8. package/RELEASE_v2.16.0.md +149 -0
  9. package/TECHNICAL_README.md +253 -0
  10. package/adapters/README.md +36 -0
  11. package/adapters/__init__.py +25 -0
  12. package/adapters/a3m_adapter/__init__.py +51 -0
  13. package/adapters/a3m_adapter/adapter/__init__.py +22 -0
  14. package/adapters/a3m_adapter/adapter/autogen.py +169 -0
  15. package/adapters/a3m_adapter/adapter/config.py +100 -0
  16. package/adapters/a3m_adapter/adapter/haystack.py +197 -0
  17. package/adapters/a3m_adapter/adapter/langchain.py +155 -0
  18. package/adapters/a3m_adapter/adapter/langgraph.py +196 -0
  19. package/adapters/a3m_adapter/adapter/llamaindex.py +162 -0
  20. package/adapters/a3m_adapter/adapter/pinecone.py +217 -0
  21. package/adapters/a3m_adapter/adapter/vercel.py +188 -0
  22. package/adapters/a3m_adapter/tests/__init__.py +1 -0
  23. package/adapters/a3m_adapter/tests/test_adapters.py +118 -0
  24. package/adapters/a3m_adapter/tests/test_integration.py +80 -0
  25. package/adapters/requirements-dev.txt +6 -0
  26. package/adapters/requirements.txt +4 -0
  27. package/adapters/setup.py +23 -0
  28. package/demo.py +251 -0
  29. package/discoverability-diagnosis.md +280 -0
  30. package/dist/analytics/costAnalytics.d.ts.map +1 -1
  31. package/dist/benchmark/reproducible.d.ts.map +1 -1
  32. package/dist/cache/semanticCache.d.ts.map +1 -1
  33. package/dist/cli/setupWizard.d.ts +257 -50
  34. package/dist/cli/setupWizard.d.ts.map +1 -1
  35. package/dist/cli/setupWizard.js +419 -109
  36. package/dist/cli/setupWizard.js.map +1 -1
  37. package/dist/cli/tui.d.ts +6 -0
  38. package/dist/cli/tui.js +96 -67
  39. package/dist/cli/tui.js.map +1 -0
  40. package/dist/cli.js +9 -0
  41. package/dist/cost/budgetEnforcer.d.ts.map +1 -1
  42. package/dist/cost/costTracker.d.ts.map +1 -1
  43. package/dist/ensemble/multiRoundDialog.d.ts.map +1 -1
  44. package/dist/ensemble/shapleyValue.d.ts.map +1 -1
  45. package/dist/ensemble.d.ts +1 -1
  46. package/dist/ensemble.js +141 -0
  47. package/dist/integrations/langchainAdapter.d.ts.map +1 -1
  48. package/dist/integrations/langchainAdapter.js +3 -3
  49. package/dist/integrations/langchainAdapter.js.map +1 -1
  50. package/dist/integrations/oauth.d.ts.map +1 -1
  51. package/dist/integrations/scienceAdapter.d.ts.map +1 -1
  52. package/dist/memory/autoFetch.d.ts.map +1 -1
  53. package/dist/memory/hybridMemory.d.ts.map +1 -1
  54. package/dist/memory/memoryTree.d.ts.map +1 -1
  55. package/dist/memory/obsidianVault.d.ts.map +1 -1
  56. package/dist/memory/reasoningBank.d.ts.map +1 -1
  57. package/dist/observability/metrics.d.ts.map +1 -1
  58. package/dist/observability/tracer.d.ts.map +1 -1
  59. package/dist/providers/providerConfig.d.ts.map +1 -1
  60. package/dist/providers/providerConfig.js +32 -17
  61. package/dist/providers/providerConfig.js.map +1 -1
  62. package/dist/routing/advancedRouter.d.ts.map +1 -1
  63. package/dist/routing/advancedRouter.js +106 -14
  64. package/dist/routing/advancedRouter.js.map +1 -1
  65. package/dist/routing/providerHealth.d.ts.map +1 -1
  66. package/dist/routing/providerRetry.d.ts.map +1 -1
  67. package/dist/routing/shadowSampler.d.ts.map +1 -0
  68. package/dist/routing/shadowSampler.js.map +1 -1
  69. package/dist/security/guardrails.d.ts.map +1 -1
  70. package/dist/server/handlers/chatHandler.d.ts.map +1 -1
  71. package/dist/server/handlers/completionsHandler.d.ts.map +1 -1
  72. package/dist/server/handlers/embeddingsHandler.d.ts.map +1 -1
  73. package/dist/server/handlers/healthHandler.d.ts.map +1 -1
  74. package/dist/server/handlers/metricsHandler.d.ts.map +1 -1
  75. package/dist/server/handlers/modelsHandler.d.ts.map +1 -1
  76. package/dist/server/metrics.d.ts.map +1 -1
  77. package/dist/server/proxyServer.d.ts.map +1 -1
  78. package/dist/server/router.d.ts.map +1 -1
  79. package/dist/server/state.d.ts.map +1 -1
  80. package/dist/skills/__tests__/skill_manager.test.js +5 -265
  81. package/dist/skills/__tests__/skill_manager.test.js.map +1 -1
  82. package/dist/utils/tokenUtils.d.ts.map +1 -1
  83. package/docker-compose.yml +84 -60
  84. package/docs/ARTICLE_Biology_Inspired_Routing.md +208 -0
  85. package/docs/ARTICLE_Master.md +78 -0
  86. package/docs/ARTICLE_Master_CN.md +78 -0
  87. package/docs/ARTICLE_OpenRouter_Stripe.md +140 -0
  88. package/docs/DEVPTO_ARTICLE.md +84 -0
  89. package/docs/HUMAN_STYLE_GUIDE.md +75 -0
  90. package/docs/IMPRINT_PLAN.md +88 -0
  91. package/docs/OPENROUTER_ALTERNATIVE.md +184 -0
  92. package/docs/SOCIAL_CAMPAIGN.md +316 -0
  93. package/docs/anthropic.html +45 -0
  94. package/docs/best-llm-routers-2025.html +157 -0
  95. package/docs/cerebras.html +43 -0
  96. package/docs/cli-cheatsheet.md +286 -212
  97. package/docs/comparison.md +2 -2
  98. package/docs/deepseek.html +44 -0
  99. package/docs/google.html +47 -0
  100. package/docs/groq.html +44 -0
  101. package/docs/llms-full.txt +360 -138
  102. package/docs/llms.txt +70 -71
  103. package/docs/mistral.html +43 -0
  104. package/docs/ollama.html +50 -0
  105. package/docs/openai.html +57 -0
  106. package/docs/sitemap.xml +69 -57
  107. package/docs-site/blog/best-llm-routers-2025.html +157 -0
  108. package/docs-site/index.html +68 -9
  109. package/docs-site/providers/anthropic.html +45 -0
  110. package/docs-site/providers/cerebras.html +43 -0
  111. package/docs-site/providers/deepseek.html +44 -0
  112. package/docs-site/providers/google.html +47 -0
  113. package/docs-site/providers/groq.html +44 -0
  114. package/docs-site/providers/index.html +41 -0
  115. package/docs-site/providers/mistral.html +43 -0
  116. package/docs-site/providers/ollama.html +50 -0
  117. package/docs-site/providers/openai.html +57 -0
  118. package/docs-site/sitemap.xml +69 -0
  119. package/llms.txt +70 -62
  120. package/package.json +44 -182
  121. package/packages/agentkit-adapter/LICENSE +21 -0
  122. package/packages/agentkit-adapter/README.md +126 -0
  123. package/packages/agentkit-adapter/examples/agentkit-example.ts +139 -0
  124. package/packages/agentkit-adapter/package.json +57 -0
  125. package/packages/agentkit-adapter/src/adapter.ts +381 -0
  126. package/packages/agentkit-adapter/src/index.ts +36 -0
  127. package/packages/agentkit-adapter/src/types.ts +105 -0
  128. package/packages/agentkit-adapter/src/util.ts +13 -0
  129. package/packages/agentkit-adapter/tsconfig.json +22 -0
  130. package/prometheus.yml +8 -0
  131. package/python/README.md +35 -81
  132. package/python/a3m/__init__.py +32 -3
  133. package/python/a3m/adapters/__init__.py +21 -0
  134. package/python/a3m/adapters/langchain.py +190 -0
  135. package/python/a3m/adapters/llamaindex.py +249 -0
  136. package/python/a3m/adapters/qdrant.py +240 -0
  137. package/python/a3m/adapters/weaviate.py +263 -0
  138. package/python/a3m/client.py +5 -0
  139. package/python/build_verify.sh +32 -0
  140. package/python/mcp-server/README.md +172 -0
  141. package/python/mcp-server/a3m_mcp/__init__.py +25 -0
  142. package/python/mcp-server/a3m_mcp/__main__.py +15 -0
  143. package/python/mcp-server/a3m_mcp/server.py +205 -0
  144. package/python/mcp-server/pyproject.toml +24 -0
  145. package/python/pyproject.toml +60 -6
  146. package/python/setup.py +3 -28
  147. package/scripts/submit-sitemap.sh +52 -0
  148. package/src/__types__/registry.d.ts +14 -0
  149. package/src/cli/setupWizard.ts +443 -112
  150. package/src/cli/tui.ts +159 -0
  151. package/src/ensemble.ts +154 -1
  152. package/src/integrations/langchainAdapter.ts +2 -2
  153. package/src/providers/providerConfig.ts +32 -17
  154. package/src/providers/registry.js +27 -0
  155. package/src/routing/advancedRouter.ts +99 -14
  156. package/src/routing/shadowSampler.ts +1 -1
  157. package/test-install/package.json +12 -0
  158. package/tests/tsconfig.json +0 -1
  159. package/tmlpd-pi-extension/README.md +105 -44
  160. package/tmlpd-pi-extension/docs/demo.svg +33 -0
  161. package/tmlpd-pi-extension/package.json +35 -106
  162. package/tmlpd-pi-extension/src/tokenOptimization/contextStratifier.ts +163 -0
  163. package/tmlpd-pi-extension/src/tokenOptimization/fetchOnceLocal.ts +136 -0
  164. package/tmlpd-pi-extension/src/tokenOptimization/index.ts +197 -0
  165. package/tmlpd-pi-extension/src/tokenOptimization/interAgentCompression.ts +157 -0
  166. package/tmlpd-pi-extension/src/tokenOptimization/schemaContract.ts +101 -0
  167. package/tmlpd-pi-extension/src/tokenOptimization/semanticCache.ts +248 -0
  168. package/tmlpd-pi-extension/src/tokenOptimization/tokenAwareFallback.ts +192 -0
  169. package/tmlpd-pi-extension/test/verify.js +21 -0
  170. package/tsconfig.build.json +3 -2
  171. package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts +0 -12
  172. package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts.map +0 -1
  173. package/packages/a3m-vercel-ai/dist/a3m-language-model.js +0 -289
  174. package/packages/a3m-vercel-ai/dist/a3m-language-model.js.map +0 -1
  175. package/packages/a3m-vercel-ai/dist/index.d.ts +0 -82
  176. package/packages/a3m-vercel-ai/dist/index.d.ts.map +0 -1
  177. package/packages/a3m-vercel-ai/dist/index.js +0 -79
  178. package/packages/a3m-vercel-ai/dist/index.js.map +0 -1
  179. package/packages/a3m-vercel-ai/dist/types.d.ts +0 -97
  180. package/packages/a3m-vercel-ai/dist/types.d.ts.map +0 -1
  181. package/packages/a3m-vercel-ai/dist/types.js +0 -5
  182. package/packages/a3m-vercel-ai/dist/types.js.map +0 -1
  183. package/src/skills/__tests__/skill_manager.test.ts +0 -328
  184. package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts +0 -114
  185. package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts.map +0 -1
  186. package/tmlpd-pi-extension/dist/cache/prefixCache.js +0 -285
  187. package/tmlpd-pi-extension/dist/cache/prefixCache.js.map +0 -1
  188. package/tmlpd-pi-extension/dist/cache/responseCache.d.ts +0 -58
  189. package/tmlpd-pi-extension/dist/cache/responseCache.d.ts.map +0 -1
  190. package/tmlpd-pi-extension/dist/cache/responseCache.js +0 -153
  191. package/tmlpd-pi-extension/dist/cache/responseCache.js.map +0 -1
  192. package/tmlpd-pi-extension/dist/cli.js +0 -59
  193. package/tmlpd-pi-extension/dist/cost/costTracker.d.ts +0 -95
  194. package/tmlpd-pi-extension/dist/cost/costTracker.d.ts.map +0 -1
  195. package/tmlpd-pi-extension/dist/cost/costTracker.js +0 -240
  196. package/tmlpd-pi-extension/dist/cost/costTracker.js.map +0 -1
  197. package/tmlpd-pi-extension/dist/index.d.ts +0 -723
  198. package/tmlpd-pi-extension/dist/index.d.ts.map +0 -1
  199. package/tmlpd-pi-extension/dist/index.js +0 -239
  200. package/tmlpd-pi-extension/dist/index.js.map +0 -1
  201. package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts +0 -82
  202. package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts.map +0 -1
  203. package/tmlpd-pi-extension/dist/memory/episodicMemory.js +0 -145
  204. package/tmlpd-pi-extension/dist/memory/episodicMemory.js.map +0 -1
  205. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts +0 -102
  206. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts.map +0 -1
  207. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js +0 -207
  208. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js.map +0 -1
  209. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts +0 -85
  210. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts.map +0 -1
  211. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js +0 -210
  212. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js.map +0 -1
  213. package/tmlpd-pi-extension/dist/providers/localProvider.d.ts +0 -102
  214. package/tmlpd-pi-extension/dist/providers/localProvider.d.ts.map +0 -1
  215. package/tmlpd-pi-extension/dist/providers/localProvider.js +0 -338
  216. package/tmlpd-pi-extension/dist/providers/localProvider.js.map +0 -1
  217. package/tmlpd-pi-extension/dist/providers/registry.d.ts +0 -55
  218. package/tmlpd-pi-extension/dist/providers/registry.d.ts.map +0 -1
  219. package/tmlpd-pi-extension/dist/providers/registry.js +0 -138
  220. package/tmlpd-pi-extension/dist/providers/registry.js.map +0 -1
  221. package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts +0 -68
  222. package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts.map +0 -1
  223. package/tmlpd-pi-extension/dist/routing/advancedRouter.js +0 -332
  224. package/tmlpd-pi-extension/dist/routing/advancedRouter.js.map +0 -1
  225. package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts +0 -101
  226. package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts.map +0 -1
  227. package/tmlpd-pi-extension/dist/tools/tmlpdTools.js +0 -368
  228. package/tmlpd-pi-extension/dist/tools/tmlpdTools.js.map +0 -1
  229. package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts +0 -96
  230. package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts.map +0 -1
  231. package/tmlpd-pi-extension/dist/utils/batchProcessor.js +0 -170
  232. package/tmlpd-pi-extension/dist/utils/batchProcessor.js.map +0 -1
  233. package/tmlpd-pi-extension/dist/utils/compression.d.ts +0 -61
  234. package/tmlpd-pi-extension/dist/utils/compression.d.ts.map +0 -1
  235. package/tmlpd-pi-extension/dist/utils/compression.js +0 -281
  236. package/tmlpd-pi-extension/dist/utils/compression.js.map +0 -1
  237. package/tmlpd-pi-extension/dist/utils/reliability.d.ts +0 -74
  238. package/tmlpd-pi-extension/dist/utils/reliability.d.ts.map +0 -1
  239. package/tmlpd-pi-extension/dist/utils/reliability.js +0 -177
  240. package/tmlpd-pi-extension/dist/utils/reliability.js.map +0 -1
  241. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts +0 -117
  242. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts.map +0 -1
  243. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js +0 -246
  244. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js.map +0 -1
  245. package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts +0 -50
  246. package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts.map +0 -1
  247. package/tmlpd-pi-extension/dist/utils/tokenUtils.js +0 -124
  248. package/tmlpd-pi-extension/dist/utils/tokenUtils.js.map +0 -1
package/src/cli/tui.ts ADDED
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * A3M Router Quick TUI
4
+ * Zero-config experience - no API key needed for free tier
5
+ */
6
+
7
+ import * as readline from 'readline';
8
+
9
+ const args = process.argv.slice(2);
10
+
11
+ if (args.length === 0) {
12
+ console.log(`
13
+ ╔══════════════════════════════════════════════╗
14
+ ║ 🔀 A3M Router - Zero Config ║
15
+ ╠══════════════════════════════════════════════╣
16
+ ║ ║
17
+ ║ One prompt in. The right model out. ║
18
+ ║ ║
19
+ ║ Usage: ║
20
+ ║ a3m hello world # Route a query ║
21
+ ║ a3m --serve # Start proxy ║
22
+ ║ a3m --setup # Configure API keys ║
23
+ ║ a3m --demo # Try without keys ║
24
+ ║ ║
25
+ ╚══════════════════════════════════════════════╝
26
+ `);
27
+ process.exit(0);
28
+ }
29
+
30
+ const command = args[0];
31
+
32
+ // Demo mode - works without API key
33
+ if (command === '--demo') {
34
+ console.log(`
35
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
+ 🎮 A3M Router Demo Mode
37
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+
39
+ Try these queries:
40
+ • "Write Python hello world"
41
+ • "Explain quantum physics"
42
+ • "Translate to Japanese: Hello"
43
+ • "Write a haiku about coding"
44
+ `);
45
+
46
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
47
+
48
+ const demoResults = [
49
+ { provider: 'groq', model: 'llama-3.3-70b-versatile', tier: 'free', cost: '$0.00' },
50
+ { provider: 'deepseek', model: 'deepseek-chat-v3', tier: 'cheap', cost: '$0.00' },
51
+ { provider: 'kimi', model: 'moonshot-v1-8k', tier: 'cheap', cost: '$0.00' },
52
+ { provider: 'openai', model: 'gpt-4o-mini', tier: 'paid', cost: '$0.00' },
53
+ ];
54
+
55
+ let count = 0;
56
+ function ask(): void {
57
+ rl.question('\n🔀 Query: ', (q: string) => {
58
+ if (!q || q.toLowerCase() === 'exit') {
59
+ console.log('\n👋 Ready for the real thing? Run: npm install adaptive-memory-multi-model-router\n');
60
+ rl.close();
61
+ return;
62
+ }
63
+ const r = demoResults[count++ % demoResults.length];
64
+ console.log(`\n 🏆 Best: ${r.provider} / ${r.model}`);
65
+ console.log(` 💰 Cost: ${r.cost} (${r.tier})`);
66
+ ask();
67
+ });
68
+ }
69
+ ask();
70
+ return;
71
+ }
72
+
73
+ // Serve mode
74
+ if (command === '--serve') {
75
+ console.log(`
76
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
77
+ 🚀 A3M Router Proxy Server
78
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
79
+
80
+ Server: http://localhost:8787/v1/chat/completions
81
+ API: OpenAI-compatible
82
+
83
+ No API keys configured? Add free tier:
84
+ GROQ_API_KEY=your_key npx a3m-router serve
85
+
86
+ Or run setup: a3m --setup
87
+ `);
88
+
89
+ try {
90
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
91
+ const server = require('../server/proxyServer.js');
92
+ const port = parseInt(args[1], 10) || 8787;
93
+ server.createProxyServer({ port });
94
+ console.log(`\n✅ Server running at http://localhost:${port}`);
95
+ } catch (e: unknown) {
96
+ const msg = e instanceof Error ? e.message : String(e);
97
+ console.log('\n⚠️ Run "npm install" first, then "npm run build"\n');
98
+ console.error('Error:', msg);
99
+ }
100
+ return;
101
+ }
102
+
103
+ // Setup mode
104
+ if (command === '--setup') {
105
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
106
+ const wizard = require('./setupWizard.js');
107
+ wizard.runWizard();
108
+ return;
109
+ }
110
+
111
+ // Zero-config mode - route with free tier defaults
112
+ const query = args.join(' ');
113
+
114
+ // Check for GROQ_API_KEY first (free tier)
115
+ if (process.env.GROQ_API_KEY) {
116
+ console.log('\n🔀 Routing: "' + query + '"');
117
+ console.log('📡 Using free tier (Groq)...\n');
118
+
119
+ // Quick route via groq
120
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
121
+ const { routeQuery } = require('../index.js');
122
+ routeQuery(query).then((r: { provider?: string; model?: string; estimated_cost?: number }) => {
123
+ console.log(` 🏆 Provider: ${r.provider || 'groq'}`);
124
+ console.log(` 🤖 Model: ${r.model || 'llama-3.3-70b-versatile'}`);
125
+ console.log(` 💰 Est: $${(r.estimated_cost || 0.0001).toFixed(6)}`);
126
+ }).catch((e: unknown) => {
127
+ const msg = e instanceof Error ? e.message : String(e);
128
+ console.log(' ⚠️ Error:', msg);
129
+ console.log(' Tip: Get free key at https://console.groq.com\n');
130
+ });
131
+ return;
132
+ }
133
+
134
+ // No API key - show helpful message
135
+ console.log(`
136
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
137
+ ⚠️ No API key detected
138
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
139
+
140
+ A3M Router needs at least one API key to route queries.
141
+
142
+ 🔥 FREE TIER OPTIONS:
143
+ 1. Groq (fastest, 1000+ req/day free)
144
+ → https://console.groq.com/apikeys
145
+ → Set: export GROQ_API_KEY=your_key
146
+
147
+ 2. Cerebras (fastest inference, free)
148
+ → https://cerebras.ai/labs
149
+ → Set: export CEREBRAS_API_KEY=your_key
150
+
151
+ 3. Google AI (Gemini, free tier)
152
+ → https://aistudio.google.com/app/apikey
153
+ → Set: export GOOGLE_API_KEY=your_key
154
+
155
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
156
+
157
+ Quick setup: npx a3m --setup
158
+ Quick start: GROQ_API_KEY=your_key npx a3m-router serve
159
+ `);
package/src/ensemble.ts CHANGED
@@ -21,6 +21,126 @@ interface RouteDecision {
21
21
  reasoning: string;
22
22
  }
23
23
 
24
+ // ============================================================
25
+ // SEMANTIC VOTING: Cluster similar answers together
26
+ // ============================================================
27
+
28
+ /**
29
+ * Simple word-overlap based semantic similarity
30
+ * Returns 0-1 similarity score
31
+ */
32
+ function wordOverlapSimilarity(a: string, b: string): number {
33
+ // Normalize: lowercase, remove code blocks, extract words
34
+ const normalize = (s: string) => {
35
+ return s.toLowerCase()
36
+ .replace(/```[\s\S]*?```/g, ' CODE ') // Replace code blocks
37
+ .replace(/`[^`]*`/g, ' CODE ') // Replace inline code
38
+ .replace(/[^a-z0-9\s]/g, ' ') // Remove punctuation
39
+ .split(/\s+/)
40
+ .filter(w => w.length > 2); // Remove stopwords
41
+ };
42
+
43
+ const wordsA = normalize(a);
44
+ const wordsB = normalize(b);
45
+
46
+ if (wordsA.length === 0 || wordsB.length === 0) return 0;
47
+
48
+ // Jaccard similarity
49
+ const setA = new Set(wordsA);
50
+ const setB = new Set(wordsB);
51
+ let intersection = 0;
52
+ for (const w of setA) {
53
+ if (setB.has(w)) intersection++;
54
+ }
55
+ const union = setA.size + setB.size - intersection;
56
+ return union > 0 ? intersection / union : 0;
57
+ }
58
+
59
+ /**
60
+ * Cluster answers by semantic similarity using agglomerative clustering
61
+ * Returns clusters of (representative, [provider indices])
62
+ */
63
+ function semanticCluster(
64
+ answers: { provider: string; answer: string }[],
65
+ threshold = 0.6
66
+ ): { cluster: string; providers: string[] }[] {
67
+ if (answers.length === 0) return [];
68
+ if (answers.length === 1) return [{ cluster: answers[0].answer, providers: [answers[0].provider] }];
69
+
70
+ // Build similarity matrix
71
+ const n = answers.length;
72
+ const sims: number[][] = Array(n).fill(null).map(() => Array(n).fill(0));
73
+ for (let i = 0; i < n; i++) {
74
+ for (let j = 0; j < n; j++) {
75
+ if (i === j) {
76
+ sims[i][j] = 1.0;
77
+ } else if (j > i) {
78
+ const s = wordOverlapSimilarity(answers[i].answer, answers[j].answer);
79
+ sims[i][j] = s;
80
+ sims[j][i] = s;
81
+ }
82
+ }
83
+ }
84
+
85
+ // Agglomerative clustering: greedily merge most similar pairs
86
+ const clusters: Set<number>[] = answers.map((_, i) => new Set([i]));
87
+ const active = new Set(answers.map((_, i) => i));
88
+
89
+ while (active.size > 1) {
90
+ let bestSim = 0;
91
+ let bestI = -1, bestJ = -1;
92
+
93
+ for (const i of active) {
94
+ for (const j of active) {
95
+ if (i >= j) continue;
96
+ // Average similarity between all pairs in two clusters
97
+ let sumSim = 0, count = 0;
98
+ for (const ci of clusters[i]) {
99
+ for (const cj of clusters[j]) {
100
+ sumSim += sims[ci][cj];
101
+ count++;
102
+ }
103
+ }
104
+ const avgSim = count > 0 ? sumSim / count : 0;
105
+ if (avgSim > bestSim) {
106
+ bestSim = avgSim;
107
+ bestI = i;
108
+ bestJ = j;
109
+ }
110
+ }
111
+ }
112
+
113
+ if (bestSim < threshold || bestI < 0) break;
114
+
115
+ // Merge clusters
116
+ for (const idx of clusters[bestJ]) {
117
+ clusters[bestI].add(idx);
118
+ }
119
+ active.delete(bestJ);
120
+ }
121
+
122
+ // Build result
123
+ return clusters
124
+ .filter((_, i) => active.has(i))
125
+ .map(cluster => {
126
+ const indices = Array.from(cluster);
127
+ // Use the longest answer as cluster representative (most informative)
128
+ let longestIdx = indices[0];
129
+ let longestLen = answers[indices[0]].answer.length;
130
+ for (const idx of indices) {
131
+ if (answers[idx].answer.length > longestLen) {
132
+ longestLen = answers[idx].answer.length;
133
+ longestIdx = idx;
134
+ }
135
+ }
136
+ return {
137
+ cluster: answers[longestIdx].answer,
138
+ providers: indices.map(i => answers[i].provider)
139
+ };
140
+ })
141
+ .sort((a, b) => b.providers.length - a.providers.length); // Largest cluster first
142
+ }
143
+
24
144
  // Type alias for external consumers
25
145
  export type RouterDecision = RouteDecision;
26
146
 
@@ -29,7 +149,7 @@ export type RouterDecision = RouteDecision;
29
149
  export const A3MRouter = createA3MRouter as any;
30
150
  export { createA3MRouter };
31
151
 
32
- export type EnsembleStrategy = 'majority' | 'weighted' | 'conservative' | 'shapley';
152
+ export type EnsembleStrategy = 'majority' | 'weighted' | 'conservative' | 'shapley' | 'semantic';
33
153
 
34
154
  export interface EnsembleResponse {
35
155
  finalAnswer: string;
@@ -157,6 +277,39 @@ export class EnsembleOrchestrator {
157
277
  this.handicapCalc.record(r.provider, cost, isCorrect(r.answer));
158
278
  });
159
279
  }
280
+ else if (strategy === 'semantic') {
281
+ // === SEMANTIC VOTING: Cluster similar answers together ===
282
+ // This handles cases where models give equivalent answers in different words
283
+ // e.g., "The answer is 42" vs "42 is correct" would be clustered together
284
+
285
+ const clusters = semanticCluster(successful, 0.55); // 55% similarity threshold
286
+
287
+ if (clusters.length === 0) {
288
+ winnerAnswer = 'UNCERTAIN';
289
+ confidence = 0;
290
+ winnerProvider = 'none';
291
+ } else {
292
+ // Use weighted voting: cluster size * average provider weight
293
+ const clusterScores = clusters.map(cluster => {
294
+ const totalWeight = cluster.providers.reduce((sum, p) => sum + (weights[p] || 1.0), 0);
295
+ return {
296
+ cluster: cluster.cluster,
297
+ providers: cluster.providers,
298
+ score: cluster.providers.length * totalWeight / cluster.providers.length
299
+ };
300
+ }).sort((a, b) => b.score - a.score);
301
+
302
+ winnerAnswer = clusterScores[0].cluster;
303
+ winnerProvider = clusterScores[0].providers[0];
304
+ confidence = clusterScores[0].providers.length / successful.length;
305
+
306
+ // Log cluster info for debugging
307
+ console.log(`[SEMANTIC] ${clusters.length} clusters formed, winner has ${clusterScores[0].providers.length}/${successful.length} providers`);
308
+ clusters.slice(1, 3).forEach((c, i) => {
309
+ console.log(`[SEMANTIC] Cluster ${i+2}: ${c.providers.length} providers, preview: ${c.cluster.substring(0, 50)}...`);
310
+ });
311
+ }
312
+ }
160
313
 
161
314
  // Record loyalty: successful collaborations build trust
162
315
  if (strategy === 'shapley') {
@@ -74,7 +74,7 @@ import {
74
74
  registerProvider,
75
75
  } from '../providers/providerConfig.js';
76
76
 
77
- import { ProviderRegistry } from '../providers/registry.js';
77
+ import { ProviderRegistry } from '../providers/registry';
78
78
 
79
79
  // ============================================================
80
80
  // TYPES
@@ -136,7 +136,7 @@ function langchainMessagesToOpenAI(messages: BaseMessage[]): Array<{
136
136
  tool_calls?: A3MToolCall[];
137
137
  }> {
138
138
  return messages.map((msg) => {
139
- const role = msg._getType() as 'system' | 'user' | 'assistant' | 'tool';
139
+ const role = ((msg as any)._getType()) as 'system' | 'user' | 'assistant' | 'tool';
140
140
 
141
141
  let content: string | Array<{ type: string; text?: string; image_url?: any }>;
142
142
  if (typeof msg.content === 'string') {
@@ -639,30 +639,44 @@ export const DEFAULT_PROVIDERS: Record<string, ProviderDefinition> = {
639
639
  baseUrl: 'https://openrouter.ai/api/v1/chat/completions',
640
640
  apiKeyEnv: 'OPENROUTER_API_KEY',
641
641
  models: [
642
- // Premium-tier free models (high context, powerful) assigned equivalent costs for routing
643
- // so the router classifies them as mid/premium tier
644
- 'moonshotai/kimi-k2.6:free',
645
- 'qwen/qwen3-coder:free',
646
- 'nvidia/nemotron-3-ultra-550b-a55b:free',
647
- 'nvidia/nemotron-3-super-120b-a12b:free',
648
- 'google/gemma-4-31b-it:free',
649
- 'qwen/qwen3-next-80b-a3b-instruct:free',
650
- 'nousresearch/hermes-3-llama-3.1-405b:free',
651
- 'openai/gpt-oss-120b:free',
652
- 'meta-llama/llama-3.3-70b-instruct:free',
653
- // Paid models (passthrough pricing — set representative costs for routing)
642
+ // VERIFIED WORKING FREE MODELS (tested 2026-08-16 + recheck)
643
+ // Premium-tier free models (high context, powerful)
644
+ 'nvidia/nemotron-3.5-lightning:free', // 1M context, reasoning
645
+ 'nvidia/nemotron-3-ultra-550b-a55b:free', // 1M context, premium quality
646
+ 'nvidia/nemotron-3-super-120b-a12b:free', // 262K context
647
+ 'google/gemma-4-31b-it:free', // 262K context, multilingual
648
+ 'google/gemma-4-26b-a4b-it:free', // 262K context
649
+ // Reasoning-capable models
650
+ 'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free', // 256K, reasoning
651
+ // Mid-tier free models
652
+ 'nvidia/nemotron-3-nano-30b-a3b:free', // 256K context
653
+ 'nvidia/nemotron-nano-9b-v2:free', // 128K context
654
+ 'nvidia/nemotron-nano-12b-v2-vl:free', // 128K context, multimodal
655
+ // Auto-route free model
656
+ 'openrouter/free', // Auto-selects best available free model
657
+ // ❌ REMOVED (unavailable): kimi-k2.6, qwen3-coder, qwen3-next, hermes-3-405b, gpt-oss-120b, llama-3.3-70b, laguna-s-2.1
658
+ // FREE Gemini 3.x (newer, better than 2.x)
659
+ 'google/gemini-3.5-flash:free', // 131K context, reasoning, free
660
+ 'google/gemini-3.6-flash:free', // 131K context, reasoning, free
661
+ // FREE Qwen 3.7 (latest Qwen with extended context)
662
+ 'qwen/qwen3.7-flash:free', // 131K context, reasoning, free
663
+ 'qwen/qwen3.7-max:free', // 131K context, reasoning, free
664
+ // FREE DeepSeek V4 Flash
665
+ 'deepseek/deepseek-v4-flash-latest:free', // 131K context, reasoning, free
666
+ // Paid models (passthrough pricing)
654
667
  'openai/gpt-4o',
655
668
  'anthropic/claude-3.5-sonnet',
656
669
  'google/gemini-pro-1.5',
657
670
  'meta-llama/llama-3.1-70b-instruct',
658
671
  'mistralai/mistral-large',
659
672
  ],
660
- costPerK: { input: 0, output: 0 }, // Passthrough pricing router uses model-specific costs
661
- tier: 'cheap',
673
+ costPerK: { input: 0, output: 0 }, // Free tier models cost 0; paid use passthrough
674
+ tier: 'mid', // Changed from 'cheap' — premium free models are mid-tier quality
662
675
  format: 'openai',
663
676
  type: 'api',
664
677
  priority: 34,
665
- maxTokens: 128000,
678
+ maxTokens: 1048576, // Support 1M context for nemotron-3.5-lightning
679
+ strategy: 'balanced',
666
680
  },
667
681
 
668
682
  // ========================================================================
@@ -718,12 +732,13 @@ export const DEFAULT_PROVIDERS: Record<string, ProviderDefinition> = {
718
732
  'mistralai/mistral-large-3-675b-instruct-2512',
719
733
  'z-ai/glm-5.1',
720
734
  ],
721
- costPerK: { input: 0, output: 0 },
735
+ costPerK: { input: 0, output: 0 }, // Free tier available
722
736
  tier: 'free',
723
737
  format: 'openai',
724
738
  type: 'api',
725
739
  priority: 4,
726
- maxTokens: 8192,
740
+ maxTokens: 131072, // Support long context for premium models
741
+ strategy: 'balanced',
727
742
  },
728
743
 
729
744
  // ========================================================================
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Provider Registry - Central registry for all A3M Router providers
3
+ */
4
+ export const PROVIDERS = new Map();
5
+
6
+ // Register providers by name
7
+ export function registerProvider(name, config) {
8
+ PROVIDERS.set(name, config);
9
+ }
10
+
11
+ // Get provider by name
12
+ export function getProvider(name) {
13
+ return PROVIDERS.get(name);
14
+ }
15
+
16
+ // List all registered providers
17
+ export function listProviders() {
18
+ return [...PROVIDERS.entries()];
19
+ }
20
+
21
+ // Remove provider by name
22
+ export function removeProvider(name) {
23
+ return PROVIDERS.delete(name);
24
+ }
25
+
26
+ // Export all providers
27
+ export * from './registry.js';
@@ -137,6 +137,12 @@ function buildModelProfiles(): Record<string, ModelProfile> {
137
137
  'google/gemini-pro-1.5': [1.25, 5],
138
138
  'meta-llama/llama-3.1-70b-instruct': [0.18, 0.18],
139
139
  'mistralai/mistral-large': [2, 6],
140
+ // Newer free models (these override the default 0,0 for free tier)
141
+ 'gemini-3.5-flash:free': [0, 0],
142
+ 'gemini-3.6-flash:free': [0, 0],
143
+ 'qwen3.7-flash:free': [0, 0],
144
+ 'qwen3.7-max:free': [0, 0],
145
+ 'deepseek-v4-flash-latest:free': [0, 0],
140
146
  };
141
147
  const orKey = model.includes('/') ? model : 'openrouter/' + model;
142
148
  // Try matching by full key or by model name
@@ -165,18 +171,46 @@ function buildModelProfiles(): Record<string, ModelProfile> {
165
171
  // OpenRouter premium free models get quality boosts
166
172
  if (provider.name === 'OpenRouter') {
167
173
  const modelLower = modelKey.toLowerCase();
168
- // Premium-tier free models (large, high-context)
169
- if (modelLower.includes('kimi') || modelLower.includes('qwen3-coder') ||
174
+ // Premium-tier free models (large, high-context) — these are equivalent to paid mid-tier
175
+ if (modelLower.includes('kimi-k2') || modelLower.includes('qwen3-coder') ||
170
176
  modelLower.includes('nemotron-3-ultra') || modelLower.includes('nemotron-3-super') ||
171
- modelLower.includes('hermes-3') || modelLower.includes('gemma-4')) {
177
+ modelLower.includes('nemotron-3.5-lightning') || modelLower.includes('nemotron-3-nano-omni') ||
178
+ modelLower.includes('hermes-3') || modelLower.includes('gemma-4-31b') ||
179
+ modelLower.includes('laguna-s-2.1') ||
180
+ // Gemini 3.x free models — latest reasoning models
181
+ modelLower.includes('gemini-3.5-flash') || modelLower.includes('gemini-3.6-flash') ||
182
+ // Qwen 3.7 free models — latest with extended context
183
+ modelLower.includes('qwen3.7-flash') || modelLower.includes('qwen3.7-max') ||
184
+ // DeepSeek V4 free
185
+ modelLower.includes('deepseek-v4-flash')) {
172
186
  strengths.push('reasoning', 'long-context', 'premium');
173
187
  }
174
- // Mid-tier free models (good quality, smaller)
188
+ // Mid-tier free models (good quality, smaller) — equivalent to cheap tier
175
189
  else if (modelLower.includes('gpt-oss') || modelLower.includes('qwen3-next') ||
176
- modelLower.includes('gemma-4') || modelLower.includes('llama-3.3')) {
190
+ modelLower.includes('gemma-4-26b') || modelLower.includes('llama-3.3-70b') ||
191
+ modelLower.includes('nemotron-3-nano-30b') || modelLower.includes('nemotron-nano-9b')) {
177
192
  strengths.push('fast', 'reasoning');
178
193
  }
179
- // Budget free models
194
+ // Budget free models (small models)
195
+ else {
196
+ strengths.push('fast');
197
+ }
198
+ }
199
+ // NVIDIA NIM premium free models (high quality, long context)
200
+ if (provider.name === 'NVIDIA NIM') {
201
+ const modelLower = modelKey.toLowerCase();
202
+ // Premium tier: nemotron-super, gemma-4-31b, llama-3.3-70b
203
+ if (modelLower.includes('nemotron-3-super') || modelLower.includes('gemma-4-31b') ||
204
+ modelLower.includes('llama-3.3-70b') || modelLower.includes('mistral-large') ||
205
+ modelLower.includes('glm-5')) {
206
+ strengths.push('reasoning', 'long-context', 'premium');
207
+ }
208
+ // Mid tier: nemotron-mini, llama-3.1-8b, qwen
209
+ else if (modelLower.includes('nemotron-mini') || modelLower.includes('llama-3.1') ||
210
+ modelLower.includes('qwen')) {
211
+ strengths.push('fast', 'reasoning');
212
+ }
213
+ // Budget tier
180
214
  else {
181
215
  strengths.push('fast');
182
216
  }
@@ -200,6 +234,10 @@ function buildModelProfiles(): Record<string, ModelProfile> {
200
234
  if (provider.name === 'Anthropic') {
201
235
  strengths.push('reasoning', 'creative', 'analysis');
202
236
  }
237
+ // Cloudflare Workers AI models (free but limited)
238
+ if (provider.name === 'Cloudflare Workers AI') {
239
+ strengths.push('free', 'fast');
240
+ }
203
241
 
204
242
  // Detect multimodal support
205
243
  const supportsMultimodal = provider.supports_multimodal === true;
@@ -209,6 +247,30 @@ function buildModelProfiles(): Record<string, ModelProfile> {
209
247
  strengths.push(provider.strategy);
210
248
  }
211
249
 
250
+ // === QUALITY SCORE ASSIGNMENT ===
251
+ // Priority: premium/reasoning > fast+paid > fast/free > budget > default
252
+ // Free models with premium/reasoning tags get same quality as paid models
253
+ let quality_score: number;
254
+ if (strengths.includes('premium') || strengths.includes('reasoning')) {
255
+ // Premium models (including premium free models like nemotron-3-super:free)
256
+ // get high quality score — same as paid premium models
257
+ quality_score = 0.92;
258
+ } else if (strengths.includes('fast') && costPerKInput > 0.3) {
259
+ // Fast mid-tier models (Groq, Cerebras, etc.) get competitive quality
260
+ quality_score = 0.89;
261
+ } else if (strengths.includes('fast') && costPerKInput === 0) {
262
+ // Fast free models (Cloudflare, local models) get moderate quality
263
+ quality_score = 0.78;
264
+ } else if (strengths.includes('budget')) {
265
+ // Budget models (small, cheap)
266
+ quality_score = 0.75;
267
+ } else if (provider.name === 'OpenRouter' && costPerKInput === 0) {
268
+ // OpenRouter free models that don't match premium patterns get boosted
269
+ quality_score = 0.82;
270
+ } else {
271
+ quality_score = 0.80;
272
+ }
273
+
212
274
  profiles[modelKey] = {
213
275
  name: modelKey,
214
276
  provider: providerId,
@@ -216,9 +278,7 @@ function buildModelProfiles(): Record<string, ModelProfile> {
216
278
  cost_per_1k_input: costPerKInput,
217
279
  cost_per_1k_output: costPerKOutput,
218
280
  latency_ms: provider.type === 'cli' ? 5000 : (provider.priority * 200 + 300),
219
- quality_score: (strengths.includes('premium') || strengths.includes('reasoning')) ? 0.94 :
220
- (strengths.includes('fast') && costPerKInput > 0.3) ? 0.95 : // Boosted for mid-tier competitiveness
221
- (strengths.includes('budget') || strengths.includes('free')) ? 0.72 : 0.80,
281
+ quality_score,
222
282
  strengths,
223
283
  context_window: provider.maxTokens || 8192,
224
284
  type: provider.type,
@@ -609,7 +669,13 @@ function scoreModelFit(model: ModelProfile, features: QueryFeatures): number {
609
669
  // If user has free + groq + openai → quartiles split them naturally.
610
670
  const modelCost = (model.cost_per_1k_input + model.cost_per_1k_output) / 2;
611
671
  let tierFromModel: string;
612
- if (model.strengths.includes('free') || modelCost === 0) {
672
+ // Premium free models (with 'premium' or 'reasoning' tag) are treated as 'mid' tier
673
+ // They should be selected for quality, not penalized for being "free"
674
+ const isPremiumFree = model.strengths.includes('free') &&
675
+ (model.strengths.includes('premium') || model.strengths.includes('reasoning'));
676
+ if (isPremiumFree) {
677
+ tierFromModel = 'mid'; // Treat premium free models as mid-tier for scoring
678
+ } else if (model.strengths.includes('free') || modelCost === 0) {
613
679
  tierFromModel = 'free';
614
680
  } else if (modelCost <= _costPercentiles.p25) {
615
681
  tierFromModel = 'cheap';
@@ -662,8 +728,21 @@ function scoreModelFit(model: ModelProfile, features: QueryFeatures): number {
662
728
  };
663
729
  const bonuses = domainBonus[features.domain] || [];
664
730
  if (bonuses.some(b => model.strengths.includes(b))) {
665
- const is_budget = model.strengths.includes('budget') || model.strengths.includes('free');
666
- score += is_budget ? 0.05 : 0.25; // Budget gets minimal boost
731
+ // Separate budget from premium free models
732
+ const isBudget = model.strengths.includes('budget');
733
+ const isPremiumFree = model.strengths.includes('free') &&
734
+ (model.strengths.includes('premium') || model.strengths.includes('reasoning'));
735
+ const isBudgetFree = model.strengths.includes('free') && !isPremiumFree;
736
+
737
+ if (isBudget) {
738
+ score += 0.05; // Budget models get minimal domain boost
739
+ } else if (isPremiumFree) {
740
+ score += 0.15; // Premium free models get significant boost (quality match)
741
+ } else if (isBudgetFree) {
742
+ score += 0.05; // Budget free models get minimal boost
743
+ } else {
744
+ score += 0.25; // Paid premium models get full boost
745
+ }
667
746
  }
668
747
  }
669
748
 
@@ -706,9 +785,15 @@ function scoreModelFit(model: ModelProfile, features: QueryFeatures): number {
706
785
  score *= 0.8; // Mild penalty for conservative providers on low-risk (cost waste)
707
786
  }
708
787
 
709
- // Free tier preference for simple queries
788
+ // Free tier preference for simple queries — ONLY for truly budget free models
789
+ // Premium free models (nemotron-super, gemma-4-31b, etc.) should be selected on quality,
790
+ // not because they're free, so they don't get this bonus
710
791
  if (features.complexity < 0.5 && model.strengths.includes('free')) {
711
- score += 0.2;
792
+ const isBudgetFree = !model.strengths.includes('premium') && !model.strengths.includes('reasoning');
793
+ if (isBudgetFree) {
794
+ score += 0.05; // Small bonus only — prevent budget free models from hitting cap
795
+ }
796
+ // Premium free models: NO bonus — they're selected on quality merits
712
797
  }
713
798
 
714
799
  // Fast provider for simple queries
@@ -127,7 +127,7 @@ export interface ShadowedResponse {
127
127
  * complexity_adj = query complexity signal
128
128
  */
129
129
  export class ShadowSampler {
130
- private config: Required<Omit<ShadowSamplerConfig, 'forceShadowProvider'>> & { forceShadowProvider: boolean };
130
+ private config: Required<Omit<ShadowSamplerConfig, 'forceShadowProvider' | 'shadowProvider'>> & { forceShadowProvider: boolean; shadowProvider: string | null; };
131
131
 
132
132
  // Cached shadow provider (auto-selected on first call)
133
133
  private _shadowProvider: string | null = null;
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "test-install",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC"
12
+ }
@@ -14,7 +14,6 @@
14
14
  "noEmit": true
15
15
  },
16
16
  "include": [
17
- "../tmlpd-pi-extension/src/**/*.ts",
18
17
  "../src/**/*.ts",
19
18
  "./**/*.ts"
20
19
  ]