adaptive-memory-multi-model-router 2.15.4 → 2.15.5

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 (173) hide show
  1. package/.github/workflows/adapters-ci.yml +142 -0
  2. package/.github/workflows/ci.yml +2 -5
  3. package/.github/workflows/pypi-publish.yml +102 -0
  4. package/AGENT_COUNCIL_FINDINGS.md +123 -98
  5. package/CAMPAIGN_SUMMARY.md +87 -0
  6. package/CHANGELOG.md +7 -1
  7. package/ENTERPRISE_INTEGRATIONS.md +94 -0
  8. package/INTEGRATION_PROGRESS.md +96 -0
  9. package/OPPORTUNITIES_100.md +277 -0
  10. package/POPULARITY_BOOSTERS.md +1 -1
  11. package/README.md +137 -142
  12. package/README_ja.md +17 -21
  13. package/README_zh.md +13 -17
  14. package/TODO_BROWSER_AUTOMATION.md +87 -0
  15. package/a3m-integrations-summary.md +73 -0
  16. package/adapters/a3m_adapter/__init__.py +39 -3
  17. package/adapters/a3m_adapter/adapter/__init__.py +16 -1
  18. package/adapters/a3m_adapter/adapter/autogen.py +169 -0
  19. package/adapters/a3m_adapter/adapter/haystack.py +197 -0
  20. package/adapters/a3m_adapter/adapter/langgraph.py +196 -0
  21. package/adapters/a3m_adapter/adapter/pinecone.py +217 -0
  22. package/adapters/a3m_adapter/adapter/vercel.py +188 -0
  23. package/adapters/a3m_adapter/tests/test_adapters.py +29 -31
  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/apps/cost-calculator/README.md +72 -0
  28. package/apps/cost-calculator/calculator.css +280 -0
  29. package/apps/cost-calculator/calculator.js +150 -0
  30. package/apps/cost-calculator/index.html +321 -0
  31. package/apps/cost-calculator/package.json +13 -0
  32. package/articles/ANNOUNCEMENT_reddit_ml.md +76 -0
  33. package/articles/ANNOUNCEMENT_vc/347/244/276/345/214/272.md +71 -0
  34. package/articles/ANNOUNCEMENT_vercel.md +85 -0
  35. package/articles/CHINESE_DIRECTORIES.md +2 -2
  36. package/articles/CHINESE_SUBMISSIONS_READY.md +5 -5
  37. package/articles/HN_POST_READY.md +16 -20
  38. package/articles/INDIEHACKERS_READY.md +19 -34
  39. package/demo.py +251 -0
  40. package/dist/analytics/costAnalytics.d.ts +0 -1
  41. package/dist/benchmark/reproducible.d.ts +0 -1
  42. package/dist/cache/semanticCache.d.ts +0 -1
  43. package/dist/cost/budgetEnforcer.d.ts +0 -1
  44. package/dist/cost/costTracker.d.ts +0 -1
  45. package/dist/ensemble/multiRoundDialog.d.ts +0 -1
  46. package/dist/ensemble/shapleyValue.d.ts +0 -1
  47. package/dist/ensemble.d.ts +0 -1
  48. package/dist/index.d.ts +0 -1
  49. package/dist/integrations/oauth.d.ts +0 -1
  50. package/dist/integrations/scienceAdapter.d.ts +0 -1
  51. package/dist/memory/autoFetch.d.ts +0 -1
  52. package/dist/memory/hybridMemory.d.ts +0 -1
  53. package/dist/memory/memoryTree.d.ts +0 -1
  54. package/dist/memory/obsidianVault.d.ts +0 -1
  55. package/dist/memory/reasoningBank.d.ts +0 -1
  56. package/dist/observability/changeWatch.d.ts +0 -1
  57. package/dist/observability/fatigueDetector.d.ts +0 -1
  58. package/dist/observability/index.d.ts +0 -1
  59. package/dist/observability/metrics.d.ts +0 -1
  60. package/dist/observability/middleware.d.ts +0 -1
  61. package/dist/observability/tracer.d.ts +0 -1
  62. package/dist/observability/types.d.ts +0 -1
  63. package/dist/providers/providerConfig.d.ts +5 -2
  64. package/dist/providers/providerConfig.js +1006 -1
  65. package/dist/providers/providerConfig.js.map +1 -1
  66. package/dist/routing/advancedRouter.d.ts +1 -2
  67. package/dist/routing/advancedRouter.js +84 -0
  68. package/dist/routing/advancedRouter.js.map +1 -1
  69. package/dist/routing/crossModelValidation.d.ts +0 -1
  70. package/dist/routing/providerHealth.d.ts +77 -2
  71. package/dist/routing/providerHealth.js +190 -4
  72. package/dist/routing/providerHealth.js.map +1 -1
  73. package/dist/routing/providerRetry.d.ts +0 -1
  74. package/dist/routing/shadowSampler.d.ts +187 -0
  75. package/dist/routing/shadowSampler.js +306 -0
  76. package/dist/routing/shadowSampler.js.map +1 -0
  77. package/dist/sdk.d.ts +0 -1
  78. package/dist/security/guardrails.d.ts +0 -1
  79. package/dist/server/dashboard.d.ts +0 -1
  80. package/dist/server/handlers/chatHandler.d.ts +0 -1
  81. package/dist/server/handlers/completionsHandler.d.ts +0 -1
  82. package/dist/server/handlers/embeddingsHandler.d.ts +0 -1
  83. package/dist/server/handlers/healthHandler.d.ts +0 -1
  84. package/dist/server/handlers/metricsHandler.d.ts +0 -1
  85. package/dist/server/handlers/modelsHandler.d.ts +0 -1
  86. package/dist/server/metrics.d.ts +0 -1
  87. package/dist/server/modelMapper.d.ts +0 -1
  88. package/dist/server/proxyServer.d.ts +0 -1
  89. package/dist/server/router.d.ts +0 -1
  90. package/dist/server/state.d.ts +0 -1
  91. package/dist/tui/dashboard.d.ts +0 -1
  92. package/dist/utils/costUtils.d.ts +0 -1
  93. package/dist/utils/sorting.d.ts +0 -1
  94. package/dist/utils/tokenUtils.d.ts +0 -1
  95. package/docker-compose.yml +84 -60
  96. package/docs/comparison.md +2 -2
  97. package/docs/llms-full.txt +89 -0
  98. package/docs/llms.txt +25 -67
  99. package/docs-site/index.html +9 -9
  100. package/integrations/browser-use/README.md +155 -0
  101. package/integrations/browser-use/examples/form-filling.ts +179 -0
  102. package/integrations/browser-use/index.ts +145 -0
  103. package/integrations/browser-use/package.json +34 -0
  104. package/integrations/browser-use/tsconfig.json +17 -0
  105. package/integrations/mcp-browser/README.md +177 -0
  106. package/integrations/mcp-browser/package.json +38 -0
  107. package/integrations/mcp-browser/src/index.ts +297 -0
  108. package/integrations/sota-browser/README.md +191 -0
  109. package/integrations/sota-browser/package.json +34 -0
  110. package/llms-full.txt +6 -6
  111. package/llms.txt +25 -67
  112. package/package.json +34 -5
  113. package/packages/a3m-vercel-ai/README.md +161 -0
  114. package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts +12 -0
  115. package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts.map +1 -0
  116. package/packages/a3m-vercel-ai/dist/a3m-language-model.js +289 -0
  117. package/packages/a3m-vercel-ai/dist/a3m-language-model.js.map +1 -0
  118. package/packages/a3m-vercel-ai/dist/index.d.ts +82 -0
  119. package/packages/a3m-vercel-ai/dist/index.d.ts.map +1 -0
  120. package/packages/a3m-vercel-ai/dist/index.js +79 -0
  121. package/packages/a3m-vercel-ai/dist/index.js.map +1 -0
  122. package/packages/a3m-vercel-ai/dist/types.d.ts +97 -0
  123. package/packages/a3m-vercel-ai/dist/types.d.ts.map +1 -0
  124. package/packages/a3m-vercel-ai/dist/types.js +5 -0
  125. package/packages/a3m-vercel-ai/dist/types.js.map +1 -0
  126. package/packages/a3m-vercel-ai/package-lock.json +969 -0
  127. package/packages/a3m-vercel-ai/package.json +46 -0
  128. package/packages/a3m-vercel-ai/src/a3m-language-model.ts +381 -0
  129. package/packages/a3m-vercel-ai/src/index.ts +104 -0
  130. package/packages/a3m-vercel-ai/src/types.ts +116 -0
  131. package/packages/a3m-vercel-ai/tsconfig.json +20 -0
  132. package/packages/agentkit-adapter/LICENSE +21 -0
  133. package/packages/agentkit-adapter/README.md +126 -0
  134. package/packages/agentkit-adapter/examples/agentkit-example.ts +139 -0
  135. package/packages/agentkit-adapter/package.json +57 -0
  136. package/packages/agentkit-adapter/src/adapter.ts +381 -0
  137. package/packages/agentkit-adapter/src/index.ts +36 -0
  138. package/packages/agentkit-adapter/src/types.ts +105 -0
  139. package/packages/agentkit-adapter/src/util.ts +13 -0
  140. package/packages/agentkit-adapter/tsconfig.json +22 -0
  141. package/prometheus.yml +8 -0
  142. package/python/README.md +92 -65
  143. package/python/a3m/__init__.py +32 -3
  144. package/python/a3m/adapters/__init__.py +21 -0
  145. package/python/a3m/adapters/langchain.py +190 -0
  146. package/python/a3m/adapters/llamaindex.py +249 -0
  147. package/python/a3m/adapters/qdrant.py +240 -0
  148. package/python/a3m/adapters/weaviate.py +263 -0
  149. package/python/a3m/client.py +5 -0
  150. package/python/a3m_router.egg-info/PKG-INFO +172 -0
  151. package/python/a3m_router.egg-info/SOURCES.txt +17 -0
  152. package/python/a3m_router.egg-info/dependency_links.txt +1 -0
  153. package/python/a3m_router.egg-info/requires.txt +24 -0
  154. package/python/a3m_router.egg-info/top_level.txt +1 -0
  155. package/python/build_verify.sh +32 -0
  156. package/python/dist/a3m_router-2.2.1-py3-none-any.whl +0 -0
  157. package/python/dist/a3m_router-2.2.1.tar.gz +0 -0
  158. package/python/dist/a3m_router-2.2.2-py3-none-any.whl +0 -0
  159. package/python/dist/a3m_router-2.2.2.tar.gz +0 -0
  160. package/python/mcp-server/README.md +172 -0
  161. package/python/mcp-server/a3m_mcp/__init__.py +15 -0
  162. package/python/mcp-server/a3m_mcp/__main__.py +15 -0
  163. package/python/mcp-server/a3m_mcp/server.py +339 -0
  164. package/python/mcp-server/pyproject.toml +24 -0
  165. package/python/pyproject.toml +56 -5
  166. package/python/setup.py +3 -28
  167. package/src/providers/providerConfig.ts +1053 -1
  168. package/summary.txt +38 -0
  169. package/tsconfig.build.json +1 -2
  170. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -94
  171. package/.github/ISSUE_TEMPLATE/config.yml +0 -17
  172. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -71
  173. package/.well-known/ai-plugin.json +0 -16
