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.
- package/LICENSE +21 -0
- package/README.md +146 -66
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/airtable.js +20 -0
- package/dist/integrations/discord.js +18 -0
- package/dist/integrations/github.js +23 -0
- package/dist/integrations/gmail.js +19 -0
- package/dist/integrations/google-calendar.js +18 -0
- package/dist/integrations/index.js +61 -0
- package/dist/integrations/jira.js +21 -0
- package/dist/integrations/linear.js +19 -0
- package/dist/integrations/notion.js +19 -0
- package/dist/integrations/slack.js +18 -0
- package/dist/integrations/telegram.js +19 -0
- package/dist/providers/registry.js +7 -3
- package/docs/ARCHITECTURAL-IMPROVEMENTS-2025.md +1391 -0
- package/docs/ARCHITECTURAL-IMPROVEMENTS-REVISED-2025.md +1051 -0
- package/docs/CONFIGURATION.md +476 -0
- package/docs/COUNCIL_DECISION.json +308 -0
- package/docs/COUNCIL_SUMMARY.md +265 -0
- package/docs/COUNCIL_V2.2_DECISION.md +416 -0
- package/docs/IMPROVEMENT_ROADMAP.md +515 -0
- package/docs/LLM_COUNCIL_DECISION.md +508 -0
- package/docs/QUICK_START_VISIBILITY.md +782 -0
- package/docs/REDDIT_GAP_ANALYSIS.md +299 -0
- package/docs/RESEARCH_BACKED_IMPROVEMENTS.md +1180 -0
- package/docs/TMLPD_QNA.md +751 -0
- package/docs/TMLPD_V2.1_COMPLETE.md +763 -0
- package/docs/TMLPD_V2.2_RESEARCH_ROADMAP.md +754 -0
- package/docs/V2.2_IMPLEMENTATION_COMPLETE.md +446 -0
- package/docs/V2_IMPLEMENTATION_GUIDE.md +388 -0
- package/docs/VISIBILITY_ADOPTION_PLAN.md +1005 -0
- package/docs/launch-content/LAUNCH_EXECUTION_CHECKLIST.md +421 -0
- package/docs/launch-content/README.md +457 -0
- package/docs/launch-content/assets/cost_comparison_100_tasks.png +0 -0
- package/docs/launch-content/assets/cumulative_savings.png +0 -0
- package/docs/launch-content/assets/parallel_speedup.png +0 -0
- package/docs/launch-content/assets/provider_pricing_comparison.png +0 -0
- package/docs/launch-content/assets/task_breakdown_comparison.png +0 -0
- package/docs/launch-content/generate_charts.py +313 -0
- package/docs/launch-content/hn_show_post.md +139 -0
- package/docs/launch-content/partner_outreach_templates.md +745 -0
- package/docs/launch-content/reddit_posts.md +467 -0
- package/docs/launch-content/twitter_thread.txt +460 -0
- package/examples/QUICKSTART.md +1 -1
- package/openclaw-alexa-bridge/ALL_REMAINING_FIXES_PLAN.md +313 -0
- package/openclaw-alexa-bridge/REMAINING_FIXES_SUMMARY.md +277 -0
- package/openclaw-alexa-bridge/src/alexa_handler_no_tmlpd.js +1234 -0
- package/openclaw-alexa-bridge/test_fixes.js +77 -0
- package/package.json +120 -29
- package/package.json.tmp +0 -0
- package/qna/TMLPD_QNA.md +3 -3
- package/skill/SKILL.md +2 -2
- package/src/__tests__/integration/tmpld_integration.test.py +540 -0
- package/src/agents/skill_enhanced_agent.py +318 -0
- package/src/memory/__init__.py +15 -0
- package/src/memory/agentic_memory.py +353 -0
- package/src/memory/semantic_memory.py +444 -0
- package/src/memory/simple_memory.py +466 -0
- package/src/memory/working_memory.py +447 -0
- package/src/orchestration/__init__.py +52 -0
- package/src/orchestration/execution_engine.py +353 -0
- package/src/orchestration/halo_orchestrator.py +367 -0
- package/src/orchestration/mcts_workflow.py +498 -0
- package/src/orchestration/role_assigner.py +473 -0
- package/src/orchestration/task_planner.py +522 -0
- package/src/providers/__init__.py +67 -0
- package/src/providers/anthropic.py +304 -0
- package/src/providers/base.py +241 -0
- package/src/providers/cerebras.py +373 -0
- package/src/providers/registry.py +476 -0
- package/src/routing/__init__.py +30 -0
- package/src/routing/universal_router.py +621 -0
- package/src/skills/TMLPD-QUICKREF.md +210 -0
- package/src/skills/TMLPD-SETUP-SUMMARY.md +157 -0
- package/src/skills/TMLPD.md +540 -0
- package/src/skills/__tests__/skill_manager.test.ts +328 -0
- package/src/skills/skill_manager.py +385 -0
- package/src/skills/test-tmlpd.sh +108 -0
- package/src/skills/tmlpd-category.yaml +67 -0
- package/src/skills/tmlpd-monitoring.yaml +188 -0
- package/src/skills/tmlpd-phase.yaml +132 -0
- package/src/state/__init__.py +17 -0
- package/src/state/simple_checkpoint.py +508 -0
- package/src/tmlpd_agent.py +464 -0
- package/src/tmpld_v2.py +427 -0
- package/src/workflows/__init__.py +18 -0
- package/src/workflows/advanced_difficulty_classifier.py +377 -0
- package/src/workflows/chaining_executor.py +417 -0
- package/src/workflows/difficulty_integration.py +209 -0
- package/src/workflows/orchestrator.py +469 -0
- package/src/workflows/orchestrator_executor.py +456 -0
- package/src/workflows/parallelization_executor.py +382 -0
- package/src/workflows/router.py +311 -0
- package/test_integration_simple.py +86 -0
- package/test_mcts_workflow.py +150 -0
- package/test_templd_integration.py +262 -0
- package/test_universal_router.py +275 -0
- package/tmlpd-pi-extension/README.md +36 -0
- package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts +114 -0
- package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/cache/prefixCache.js +285 -0
- package/tmlpd-pi-extension/dist/cache/prefixCache.js.map +1 -0
- package/tmlpd-pi-extension/dist/cache/responseCache.d.ts +58 -0
- package/tmlpd-pi-extension/dist/cache/responseCache.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/cache/responseCache.js +153 -0
- package/tmlpd-pi-extension/dist/cache/responseCache.js.map +1 -0
- package/tmlpd-pi-extension/dist/cli.js +59 -0
- package/tmlpd-pi-extension/dist/cost/costTracker.d.ts +95 -0
- package/tmlpd-pi-extension/dist/cost/costTracker.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/cost/costTracker.js +240 -0
- package/tmlpd-pi-extension/dist/cost/costTracker.js.map +1 -0
- package/tmlpd-pi-extension/dist/index.d.ts +723 -0
- package/tmlpd-pi-extension/dist/index.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/index.js +239 -0
- package/tmlpd-pi-extension/dist/index.js.map +1 -0
- package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts +82 -0
- package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/memory/episodicMemory.js +145 -0
- package/tmlpd-pi-extension/dist/memory/episodicMemory.js.map +1 -0
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts +102 -0
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js +207 -0
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js.map +1 -0
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts +85 -0
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js +210 -0
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js.map +1 -0
- package/tmlpd-pi-extension/dist/providers/localProvider.d.ts +102 -0
- package/tmlpd-pi-extension/dist/providers/localProvider.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/providers/localProvider.js +338 -0
- package/tmlpd-pi-extension/dist/providers/localProvider.js.map +1 -0
- package/tmlpd-pi-extension/dist/providers/registry.d.ts +55 -0
- package/tmlpd-pi-extension/dist/providers/registry.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/providers/registry.js +138 -0
- package/tmlpd-pi-extension/dist/providers/registry.js.map +1 -0
- package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts +68 -0
- package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/routing/advancedRouter.js +332 -0
- package/tmlpd-pi-extension/dist/routing/advancedRouter.js.map +1 -0
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts +101 -0
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.js +368 -0
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.js.map +1 -0
- package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts +96 -0
- package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/utils/batchProcessor.js +170 -0
- package/tmlpd-pi-extension/dist/utils/batchProcessor.js.map +1 -0
- package/tmlpd-pi-extension/dist/utils/compression.d.ts +61 -0
- package/tmlpd-pi-extension/dist/utils/compression.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/utils/compression.js +281 -0
- package/tmlpd-pi-extension/dist/utils/compression.js.map +1 -0
- package/tmlpd-pi-extension/dist/utils/reliability.d.ts +74 -0
- package/tmlpd-pi-extension/dist/utils/reliability.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/utils/reliability.js +177 -0
- package/tmlpd-pi-extension/dist/utils/reliability.js.map +1 -0
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts +117 -0
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js +246 -0
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js.map +1 -0
- package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts +50 -0
- package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts.map +1 -0
- package/tmlpd-pi-extension/dist/utils/tokenUtils.js +124 -0
- package/tmlpd-pi-extension/dist/utils/tokenUtils.js.map +1 -0
- package/tmlpd-pi-extension/examples/QUICKSTART.md +183 -0
- package/tmlpd-pi-extension/package-lock.json +75 -0
- package/tmlpd-pi-extension/package.json +172 -0
- package/tmlpd-pi-extension/python/examples.py +53 -0
- package/tmlpd-pi-extension/python/integrations.py +330 -0
- package/tmlpd-pi-extension/python/setup.py +28 -0
- package/tmlpd-pi-extension/python/tmlpd.py +369 -0
- package/tmlpd-pi-extension/qna/REDDIT_GAP_ANALYSIS.md +299 -0
- package/tmlpd-pi-extension/qna/TMLPD_QNA.md +751 -0
- package/tmlpd-pi-extension/skill/SKILL.md +238 -0
- package/{src → tmlpd-pi-extension/src}/index.ts +1 -1
- package/tmlpd-pi-extension/tsconfig.json +18 -0
- package/demo/research-demo.js +0 -266
- package/notebooks/quickstart.ipynb +0 -157
- package/rust/tmlpd.h +0 -268
- package/src/cache/prefixCache.ts +0 -365
- package/src/routing/advancedRouter.ts +0 -406
- package/src/utils/speculativeDecoding.ts +0 -344
- /package/{src → tmlpd-pi-extension/src}/cache/responseCache.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/cost/costTracker.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/memory/episodicMemory.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/orchestration/haloOrchestrator.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/orchestration/mctsWorkflow.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/providers/localProvider.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/providers/registry.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/tools/tmlpdTools.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/utils/batchProcessor.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/utils/compression.ts +0 -0
- /package/{src → tmlpd-pi-extension/src}/utils/reliability.ts +0 -0
- /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"
|