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.
- package/.github/workflows/adapters-ci.yml +142 -0
- package/.github/workflows/ci.yml +2 -5
- package/.github/workflows/pypi-publish.yml +102 -0
- package/AGENT_COUNCIL_FINDINGS.md +123 -98
- package/CAMPAIGN_SUMMARY.md +87 -0
- package/CHANGELOG.md +7 -1
- package/ENTERPRISE_INTEGRATIONS.md +94 -0
- package/INTEGRATION_PROGRESS.md +96 -0
- package/OPPORTUNITIES_100.md +277 -0
- package/POPULARITY_BOOSTERS.md +1 -1
- package/README.md +137 -142
- package/README_ja.md +17 -21
- package/README_zh.md +13 -17
- package/TODO_BROWSER_AUTOMATION.md +87 -0
- package/a3m-integrations-summary.md +73 -0
- package/adapters/a3m_adapter/__init__.py +39 -3
- package/adapters/a3m_adapter/adapter/__init__.py +16 -1
- package/adapters/a3m_adapter/adapter/autogen.py +169 -0
- package/adapters/a3m_adapter/adapter/haystack.py +197 -0
- package/adapters/a3m_adapter/adapter/langgraph.py +196 -0
- package/adapters/a3m_adapter/adapter/pinecone.py +217 -0
- package/adapters/a3m_adapter/adapter/vercel.py +188 -0
- package/adapters/a3m_adapter/tests/test_adapters.py +29 -31
- package/adapters/a3m_adapter/tests/test_integration.py +80 -0
- package/adapters/requirements-dev.txt +6 -0
- package/adapters/requirements.txt +4 -0
- package/apps/cost-calculator/README.md +72 -0
- package/apps/cost-calculator/calculator.css +280 -0
- package/apps/cost-calculator/calculator.js +150 -0
- package/apps/cost-calculator/index.html +321 -0
- package/apps/cost-calculator/package.json +13 -0
- package/articles/ANNOUNCEMENT_reddit_ml.md +76 -0
- package/articles/ANNOUNCEMENT_vc/347/244/276/345/214/272.md +71 -0
- package/articles/ANNOUNCEMENT_vercel.md +85 -0
- package/articles/CHINESE_DIRECTORIES.md +2 -2
- package/articles/CHINESE_SUBMISSIONS_READY.md +5 -5
- package/articles/HN_POST_READY.md +16 -20
- package/articles/INDIEHACKERS_READY.md +19 -34
- package/demo.py +251 -0
- package/dist/analytics/costAnalytics.d.ts +0 -1
- package/dist/benchmark/reproducible.d.ts +0 -1
- package/dist/cache/semanticCache.d.ts +0 -1
- package/dist/cost/budgetEnforcer.d.ts +0 -1
- package/dist/cost/costTracker.d.ts +0 -1
- package/dist/ensemble/multiRoundDialog.d.ts +0 -1
- package/dist/ensemble/shapleyValue.d.ts +0 -1
- package/dist/ensemble.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/integrations/oauth.d.ts +0 -1
- package/dist/integrations/scienceAdapter.d.ts +0 -1
- package/dist/memory/autoFetch.d.ts +0 -1
- package/dist/memory/hybridMemory.d.ts +0 -1
- package/dist/memory/memoryTree.d.ts +0 -1
- package/dist/memory/obsidianVault.d.ts +0 -1
- package/dist/memory/reasoningBank.d.ts +0 -1
- package/dist/observability/changeWatch.d.ts +0 -1
- package/dist/observability/fatigueDetector.d.ts +0 -1
- package/dist/observability/index.d.ts +0 -1
- package/dist/observability/metrics.d.ts +0 -1
- package/dist/observability/middleware.d.ts +0 -1
- package/dist/observability/tracer.d.ts +0 -1
- package/dist/observability/types.d.ts +0 -1
- package/dist/providers/providerConfig.d.ts +5 -2
- package/dist/providers/providerConfig.js +1006 -1
- package/dist/providers/providerConfig.js.map +1 -1
- package/dist/routing/advancedRouter.d.ts +1 -2
- package/dist/routing/advancedRouter.js +84 -0
- package/dist/routing/advancedRouter.js.map +1 -1
- package/dist/routing/crossModelValidation.d.ts +0 -1
- package/dist/routing/providerHealth.d.ts +77 -2
- package/dist/routing/providerHealth.js +190 -4
- package/dist/routing/providerHealth.js.map +1 -1
- package/dist/routing/providerRetry.d.ts +0 -1
- package/dist/routing/shadowSampler.d.ts +187 -0
- package/dist/routing/shadowSampler.js +306 -0
- package/dist/routing/shadowSampler.js.map +1 -0
- package/dist/sdk.d.ts +0 -1
- package/dist/security/guardrails.d.ts +0 -1
- package/dist/server/dashboard.d.ts +0 -1
- package/dist/server/handlers/chatHandler.d.ts +0 -1
- package/dist/server/handlers/completionsHandler.d.ts +0 -1
- package/dist/server/handlers/embeddingsHandler.d.ts +0 -1
- package/dist/server/handlers/healthHandler.d.ts +0 -1
- package/dist/server/handlers/metricsHandler.d.ts +0 -1
- package/dist/server/handlers/modelsHandler.d.ts +0 -1
- package/dist/server/metrics.d.ts +0 -1
- package/dist/server/modelMapper.d.ts +0 -1
- package/dist/server/proxyServer.d.ts +0 -1
- package/dist/server/router.d.ts +0 -1
- package/dist/server/state.d.ts +0 -1
- package/dist/tui/dashboard.d.ts +0 -1
- package/dist/utils/costUtils.d.ts +0 -1
- package/dist/utils/sorting.d.ts +0 -1
- package/dist/utils/tokenUtils.d.ts +0 -1
- package/docker-compose.yml +84 -60
- package/docs/comparison.md +2 -2
- package/docs/llms-full.txt +89 -0
- package/docs/llms.txt +25 -67
- package/docs-site/index.html +9 -9
- package/integrations/browser-use/README.md +155 -0
- package/integrations/browser-use/examples/form-filling.ts +179 -0
- package/integrations/browser-use/index.ts +145 -0
- package/integrations/browser-use/package.json +34 -0
- package/integrations/browser-use/tsconfig.json +17 -0
- package/integrations/mcp-browser/README.md +177 -0
- package/integrations/mcp-browser/package.json +38 -0
- package/integrations/mcp-browser/src/index.ts +297 -0
- package/integrations/sota-browser/README.md +191 -0
- package/integrations/sota-browser/package.json +34 -0
- package/llms-full.txt +6 -6
- package/llms.txt +25 -67
- package/package.json +34 -5
- package/packages/a3m-vercel-ai/README.md +161 -0
- package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts +12 -0
- package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts.map +1 -0
- package/packages/a3m-vercel-ai/dist/a3m-language-model.js +289 -0
- package/packages/a3m-vercel-ai/dist/a3m-language-model.js.map +1 -0
- package/packages/a3m-vercel-ai/dist/index.d.ts +82 -0
- package/packages/a3m-vercel-ai/dist/index.d.ts.map +1 -0
- package/packages/a3m-vercel-ai/dist/index.js +79 -0
- package/packages/a3m-vercel-ai/dist/index.js.map +1 -0
- package/packages/a3m-vercel-ai/dist/types.d.ts +97 -0
- package/packages/a3m-vercel-ai/dist/types.d.ts.map +1 -0
- package/packages/a3m-vercel-ai/dist/types.js +5 -0
- package/packages/a3m-vercel-ai/dist/types.js.map +1 -0
- package/packages/a3m-vercel-ai/package-lock.json +969 -0
- package/packages/a3m-vercel-ai/package.json +46 -0
- package/packages/a3m-vercel-ai/src/a3m-language-model.ts +381 -0
- package/packages/a3m-vercel-ai/src/index.ts +104 -0
- package/packages/a3m-vercel-ai/src/types.ts +116 -0
- package/packages/a3m-vercel-ai/tsconfig.json +20 -0
- package/packages/agentkit-adapter/LICENSE +21 -0
- package/packages/agentkit-adapter/README.md +126 -0
- package/packages/agentkit-adapter/examples/agentkit-example.ts +139 -0
- package/packages/agentkit-adapter/package.json +57 -0
- package/packages/agentkit-adapter/src/adapter.ts +381 -0
- package/packages/agentkit-adapter/src/index.ts +36 -0
- package/packages/agentkit-adapter/src/types.ts +105 -0
- package/packages/agentkit-adapter/src/util.ts +13 -0
- package/packages/agentkit-adapter/tsconfig.json +22 -0
- package/prometheus.yml +8 -0
- package/python/README.md +92 -65
- package/python/a3m/__init__.py +32 -3
- package/python/a3m/adapters/__init__.py +21 -0
- package/python/a3m/adapters/langchain.py +190 -0
- package/python/a3m/adapters/llamaindex.py +249 -0
- package/python/a3m/adapters/qdrant.py +240 -0
- package/python/a3m/adapters/weaviate.py +263 -0
- package/python/a3m/client.py +5 -0
- package/python/a3m_router.egg-info/PKG-INFO +172 -0
- package/python/a3m_router.egg-info/SOURCES.txt +17 -0
- package/python/a3m_router.egg-info/dependency_links.txt +1 -0
- package/python/a3m_router.egg-info/requires.txt +24 -0
- package/python/a3m_router.egg-info/top_level.txt +1 -0
- package/python/build_verify.sh +32 -0
- package/python/dist/a3m_router-2.2.1-py3-none-any.whl +0 -0
- package/python/dist/a3m_router-2.2.1.tar.gz +0 -0
- package/python/dist/a3m_router-2.2.2-py3-none-any.whl +0 -0
- package/python/dist/a3m_router-2.2.2.tar.gz +0 -0
- package/python/mcp-server/README.md +172 -0
- package/python/mcp-server/a3m_mcp/__init__.py +15 -0
- package/python/mcp-server/a3m_mcp/__main__.py +15 -0
- package/python/mcp-server/a3m_mcp/server.py +339 -0
- package/python/mcp-server/pyproject.toml +24 -0
- package/python/pyproject.toml +56 -5
- package/python/setup.py +3 -28
- package/src/providers/providerConfig.ts +1053 -1
- package/summary.txt +38 -0
- package/tsconfig.build.json +1 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -94
- package/.github/ISSUE_TEMPLATE/config.yml +0 -17
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -71
- 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
|
|
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__ = [
|
|
15
|
-
|
|
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__ = [
|
|
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
|
+
)
|