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.
Files changed (304) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +51 -1
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +26 -10
  7. htmlgraph/agent_registry.py +2 -1
  8. htmlgraph/analytics/__init__.py +8 -1
  9. htmlgraph/analytics/cli.py +86 -20
  10. htmlgraph/analytics/cost_analyzer.py +391 -0
  11. htmlgraph/analytics/cost_monitor.py +664 -0
  12. htmlgraph/analytics/cost_reporter.py +675 -0
  13. htmlgraph/analytics/cross_session.py +617 -0
  14. htmlgraph/analytics/dependency.py +10 -6
  15. htmlgraph/analytics/pattern_learning.py +771 -0
  16. htmlgraph/analytics/session_graph.py +707 -0
  17. htmlgraph/analytics/strategic/__init__.py +80 -0
  18. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  19. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  20. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  21. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  22. htmlgraph/analytics/work_type.py +67 -27
  23. htmlgraph/analytics_index.py +53 -20
  24. htmlgraph/api/__init__.py +3 -0
  25. htmlgraph/api/cost_alerts_websocket.py +416 -0
  26. htmlgraph/api/main.py +2498 -0
  27. htmlgraph/api/static/htmx.min.js +1 -0
  28. htmlgraph/api/static/style-redesign.css +1344 -0
  29. htmlgraph/api/static/style.css +1079 -0
  30. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  31. htmlgraph/api/templates/dashboard.html +794 -0
  32. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  33. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  34. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  35. htmlgraph/api/templates/partials/agents.html +317 -0
  36. htmlgraph/api/templates/partials/event-traces.html +373 -0
  37. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  38. htmlgraph/api/templates/partials/features.html +578 -0
  39. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  40. htmlgraph/api/templates/partials/metrics.html +346 -0
  41. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  42. htmlgraph/api/templates/partials/orchestration.html +198 -0
  43. htmlgraph/api/templates/partials/spawners.html +375 -0
  44. htmlgraph/api/templates/partials/work-items.html +613 -0
  45. htmlgraph/api/websocket.py +538 -0
  46. htmlgraph/archive/__init__.py +24 -0
  47. htmlgraph/archive/bloom.py +234 -0
  48. htmlgraph/archive/fts.py +297 -0
  49. htmlgraph/archive/manager.py +583 -0
  50. htmlgraph/archive/search.py +244 -0
  51. htmlgraph/atomic_ops.py +560 -0
  52. htmlgraph/attribute_index.py +2 -1
  53. htmlgraph/bounded_paths.py +539 -0
  54. htmlgraph/builders/base.py +57 -2
  55. htmlgraph/builders/bug.py +19 -3
  56. htmlgraph/builders/chore.py +19 -3
  57. htmlgraph/builders/epic.py +19 -3
  58. htmlgraph/builders/feature.py +27 -3
  59. htmlgraph/builders/insight.py +2 -1
  60. htmlgraph/builders/metric.py +2 -1
  61. htmlgraph/builders/pattern.py +2 -1
  62. htmlgraph/builders/phase.py +19 -3
  63. htmlgraph/builders/spike.py +29 -3
  64. htmlgraph/builders/track.py +42 -1
  65. htmlgraph/cigs/__init__.py +81 -0
  66. htmlgraph/cigs/autonomy.py +385 -0
  67. htmlgraph/cigs/cost.py +475 -0
  68. htmlgraph/cigs/messages_basic.py +472 -0
  69. htmlgraph/cigs/messaging.py +365 -0
  70. htmlgraph/cigs/models.py +771 -0
  71. htmlgraph/cigs/pattern_storage.py +427 -0
  72. htmlgraph/cigs/patterns.py +503 -0
  73. htmlgraph/cigs/posttool_analyzer.py +234 -0
  74. htmlgraph/cigs/reporter.py +818 -0
  75. htmlgraph/cigs/tracker.py +317 -0
  76. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  77. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  78. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  79. htmlgraph/cli/__init__.py +42 -0
  80. htmlgraph/cli/__main__.py +6 -0
  81. htmlgraph/cli/analytics.py +1424 -0
  82. htmlgraph/cli/base.py +685 -0
  83. htmlgraph/cli/constants.py +206 -0
  84. htmlgraph/cli/core.py +954 -0
  85. htmlgraph/cli/main.py +147 -0
  86. htmlgraph/cli/models.py +475 -0
  87. htmlgraph/cli/templates/__init__.py +1 -0
  88. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  89. htmlgraph/cli/work/__init__.py +239 -0
  90. htmlgraph/cli/work/browse.py +115 -0
  91. htmlgraph/cli/work/features.py +568 -0
  92. htmlgraph/cli/work/orchestration.py +676 -0
  93. htmlgraph/cli/work/report.py +728 -0
  94. htmlgraph/cli/work/sessions.py +466 -0
  95. htmlgraph/cli/work/snapshot.py +559 -0
  96. htmlgraph/cli/work/tracks.py +486 -0
  97. htmlgraph/cli_commands/__init__.py +1 -0
  98. htmlgraph/cli_commands/feature.py +195 -0
  99. htmlgraph/cli_framework.py +115 -0
  100. htmlgraph/collections/__init__.py +2 -0
  101. htmlgraph/collections/base.py +197 -14
  102. htmlgraph/collections/bug.py +2 -1
  103. htmlgraph/collections/chore.py +2 -1
  104. htmlgraph/collections/epic.py +2 -1
  105. htmlgraph/collections/feature.py +2 -1
  106. htmlgraph/collections/insight.py +2 -1
  107. htmlgraph/collections/metric.py +2 -1
  108. htmlgraph/collections/pattern.py +2 -1
  109. htmlgraph/collections/phase.py +2 -1
  110. htmlgraph/collections/session.py +194 -0
  111. htmlgraph/collections/spike.py +13 -2
  112. htmlgraph/collections/task_delegation.py +241 -0
  113. htmlgraph/collections/todo.py +14 -1
  114. htmlgraph/collections/traces.py +487 -0
  115. htmlgraph/config/cost_models.json +56 -0
  116. htmlgraph/config.py +190 -0
  117. htmlgraph/context_analytics.py +2 -1
  118. htmlgraph/converter.py +116 -7
  119. htmlgraph/cost_analysis/__init__.py +5 -0
  120. htmlgraph/cost_analysis/analyzer.py +438 -0
  121. htmlgraph/dashboard.html +2246 -248
  122. htmlgraph/dashboard.html.backup +6592 -0
  123. htmlgraph/dashboard.html.bak +7181 -0
  124. htmlgraph/dashboard.html.bak2 +7231 -0
  125. htmlgraph/dashboard.html.bak3 +7232 -0
  126. htmlgraph/db/__init__.py +38 -0
  127. htmlgraph/db/queries.py +790 -0
  128. htmlgraph/db/schema.py +1788 -0
  129. htmlgraph/decorators.py +317 -0
  130. htmlgraph/dependency_models.py +2 -1
  131. htmlgraph/deploy.py +26 -27
  132. htmlgraph/docs/API_REFERENCE.md +841 -0
  133. htmlgraph/docs/HTTP_API.md +750 -0
  134. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  135. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  136. htmlgraph/docs/README.md +532 -0
  137. htmlgraph/docs/__init__.py +77 -0
  138. htmlgraph/docs/docs_version.py +55 -0
  139. htmlgraph/docs/metadata.py +93 -0
  140. htmlgraph/docs/migrations.py +232 -0
  141. htmlgraph/docs/template_engine.py +143 -0
  142. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  143. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  144. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  145. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  146. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  147. htmlgraph/docs/version_check.py +163 -0
  148. htmlgraph/edge_index.py +2 -1
  149. htmlgraph/error_handler.py +544 -0
  150. htmlgraph/event_log.py +86 -37
  151. htmlgraph/event_migration.py +2 -1
  152. htmlgraph/file_watcher.py +12 -8
  153. htmlgraph/find_api.py +2 -1
  154. htmlgraph/git_events.py +67 -9
  155. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  156. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  157. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  158. htmlgraph/hooks/__init__.py +8 -0
  159. htmlgraph/hooks/bootstrap.py +169 -0
  160. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  161. htmlgraph/hooks/concurrent_sessions.py +208 -0
  162. htmlgraph/hooks/context.py +350 -0
  163. htmlgraph/hooks/drift_handler.py +525 -0
  164. htmlgraph/hooks/event_tracker.py +790 -99
  165. htmlgraph/hooks/git_commands.py +175 -0
  166. htmlgraph/hooks/installer.py +5 -1
  167. htmlgraph/hooks/orchestrator.py +327 -76
  168. htmlgraph/hooks/orchestrator_reflector.py +31 -4
  169. htmlgraph/hooks/post_tool_use_failure.py +32 -7
  170. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  171. htmlgraph/hooks/posttooluse.py +92 -19
  172. htmlgraph/hooks/pretooluse.py +527 -7
  173. htmlgraph/hooks/prompt_analyzer.py +637 -0
  174. htmlgraph/hooks/session_handler.py +668 -0
  175. htmlgraph/hooks/session_summary.py +395 -0
  176. htmlgraph/hooks/state_manager.py +504 -0
  177. htmlgraph/hooks/subagent_detection.py +202 -0
  178. htmlgraph/hooks/subagent_stop.py +369 -0
  179. htmlgraph/hooks/task_enforcer.py +99 -4
  180. htmlgraph/hooks/validator.py +212 -91
  181. htmlgraph/ids.py +2 -1
  182. htmlgraph/learning.py +125 -100
  183. htmlgraph/mcp_server.py +2 -1
  184. htmlgraph/models.py +217 -18
  185. htmlgraph/operations/README.md +62 -0
  186. htmlgraph/operations/__init__.py +79 -0
  187. htmlgraph/operations/analytics.py +339 -0
  188. htmlgraph/operations/bootstrap.py +289 -0
  189. htmlgraph/operations/events.py +244 -0
  190. htmlgraph/operations/fastapi_server.py +231 -0
  191. htmlgraph/operations/hooks.py +350 -0
  192. htmlgraph/operations/initialization.py +597 -0
  193. htmlgraph/operations/initialization.py.backup +228 -0
  194. htmlgraph/operations/server.py +303 -0
  195. htmlgraph/orchestration/__init__.py +58 -0
  196. htmlgraph/orchestration/claude_launcher.py +179 -0
  197. htmlgraph/orchestration/command_builder.py +72 -0
  198. htmlgraph/orchestration/headless_spawner.py +281 -0
  199. htmlgraph/orchestration/live_events.py +377 -0
  200. htmlgraph/orchestration/model_selection.py +327 -0
  201. htmlgraph/orchestration/plugin_manager.py +140 -0
  202. htmlgraph/orchestration/prompts.py +137 -0
  203. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  204. htmlgraph/orchestration/spawners/__init__.py +16 -0
  205. htmlgraph/orchestration/spawners/base.py +194 -0
  206. htmlgraph/orchestration/spawners/claude.py +173 -0
  207. htmlgraph/orchestration/spawners/codex.py +435 -0
  208. htmlgraph/orchestration/spawners/copilot.py +294 -0
  209. htmlgraph/orchestration/spawners/gemini.py +471 -0
  210. htmlgraph/orchestration/subprocess_runner.py +36 -0
  211. htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
  212. htmlgraph/orchestration.md +563 -0
  213. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  214. htmlgraph/orchestrator.py +2 -1
  215. htmlgraph/orchestrator_config.py +357 -0
  216. htmlgraph/orchestrator_mode.py +115 -4
  217. htmlgraph/parallel.py +2 -1
  218. htmlgraph/parser.py +86 -6
  219. htmlgraph/path_query.py +608 -0
  220. htmlgraph/pattern_matcher.py +636 -0
  221. htmlgraph/pydantic_models.py +476 -0
  222. htmlgraph/quality_gates.py +350 -0
  223. htmlgraph/query_builder.py +2 -1
  224. htmlgraph/query_composer.py +509 -0
  225. htmlgraph/reflection.py +443 -0
  226. htmlgraph/refs.py +344 -0
  227. htmlgraph/repo_hash.py +512 -0
  228. htmlgraph/repositories/__init__.py +292 -0
  229. htmlgraph/repositories/analytics_repository.py +455 -0
  230. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  231. htmlgraph/repositories/feature_repository.py +581 -0
  232. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  233. htmlgraph/repositories/feature_repository_memory.py +607 -0
  234. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  235. htmlgraph/repositories/filter_service.py +620 -0
  236. htmlgraph/repositories/filter_service_standard.py +445 -0
  237. htmlgraph/repositories/shared_cache.py +621 -0
  238. htmlgraph/repositories/shared_cache_memory.py +395 -0
  239. htmlgraph/repositories/track_repository.py +552 -0
  240. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  241. htmlgraph/repositories/track_repository_memory.py +508 -0
  242. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  243. htmlgraph/sdk/__init__.py +398 -0
  244. htmlgraph/sdk/__init__.pyi +14 -0
  245. htmlgraph/sdk/analytics/__init__.py +19 -0
  246. htmlgraph/sdk/analytics/engine.py +155 -0
  247. htmlgraph/sdk/analytics/helpers.py +178 -0
  248. htmlgraph/sdk/analytics/registry.py +109 -0
  249. htmlgraph/sdk/base.py +484 -0
  250. htmlgraph/sdk/constants.py +216 -0
  251. htmlgraph/sdk/core.pyi +308 -0
  252. htmlgraph/sdk/discovery.py +120 -0
  253. htmlgraph/sdk/help/__init__.py +12 -0
  254. htmlgraph/sdk/help/mixin.py +699 -0
  255. htmlgraph/sdk/mixins/__init__.py +15 -0
  256. htmlgraph/sdk/mixins/attribution.py +113 -0
  257. htmlgraph/sdk/mixins/mixin.py +410 -0
  258. htmlgraph/sdk/operations/__init__.py +12 -0
  259. htmlgraph/sdk/operations/mixin.py +427 -0
  260. htmlgraph/sdk/orchestration/__init__.py +17 -0
  261. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  262. htmlgraph/sdk/orchestration/spawner.py +204 -0
  263. htmlgraph/sdk/planning/__init__.py +19 -0
  264. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  265. htmlgraph/sdk/planning/mixin.py +211 -0
  266. htmlgraph/sdk/planning/parallel.py +186 -0
  267. htmlgraph/sdk/planning/queue.py +210 -0
  268. htmlgraph/sdk/planning/recommendations.py +87 -0
  269. htmlgraph/sdk/planning/smart_planning.py +319 -0
  270. htmlgraph/sdk/session/__init__.py +19 -0
  271. htmlgraph/sdk/session/continuity.py +57 -0
  272. htmlgraph/sdk/session/handoff.py +110 -0
  273. htmlgraph/sdk/session/info.py +309 -0
  274. htmlgraph/sdk/session/manager.py +103 -0
  275. htmlgraph/sdk/strategic/__init__.py +26 -0
  276. htmlgraph/sdk/strategic/mixin.py +563 -0
  277. htmlgraph/server.py +295 -107
  278. htmlgraph/session_hooks.py +300 -0
  279. htmlgraph/session_manager.py +285 -3
  280. htmlgraph/session_registry.py +587 -0
  281. htmlgraph/session_state.py +436 -0
  282. htmlgraph/session_warning.py +2 -1
  283. htmlgraph/sessions/__init__.py +23 -0
  284. htmlgraph/sessions/handoff.py +756 -0
  285. htmlgraph/system_prompts.py +450 -0
  286. htmlgraph/templates/orchestration-view.html +350 -0
  287. htmlgraph/track_builder.py +33 -1
  288. htmlgraph/track_manager.py +38 -0
  289. htmlgraph/transcript.py +18 -5
  290. htmlgraph/validation.py +115 -0
  291. htmlgraph/watch.py +2 -1
  292. htmlgraph/work_type_utils.py +2 -1
  293. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
  294. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
  295. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  296. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  297. htmlgraph/cli.py +0 -4839
  298. htmlgraph/sdk.py +0 -2359
  299. htmlgraph-0.20.1.dist-info/RECORD +0 -118
  300. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  301. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  302. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  303. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  304. {htmlgraph-0.20.1.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>"""