htmlgraph 0.9.3__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 +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- 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 +192 -100
- 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 +190 -14
- htmlgraph/analytics_index.py +135 -51
- 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 +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- 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 +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- 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 +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- 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 +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- 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 +45 -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 +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- 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 +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- 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/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- 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/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- 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 +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CostReporter for OTEL ROI Analysis - Phase 1.
|
|
3
|
+
|
|
4
|
+
Generates interactive HTML dashboards showing cost analysis of Task delegations.
|
|
5
|
+
Creates visualizations of delegation costs, ROI, and optimization recommendations.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Top 10 most expensive Task delegations
|
|
9
|
+
- Cost breakdown by subagent type and tool type
|
|
10
|
+
- ROI analysis comparing delegation vs direct execution
|
|
11
|
+
- Interactive charts using Chart.js
|
|
12
|
+
- Dark theme matching HtmlGraph visual style
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
from htmlgraph.analytics.cost_analyzer import CostAnalyzer
|
|
16
|
+
from htmlgraph.analytics.cost_reporter import CostReporter
|
|
17
|
+
|
|
18
|
+
analyzer = CostAnalyzer()
|
|
19
|
+
reporter = CostReporter()
|
|
20
|
+
html = reporter.generate_dashboard(analyzer)
|
|
21
|
+
reporter.save_dashboard(html, "cost-analysis.html")
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from htmlgraph.analytics.cost_analyzer import CostAnalyzer
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CostReporter:
|
|
33
|
+
"""Generate interactive HTML dashboards from cost analysis data."""
|
|
34
|
+
|
|
35
|
+
THEME = {
|
|
36
|
+
"bg_primary": "#1a1a2e",
|
|
37
|
+
"bg_secondary": "#16213e",
|
|
38
|
+
"text_primary": "#e0e0e0",
|
|
39
|
+
"text_secondary": "#b0b0b0",
|
|
40
|
+
"accent_primary": "#0f3460",
|
|
41
|
+
"accent_secondary": "#e94560",
|
|
42
|
+
"success": "#4caf50",
|
|
43
|
+
"warning": "#ff9800",
|
|
44
|
+
"error": "#f44336",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
"""Initialize CostReporter."""
|
|
49
|
+
self.generated_at = datetime.now()
|
|
50
|
+
|
|
51
|
+
def generate_dashboard(self, analyzer: CostAnalyzer) -> str:
|
|
52
|
+
"""
|
|
53
|
+
Generate complete interactive HTML dashboard.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
analyzer: CostAnalyzer instance with cost data
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Complete HTML document as string
|
|
60
|
+
"""
|
|
61
|
+
# Gather data
|
|
62
|
+
cost_breakdown = analyzer.get_cost_breakdown()
|
|
63
|
+
roi_stats = analyzer.get_roi_stats()
|
|
64
|
+
top_delegations = analyzer.get_top_delegations(10)
|
|
65
|
+
all_delegations = analyzer.get_task_delegations_with_costs()
|
|
66
|
+
|
|
67
|
+
# Build dashboard components
|
|
68
|
+
html_parts = [
|
|
69
|
+
self._html_header(),
|
|
70
|
+
self._html_styles(),
|
|
71
|
+
self._html_body_open(),
|
|
72
|
+
self._html_header_section(roi_stats),
|
|
73
|
+
self._html_summary_cards(roi_stats, cost_breakdown),
|
|
74
|
+
self._html_charts_section(cost_breakdown, all_delegations),
|
|
75
|
+
self._html_top_delegations_table(top_delegations),
|
|
76
|
+
self._html_insights_section(roi_stats, cost_breakdown),
|
|
77
|
+
self._html_footer_section(),
|
|
78
|
+
self._html_body_close(),
|
|
79
|
+
self._html_scripts(cost_breakdown, all_delegations),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
return "\n".join(html_parts)
|
|
83
|
+
|
|
84
|
+
def save_dashboard(self, html: str, path: str | Path) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Save HTML dashboard to file.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
html: HTML content to save
|
|
90
|
+
path: File path to save to
|
|
91
|
+
"""
|
|
92
|
+
path = Path(path)
|
|
93
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
path.write_text(html, encoding="utf-8")
|
|
95
|
+
|
|
96
|
+
# ===== HTML Generation Methods =====
|
|
97
|
+
|
|
98
|
+
def _html_header(self) -> str:
|
|
99
|
+
"""Generate HTML document header."""
|
|
100
|
+
return """<!DOCTYPE html>
|
|
101
|
+
<html lang="en">
|
|
102
|
+
<head>
|
|
103
|
+
<meta charset="UTF-8">
|
|
104
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
105
|
+
<title>Cost Attribution Dashboard - HtmlGraph OTEL ROI Analysis</title>
|
|
106
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
|
|
107
|
+
</head>"""
|
|
108
|
+
|
|
109
|
+
def _html_styles(self) -> str:
|
|
110
|
+
"""Generate CSS styles."""
|
|
111
|
+
return f"""<style>
|
|
112
|
+
* {{
|
|
113
|
+
margin: 0;
|
|
114
|
+
padding: 0;
|
|
115
|
+
box-sizing: border-box;
|
|
116
|
+
}}
|
|
117
|
+
|
|
118
|
+
body {{
|
|
119
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
120
|
+
background-color: {self.THEME["bg_primary"]};
|
|
121
|
+
color: {self.THEME["text_primary"]};
|
|
122
|
+
line-height: 1.6;
|
|
123
|
+
}}
|
|
124
|
+
|
|
125
|
+
a {{
|
|
126
|
+
color: {self.THEME["accent_secondary"]};
|
|
127
|
+
text-decoration: none;
|
|
128
|
+
}}
|
|
129
|
+
|
|
130
|
+
a:hover {{
|
|
131
|
+
text-decoration: underline;
|
|
132
|
+
}}
|
|
133
|
+
|
|
134
|
+
.container {{
|
|
135
|
+
max-width: 1400px;
|
|
136
|
+
margin: 0 auto;
|
|
137
|
+
padding: 20px;
|
|
138
|
+
}}
|
|
139
|
+
|
|
140
|
+
.header {{
|
|
141
|
+
border-bottom: 2px solid {self.THEME["accent_primary"]};
|
|
142
|
+
padding-bottom: 20px;
|
|
143
|
+
margin-bottom: 30px;
|
|
144
|
+
}}
|
|
145
|
+
|
|
146
|
+
.header h1 {{
|
|
147
|
+
font-size: 2.5em;
|
|
148
|
+
margin-bottom: 10px;
|
|
149
|
+
color: {self.THEME["text_primary"]};
|
|
150
|
+
}}
|
|
151
|
+
|
|
152
|
+
.header .subtitle {{
|
|
153
|
+
color: {self.THEME["text_secondary"]};
|
|
154
|
+
font-size: 0.95em;
|
|
155
|
+
}}
|
|
156
|
+
|
|
157
|
+
.header-meta {{
|
|
158
|
+
margin-top: 15px;
|
|
159
|
+
display: flex;
|
|
160
|
+
gap: 20px;
|
|
161
|
+
flex-wrap: wrap;
|
|
162
|
+
font-size: 0.9em;
|
|
163
|
+
color: {self.THEME["text_secondary"]};
|
|
164
|
+
}}
|
|
165
|
+
|
|
166
|
+
.cards {{
|
|
167
|
+
display: grid;
|
|
168
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
169
|
+
gap: 20px;
|
|
170
|
+
margin-bottom: 40px;
|
|
171
|
+
}}
|
|
172
|
+
|
|
173
|
+
.card {{
|
|
174
|
+
background-color: {self.THEME["bg_secondary"]};
|
|
175
|
+
border: 1px solid {self.THEME["accent_primary"]};
|
|
176
|
+
border-radius: 8px;
|
|
177
|
+
padding: 25px;
|
|
178
|
+
transition: all 0.3s ease;
|
|
179
|
+
}}
|
|
180
|
+
|
|
181
|
+
.card:hover {{
|
|
182
|
+
border-color: {self.THEME["accent_secondary"]};
|
|
183
|
+
transform: translateY(-5px);
|
|
184
|
+
box-shadow: 0 10px 30px rgba(233, 69, 96, 0.2);
|
|
185
|
+
}}
|
|
186
|
+
|
|
187
|
+
.card-label {{
|
|
188
|
+
color: {self.THEME["text_secondary"]};
|
|
189
|
+
font-size: 0.85em;
|
|
190
|
+
text-transform: uppercase;
|
|
191
|
+
letter-spacing: 0.5px;
|
|
192
|
+
margin-bottom: 10px;
|
|
193
|
+
}}
|
|
194
|
+
|
|
195
|
+
.card-value {{
|
|
196
|
+
font-size: 2.5em;
|
|
197
|
+
font-weight: bold;
|
|
198
|
+
margin-bottom: 8px;
|
|
199
|
+
color: {self.THEME["text_primary"]};
|
|
200
|
+
}}
|
|
201
|
+
|
|
202
|
+
.card-unit {{
|
|
203
|
+
color: {self.THEME["text_secondary"]};
|
|
204
|
+
font-size: 0.9em;
|
|
205
|
+
font-weight: normal;
|
|
206
|
+
}}
|
|
207
|
+
|
|
208
|
+
.card-meta {{
|
|
209
|
+
color: {self.THEME["text_secondary"]};
|
|
210
|
+
font-size: 0.85em;
|
|
211
|
+
margin-top: 12px;
|
|
212
|
+
padding-top: 12px;
|
|
213
|
+
border-top: 1px solid {self.THEME["accent_primary"]};
|
|
214
|
+
}}
|
|
215
|
+
|
|
216
|
+
.status-good {{
|
|
217
|
+
color: {self.THEME["success"]};
|
|
218
|
+
}}
|
|
219
|
+
|
|
220
|
+
.status-warning {{
|
|
221
|
+
color: {self.THEME["warning"]};
|
|
222
|
+
}}
|
|
223
|
+
|
|
224
|
+
.status-error {{
|
|
225
|
+
color: {self.THEME["error"]};
|
|
226
|
+
}}
|
|
227
|
+
|
|
228
|
+
.charts {{
|
|
229
|
+
display: grid;
|
|
230
|
+
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
231
|
+
gap: 30px;
|
|
232
|
+
margin-bottom: 40px;
|
|
233
|
+
}}
|
|
234
|
+
|
|
235
|
+
.chart-container {{
|
|
236
|
+
background-color: {self.THEME["bg_secondary"]};
|
|
237
|
+
border: 1px solid {self.THEME["accent_primary"]};
|
|
238
|
+
border-radius: 8px;
|
|
239
|
+
padding: 20px;
|
|
240
|
+
}}
|
|
241
|
+
|
|
242
|
+
.chart-title {{
|
|
243
|
+
font-size: 1.3em;
|
|
244
|
+
font-weight: 600;
|
|
245
|
+
margin-bottom: 20px;
|
|
246
|
+
color: {self.THEME["text_primary"]};
|
|
247
|
+
}}
|
|
248
|
+
|
|
249
|
+
.chart-canvas {{
|
|
250
|
+
max-height: 400px;
|
|
251
|
+
}}
|
|
252
|
+
|
|
253
|
+
.table-section {{
|
|
254
|
+
margin-bottom: 40px;
|
|
255
|
+
}}
|
|
256
|
+
|
|
257
|
+
.table-section h2 {{
|
|
258
|
+
font-size: 1.5em;
|
|
259
|
+
margin-bottom: 20px;
|
|
260
|
+
color: {self.THEME["text_primary"]};
|
|
261
|
+
}}
|
|
262
|
+
|
|
263
|
+
.table-wrapper {{
|
|
264
|
+
overflow-x: auto;
|
|
265
|
+
background-color: {self.THEME["bg_secondary"]};
|
|
266
|
+
border: 1px solid {self.THEME["accent_primary"]};
|
|
267
|
+
border-radius: 8px;
|
|
268
|
+
}}
|
|
269
|
+
|
|
270
|
+
table {{
|
|
271
|
+
width: 100%;
|
|
272
|
+
border-collapse: collapse;
|
|
273
|
+
}}
|
|
274
|
+
|
|
275
|
+
th {{
|
|
276
|
+
background-color: {self.THEME["accent_primary"]};
|
|
277
|
+
padding: 15px;
|
|
278
|
+
text-align: left;
|
|
279
|
+
font-weight: 600;
|
|
280
|
+
color: {self.THEME["text_primary"]};
|
|
281
|
+
border-bottom: 2px solid {self.THEME["accent_secondary"]};
|
|
282
|
+
}}
|
|
283
|
+
|
|
284
|
+
td {{
|
|
285
|
+
padding: 12px 15px;
|
|
286
|
+
border-bottom: 1px solid {self.THEME["accent_primary"]};
|
|
287
|
+
color: {self.THEME["text_primary"]};
|
|
288
|
+
}}
|
|
289
|
+
|
|
290
|
+
tr:hover {{
|
|
291
|
+
background-color: {self.THEME["accent_primary"]};
|
|
292
|
+
}}
|
|
293
|
+
|
|
294
|
+
.insights {{
|
|
295
|
+
background-color: {self.THEME["bg_secondary"]};
|
|
296
|
+
border: 1px solid {self.THEME["accent_primary"]};
|
|
297
|
+
border-radius: 8px;
|
|
298
|
+
padding: 25px;
|
|
299
|
+
margin-bottom: 40px;
|
|
300
|
+
}}
|
|
301
|
+
|
|
302
|
+
.insights h2 {{
|
|
303
|
+
font-size: 1.5em;
|
|
304
|
+
margin-bottom: 20px;
|
|
305
|
+
color: {self.THEME["text_primary"]};
|
|
306
|
+
}}
|
|
307
|
+
|
|
308
|
+
.insight {{
|
|
309
|
+
margin-bottom: 20px;
|
|
310
|
+
padding: 15px;
|
|
311
|
+
background-color: {self.THEME["bg_primary"]};
|
|
312
|
+
border-left: 4px solid {self.THEME["accent_secondary"]};
|
|
313
|
+
border-radius: 4px;
|
|
314
|
+
}}
|
|
315
|
+
|
|
316
|
+
.insight-title {{
|
|
317
|
+
font-weight: 600;
|
|
318
|
+
color: {self.THEME["accent_secondary"]};
|
|
319
|
+
margin-bottom: 8px;
|
|
320
|
+
}}
|
|
321
|
+
|
|
322
|
+
.insight-text {{
|
|
323
|
+
color: {self.THEME["text_secondary"]};
|
|
324
|
+
line-height: 1.6;
|
|
325
|
+
}}
|
|
326
|
+
|
|
327
|
+
.footer {{
|
|
328
|
+
text-align: center;
|
|
329
|
+
padding-top: 20px;
|
|
330
|
+
border-top: 1px solid {self.THEME["accent_primary"]};
|
|
331
|
+
color: {self.THEME["text_secondary"]};
|
|
332
|
+
font-size: 0.85em;
|
|
333
|
+
}}
|
|
334
|
+
|
|
335
|
+
@media (max-width: 768px) {{
|
|
336
|
+
.charts {{
|
|
337
|
+
grid-template-columns: 1fr;
|
|
338
|
+
}}
|
|
339
|
+
|
|
340
|
+
.header h1 {{
|
|
341
|
+
font-size: 1.8em;
|
|
342
|
+
}}
|
|
343
|
+
|
|
344
|
+
.card-value {{
|
|
345
|
+
font-size: 2em;
|
|
346
|
+
}}
|
|
347
|
+
|
|
348
|
+
table {{
|
|
349
|
+
font-size: 0.9em;
|
|
350
|
+
}}
|
|
351
|
+
|
|
352
|
+
th, td {{
|
|
353
|
+
padding: 10px;
|
|
354
|
+
}}
|
|
355
|
+
}}
|
|
356
|
+
</style>"""
|
|
357
|
+
|
|
358
|
+
def _html_body_open(self) -> str:
|
|
359
|
+
"""Generate opening body tag."""
|
|
360
|
+
return "<body>"
|
|
361
|
+
|
|
362
|
+
def _html_body_close(self) -> str:
|
|
363
|
+
"""Generate closing body tag."""
|
|
364
|
+
return "</body></html>"
|
|
365
|
+
|
|
366
|
+
def _html_header_section(self, roi_stats: Any) -> str:
|
|
367
|
+
"""Generate dashboard header section."""
|
|
368
|
+
generated_at = self.generated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
369
|
+
|
|
370
|
+
return f"""<div class="container">
|
|
371
|
+
<div class="header">
|
|
372
|
+
<h1>Cost Attribution Dashboard</h1>
|
|
373
|
+
<p class="subtitle">OTEL ROI Analysis - Phase 1 MVP</p>
|
|
374
|
+
<div class="header-meta">
|
|
375
|
+
<span>Generated: <strong>{generated_at}</strong></span>
|
|
376
|
+
<span>Total Delegations: <strong>{roi_stats.total_delegations}</strong></span>
|
|
377
|
+
<span>Status: <strong class="status-good">✓ Active</strong></span>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
"""
|
|
381
|
+
|
|
382
|
+
def _html_summary_cards(self, roi_stats: Any, cost_breakdown: Any) -> str:
|
|
383
|
+
"""Generate summary metric cards."""
|
|
384
|
+
total_cost_usd = roi_stats.total_delegation_cost * 0.0000045
|
|
385
|
+
|
|
386
|
+
return f"""<div class="cards">
|
|
387
|
+
<div class="card">
|
|
388
|
+
<div class="card-label">Total Delegation Cost</div>
|
|
389
|
+
<div class="card-value">${total_cost_usd:.2f}<span class="card-unit"> USD</span></div>
|
|
390
|
+
<div class="card-meta">{roi_stats.total_delegation_cost:,} tokens</div>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<div class="card">
|
|
394
|
+
<div class="card-label">Estimated Direct Cost</div>
|
|
395
|
+
<div class="card-value">${roi_stats.estimated_direct_cost * 0.0000045:.2f}<span class="card-unit"> USD</span></div>
|
|
396
|
+
<div class="card-meta">{roi_stats.estimated_direct_cost:,} tokens (2.5x overhead)</div>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div class="card">
|
|
400
|
+
<div class="card-label">Estimated Savings</div>
|
|
401
|
+
<div class="card-value"><span class="status-good">${roi_stats.estimated_savings * 0.0000045:.2f}</span></div>
|
|
402
|
+
<div class="card-meta"><span class="status-good">{roi_stats.savings_percentage:.0f}% reduction</span></div>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<div class="card">
|
|
406
|
+
<div class="card-label">Avg Cost per Delegation</div>
|
|
407
|
+
<div class="card-value">{roi_stats.avg_cost_per_delegation:,.0f}<span class="card-unit"> tokens</span></div>
|
|
408
|
+
<div class="card-meta">Across {roi_stats.total_delegations} delegations</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
def _html_charts_section(self, cost_breakdown: Any, all_delegations: Any) -> str:
|
|
414
|
+
"""Generate charts section with visualizations."""
|
|
415
|
+
# Prepare data for charts
|
|
416
|
+
subagent_labels = list(cost_breakdown.by_subagent.keys())
|
|
417
|
+
subagent_data = list(cost_breakdown.by_subagent.values())
|
|
418
|
+
|
|
419
|
+
tool_labels = list(cost_breakdown.by_tool.keys())
|
|
420
|
+
tool_data = list(cost_breakdown.by_tool.values())
|
|
421
|
+
|
|
422
|
+
# Embed data as JSON for JavaScript
|
|
423
|
+
subagent_labels_json = json.dumps(subagent_labels)
|
|
424
|
+
subagent_data_json = json.dumps(subagent_data)
|
|
425
|
+
tool_labels_json = json.dumps(tool_labels)
|
|
426
|
+
tool_data_json = json.dumps(tool_data)
|
|
427
|
+
|
|
428
|
+
return f"""<div class="charts">
|
|
429
|
+
<div class="chart-container">
|
|
430
|
+
<div class="chart-title">Cost by Subagent Type</div>
|
|
431
|
+
<canvas id="chartBySubagent" class="chart-canvas"></canvas>
|
|
432
|
+
</div>
|
|
433
|
+
|
|
434
|
+
<div class="chart-container">
|
|
435
|
+
<div class="chart-title">Cost by Tool Type</div>
|
|
436
|
+
<canvas id="chartByTool" class="chart-canvas"></canvas>
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<script>
|
|
441
|
+
window.chartData = {{
|
|
442
|
+
subagent_labels: {subagent_labels_json},
|
|
443
|
+
subagent_data: {subagent_data_json},
|
|
444
|
+
tool_labels: {tool_labels_json},
|
|
445
|
+
tool_data: {tool_data_json}
|
|
446
|
+
}};
|
|
447
|
+
</script>
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
def _html_top_delegations_table(self, top_delegations: list[Any]) -> str:
|
|
451
|
+
"""Generate top delegations table."""
|
|
452
|
+
table_rows = ""
|
|
453
|
+
|
|
454
|
+
for i, delegation in enumerate(top_delegations, 1):
|
|
455
|
+
cost_usd = delegation.total_cost_tokens * 0.0000045
|
|
456
|
+
timestamp = delegation.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
|
457
|
+
|
|
458
|
+
table_rows += f""" <tr>
|
|
459
|
+
<td>{i}</td>
|
|
460
|
+
<td>{timestamp}</td>
|
|
461
|
+
<td><strong>{delegation.subagent_type}</strong></td>
|
|
462
|
+
<td>{delegation.tool_count}</td>
|
|
463
|
+
<td>{delegation.total_cost_tokens:,}</td>
|
|
464
|
+
<td>${cost_usd:.2f}</td>
|
|
465
|
+
<td><code style="font-size: 0.85em; color: #b0b0b0;">{delegation.event_id[:16]}...</code></td>
|
|
466
|
+
</tr>
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
return f"""<div class="table-section">
|
|
470
|
+
<h2>Top 10 Most Expensive Delegations</h2>
|
|
471
|
+
<div class="table-wrapper">
|
|
472
|
+
<table>
|
|
473
|
+
<thead>
|
|
474
|
+
<tr>
|
|
475
|
+
<th>#</th>
|
|
476
|
+
<th>Timestamp</th>
|
|
477
|
+
<th>Subagent Type</th>
|
|
478
|
+
<th>Tool Count</th>
|
|
479
|
+
<th>Cost (tokens)</th>
|
|
480
|
+
<th>Cost (USD)</th>
|
|
481
|
+
<th>Event ID</th>
|
|
482
|
+
</tr>
|
|
483
|
+
</thead>
|
|
484
|
+
<tbody>
|
|
485
|
+
{table_rows} </tbody>
|
|
486
|
+
</table>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
def _html_insights_section(self, roi_stats: Any, cost_breakdown: Any) -> str:
|
|
492
|
+
"""Generate insights and recommendations."""
|
|
493
|
+
insights = []
|
|
494
|
+
|
|
495
|
+
# Insight 1: ROI summary
|
|
496
|
+
if roi_stats.savings_percentage > 40:
|
|
497
|
+
insights.append(
|
|
498
|
+
(
|
|
499
|
+
"Strong Delegation ROI",
|
|
500
|
+
f"Your delegation strategy is effective. By properly delegating work, "
|
|
501
|
+
f"you're achieving approximately {roi_stats.savings_percentage:.0f}% cost savings "
|
|
502
|
+
f"compared to direct execution.",
|
|
503
|
+
)
|
|
504
|
+
)
|
|
505
|
+
else:
|
|
506
|
+
insights.append(
|
|
507
|
+
(
|
|
508
|
+
"Delegation Opportunity",
|
|
509
|
+
f"Current delegation strategy shows {roi_stats.savings_percentage:.0f}% potential savings. "
|
|
510
|
+
f"Consider delegating more complex work to specialized subagents.",
|
|
511
|
+
)
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# Insight 2: Most expensive subagent
|
|
515
|
+
if cost_breakdown.by_subagent:
|
|
516
|
+
top_subagent = max(cost_breakdown.by_subagent.items(), key=lambda x: x[1])
|
|
517
|
+
insights.append(
|
|
518
|
+
(
|
|
519
|
+
f"Highest Cost Subagent: {top_subagent[0]}",
|
|
520
|
+
f"This subagent type accounts for {top_subagent[1]:,} tokens. "
|
|
521
|
+
f"Review the work being delegated here for optimization opportunities.",
|
|
522
|
+
)
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
# Insight 3: Most expensive tool
|
|
526
|
+
if cost_breakdown.by_tool:
|
|
527
|
+
top_tool = max(cost_breakdown.by_tool.items(), key=lambda x: x[1])
|
|
528
|
+
insights.append(
|
|
529
|
+
(
|
|
530
|
+
f"Highest Cost Tool: {top_tool[0]}",
|
|
531
|
+
f"This tool is consuming {top_tool[1]:,} tokens. "
|
|
532
|
+
f"Consider batching operations or using more efficient approaches.",
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
# Insight 4: Parallelization benefit
|
|
537
|
+
insights.append(
|
|
538
|
+
(
|
|
539
|
+
"Parallelization Benefit",
|
|
540
|
+
f"Subagent delegation enables {roi_stats.avg_parallelization_factor:.1f}x efficiency gain "
|
|
541
|
+
f"through parallel execution and focused context.",
|
|
542
|
+
)
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
insights_html = ""
|
|
546
|
+
for title, text in insights:
|
|
547
|
+
insights_html += f""" <div class="insight">
|
|
548
|
+
<div class="insight-title">{title}</div>
|
|
549
|
+
<div class="insight-text">{text}</div>
|
|
550
|
+
</div>
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
return f"""<div class="insights">
|
|
554
|
+
<h2>Insights & Recommendations</h2>
|
|
555
|
+
{insights_html} </div>
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
def _html_footer_section(self) -> str:
|
|
559
|
+
"""Generate footer section."""
|
|
560
|
+
return """ <div class="footer">
|
|
561
|
+
<p>HtmlGraph Cost Attribution Dashboard | OTEL ROI Analysis Phase 1 | <a href="https://code.claude.com">Claude Code</a></p>
|
|
562
|
+
</div>
|
|
563
|
+
</div>"""
|
|
564
|
+
|
|
565
|
+
def _html_scripts(self, cost_breakdown: Any, all_delegations: Any) -> str:
|
|
566
|
+
"""Generate JavaScript for interactive charts."""
|
|
567
|
+
return """<script>
|
|
568
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
569
|
+
const data = window.chartData || {
|
|
570
|
+
subagent_labels: [],
|
|
571
|
+
subagent_data: [],
|
|
572
|
+
tool_labels: [],
|
|
573
|
+
tool_data: []
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
const chartOptions = {
|
|
577
|
+
responsive: true,
|
|
578
|
+
maintainAspectRatio: true,
|
|
579
|
+
plugins: {
|
|
580
|
+
legend: {
|
|
581
|
+
labels: {
|
|
582
|
+
color: '#e0e0e0',
|
|
583
|
+
font: { size: 12 }
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
tooltip: {
|
|
587
|
+
backgroundColor: 'rgba(26, 26, 46, 0.8)',
|
|
588
|
+
titleColor: '#e0e0e0',
|
|
589
|
+
bodyColor: '#b0b0b0',
|
|
590
|
+
borderColor: '#0f3460',
|
|
591
|
+
borderWidth: 1
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
scales: {
|
|
595
|
+
y: {
|
|
596
|
+
ticks: { color: '#b0b0b0' },
|
|
597
|
+
grid: { color: '#16213e' }
|
|
598
|
+
},
|
|
599
|
+
x: {
|
|
600
|
+
ticks: { color: '#b0b0b0' },
|
|
601
|
+
grid: { color: '#16213e' }
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// Doughnut chart - Cost by Subagent Type
|
|
607
|
+
if (data.subagent_labels.length > 0) {
|
|
608
|
+
const ctxSubagent = document.getElementById('chartBySubagent');
|
|
609
|
+
if (ctxSubagent) {
|
|
610
|
+
new Chart(ctxSubagent, {
|
|
611
|
+
type: 'doughnut',
|
|
612
|
+
data: {
|
|
613
|
+
labels: data.subagent_labels,
|
|
614
|
+
datasets: [{
|
|
615
|
+
label: 'Cost (tokens)',
|
|
616
|
+
data: data.subagent_data,
|
|
617
|
+
backgroundColor: [
|
|
618
|
+
'#e94560',
|
|
619
|
+
'#ff9800',
|
|
620
|
+
'#ff6f00',
|
|
621
|
+
'#d84315',
|
|
622
|
+
'#c62828',
|
|
623
|
+
'#0f3460'
|
|
624
|
+
],
|
|
625
|
+
borderColor: '#1a1a2e',
|
|
626
|
+
borderWidth: 2
|
|
627
|
+
}]
|
|
628
|
+
},
|
|
629
|
+
options: {
|
|
630
|
+
...chartOptions,
|
|
631
|
+
plugins: {
|
|
632
|
+
...chartOptions.plugins,
|
|
633
|
+
legend: {
|
|
634
|
+
...chartOptions.plugins.legend,
|
|
635
|
+
position: 'bottom'
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Bar chart - Cost by Tool
|
|
644
|
+
if (data.tool_labels.length > 0) {
|
|
645
|
+
const ctxTool = document.getElementById('chartByTool');
|
|
646
|
+
if (ctxTool) {
|
|
647
|
+
new Chart(ctxTool, {
|
|
648
|
+
type: 'bar',
|
|
649
|
+
data: {
|
|
650
|
+
labels: data.tool_labels,
|
|
651
|
+
datasets: [{
|
|
652
|
+
label: 'Cost (tokens)',
|
|
653
|
+
data: data.tool_data,
|
|
654
|
+
backgroundColor: '#0f3460',
|
|
655
|
+
borderColor: '#e94560',
|
|
656
|
+
borderWidth: 2,
|
|
657
|
+
borderRadius: 4
|
|
658
|
+
}]
|
|
659
|
+
},
|
|
660
|
+
options: {
|
|
661
|
+
...chartOptions,
|
|
662
|
+
indexAxis: 'y',
|
|
663
|
+
plugins: {
|
|
664
|
+
...chartOptions.plugins,
|
|
665
|
+
legend: {
|
|
666
|
+
...chartOptions.plugins.legend,
|
|
667
|
+
display: true
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
</script>"""
|