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
htmlgraph/learning.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Active Learning Persistence Module.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Bridges TranscriptAnalytics to the HtmlGraph for persistent learning.
|
|
|
5
7
|
Analyzes sessions and persists patterns, insights, and metrics to the graph.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from collections import Counter
|
|
11
12
|
from datetime import datetime
|
|
@@ -150,68 +151,86 @@ class LearningPersistence:
|
|
|
150
151
|
return health
|
|
151
152
|
|
|
152
153
|
def persist_patterns(self, min_count: int = 2) -> list[str]:
|
|
153
|
-
"""Detect and persist workflow patterns
|
|
154
|
+
"""Detect and persist workflow patterns IN SESSIONS (not as separate files).
|
|
155
|
+
|
|
156
|
+
This refactored version stores patterns inline within session HTML files
|
|
157
|
+
to avoid creating 2,890+ individual pattern files.
|
|
154
158
|
|
|
155
159
|
Args:
|
|
156
160
|
min_count: Minimum occurrences to persist a pattern
|
|
157
161
|
|
|
158
162
|
Returns:
|
|
159
|
-
List of
|
|
163
|
+
List of session IDs that had patterns updated
|
|
160
164
|
"""
|
|
161
|
-
# Collect tool sequences
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
# Collect tool sequences per session (not globally)
|
|
166
|
+
session_ids_updated: list[str] = []
|
|
167
|
+
|
|
164
168
|
for session in self.sdk.session_manager.session_converter.load_all():
|
|
165
|
-
if session.activity_log:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
for
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
pattern
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
now = datetime.now()
|
|
196
|
-
pattern = (
|
|
197
|
-
self.sdk.patterns.create(f"Pattern: {' -> '.join(seq)}")
|
|
198
|
-
.set_sequence(list(seq))
|
|
199
|
-
.set_pattern_type(pattern_type)
|
|
200
|
-
.set_detection_count(count)
|
|
201
|
-
.set_first_detected(now)
|
|
202
|
-
.set_last_detected(now)
|
|
203
|
-
.save()
|
|
169
|
+
if not session.activity_log:
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
# Extract 3-tool sequences from this session
|
|
173
|
+
tools = [
|
|
174
|
+
a.tool if not isinstance(a, dict) else a.get("tool", "")
|
|
175
|
+
for a in session.activity_log
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
# Count sequences in this session
|
|
179
|
+
sequences: list[tuple[Any, ...]] = []
|
|
180
|
+
for i in range(len(tools) - 2):
|
|
181
|
+
seq = tools[i : i + 3]
|
|
182
|
+
if all(seq): # No empty tools
|
|
183
|
+
sequences.append(tuple(seq))
|
|
184
|
+
|
|
185
|
+
seq_counts = Counter(sequences)
|
|
186
|
+
|
|
187
|
+
# Update session's detected_patterns
|
|
188
|
+
patterns_updated = False
|
|
189
|
+
for seq, count in seq_counts.items(): # type: ignore[assignment]
|
|
190
|
+
if count >= min_count:
|
|
191
|
+
# Check if pattern already exists in this session
|
|
192
|
+
existing = next(
|
|
193
|
+
(
|
|
194
|
+
p
|
|
195
|
+
for p in session.detected_patterns
|
|
196
|
+
if p.get("sequence") == list(seq)
|
|
197
|
+
),
|
|
198
|
+
None,
|
|
204
199
|
)
|
|
205
|
-
|
|
200
|
+
|
|
201
|
+
if existing:
|
|
202
|
+
# Update existing pattern
|
|
203
|
+
existing["detection_count"] = count
|
|
204
|
+
existing["last_detected"] = datetime.now().isoformat()
|
|
205
|
+
patterns_updated = True
|
|
206
|
+
else:
|
|
207
|
+
# Add new pattern to session
|
|
208
|
+
pattern_type = self._classify_pattern(list(seq))
|
|
209
|
+
now = datetime.now()
|
|
210
|
+
session.detected_patterns.append(
|
|
211
|
+
{
|
|
212
|
+
"sequence": list(seq),
|
|
213
|
+
"pattern_type": pattern_type,
|
|
214
|
+
"detection_count": count,
|
|
215
|
+
"first_detected": now.isoformat(),
|
|
216
|
+
"last_detected": now.isoformat(),
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
patterns_updated = True
|
|
220
|
+
|
|
221
|
+
# Save updated session if patterns were modified
|
|
222
|
+
if patterns_updated:
|
|
223
|
+
self.sdk.session_manager.session_converter.save(session)
|
|
224
|
+
session_ids_updated.append(session.id)
|
|
206
225
|
|
|
207
226
|
# Also persist parallel patterns
|
|
208
|
-
|
|
209
|
-
|
|
227
|
+
parallel_session_ids = self.persist_parallel_patterns(min_count=min_count)
|
|
228
|
+
session_ids_updated.extend(parallel_session_ids)
|
|
210
229
|
|
|
211
|
-
return
|
|
230
|
+
return session_ids_updated
|
|
212
231
|
|
|
213
232
|
def persist_parallel_patterns(self, min_count: int = 2) -> list[str]:
|
|
214
|
-
"""Detect and persist parallel execution patterns
|
|
233
|
+
"""Detect and persist parallel execution patterns IN SESSIONS.
|
|
215
234
|
|
|
216
235
|
Identifies when multiple tools are invoked in parallel (same parent_activity_id).
|
|
217
236
|
This is especially useful for detecting orchestrator patterns like parallel Task delegation.
|
|
@@ -220,12 +239,11 @@ class LearningPersistence:
|
|
|
220
239
|
min_count: Minimum occurrences to persist a pattern
|
|
221
240
|
|
|
222
241
|
Returns:
|
|
223
|
-
List of
|
|
242
|
+
List of session IDs that had parallel patterns updated
|
|
224
243
|
"""
|
|
225
244
|
from collections import defaultdict
|
|
226
245
|
|
|
227
|
-
|
|
228
|
-
parallel_patterns: list[tuple[str, ...]] = []
|
|
246
|
+
session_ids_updated: list[str] = []
|
|
229
247
|
|
|
230
248
|
for session in self.sdk.session_manager.session_converter.load_all():
|
|
231
249
|
if not session.activity_log:
|
|
@@ -242,12 +260,12 @@ class LearningPersistence:
|
|
|
242
260
|
if parent_id: # Only track activities with a parent
|
|
243
261
|
parent_groups[parent_id].append(activity)
|
|
244
262
|
|
|
245
|
-
#
|
|
263
|
+
# Collect parallel patterns for this session
|
|
264
|
+
parallel_patterns: list[tuple[str, ...]] = []
|
|
246
265
|
for parent_id, activities in parent_groups.items():
|
|
247
266
|
if len(activities) < 2:
|
|
248
267
|
continue
|
|
249
268
|
|
|
250
|
-
# Check if activities overlap in time (parallel execution)
|
|
251
269
|
# Sort by timestamp
|
|
252
270
|
sorted_activities = sorted(
|
|
253
271
|
activities,
|
|
@@ -268,50 +286,57 @@ class LearningPersistence:
|
|
|
268
286
|
if all(tools):
|
|
269
287
|
parallel_patterns.append(tools)
|
|
270
288
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
pattern.properties["parallel_count"] = len(tools)
|
|
291
|
-
pattern.properties["is_parallel"] = True
|
|
292
|
-
self.sdk.patterns.update(pattern)
|
|
293
|
-
pattern_ids.append(pattern.id)
|
|
294
|
-
else:
|
|
295
|
-
# Create new parallel pattern
|
|
296
|
-
pattern_type = self._classify_pattern(tool_names, is_parallel=True)
|
|
297
|
-
now = datetime.now()
|
|
298
|
-
pattern = (
|
|
299
|
-
self.sdk.patterns.create(pattern_name)
|
|
300
|
-
.set_sequence(tool_names)
|
|
301
|
-
.set_pattern_type(pattern_type)
|
|
302
|
-
.set_detection_count(count)
|
|
303
|
-
.set_first_detected(now)
|
|
304
|
-
.set_last_detected(now)
|
|
305
|
-
.save()
|
|
289
|
+
# Count parallel patterns in this session
|
|
290
|
+
pattern_counts = Counter(parallel_patterns)
|
|
291
|
+
|
|
292
|
+
# Update session's detected_patterns with parallel patterns
|
|
293
|
+
patterns_updated = False
|
|
294
|
+
for tools, count in pattern_counts.items():
|
|
295
|
+
if count >= min_count:
|
|
296
|
+
tool_names = list(tools)
|
|
297
|
+
|
|
298
|
+
# Check if pattern already exists in this session
|
|
299
|
+
# Parallel patterns have special naming: "Parallel[N]: tool1 || tool2"
|
|
300
|
+
existing = next(
|
|
301
|
+
(
|
|
302
|
+
p
|
|
303
|
+
for p in session.detected_patterns
|
|
304
|
+
if p.get("sequence") == tool_names
|
|
305
|
+
and p.get("is_parallel", False)
|
|
306
|
+
),
|
|
307
|
+
None,
|
|
306
308
|
)
|
|
307
|
-
# Mark as parallel in properties
|
|
308
|
-
pattern.properties = pattern.properties or {}
|
|
309
|
-
pattern.properties["parallel_count"] = len(tools)
|
|
310
|
-
pattern.properties["is_parallel"] = True
|
|
311
|
-
self.sdk.patterns.update(pattern)
|
|
312
|
-
pattern_ids.append(pattern.id)
|
|
313
309
|
|
|
314
|
-
|
|
310
|
+
if existing:
|
|
311
|
+
# Update existing parallel pattern
|
|
312
|
+
existing["detection_count"] = count
|
|
313
|
+
existing["last_detected"] = datetime.now().isoformat()
|
|
314
|
+
patterns_updated = True
|
|
315
|
+
else:
|
|
316
|
+
# Add new parallel pattern to session
|
|
317
|
+
pattern_type = self._classify_pattern(
|
|
318
|
+
tool_names, is_parallel=True
|
|
319
|
+
)
|
|
320
|
+
now = datetime.now()
|
|
321
|
+
session.detected_patterns.append(
|
|
322
|
+
{
|
|
323
|
+
"sequence": tool_names,
|
|
324
|
+
"pattern_type": pattern_type,
|
|
325
|
+
"detection_count": count,
|
|
326
|
+
"first_detected": now.isoformat(),
|
|
327
|
+
"last_detected": now.isoformat(),
|
|
328
|
+
"is_parallel": True,
|
|
329
|
+
"parallel_count": len(tools),
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
patterns_updated = True
|
|
333
|
+
|
|
334
|
+
# Save updated session if patterns were modified
|
|
335
|
+
if patterns_updated:
|
|
336
|
+
self.sdk.session_manager.session_converter.save(session)
|
|
337
|
+
session_ids_updated.append(session.id)
|
|
338
|
+
|
|
339
|
+
return session_ids_updated
|
|
315
340
|
|
|
316
341
|
def _classify_pattern(self, sequence: list[str], is_parallel: bool = False) -> str:
|
|
317
342
|
"""Classify a pattern as optimal, anti-pattern, or neutral.
|
|
@@ -329,17 +354,17 @@ class LearningPersistence:
|
|
|
329
354
|
if is_parallel:
|
|
330
355
|
# Parallel Task delegation is optimal (orchestrator pattern)
|
|
331
356
|
if all(tool == "Task" for tool in sequence) and len(sequence) >= 2:
|
|
332
|
-
return "
|
|
357
|
+
return "optimal"
|
|
333
358
|
# Mixed parallel operations can also be optimal
|
|
334
359
|
if "Task" in sequence:
|
|
335
|
-
return "
|
|
360
|
+
return "optimal"
|
|
336
361
|
# Other parallel patterns are neutral
|
|
337
362
|
return "neutral"
|
|
338
363
|
|
|
339
364
|
# Sequential anti-patterns for orchestrators
|
|
340
365
|
# Multiple sequential Tasks without parallelism is an anti-pattern
|
|
341
366
|
if seq == ("Task", "Task", "Task"):
|
|
342
|
-
return "
|
|
367
|
+
return "anti-pattern"
|
|
343
368
|
|
|
344
369
|
# Known optimal patterns (sequential)
|
|
345
370
|
optimal = [
|
htmlgraph/mcp_server.py
CHANGED