@@ -0,0 +1,196 @@
1
+ """
2
+ A3M Router Adapter for LangGraph (Microsoft's agent framework).
3
+
4
+ Drop-in replacement for LangGraph's stateful agent that routes through A3M Router
5
+ for intelligent, cost-optimized multi-step conversations.
6
+
7
+ Usage:
8
+ from langgraph.prebuilt import create_react_agent
9
+ from a3m_adapter import A3MLangGraphAdapter
10
+
11
+ adapter = A3MLangGraphAdapter(model='auto', parallel_ensemble=2)
12
+
13
+ agent = create_react_agent(adapter, tools=[...])
14
+
15
+ result = agent.invoke({"messages": [{"role": "user", "content": "Hello"]})
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ from typing import Any, Dict, List, TypedDict
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ LANGGRAPH_AVAILABLE = False
26
+ try:
27
+ import langgraph
28
+ from langgraph.prebuilt import create_react_agent
29
+ from langchain_core.messages import BaseMessage, AIMessage, HumanMessage
30
+ LANGGRAPH_AVAILABLE = True
31
+ except ImportError:
32
+ logger.warning("LangGraph not installed. Install with: pip install langgraph")
33
+
34
+ A3M_AVAILABLE = False
35
+ try:
36
+ from a3m.router import A3MRouter, RouteResponse
37
+ A3M_AVAILABLE = True
38
+ except ImportError:
39
+ logger.warning(
40
+ "A3M Router not installed. Install with: pip install adaptive-memory-multi-model-router"
41
+ )
42
+
43
+
44
+ class A3MLangGraphAdapter:
45
+ """
46
+ A3M Router adapter for LangGraph's prebuilt agents.
47
+
48
+ Enables LangGraph agents to use A3M Router for automatic model selection
49
+ across 47+ providers with cost optimization and stateful conversations.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ model: str = "auto",
55
+ temperature: float = 0.7,
56
+ max_tokens: int = 4096,
57
+ parallel_ensemble: int = 1,
58
+ api_key: Optional[str] = None,
59
+ **kwargs: Any,
60
+ ) -> None:
61
+ """
62
+ Initialize A3M Router adapter for LangGraph.
63
+ """
64
+ self.model = model
65
+ self.temperature = temperature
66
+ self.max_tokens = max_tokens
67
+ self.parallel_ensemble = parallel_ensemble
68
+ self.api_key = api_key
69
+ self._a3m_router = None
70
+ self._initialized = False
71
+ self._kwargs = kwargs
72
+
73
+ def _ensure_router(self) -> None:
74
+ """Lazily initialize the A3M router."""
75
+ if self._initialized:
76
+ return
77
+
78
+ if not A3M_AVAILABLE:
79
+ raise ImportError(
80
+ "A3M Router is not installed. "
81
+ "Install with: pip install adaptive-memory-multi-model-router"
82
+ )
83
+
84
+ self._a3m_router = A3MRouter(
85
+ model=self.model,
86
+ temperature=self.temperature,
87
+ parallel_ensemble=self.parallel_ensemble,
88
+ )
89
+ self._initialized = True
90
+ logger.info(
91
+ "A3M Router initialized for LangGraph: model=%s, ensemble=%d",
92
+ self.model,
93
+ self.parallel_ensemble,
94
+ )
95
+
96
+ def get_model(self):
97
+ """
98
+ Get the underlying model for LangGraph.
99
+
100
+ Returns an object compatible with LangGraph's prebuilt agents.
101
+ """
102
+ self._ensure_router()
103
+ return self
104
+
105
+ def __call__(
106
+ self,
107
+ state: Dict[str, Any],
108
+ **kwargs: Any,
109
+ ) -> Dict[str, Any]:
110
+ """
111
+ LangGraph-compatible callable for node execution.
112
+
113
+ Args:
114
+ state: LangGraph state dict with 'messages' key
115
+
116
+ Returns:
117
+ Updated state dict
118
+ """
119
+ self._ensure_router()
120
+
121
+ messages = state.get("messages", [])
122
+
123
+ # Convert LangGraph messages to A3M format
124
+ a3m_messages = self._convert_messages(messages)
125
+
126
+ import asyncio
127
+ loop = asyncio.get_event_loop()
128
+ route_result = loop.run_in_executor(
129
+ None,
130
+ lambda: self._a3m_router.route(
131
+ messages=a3m_messages,
132
+ temperature=kwargs.get("temperature", self.temperature),
133
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
134
+ **kwargs,
135
+ ),
136
+ )
137
+
138
+ # Add response to messages
139
+ new_messages = messages + [
140
+ AIMessage(content=route_result.content)
141
+ ]
142
+
143
+ return {
144
+ **state,
145
+ "messages": new_messages,
146
+ }
147
+
148
+ def _convert_messages(
149
+ self,
150
+ messages: List[BaseMessage],
151
+ ) -> List[Dict[str, Any]]:
152
+ """Convert LangGraph messages to A3M format."""
153
+ a3m_messages = []
154
+ for msg in messages:
155
+ if isinstance(msg, HumanMessage):
156
+ a3m_messages.append({"role": "user", "content": msg.content})
157
+ elif isinstance(msg, AIMessage):
158
+ a3m_messages.append({"role": "assistant", "content": msg.content})
159
+ else:
160
+ a3m_messages.append({"role": "user", "content": str(msg)})
161
+ return a3m_messages
162
+
163
+ async def ainvoke(
164
+ self,
165
+ state: Dict[str, Any],
166
+ **kwargs: Any,
167
+ ) -> Dict[str, Any]:
168
+ """Async version of __call__."""
169
+ self._ensure_router()
170
+
171
+ messages = state.get("messages", [])
172
+ a3m_messages = self._convert_messages(messages)
173
+
174
+ route_result = await self._a3m_router.aroute(
175
+ messages=a3m_messages,
176
+ temperature=kwargs.get("temperature", self.temperature),
177
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
178
+ **kwargs,
179
+ )
180
+
181
+ new_messages = messages + [
182
+ AIMessage(content=route_result.content)
183
+ ]
184
+
185
+ return {
186
+ **state,
187
+ "messages": new_messages,
188
+ }
189
+
190
+ def __repr__(self) -> str:
191
+ return (
192
+ f"A3MLangGraphAdapter("
193
+ f"model={self.model!r}, "
194
+ f"temperature={self.temperature}, "
195
+ f"ensemble={self.parallel_ensemble})"
196
+ )
@@ -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
+ )