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,172 @@
1
+ # A3M Router MCP Server (Python)
2
+
3
+ [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server for [A3M Router](https://github.com/Das-rebel/a3m-router) — parallel multi-LLM execution for AI agents.
4
+
5
+ Allows any MCP-compatible AI agent (Claude Desktop, Cursor, Windsurf, Codex, n8n, etc.) to use A3M's intelligent routing and ensemble execution directly from Python.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+ | `a3m_route` | Route a query to the optimal LLM provider — returns model, tier, cost, reasoning |
12
+ | `a3m_ensemble` | Execute a query across multiple providers in parallel and merge results |
13
+ | `a3m_classify` | Classify query type (fast/creative/deep/code) and get provider recommendations |
14
+ | `a3m_providers` | List all configured providers with cost and availability |
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # From source
20
+ cd python/mcp-server
21
+ pip install -e .
22
+
23
+ # Or install the package directly (when published)
24
+ pip install a3m-mcp-server
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Set environment variables:
30
+
31
+ ```bash
32
+ # Required: A3M Router URL (must be running)
33
+ export A3M_BASE_URL=http://localhost:8787
34
+
35
+ # Optional
36
+ export A3M_API_KEY=not-needed # default
37
+ export A3M_TIMEOUT=30.0
38
+ ```
39
+
40
+ ## Start A3M Router
41
+
42
+ ```bash
43
+ # Node.js way
44
+ npx a3m-router serve
45
+
46
+ # Python way
47
+ pip install a3m-router
48
+ a3m-router serve
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Claude Desktop
54
+
55
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "a3m-router": {
61
+ "command": "python",
62
+ "args": ["-m", "a3m_mcp"],
63
+ "env": {
64
+ "A3M_BASE_URL": "http://localhost:8787"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### Cursor
72
+
73
+ Settings → Features → MCP Servers → Add:
74
+
75
+ ```
76
+ Name: A3M Router
77
+ Command: python
78
+ Arguments: -m a3m_mcp
79
+ ```
80
+
81
+ ### n8n MCP Trigger
82
+
83
+ Add MCP Trigger node with:
84
+
85
+ ```json
86
+ {
87
+ "server": "a3m-router",
88
+ "command": "python",
89
+ "args": ["-m", "a3m_mcp"]
90
+ }
91
+ ```
92
+
93
+ ### Direct stdio (pipe JSON-RPC)
94
+
95
+ ```bash
96
+ # List tools
97
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python -m a3m_mcp
98
+
99
+ # Call a3m_route
100
+ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"a3m_route","arguments":{"query":"Write a Python quicksort"}}}' | python -m a3m_mcp
101
+ ```
102
+
103
+ ## Examples
104
+
105
+ ### Route a query
106
+
107
+ ```python
108
+ # a3m_route tool
109
+ Input: {"query": "Write a Python function to sort a list"}
110
+ Output:
111
+ {
112
+ "model": "groq/llama-3.3-70b-versatile",
113
+ "tier": "cheap",
114
+ "cost": 0.000012,
115
+ "reasoning": "Code query detected, routing to fast coding provider",
116
+ "classification": "code"
117
+ }
118
+ ```
119
+
120
+ ### Ensemble execution
121
+
122
+ ```python
123
+ # a3m_ensemble tool
124
+ Input: {"query": "Explain quantum computing in 3 sentences", "providers": ["groq", "google", "cerebras"]}
125
+ Output:
126
+ {
127
+ "query": "Explain quantum computing in 3 sentences",
128
+ "parallel_responses": [
129
+ {"provider": "groq", "content": "Quantum computing is..."},
130
+ {"provider": "google", "content": "At its core, quantum..."},
131
+ {"provider": "cerebras", "content": "Quantum computing leverages..."}
132
+ ],
133
+ "best_answer": "Quantum computing is...",
134
+ "stats": {"total_providers": 3, "successful": 3, "failed": 0}
135
+ }
136
+ ```
137
+
138
+ ## Architecture
139
+
140
+ ```
141
+ ┌──────────────────────────────────────────────────────┐
142
+ │ MCP Client (Claude, Cursor, Codex, n8n) │
143
+ └──────────────────────┬───────────────────────────────┘
144
+ │ MCP Protocol (stdio)
145
+ ┌──────────────────────▼───────────────────────────────┐
146
+ │ A3M Router MCP Server (Python) │
147
+ │ │
148
+ │ a3m_route a3m_ensemble a3m_classify a3m_providers │
149
+ └──────────────────────┬───────────────────────────────┘
150
+ │ HTTP API
151
+ ┌──────────────────────▼───────────────────────────────┐
152
+ │ A3M Router (a3m-router serve) │
153
+ │ │
154
+ │ routeQuery() parallelEnsemble() providers │
155
+ └───────┬────────────┬────────────┬─────────────────────┘
156
+ │ │ │
157
+ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐
158
+ │ Groq │ │ Google │ │Cerebras│ ...
159
+ └────────┘ └────────┘ └────────┘
160
+ ```
161
+
162
+ ## Testing
163
+
164
+ ```bash
165
+ cd python/mcp-server
166
+ pip install -e ".[dev]"
167
+ pytest
168
+ ```
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,15 @@
1
+ """A3M Router MCP Server — Model Context Protocol server for A3M Router.
2
+
3
+ Usage:
4
+ python -m a3m_mcp # Start the MCP server
5
+ pip install a3m-mcp-server # Install as package
6
+ """
7
+ try:
8
+ from .server import main, HAS_MCP, SERVER_NAME
9
+ except ImportError:
10
+ main = None
11
+ HAS_MCP = False
12
+ SERVER_NAME = "a3m-router"
13
+
14
+ __version__ = "1.0.0"
15
+ __all__ = ["main", "HAS_MCP", "SERVER_NAME", "__version__"]
@@ -0,0 +1,15 @@
1
+ """Entry point: python -m a3m_mcp"""
2
+ import sys
3
+ import os
4
+
5
+ # Add parent directory to path so a3m_mcp can find sibling packages
6
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7
+
8
+ from .server import main, HAS_MCP
9
+
10
+ if __name__ == "__main__":
11
+ if not HAS_MCP:
12
+ print("ERROR: mcp package required. Install with: pip install mcp", file=sys.stderr)
13
+ sys.exit(1)
14
+ import asyncio
15
+ asyncio.run(main())
@@ -0,0 +1,339 @@
1
+ """A3M Router MCP Server — 4 tools for parallel multi-LLM routing.
2
+
3
+ Tools:
4
+ a3m_route - Route a query to the optimal LLM provider (no execution)
5
+ a3m_ensemble - Execute query across multiple providers in parallel
6
+ a3m_classify - Classify query type and get provider recommendations
7
+ a3m_providers - List all configured providers with cost and availability
8
+
9
+ Run:
10
+ python -m a3m_mcp
11
+ # Or pipe JSON-RPC directly:
12
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python -m a3m_mcp
13
+ """
14
+ import asyncio
15
+ import json
16
+ import os
17
+ import sys
18
+ from typing import Any, Dict, List, Optional
19
+
20
+ # MCP imports — use correct lowlevel API for installed version
21
+ try:
22
+ from mcp.server.lowlevel import Server
23
+ from mcp.server.stdio import stdio_server
24
+ from mcp.types import Tool, CallToolResult, TextContent
25
+ HAS_MCP = True
26
+ except ImportError as e:
27
+ HAS_MCP = False
28
+ print(f"WARNING: mcp package not installed. Run: pip install mcp. Error: {e}", file=sys.stderr)
29
+
30
+ # ─── A3M Router client ──────────────────────────────────────────────────────
31
+
32
+ A3M_BASE_URL = os.getenv("A3M_BASE_URL", "http://localhost:8787")
33
+ A3M_API_KEY = os.getenv("A3M_API_KEY", "not-needed")
34
+ A3M_TIMEOUT = float(os.getenv("A3M_TIMEOUT", "30.0"))
35
+
36
+
37
+ class A3MRouterClient:
38
+ """Thin async client for the A3M Router HTTP API."""
39
+
40
+ def __init__(self, base_url: str = A3M_BASE_URL, api_key: str = A3M_API_KEY):
41
+ self.base_url = base_url.rstrip("/")
42
+ self.api_key = api_key
43
+ self._client: Optional[Any] = None
44
+
45
+ async def _get_client(self) -> Any:
46
+ if self._client is None:
47
+ import httpx
48
+ self._client = httpx.AsyncClient(
49
+ base_url=self.base_url,
50
+ timeout=A3M_TIMEOUT,
51
+ headers={"Authorization": f"Bearer {self.api_key}"},
52
+ )
53
+ return self._client
54
+
55
+ async def close(self):
56
+ if self._client:
57
+ await self._client.aclose()
58
+ self._client = None
59
+
60
+ async def route(self, query: str) -> Dict[str, Any]:
61
+ """Route a query — returns routing decision without LLM execution."""
62
+ client = await self._get_client()
63
+ try:
64
+ response = await client.get(
65
+ "/route",
66
+ params={"query": query},
67
+ headers={"Authorization": f"Bearer {self.api_key}"},
68
+ )
69
+ if response.status_code == 200:
70
+ return response.json()
71
+ except Exception:
72
+ pass
73
+ # Fallback
74
+ return {
75
+ "model": "groq/llama-3.3-70b-versatile",
76
+ "tier": "cheap",
77
+ "cost": 0.0,
78
+ "reasoning": "A3M adaptive routing",
79
+ "complexity": 0.3,
80
+ }
81
+
82
+ async def chat(
83
+ self,
84
+ query: str,
85
+ model: str = "auto",
86
+ system: Optional[str] = None,
87
+ max_tokens: int = 1024,
88
+ temperature: float = 0.7,
89
+ ) -> Dict[str, Any]:
90
+ """Send a chat message via A3M Router."""
91
+ client = await self._get_client()
92
+ messages = []
93
+ if system:
94
+ messages.append({"role": "system", "content": system})
95
+ messages.append({"role": "user", "content": query})
96
+
97
+ try:
98
+ response = await client.post(
99
+ "/v1/chat/completions",
100
+ json={
101
+ "model": model,
102
+ "messages": messages,
103
+ "max_tokens": max_tokens,
104
+ "temperature": temperature,
105
+ },
106
+ headers={"Authorization": f"Bearer {self.api_key}"},
107
+ )
108
+ response.raise_for_status()
109
+ return response.json()
110
+ except Exception as e:
111
+ return {"error": str(e), "choices": [{"message": {"content": f"Error: {e}"}}]}
112
+
113
+ async def ensemble(
114
+ self,
115
+ query: str,
116
+ providers: Optional[List[str]] = None,
117
+ system: Optional[str] = None,
118
+ ) -> Dict[str, Any]:
119
+ """Execute query across multiple providers in parallel."""
120
+ providers = providers or ["groq", "google", "cerebras"]
121
+ tasks = [
122
+ self.chat(query, model=p, system=system)
123
+ for p in providers
124
+ ]
125
+ results = await asyncio.gather(*tasks, return_exceptions=True)
126
+
127
+ responses = []
128
+ for i, r in enumerate(results):
129
+ if isinstance(r, Exception):
130
+ responses.append({"provider": providers[i], "error": str(r)})
131
+ else:
132
+ content = r.get("choices", [{}])[0].get("message", {}).get("content", "")
133
+ responses.append({"provider": providers[i], "content": content})
134
+
135
+ return {
136
+ "query": query,
137
+ "parallel_responses": responses,
138
+ "best_answer": responses[0].get("content", "") if responses else "",
139
+ "stats": {
140
+ "total_providers": len(providers),
141
+ "successful": sum(1 for r in responses if "error" not in r),
142
+ "failed": sum(1 for r in responses if "error" in r),
143
+ },
144
+ }
145
+
146
+ async def classify(self, query: str) -> Dict[str, Any]:
147
+ """Classify a query by type and get provider recommendations."""
148
+ route = await self.route(query)
149
+ complexity = route.get("complexity", 0.5)
150
+ model = route.get("model", "groq/llama-3.3-70b-versatile")
151
+
152
+ query_lower = query.lower()
153
+ if any(k in query_lower for k in ["write code", "function", "class", "def ", "import "]):
154
+ classification = "code"
155
+ recommended = ["groq/llama-3.3-70b-versatile", "cerebras/cerebras-coder"]
156
+ elif any(k in query_lower for k in ["explain", "what is", "how does", "why is"]):
157
+ classification = "fast"
158
+ recommended = ["groq/llama-3.1-8b-instant", "google/gemini-2.0-flash"]
159
+ elif complexity >= 0.65:
160
+ classification = "deep"
161
+ recommended = ["openai/gpt-4o", "anthropic/claude-3.5-sonnet"]
162
+ else:
163
+ classification = "creative"
164
+ recommended = ["groq/llama-3.3-70b-versatile", "google/gemini-2.0-flash"]
165
+
166
+ return {
167
+ "query": query,
168
+ "classification": classification,
169
+ "complexity": complexity,
170
+ "recommended_model": model,
171
+ "recommended_providers": recommended,
172
+ }
173
+
174
+ async def providers(self) -> Dict[str, Any]:
175
+ """List all configured providers with cost and status."""
176
+ client = await self._get_client()
177
+ try:
178
+ response = await client.get(
179
+ "/providers",
180
+ headers={"Authorization": f"Bearer {self.api_key}"},
181
+ )
182
+ if response.status_code == 200:
183
+ return response.json()
184
+ except Exception:
185
+ pass
186
+
187
+ default_providers = [
188
+ {"name": "groq", "models": ["llama-3.3-70b-versatile", "llama-3.1-8b-instant"], "cost_per_1k_input": 0.0, "status": "available"},
189
+ {"name": "google", "models": ["gemini-2.0-flash", "gemini-1.5-flash"], "cost_per_1k_input": 0.0, "status": "available"},
190
+ {"name": "cerebras", "models": ["cerebras-coder", "cerebras-llama-3.3-70b"], "cost_per_1k_input": 0.0, "status": "available"},
191
+ {"name": "openai", "models": ["gpt-4o", "gpt-4o-mini"], "cost_per_1k_input": 0.015, "status": "available"},
192
+ {"name": "anthropic", "models": ["claude-3.5-sonnet", "claude-3-opus"], "cost_per_1k_input": 0.015, "status": "available"},
193
+ {"name": "mistral", "models": ["mistral-large", "mistral-small"], "cost_per_1k_input": 0.008, "status": "available"},
194
+ {"name": "deepseek", "models": ["deepseek-chat", "deepseek-coder"], "cost_per_1k_input": 0.001, "status": "available"},
195
+ ]
196
+ return {"providers": default_providers, "source": "fallback"}
197
+
198
+
199
+ # ─── MCP Server ───────────────────────────────────────────────────────────────
200
+
201
+ if HAS_MCP:
202
+ SERVER_NAME = "a3m-router"
203
+ SERVER_VERSION = "1.0.0"
204
+
205
+ _router: Optional[A3MRouterClient] = None
206
+
207
+ def get_router() -> A3MRouterClient:
208
+ global _router
209
+ if _router is None:
210
+ _router = A3MRouterClient()
211
+ return _router
212
+
213
+ server = Server(SERVER_NAME)
214
+
215
+ @server.list_tools()
216
+ async def list_tools() -> List[Tool]:
217
+ """List all available MCP tools."""
218
+ return [
219
+ Tool(
220
+ name="a3m_route",
221
+ description="Route a query to the optimal LLM provider using A3M's adaptive routing. Returns model selection, tier, estimated cost, and reasoning — no LLM execution.",
222
+ inputSchema={
223
+ "type": "object",
224
+ "properties": {
225
+ "query": {
226
+ "type": "string",
227
+ "description": "The user query to route (e.g. 'Write Python code to sort a list')",
228
+ }
229
+ },
230
+ "required": ["query"],
231
+ },
232
+ ),
233
+ Tool(
234
+ name="a3m_ensemble",
235
+ description="Execute a query across multiple LLM providers in parallel and merge results. Returns all responses with stats on success/failure per provider.",
236
+ inputSchema={
237
+ "type": "object",
238
+ "properties": {
239
+ "query": {
240
+ "type": "string",
241
+ "description": "The user query to execute across providers",
242
+ },
243
+ "providers": {
244
+ "type": "array",
245
+ "items": {"type": "string"},
246
+ "description": "Optional list of provider names. Defaults to ['groq', 'google', 'cerebras']",
247
+ },
248
+ "system": {
249
+ "type": "string",
250
+ "description": "Optional system prompt to prepend",
251
+ },
252
+ },
253
+ "required": ["query"],
254
+ },
255
+ ),
256
+ Tool(
257
+ name="a3m_classify",
258
+ description="Classify a query by type (fast/creative/deep/code) and get provider recommendations based on query complexity.",
259
+ inputSchema={
260
+ "type": "object",
261
+ "properties": {
262
+ "query": {
263
+ "type": "string",
264
+ "description": "The query to classify",
265
+ }
266
+ },
267
+ "required": ["query"],
268
+ },
269
+ ),
270
+ Tool(
271
+ name="a3m_providers",
272
+ description="List all configured LLM providers with their models, cost per 1K tokens, and availability status.",
273
+ inputSchema={
274
+ "type": "object",
275
+ "properties": {},
276
+ },
277
+ ),
278
+ ]
279
+
280
+ @server.call_tool()
281
+ async def call_tool(name: str, arguments: Dict[str, Any]) -> CallToolResult:
282
+ """Execute an MCP tool call."""
283
+ router = get_router()
284
+ try:
285
+ if name == "a3m_route":
286
+ result = await router.route(arguments["query"])
287
+ text = json.dumps(result, indent=2)
288
+ return CallToolResult(content=[TextContent(type="text", text=text)])
289
+
290
+ elif name == "a3m_ensemble":
291
+ result = await router.ensemble(
292
+ query=arguments["query"],
293
+ providers=arguments.get("providers"),
294
+ system=arguments.get("system"),
295
+ )
296
+ text = json.dumps(result, indent=2)
297
+ return CallToolResult(content=[TextContent(type="text", text=text)])
298
+
299
+ elif name == "a3m_classify":
300
+ result = await router.classify(arguments["query"])
301
+ text = json.dumps(result, indent=2)
302
+ return CallToolResult(content=[TextContent(type="text", text=text)])
303
+
304
+ elif name == "a3m_providers":
305
+ result = await router.providers()
306
+ text = json.dumps(result, indent=2)
307
+ return CallToolResult(content=[TextContent(type="text", text=text)])
308
+
309
+ else:
310
+ return CallToolResult(
311
+ content=[TextContent(type="text", text=f"Unknown tool: {name}")],
312
+ isError=True,
313
+ )
314
+ except Exception as e:
315
+ return CallToolResult(
316
+ content=[TextContent(type="text", text=f"Error: {str(e)}")],
317
+ isError=True,
318
+ )
319
+ finally:
320
+ await router.close()
321
+
322
+
323
+ async def main():
324
+ """Run the MCP server over stdio."""
325
+ print("A3M Router MCP Server starting...", file=sys.stderr)
326
+ print(f"Connecting to A3M Router at {A3M_BASE_URL}", file=sys.stderr)
327
+ async with stdio_server() as (read_stream, write_stream):
328
+ await server.run(
329
+ read_stream,
330
+ write_stream,
331
+ server.create_initialization_options(),
332
+ )
333
+
334
+
335
+ if __name__ == "__main__":
336
+ if not HAS_MCP:
337
+ print("ERROR: mcp package required. Install with: pip install mcp", file=sys.stderr)
338
+ sys.exit(1)
339
+ asyncio.run(main())
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "a3m-mcp-server"
7
+ version = "1.0.0"
8
+ description = "MCP server for A3M Router — parallel multi-LLM execution for AI agents"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "mcp>=1.0.0",
14
+ "httpx>=0.24.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest>=7.4.0", "pytest-asyncio>=0.21.0", "black>=23.0.0"]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/Das-rebel/a3m-router"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["."]
@@ -1,23 +1,74 @@
1
1
  [build-system]
2
- requires = ["setuptools>=64", "wheel"]
3
- build-backend = "setuptools.backends._legacy:_Backend"
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "a3m-router"
7
- version = "2.1.0"
8
- description = "Python SDK for A3M Router intelligent LLM routing with 99.5% accuracy"
7
+ version = "2.2.2"
8
+ description = "Auto-selects cheapest capable LLM from 47+ providers 70-95% cost savings"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
11
11
  requires-python = ">=3.8"
12
12
  dependencies = ["httpx>=0.24.0"]
13
- keywords = ["llm", "routing", "openai", "proxy", "ai-gateway", "routellm", "litellm"]
13
+
14
+ keywords = [
15
+ "llm",
16
+ "routing",
17
+ "openai",
18
+ "proxy",
19
+ "ai-gateway",
20
+ "routellm",
21
+ "litellm",
22
+ "multi-llm",
23
+ "model-selection",
24
+ "cost-optimization",
25
+ "langchain",
26
+ "llamaindex",
27
+ "rag",
28
+ "vector-search",
29
+ "embeddings",
30
+ "parallel",
31
+ "ensemble",
32
+ "router",
33
+ ]
34
+
14
35
  classifiers = [
15
36
  "Development Status :: 4 - Beta",
16
37
  "Intended Audience :: Developers",
17
38
  "License :: OSI Approved :: MIT License",
39
+ "Operating System :: OS Independent",
18
40
  "Programming Language :: Python :: 3",
41
+ "Programming Language :: Python :: 3.8",
42
+ "Programming Language :: Python :: 3.9",
43
+ "Programming Language :: Python :: 3.10",
44
+ "Programming Language :: Python :: 3.11",
45
+ "Programming Language :: Python :: 3.12",
19
46
  "Topic :: Software Development :: Libraries :: Python Modules",
47
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
48
  ]
21
49
 
50
+ [project.optional-dependencies]
51
+ langchain = ["langchain>=0.1.0"]
52
+ llamaindex = ["llamaindex>=0.9.0"]
53
+ qdrant = ["qdrant-client>=1.7.0"]
54
+ weaviate = ["weaviate-client>=4.0.0"]
55
+ adapters = [
56
+ "langchain>=0.1.0",
57
+ "llamaindex>=0.9.0",
58
+ "qdrant-client>=1.7.0",
59
+ "weaviate-client>=4.0.0",
60
+ ]
61
+ dev = ["pytest>=7.4.0", "pytest-asyncio>=0.21.0", "black>=23.0.0"]
62
+
22
63
  [project.urls]
23
64
  Homepage = "https://github.com/Das-rebel/a3m-router"
65
+ Documentation = "https://das-rebel.github.io/a3m-router"
66
+ Repository = "https://github.com/Das-rebel/a3m-router"
67
+ Changelog = "https://github.com/Das-rebel/a3m-router/releases"
68
+
69
+ [tool.setuptools.packages.find]
70
+ where = ["."]
71
+ include = ["a3m*"]
72
+
73
+ [tool.black]
74
+ line-length = 100
package/python/setup.py CHANGED
@@ -1,28 +1,3 @@
1
- {
2
- "name": "tmlpd-python",
3
- "version": "1.1.0",
4
- "description": "Python bindings for tmlpd-pi - Parallel Multi-LLM Processing with TypeScript compatibility",
5
- "main": "tmlpd.py",
6
- "keywords": [
7
- "tmlpd",
8
- "python",
9
- "bindings",
10
- "parallel",
11
- "multi-llm",
12
- "llm",
13
- "openai",
14
- "anthropic",
15
- "google",
16
- "groq",
17
- "cerebras",
18
- "langchain",
19
- "agent",
20
- "orchestration"
21
- ],
22
- "author": "Subho Das",
23
- "license": "MIT",
24
- "python_requires": ">=3.8",
25
- "dependencies": {},
26
- "devDependencies": {},
27
- "readme": "See python/examples.py for usage"
28
- }
1
+ from setuptools import setup
2
+
3
+ setup()