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
@@ -0,0 +1,217 @@
1
+ """
2
+ A3M Router Adapter for Pinecone Vector Database.
3
+
4
+ Enables Pinecone's managed vector database to use A3M Router for
5
+ intelligent query routing and cost-optimized embeddings.
6
+
7
+ Usage:
8
+ from pinecone import Pinecone
9
+ from a3m_adapter import A3MPineconeAdapter
10
+
11
+ # Create A3M-powered embeddings
12
+ embed_adapter = A3MPineconeAdapter(model="auto")
13
+
14
+ # Generate embeddings
15
+ embedding = embed_adapter.embed("What is quantum computing?")
16
+
17
+ # Query Pinecone
18
+ results = index.query(
19
+ vector=embedding,
20
+ top_k=5,
21
+ )
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ from typing import Any, Dict, List, Optional
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ A3M_AVAILABLE = False
32
+ try:
33
+ from a3m.router import A3MRouter
34
+ A3M_AVAILABLE = True
35
+ except ImportError:
36
+ logger.warning(
37
+ "A3M Router not installed. Install with: pip install adaptive-memory-multi-model-router"
38
+ )
39
+
40
+
41
+ class A3MPineconeAdapter:
42
+ """
43
+ A3M Router adapter for Pinecone embeddings.
44
+
45
+ Provides intelligent embedding generation through A3M Router
46
+ with automatic model selection for cost optimization.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ model: str = "auto",
52
+ embed_model: str = "auto",
53
+ parallel_ensemble: int = 1,
54
+ api_key: Optional[str] = None,
55
+ **kwargs: Any,
56
+ ) -> None:
57
+ """
58
+ Initialize A3M Router adapter for Pinecone.
59
+ """
60
+ self.model = model
61
+ self.embed_model = embed_model or "auto"
62
+ self.parallel_ensemble = parallel_ensemble
63
+ self.api_key = api_key
64
+ self._a3m_router = None
65
+ self._initialized = False
66
+ self._kwargs = kwargs
67
+
68
+ def _ensure_router(self) -> None:
69
+ """Lazily initialize the A3M router."""
70
+ if self._initialized:
71
+ return
72
+
73
+ if not A3M_AVAILABLE:
74
+ raise ImportError(
75
+ "A3M Router is not installed. "
76
+ "Install with: pip install adaptive-memory-multi-model-router"
77
+ )
78
+
79
+ self._a3m_router = A3MRouter(
80
+ model=self.model,
81
+ parallel_ensemble=self.parallel_ensemble,
82
+ )
83
+ self._initialized = True
84
+ logger.info(
85
+ "A3M Router initialized for Pinecone: embed_model=%s",
86
+ self.embed_model,
87
+ )
88
+
89
+ def embed(
90
+ self,
91
+ texts: List[str],
92
+ **kwargs: Any,
93
+ ) -> List[List[float]]:
94
+ """
95
+ Generate embeddings for texts using A3M Router.
96
+
97
+ Args:
98
+ texts: List of text strings to embed
99
+
100
+ Returns:
101
+ List of embedding vectors
102
+ """
103
+ self._ensure_router()
104
+
105
+ import asyncio
106
+ loop = asyncio.get_event_loop()
107
+
108
+ # For embeddings, we typically call the router with a special embedding mode
109
+ # Since A3M supports /v1/embeddings endpoint
110
+ results = loop.run_in_executor(
111
+ None,
112
+ lambda: self._a3m_router.embed(
113
+ texts=texts,
114
+ model=self.embed_model,
115
+ **kwargs,
116
+ ),
117
+ )
118
+
119
+ return results
120
+
121
+ def embed_query(
122
+ self,
123
+ text: str,
124
+ **kwargs: Any,
125
+ ) -> List[float]:
126
+ """
127
+ Generate embedding for a single query.
128
+
129
+ Args:
130
+ text: Text to embed
131
+
132
+ Returns:
133
+ Embedding vector
134
+ """
135
+ embeddings = self.embed([text], **kwargs)
136
+ return embeddings[0] if embeddings else []
137
+
138
+ async def aembed(
139
+ self,
140
+ texts: List[str],
141
+ **kwargs: Any,
142
+ ) -> List[List[float]]:
143
+ """Async version of embed."""
144
+ self._ensure_router()
145
+
146
+ results = await self._a3m_router.aembed(
147
+ texts=texts,
148
+ model=self.embed_model,
149
+ **kwargs,
150
+ )
151
+
152
+ return results
153
+
154
+ def rag_query(
155
+ self,
156
+ query: str,
157
+ index,
158
+ top_k: int = 5,
159
+ **kwargs: Any,
160
+ ) -> Dict[str, Any]:
161
+ """
162
+ Perform RAG query: embed + Pinecone search + context.
163
+
164
+ Args:
165
+ query: The search query
166
+ index: Pinecone index to query
167
+ top_k: Number of results to retrieve
168
+
169
+ Returns:
170
+ Dict with 'results', 'context', 'provider', 'cost'
171
+ """
172
+ # 1. Embed query
173
+ query_embedding = self.embed_query(query)
174
+
175
+ # 2. Search Pinecone
176
+ search_results = index.query(
177
+ vector=query_embedding,
178
+ top_k=top_k,
179
+ include_metadata=True,
180
+ )
181
+
182
+ # 3. Build context from results
183
+ context = "\n\n".join([
184
+ match.get('metadata', {}).get('text', str(match.get('id', '')))
185
+ for match in search_results.get('matches', [])[:3]
186
+ ])
187
+
188
+ # 4. Route the full query through A3M
189
+ import asyncio
190
+ loop = asyncio.get_event_loop()
191
+ route_result = loop.run_in_executor(
192
+ None,
193
+ lambda: self._a3m_router.route(
194
+ messages=[{
195
+ "role": "user",
196
+ "content": f"Context:\n{context}\n\nQuestion: {query}"
197
+ }],
198
+ temperature=self._kwargs.get("temperature", 0.7),
199
+ **kwargs,
200
+ ),
201
+ )
202
+
203
+ return {
204
+ "results": search_results.get('matches', []),
205
+ "context": context,
206
+ "answer": route_result.content,
207
+ "provider": getattr(route_result, 'provider', 'a3m'),
208
+ "cost": getattr(route_result, 'cost', 0.0),
209
+ }
210
+
211
+ def __repr__(self) -> str:
212
+ return (
213
+ f"A3MPineconeAdapter("
214
+ f"model={self.model!r}, "
215
+ f"embed_model={self.embed_model!r}, "
216
+ f"ensemble={self.parallel_ensemble})"
217
+ )
@@ -0,0 +1,188 @@
1
+ """
2
+ A3M Router Adapter for Vercel AI SDK.
3
+
4
+ Drop-in replacement for Vercel AI SDK's AI function that routes through A3M Router
5
+ for intelligent, cost-optimized responses in Next.js and other JavaScript environments.
6
+
7
+ Usage (JavaScript):
8
+ import { generateText } from 'ai';
9
+ import { createA3MProvider } from 'a3m-adapter/vercel';
10
+
11
+ const result = await generateText({
12
+ model: createA3MProvider({ model: 'auto', parallel_ensemble: 2 }),
13
+ prompt: 'What is the meaning of life?',
14
+ });
15
+
16
+ Usage (Python):
17
+ from a3m_adapter import A3MVercelAdapter
18
+
19
+ adapter = A3MVercelAdapter(model='auto', temperature=0.7)
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import logging
25
+ from typing import Any, Dict, List, Optional
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ A3M_AVAILABLE = False
30
+ try:
31
+ from a3m.router import A3MRouter, RouteResponse
32
+ A3M_AVAILABLE = True
33
+ except ImportError:
34
+ logger.warning(
35
+ "A3M Router not installed. Install with: pip install adaptive-memory-multi-model-router"
36
+ )
37
+
38
+
39
+ class A3MVercelAdapter:
40
+ """
41
+ A3M Router adapter for Vercel AI SDK compatibility.
42
+
43
+ Provides a drop-in replacement that routes through A3M Router
44
+ instead of calling OpenAI/Anthropic directly.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ model: str = "auto",
50
+ temperature: float = 0.7,
51
+ max_tokens: int = 4096,
52
+ parallel_ensemble: int = 1,
53
+ api_key: Optional[str] = None,
54
+ **kwargs: Any,
55
+ ) -> None:
56
+ """
57
+ Initialize A3M Router adapter for Vercel AI SDK.
58
+ """
59
+ self.model = model
60
+ self.temperature = temperature
61
+ self.max_tokens = max_tokens
62
+ self.parallel_ensemble = parallel_ensemble
63
+ self.api_key = api_key
64
+ self._a3m_router = None
65
+ self._initialized = False
66
+ self._kwargs = kwargs
67
+
68
+ def _ensure_router(self) -> None:
69
+ """Lazily initialize the A3M router."""
70
+ if self._initialized:
71
+ return
72
+
73
+ if not A3M_AVAILABLE:
74
+ raise ImportError(
75
+ "A3M Router is not installed. "
76
+ "Install with: pip install adaptive-memory-multi-model-router"
77
+ )
78
+
79
+ self._a3m_router = A3MRouter(
80
+ model=self.model,
81
+ temperature=self.temperature,
82
+ parallel_ensemble=self.parallel_ensemble,
83
+ )
84
+ self._initialized = True
85
+ logger.info(
86
+ "A3M Router initialized for Vercel AI SDK: model=%s",
87
+ self.model,
88
+ )
89
+
90
+ def __call__(
91
+ self,
92
+ prompt: str,
93
+ **kwargs: Any,
94
+ ) -> Dict[str, Any]:
95
+ """
96
+ Generate text from prompt (Vercel AI SDK compatible interface).
97
+
98
+ Args:
99
+ prompt: The prompt string
100
+
101
+ Returns:
102
+ Dict with 'text', 'provider', 'usage', 'finishReason'
103
+ """
104
+ self._ensure_router()
105
+
106
+ messages = [{"role": "user", "content": prompt}]
107
+
108
+ import asyncio
109
+ loop = asyncio.get_event_loop()
110
+ route_result = loop.run_in_executor(
111
+ None,
112
+ lambda: self._a3m_router.route(
113
+ messages=messages,
114
+ temperature=kwargs.get("temperature", self.temperature),
115
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
116
+ **kwargs,
117
+ ),
118
+ )
119
+
120
+ return {
121
+ "text": route_result.content,
122
+ "provider": getattr(route_result, 'provider', 'a3m'),
123
+ "finishReason": getattr(route_result, 'finish_reason', 'stop'),
124
+ "usage": {
125
+ "promptTokens": getattr(route_result, 'prompt_tokens', 0),
126
+ "completionTokens": getattr(route_result, 'completion_tokens', 0),
127
+ "totalTokens": getattr(route_result, 'total_tokens', 0),
128
+ },
129
+ }
130
+
131
+ async def generate(
132
+ self,
133
+ prompt: str,
134
+ **kwargs: Any,
135
+ ) -> Dict[str, Any]:
136
+ """Async generate for Vercel AI SDK."""
137
+ self._ensure_router()
138
+
139
+ messages = [{"role": "user", "content": prompt}]
140
+
141
+ route_result = await self._a3m_router.aroute(
142
+ messages=messages,
143
+ temperature=kwargs.get("temperature", self.temperature),
144
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
145
+ **kwargs,
146
+ )
147
+
148
+ return {
149
+ "text": route_result.content,
150
+ "provider": getattr(route_result, 'provider', 'a3m'),
151
+ "finishReason": getattr(route_result, 'finish_reason', 'stop'),
152
+ "usage": {
153
+ "promptTokens": getattr(route_result, 'prompt_tokens', 0),
154
+ "completionTokens": getattr(route_result, 'completion_tokens', 0),
155
+ "totalTokens": getattr(route_result, 'total_tokens', 0),
156
+ },
157
+ }
158
+
159
+ def __repr__(self) -> str:
160
+ return (
161
+ f"A3MVercelAdapter("
162
+ f"model={self.model!r}, "
163
+ f"temperature={self.temperature}, "
164
+ f"max_tokens={self.max_tokens})"
165
+ )
166
+
167
+
168
+ # JavaScript-compatible factory function
169
+ def createA3MProvider(config: Dict[str, Any]) -> A3MVercelAdapter:
170
+ """
171
+ Create an A3M Provider for Vercel AI SDK (JavaScript usage).
172
+
173
+ Usage:
174
+ import { generateText } from 'ai';
175
+ import { createA3MProvider } from 'a3m-adapter/vercel';
176
+
177
+ const result = await generateText({
178
+ model: createA3MProvider({ model: 'auto', parallel_ensemble: 2 }),
179
+ prompt: 'What is 2+2?',
180
+ });
181
+ """
182
+ return A3MVercelAdapter(
183
+ model=config.get("model", "auto"),
184
+ temperature=config.get("temperature", 0.7),
185
+ max_tokens=config.get("max_tokens", 4096),
186
+ parallel_ensemble=config.get("parallel_ensemble", 1),
187
+ api_key=config.get("api_key"),
188
+ )
@@ -0,0 +1 @@
1
+ """Tests for A3M adapters."""
@@ -0,0 +1,118 @@
1
+ """
2
+ Test script for A3M Router adapters.
3
+
4
+ Tests the LangChain and LlamaIndex adapters to ensure they:
5
+ 1. Initialize correctly
6
+ 2. Route requests properly
7
+ 3. Return expected response types
8
+ 4. Handle errors gracefully
9
+ """
10
+
11
+ import sys
12
+ import os
13
+ import logging
14
+
15
+ # Add current directory to path
16
+ sys.path.insert(0, '.')
17
+
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def test_langchain_adapter():
23
+ """Test LangChain adapter."""
24
+ print("Testing LangChain adapter...")
25
+
26
+ try:
27
+ from a3m_adapter import A3MLangChainAdapter
28
+
29
+ # Initialize
30
+ llm = A3MLangChainAdapter(model="auto", temperature=0.7)
31
+ print(f"✅ Initialized: {llm}")
32
+
33
+ # Test simple generation
34
+ # Note: This would make actual API calls - we'll skip for now
35
+ # In a real test, we'd mock the A3M router
36
+ print("✅ LangChain adapter structure OK")
37
+ assert llm is not None
38
+
39
+ except Exception as e:
40
+ print(f"❌ LangChain adapter failed: {e}")
41
+ raise
42
+
43
+
44
+ def test_llamaindex_adapter():
45
+ """Test LlamaIndex adapter."""
46
+ print("Testing LlamaIndex adapter...")
47
+
48
+ try:
49
+ from a3m_adapter import A3MLlamaIndexAdapter
50
+
51
+ # Initialize
52
+ llm = A3MLlamaIndexAdapter(model="auto", temperature=0.5)
53
+ print(f"✅ Initialized: {llm}")
54
+
55
+ # Check metadata
56
+ metadata = llm.metadata
57
+ print(f"✅ Metadata: {metadata}")
58
+ assert metadata is not None
59
+
60
+ except Exception as e:
61
+ print(f"❌ LlamaIndex adapter failed: {e}")
62
+ raise
63
+
64
+
65
+ def test_config():
66
+ """Test configuration."""
67
+ print("Testing configuration...")
68
+
69
+ try:
70
+ from a3m_adapter import A3MConfig
71
+
72
+ # Test defaults
73
+ config = A3MConfig()
74
+ print(f"✅ Default config: model={config.model}")
75
+
76
+ # Test to_dict
77
+ data = config.to_dict()
78
+ assert 'model' in data
79
+ print("✅ Config to_dict works")
80
+
81
+ # Test JSON serialization
82
+ json_str = config.to_json()
83
+ assert '"model"' in json_str
84
+ print("✅ Config JSON serialization works")
85
+
86
+ except Exception as e:
87
+ print(f"❌ Config test failed: {e}")
88
+ raise
89
+
90
+
91
+ def main():
92
+ """Run all tests."""
93
+ print("=" * 50)
94
+ print("A3M Router Adapter Tests")
95
+ print("=" * 50)
96
+
97
+ tests = [
98
+ test_config,
99
+ test_langchain_adapter,
100
+ test_llamaindex_adapter,
101
+ ]
102
+
103
+ passed = 0
104
+ total = len(tests)
105
+
106
+ for test in tests:
107
+ test()
108
+ passed += 1
109
+ print()
110
+
111
+ print("=" * 50)
112
+ print(f"Results: {passed}/{total} tests passed")
113
+ print("🎉 All tests passed!")
114
+ return 0
115
+
116
+
117
+ if __name__ == "__main__":
118
+ sys.exit(main())
@@ -0,0 +1,80 @@
1
+ """
2
+ Integration tests for A3M Router adapters.
3
+ Requires A3M Router server running on localhost:8787
4
+ """
5
+
6
+ import pytest
7
+ import os
8
+
9
+
10
+ @pytest.fixture
11
+ def a3m_server_url():
12
+ """Get A3M Router server URL."""
13
+ return os.environ.get("A3M_SERVER_URL", "http://localhost:8787")
14
+
15
+
16
+ @pytest.fixture
17
+ def skip_if_no_server():
18
+ """Skip test if server is not available."""
19
+ import requests
20
+ try:
21
+ resp = requests.get("http://localhost:8787/health", timeout=2)
22
+ if resp.status_code != 200:
23
+ pytest.skip("A3M Router server not running")
24
+ except:
25
+ pytest.skip("A3M Router server not running")
26
+
27
+
28
+ @pytest.mark.integration
29
+ def test_simple_chat_completion(a3m_server_url, skip_if_no_server):
30
+ """Test simple chat completion via HTTP API."""
31
+ import requests
32
+
33
+ response = requests.post(
34
+ f"{a3m_server_url}/v1/chat/completions",
35
+ json={
36
+ "model": "auto",
37
+ "messages": [{"role": "user", "content": "What is 2+2?"}]
38
+ },
39
+ timeout=30
40
+ )
41
+
42
+ assert response.status_code == 200
43
+ data = response.json()
44
+ assert "choices" in data
45
+ assert len(data["choices"]) > 0
46
+ assert "message" in data["choices"][0]
47
+ assert data["choices"][0]["message"]["content"]
48
+
49
+
50
+ @pytest.mark.integration
51
+ def test_parallel_ensemble(a3m_server_url, skip_if_no_server):
52
+ """Test parallel ensemble with multiple providers."""
53
+ import requests
54
+
55
+ response = requests.post(
56
+ f"{a3m_server_url}/v1/chat/completions",
57
+ json={
58
+ "model": "auto",
59
+ "messages": [{"role": "user", "content": "Explain gravity"}],
60
+ "parallel_ensemble": 3,
61
+ },
62
+ timeout=60
63
+ )
64
+
65
+ assert response.status_code == 200
66
+ data = response.json()
67
+ assert "choices" in data
68
+ assert "provider" in data
69
+
70
+
71
+ @pytest.mark.integration
72
+ def test_health_endpoint(a3m_server_url, skip_if_no_server):
73
+ """Test health endpoint."""
74
+ import requests
75
+
76
+ response = requests.get(f"{a3m_server_url}/health", timeout=10)
77
+
78
+ assert response.status_code == 200
79
+ data = response.json()
80
+ assert "providers" in data or "status" in data
@@ -0,0 +1,6 @@
1
+ -r requirements.txt
2
+ black>=21.12
3
+ isort>=5.10.1
4
+ flake8>=3.9.0
5
+ build>=0.10.0
6
+ twine>=4.0.0
@@ -0,0 +1,4 @@
1
+ requests>=2.25.1
2
+ pydantic>=1.9.0
3
+ pytest>=6.0
4
+ pytest-asyncio>=0.21.0
@@ -0,0 +1,23 @@
1
+ from setuptools import setup, find_packages
2
+ import os
3
+
4
+ setup(
5
+ name="a3m_adapter",
6
+ version="1.0.0",
7
+ description="A3M Router adapters for LangChain, LlamaIndex, and other LLM frameworks",
8
+ long_description=open("README.md").read() if os.path.exists("README.md") else "",
9
+ long_description_content_type="text/markdown",
10
+ author="A3M Team",
11
+ author_email="hello@a3m.ai",
12
+ packages=find_packages(),
13
+ install_requires=[
14
+ "requests>=2.25.1",
15
+ "pydantic>=1.9.0",
16
+ ],
17
+ extras_require={
18
+ "langchain": ["langchain>=0.0.365", "langchain-core>=0.0.365"],
19
+ "llamaindex": ["llama-index>=0.8.0"],
20
+ "dev": ["pytest>=6.0"],
21
+ },
22
+ python_requires=">=3.8",
23
+ )