adaptive-memory-multi-model-router 1.2.2 → 1.3.1

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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -66
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/integrations/airtable.js +20 -0
  6. package/dist/integrations/discord.js +18 -0
  7. package/dist/integrations/github.js +23 -0
  8. package/dist/integrations/gmail.js +19 -0
  9. package/dist/integrations/google-calendar.js +18 -0
  10. package/dist/integrations/index.js +61 -0
  11. package/dist/integrations/jira.js +21 -0
  12. package/dist/integrations/linear.js +19 -0
  13. package/dist/integrations/notion.js +19 -0
  14. package/dist/integrations/slack.js +18 -0
  15. package/dist/integrations/telegram.js +19 -0
  16. package/dist/providers/registry.js +7 -3
  17. package/docs/ARCHITECTURAL-IMPROVEMENTS-2025.md +1391 -0
  18. package/docs/ARCHITECTURAL-IMPROVEMENTS-REVISED-2025.md +1051 -0
  19. package/docs/CONFIGURATION.md +476 -0
  20. package/docs/COUNCIL_DECISION.json +308 -0
  21. package/docs/COUNCIL_SUMMARY.md +265 -0
  22. package/docs/COUNCIL_V2.2_DECISION.md +416 -0
  23. package/docs/IMPROVEMENT_ROADMAP.md +515 -0
  24. package/docs/LLM_COUNCIL_DECISION.md +508 -0
  25. package/docs/QUICK_START_VISIBILITY.md +782 -0
  26. package/docs/REDDIT_GAP_ANALYSIS.md +299 -0
  27. package/docs/RESEARCH_BACKED_IMPROVEMENTS.md +1180 -0
  28. package/docs/TMLPD_QNA.md +751 -0
  29. package/docs/TMLPD_V2.1_COMPLETE.md +763 -0
  30. package/docs/TMLPD_V2.2_RESEARCH_ROADMAP.md +754 -0
  31. package/docs/V2.2_IMPLEMENTATION_COMPLETE.md +446 -0
  32. package/docs/V2_IMPLEMENTATION_GUIDE.md +388 -0
  33. package/docs/VISIBILITY_ADOPTION_PLAN.md +1005 -0
  34. package/docs/launch-content/LAUNCH_EXECUTION_CHECKLIST.md +421 -0
  35. package/docs/launch-content/README.md +457 -0
  36. package/docs/launch-content/assets/cost_comparison_100_tasks.png +0 -0
  37. package/docs/launch-content/assets/cumulative_savings.png +0 -0
  38. package/docs/launch-content/assets/parallel_speedup.png +0 -0
  39. package/docs/launch-content/assets/provider_pricing_comparison.png +0 -0
  40. package/docs/launch-content/assets/task_breakdown_comparison.png +0 -0
  41. package/docs/launch-content/generate_charts.py +313 -0
  42. package/docs/launch-content/hn_show_post.md +139 -0
  43. package/docs/launch-content/partner_outreach_templates.md +745 -0
  44. package/docs/launch-content/reddit_posts.md +467 -0
  45. package/docs/launch-content/twitter_thread.txt +460 -0
  46. package/examples/QUICKSTART.md +1 -1
  47. package/openclaw-alexa-bridge/ALL_REMAINING_FIXES_PLAN.md +313 -0
  48. package/openclaw-alexa-bridge/REMAINING_FIXES_SUMMARY.md +277 -0
  49. package/openclaw-alexa-bridge/src/alexa_handler_no_tmlpd.js +1234 -0
  50. package/openclaw-alexa-bridge/test_fixes.js +77 -0
  51. package/package.json +120 -29
  52. package/package.json.tmp +0 -0
  53. package/qna/TMLPD_QNA.md +3 -3
  54. package/skill/SKILL.md +2 -2
  55. package/src/__tests__/integration/tmpld_integration.test.py +540 -0
  56. package/src/agents/skill_enhanced_agent.py +318 -0
  57. package/src/memory/__init__.py +15 -0
  58. package/src/memory/agentic_memory.py +353 -0
  59. package/src/memory/semantic_memory.py +444 -0
  60. package/src/memory/simple_memory.py +466 -0
  61. package/src/memory/working_memory.py +447 -0
  62. package/src/orchestration/__init__.py +52 -0
  63. package/src/orchestration/execution_engine.py +353 -0
  64. package/src/orchestration/halo_orchestrator.py +367 -0
  65. package/src/orchestration/mcts_workflow.py +498 -0
  66. package/src/orchestration/role_assigner.py +473 -0
  67. package/src/orchestration/task_planner.py +522 -0
  68. package/src/providers/__init__.py +67 -0
  69. package/src/providers/anthropic.py +304 -0
  70. package/src/providers/base.py +241 -0
  71. package/src/providers/cerebras.py +373 -0
  72. package/src/providers/registry.py +476 -0
  73. package/src/routing/__init__.py +30 -0
  74. package/src/routing/universal_router.py +621 -0
  75. package/src/skills/TMLPD-QUICKREF.md +210 -0
  76. package/src/skills/TMLPD-SETUP-SUMMARY.md +157 -0
  77. package/src/skills/TMLPD.md +540 -0
  78. package/src/skills/__tests__/skill_manager.test.ts +328 -0
  79. package/src/skills/skill_manager.py +385 -0
  80. package/src/skills/test-tmlpd.sh +108 -0
  81. package/src/skills/tmlpd-category.yaml +67 -0
  82. package/src/skills/tmlpd-monitoring.yaml +188 -0
  83. package/src/skills/tmlpd-phase.yaml +132 -0
  84. package/src/state/__init__.py +17 -0
  85. package/src/state/simple_checkpoint.py +508 -0
  86. package/src/tmlpd_agent.py +464 -0
  87. package/src/tmpld_v2.py +427 -0
  88. package/src/workflows/__init__.py +18 -0
  89. package/src/workflows/advanced_difficulty_classifier.py +377 -0
  90. package/src/workflows/chaining_executor.py +417 -0
  91. package/src/workflows/difficulty_integration.py +209 -0
  92. package/src/workflows/orchestrator.py +469 -0
  93. package/src/workflows/orchestrator_executor.py +456 -0
  94. package/src/workflows/parallelization_executor.py +382 -0
  95. package/src/workflows/router.py +311 -0
  96. package/test_integration_simple.py +86 -0
  97. package/test_mcts_workflow.py +150 -0
  98. package/test_templd_integration.py +262 -0
  99. package/test_universal_router.py +275 -0
  100. package/tmlpd-pi-extension/README.md +36 -0
  101. package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts +114 -0
  102. package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts.map +1 -0
  103. package/tmlpd-pi-extension/dist/cache/prefixCache.js +285 -0
  104. package/tmlpd-pi-extension/dist/cache/prefixCache.js.map +1 -0
  105. package/tmlpd-pi-extension/dist/cache/responseCache.d.ts +58 -0
  106. package/tmlpd-pi-extension/dist/cache/responseCache.d.ts.map +1 -0
  107. package/tmlpd-pi-extension/dist/cache/responseCache.js +153 -0
  108. package/tmlpd-pi-extension/dist/cache/responseCache.js.map +1 -0
  109. package/tmlpd-pi-extension/dist/cli.js +59 -0
  110. package/tmlpd-pi-extension/dist/cost/costTracker.d.ts +95 -0
  111. package/tmlpd-pi-extension/dist/cost/costTracker.d.ts.map +1 -0
  112. package/tmlpd-pi-extension/dist/cost/costTracker.js +240 -0
  113. package/tmlpd-pi-extension/dist/cost/costTracker.js.map +1 -0
  114. package/tmlpd-pi-extension/dist/index.d.ts +723 -0
  115. package/tmlpd-pi-extension/dist/index.d.ts.map +1 -0
  116. package/tmlpd-pi-extension/dist/index.js +239 -0
  117. package/tmlpd-pi-extension/dist/index.js.map +1 -0
  118. package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts +82 -0
  119. package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts.map +1 -0
  120. package/tmlpd-pi-extension/dist/memory/episodicMemory.js +145 -0
  121. package/tmlpd-pi-extension/dist/memory/episodicMemory.js.map +1 -0
  122. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts +102 -0
  123. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts.map +1 -0
  124. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js +207 -0
  125. package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js.map +1 -0
  126. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts +85 -0
  127. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts.map +1 -0
  128. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js +210 -0
  129. package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js.map +1 -0
  130. package/tmlpd-pi-extension/dist/providers/localProvider.d.ts +102 -0
  131. package/tmlpd-pi-extension/dist/providers/localProvider.d.ts.map +1 -0
  132. package/tmlpd-pi-extension/dist/providers/localProvider.js +338 -0
  133. package/tmlpd-pi-extension/dist/providers/localProvider.js.map +1 -0
  134. package/tmlpd-pi-extension/dist/providers/registry.d.ts +55 -0
  135. package/tmlpd-pi-extension/dist/providers/registry.d.ts.map +1 -0
  136. package/tmlpd-pi-extension/dist/providers/registry.js +138 -0
  137. package/tmlpd-pi-extension/dist/providers/registry.js.map +1 -0
  138. package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts +68 -0
  139. package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts.map +1 -0
  140. package/tmlpd-pi-extension/dist/routing/advancedRouter.js +332 -0
  141. package/tmlpd-pi-extension/dist/routing/advancedRouter.js.map +1 -0
  142. package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts +101 -0
  143. package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts.map +1 -0
  144. package/tmlpd-pi-extension/dist/tools/tmlpdTools.js +368 -0
  145. package/tmlpd-pi-extension/dist/tools/tmlpdTools.js.map +1 -0
  146. package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts +96 -0
  147. package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts.map +1 -0
  148. package/tmlpd-pi-extension/dist/utils/batchProcessor.js +170 -0
  149. package/tmlpd-pi-extension/dist/utils/batchProcessor.js.map +1 -0
  150. package/tmlpd-pi-extension/dist/utils/compression.d.ts +61 -0
  151. package/tmlpd-pi-extension/dist/utils/compression.d.ts.map +1 -0
  152. package/tmlpd-pi-extension/dist/utils/compression.js +281 -0
  153. package/tmlpd-pi-extension/dist/utils/compression.js.map +1 -0
  154. package/tmlpd-pi-extension/dist/utils/reliability.d.ts +74 -0
  155. package/tmlpd-pi-extension/dist/utils/reliability.d.ts.map +1 -0
  156. package/tmlpd-pi-extension/dist/utils/reliability.js +177 -0
  157. package/tmlpd-pi-extension/dist/utils/reliability.js.map +1 -0
  158. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts +117 -0
  159. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts.map +1 -0
  160. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js +246 -0
  161. package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js.map +1 -0
  162. package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts +50 -0
  163. package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts.map +1 -0
  164. package/tmlpd-pi-extension/dist/utils/tokenUtils.js +124 -0
  165. package/tmlpd-pi-extension/dist/utils/tokenUtils.js.map +1 -0
  166. package/tmlpd-pi-extension/examples/QUICKSTART.md +183 -0
  167. package/tmlpd-pi-extension/package-lock.json +75 -0
  168. package/tmlpd-pi-extension/package.json +172 -0
  169. package/tmlpd-pi-extension/python/examples.py +53 -0
  170. package/tmlpd-pi-extension/python/integrations.py +330 -0
  171. package/tmlpd-pi-extension/python/setup.py +28 -0
  172. package/tmlpd-pi-extension/python/tmlpd.py +369 -0
  173. package/tmlpd-pi-extension/qna/REDDIT_GAP_ANALYSIS.md +299 -0
  174. package/tmlpd-pi-extension/qna/TMLPD_QNA.md +751 -0
  175. package/tmlpd-pi-extension/skill/SKILL.md +238 -0
  176. package/{src → tmlpd-pi-extension/src}/index.ts +1 -1
  177. package/tmlpd-pi-extension/tsconfig.json +18 -0
  178. package/demo/research-demo.js +0 -266
  179. package/notebooks/quickstart.ipynb +0 -157
  180. package/rust/tmlpd.h +0 -268
  181. package/src/cache/prefixCache.ts +0 -365
  182. package/src/routing/advancedRouter.ts +0 -406
  183. package/src/utils/speculativeDecoding.ts +0 -344
  184. /package/{src → tmlpd-pi-extension/src}/cache/responseCache.ts +0 -0
  185. /package/{src → tmlpd-pi-extension/src}/cost/costTracker.ts +0 -0
  186. /package/{src → tmlpd-pi-extension/src}/memory/episodicMemory.ts +0 -0
  187. /package/{src → tmlpd-pi-extension/src}/orchestration/haloOrchestrator.ts +0 -0
  188. /package/{src → tmlpd-pi-extension/src}/orchestration/mctsWorkflow.ts +0 -0
  189. /package/{src → tmlpd-pi-extension/src}/providers/localProvider.ts +0 -0
  190. /package/{src → tmlpd-pi-extension/src}/providers/registry.ts +0 -0
  191. /package/{src → tmlpd-pi-extension/src}/tools/tmlpdTools.ts +0 -0
  192. /package/{src → tmlpd-pi-extension/src}/utils/batchProcessor.ts +0 -0
  193. /package/{src → tmlpd-pi-extension/src}/utils/compression.ts +0 -0
  194. /package/{src → tmlpd-pi-extension/src}/utils/reliability.ts +0 -0
  195. /package/{src → tmlpd-pi-extension/src}/utils/tokenUtils.ts +0 -0
