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,73 @@
1
+ # A3M Router Integration Summary
2
+
3
+ ## Repositories Created This Session
4
+
5
+ | # | Repo | Description | Stars | Status |
6
+ |---|------|-------------|-------|--------|
7
+ | 1 | [browser-use](https://github.com/browser-use/browser-use/pull/5378) | Browser automation | 108k | PR Open |
8
+ | 2 | [crewAI](https://github.com/crewAIInc/crewAI/pull/6794) | Multi-agent framework | 57k | PR Open |
9
+ | 3 | [n8n-nodes-a3m-router](https://github.com/Das-rebel/n8n-nodes-a3m-router) | n8n workflow automation | 55k | Pushed |
10
+ | 4 | [a3m-autogen](https://github.com/Das-rebel/a3m-autogen) | AutoGen examples | 35k | Pushed |
11
+ | 5 | [a3m-cursor-mcp](https://github.com/Das-rebel/a3m-cursor-mcp) | Cursor MCP server | 45k | Pushed |
12
+ | 6 | [a3m-llamaindex](https://github.com/Das-rebel/a3m-llamaindex) | LlamaIndex RAG | 35k | Pushed |
13
+
14
+ ## Total Potential Impact
15
+
16
+ - **Total stars of integrated tools**: 370k+
17
+ - **PRs submitted**: 2 (browser-use, crewAI)
18
+ - **Repositories created**: 4 new
19
+
20
+ ## Value Proposition
21
+
22
+ ### For Tool Users
23
+ - 70-95% cost savings on LLM calls
24
+ - Automatic model selection
25
+ - No configuration needed
26
+ - Built-in fallback handling
27
+
28
+ ### For Tool Maintainers
29
+ - Differentiation from LiteLLM (CVE-free)
30
+ - Value-add for users
31
+ - Easy integration (OpenAI-compatible)
32
+
33
+ ## Next Steps
34
+
35
+ ### High Priority
36
+ 1. Get browser-use PR merged
37
+ 2. Get crewAI PR merged
38
+ 3. Create n8n community node submission
39
+
40
+ ### Medium Priority
41
+ 4. AutoGen official integration
42
+ 5. LlamaIndex official integration
43
+ 6. LangChain enhanced docs
44
+
45
+ ### Lower Priority
46
+ 7. RPA tool integrations (UiPath, Power Automate)
47
+ 8. Enterprise integrations (Salesforce, HubSpot)
48
+ 9. More framework integrations
49
+
50
+ ## Key Markets
51
+
52
+ 1. **AI Agent Frameworks**: 150k+ combined stars
53
+ 2. **Browser Automation**: 100k+ combined stars
54
+ 3. **Workflow Automation**: 55k+ stars
55
+ 4. **Coding Tools**: 45k+ stars
56
+ 5. **RAG Platforms**: 35k+ stars
57
+
58
+ ## Competitive Advantage
59
+
60
+ | Feature | LiteLLM | A3M Router |
61
+ |---------|---------|-------------|
62
+ | Security | CVE-2026-42271 | Clean |
63
+ | Size | Heavy | Lightweight |
64
+ | Routing | Heuristic | Biology-inspired |
65
+ | Cost | Markup | Provider rate |
66
+ | Maintenance | Slow fixes | Fast |
67
+
68
+ ## Resources
69
+
70
+ - [A3M Router](https://github.com/Das-rebel/a3m-router)
71
+ - [Integration Progress](./INTEGRATION_PROGRESS.md)
72
+ - [Opportunities List](./OPPORTUNITIES_100.md)
73
+
@@ -1,15 +1,51 @@
1
1
  """
2
2
  A3M Router Adapters for LLM Frameworks.
3
3
 
4
- Provides drop-in adapters for:
4
+ Provides drop-in adapters to integrate A3M Router with popular frameworks:
5
5
  - LangChain (A3MLangChainAdapter)
6
6
  - LlamaIndex (A3MLlamaIndexAdapter)
7
+ - AutoGen (A3MAutoGenAdapter)
8
+ - Vercel AI SDK (A3MVercelAdapter)
9
+ - Haystack (A3MHaystackAdapter)
10
+ - Pinecone (A3MPineconeAdapter)
11
+ - LangGraph (A3MLangGraphAdapter)
7
12
  - Configuration management (A3MConfig)
13
+
14
+ Usage:
15
+ from a3m_adapter import (
16
+ A3MLangChainAdapter,
17
+ A3MLlamaIndexAdapter,
18
+ A3MAutoGenAdapter,
19
+ A3MVercelAdapter,
20
+ A3MHaystackAdapter,
21
+ A3MPineconeAdapter,
22
+ A3MLangGraphAdapter,
23
+ A3MConfig,
24
+ )
8
25
  """
9
26
 
10
27
  from .adapter.langchain import A3MLangChainAdapter
11
28
  from .adapter.llamaindex import A3MLlamaIndexAdapter
29
+ from .adapter.autogen import A3MAutoGenAdapter
30
+ from .adapter.vercel import A3MVercelAdapter, createA3MProvider
31
+ from .adapter.haystack import A3MHaystackAdapter
32
+ from .adapter.pinecone import A3MPineconeAdapter
33
+ from .adapter.langgraph import A3MLangGraphAdapter
12
34
  from .adapter.config import A3MConfig
13
35
 
14
- __all__ = ['A3MLangChainAdapter', 'A3MLlamaIndexAdapter', 'A3MConfig']
15
- __version__ = '1.0.0'
36
+ __all__ = [
37
+ # Core adapters
38
+ 'A3MLangChainAdapter',
39
+ 'A3MLlamaIndexAdapter',
40
+ 'A3MAutoGenAdapter',
41
+ 'A3MVercelAdapter',
42
+ 'A3MHaystackAdapter',
43
+ 'A3MPineconeAdapter',
44
+ 'A3MLangGraphAdapter',
45
+ # Config
46
+ 'A3MConfig',
47
+ # Utilities
48
+ 'createA3MProvider',
49
+ ]
50
+
51
+ __version__ = '2.0.0'
@@ -2,6 +2,21 @@
2
2
 
3
3
  from .langchain import A3MLangChainAdapter
4
4
  from .llamaindex import A3MLlamaIndexAdapter
5
+ from .autogen import A3MAutoGenAdapter
6
+ from .vercel import A3MVercelAdapter, createA3MProvider
7
+ from .haystack import A3MHaystackAdapter
8
+ from .pinecone import A3MPineconeAdapter
9
+ from .langgraph import A3MLangGraphAdapter
5
10
  from .config import A3MConfig
6
11
 
7
- __all__ = ['A3MLangChainAdapter', 'A3MLlamaIndexAdapter', 'A3MConfig']
12
+ __all__ = [
13
+ 'A3MLangChainAdapter',
14
+ 'A3MLlamaIndexAdapter',
15
+ 'A3MAutoGenAdapter',
16
+ 'A3MVercelAdapter',
17
+ 'createA3MProvider',
18
+ 'A3MHaystackAdapter',
19
+ 'A3MPineconeAdapter',
20
+ 'A3MLangGraphAdapter',
21
+ 'A3MConfig',
22
+ ]
@@ -0,0 +1,169 @@
1
+ """
2
+ A3M Router Adapter for AutoGen (Microsoft).
3
+
4
+ Drop-in replacement for AutoGen's LLMAgent that routes through A3M Router
5
+ for intelligent, cost-optimized multi-agent conversations.
6
+
7
+ Usage:
8
+ from autogen import ConversableAgent
9
+ from a3m_adapter import A3MAutoGenAdapter
10
+
11
+ llm_config = {
12
+ "model": "auto",
13
+ "temperature": 0.7,
14
+ "parallel_ensemble": 2,
15
+ }
16
+
17
+ assistant = ConversableAgent(
18
+ name="assistant",
19
+ llm_config=llm_config,
20
+ )
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
+ A3M_AVAILABLE = False
31
+ try:
32
+ from a3m.router import A3MRouter, RouteResponse
33
+ A3M_AVAILABLE = True
34
+ except ImportError:
35
+ logger.warning(
36
+ "A3M Router not installed. Install with: pip install adaptive-memory-multi-model-router"
37
+ )
38
+
39
+
40
+ class A3MAutoGenAdapter:
41
+ """
42
+ A3M Router adapter for AutoGen's ConversableAgent.
43
+
44
+ Enables AutoGen agents to use A3M Router for automatic model selection
45
+ across 47+ providers with cost optimization.
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ model: str = "auto",
51
+ temperature: float = 0.7,
52
+ max_tokens: Optional[int] = 4096,
53
+ parallel_ensemble: int = 1,
54
+ api_key: Optional[str] = None,
55
+ **kwargs: Any,
56
+ ) -> None:
57
+ """
58
+ Initialize A3M Router adapter for AutoGen.
59
+ """
60
+ self.model = model
61
+ self.temperature = temperature
62
+ self.max_tokens = max_tokens
63
+ self.parallel_ensemble = parallel_ensemble
64
+ self.api_key = api_key
65
+ self._a3m_router = None
66
+ self._initialized = False
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 AutoGen: model=%s, ensemble=%d",
87
+ self.model,
88
+ self.parallel_ensemble,
89
+ )
90
+
91
+ def create_agent_config(self) -> Dict[str, Any]:
92
+ """
93
+ Create AutoGen-compatible agent config.
94
+
95
+ Returns a config dict that can be passed to ConversableAgent.
96
+ """
97
+ return {
98
+ "model": self.model,
99
+ "temperature": self.temperature,
100
+ "max_tokens": self.max_tokens,
101
+ "parallel_ensemble": self.parallel_ensemble,
102
+ "a3m_router": self, # Pass self as the router
103
+ }
104
+
105
+ def chat(
106
+ self,
107
+ messages: List[Dict[str, str]],
108
+ **kwargs: Any,
109
+ ) -> Dict[str, Any]:
110
+ """
111
+ Generate a response using A3M Router.
112
+
113
+ Args:
114
+ messages: List of message dicts with 'role' and 'content'
115
+
116
+ Returns:
117
+ Response dict with 'content', 'provider', 'cost'
118
+ """
119
+ self._ensure_router()
120
+
121
+ import asyncio
122
+ loop = asyncio.get_event_loop()
123
+ route_result = loop.run_in_executor(
124
+ None,
125
+ lambda: self._a3m_router.route(
126
+ messages=messages,
127
+ temperature=self.temperature,
128
+ max_tokens=self.max_tokens,
129
+ **kwargs,
130
+ ),
131
+ )
132
+
133
+ return {
134
+ "content": route_result.content,
135
+ "provider": getattr(route_result, 'provider', 'unknown'),
136
+ "cost": getattr(route_result, 'cost', 0.0),
137
+ "finish_reason": getattr(route_result, 'finish_reason', 'stop'),
138
+ }
139
+
140
+ async def achat(
141
+ self,
142
+ messages: List[Dict[str, str]],
143
+ **kwargs: Any,
144
+ ) -> Dict[str, Any]:
145
+ """Async version of chat."""
146
+ self._ensure_router()
147
+
148
+ route_result = await self._a3m_router.aroute(
149
+ messages=messages,
150
+ temperature=self.temperature,
151
+ max_tokens=self.max_tokens,
152
+ **kwargs,
153
+ )
154
+
155
+ return {
156
+ "content": route_result.content,
157
+ "provider": getattr(route_result, 'provider', 'unknown'),
158
+ "cost": getattr(route_result, 'cost', 0.0),
159
+ "finish_reason": getattr(route_result, 'finish_reason', 'stop'),
160
+ }
161
+
162
+ def __repr__(self) -> str:
163
+ return (
164
+ f"A3MAutoGenAdapter("
165
+ f"model={self.model!r}, "
166
+ f"temperature={self.temperature}, "
167
+ f"max_tokens={self.max_tokens}, "
168
+ f"ensemble={self.parallel_ensemble})"
169
+ )
@@ -0,0 +1,197 @@
1
+ """
2
+ A3M Router Adapter for Haystack (Deepset's RAG framework).
3
+
4
+ Drop-in replacement for Haystack's OpenAIGenerator that routes through A3M Router
5
+ for intelligent, cost-optimized RAG pipelines.
6
+
7
+ Usage:
8
+ from haystack import Pipeline
9
+ from haystack.nodes import Retriever, PromptNode
10
+ from a3m_adapter import A3MHaystackAdapter
11
+
12
+ prompt_node = PromptNode(
13
+ "auto",
14
+ api_key=None,
15
+ generator_type='openai',
16
+ model_adapter=A3MHaystackAdapter(model='auto', parallel_ensemble=2),
17
+ )
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ HAYSTACK_AVAILABLE = False
28
+ try:
29
+ from haystack.nodes.base import BaseGenerator
30
+ HAYSTACK_AVAILABLE = True
31
+ except ImportError:
32
+ logger.warning("Haystack not installed. Install with: pip install farm-haystack")
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 A3MHaystackAdapter:
45
+ """
46
+ A3M Router adapter for Haystack's PromptNode.
47
+
48
+ Enables Haystack RAG pipelines to use A3M Router for automatic model selection
49
+ across 47+ providers with cost optimization.
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 Haystack.
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 Haystack: model=%s",
92
+ self.model,
93
+ )
94
+
95
+ def predict(
96
+ self,
97
+ query: str,
98
+ documents: Optional[List[Any]] = None,
99
+ **kwargs: Any,
100
+ ) -> Dict[str, Any]:
101
+ """
102
+ Generate answer from query and optional retrieved documents.
103
+
104
+ Args:
105
+ query: The search query
106
+ documents: Optional list of retrieved documents for RAG
107
+
108
+ Returns:
109
+ Dict with 'answers', 'provider', 'cost'
110
+ """
111
+ self._ensure_router()
112
+
113
+ # Build context from documents if provided
114
+ if documents:
115
+ context = "\n\n".join([
116
+ f"Document {i+1}: {getattr(doc, 'content', str(doc))}"
117
+ for i, doc in enumerate(documents[:5]) # Limit to 5 docs
118
+ ])
119
+ prompt = f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer:"
120
+ else:
121
+ prompt = query
122
+
123
+ messages = [{"role": "user", "content": prompt}]
124
+
125
+ import asyncio
126
+ loop = asyncio.get_event_loop()
127
+ route_result = loop.run_in_executor(
128
+ None,
129
+ lambda: self._a3m_router.route(
130
+ messages=messages,
131
+ temperature=kwargs.get("temperature", self.temperature),
132
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
133
+ **kwargs,
134
+ ),
135
+ )
136
+
137
+ return {
138
+ "answers": [{"answer": route_result.content, "score": 1.0}],
139
+ "provider": getattr(route_result, 'provider', 'a3m'),
140
+ "cost": getattr(route_result, 'cost', 0.0),
141
+ }
142
+
143
+ async def apredict(
144
+ self,
145
+ query: str,
146
+ documents: Optional[List[Any]] = None,
147
+ **kwargs: Any,
148
+ ) -> Dict[str, Any]:
149
+ """Async predict for Haystack."""
150
+ self._ensure_router()
151
+
152
+ if documents:
153
+ context = "\n\n".join([
154
+ f"Document {i+1}: {getattr(doc, 'content', str(doc))}"
155
+ for i, doc in enumerate(documents[:5])
156
+ ])
157
+ prompt = f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer:"
158
+ else:
159
+ prompt = query
160
+
161
+ messages = [{"role": "user", "content": prompt}]
162
+
163
+ route_result = await self._a3m_router.aroute(
164
+ messages=messages,
165
+ temperature=kwargs.get("temperature", self.temperature),
166
+ max_tokens=kwargs.get("max_tokens", self.max_tokens),
167
+ **kwargs,
168
+ )
169
+
170
+ return {
171
+ "answers": [{"answer": route_result.content, "score": 1.0}],
172
+ "provider": getattr(route_result, 'provider', 'a3m'),
173
+ "cost": getattr(route_result, 'cost', 0.0),
174
+ }
175
+
176
+ def run(
177
+ self,
178
+ query: str,
179
+ documents: Optional[List[Any]] = None,
180
+ **kwargs: Any,
181
+ ) -> tuple[Dict[str, Any], str]:
182
+ """
183
+ Haystack-compatible run method.
184
+
185
+ Returns:
186
+ Tuple of (results dict, pipeline run metadata)
187
+ """
188
+ result = self.predict(query, documents, **kwargs)
189
+ return (result, "a3m-haystack")
190
+
191
+ def __repr__(self) -> str:
192
+ return (
193
+ f"A3MHaystackAdapter("
194
+ f"model={self.model!r}, "
195
+ f"temperature={self.temperature}, "
196
+ f"max_tokens={self.max_tokens})"
197
+ )