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,240 @@
1
+ """
2
+ A3M Router Qdrant adapter.
3
+
4
+ Enables A3M Router as the embedding + reranking layer for Qdrant vector search.
5
+
6
+ Usage:
7
+ from qdrant_client import QdrantClient
8
+ from a3m.adapters import QdrantAdapter
9
+
10
+ # Create A3M-powered Qdrant client
11
+ client = QdrantAdapter(
12
+ base_url="http://localhost:8787",
13
+ collection_name="my_docs",
14
+ )
15
+
16
+ # Generate embeddings via A3M
17
+ query_embedding = client.embed_query("What is machine learning?")
18
+
19
+ # Search Qdrant
20
+ results = client.search(query_vector=query_embedding, limit=5)
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import logging
26
+ from typing import Any, Dict, List, Optional, Union
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ QDRANT_AVAILABLE = False
31
+ try:
32
+ from qdrant_client import QdrantClient
33
+ from qdrant_client.http.exceptions import UnexpectedResponse
34
+ QDRANT_AVAILABLE = True
35
+ except ImportError:
36
+ logger.warning("Qdrant client not installed. pip install qdrant-client")
37
+
38
+ from a3m.client import A3MRouter, A3MRouterError
39
+
40
+ if QDRANT_AVAILABLE:
41
+ class QdrantAdapter(QdrantClient):
42
+ """
43
+ Qdrant client wrapper that uses A3M Router for embeddings.
44
+
45
+ Provides intelligent embedding generation and can be used as a
46
+ drop-in replacement for QdrantClient in most use cases.
47
+
48
+ Args:
49
+ base_url: A3M Router server URL (for embeddings).
50
+ collection_name: Default collection to search.
51
+ embed_model: Embedding model ("auto" for A3M selection).
52
+ host: Qdrant server host.
53
+ port: Qdrant server port.
54
+ **kwargs: Additional QdrantClient options.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ base_url: str = "http://localhost:8787",
60
+ collection_name: Optional[str] = None,
61
+ embed_model: str = "auto",
62
+ host: Optional[str] = None,
63
+ port: Optional[int] = None,
64
+ **kwargs: Any,
65
+ ) -> None:
66
+ # Initialize QdrantClient
67
+ if host and port:
68
+ super().__init__(host=host, port=port, **kwargs)
69
+ else:
70
+ super().__init__(**kwargs)
71
+
72
+ self._a3m_router = A3MRouter(base_url=base_url, default_model=embed_model)
73
+ self._collection_name = collection_name
74
+ self._embed_model = embed_model
75
+
76
+ def embed_query(
77
+ self,
78
+ query: str,
79
+ **kwargs: Any,
80
+ ) -> List[float]:
81
+ """
82
+ Generate embedding for a query string.
83
+
84
+ Args:
85
+ query: Text to embed.
86
+
87
+ Returns:
88
+ Embedding vector (list of floats).
89
+ """
90
+ try:
91
+ response = self._a3m_router.embed(texts=[query], model=self._embed_model)
92
+ return response.embedding
93
+ except A3MRouterError as e:
94
+ logger.error(f"A3M Router embedding error: {e}")
95
+ raise
96
+
97
+ def embedTexts(
98
+ self,
99
+ texts: List[str],
100
+ **kwargs: Any,
101
+ ) -> List[List[float]]:
102
+ """
103
+ Generate embeddings for multiple texts.
104
+
105
+ Args:
106
+ texts: List of texts to embed.
107
+
108
+ Returns:
109
+ List of embedding vectors.
110
+ """
111
+ try:
112
+ response = self._a3m_router.embed(texts=texts, model=self._embed_model)
113
+ if isinstance(response, list):
114
+ return [r.embedding for r in response]
115
+ return [response.embedding]
116
+ except A3MRouterError as e:
117
+ logger.error(f"A3M Router embedding error: {e}")
118
+ raise
119
+
120
+ def search(
121
+ self,
122
+ collection_name: Optional[str] = None,
123
+ query_vector: Optional[List[float]] = None,
124
+ query_filter: Optional[Any] = None,
125
+ limit: int = 10,
126
+ offset: Optional[int] = None,
127
+ with_vectors: bool = False,
128
+ with_payload: bool = True,
129
+ **kwargs: Any,
130
+ ) -> List[Any]:
131
+ """
132
+ Search Qdrant with A3M-generated query embedding.
133
+
134
+ Args:
135
+ collection_name: Collection to search. Uses default if None.
136
+ query_vector: Query embedding. If None, uses A3M to embed a query.
137
+ query_filter: Qdrant filter condition.
138
+ limit: Number of results to return.
139
+ offset: Pagination offset.
140
+ with_vectors: Include vectors in results.
141
+ with_payload: Include payload in results.
142
+
143
+ Returns:
144
+ List of search results.
145
+ """
146
+ collection = collection_name or self._collection_name
147
+ if not collection:
148
+ raise ValueError("collection_name is required")
149
+
150
+ if query_vector is None:
151
+ raise ValueError("query_vector is required")
152
+
153
+ return super().search(
154
+ collection_name=collection,
155
+ query_vector=query_vector,
156
+ query_filter=query_filter,
157
+ limit=limit,
158
+ offset=offset,
159
+ with_vectors=with_vectors,
160
+ with_payload=with_payload,
161
+ **kwargs,
162
+ )
163
+
164
+ def rag_search(
165
+ self,
166
+ query: str,
167
+ collection_name: Optional[str] = None,
168
+ limit: int = 5,
169
+ rerank: bool = True,
170
+ **kwargs: Any,
171
+ ) -> Dict[str, Any]:
172
+ """
173
+ Perform RAG search: embed query + Qdrant search + context build.
174
+
175
+ Args:
176
+ query: User query string.
177
+ collection_name: Collection to search.
178
+ limit: Number of chunks to retrieve.
179
+ rerank: Whether to rerank results using A3M.
180
+
181
+ Returns:
182
+ Dict with 'chunks', 'context', 'answer', 'provider'.
183
+ """
184
+ collection = collection_name or self._collection_name
185
+ if not collection:
186
+ raise ValueError("collection_name is required")
187
+
188
+ # 1. Embed query via A3M
189
+ query_embedding = self.embed_query(query)
190
+
191
+ # 2. Search Qdrant
192
+ results = self.search(
193
+ collection_name=collection,
194
+ query_vector=query_embedding,
195
+ limit=limit,
196
+ with_payload=True,
197
+ )
198
+
199
+ # 3. Build context from results
200
+ chunks = []
201
+ for r in results:
202
+ payload = r.payload or {}
203
+ text = payload.get("text", payload.get("content", str(r.id)))
204
+ chunks.append({"id": r.id, "text": text, "score": r.score})
205
+
206
+ context = "\n\n".join([c["text"] for c in chunks])
207
+
208
+ # 4. Generate answer via A3M if reranking
209
+ answer = None
210
+ provider = None
211
+ if rerank and chunks:
212
+ try:
213
+ response = self._a3m_router.chat(
214
+ messages=[{
215
+ "role": "user",
216
+ "content": f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer concisely:"
217
+ }],
218
+ model=self._embed_model,
219
+ )
220
+ answer = response.content
221
+ provider = response.provider
222
+ except A3MRouterError:
223
+ answer = chunks[0]["text"] if chunks else ""
224
+ provider = "qdrant"
225
+
226
+ return {
227
+ "chunks": chunks,
228
+ "context": context,
229
+ "answer": answer,
230
+ "provider": provider,
231
+ "query_embedding": query_embedding[:10], # First 10 dims for debug
232
+ }
233
+
234
+ else:
235
+ class QdrantAdapter:
236
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
237
+ raise ImportError(
238
+ "Qdrant client not installed. "
239
+ "Install with: pip install qdrant-client"
240
+ )
@@ -0,0 +1,263 @@
1
+ """
2
+ A3M Router Weaviate adapter.
3
+
4
+ Enables A3M Router as the embedding layer for Weaviate vector search.
5
+
6
+ Usage:
7
+ import weaviate
8
+ from a3m.adapters import WeaviateAdapter
9
+
10
+ # Create A3M-powered Weaviate client
11
+ client = WeaviateAdapter(
12
+ a3m_base_url="http://localhost:8787",
13
+ weaviate_url="http://localhost:8080",
14
+ )
15
+
16
+ # Generate embeddings via A3M
17
+ query_embedding = client.embed_query("What is AI?")
18
+
19
+ # Search Weaviate
20
+ results = client.query.get("Article", ["title", "content"]).with_near_vector({
21
+ "vector": query_embedding
22
+ }).with_limit(5).do()
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import logging
28
+ from typing import Any, Dict, List, Optional
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ WEAVIATE_AVAILABLE = False
33
+ try:
34
+ import weaviate
35
+ from weaviate import Client as WeaviateClient
36
+ from weaviate.types import NUMBERS
37
+ WEAVIATE_AVAILABLE = True
38
+ except ImportError:
39
+ logger.warning("Weaviate client not installed. pip install weaviate-client")
40
+
41
+ from a3m.client import A3MRouter, A3MRouterError
42
+
43
+ if WEAVIATE_AVAILABLE:
44
+ class WeaviateAdapter(WeaviateClient):
45
+ """
46
+ Weaviate client wrapper that uses A3M Router for embeddings.
47
+
48
+ Provides intelligent embedding generation for Weaviate's
49
+ vector search with automatic model selection.
50
+
51
+ Args:
52
+ a3m_base_url: A3M Router server URL (for embeddings).
53
+ embed_model: Embedding model ("auto" for A3M selection).
54
+ weaviate_url: Weaviate server URL.
55
+ weaviate_client: Existing Weaviate client (for hybrid use).
56
+ **kwargs: Additional WeaviateClient options.
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ a3m_base_url: str = "http://localhost:8787",
62
+ embed_model: str = "auto",
63
+ weaviate_url: Optional[str] = None,
64
+ weaviate_client: Optional[WeaviateClient] = None,
65
+ **kwargs: Any,
66
+ ) -> None:
67
+ # Initialize Weaviate client if URL provided
68
+ if weaviate_client:
69
+ # Copy internal state from provided client
70
+ self._connection = weaviate_client._connection
71
+ self._batch = weaviate_client._batch
72
+ self._schema = weaviate_client._schema
73
+ elif weaviate_url:
74
+ super().__init__(url=weaviate_url, **kwargs)
75
+ else:
76
+ # Initialize with empty client for embedding-only use
77
+ self._connection = None
78
+ self._batch = None
79
+ self._schema = None
80
+
81
+ self._a3m_router = A3MRouter(base_url=a3m_base_url, default_model=embed_model)
82
+ self._embed_model = embed_model
83
+
84
+ def embed_query(
85
+ self,
86
+ query: str,
87
+ **kwargs: Any,
88
+ ) -> List[float]:
89
+ """
90
+ Generate embedding for a query string.
91
+
92
+ Args:
93
+ query: Text to embed.
94
+
95
+ Returns:
96
+ Embedding vector (list of floats).
97
+ """
98
+ try:
99
+ response = self._a3m_router.embed(texts=[query], model=self._embed_model)
100
+ return response.embedding
101
+ except A3MRouterError as e:
102
+ logger.error(f"A3M Router embedding error: {e}")
103
+ raise
104
+
105
+ def embed_texts(
106
+ self,
107
+ texts: List[str],
108
+ **kwargs: Any,
109
+ ) -> List[List[float]]:
110
+ """
111
+ Generate embeddings for multiple texts.
112
+
113
+ Args:
114
+ texts: List of texts to embed.
115
+
116
+ Returns:
117
+ List of embedding vectors.
118
+ """
119
+ try:
120
+ response = self._a3m_router.embed(texts=texts, model=self._embed_model)
121
+ if isinstance(response, list):
122
+ return [r.embedding for r in response]
123
+ return [response.embedding]
124
+ except A3MRouterError as e:
125
+ logger.error(f"A3M Router embedding error: {e}")
126
+ raise
127
+
128
+ def with_near_text(
129
+ self,
130
+ query: str,
131
+ **kwargs: Any,
132
+ ) -> "WeaviateNearText":
133
+ """
134
+ Create a near-text search using A3M embeddings.
135
+
136
+ Args:
137
+ query: Text to search for.
138
+
139
+ Returns:
140
+ WeaviateNearText object ready for .do()
141
+ """
142
+ embedding = self.embed_query(query)
143
+ return WeaviateNearText(
144
+ client=self,
145
+ text=query,
146
+ embedding=embedding,
147
+ **kwargs,
148
+ )
149
+
150
+ def rag_search(
151
+ self,
152
+ query: str,
153
+ class_name: str,
154
+ properties: List[str],
155
+ limit: int = 5,
156
+ **kwargs: Any,
157
+ ) -> Dict[str, Any]:
158
+ """
159
+ Perform RAG search: embed query + Weaviate search + context build.
160
+
161
+ Args:
162
+ query: User query string.
163
+ class_name: Weaviate class to search.
164
+ properties: Properties to return.
165
+ limit: Number of results.
166
+
167
+ Returns:
168
+ Dict with 'chunks', 'context', 'answer', 'provider'.
169
+ """
170
+ # 1. Embed query via A3M
171
+ query_embedding = self.embed_query(query)
172
+
173
+ # 2. Search Weaviate
174
+ try:
175
+ results = (
176
+ self.query
177
+ .get(class_name, properties)
178
+ .with_near_vector({"vector": query_embedding})
179
+ .with_limit(limit)
180
+ .do()
181
+ )
182
+ except Exception as e:
183
+ logger.warning(f"Weaviate search error: {e}")
184
+ results = {"data": {"Get": {class_name: []}}}
185
+
186
+ # 3. Extract chunks
187
+ data = results.get("data", {}).get("Get", {}).get(class_name, [])
188
+ chunks = []
189
+ for item in data:
190
+ text = " ".join([str(item.get(p, "")) for p in properties if p in item])
191
+ chunks.append(text)
192
+
193
+ context = "\n\n".join(chunks)
194
+
195
+ # 4. Generate answer via A3M
196
+ answer = None
197
+ provider = None
198
+ if chunks:
199
+ try:
200
+ response = self._a3m_router.chat(
201
+ messages=[{
202
+ "role": "user",
203
+ "content": f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer concisely:"
204
+ }],
205
+ model=self._embed_model,
206
+ )
207
+ answer = response.content
208
+ provider = response.provider
209
+ except A3MRouterError:
210
+ answer = chunks[0] if chunks else ""
211
+ provider = "weaviate"
212
+
213
+ return {
214
+ "chunks": chunks,
215
+ "context": context,
216
+ "answer": answer,
217
+ "provider": provider,
218
+ }
219
+
220
+ class WeaviateNearText:
221
+ """Helper class for near-text search with A3M embeddings."""
222
+
223
+ def __init__(
224
+ self,
225
+ client: WeaviateAdapter,
226
+ text: str,
227
+ embedding: List[float],
228
+ certainty: Optional[float] = None,
229
+ distance: Optional[float] = None,
230
+ ) -> None:
231
+ self._client = client
232
+ self._text = text
233
+ self._embedding = embedding
234
+ self._certainty = certainty
235
+ self._distance = distance
236
+
237
+ def with_limit(self, limit: int) -> "WeaviateNearText":
238
+ """Set result limit."""
239
+ self._limit = limit
240
+ return self
241
+
242
+ def do(self) -> Dict[str, Any]:
243
+ """Execute the search."""
244
+ near_vector = {"vector": self._embedding}
245
+ if self._certainty is not None:
246
+ near_vector["certainty"] = self._certainty
247
+ if self._distance is not None:
248
+ near_vector["distance"] = self._distance
249
+
250
+ return self._client.query.get(
251
+ self._class_name,
252
+ self._properties
253
+ ).with_near_vector(near_vector).with_limit(
254
+ getattr(self, "_limit", 10)
255
+ ).do()
256
+
257
+ else:
258
+ class WeaviateAdapter:
259
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
260
+ raise ImportError(
261
+ "Weaviate client not installed. "
262
+ "Install with: pip install weaviate-client"
263
+ )
@@ -188,3 +188,8 @@ class A3MRouter:
188
188
 
