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,487 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Tool Execution Traces Collection
|
|
9
|
+
|
|
10
|
+
Provides query interface for tool execution traces stored in tool_traces table.
|
|
11
|
+
Enables analysis of tool performance, error patterns, and execution hierarchies.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
>>> from htmlgraph.sdk import SDK
|
|
15
|
+
>>> sdk = SDK(agent="claude")
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Get traces for current session
|
|
18
|
+
>>> traces = sdk.traces.get_traces(session_id="sess-abc123")
|
|
19
|
+
>>> for trace in traces:
|
|
20
|
+
... logger.info(f"{trace.tool_name}: {trace.duration_ms}ms")
|
|
21
|
+
>>>
|
|
22
|
+
>>> # Find slow tool calls
|
|
23
|
+
>>> slow = sdk.traces.get_slow_traces(threshold_ms=1000)
|
|
24
|
+
>>>
|
|
25
|
+
>>> # Get hierarchical view (parent-child relationships)
|
|
26
|
+
>>> tree = sdk.traces.get_trace_tree(trace_id="trace-xyz")
|
|
27
|
+
>>> logger.info(f"Root: {tree.root.tool_name}")
|
|
28
|
+
>>> logger.info(f"Children: {len(tree.children)}")
|
|
29
|
+
>>>
|
|
30
|
+
>>> # Get error traces for debugging
|
|
31
|
+
>>> errors = sdk.traces.get_error_traces(session_id="sess-abc123")
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
from dataclasses import dataclass
|
|
36
|
+
from datetime import datetime, timezone
|
|
37
|
+
from typing import TYPE_CHECKING
|
|
38
|
+
|
|
39
|
+
from htmlgraph.db.schema import HtmlGraphDB
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from htmlgraph.sdk import SDK
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class TraceRecord:
|
|
47
|
+
"""
|
|
48
|
+
Single tool execution trace.
|
|
49
|
+
|
|
50
|
+
Represents one complete execution of a tool with timing, input, output, and status.
|
|
51
|
+
Parent-child relationships via parent_tool_use_id enable hierarchical analysis.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
tool_use_id: Unique identifier for this tool execution (UUID v4)
|
|
55
|
+
trace_id: Parent trace ID for grouping related executions
|
|
56
|
+
session_id: Session this execution belongs to
|
|
57
|
+
tool_name: Name of the tool executed (e.g., "Bash", "Read", "Write")
|
|
58
|
+
tool_input: Input parameters passed to the tool (dict, may be None)
|
|
59
|
+
tool_output: Result returned by the tool (dict, may be None if not yet complete)
|
|
60
|
+
start_time: When execution started (UTC datetime)
|
|
61
|
+
end_time: When execution ended (UTC datetime, None if still running)
|
|
62
|
+
duration_ms: Milliseconds to complete (None if still running or error)
|
|
63
|
+
status: Execution status (started, completed, failed, timeout, cancelled)
|
|
64
|
+
error_message: Error details if status is 'failed'
|
|
65
|
+
parent_tool_use_id: tool_use_id of parent tool if nested (None if top-level)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
tool_use_id: str
|
|
69
|
+
trace_id: str
|
|
70
|
+
session_id: str
|
|
71
|
+
tool_name: str
|
|
72
|
+
tool_input: dict | None
|
|
73
|
+
tool_output: dict | None
|
|
74
|
+
start_time: datetime
|
|
75
|
+
end_time: datetime | None
|
|
76
|
+
duration_ms: int | None
|
|
77
|
+
status: str | None
|
|
78
|
+
error_message: str | None
|
|
79
|
+
parent_tool_use_id: str | None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class TraceTree:
|
|
84
|
+
"""
|
|
85
|
+
Hierarchical view of traces (parent-child relationships).
|
|
86
|
+
|
|
87
|
+
Enables analysis of nested tool executions where one tool invokes others.
|
|
88
|
+
Example: Bash tool calls may invoke other tools as nested executions.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
root: Root trace record (this execution)
|
|
92
|
+
children: List of child traces (tools invoked by this tool)
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
root: TraceRecord
|
|
96
|
+
children: list[TraceTree]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TraceCollection:
|
|
100
|
+
"""
|
|
101
|
+
Query interface for tool execution traces.
|
|
102
|
+
|
|
103
|
+
Provides methods to retrieve, filter, and analyze tool execution traces
|
|
104
|
+
stored in the tool_traces database table. Supports querying by session,
|
|
105
|
+
tool name, performance thresholds, and error status.
|
|
106
|
+
|
|
107
|
+
All queries return data sorted by start_time DESC (newest first).
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> sdk = SDK(agent="claude")
|
|
111
|
+
>>> traces = sdk.traces
|
|
112
|
+
>>>
|
|
113
|
+
>>> # Single trace
|
|
114
|
+
>>> trace = traces.get_trace("tool-use-id-123")
|
|
115
|
+
>>>
|
|
116
|
+
>>> # All traces for session
|
|
117
|
+
>>> all_traces = traces.get_traces("sess-123")
|
|
118
|
+
>>>
|
|
119
|
+
>>> # By tool name
|
|
120
|
+
>>> bash_traces = traces.get_traces_by_tool("Bash")
|
|
121
|
+
>>>
|
|
122
|
+
>>> # Performance analysis
|
|
123
|
+
>>> slow = traces.get_slow_traces(threshold_ms=1000)
|
|
124
|
+
>>> errors = traces.get_error_traces("sess-123")
|
|
125
|
+
>>>
|
|
126
|
+
>>> # Hierarchical view
|
|
127
|
+
>>> tree = traces.get_trace_tree("trace-id-123")
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, sdk: SDK):
|
|
131
|
+
"""
|
|
132
|
+
Initialize traces collection.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
sdk: Parent SDK instance
|
|
136
|
+
"""
|
|
137
|
+
self._sdk = sdk
|
|
138
|
+
self._db = HtmlGraphDB()
|
|
139
|
+
|
|
140
|
+
def _row_to_trace(self, row: tuple) -> TraceRecord:
|
|
141
|
+
"""
|
|
142
|
+
Convert database row to TraceRecord dataclass.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
row: SQLite row tuple from tool_traces query
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
TraceRecord with parsed fields
|
|
149
|
+
"""
|
|
150
|
+
import json
|
|
151
|
+
|
|
152
|
+
# Unpack tuple: (tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
153
|
+
# tool_output, start_time, end_time, duration_ms, status,
|
|
154
|
+
# error_message, parent_tool_use_id)
|
|
155
|
+
(
|
|
156
|
+
tool_use_id,
|
|
157
|
+
trace_id,
|
|
158
|
+
session_id,
|
|
159
|
+
tool_name,
|
|
160
|
+
tool_input_json,
|
|
161
|
+
tool_output_json,
|
|
162
|
+
start_time_iso,
|
|
163
|
+
end_time_iso,
|
|
164
|
+
duration_ms,
|
|
165
|
+
status,
|
|
166
|
+
error_message,
|
|
167
|
+
parent_tool_use_id,
|
|
168
|
+
) = row
|
|
169
|
+
|
|
170
|
+
# Parse JSON fields
|
|
171
|
+
tool_input = None
|
|
172
|
+
if tool_input_json:
|
|
173
|
+
try:
|
|
174
|
+
tool_input = json.loads(tool_input_json)
|
|
175
|
+
except (json.JSONDecodeError, TypeError):
|
|
176
|
+
tool_input = None
|
|
177
|
+
|
|
178
|
+
tool_output = None
|
|
179
|
+
if tool_output_json:
|
|
180
|
+
try:
|
|
181
|
+
tool_output = json.loads(tool_output_json)
|
|
182
|
+
except (json.JSONDecodeError, TypeError):
|
|
183
|
+
tool_output = None
|
|
184
|
+
|
|
185
|
+
# Parse timestamps
|
|
186
|
+
start_time: datetime | None = None
|
|
187
|
+
if start_time_iso:
|
|
188
|
+
try:
|
|
189
|
+
start_time = datetime.fromisoformat(
|
|
190
|
+
start_time_iso.replace("Z", "+00:00")
|
|
191
|
+
)
|
|
192
|
+
except (ValueError, AttributeError):
|
|
193
|
+
start_time = None
|
|
194
|
+
|
|
195
|
+
end_time: datetime | None = None
|
|
196
|
+
if end_time_iso:
|
|
197
|
+
try:
|
|
198
|
+
end_time = datetime.fromisoformat(end_time_iso.replace("Z", "+00:00"))
|
|
199
|
+
except (ValueError, AttributeError):
|
|
200
|
+
end_time = None
|
|
201
|
+
|
|
202
|
+
# Use current time if start_time is missing
|
|
203
|
+
if start_time is None:
|
|
204
|
+
start_time = datetime.now(timezone.utc)
|
|
205
|
+
|
|
206
|
+
return TraceRecord(
|
|
207
|
+
tool_use_id=tool_use_id,
|
|
208
|
+
trace_id=trace_id,
|
|
209
|
+
session_id=session_id,
|
|
210
|
+
tool_name=tool_name,
|
|
211
|
+
tool_input=tool_input,
|
|
212
|
+
tool_output=tool_output,
|
|
213
|
+
start_time=start_time,
|
|
214
|
+
end_time=end_time,
|
|
215
|
+
duration_ms=duration_ms,
|
|
216
|
+
status=status,
|
|
217
|
+
error_message=error_message,
|
|
218
|
+
parent_tool_use_id=parent_tool_use_id,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def get_trace(self, tool_use_id: str) -> TraceRecord | None:
|
|
222
|
+
"""
|
|
223
|
+
Get single trace by tool_use_id.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
tool_use_id: Unique tool execution ID (UUID v4)
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
TraceRecord if found, None otherwise
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
if not self._db.connection:
|
|
233
|
+
self._db.connect()
|
|
234
|
+
|
|
235
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
236
|
+
cursor.execute(
|
|
237
|
+
"""
|
|
238
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
239
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
240
|
+
error_message, parent_tool_use_id
|
|
241
|
+
FROM tool_traces
|
|
242
|
+
WHERE tool_use_id = ?
|
|
243
|
+
""",
|
|
244
|
+
(tool_use_id,),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
row = cursor.fetchone()
|
|
248
|
+
if row:
|
|
249
|
+
return self._row_to_trace(row)
|
|
250
|
+
|
|
251
|
+
return None
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.info(f"Error getting trace {tool_use_id}: {e}")
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
def get_traces(
|
|
257
|
+
self,
|
|
258
|
+
session_id: str,
|
|
259
|
+
limit: int = 100,
|
|
260
|
+
start_time: datetime | None = None,
|
|
261
|
+
) -> list[TraceRecord]:
|
|
262
|
+
"""
|
|
263
|
+
Get traces for a session, ordered by start_time DESC (newest first).
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
session_id: Session to query
|
|
267
|
+
limit: Maximum traces to return (default 100)
|
|
268
|
+
start_time: Optional filter - only traces after this time
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
List of TraceRecord objects, newest first
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
if not self._db.connection:
|
|
275
|
+
self._db.connect()
|
|
276
|
+
|
|
277
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
278
|
+
|
|
279
|
+
if start_time:
|
|
280
|
+
start_time_iso = start_time.isoformat()
|
|
281
|
+
cursor.execute(
|
|
282
|
+
"""
|
|
283
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
284
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
285
|
+
error_message, parent_tool_use_id
|
|
286
|
+
FROM tool_traces
|
|
287
|
+
WHERE session_id = ? AND start_time >= ?
|
|
288
|
+
ORDER BY start_time DESC
|
|
289
|
+
LIMIT ?
|
|
290
|
+
""",
|
|
291
|
+
(session_id, start_time_iso, limit),
|
|
292
|
+
)
|
|
293
|
+
else:
|
|
294
|
+
cursor.execute(
|
|
295
|
+
"""
|
|
296
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
297
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
298
|
+
error_message, parent_tool_use_id
|
|
299
|
+
FROM tool_traces
|
|
300
|
+
WHERE session_id = ?
|
|
301
|
+
ORDER BY start_time DESC
|
|
302
|
+
LIMIT ?
|
|
303
|
+
""",
|
|
304
|
+
(session_id, limit),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
rows = cursor.fetchall()
|
|
308
|
+
return [self._row_to_trace(row) for row in rows]
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.info(f"Error getting traces for session {session_id}: {e}")
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
def get_traces_by_tool(self, tool_name: str, limit: int = 100) -> list[TraceRecord]:
|
|
314
|
+
"""
|
|
315
|
+
Get traces for specific tool name.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
tool_name: Name of the tool (e.g., "Bash", "Read", "Write")
|
|
319
|
+
limit: Maximum traces to return (default 100)
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
List of TraceRecord objects, newest first
|
|
323
|
+
"""
|
|
324
|
+
try:
|
|
325
|
+
if not self._db.connection:
|
|
326
|
+
self._db.connect()
|
|
327
|
+
|
|
328
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
329
|
+
cursor.execute(
|
|
330
|
+
"""
|
|
331
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
332
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
333
|
+
error_message, parent_tool_use_id
|
|
334
|
+
FROM tool_traces
|
|
335
|
+
WHERE tool_name = ?
|
|
336
|
+
ORDER BY start_time DESC
|
|
337
|
+
LIMIT ?
|
|
338
|
+
""",
|
|
339
|
+
(tool_name, limit),
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
rows = cursor.fetchall()
|
|
343
|
+
return [self._row_to_trace(row) for row in rows]
|
|
344
|
+
except Exception as e:
|
|
345
|
+
logger.info(f"Error getting traces for tool {tool_name}: {e}")
|
|
346
|
+
return []
|
|
347
|
+
|
|
348
|
+
def get_trace_tree(self, trace_id: str) -> TraceTree | None:
|
|
349
|
+
"""
|
|
350
|
+
Get hierarchical view with parent-child relationships.
|
|
351
|
+
|
|
352
|
+
Recursively builds a tree of traces where each node can have children
|
|
353
|
+
(tools invoked by that tool). Useful for understanding nested execution.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
trace_id: Root trace_id to build tree from
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
TraceTree with root and children, or None if not found
|
|
360
|
+
"""
|
|
361
|
+
try:
|
|
362
|
+
if not self._db.connection:
|
|
363
|
+
self._db.connect()
|
|
364
|
+
|
|
365
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
366
|
+
|
|
367
|
+
# Get root trace
|
|
368
|
+
cursor.execute(
|
|
369
|
+
"""
|
|
370
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
371
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
372
|
+
error_message, parent_tool_use_id
|
|
373
|
+
FROM tool_traces
|
|
374
|
+
WHERE trace_id = ?
|
|
375
|
+
ORDER BY start_time DESC
|
|
376
|
+
LIMIT 1
|
|
377
|
+
""",
|
|
378
|
+
(trace_id,),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
root_row = cursor.fetchone()
|
|
382
|
+
if not root_row:
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
root = self._row_to_trace(root_row)
|
|
386
|
+
|
|
387
|
+
# Recursively get children
|
|
388
|
+
def build_tree(parent_trace: TraceRecord) -> TraceTree:
|
|
389
|
+
cursor.execute(
|
|
390
|
+
"""
|
|
391
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
392
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
393
|
+
error_message, parent_tool_use_id
|
|
394
|
+
FROM tool_traces
|
|
395
|
+
WHERE parent_tool_use_id = ?
|
|
396
|
+
ORDER BY start_time ASC
|
|
397
|
+
""",
|
|
398
|
+
(parent_trace.tool_use_id,),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
child_rows = cursor.fetchall()
|
|
402
|
+
children = []
|
|
403
|
+
for child_row in child_rows:
|
|
404
|
+
child = self._row_to_trace(child_row)
|
|
405
|
+
children.append(build_tree(child))
|
|
406
|
+
|
|
407
|
+
return TraceTree(root=parent_trace, children=children)
|
|
408
|
+
|
|
409
|
+
return build_tree(root)
|
|
410
|
+
except Exception as e:
|
|
411
|
+
logger.info(f"Error getting trace tree for {trace_id}: {e}")
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
def get_slow_traces(self, threshold_ms: int, limit: int = 100) -> list[TraceRecord]:
|
|
415
|
+
"""
|
|
416
|
+
Find traces exceeding duration threshold.
|
|
417
|
+
|
|
418
|
+
Useful for identifying performance bottlenecks.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
threshold_ms: Minimum duration in milliseconds
|
|
422
|
+
limit: Maximum traces to return (default 100)
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
List of slow TraceRecord objects, slowest first
|
|
426
|
+
"""
|
|
427
|
+
try:
|
|
428
|
+
if not self._db.connection:
|
|
429
|
+
self._db.connect()
|
|
430
|
+
|
|
431
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
432
|
+
cursor.execute(
|
|
433
|
+
"""
|
|
434
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
435
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
436
|
+
error_message, parent_tool_use_id
|
|
437
|
+
FROM tool_traces
|
|
438
|
+
WHERE duration_ms IS NOT NULL AND duration_ms >= ?
|
|
439
|
+
ORDER BY duration_ms DESC
|
|
440
|
+
LIMIT ?
|
|
441
|
+
""",
|
|
442
|
+
(threshold_ms, limit),
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
rows = cursor.fetchall()
|
|
446
|
+
return [self._row_to_trace(row) for row in rows]
|
|
447
|
+
except Exception as e:
|
|
448
|
+
logger.info(f"Error getting slow traces: {e}")
|
|
449
|
+
return []
|
|
450
|
+
|
|
451
|
+
def get_error_traces(self, session_id: str, limit: int = 100) -> list[TraceRecord]:
|
|
452
|
+
"""
|
|
453
|
+
Get traces with errors/failures.
|
|
454
|
+
|
|
455
|
+
Filters for traces with status='failed' or error_message is not null.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
session_id: Session to query
|
|
459
|
+
limit: Maximum traces to return (default 100)
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
List of error TraceRecord objects, newest first
|
|
463
|
+
"""
|
|
464
|
+
try:
|
|
465
|
+
if not self._db.connection:
|
|
466
|
+
self._db.connect()
|
|
467
|
+
|
|
468
|
+
cursor = self._db.connection.cursor() # type: ignore[union-attr]
|
|
469
|
+
cursor.execute(
|
|
470
|
+
"""
|
|
471
|
+
SELECT tool_use_id, trace_id, session_id, tool_name, tool_input,
|
|
472
|
+
tool_output, start_time, end_time, duration_ms, status,
|
|
473
|
+
error_message, parent_tool_use_id
|
|
474
|
+
FROM tool_traces
|
|
475
|
+
WHERE session_id = ? AND (status IN ('failed', 'timeout', 'cancelled')
|
|
476
|
+
OR error_message IS NOT NULL)
|
|
477
|
+
ORDER BY start_time DESC
|
|
478
|
+
LIMIT ?
|
|
479
|
+
""",
|
|
480
|
+
(session_id, limit),
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
rows = cursor.fetchall()
|
|
484
|
+
return [self._row_to_trace(row) for row in rows]
|
|
485
|
+
except Exception as e:
|
|
486
|
+
logger.info(f"Error getting error traces: {e}")
|
|
487
|
+
return []
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Model pricing configuration for cost tracking",
|
|
3
|
+
"last_updated": "2026-01-16",
|
|
4
|
+
"models": {
|
|
5
|
+
"claude-haiku-4-5-20251001": {
|
|
6
|
+
"name": "Claude Haiku",
|
|
7
|
+
"display_name": "Haiku 4.5",
|
|
8
|
+
"input_cost_per_mtok": 0.80,
|
|
9
|
+
"output_cost_per_mtok": 4.00,
|
|
10
|
+
"description": "Fast, efficient model for routine tasks"
|
|
11
|
+
},
|
|
12
|
+
"claude-sonnet-4-20250514": {
|
|
13
|
+
"name": "Claude Sonnet",
|
|
14
|
+
"display_name": "Sonnet 4",
|
|
15
|
+
"input_cost_per_mtok": 3.00,
|
|
16
|
+
"output_cost_per_mtok": 15.00,
|
|
17
|
+
"description": "Balanced model for most tasks"
|
|
18
|
+
},
|
|
19
|
+
"claude-opus-4-1-20250805": {
|
|
20
|
+
"name": "Claude Opus",
|
|
21
|
+
"display_name": "Opus 4.1",
|
|
22
|
+
"input_cost_per_mtok": 15.00,
|
|
23
|
+
"output_cost_per_mtok": 75.00,
|
|
24
|
+
"description": "Most capable model for complex reasoning"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"gemini_models": {
|
|
28
|
+
"gemini-2.0-flash": {
|
|
29
|
+
"name": "Gemini 2.0 Flash",
|
|
30
|
+
"input_cost_per_mtok": 0.075,
|
|
31
|
+
"output_cost_per_mtok": 0.30,
|
|
32
|
+
"description": "Fast multimodal model"
|
|
33
|
+
},
|
|
34
|
+
"gemini-2.0-pro": {
|
|
35
|
+
"name": "Gemini 2.0 Pro",
|
|
36
|
+
"input_cost_per_mtok": 1.50,
|
|
37
|
+
"output_cost_per_mtok": 6.00,
|
|
38
|
+
"description": "Advanced reasoning model"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"defaults": {
|
|
42
|
+
"input_cost_per_mtok": 2.00,
|
|
43
|
+
"output_cost_per_mtok": 10.00
|
|
44
|
+
},
|
|
45
|
+
"alert_thresholds": {
|
|
46
|
+
"budget_warning_percent": 80,
|
|
47
|
+
"budget_critical_percent": 95,
|
|
48
|
+
"model_overage_multiplier": 1.5,
|
|
49
|
+
"trajectory_overage_multiplier": 1.3
|
|
50
|
+
},
|
|
51
|
+
"tracking": {
|
|
52
|
+
"accuracy_target_percent": 5,
|
|
53
|
+
"cost_calculation_method": "token_based",
|
|
54
|
+
"rounding_precision": 4
|
|
55
|
+
}
|
|
56
|
+
}
|