htmlgraph 0.20.1__py3-none-any.whl → 0.27.5__py3-none-any.whl
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.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +51 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/__init__.py +8 -1
- htmlgraph/analytics/cli.py +86 -20
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +10 -6
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +67 -27
- htmlgraph/analytics_index.py +53 -20
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +2 -1
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/base.py +57 -2
- htmlgraph/builders/bug.py +19 -3
- htmlgraph/builders/chore.py +19 -3
- htmlgraph/builders/epic.py +19 -3
- htmlgraph/builders/feature.py +27 -3
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +19 -3
- htmlgraph/builders/spike.py +29 -3
- htmlgraph/builders/track.py +42 -1
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +2 -0
- htmlgraph/collections/base.py +197 -14
- htmlgraph/collections/bug.py +2 -1
- htmlgraph/collections/chore.py +2 -1
- htmlgraph/collections/epic.py +2 -1
- htmlgraph/collections/feature.py +2 -1
- htmlgraph/collections/insight.py +2 -1
- htmlgraph/collections/metric.py +2 -1
- htmlgraph/collections/pattern.py +2 -1
- htmlgraph/collections/phase.py +2 -1
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +13 -2
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +116 -7
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2246 -248
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +2 -1
- htmlgraph/deploy.py +26 -27
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +2 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +86 -37
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +67 -9
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +790 -99
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +327 -76
- htmlgraph/hooks/orchestrator_reflector.py +31 -4
- htmlgraph/hooks/post_tool_use_failure.py +32 -7
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +92 -19
- htmlgraph/hooks/pretooluse.py +527 -7
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +99 -4
- htmlgraph/hooks/validator.py +212 -91
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +125 -100
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +217 -18
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +115 -4
- htmlgraph/parallel.py +2 -1
- htmlgraph/parser.py +86 -6
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +2 -1
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +295 -107
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +285 -3
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +33 -1
- htmlgraph/track_manager.py +38 -0
- htmlgraph/transcript.py +18 -5
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -4839
- htmlgraph/sdk.py +0 -2359
- htmlgraph-0.20.1.dist-info/RECORD +0 -118
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuggestionEngine - Generates context-aware suggestions with ranking.
|
|
3
|
+
|
|
4
|
+
This module provides intelligent suggestion generation based on detected patterns:
|
|
5
|
+
1. Proactive suggestions - Suggest next actions based on detected patterns
|
|
6
|
+
2. Delegation recommendations - Suggest which subagent would be best for current task
|
|
7
|
+
3. Parameter optimization - Suggest optimal token budgets, parallelization levels
|
|
8
|
+
4. Risk assessment - Suggest when to use Sonnet vs Opus based on task complexity
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from htmlgraph.analytics.strategic import SuggestionEngine
|
|
12
|
+
|
|
13
|
+
engine = SuggestionEngine(db_path)
|
|
14
|
+
|
|
15
|
+
# Get suggestions for current context
|
|
16
|
+
suggestions = engine.suggest(context)
|
|
17
|
+
|
|
18
|
+
# Rank suggestions by relevance
|
|
19
|
+
ranked = engine.rank(suggestions)
|
|
20
|
+
|
|
21
|
+
# Generate Task() code from pattern
|
|
22
|
+
code = engine.generate_task_code(pattern)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import hashlib
|
|
26
|
+
import json
|
|
27
|
+
import logging
|
|
28
|
+
import sqlite3
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
from enum import Enum
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from htmlgraph.analytics.strategic.pattern_detector import (
|
|
35
|
+
DelegationChain,
|
|
36
|
+
ErrorPattern,
|
|
37
|
+
Pattern,
|
|
38
|
+
PatternDetector,
|
|
39
|
+
ToolSequencePattern,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SuggestionType(Enum):
|
|
46
|
+
"""Types of suggestions that can be generated."""
|
|
47
|
+
|
|
48
|
+
NEXT_ACTION = "next_action" # Suggest next tool/action
|
|
49
|
+
DELEGATION = "delegation" # Suggest which agent to delegate to
|
|
50
|
+
PARAMETER = "parameter" # Suggest optimal parameters
|
|
51
|
+
MODEL_SELECTION = "model_selection" # Suggest which model to use
|
|
52
|
+
ERROR_RESOLUTION = "error_resolution" # Suggest how to resolve error
|
|
53
|
+
WORKFLOW = "workflow" # Suggest workflow optimization
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class TaskContext:
|
|
58
|
+
"""
|
|
59
|
+
Context for generating suggestions.
|
|
60
|
+
|
|
61
|
+
Captures current state to enable context-aware suggestions.
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
session_id: Current session ID
|
|
65
|
+
recent_tools: List of recently used tools (most recent last)
|
|
66
|
+
current_task: Description of current task (if known)
|
|
67
|
+
recent_errors: List of recent error messages
|
|
68
|
+
agent_type: Current agent type (orchestrator, researcher, etc.)
|
|
69
|
+
model: Current model being used
|
|
70
|
+
token_budget: Remaining token budget (if tracked)
|
|
71
|
+
feature_id: Current feature being worked on (if known)
|
|
72
|
+
metadata: Additional context data
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
session_id: str = ""
|
|
76
|
+
recent_tools: list[str] = field(default_factory=list)
|
|
77
|
+
current_task: str = ""
|
|
78
|
+
recent_errors: list[str] = field(default_factory=list)
|
|
79
|
+
agent_type: str = "orchestrator"
|
|
80
|
+
model: str = "claude-sonnet"
|
|
81
|
+
token_budget: int | None = None
|
|
82
|
+
feature_id: str | None = None
|
|
83
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_hook_input(cls, hook_input: dict[str, Any]) -> "TaskContext":
|
|
87
|
+
"""
|
|
88
|
+
Create TaskContext from PreToolUse hook input.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
hook_input: Hook input dictionary
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
TaskContext instance
|
|
95
|
+
"""
|
|
96
|
+
return cls(
|
|
97
|
+
session_id=hook_input.get("session_id", ""),
|
|
98
|
+
recent_tools=hook_input.get("recent_tools", []),
|
|
99
|
+
current_task=hook_input.get("prompt", ""),
|
|
100
|
+
recent_errors=hook_input.get("recent_errors", []),
|
|
101
|
+
agent_type=hook_input.get("agent_type", "orchestrator"),
|
|
102
|
+
model=hook_input.get("model", "claude-sonnet"),
|
|
103
|
+
token_budget=hook_input.get("token_budget"),
|
|
104
|
+
feature_id=hook_input.get("feature_id"),
|
|
105
|
+
metadata=hook_input.get("metadata", {}),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class Suggestion:
|
|
111
|
+
"""
|
|
112
|
+
A suggestion generated by the engine.
|
|
113
|
+
|
|
114
|
+
Attributes:
|
|
115
|
+
suggestion_id: Unique identifier
|
|
116
|
+
suggestion_type: Type of suggestion
|
|
117
|
+
title: Short title for the suggestion
|
|
118
|
+
description: Detailed explanation
|
|
119
|
+
confidence: Confidence score (0.0-1.0)
|
|
120
|
+
relevance: Relevance to current context (0.0-1.0)
|
|
121
|
+
action_code: Code snippet to execute the suggestion (if applicable)
|
|
122
|
+
source_patterns: Pattern IDs that informed this suggestion
|
|
123
|
+
metadata: Additional suggestion-specific data
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
suggestion_id: str
|
|
127
|
+
suggestion_type: SuggestionType
|
|
128
|
+
title: str
|
|
129
|
+
description: str
|
|
130
|
+
confidence: float = 0.0
|
|
131
|
+
relevance: float = 0.0
|
|
132
|
+
action_code: str | None = None
|
|
133
|
+
source_patterns: list[str] = field(default_factory=list)
|
|
134
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
135
|
+
|
|
136
|
+
def to_dict(self) -> dict[str, Any]:
|
|
137
|
+
"""Convert suggestion to dictionary for serialization."""
|
|
138
|
+
return {
|
|
139
|
+
"suggestion_id": self.suggestion_id,
|
|
140
|
+
"suggestion_type": self.suggestion_type.value,
|
|
141
|
+
"title": self.title,
|
|
142
|
+
"description": self.description,
|
|
143
|
+
"confidence": self.confidence,
|
|
144
|
+
"relevance": self.relevance,
|
|
145
|
+
"action_code": self.action_code,
|
|
146
|
+
"source_patterns": self.source_patterns,
|
|
147
|
+
"metadata": self.metadata,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def generate_id(suggestion_type: SuggestionType, content: str) -> str:
|
|
152
|
+
"""Generate unique suggestion ID."""
|
|
153
|
+
hash_input = f"{suggestion_type.value}:{content}"
|
|
154
|
+
hash_obj = hashlib.md5(hash_input.encode())
|
|
155
|
+
return f"sug-{hash_obj.hexdigest()[:8]}"
|
|
156
|
+
|
|
157
|
+
def score(self) -> float:
|
|
158
|
+
"""
|
|
159
|
+
Calculate overall score for ranking.
|
|
160
|
+
|
|
161
|
+
Score = (confidence * 0.5) + (relevance * 0.5)
|
|
162
|
+
"""
|
|
163
|
+
return (self.confidence * 0.5) + (self.relevance * 0.5)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class SuggestionEngine:
|
|
167
|
+
"""
|
|
168
|
+
Generates context-aware suggestions based on detected patterns.
|
|
169
|
+
|
|
170
|
+
The engine:
|
|
171
|
+
1. Analyzes current context (recent tools, errors, task)
|
|
172
|
+
2. Matches context against known patterns
|
|
173
|
+
3. Generates relevant suggestions
|
|
174
|
+
4. Ranks suggestions by confidence and relevance
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
def __init__(self, db_path: Path | str | None = None):
|
|
178
|
+
"""
|
|
179
|
+
Initialize suggestion engine.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
db_path: Path to HtmlGraph database. If None, uses default location.
|
|
183
|
+
"""
|
|
184
|
+
if db_path is None:
|
|
185
|
+
from htmlgraph.config import get_database_path
|
|
186
|
+
|
|
187
|
+
db_path = get_database_path()
|
|
188
|
+
|
|
189
|
+
self.db_path = Path(db_path)
|
|
190
|
+
self._pattern_detector = PatternDetector(db_path)
|
|
191
|
+
self._conn: sqlite3.Connection | None = None
|
|
192
|
+
|
|
193
|
+
def _get_connection(self) -> sqlite3.Connection:
|
|
194
|
+
"""Get database connection with row factory."""
|
|
195
|
+
if self._conn is None:
|
|
196
|
+
self._conn = sqlite3.connect(str(self.db_path))
|
|
197
|
+
self._conn.row_factory = sqlite3.Row
|
|
198
|
+
return self._conn
|
|
199
|
+
|
|
200
|
+
def close(self) -> None:
|
|
201
|
+
"""Close database connections."""
|
|
202
|
+
if self._conn:
|
|
203
|
+
self._conn.close()
|
|
204
|
+
self._conn = None
|
|
205
|
+
self._pattern_detector.close()
|
|
206
|
+
|
|
207
|
+
def suggest(
|
|
208
|
+
self,
|
|
209
|
+
context: TaskContext,
|
|
210
|
+
max_suggestions: int = 5,
|
|
211
|
+
) -> list[Suggestion]:
|
|
212
|
+
"""
|
|
213
|
+
Generate suggestions based on current context.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
context: Current task context
|
|
217
|
+
max_suggestions: Maximum number of suggestions to return
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
List of suggestions sorted by score
|
|
221
|
+
"""
|
|
222
|
+
suggestions: list[Suggestion] = []
|
|
223
|
+
|
|
224
|
+
# Generate suggestions from different sources
|
|
225
|
+
suggestions.extend(self._suggest_from_tool_patterns(context))
|
|
226
|
+
suggestions.extend(self._suggest_from_delegation_patterns(context))
|
|
227
|
+
suggestions.extend(self._suggest_from_error_patterns(context))
|
|
228
|
+
suggestions.extend(self._suggest_model_selection(context))
|
|
229
|
+
|
|
230
|
+
# Rank and limit
|
|
231
|
+
ranked = self.rank(suggestions)
|
|
232
|
+
return ranked[:max_suggestions]
|
|
233
|
+
|
|
234
|
+
def _suggest_from_tool_patterns(self, context: TaskContext) -> list[Suggestion]:
|
|
235
|
+
"""
|
|
236
|
+
Generate next-action suggestions from tool sequence patterns.
|
|
237
|
+
|
|
238
|
+
If recent tools match the start of a known high-success pattern,
|
|
239
|
+
suggest completing the pattern.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
context: Current task context
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
List of next-action suggestions
|
|
246
|
+
"""
|
|
247
|
+
suggestions: list[Suggestion] = []
|
|
248
|
+
|
|
249
|
+
if len(context.recent_tools) < 2:
|
|
250
|
+
return suggestions
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
# Get tool sequence patterns
|
|
254
|
+
patterns = self._pattern_detector.detect_tool_sequences(
|
|
255
|
+
window_size=3, min_frequency=3, days_back=30
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Find patterns that match recent tool sequence
|
|
259
|
+
recent = context.recent_tools[-2:] # Last 2 tools
|
|
260
|
+
|
|
261
|
+
for pattern in patterns:
|
|
262
|
+
if not isinstance(pattern, ToolSequencePattern):
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
# Check if recent tools match start of pattern
|
|
266
|
+
if len(pattern.sequence) >= 3 and pattern.sequence[:2] == recent:
|
|
267
|
+
next_tool = pattern.sequence[2]
|
|
268
|
+
|
|
269
|
+
# Calculate relevance based on pattern success rate
|
|
270
|
+
relevance = pattern.success_rate / 100
|
|
271
|
+
|
|
272
|
+
suggestion = Suggestion(
|
|
273
|
+
suggestion_id=Suggestion.generate_id(
|
|
274
|
+
SuggestionType.NEXT_ACTION, f"next:{next_tool}"
|
|
275
|
+
),
|
|
276
|
+
suggestion_type=SuggestionType.NEXT_ACTION,
|
|
277
|
+
title=f"Consider using {next_tool} next",
|
|
278
|
+
description=(
|
|
279
|
+
f"Pattern '{' → '.join(pattern.sequence)}' has "
|
|
280
|
+
f"{pattern.success_rate:.0f}% success rate across "
|
|
281
|
+
f"{pattern.frequency} occurrences."
|
|
282
|
+
),
|
|
283
|
+
confidence=pattern.confidence,
|
|
284
|
+
relevance=relevance,
|
|
285
|
+
source_patterns=[pattern.pattern_id],
|
|
286
|
+
metadata={
|
|
287
|
+
"next_tool": next_tool,
|
|
288
|
+
"full_sequence": pattern.sequence,
|
|
289
|
+
"success_rate": pattern.success_rate,
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
suggestions.append(suggestion)
|
|
293
|
+
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logger.warning(f"Error generating tool pattern suggestions: {e}")
|
|
296
|
+
|
|
297
|
+
return suggestions
|
|
298
|
+
|
|
299
|
+
def _suggest_from_delegation_patterns(
|
|
300
|
+
self, context: TaskContext
|
|
301
|
+
) -> list[Suggestion]:
|
|
302
|
+
"""
|
|
303
|
+
Generate delegation suggestions from chain patterns.
|
|
304
|
+
|
|
305
|
+
Suggest which agent to delegate to based on successful delegation chains.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
context: Current task context
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
List of delegation suggestions
|
|
312
|
+
"""
|
|
313
|
+
suggestions: list[Suggestion] = []
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
# Get delegation chain patterns
|
|
317
|
+
patterns = self._pattern_detector.detect_delegation_chains(
|
|
318
|
+
min_frequency=2, days_back=30
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
current_agent = context.agent_type
|
|
322
|
+
|
|
323
|
+
for pattern in patterns:
|
|
324
|
+
if not isinstance(pattern, DelegationChain):
|
|
325
|
+
continue
|
|
326
|
+
|
|
327
|
+
# Check if current agent is in chain
|
|
328
|
+
if current_agent in pattern.agents:
|
|
329
|
+
idx = pattern.agents.index(current_agent)
|
|
330
|
+
if idx < len(pattern.agents) - 1:
|
|
331
|
+
next_agent = pattern.agents[idx + 1]
|
|
332
|
+
|
|
333
|
+
# Calculate relevance based on pattern success rate
|
|
334
|
+
relevance = pattern.success_rate / 100
|
|
335
|
+
|
|
336
|
+
suggestion = Suggestion(
|
|
337
|
+
suggestion_id=Suggestion.generate_id(
|
|
338
|
+
SuggestionType.DELEGATION, f"delegate:{next_agent}"
|
|
339
|
+
),
|
|
340
|
+
suggestion_type=SuggestionType.DELEGATION,
|
|
341
|
+
title=f"Consider delegating to {next_agent}",
|
|
342
|
+
description=(
|
|
343
|
+
f"Delegation chain '{' → '.join(pattern.agents)}' has "
|
|
344
|
+
f"{pattern.success_rate:.0f}% success rate."
|
|
345
|
+
),
|
|
346
|
+
confidence=pattern.confidence,
|
|
347
|
+
relevance=relevance,
|
|
348
|
+
action_code=self._generate_delegation_code(
|
|
349
|
+
next_agent, context
|
|
350
|
+
),
|
|
351
|
+
source_patterns=[pattern.pattern_id],
|
|
352
|
+
metadata={
|
|
353
|
+
"next_agent": next_agent,
|
|
354
|
+
"full_chain": pattern.agents,
|
|
355
|
+
"success_rate": pattern.success_rate,
|
|
356
|
+
},
|
|
357
|
+
)
|
|
358
|
+
suggestions.append(suggestion)
|
|
359
|
+
|
|
360
|
+
except Exception as e:
|
|
361
|
+
logger.warning(f"Error generating delegation suggestions: {e}")
|
|
362
|
+
|
|
363
|
+
return suggestions
|
|
364
|
+
|
|
365
|
+
def _suggest_from_error_patterns(self, context: TaskContext) -> list[Suggestion]:
|
|
366
|
+
"""
|
|
367
|
+
Generate error resolution suggestions from error patterns.
|
|
368
|
+
|
|
369
|
+
If recent errors match known patterns, suggest resolution strategies.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
context: Current task context
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
List of error resolution suggestions
|
|
376
|
+
"""
|
|
377
|
+
suggestions: list[Suggestion] = []
|
|
378
|
+
|
|
379
|
+
if not context.recent_errors:
|
|
380
|
+
return suggestions
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
# Get error patterns
|
|
384
|
+
patterns = self._pattern_detector.detect_error_patterns(
|
|
385
|
+
min_frequency=2, days_back=30
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
for error in context.recent_errors[-3:]: # Check last 3 errors
|
|
389
|
+
error_lower = error.lower()
|
|
390
|
+
|
|
391
|
+
for pattern in patterns:
|
|
392
|
+
if not isinstance(pattern, ErrorPattern):
|
|
393
|
+
continue
|
|
394
|
+
|
|
395
|
+
# Check if error matches pattern
|
|
396
|
+
if pattern.error_type in error_lower or any(
|
|
397
|
+
word in error_lower
|
|
398
|
+
for word in pattern.error_message_pattern.lower().split()
|
|
399
|
+
):
|
|
400
|
+
# Build resolution description
|
|
401
|
+
resolution_desc = ""
|
|
402
|
+
if pattern.resolution_strategies:
|
|
403
|
+
strategies = ", ".join(pattern.resolution_strategies[:3])
|
|
404
|
+
resolution_desc = f" Common resolutions: {strategies}."
|
|
405
|
+
|
|
406
|
+
suggestion = Suggestion(
|
|
407
|
+
suggestion_id=Suggestion.generate_id(
|
|
408
|
+
SuggestionType.ERROR_RESOLUTION,
|
|
409
|
+
f"error:{pattern.error_type}",
|
|
410
|
+
),
|
|
411
|
+
suggestion_type=SuggestionType.ERROR_RESOLUTION,
|
|
412
|
+
title=f"Resolve {pattern.error_type}",
|
|
413
|
+
description=(
|
|
414
|
+
f"This error pattern has occurred {pattern.frequency} times "
|
|
415
|
+
f"with {pattern.success_rate:.0f}% resolution rate.{resolution_desc}"
|
|
416
|
+
),
|
|
417
|
+
confidence=pattern.confidence,
|
|
418
|
+
relevance=0.8, # High relevance for matching errors
|
|
419
|
+
source_patterns=[pattern.pattern_id],
|
|
420
|
+
metadata={
|
|
421
|
+
"error_type": pattern.error_type,
|
|
422
|
+
"resolution_strategies": pattern.resolution_strategies,
|
|
423
|
+
"tool_context": pattern.tool_context,
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
suggestions.append(suggestion)
|
|
427
|
+
break # One suggestion per error
|
|
428
|
+
|
|
429
|
+
except Exception as e:
|
|
430
|
+
logger.warning(f"Error generating error resolution suggestions: {e}")
|
|
431
|
+
|
|
432
|
+
return suggestions
|
|
433
|
+
|
|
434
|
+
def _suggest_model_selection(self, context: TaskContext) -> list[Suggestion]:
|
|
435
|
+
"""
|
|
436
|
+
Suggest model selection based on task complexity.
|
|
437
|
+
|
|
438
|
+
Uses heuristics to determine if Haiku, Sonnet, or Opus is appropriate.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
context: Current task context
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
List of model selection suggestions
|
|
445
|
+
"""
|
|
446
|
+
suggestions: list[Suggestion] = []
|
|
447
|
+
|
|
448
|
+
task = context.current_task.lower()
|
|
449
|
+
|
|
450
|
+
# Simple heuristics for model selection
|
|
451
|
+
# Complex tasks that benefit from Opus
|
|
452
|
+
complex_indicators = [
|
|
453
|
+
"refactor",
|
|
454
|
+
"architecture",
|
|
455
|
+
"design",
|
|
456
|
+
"optimize",
|
|
457
|
+
"security",
|
|
458
|
+
"performance",
|
|
459
|
+
"complex",
|
|
460
|
+
"critical",
|
|
461
|
+
]
|
|
462
|
+
|
|
463
|
+
# Simple tasks suitable for Haiku
|
|
464
|
+
simple_indicators = [
|
|
465
|
+
"format",
|
|
466
|
+
"lint",
|
|
467
|
+
"typo",
|
|
468
|
+
"rename",
|
|
469
|
+
"move",
|
|
470
|
+
"copy",
|
|
471
|
+
"list",
|
|
472
|
+
"show",
|
|
473
|
+
"status",
|
|
474
|
+
]
|
|
475
|
+
|
|
476
|
+
is_complex = any(ind in task for ind in complex_indicators)
|
|
477
|
+
is_simple = any(ind in task for ind in simple_indicators)
|
|
478
|
+
|
|
479
|
+
current_model = context.model.lower()
|
|
480
|
+
|
|
481
|
+
if is_complex and "opus" not in current_model:
|
|
482
|
+
suggestion = Suggestion(
|
|
483
|
+
suggestion_id=Suggestion.generate_id(
|
|
484
|
+
SuggestionType.MODEL_SELECTION, "use:opus"
|
|
485
|
+
),
|
|
486
|
+
suggestion_type=SuggestionType.MODEL_SELECTION,
|
|
487
|
+
title="Consider using Opus for this task",
|
|
488
|
+
description=(
|
|
489
|
+
"This task appears complex and may benefit from Claude Opus's "
|
|
490
|
+
"enhanced reasoning capabilities."
|
|
491
|
+
),
|
|
492
|
+
confidence=0.6,
|
|
493
|
+
relevance=0.7,
|
|
494
|
+
metadata={"recommended_model": "claude-opus", "reason": "complex_task"},
|
|
495
|
+
)
|
|
496
|
+
suggestions.append(suggestion)
|
|
497
|
+
|
|
498
|
+
elif is_simple and "haiku" not in current_model:
|
|
499
|
+
suggestion = Suggestion(
|
|
500
|
+
suggestion_id=Suggestion.generate_id(
|
|
501
|
+
SuggestionType.MODEL_SELECTION, "use:haiku"
|
|
502
|
+
),
|
|
503
|
+
suggestion_type=SuggestionType.MODEL_SELECTION,
|
|
504
|
+
title="Consider using Haiku for this task",
|
|
505
|
+
description=(
|
|
506
|
+
"This task appears simple and Haiku could complete it faster "
|
|
507
|
+
"and more cost-effectively."
|
|
508
|
+
),
|
|
509
|
+
confidence=0.6,
|
|
510
|
+
relevance=0.6,
|
|
511
|
+
metadata={"recommended_model": "claude-haiku", "reason": "simple_task"},
|
|
512
|
+
)
|
|
513
|
+
suggestions.append(suggestion)
|
|
514
|
+
|
|
515
|
+
return suggestions
|
|
516
|
+
|
|
517
|
+
def rank(self, suggestions: list[Suggestion]) -> list[Suggestion]:
|
|
518
|
+
"""
|
|
519
|
+
Rank suggestions by score.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
suggestions: List of suggestions to rank
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Sorted list of suggestions (highest score first)
|
|
526
|
+
"""
|
|
527
|
+
return sorted(suggestions, key=lambda s: s.score(), reverse=True)
|
|
528
|
+
|
|
529
|
+
def explain(self, suggestion: Suggestion) -> str:
|
|
530
|
+
"""
|
|
531
|
+
Generate a human-readable explanation for a suggestion.
|
|
532
|
+
|
|
533
|
+
Args:
|
|
534
|
+
suggestion: Suggestion to explain
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
Explanation text
|
|
538
|
+
"""
|
|
539
|
+
explanation_parts = [
|
|
540
|
+
f"**{suggestion.title}**",
|
|
541
|
+
"",
|
|
542
|
+
f"{suggestion.description}",
|
|
543
|
+
"",
|
|
544
|
+
f"Confidence: {suggestion.confidence:.0%}",
|
|
545
|
+
f"Relevance: {suggestion.relevance:.0%}",
|
|
546
|
+
]
|
|
547
|
+
|
|
548
|
+
if suggestion.source_patterns:
|
|
549
|
+
explanation_parts.append(
|
|
550
|
+
f"Based on patterns: {', '.join(suggestion.source_patterns)}"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if suggestion.action_code:
|
|
554
|
+
explanation_parts.extend(
|
|
555
|
+
[
|
|
556
|
+
"",
|
|
557
|
+
"Suggested code:",
|
|
558
|
+
"```python",
|
|
559
|
+
suggestion.action_code,
|
|
560
|
+
"```",
|
|
561
|
+
]
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
return "\n".join(explanation_parts)
|
|
565
|
+
|
|
566
|
+
def generate_task_code(self, pattern: Pattern) -> str:
|
|
567
|
+
"""
|
|
568
|
+
Generate Task() code snippet from a pattern.
|
|
569
|
+
|
|
570
|
+
Creates executable code that applies the pattern.
|
|
571
|
+
|
|
572
|
+
Args:
|
|
573
|
+
pattern: Pattern to generate code from
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
Python code snippet for Task() call
|
|
577
|
+
"""
|
|
578
|
+
if isinstance(pattern, ToolSequencePattern):
|
|
579
|
+
return self._generate_sequence_task_code(pattern)
|
|
580
|
+
elif isinstance(pattern, DelegationChain):
|
|
581
|
+
return self._generate_chain_task_code(pattern)
|
|
582
|
+
elif isinstance(pattern, ErrorPattern):
|
|
583
|
+
return self._generate_error_resolution_code(pattern)
|
|
584
|
+
else:
|
|
585
|
+
return "# No code generation available for this pattern type"
|
|
586
|
+
|
|
587
|
+
def _generate_sequence_task_code(self, pattern: ToolSequencePattern) -> str:
|
|
588
|
+
"""Generate Task() code for tool sequence pattern."""
|
|
589
|
+
sequence_desc = " → ".join(pattern.sequence)
|
|
590
|
+
return f'''# Apply pattern: {sequence_desc}
|
|
591
|
+
# Success rate: {pattern.success_rate:.0f}%
|
|
592
|
+
Task(
|
|
593
|
+
prompt="""
|
|
594
|
+
Follow this successful workflow pattern:
|
|
595
|
+
{sequence_desc}
|
|
596
|
+
|
|
597
|
+
Execute each step in sequence, validating results before proceeding.
|
|
598
|
+
""",
|
|
599
|
+
max_tokens=8000
|
|
600
|
+
)'''
|
|
601
|
+
|
|
602
|
+
def _generate_chain_task_code(self, pattern: DelegationChain) -> str:
|
|
603
|
+
"""Generate Task() code for delegation chain pattern."""
|
|
604
|
+
if len(pattern.agents) < 2:
|
|
605
|
+
return "# Invalid delegation chain"
|
|
606
|
+
|
|
607
|
+
first_agent = pattern.agents[0]
|
|
608
|
+
second_agent = pattern.agents[1]
|
|
609
|
+
chain_desc = " → ".join(pattern.agents)
|
|
610
|
+
|
|
611
|
+
return f'''# Apply delegation chain: {chain_desc}
|
|
612
|
+
# Success rate: {pattern.success_rate:.0f}%
|
|
613
|
+
Task(
|
|
614
|
+
prompt="""
|
|
615
|
+
As {first_agent}, analyze the task and delegate to {second_agent}.
|
|
616
|
+
|
|
617
|
+
Successful chain pattern: {chain_desc}
|
|
618
|
+
""",
|
|
619
|
+
max_tokens=8000
|
|
620
|
+
)'''
|
|
621
|
+
|
|
622
|
+
def _generate_error_resolution_code(self, pattern: ErrorPattern) -> str:
|
|
623
|
+
"""Generate Task() code for error resolution pattern."""
|
|
624
|
+
strategies = (
|
|
625
|
+
", ".join(pattern.resolution_strategies[:3])
|
|
626
|
+
if pattern.resolution_strategies
|
|
627
|
+
else "analyze and fix"
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
return f'''# Resolve {pattern.error_type}
|
|
631
|
+
# Resolution rate: {pattern.success_rate:.0f}%
|
|
632
|
+
Task(
|
|
633
|
+
prompt="""
|
|
634
|
+
Resolve {pattern.error_type} error.
|
|
635
|
+
|
|
636
|
+
Common resolution strategies: {strategies}
|
|
637
|
+
|
|
638
|
+
Tools commonly used in context: {", ".join(pattern.tool_context[:3]) if pattern.tool_context else "various"}
|
|
639
|
+
""",
|
|
640
|
+
max_tokens=8000
|
|
641
|
+
)'''
|
|
642
|
+
|
|
643
|
+
def _generate_delegation_code(self, target_agent: str, context: TaskContext) -> str:
|
|
644
|
+
"""Generate Task() code for delegation suggestion."""
|
|
645
|
+
return f'''Task(
|
|
646
|
+
prompt="""
|
|
647
|
+
Delegate to {target_agent} agent for the following task:
|
|
648
|
+
{context.current_task[:200] if context.current_task else "Current task"}
|
|
649
|
+
""",
|
|
650
|
+
max_tokens=8000
|
|
651
|
+
)'''
|
|
652
|
+
|
|
653
|
+
def store_suggestion(self, suggestion: Suggestion, context: TaskContext) -> bool:
|
|
654
|
+
"""
|
|
655
|
+
Store a suggestion in the database for feedback tracking.
|
|
656
|
+
|
|
657
|
+
Args:
|
|
658
|
+
suggestion: Suggestion to store
|
|
659
|
+
context: Context in which suggestion was generated
|
|
660
|
+
|
|
661
|
+
Returns:
|
|
662
|
+
True if stored successfully, False otherwise
|
|
663
|
+
"""
|
|
664
|
+
conn = self._get_connection()
|
|
665
|
+
cursor = conn.cursor()
|
|
666
|
+
|
|
667
|
+
try:
|
|
668
|
+
cursor.execute(
|
|
669
|
+
"""
|
|
670
|
+
INSERT INTO delegation_suggestions
|
|
671
|
+
(suggestion_id, suggestion_type, session_id, title, description,
|
|
672
|
+
confidence, relevance, action_code, source_patterns, metadata,
|
|
673
|
+
created_at)
|
|
674
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
675
|
+
""",
|
|
676
|
+
(
|
|
677
|
+
suggestion.suggestion_id,
|
|
678
|
+
suggestion.suggestion_type.value,
|
|
679
|
+
context.session_id,
|
|
680
|
+
suggestion.title,
|
|
681
|
+
suggestion.description,
|
|
682
|
+
suggestion.confidence,
|
|
683
|
+
suggestion.relevance,
|
|
684
|
+
suggestion.action_code,
|
|
685
|
+
json.dumps(suggestion.source_patterns),
|
|
686
|
+
json.dumps(suggestion.metadata),
|
|
687
|
+
),
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
conn.commit()
|
|
691
|
+
return True
|
|
692
|
+
|
|
693
|
+
except sqlite3.Error as e:
|
|
694
|
+
logger.error(f"Error storing suggestion: {e}")
|
|
695
|
+
return False
|
|
696
|
+
|
|
697
|
+
def get_recent_suggestions(
|
|
698
|
+
self,
|
|
699
|
+
session_id: str,
|
|
700
|
+
limit: int = 10,
|
|
701
|
+
) -> list[Suggestion]:
|
|
702
|
+
"""
|
|
703
|
+
Get recent suggestions for a session.
|
|
704
|
+
|
|
705
|
+
Args:
|
|
706
|
+
session_id: Session to query
|
|
707
|
+
limit: Maximum number of suggestions
|
|
708
|
+
|
|
709
|
+
Returns:
|
|
710
|
+
List of recent suggestions
|
|
711
|
+
"""
|
|
712
|
+
conn = self._get_connection()
|
|
713
|
+
cursor = conn.cursor()
|
|
714
|
+
|
|
715
|
+
try:
|
|
716
|
+
cursor.execute(
|
|
717
|
+
"""
|
|
718
|
+
SELECT * FROM delegation_suggestions
|
|
719
|
+
WHERE session_id = ?
|
|
720
|
+
ORDER BY created_at DESC
|
|
721
|
+
LIMIT ?
|
|
722
|
+
""",
|
|
723
|
+
(session_id, limit),
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
suggestions = []
|
|
727
|
+
for row in cursor.fetchall():
|
|
728
|
+
suggestion = Suggestion(
|
|
729
|
+
suggestion_id=row["suggestion_id"],
|
|
730
|
+
suggestion_type=SuggestionType(row["suggestion_type"]),
|
|
731
|
+
title=row["title"],
|
|
732
|
+
description=row["description"],
|
|
733
|
+
confidence=row["confidence"],
|
|
734
|
+
relevance=row["relevance"],
|
|
735
|
+
action_code=row["action_code"],
|
|
736
|
+
source_patterns=json.loads(row["source_patterns"])
|
|
737
|
+
if row["source_patterns"]
|
|
738
|
+
else [],
|
|
739
|
+
metadata=json.loads(row["metadata"]) if row["metadata"] else {},
|
|
740
|
+
)
|
|
741
|
+
suggestions.append(suggestion)
|
|
742
|
+
|
|
743
|
+
return suggestions
|
|
744
|
+
|
|
745
|
+
except sqlite3.Error as e:
|
|
746
|
+
logger.error(f"Error retrieving suggestions: {e}")
|
|
747
|
+
return []
|