189
189
  async def __aexit__(self, *args):
190
190
  await self.close()
191
+
192
+
193
+ class A3MRouterError(Exception):
194
+ """Base exception for A3MRouter errors."""
195
+ pass
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # Build and verify Python SDK
3
+ set -e
4
+
5
+ echo "=== Building A3M Router Python SDK v2.2.0 ==="
6
+ cd "$(dirname "$0")"
7
+
8
+ echo "1. Installing build dependencies..."
9
+ pip install build twine 2>/dev/null || pip3 install build twine
10
+
11
+ echo "2. Cleaning old builds..."
12
+ rm -rf dist/ build/ *.egg-info
13
+
14
+ echo "3. Building package..."
15
+ python -m build
16
+
17
+ echo "4. Checking package..."
18
+ python -m twine check dist/*
19
+
20
+ echo "5. Listing built files..."
21
+ ls -lh dist/
22
+
23
+ echo ""
24
+ echo "=== Build complete! ==="
25
+ echo "To publish to Test PyPI:"
26
+ echo " cd python && python -m twine upload --repository testpypi dist/*"
27
+ echo ""
28
+ echo "To publish to PyPI:"
29
+ echo " cd python && python -m twine upload dist/*"
30
+ echo ""
31
+ echo "To install locally:"
32
+ echo " pip install dist/a3m_router-*.whl"