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,377 @@
1
+ """
2
+ Phase 2b: Advanced Difficulty Classifier
3
+
4
+ Enhanced difficulty classification with multi-factor scoring
5
+ based on arXiv:2509.11079 and empirical analysis.
6
+ """
7
+
8
+ import re
9
+ from typing import Dict, Any, List, Tuple
10
+ from collections import Counter
11
+
12
+
13
+ class AdvancedDifficultyClassifier:
14
+ """
15
+ Advanced difficulty classifier with enhanced features.
16
+
17
+ Improvements over base classifier:
18
+ - Context-aware scoring
19
+ - Historical performance tracking
20
+ - Dynamic threshold adjustment
21
+ - Multi-modal feature extraction
22
+ """
23
+
24
+ DIFFICULTY_LEVELS = {
25
+ "TRIVIAL": range(0, 20),
26
+ "SIMPLE": range(20, 40),
27
+ "MEDIUM": range(40, 60),
28
+ "COMPLEX": range(60, 80),
29
+ "EXPERT": range(80, 100)
30
+ }
31
+
32
+ # Enhanced keyword dictionaries
33
+ MULTI_STEP_KEYWORDS = {
34
+ "high": ["then", "after", "before", "followed by", "subsequently", "finally"],
35
+ "medium": ["multiple", "several", "sequence", "chain", "series"],
36
+ "low": ["iterate", "refine", "improve", "optimize"]
37
+ }
38
+
39
+ TECHNICAL_KEYWORDS = {
40
+ "architecture": ["architecture", "system design", "microservices", "monolith"],
41
+ "implementation": ["implement", "integrate", "build", "create", "develop"],
42
+ "optimization": ["optimize", "refactor", "improve performance", "efficiency"],
43
+ "infrastructure": ["api", "database", "authentication", "deployment", "scaling"],
44
+ "algorithms": ["algorithm", "data structure", "computational", "complexity"],
45
+ "security": ["security", "authentication", "authorization", "encryption"],
46
+ "data": ["database", "storage", "persistence", "data modeling", "migration"]
47
+ }
48
+
49
+ DOMAIN_KEYWORDS = {
50
+ "frontend": ["react", "vue", "angular", "component", "ui", "ux", "css", "html"],
51
+ "backend": ["api", "server", "microservice", "endpoint", "middleware"],
52
+ "data": ["database", "sql", "nosql", "orm", "migration", "query"],
53
+ "devops": ["deploy", "docker", "kubernetes", "ci/cd", "pipeline"],
54
+ "testing": ["test", "spec", "mock", "coverage", "unit", "integration"],
55
+ "mobile": ["mobile", "ios", "android", "react native", "flutter"]
56
+ }
57
+
58
+ def __init__(self, learning_enabled: bool = True):
59
+ """
60
+ Initialize advanced difficulty classifier.
61
+
62
+ Args:
63
+ learning_enabled: Enable learning from past executions
64
+ """
65
+ self.learning_enabled = learning_enabled
66
+ self.history: List[Dict[str, Any]] = []
67
+
68
+ def classify_difficulty(self, task: Dict[str, Any]) -> Dict[str, Any]:
69
+ """
70
+ Classify task difficulty with enhanced multi-factor analysis.
71
+
72
+ Args:
73
+ task: Task with description, requirements, context
74
+
75
+ Returns:
76
+ Dict with difficulty level, score, and breakdown
77
+ """
78
+ description = task.get("description", "")
79
+ requirements = task.get("requirements", "")
80
+ context = task.get("context", "")
81
+
82
+ # Combine all text
83
+ full_text = f"{description} {requirements} {context}".lower()
84
+
85
+ # Calculate scores
86
+ score_breakdown = {
87
+ "length": self._score_length(description),
88
+ "multi_step": self._score_multi_step(full_text),
89
+ "technical": self._score_technical(full_text),
90
+ "requirements": self._score_requirements(task),
91
+ "dependencies": self._score_dependencies(full_text),
92
+ "domain": self._score_domain(full_text),
93
+ "complexity": self._score_complexity(full_text),
94
+ "ambiguity": self._score_ambiguity(task)
95
+ }
96
+
97
+ # Calculate total score
98
+ total_score = sum(score_breakdown.values())
99
+
100
+ # Map to difficulty level
101
+ difficulty = self._map_score_to_difficulty(total_score)
102
+
103
+ # Adjust based on learning if enabled
104
+ if self.learning_enabled and self.history:
105
+ adjusted_difficulty = self._adjust_with_learning(
106
+ difficulty,
107
+ description,
108
+ total_score
109
+ )
110
+ else:
111
+ adjusted_difficulty = difficulty
112
+
113
+ return {
114
+ "difficulty": adjusted_difficulty,
115
+ "score": total_score,
116
+ "breakdown": score_breakdown,
117
+ "confidence": self._calculate_confidence(score_breakdown)
118
+ }
119
+
120
+ def _score_length(self, description: str) -> float:
121
+ """Score based on task length (0-15 points)"""
122
+ word_count = len(description.split())
123
+ char_count = len(description)
124
+
125
+ # Word count score (0-10 points)
126
+ word_score = min(word_count / 15, 10)
127
+
128
+ # Character count score (0-5 points)
129
+ char_score = min(char_count / 500, 5)
130
+
131
+ return word_score + char_score
132
+
133
+ def _score_multi_step(self, text: str) -> float:
134
+ """Score based on multi-step indicators (0-20 points)"""
135
+ score = 0.0
136
+
137
+ # High-weight keywords
138
+ high_count = sum(1 for kw in self.MULTI_STEP_KEYWORDS["high"] if kw in text)
139
+ score += min(high_count * 4, 8)
140
+
141
+ # Medium-weight keywords
142
+ medium_count = sum(1 for kw in self.MULTI_STEP_KEYWORDS["medium"] if kw in text)
143
+ score += min(medium_count * 3, 6)
144
+
145
+ # Low-weight keywords
146
+ low_count = sum(1 for kw in self.MULTI_STEP_KEYWORDS["low"] if kw in text)
147
+ score += min(low_count * 2, 6)
148
+
149
+ return score
150
+
151
+ def _score_technical(self, text: str) -> float:
152
+ """Score based on technical complexity (0-25 points)"""
153
+ score = 0.0
154
+
155
+ for category, keywords in self.TECHNICAL_KEYWORDS.items():
156
+ category_count = sum(1 for kw in keywords if kw in text)
157
+ if category_count > 0:
158
+ # Different categories have different weights
159
+ if category == "architecture":
160
+ score += min(category_count * 4, 8)
161
+ elif category in ["implementation", "infrastructure"]:
162
+ score += min(category_count * 3, 6)
163
+ else:
164
+ score += min(category_count * 2, 4)
165
+
166
+ return min(score, 25)
167
+
168
+ def _score_requirements(self, task: Dict[str, Any]) -> float:
169
+ """Score based on requirements specificity (0-10 points)"""
170
+ score = 0.0
171
+
172
+ if task.get("requirements"):
173
+ requirements_length = len(task.get("requirements", "").split())
174
+ score += min(requirements_length / 10, 5)
175
+
176
+ if task.get("context"):
177
+ context_length = len(task.get("context", "").split())
178
+ score += min(context_length / 10, 3)
179
+
180
+ if task.get("constraints"):
181
+ score += 2
182
+
183
+ return score
184
+
185
+ def _score_dependencies(self, text: str) -> float:
186
+ """Score based on task dependencies (0-10 points)"""
187
+ dependency_keywords = [
188
+ "depends", "requires", "needs", "after", "before",
189
+ "prerequisite", "rely", "blocking", "blocked by"
190
+ ]
191
+
192
+ count = sum(1 for kw in dependency_keywords if kw in text)
193
+ return min(count * 2.5, 10)
194
+
195
+ def _score_domain(self, text: str) -> float:
196
+ """Score based on domain-specific complexity (0-10 points)"""
197
+ domain_scores = {}
198
+
199
+ for domain, keywords in self.DOMAIN_KEYWORDS.items():
200
+ count = sum(1 for kw in keywords if kw in text)
201
+ domain_scores[domain] = count
202
+
203
+ # Multiple domains = higher complexity
204
+ domains_present = sum(1 for score in domain_scores.values() if score > 0)
205
+
206
+ return min(domains_present * 2.5, 10)
207
+
208
+ def _score_complexity(self, text: str) -> float:
209
+ """Score based on complexity indicators (0-10 points)"""
210
+ complexity_keywords = {
211
+ "high": ["distributed", "scalable", "real-time", "concurrent", "async"],
212
+ "medium": ["optimize", "efficient", "performance", "integration"]
213
+ }
214
+
215
+ score = 0.0
216
+ for kw in complexity_keywords["high"]:
217
+ if kw in text:
218
+ score += 2.5
219
+
220
+ for kw in complexity_keywords["medium"]:
221
+ if kw in text:
222
+ score += 1.0
223
+
224
+ return min(score, 10)
225
+
226
+ def _score_ambiguity(self, task: Dict[str, Any]) -> float:
227
+ """Score based on ambiguity (reduces score for vagueness) (0-0 points)"""
228
+ description = task.get("description", "").lower()
229
+
230
+ ambiguity_keywords = [
231
+ "somehow", "maybe", "possibly", "try to", "figure out",
232
+ "something", "things", "stuff", "etc"
233
+ ]
234
+
235
+ ambiguity_count = sum(1 for kw in ambiguity_keywords if kw in description)
236
+
237
+ # Penalize ambiguity (subtract from total, but we'll cap at 0)
238
+ return min(ambiguity_count * -1, 0)
239
+
240
+ def _map_score_to_difficulty(self, score: float) -> str:
241
+ """Map numerical score to difficulty level"""
242
+ for level, range_obj in self.DIFFICULTY_LEVELS.items():
243
+ if score in range_obj:
244
+ return level
245
+ return "MEDIUM"
246
+
247
+ def _calculate_confidence(self, breakdown: Dict[str, float]) -> float:
248
+ """
249
+ Calculate confidence in classification.
250
+
251
+ Higher confidence when scores are consistently high or low,
252
+ rather than mixed.
253
+ """
254
+ values = list(breakdown.values())
255
+
256
+ # Calculate variance
257
+ mean = sum(values) / len(values)
258
+ variance = sum((x - mean) ** 2 for x in values) / len(values)
259
+
260
+ # Lower variance = higher confidence
261
+ confidence = max(0, 1 - (variance / 100))
262
+ return round(confidence, 2)
263
+
264
+ def _adjust_with_learning(
265
+ self,
266
+ current_difficulty: str,
267
+ description: str,
268
+ current_score: float
269
+ ) -> str:
270
+ """Adjust classification based on historical performance"""
271
+ # Find similar tasks in history
272
+ similar_tasks = [
273
+ h for h in self.history
274
+ if self._similarity(description, h["description"]) > 0.7
275
+ ]
276
+
277
+ if not similar_tasks:
278
+ return current_difficulty
279
+
280
+ # Analyze patterns
281
+ actual_difficulties = [t.get("actual_difficulty") for t in similar_tasks]
282
+
283
+ # If similar tasks were consistently harder/easier
284
+ if actual_difficulties:
285
+ most_common = Counter(actual_difficulties).most_common(1)[0][0]
286
+
287
+ # Adjust if there's a consistent pattern
288
+ if most_common != current_difficulty:
289
+ # Log adjustment
290
+ print(f"📚 Learning: Adjusting {current_difficulty} -> {most_common}")
291
+
292
+ return most_common
293
+
294
+ return current_difficulty
295
+
296
+ def _similarity(self, text1: str, text2: str) -> float:
297
+ """Calculate text similarity using word overlap"""
298
+ words1 = set(re.findall(r'\w+', text1.lower()))
299
+ words2 = set(re.findall(r'\w+', text2.lower()))
300
+
301
+ if not words1 or not words2:
302
+ return 0.0
303
+
304
+ intersection = words1 & words2
305
+ union = words1 | words2
306
+
307
+ return len(intersection) / len(union) if union else 0.0
308
+
309
+ def record_outcome(
310
+ self,
311
+ task: Dict[str, Any],
312
+ predicted_difficulty: str,
313
+ actual_difficulty: str,
314
+ execution_time: float,
315
+ success: bool
316
+ ):
317
+ """
318
+ Record actual execution outcome for learning.
319
+
320
+ Args:
321
+ task: Original task
322
+ predicted_difficulty: What was predicted
323
+ actual_difficulty: What it should have been
324
+ execution_time: How long it took
325
+ success: Whether it succeeded
326
+ """
327
+ if not self.learning_enabled:
328
+ return
329
+
330
+ self.history.append({
331
+ "description": task.get("description", ""),
332
+ "predicted_difficulty": predicted_difficulty,
333
+ "actual_difficulty": actual_difficulty,
334
+ "execution_time": execution_time,
335
+ "success": success,
336
+ "timestamp": __import__("datetime").datetime.now().isoformat()
337
+ })
338
+
339
+ # Keep history manageable
340
+ if len(self.history) > 1000:
341
+ self.history = self.history[-1000:]
342
+
343
+ def get_learning_stats(self) -> Dict[str, Any]:
344
+ """Get learning statistics"""
345
+ if not self.history:
346
+ return {"message": "No learning data yet"}
347
+
348
+ correct_predictions = sum(
349
+ 1 for h in self.history
350
+ if h["predicted_difficulty"] == h["actual_difficulty"]
351
+ )
352
+
353
+ return {
354
+ "total_records": len(self.history),
355
+ "accuracy": correct_predictions / len(self.history) if self.history else 0,
356
+ "difficulty_distribution": Counter(
357
+ h["actual_difficulty"] for h in self.history
358
+ ),
359
+ "avg_execution_time_by_difficulty": self._avg_time_by_difficulty()
360
+ }
361
+
362
+ def _avg_time_by_difficulty(self) -> Dict[str, float]:
363
+ """Calculate average execution time by difficulty"""
364
+ times_by_difficulty = {}
365
+
366
+ for h in self.history:
367
+ difficulty = h["actual_difficulty"]
368
+ time = h["execution_time"]
369
+
370
+ if difficulty not in times_by_difficulty:
371
+ times_by_difficulty[difficulty] = []
372
+ times_by_difficulty[difficulty].append(time)
373
+
374
+ return {
375
+ difficulty: sum(times) / len(times)
376
+ for difficulty, times in times_by_difficulty.items()
377
+ }