@@ -0,0 +1,476 @@
1
+ """
2
+ Provider Registry and Routing - Agent 4 Output
3
+
4
+ Implements provider registry, health monitoring, and intelligent routing.
5
+ """
6
+
7
+ import asyncio
8
+ from typing import Dict, List, Optional, Any
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+ import json
12
+
13
+ from .base import BaseProvider, ProviderConfig, ProviderHealth, ProviderResponse
14
+ from .anthropic import AnthropicProvider, OpenAIProvider
15
+ from .cerebras import CerebrasProvider, GroqProvider, TogetherProvider
16
+
17
+
18
+ class ProviderRegistry:
19
+ """
20
+ Central registry for all LLM providers.
21
+
22
+ Features:
23
+ - Provider registration and management
24
+ - Health monitoring with automatic failover
25
+ - Cost-aware provider selection
26
+ - Performance metrics tracking
27
+ """
28
+
29
+ def __init__(self, config_path: Optional[str] = None):
30
+ """
31
+ Initialize provider registry.
32
+
33
+ Args:
34
+ config_path: Path to provider configuration file (YAML or JSON)
35
+ """
36
+ self.providers: Dict[str, BaseProvider] = {}
37
+ self.config = self._load_config(config_path)
38
+ self.health_check_interval = 60 # seconds
39
+ self._health_task = None
40
+
41
+ # Register providers from config
42
+ self._register_providers()
43
+
44
+ def _load_config(self, config_path: Optional[str]) -> Dict[str, Any]:
45
+ """Load provider configuration"""
46
+ if config_path and Path(config_path).exists():
47
+ # Load from file
48
+ with open(config_path, 'r') as f:
49
+ if config_path.endswith('.json'):
50
+ return json.load(f)
51
+ else:
52
+ # Assume YAML (requires pyyaml)
53
+ import yaml
54
+ return yaml.safe_load(f)
55
+
56
+ # Default configuration
57
+ return {
58
+ "providers": [
59
+ {
60
+ "name": "anthropic",
61
+ "model": "claude-sonnet-4",
62
+ "api_key_env": "ANTHROPIC_API_KEY",
63
+ "priority": 1,
64
+ "enabled": True
65
+ },
66
+ {
67
+ "name": "openai",
68
+ "model": "gpt-4o",
69
+ "api_key_env": "OPENAI_API_KEY",
70
+ "priority": 2,
71
+ "enabled": True
72
+ },
73
+ {
74
+ "name": "cerebras",
75
+ "model": "llama-3.3-70b",
76
+ "api_key_env": "CEREBRAS_API_KEY",
77
+ "priority": 3,
78
+ "enabled": True
79
+ },
80
+ {
81
+ "name": "groq",
82
+ "model": "llama-3.3-70b-8192",
83
+ "api_key_env": "GROQ_API_KEY",
84
+ "priority": 4,
85
+ "enabled": True
86
+ }
87
+ ]
88
+ }
89
+
90
+ def _register_providers(self):
91
+ """Register providers from configuration"""
92
+ provider_classes = {
93
+ "anthropic": AnthropicProvider,
94
+ "openai": OpenAIProvider,
95
+ "cerebras": CerebrasProvider,
96
+ "groq": GroqProvider,
97
+ "together": TogetherProvider,
98
+ }
99
+
100
+ for provider_config in self.config.get("providers", []):
101
+ if not provider_config.get("enabled", True):
102
+ continue
103
+
104
+ provider_class = provider_classes.get(provider_config["name"])
105
+ if not provider_class:
106
+ continue
107
+
108
+ config = ProviderConfig(
109
+ name=provider_config["name"],
110
+ model=provider_config["model"],
111
+ api_key_env=provider_config["api_key_env"],
112
+ priority=provider_config.get("priority", 1),
113
+ max_retries=provider_config.get("max_retries", 3),
114
+ timeout=provider_config.get("timeout", 30),
115
+ enabled=provider_config.get("enabled", True)
116
+ )
117
+
118
+ try:
119
+ provider = provider_class(config)
120
+ self.providers[provider_config["name"]] = provider
121
+ print(f"✓ Registered provider: {provider_config['name']}")
122
+ except Exception as e:
123
+ print(f"✗ Failed to register {provider_config['name']}: {e}")
124
+
125
+ def get_provider(self, name: str) -> Optional[BaseProvider]:
126
+ """Get provider by name"""
127
+ return self.providers.get(name)
128
+
129
+ def get_healthy_providers(self) -> List[BaseProvider]:
130
+ """Get all healthy providers, sorted by priority"""
131
+ healthy = []
132
+
133
+ for provider in self.providers.values():
134
+ health = provider.get_health()
135
+
136
+ if health.status == "healthy":
137
+ healthy.append(provider)
138
+
139
+ # Sort by priority (lower number = higher priority)
140
+ healthy.sort(key=lambda p: p.config.priority)
141
+
142
+ return healthy
143
+
144
+ def get_all_providers(self) -> List[BaseProvider]:
145
+ """Get all providers, sorted by priority"""
146
+ all_providers = list(self.providers.values())
147
+ all_providers.sort(key=lambda p: p.config.priority)
148
+ return all_providers
149
+
150
+ async def start_health_monitoring(self):
151
+ """Start background health monitoring"""
152
+ if self._health_task is None:
153
+ self._health_task = asyncio.create_task(self._health_monitor_loop())
154
+
155
+ async def stop_health_monitoring(self):
156
+ """Stop background health monitoring"""
157
+ if self._health_task:
158
+ self._health_task.cancel()
159
+ try:
160
+ await self._health_task
161
+ except asyncio.CancelledError:
162
+ pass
163
+ self._health_task = None
164
+
165
+ async def _health_monitor_loop(self):
166
+ """Background health check loop"""
167
+ while True:
168
+ try:
169
+ await self.check_all_providers()
170
+ await asyncio.sleep(self.health_check_interval)
171
+ except asyncio.CancelledError:
172
+ break
173
+ except Exception as e:
174
+ print(f"Health check error: {e}")
175
+ await asyncio.sleep(self.health_check_interval)
176
+
177
+ async def check_all_providers(self) -> Dict[str, ProviderHealth]:
178
+ """Check health of all providers"""
179
+ health_status = {}
180
+
181
+ for name, provider in self.providers.items():
182
+ try:
183
+ health = await provider.health_check()
184
+ health_status[name] = health
185
+ except Exception as e:
186
+ health_status[name] = ProviderHealth(
187
+ status="unhealthy",
188
+ last_check=datetime.now().isoformat(),
189
+ consecutive_failures=0,
190
+ last_error=str(e)
191
+ )
192
+
193
+ return health_status
194
+
195
+ def get_registry_stats(self) -> Dict[str, Any]:
196
+ """Get registry statistics"""
197
+ total_providers = len(self.providers)
198
+ healthy_providers = len(self.get_healthy_providers())
199
+
200
+ total_stats = {
201
+ "total_providers": total_providers,
202
+ "healthy_providers": healthy_providers,
203
+ "unhealthy_providers": total_providers - healthy_providers,
204
+ "providers": {}
205
+ }
206
+
207
+ for name, provider in self.providers.items():
208
+ total_stats["providers"][name] = provider.get_stats()
209
+
210
+ return total_stats
211
+
212
+
213
+ class IntelligentRouter:
214
+ """
215
+ Intelligent routing based on task difficulty and provider capabilities.
216
+
217
+ Implements difficulty-aware routing from arXiv:2509.11079
218
+ """
219
+
220
+ # Difficulty levels
221
+ DIFFICULTY_LEVELS = {
222
+ "TRIVIAL": range(0, 20),
223
+ "SIMPLE": range(20, 40),
224
+ "MEDIUM": range(40, 60),
225
+ "COMPLEX": range(60, 80),
226
+ "EXPERT": range(80, 100)
227
+ }
228
+
229
+ # Provider preferences by difficulty
230
+ PROVIDER_PREFERENCES = {
231
+ "TRIVIAL": ["cerebras", "groq", "together"], # Fastest, cheapest
232
+ "SIMPLE": ["cerebras", "groq", "openai"], # Fast
233
+ "MEDIUM": ["openai", "anthropic"], # Balanced
234
+ "COMPLEX": ["anthropic", "openai"], # Quality
235
+ "EXPERT": ["anthropic"] # Best
236
+ }
237
+
238
+ def __init__(self, registry: ProviderRegistry):
239
+ """
240
+ Initialize intelligent router.
241
+
242
+ Args:
243
+ registry: ProviderRegistry instance
244
+ """
245
+ self.registry = registry
246
+
247
+ def classify_difficulty(self, task: Dict[str, Any]) -> str:
248
+ """
249
+ Classify task difficulty.
250
+
251
+ Based on arXiv:2509.11079 (Difficulty-Aware Agent Orchestration)
252
+
253
+ Factors:
254
+ - Task length (word count)
255
+ - Multi-step indicators
256
+ - Technical complexity
257
+ - Requirements specificity
258
+ - Dependencies
259
+ """
260
+ score = 0
261
+
262
+ # Factor 1: Length (0-20 points)
263
+ description = task.get("description", "")
264
+ word_count = len(description.split())
265
+ score += min(word_count / 10, 20)
266
+
267
+ # Factor 2: Multi-step (0-25 points)
268
+ multi_step_keywords = [
269
+ "then", "after", "before", "followed by",
270
+ "multiple", "several", "sequence", "chain",
271
+ "iterate", "refine", "improve"
272
+ ]
273
+ multi_step_count = sum(
274
+ 1 for kw in multi_step_keywords
275
+ if kw in description.lower()
276
+ )
277
+ score += min(multi_step_count * 5, 25)
278
+
279
+ # Factor 3: Technical complexity (0-30 points)
280
+ technical_keywords = [
281
+ "implement", "integrate", "optimize", "architecture",
282
+ "system", "api", "database", "authentication", "deployment",
283
+ "algorithm", "performance", "security", "scalability"
284
+ ]
285
+ tech_count = sum(
286
+ 1 for kw in technical_keywords
287
+ if kw in description.lower()
288
+ )
289
+ score += min(tech_count * 3, 30)
290
+
291
+ # Factor 4: Requirements (0-15 points)
292
+ if task.get("requirements"):
293
+ score += 10
294
+ if task.get("context"):
295
+ score += 5
296
+
297
+ # Factor 5: Dependencies (0-10 points)
298
+ dependency_keywords = ["depends", "requires", "needs", "after"]
299
+ if any(kw in description.lower() for kw in dependency_keywords):
300
+ score += 10
301
+
302
+ # Map to difficulty level
303
+ for level, range_obj in self.DIFFICULTY_LEVELS.items():
304
+ if score in range_obj:
305
+ return level
306
+
307
+ return "MEDIUM" # Default
308
+
309
+ def route(
310
+ self,
311
+ task: Dict[str, Any],
312
+ difficulty_override: Optional[str] = None
313
+ ) -> Optional[BaseProvider]:
314
+ """
315
+ Route task to appropriate provider based on difficulty.
316
+
317
+ Args:
318
+ task: Task to route
319
+ difficulty_override: Override automatic difficulty classification
320
+
321
+ Returns:
322
+ Selected provider or None
323
+ """
324
+ # Classify difficulty
325
+ difficulty = difficulty_override or self.classify_difficulty(task)
326
+
327
+ # Get preferred providers for this difficulty
328
+ preferred_providers = self.PROVIDER_PREFERENCES.get(difficulty, [])
329
+
330
+ # Find first healthy provider from preferences
331
+ for provider_name in preferred_providers:
332
+ provider = self.registry.get_provider(provider_name)
333
+ if provider:
334
+ health = provider.get_health()
335
+ if health.status == "healthy":
336
+ return provider
337
+
338
+ # Fallback: any healthy provider
339
+ healthy_providers = self.registry.get_healthy_providers()
340
+ if healthy_providers:
341
+ return healthy_providers[0]
342
+
343
+ return None
344
+
345
+ async def execute_with_routing(
346
+ self,
347
+ task: Dict[str, Any],
348
+ **kwargs
349
+ ) -> ProviderResponse:
350
+ """
351
+ Execute task with intelligent routing.
352
+
353
+ Routes to appropriate provider based on difficulty, then executes.
354
+ """
355
+ # Route to provider
356
+ provider = self.route(task)
357
+
358
+ if not provider:
359
+ return ProviderResponse(
360
+ success=False,
361
+ content="",
362
+ tokens_used=0,
363
+ cost=0.0,
364
+ latency_ms=0.0,
365
+ model="unknown",
366
+ provider="none",
367
+ timestamp=datetime.now().isoformat(),
368
+ metadata={},
369
+ error="No healthy providers available"
370
+ )
371
+
372
+ # Execute with provider
373
+ return await provider.execute_with_retry(
374
+ task.get("description", ""),
375
+ **kwargs
376
+ )
377
+
378
+ def explain_routing(self, task: Dict[str, Any]) -> Dict[str, Any]:
379
+ """
380
+ Explain routing decision for transparency.
381
+
382
+ Returns detailed information about why a task was routed
383
+ to a specific provider.
384
+ """
385
+ difficulty = self.classify_difficulty(task)
386
+ preferred_providers = self.PROVIDER_PREFERENCES.get(difficulty, [])
387
+
388
+ # Find selected provider
389
+ provider = self.route(task)
390
+
391
+ return {
392
+ "task": task.get("description", "")[:100],
393
+ "difficulty": difficulty,
394
+ "preferred_providers": preferred_providers,
395
+ "selected_provider": provider.config.name if provider else None,
396
+ "selected_model": provider.config.model if provider else None,
397
+ "reasoning": f"Task classified as '{difficulty}', routed to {provider.config.name if provider else 'none'}"
398
+ }
399
+
400
+
401
+ class MultiProviderExecutor:
402
+ """
403
+ High-level executor using multi-provider system.
404
+
405
+ Combines registry, health monitoring, and intelligent routing.
406
+ """
407
+
408
+ def __init__(self, config_path: Optional[str] = None):
409
+ """
410
+ Initialize multi-provider executor.
411
+
412
+ Args:
413
+ config_path: Path to provider configuration
414
+ """
415
+ self.registry = ProviderRegistry(config_path)
416
+ self.router = IntelligentRouter(self.registry)
417
+
418
+ async def start(self):
419
+ """Start health monitoring"""
420
+ await self.registry.start_health_monitoring()
421
+
422
+ async def stop(self):
423
+ """Stop health monitoring"""
424
+ await self.registry.stop_health_monitoring()
425
+
426
+ async def execute(
427
+ self,
428
+ task: Dict[str, Any],
429
+ provider_override: Optional[str] = None,
430
+ **kwargs
431
+ ) -> ProviderResponse:
432
+ """
433
+ Execute task with optimal provider.
434
+
435
+ Args:
436
+ task: Task to execute
437
+ provider_override: Force use of specific provider
438
+ **kwargs: Additional execution parameters
439
+
440
+ Returns:
441
+ ProviderResponse
442
+ """
443
+ # Use specific provider if requested
444
+ if provider_override:
445
+ provider = self.registry.get_provider(provider_override)
446
+ if not provider:
447
+ return ProviderResponse(
448
+ success=False,
449
+ content="",
450
+ tokens_used=0,
451
+ cost=0.0,
452
+ latency_ms=0.0,
453
+ model="unknown",
454
+ provider="none",
455
+ timestamp=datetime.now().isoformat(),
456
+ metadata={},
457
+ error=f"Provider not found: {provider_override}"
458
+ )
459
+
460
+ return await provider.execute_with_retry(
461
+ task.get("description", ""),
462
+ **kwargs
463
+ )
464
+
465
+ # Use intelligent routing
466
+ return await self.router.execute_with_routing(task, **kwargs)
467
+
468
+ def get_status(self) -> Dict[str, Any]:
469
+ """Get system status"""
470
+ return {
471
+ "registry": self.registry.get_registry_stats(),
472
+ "health": {
473
+ name: provider.get_health()
474
+ for name, provider in self.registry.providers.items()
475
+ }
476
+ }
@@ -0,0 +1,30 @@
1
+ """
2
+ TMLPD v2.2 Routing Module
3
+
4
+ Universal Learned Router with online adaptation and model profile learning.
5
+
6
+ Based on:
7
+ - arXiv:2502.08773 (UniRoute - Universal Routing)
8
+ - ICML 2025 (BEST-Route)
9
+ - ICLR 2024 (Hybrid LLM)
10
+
11
+ Key Features:
12
+ - Learned model profiles from execution data
13
+ - Quality prediction based on task features
14
+ - Cost-quality optimization
15
+ - Online adaptation to new models
16
+ """
17
+
18
+ from .universal_router import (
19
+ UniversalModelRouter,
20
+ ModelProfile,
21
+ RoutingDecision
22
+ )
23
+
24
+ __all__ = [
25
+ "UniversalModelRouter",
26
+ "ModelProfile",
27
+ "RoutingDecision"
28
+ ]
29
+
30
+ __version__ = "2.2.0-alpha"