htmlgraph 0.24.2__py3-none-any.whl → 0.25.0__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/__init__.py +20 -1
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/analytics/cross_session.py +4 -3
- htmlgraph/analytics/work_type.py +52 -16
- htmlgraph/analytics_index.py +51 -19
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/main.py +2115 -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 +812 -0
- htmlgraph/api/templates/dashboard.html +783 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +570 -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 +509 -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 +163 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/builders/base.py +55 -1
- htmlgraph/builders/bug.py +17 -2
- htmlgraph/builders/chore.py +17 -2
- htmlgraph/builders/epic.py +17 -2
- htmlgraph/builders/feature.py +25 -2
- htmlgraph/builders/phase.py +17 -2
- htmlgraph/builders/spike.py +27 -2
- htmlgraph/builders/track.py +14 -0
- htmlgraph/cigs/__init__.py +4 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cli.py +1427 -401
- 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 +21 -0
- htmlgraph/collections/session.py +189 -0
- htmlgraph/collections/spike.py +7 -1
- htmlgraph/collections/task_delegation.py +236 -0
- htmlgraph/collections/traces.py +482 -0
- htmlgraph/config.py +113 -0
- htmlgraph/converter.py +41 -0
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +3315 -492
- htmlgraph-0.24.2.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
- 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 +1334 -0
- 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 +710 -0
- htmlgraph/docs/README.md +533 -0
- htmlgraph/docs/version_check.py +3 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +2 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/context.py +271 -0
- htmlgraph/hooks/drift_handler.py +521 -0
- htmlgraph/hooks/event_tracker.py +405 -15
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/pretooluse.py +476 -6
- htmlgraph/hooks/prompt_analyzer.py +648 -0
- htmlgraph/hooks/session_handler.py +583 -0
- htmlgraph/hooks/state_manager.py +501 -0
- htmlgraph/hooks/subagent_stop.py +309 -0
- htmlgraph/hooks/task_enforcer.py +39 -0
- htmlgraph/models.py +111 -15
- htmlgraph/operations/fastapi_server.py +230 -0
- htmlgraph/orchestration/headless_spawner.py +22 -14
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/repo_hash.py +511 -0
- htmlgraph/sdk.py +348 -10
- htmlgraph/server.py +194 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +131 -1
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/system_prompts.py +449 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +19 -0
- htmlgraph/validation.py +115 -0
- htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task delegation collection for tracking spawned agent work.
|
|
3
|
+
|
|
4
|
+
Captures observability data for Task() calls:
|
|
5
|
+
- Which agent was spawned (gemini-spawner, codex-spawner, copilot-spawner, haiku)
|
|
6
|
+
- What task was assigned
|
|
7
|
+
- How long it took
|
|
8
|
+
- What was the output/result
|
|
9
|
+
- Cost (tokens used)
|
|
10
|
+
- Success/failure status
|
|
11
|
+
|
|
12
|
+
This data proves multi-agent orchestration works and enables dashboard attribution.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from htmlgraph.sdk import SDK
|
|
22
|
+
|
|
23
|
+
from htmlgraph.collections.base import BaseCollection
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TaskDelegationCollection(BaseCollection["TaskDelegationCollection"]):
|
|
27
|
+
"""
|
|
28
|
+
Collection interface for task delegations.
|
|
29
|
+
|
|
30
|
+
Tracks all spawned agent work with metrics:
|
|
31
|
+
- Agent type (gemini-spawner, codex-spawner, copilot-spawner, haiku)
|
|
32
|
+
- Task description
|
|
33
|
+
- Start and end timestamps
|
|
34
|
+
- Duration in seconds
|
|
35
|
+
- Tokens used
|
|
36
|
+
- Cost in USD
|
|
37
|
+
- Status (success/failure)
|
|
38
|
+
- Result summary
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> sdk = SDK(agent="orchestrator")
|
|
42
|
+
>>> delegations = sdk.task_delegations.where(agent_type="codex-spawner")
|
|
43
|
+
>>> for d in delegations:
|
|
44
|
+
... print(f"{d.agent_type}: {d.task_description} ({d.duration_seconds}s)")
|
|
45
|
+
>>>
|
|
46
|
+
>>> # Get all delegations for a specific agent
|
|
47
|
+
>>> gemini_work = sdk.task_delegations.where(agent_type="gemini-spawner")
|
|
48
|
+
>>>
|
|
49
|
+
>>> # Calculate total cost
|
|
50
|
+
>>> total_cost = sum(float(d.cost_usd or 0) for d in sdk.task_delegations.all())
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
_collection_name = "task-delegations"
|
|
54
|
+
_node_type = "task-delegation"
|
|
55
|
+
|
|
56
|
+
def __init__(self, sdk: SDK):
|
|
57
|
+
"""
|
|
58
|
+
Initialize task delegation collection.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
sdk: Parent SDK instance
|
|
62
|
+
"""
|
|
63
|
+
super().__init__(sdk, "task-delegations", "task-delegation")
|
|
64
|
+
self._sdk = sdk
|
|
65
|
+
|
|
66
|
+
def create_delegation(
|
|
67
|
+
self,
|
|
68
|
+
agent_type: str,
|
|
69
|
+
task_description: str,
|
|
70
|
+
timestamp_start: datetime | None = None,
|
|
71
|
+
tokens_used: int = 0,
|
|
72
|
+
cost_usd: float = 0.0,
|
|
73
|
+
status: str = "pending",
|
|
74
|
+
result_summary: str = "",
|
|
75
|
+
) -> Any:
|
|
76
|
+
"""
|
|
77
|
+
Record a task delegation.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
agent_type: Type of spawned agent (gemini-spawner, codex-spawner, copilot-spawner, haiku)
|
|
81
|
+
task_description: Human-readable task description
|
|
82
|
+
timestamp_start: When delegation started (defaults to now)
|
|
83
|
+
tokens_used: Number of tokens used by the agent
|
|
84
|
+
cost_usd: Cost in USD for this delegation
|
|
85
|
+
status: success/failure/pending
|
|
86
|
+
result_summary: Brief summary of the result
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Created task delegation node
|
|
90
|
+
"""
|
|
91
|
+
if timestamp_start is None:
|
|
92
|
+
timestamp_start = datetime.utcnow()
|
|
93
|
+
|
|
94
|
+
# Create delegation record via base collection
|
|
95
|
+
return self.create(
|
|
96
|
+
title=f"{agent_type}: {task_description[:50]}...",
|
|
97
|
+
).set_metadata(
|
|
98
|
+
{
|
|
99
|
+
"agent_type": agent_type,
|
|
100
|
+
"task_description": task_description,
|
|
101
|
+
"timestamp_start": timestamp_start.isoformat(),
|
|
102
|
+
"tokens_used": tokens_used,
|
|
103
|
+
"cost_usd": cost_usd,
|
|
104
|
+
"status": status,
|
|
105
|
+
"result_summary": result_summary,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def update_delegation(
|
|
110
|
+
self,
|
|
111
|
+
delegation_id: str,
|
|
112
|
+
timestamp_end: datetime | None = None,
|
|
113
|
+
tokens_used: int | None = None,
|
|
114
|
+
cost_usd: float | None = None,
|
|
115
|
+
status: str | None = None,
|
|
116
|
+
result_summary: str | None = None,
|
|
117
|
+
) -> Any:
|
|
118
|
+
"""
|
|
119
|
+
Update a delegation record with completion info.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
delegation_id: ID of the delegation to update
|
|
123
|
+
timestamp_end: When delegation completed
|
|
124
|
+
tokens_used: Updated token count
|
|
125
|
+
cost_usd: Updated cost
|
|
126
|
+
status: Updated status
|
|
127
|
+
result_summary: Updated result summary
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Updated delegation node
|
|
131
|
+
"""
|
|
132
|
+
if timestamp_end is None:
|
|
133
|
+
timestamp_end = datetime.utcnow()
|
|
134
|
+
|
|
135
|
+
with self.edit(delegation_id) as delegation:
|
|
136
|
+
if timestamp_end:
|
|
137
|
+
delegation.timestamp_end = timestamp_end.isoformat() # type: ignore[attr-defined]
|
|
138
|
+
|
|
139
|
+
# Calculate duration if we have start time
|
|
140
|
+
if hasattr(delegation, "timestamp_start") and timestamp_end:
|
|
141
|
+
start = datetime.fromisoformat(str(delegation.timestamp_start))
|
|
142
|
+
duration = (timestamp_end - start).total_seconds()
|
|
143
|
+
delegation.duration_seconds = int(duration) # type: ignore[attr-defined]
|
|
144
|
+
|
|
145
|
+
if tokens_used is not None:
|
|
146
|
+
delegation.tokens_used = tokens_used # type: ignore[attr-defined]
|
|
147
|
+
if cost_usd is not None:
|
|
148
|
+
delegation.cost_usd = cost_usd # type: ignore[attr-defined]
|
|
149
|
+
if status is not None:
|
|
150
|
+
delegation.status = status # type: ignore[assignment]
|
|
151
|
+
if result_summary is not None:
|
|
152
|
+
delegation.result_summary = result_summary # type: ignore[attr-defined]
|
|
153
|
+
|
|
154
|
+
return delegation
|
|
155
|
+
|
|
156
|
+
def get_by_agent_type(self, agent_type: str) -> list:
|
|
157
|
+
"""
|
|
158
|
+
Get all delegations for a specific agent type.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
agent_type: Type of agent (gemini-spawner, codex-spawner, etc.)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
List of delegations for that agent type
|
|
165
|
+
"""
|
|
166
|
+
return self.where(agent_type=agent_type)
|
|
167
|
+
|
|
168
|
+
def get_by_status(self, status: str) -> list:
|
|
169
|
+
"""
|
|
170
|
+
Get all delegations with a specific status.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
status: Status to filter by (success/failure/pending)
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
List of delegations with that status
|
|
177
|
+
"""
|
|
178
|
+
return self.where(status=status)
|
|
179
|
+
|
|
180
|
+
def get_stats(self) -> dict:
|
|
181
|
+
"""
|
|
182
|
+
Get aggregated statistics about delegations.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Dictionary with:
|
|
186
|
+
- total_delegations: Count of all delegations
|
|
187
|
+
- by_agent_type: Count per agent type
|
|
188
|
+
- by_status: Count per status
|
|
189
|
+
- total_tokens: Sum of all tokens used
|
|
190
|
+
- total_cost: Sum of all costs in USD
|
|
191
|
+
- average_duration: Average duration in seconds
|
|
192
|
+
"""
|
|
193
|
+
all_delegations = self.all()
|
|
194
|
+
|
|
195
|
+
if not all_delegations:
|
|
196
|
+
return {
|
|
197
|
+
"total_delegations": 0,
|
|
198
|
+
"by_agent_type": {},
|
|
199
|
+
"by_status": {},
|
|
200
|
+
"total_tokens": 0,
|
|
201
|
+
"total_cost": 0.0,
|
|
202
|
+
"average_duration": 0.0,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Count by agent type
|
|
206
|
+
by_agent_type: dict[str, int] = {}
|
|
207
|
+
for d in all_delegations:
|
|
208
|
+
agent = getattr(d, "agent_type", "unknown")
|
|
209
|
+
by_agent_type[agent] = by_agent_type.get(agent, 0) + 1
|
|
210
|
+
|
|
211
|
+
# Count by status
|
|
212
|
+
by_status: dict[str, int] = {}
|
|
213
|
+
for d in all_delegations:
|
|
214
|
+
status = getattr(d, "status", "unknown")
|
|
215
|
+
by_status[status] = by_status.get(status, 0) + 1
|
|
216
|
+
|
|
217
|
+
# Sum tokens and cost
|
|
218
|
+
total_tokens = sum(getattr(d, "tokens_used", 0) for d in all_delegations)
|
|
219
|
+
total_cost = sum(float(getattr(d, "cost_usd", 0) or 0) for d in all_delegations)
|
|
220
|
+
|
|
221
|
+
# Average duration
|
|
222
|
+
durations = [
|
|
223
|
+
getattr(d, "duration_seconds", 0)
|
|
224
|
+
for d in all_delegations
|
|
225
|
+
if hasattr(d, "duration_seconds")
|
|
226
|
+
]
|
|
227
|
+
average_duration = sum(durations) / len(durations) if durations else 0.0
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
"total_delegations": len(all_delegations),
|
|
231
|
+
"by_agent_type": by_agent_type,
|
|
232
|
+
"by_status": by_status,
|
|
233
|
+
"total_tokens": total_tokens,
|
|
234
|
+
"total_cost": round(total_cost, 4),
|
|
235
|
+
"average_duration": round(average_duration, 2),
|
|
236
|
+
}
|