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,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
|
+
}
|