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,473 @@
1
+ """
2
+ RoleAssigner - Specialized Agent Assignment
3
+
4
+ Based on arXiv:2505.13516 (HALO) and arXiv:2506.12508v3 (AgentOrchestra)
5
+
6
+ This module implements Tier 2 of HALO orchestration:
7
+ - Analyzes subtask requirements
8
+ - Assigns specialized agents with appropriate capabilities
9
+ - Creates agent configurations for each subtask
10
+ - Optimizes agent selection for cost and quality
11
+ """
12
+
13
+ import asyncio
14
+ from typing import Dict, List, Any, Optional
15
+ from dataclasses import dataclass, field
16
+ from enum import Enum
17
+
18
+ from .task_planner import SubTask, TaskType
19
+
20
+
21
+ class AgentRole(Enum):
22
+ """Specialized agent roles"""
23
+ PLANNER = "planner" # Strategic thinking, decomposition
24
+ CODER = "coder" # Code generation, implementation
25
+ ANALYST = "analyst" # Data analysis, reasoning
26
+ RESEARCHER = "researcher" # Information gathering
27
+ TESTER = "tester" # Verification, validation
28
+ DEPLOYER = "deployer" # Infrastructure, deployment
29
+ GENERALIST = "generalist" # Jack-of-all-trades
30
+
31
+
32
+ class ModelCapability(Enum):
33
+ """Model capabilities for routing"""
34
+ CODE_GENERATION = "code_generation"
35
+ TEXT_GENERATION = "text_generation"
36
+ DATA_ANALYSIS = "data_analysis"
37
+ REASONING = "reasoning"
38
+ SPEED = "speed"
39
+ COST_EFFICIENCY = "cost_efficiency"
40
+
41
+
42
+ @dataclass
43
+ class AgentConfig:
44
+ """Configuration for an assigned agent"""
45
+ agent_id: str
46
+ role: AgentRole
47
+ model_provider: str # "anthropic", "openai", "cerebras", etc.
48
+ model_name: str
49
+ capabilities: List[str]
50
+ cost_per_1k_tokens: float
51
+ quality_score: float # 0-1
52
+ temperature: float = 0.7
53
+ max_tokens: int = 4096
54
+
55
+ def to_dict(self) -> Dict:
56
+ return {
57
+ "agent_id": self.agent_id,
58
+ "role": self.role.value,
59
+ "model_provider": self.model_provider,
60
+ "model_name": self.model_name,
61
+ "capabilities": self.capabilities,
62
+ "cost_per_1k_tokens": self.cost_per_1k_tokens,
63
+ "quality_score": self.quality_score,
64
+ "temperature": self.temperature,
65
+ "max_tokens": self.max_tokens
66
+ }
67
+
68
+
69
+ @dataclass
70
+ class AgentAssignment:
71
+ """Result of agent assignment for a subtask"""
72
+ subtask_id: str
73
+ agent_config: AgentConfig
74
+ confidence: float # 0-1, how well-suited the agent is
75
+ reasoning: str
76
+
77
+
78
+ class RoleAssigner:
79
+ """
80
+ Assign specialized agents to subtasks
81
+
82
+ Implements Tier 2 of HALO orchestration:
83
+ - Analyzes subtask requirements
84
+ - Selects optimal agent for each subtask
85
+ - Balances cost and quality
86
+ - Creates agent configurations
87
+ """
88
+
89
+ def __init__(self):
90
+ """Initialize role assigner with model registry"""
91
+ self.model_registry = self._build_model_registry()
92
+ self.agent_pool = []
93
+
94
+ async def assign_roles(
95
+ self,
96
+ subtasks: List[SubTask],
97
+ optimization_target: str = "quality" # "quality", "cost", "balanced"
98
+ ) -> Dict[str, AgentAssignment]:
99
+ """
100
+ Assign agents to all subtasks
101
+
102
+ Args:
103
+ subtasks: List of subtasks requiring agents
104
+ optimization_target: "quality" (best model), "cost" (cheapest), "balanced"
105
+
106
+ Returns:
107
+ Dict mapping subtask_id -> AgentAssignment
108
+ """
109
+ assignments = {}
110
+
111
+ for subtask in subtasks:
112
+ assignment = await self._assign_agent(subtask, optimization_target)
113
+ assignments[subtask.id] = assignment
114
+
115
+ return assignments
116
+
117
+ async def _assign_agent(
118
+ self,
119
+ subtask: SubTask,
120
+ optimization_target: str
121
+ ) -> AgentAssignment:
122
+ """
123
+ Assign optimal agent for a single subtask
124
+ """
125
+ # Step 1: Determine required role based on task type
126
+ required_role = self._map_task_type_to_role(subtask.task_type)
127
+
128
+ # Step 2: Get candidate models for this role
129
+ candidates = self._get_candidates_for_role(
130
+ required_role,
131
+ subtask.required_capabilities
132
+ )
133
+
134
+ # Step 3: Score candidates based on optimization target
135
+ scored_candidates = []
136
+ for candidate in candidates:
137
+ score = self._score_candidate(
138
+ candidate,
139
+ subtask,
140
+ optimization_target
141
+ )
142
+ scored_candidates.append((candidate, score))
143
+
144
+ # Step 4: Select best candidate
145
+ scored_candidates.sort(key=lambda x: x[1], reverse=True)
146
+ best_model, best_score = scored_candidates[0]
147
+
148
+ # Step 5: Create agent config
149
+ agent_config = AgentConfig(
150
+ agent_id=f"{required_role.value}_{subtask.id}",
151
+ role=required_role,
152
+ model_provider=best_model["provider"],
153
+ model_name=best_model["model"],
154
+ capabilities=best_model["capabilities"],
155
+ cost_per_1k_tokens=best_model["cost_per_1k_tokens"],
156
+ quality_score=best_model["quality_score"],
157
+ temperature=self._determine_temperature(subtask),
158
+ max_tokens=self._determine_max_tokens(subtask)
159
+ )
160
+
161
+ # Step 6: Calculate confidence
162
+ confidence = min(1.0, best_score / 100.0)
163
+
164
+ # Step 7: Generate reasoning
165
+ reasoning = self._generate_reasoning(agent_config, subtask, best_score)
166
+
167
+ return AgentAssignment(
168
+ subtask_id=subtask.id,
169
+ agent_config=agent_config,
170
+ confidence=confidence,
171
+ reasoning=reasoning
172
+ )
173
+
174
+ def _build_model_registry(self) -> Dict[str, List[Dict]]:
175
+ """
176
+ Build registry of available models with their capabilities
177
+
178
+ Returns:
179
+ Dict mapping role -> list of model configs
180
+ """
181
+ return {
182
+ AgentRole.PLANNER: [
183
+ {
184
+ "provider": "anthropic",
185
+ "model": "claude-3-5-sonnet-20241022",
186
+ "capabilities": ["reasoning", "planning", "architecture"],
187
+ "cost_per_1k_tokens": 0.003,
188
+ "quality_score": 0.98
189
+ },
190
+ {
191
+ "provider": "openai",
192
+ "model": "gpt-4o",
193
+ "capabilities": ["reasoning", "planning"],
194
+ "cost_per_1k_tokens": 0.0025,
195
+ "quality_score": 0.95
196
+ }
197
+ ],
198
+ AgentRole.CODER: [
199
+ {
200
+ "provider": "anthropic",
201
+ "model": "claude-3-5-sonnet-20241022",
202
+ "capabilities": ["code_generation", "reasoning"],
203
+ "cost_per_1k_tokens": 0.003,
204
+ "quality_score": 0.97
205
+ },
206
+ {
207
+ "provider": "openai",
208
+ "model": "gpt-4o",
209
+ "capabilities": ["code_generation"],
210
+ "cost_per_1k_tokens": 0.0025,
211
+ "quality_score": 0.94
212
+ },
213
+ {
214
+ "provider": "cerebras",
215
+ "model": "llama-3.3-70b",
216
+ "capabilities": ["code_generation", "speed"],
217
+ "cost_per_1k_tokens": 0.0001,
218
+ "quality_score": 0.75
219
+ }
220
+ ],
221
+ AgentRole.ANALYST: [
222
+ {
223
+ "provider": "anthropic",
224
+ "model": "claude-3-5-sonnet-20241022",
225
+ "capabilities": ["data_analysis", "reasoning"],
226
+ "cost_per_1k_tokens": 0.003,
227
+ "quality_score": 0.96
228
+ },
229
+ {
230
+ "provider": "openai",
231
+ "model": "gpt-4o",
232
+ "capabilities": ["data_analysis"],
233
+ "cost_per_1k_tokens": 0.0025,
234
+ "quality_score": 0.93
235
+ }
236
+ ],
237
+ AgentRole.RESEARCHER: [
238
+ {
239
+ "provider": "openai",
240
+ "model": "gpt-4o",
241
+ "capabilities": ["text_generation", "reasoning"],
242
+ "cost_per_1k_tokens": 0.0025,
243
+ "quality_score": 0.94
244
+ },
245
+ {
246
+ "provider": "perplexity",
247
+ "model": "sonar-small-online",
248
+ "capabilities": ["text_generation", "speed", "online"],
249
+ "cost_per_1k_tokens": 0.0002,
250
+ "quality_score": 0.80
251
+ }
252
+ ],
253
+ AgentRole.TESTER: [
254
+ {
255
+ "provider": "anthropic",
256
+ "model": "claude-3-5-sonnet-20241022",
257
+ "capabilities": ["reasoning", "testing"],
258
+ "cost_per_1k_tokens": 0.003,
259
+ "quality_score": 0.95
260
+ },
261
+ {
262
+ "provider": "openai",
263
+ "model": "gpt-4o-mini",
264
+ "capabilities": ["testing", "speed"],
265
+ "cost_per_1k_tokens": 0.00015,
266
+ "quality_score": 0.85
267
+ }
268
+ ],
269
+ AgentRole.DEPLOYER: [
270
+ {
271
+ "provider": "anthropic",
272
+ "model": "claude-3-5-sonnet-20241022",
273
+ "capabilities": ["reasoning", "deployment"],
274
+ "cost_per_1k_tokens": 0.003,
275
+ "quality_score": 0.94
276
+ },
277
+ {
278
+ "provider": "openai",
279
+ "model": "gpt-4o",
280
+ "capabilities": ["deployment"],
281
+ "cost_per_1k_tokens": 0.0025,
282
+ "quality_score": 0.92
283
+ }
284
+ ],
285
+ AgentRole.GENERALIST: [
286
+ {
287
+ "provider": "anthropic",
288
+ "model": "claude-3-5-sonnet-20241022",
289
+ "capabilities": ["text_generation", "reasoning", "code_generation"],
290
+ "cost_per_1k_tokens": 0.003,
291
+ "quality_score": 0.96
292
+ },
293
+ {
294
+ "provider": "openai",
295
+ "model": "gpt-4o",
296
+ "capabilities": ["text_generation", "code_generation"],
297
+ "cost_per_1k_tokens": 0.0025,
298
+ "quality_score": 0.93
299
+ },
300
+ {
301
+ "provider": "cerebras",
302
+ "model": "llama-3.3-70b",
303
+ "capabilities": ["speed", "cost_efficiency"],
304
+ "cost_per_1k_tokens": 0.0001,
305
+ "quality_score": 0.70
306
+ }
307
+ ]
308
+ }
309
+
310
+ def _map_task_type_to_role(self, task_type: TaskType) -> AgentRole:
311
+ """Map task type to agent role"""
312
+ mapping = {
313
+ TaskType.PLANNING: AgentRole.PLANNER,
314
+ TaskType.CODING: AgentRole.CODER,
315
+ TaskType.ANALYSIS: AgentRole.ANALYST,
316
+ TaskType.RESEARCH: AgentRole.RESEARCHER,
317
+ TaskType.TESTING: AgentRole.TESTER,
318
+ TaskType.DEPLOYMENT: AgentRole.DEPLOYER
319
+ }
320
+ return mapping.get(task_type, AgentRole.GENERALIST)
321
+
322
+ def _get_candidates_for_role(
323
+ self,
324
+ role: AgentRole,
325
+ required_capabilities: List[str]
326
+ ) -> List[Dict]:
327
+ """Get candidate models for a given role"""
328
+ candidates = self.model_registry.get(role, [])
329
+
330
+ # Filter by required capabilities
331
+ if required_capabilities:
332
+ filtered = []
333
+ for candidate in candidates:
334
+ if all(cap in candidate["capabilities"] for cap in required_capabilities):
335
+ filtered.append(candidate)
336
+ candidates = filtered or self.model_registry.get(AgentRole.GENERALIST, [])
337
+
338
+ return candidates
339
+
340
+ def _score_candidate(
341
+ self,
342
+ candidate: Dict,
343
+ subtask: SubTask,
344
+ optimization_target: str
345
+ ) -> float:
346
+ """
347
+ Score a candidate model for a subtask
348
+
349
+ Returns:
350
+ Score 0-100
351
+ """
352
+ # Base score from quality
353
+ score = candidate["quality_score"] * 50
354
+
355
+ # Adjust based on optimization target
356
+ if optimization_target == "quality":
357
+ # Prioritize quality score
358
+ score += candidate["quality_score"] * 50
359
+ elif optimization_target == "cost":
360
+ # Prioritize low cost (invert cost score)
361
+ cost_score = (1.0 / (candidate["cost_per_1k_tokens"] + 0.0001)) * 10
362
+ score += cost_score
363
+ else: # "balanced"
364
+ # Balance quality and cost
365
+ cost_score = (1.0 / (candidate["cost_per_1k_tokens"] + 0.0001)) * 5
366
+ score += (candidate["quality_score"] * 25) + cost_score
367
+
368
+ # Adjust for difficulty matching
369
+ if subtask.difficulty > 70 and candidate["quality_score"] > 0.9:
370
+ score += 10 # Bonus: high-quality model for hard task
371
+ elif subtask.difficulty < 40 and candidate["cost_per_1k_tokens"] < 0.001:
372
+ score += 10 # Bonus: cheap model for easy task
373
+
374
+ return min(100, score)
375
+
376
+ def _determine_temperature(self, subtask: SubTask) -> float:
377
+ """Determine optimal temperature for subtask"""
378
+ if subtask.task_type in [TaskType.CODING, TaskType.DEPLOYMENT]:
379
+ return 0.3 # Lower temperature for precise tasks
380
+ elif subtask.task_type in [TaskType.RESEARCH, TaskType.ANALYSIS]:
381
+ return 0.7 # Medium temperature for exploration
382
+ else:
383
+ return 0.5 # Balanced default
384
+
385
+ def _determine_max_tokens(self, subtask: SubTask) -> int:
386
+ """Determine max tokens for subtask"""
387
+ if subtask.task_type == TaskType.CODING:
388
+ return 8192 # Code generation needs more tokens
389
+ elif subtask.task_type == TaskType.RESEARCH:
390
+ return 4096 # Moderate for research
391
+ else:
392
+ return 2048 # Default
393
+
394
+ def _generate_reasoning(
395
+ self,
396
+ agent_config: AgentConfig,
397
+ subtask: SubTask,
398
+ score: float
399
+ ) -> str:
400
+ """Generate human-readable reasoning for assignment"""
401
+ return (
402
+ f"Assigned {agent_config.model_provider}/{agent_config.model_name} "
403
+ f"({agent_config.role.value}) to {subtask.id}. "
404
+ f"Match score: {score:.1f}/100. "
405
+ f"Quality: {agent_config.quality_score:.2f}, "
406
+ f"Cost: ${agent_config.cost_per_1k_tokens:.4f}/1K tokens"
407
+ )
408
+
409
+ def get_assignment_stats(
410
+ self,
411
+ assignments: Dict[str, AgentAssignment]
412
+ ) -> Dict[str, Any]:
413
+ """Get statistics about agent assignments"""
414
+ role_counts = {}
415
+ provider_costs = {}
416
+
417
+ for assignment in assignments.values():
418
+ role = assignment.agent_config.role.value
419
+ role_counts[role] = role_counts.get(role, 0) + 1
420
+
421
+ provider = assignment.agent_config.model_provider
422
+ cost = assignment.agent_config.cost_per_1k_tokens
423
+ provider_costs[provider] = provider_costs.get(provider, 0) + cost
424
+
425
+ return {
426
+ "total_assignments": len(assignments),
427
+ "role_distribution": role_counts,
428
+ "estimated_total_cost_per_1k_tokens": provider_costs,
429
+ "average_confidence": sum(
430
+ a.confidence for a in assignments.values()
431
+ ) / len(assignments) if assignments else 0
432
+ }
433
+
434
+
435
+ # Example usage
436
+ async def main():
437
+ """Example of RoleAssigner usage"""
438
+ from .task_planner import TaskPlanner, TaskType
439
+
440
+ # Create planner and assigner
441
+ planner = TaskPlanner()
442
+ assigner = RoleAssigner()
443
+
444
+ # Create a complex task
445
+ task = {
446
+ "description": "Build a REST API with user authentication",
447
+ "context": {"requirements": ["JWT", "PostgreSQL"]}
448
+ }
449
+
450
+ # Decompose task
451
+ decomposition = await planner.decompose(task)
452
+
453
+ # Assign agents
454
+ assignments = await assigner.assign_roles(
455
+ decomposition.subtasks,
456
+ optimization_target="balanced"
457
+ )
458
+
459
+ print(f"Assigned {len(assignments)} agents:")
460
+ for subtask_id, assignment in assignments.items():
461
+ print(f"\n{subtask_id}:")
462
+ print(f" Agent: {assignment.agent_config.model_provider}/{assignment.agent_config.model_name}")
463
+ print(f" Role: {assignment.agent_config.role.value}")
464
+ print(f" Reasoning: {assignment.reasoning}")
465
+ print(f" Confidence: {assignment.confidence:.2f}")
466
+
467
+ # Stats
468
+ stats = assigner.get_assignment_stats(assignments)
469
+ print(f"\nStats: {stats}")
470
+
471
+
472
+ if __name__ == "__main__":
473
+ asyncio.run(main())