agentdebugx 0.1.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.
- agentdebug/__init__.py +65 -0
- agentdebug/adapters/__init__.py +10 -0
- agentdebug/adapters/base.py +22 -0
- agentdebug/adapters/langgraph.py +261 -0
- agentdebug/adapters/otel.py +151 -0
- agentdebug/adapters/raw.py +134 -0
- agentdebug/analyzers.py +152 -0
- agentdebug/attribution.py +230 -0
- agentdebug/cli.py +272 -0
- agentdebug/events.py +114 -0
- agentdebug/instrumentation.py +57 -0
- agentdebug/judges.py +258 -0
- agentdebug/llm.py +165 -0
- agentdebug/models.py +169 -0
- agentdebug/recorder.py +183 -0
- agentdebug/recovery.py +113 -0
- agentdebug/storage.py +167 -0
- agentdebug/taxonomy.py +271 -0
- agentdebug/ui/__init__.py +14 -0
- agentdebug/ui/server.py +260 -0
- agentdebugx-0.1.0.dist-info/METADATA +217 -0
- agentdebugx-0.1.0.dist-info/RECORD +25 -0
- agentdebugx-0.1.0.dist-info/WHEEL +4 -0
- agentdebugx-0.1.0.dist-info/entry_points.txt +3 -0
- agentdebugx-0.1.0.dist-info/licenses/LICENSE +21 -0
agentdebug/taxonomy.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Seed failure taxonomy for AgentDebugX.
|
|
2
|
+
|
|
3
|
+
This module starts with research-grounded failure modes and is designed to be
|
|
4
|
+
extended by generated, project-specific taxonomy nodes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from agentdebug.models import FailureMode
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _mode(
|
|
15
|
+
mode_id: str,
|
|
16
|
+
name: str,
|
|
17
|
+
family: str,
|
|
18
|
+
description: str,
|
|
19
|
+
signals: List[str],
|
|
20
|
+
suggestions: List[str],
|
|
21
|
+
source: str,
|
|
22
|
+
) -> FailureMode:
|
|
23
|
+
return FailureMode(
|
|
24
|
+
mode_id=mode_id,
|
|
25
|
+
name=name,
|
|
26
|
+
family=family,
|
|
27
|
+
description=description,
|
|
28
|
+
signals=signals,
|
|
29
|
+
suggestion_templates=suggestions,
|
|
30
|
+
source=source,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
SEED_FAILURE_MODES: Dict[str, FailureMode] = {
|
|
35
|
+
'memory.retrieval_failure': _mode(
|
|
36
|
+
'memory.retrieval_failure',
|
|
37
|
+
'Memory retrieval failure',
|
|
38
|
+
'memory',
|
|
39
|
+
'The agent failed to retrieve relevant prior state, observation, or task context.',
|
|
40
|
+
['missing context', 'forgot', 'stale context', 'retrieval miss'],
|
|
41
|
+
[
|
|
42
|
+
'Persist the missing state as structured memory and attach it to the next planning step.',
|
|
43
|
+
'Add a retrieval quality check before acting on retrieved context.',
|
|
44
|
+
],
|
|
45
|
+
'AgentDebug',
|
|
46
|
+
),
|
|
47
|
+
'memory.hallucination': _mode(
|
|
48
|
+
'memory.hallucination',
|
|
49
|
+
'Memory hallucination',
|
|
50
|
+
'memory',
|
|
51
|
+
'The agent treats unobserved or invented state as remembered fact.',
|
|
52
|
+
['invented memory', 'unsupported recall', 'false prior state'],
|
|
53
|
+
[
|
|
54
|
+
'Require memory reads to cite the source event or artifact before use.',
|
|
55
|
+
'Separate observed state from inferred state in the agent prompt.',
|
|
56
|
+
],
|
|
57
|
+
'AgentDebug',
|
|
58
|
+
),
|
|
59
|
+
'reflection.progress_misjudge': _mode(
|
|
60
|
+
'reflection.progress_misjudge',
|
|
61
|
+
'Progress misjudge',
|
|
62
|
+
'reflection',
|
|
63
|
+
'The agent incorrectly judges whether the task is solved or whether progress was made.',
|
|
64
|
+
['premature success', 'incorrect self evaluation', 'progress misjudge'],
|
|
65
|
+
[
|
|
66
|
+
'Add an external task verifier before termination.',
|
|
67
|
+
'Record explicit success criteria and compare the current state against each criterion.',
|
|
68
|
+
],
|
|
69
|
+
'AgentDebug',
|
|
70
|
+
),
|
|
71
|
+
'reflection.causal_misattribution': _mode(
|
|
72
|
+
'reflection.causal_misattribution',
|
|
73
|
+
'Causal misattribution',
|
|
74
|
+
'reflection',
|
|
75
|
+
'The agent explains failure using the wrong cause and then optimizes the wrong behavior.',
|
|
76
|
+
['wrong root cause', 'misattribution', 'incorrect blame'],
|
|
77
|
+
[
|
|
78
|
+
'Use counterfactual replay or ablation to test whether the suspected step caused the failure.',
|
|
79
|
+
'Preserve evidence links for each causal claim in the reflection.',
|
|
80
|
+
],
|
|
81
|
+
'AgentDebug',
|
|
82
|
+
),
|
|
83
|
+
'planning.constraint_ignorance': _mode(
|
|
84
|
+
'planning.constraint_ignorance',
|
|
85
|
+
'Constraint ignorance',
|
|
86
|
+
'planning',
|
|
87
|
+
'The plan ignores task, environment, policy, schema, or safety constraints.',
|
|
88
|
+
['constraint ignored', 'policy violation', 'cannot satisfy requirement'],
|
|
89
|
+
[
|
|
90
|
+
'Compile task and tool constraints into pre-action checks.',
|
|
91
|
+
'Fail closed when required constraints cannot be verified.',
|
|
92
|
+
],
|
|
93
|
+
'AgentDebug / MAST',
|
|
94
|
+
),
|
|
95
|
+
'planning.impossible_action': _mode(
|
|
96
|
+
'planning.impossible_action',
|
|
97
|
+
'Impossible action',
|
|
98
|
+
'planning',
|
|
99
|
+
'The agent plans an action that cannot be executed in the current environment.',
|
|
100
|
+
['impossible action', 'unavailable operation', 'invalid transition'],
|
|
101
|
+
[
|
|
102
|
+
'Expose environment affordances as a machine-readable action space.',
|
|
103
|
+
'Ask the planner to replan from the last valid state after an impossible action is detected.',
|
|
104
|
+
],
|
|
105
|
+
'AgentDebug',
|
|
106
|
+
),
|
|
107
|
+
'planning.inefficient_plan': _mode(
|
|
108
|
+
'planning.inefficient_plan',
|
|
109
|
+
'Inefficient plan',
|
|
110
|
+
'planning',
|
|
111
|
+
'The agent loops, over-decomposes, or burns steps without increasing task completion probability.',
|
|
112
|
+
['loop', 'step explosion', 'no progress', 'repeated action'],
|
|
113
|
+
[
|
|
114
|
+
'Add loop detection over tool calls and state deltas.',
|
|
115
|
+
'Trigger a replan when the same state-action pattern repeats.',
|
|
116
|
+
],
|
|
117
|
+
'AgentDebug / MAST',
|
|
118
|
+
),
|
|
119
|
+
'action.wrong_tool': _mode(
|
|
120
|
+
'action.wrong_tool',
|
|
121
|
+
'Wrong tool selection',
|
|
122
|
+
'action',
|
|
123
|
+
'The agent selects a tool that does not match the task intent or current state.',
|
|
124
|
+
['wrong tool', 'unknown tool', 'tool mismatch'],
|
|
125
|
+
[
|
|
126
|
+
'Add tool routing examples and negative examples for similar tools.',
|
|
127
|
+
'Use a tool-selection verifier before executing high-impact actions.',
|
|
128
|
+
],
|
|
129
|
+
'AgentDebug / AgentRx',
|
|
130
|
+
),
|
|
131
|
+
'action.invalid_action': _mode(
|
|
132
|
+
'action.invalid_action',
|
|
133
|
+
'Invalid action',
|
|
134
|
+
'action',
|
|
135
|
+
'The selected action is syntactically or semantically invalid for the environment.',
|
|
136
|
+
['invalid action', 'bad command', 'action rejected'],
|
|
137
|
+
[
|
|
138
|
+
'Validate action schemas before execution and return actionable repair hints.',
|
|
139
|
+
'Record rejected actions to fine-tune tool descriptions and examples.',
|
|
140
|
+
],
|
|
141
|
+
'AgentDebug',
|
|
142
|
+
),
|
|
143
|
+
'action.format_error': _mode(
|
|
144
|
+
'action.format_error',
|
|
145
|
+
'Format error',
|
|
146
|
+
'action',
|
|
147
|
+
'The agent produced malformed JSON, arguments, commands, or protocol messages.',
|
|
148
|
+
['json parse', 'schema validation', 'malformed', 'format error'],
|
|
149
|
+
[
|
|
150
|
+
'Enforce structured output with schema validation and retry using validation errors.',
|
|
151
|
+
'Prefer typed tool APIs over free-form command strings where possible.',
|
|
152
|
+
],
|
|
153
|
+
'AgentDebug / AgentRx',
|
|
154
|
+
),
|
|
155
|
+
'action.parameter_error': _mode(
|
|
156
|
+
'action.parameter_error',
|
|
157
|
+
'Parameter error',
|
|
158
|
+
'action',
|
|
159
|
+
'The agent called the right tool with missing, hallucinated, or invalid parameters.',
|
|
160
|
+
['missing parameter', 'invalid parameter', 'hallucinated argument'],
|
|
161
|
+
[
|
|
162
|
+
'Validate parameters against tool schemas and ask for missing user/context fields.',
|
|
163
|
+
'Log parameter provenance so hallucinated arguments are visible in traces.',
|
|
164
|
+
],
|
|
165
|
+
'AgentDebug / AgentRx',
|
|
166
|
+
),
|
|
167
|
+
'system.tool_execution_error': _mode(
|
|
168
|
+
'system.tool_execution_error',
|
|
169
|
+
'Tool execution error',
|
|
170
|
+
'system',
|
|
171
|
+
'A tool, API, browser, environment, or dependency failed during execution.',
|
|
172
|
+
['timeout', 'exception', 'http error', 'tool failed', 'api error'],
|
|
173
|
+
[
|
|
174
|
+
'Capture tool stderr/status/latency and classify retryable versus non-retryable failures.',
|
|
175
|
+
'Add idempotency keys and rollback logic for side-effecting tools.',
|
|
176
|
+
],
|
|
177
|
+
'AgentDebug / AgentRx',
|
|
178
|
+
),
|
|
179
|
+
'system.llm_limit': _mode(
|
|
180
|
+
'system.llm_limit',
|
|
181
|
+
'LLM limit',
|
|
182
|
+
'system',
|
|
183
|
+
'The model hit context, rate, content, or capability limits.',
|
|
184
|
+
['context length', 'rate limit', 'token limit', 'model refusal'],
|
|
185
|
+
[
|
|
186
|
+
'Summarize or shard long context with explicit loss accounting.',
|
|
187
|
+
'Route limit failures to a fallback model or smaller prompt plan.',
|
|
188
|
+
],
|
|
189
|
+
'AgentDebug',
|
|
190
|
+
),
|
|
191
|
+
'system.environment_error': _mode(
|
|
192
|
+
'system.environment_error',
|
|
193
|
+
'Environment error',
|
|
194
|
+
'system',
|
|
195
|
+
'The external environment changed or returned noisy observations that misled the agent.',
|
|
196
|
+
['environment changed', 'noisy observation', 'browser state', 'missing element'],
|
|
197
|
+
[
|
|
198
|
+
'Log environment snapshots and compare state before and after actions.',
|
|
199
|
+
'Use stable APIs or accessibility trees when visual/browser observations are brittle.',
|
|
200
|
+
],
|
|
201
|
+
'AgentDebug / AgentSight',
|
|
202
|
+
),
|
|
203
|
+
'multiagent.handoff_loss': _mode(
|
|
204
|
+
'multiagent.handoff_loss',
|
|
205
|
+
'Handoff context loss',
|
|
206
|
+
'multiagent',
|
|
207
|
+
'A receiving agent loses task constraints, rejected alternatives, or decision rationale.',
|
|
208
|
+
['handoff missing context', 'lost rationale', 'context handoff'],
|
|
209
|
+
[
|
|
210
|
+
'Make handoff payloads typed and include goal, constraints, evidence, confidence, and open questions.',
|
|
211
|
+
'Validate that the receiver can restate critical constraints before proceeding.',
|
|
212
|
+
],
|
|
213
|
+
'MAST / Who&When',
|
|
214
|
+
),
|
|
215
|
+
'multiagent.role_drift': _mode(
|
|
216
|
+
'multiagent.role_drift',
|
|
217
|
+
'Role drift',
|
|
218
|
+
'multiagent',
|
|
219
|
+
'An agent acts outside its assigned responsibility or duplicates another agent role.',
|
|
220
|
+
['role drift', 'responsibility overlap', 'agent conflict'],
|
|
221
|
+
[
|
|
222
|
+
'Define ownership boundaries and permitted actions per agent.',
|
|
223
|
+
'Add supervisor checks for role violations and duplicate work.',
|
|
224
|
+
],
|
|
225
|
+
'MAST / Who&When',
|
|
226
|
+
),
|
|
227
|
+
'verification.missing_task_validation': _mode(
|
|
228
|
+
'verification.missing_task_validation',
|
|
229
|
+
'Missing task validation',
|
|
230
|
+
'verification',
|
|
231
|
+
'The system accepts task completion without an independent final-state check.',
|
|
232
|
+
['no verifier', 'unchecked final answer', 'missing validation'],
|
|
233
|
+
[
|
|
234
|
+
'Add final-state validation that is independent of the acting agent.',
|
|
235
|
+
'Store task-specific success criteria in the trace before execution begins.',
|
|
236
|
+
],
|
|
237
|
+
'MAST',
|
|
238
|
+
),
|
|
239
|
+
'verification.premature_stop': _mode(
|
|
240
|
+
'verification.premature_stop',
|
|
241
|
+
'Premature stop',
|
|
242
|
+
'verification',
|
|
243
|
+
'The agent stops before satisfying the task or exits after hitting a hidden stop condition.',
|
|
244
|
+
['premature stop', 'early termination', 'max steps'],
|
|
245
|
+
[
|
|
246
|
+
'Differentiate success, abandonment, and max-step termination in the run schema.',
|
|
247
|
+
'Trigger recovery planning when termination reason is not verified success.',
|
|
248
|
+
],
|
|
249
|
+
'MAST / AgentDebug',
|
|
250
|
+
),
|
|
251
|
+
'multimodal.perception_error': _mode(
|
|
252
|
+
'multimodal.perception_error',
|
|
253
|
+
'Multimodal perception error',
|
|
254
|
+
'multimodal',
|
|
255
|
+
'The agent misreads an image, UI, audio, video, or file artifact and acts on bad perception.',
|
|
256
|
+
['visual mismatch', 'ocr error', 'audio transcription error', 'ui element mismatch'],
|
|
257
|
+
[
|
|
258
|
+
'Store raw multimodal artifacts alongside extracted text and confidence scores.',
|
|
259
|
+
'Use redundant perception channels such as accessibility trees plus screenshots for UI tasks.',
|
|
260
|
+
],
|
|
261
|
+
'AgentDebugX proposed extension',
|
|
262
|
+
),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def get_failure_mode(mode_id: str) -> Optional[FailureMode]:
|
|
267
|
+
return SEED_FAILURE_MODES.get(mode_id)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def list_failure_modes() -> List[FailureMode]:
|
|
271
|
+
return list(SEED_FAILURE_MODES.values())
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Optional dashboard for AgentDebugX.
|
|
2
|
+
|
|
3
|
+
Run with::
|
|
4
|
+
|
|
5
|
+
pip install agentdebugx[ui]
|
|
6
|
+
agentdebug serve --store-sqlite .agentdebug/errors.sqlite
|
|
7
|
+
|
|
8
|
+
The server is a single-file FastAPI app + a no-build HTML/JS frontend served
|
|
9
|
+
from ``GET /``. Everything is local-only (loopback) by default.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from agentdebug.ui.server import build_app, serve
|
|
13
|
+
|
|
14
|
+
__all__ = ['build_app', 'serve']
|
agentdebug/ui/server.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Minimal FastAPI dashboard for AgentDebugX.
|
|
2
|
+
|
|
3
|
+
Endpoints:
|
|
4
|
+
|
|
5
|
+
* ``GET /`` — single-page HTML console.
|
|
6
|
+
* ``GET /api/v1/traces`` — list trace IDs in the store.
|
|
7
|
+
* ``GET /api/v1/traces/{tid}`` — fetch a trajectory + freshly analyzed report.
|
|
8
|
+
* ``GET /api/v1/traces/{tid}/raw``— raw trajectory JSON.
|
|
9
|
+
* ``GET /api/v1/taxonomy`` — list seed failure modes.
|
|
10
|
+
* ``GET /healthz`` — liveness.
|
|
11
|
+
|
|
12
|
+
The server is intentionally tiny and built on a no-build (vanilla JS) frontend
|
|
13
|
+
so it ships with the wheel and runs without `npm`.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any, Dict, List, Optional, cast
|
|
21
|
+
|
|
22
|
+
from agentdebug.analyzers import HeuristicAnalyzer
|
|
23
|
+
from agentdebug.models import AgentTrajectory, DiagnosticReport, model_to_json
|
|
24
|
+
from agentdebug.storage import JsonlTraceStore, SQLiteTraceStore, TraceStore
|
|
25
|
+
from agentdebug.taxonomy import SEED_FAILURE_MODES
|
|
26
|
+
|
|
27
|
+
LOG = logging.getLogger('agentdebug.ui')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _to_dict(model: Any) -> Dict[str, Any]:
|
|
31
|
+
"""Pydantic v1/v2 compatible serialization to dict."""
|
|
32
|
+
if hasattr(model, 'model_dump'):
|
|
33
|
+
return cast(Dict[str, Any], model.model_dump(mode='json'))
|
|
34
|
+
return cast(Dict[str, Any], json.loads(model.json()))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_app(store: TraceStore) -> Any:
|
|
38
|
+
try:
|
|
39
|
+
from fastapi import FastAPI, HTTPException
|
|
40
|
+
from fastapi.responses import HTMLResponse, JSONResponse
|
|
41
|
+
except ImportError as exc: # pragma: no cover - exercised in docs
|
|
42
|
+
raise ImportError(
|
|
43
|
+
'AgentDebugX UI requires `fastapi` and `uvicorn`. '
|
|
44
|
+
'Install with `pip install agentdebugx[ui]`.'
|
|
45
|
+
) from exc
|
|
46
|
+
|
|
47
|
+
app = FastAPI(
|
|
48
|
+
title='AgentDebugX',
|
|
49
|
+
description='Local debug console for agent trajectories.',
|
|
50
|
+
version='0.1.0',
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@app.get('/healthz')
|
|
54
|
+
def healthz() -> Dict[str, str]:
|
|
55
|
+
return {'status': 'ok'}
|
|
56
|
+
|
|
57
|
+
@app.get('/api/v1/traces')
|
|
58
|
+
def list_traces() -> Dict[str, List[str]]:
|
|
59
|
+
return {'traces': store.list_traces()}
|
|
60
|
+
|
|
61
|
+
@app.get('/api/v1/traces/{trace_id}')
|
|
62
|
+
def get_trace(trace_id: str) -> Dict[str, Any]:
|
|
63
|
+
trajectory = store.load_trajectory(trace_id)
|
|
64
|
+
if trajectory is None:
|
|
65
|
+
raise HTTPException(status_code=404, detail=f'unknown trace_id: {trace_id}')
|
|
66
|
+
report = HeuristicAnalyzer().analyze(trajectory)
|
|
67
|
+
return {
|
|
68
|
+
'trajectory': _to_dict(trajectory),
|
|
69
|
+
'report': _to_dict(report),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@app.get('/api/v1/traces/{trace_id}/raw')
|
|
73
|
+
def get_trace_raw(trace_id: str) -> JSONResponse:
|
|
74
|
+
trajectory = store.load_trajectory(trace_id)
|
|
75
|
+
if trajectory is None:
|
|
76
|
+
raise HTTPException(status_code=404, detail=f'unknown trace_id: {trace_id}')
|
|
77
|
+
return JSONResponse(content=_to_dict(trajectory))
|
|
78
|
+
|
|
79
|
+
@app.get('/api/v1/taxonomy')
|
|
80
|
+
def get_taxonomy() -> Dict[str, Any]:
|
|
81
|
+
return {
|
|
82
|
+
'modes': [_to_dict(m) for m in SEED_FAILURE_MODES.values()],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@app.get('/', response_class=HTMLResponse)
|
|
86
|
+
def index() -> str:
|
|
87
|
+
return _INDEX_HTML
|
|
88
|
+
|
|
89
|
+
return app
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def serve(
|
|
93
|
+
store: TraceStore,
|
|
94
|
+
*,
|
|
95
|
+
host: str = '127.0.0.1',
|
|
96
|
+
port: int = 7777,
|
|
97
|
+
) -> None:
|
|
98
|
+
try:
|
|
99
|
+
import uvicorn
|
|
100
|
+
except ImportError as exc: # pragma: no cover
|
|
101
|
+
raise ImportError(
|
|
102
|
+
'AgentDebugX UI requires `uvicorn`. '
|
|
103
|
+
'Install with `pip install agentdebugx[ui]`.'
|
|
104
|
+
) from exc
|
|
105
|
+
app = build_app(store)
|
|
106
|
+
LOG.info('Serving AgentDebugX console at http://%s:%s', host, port)
|
|
107
|
+
uvicorn.run(app, host=host, port=port, log_level='warning')
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def store_from_path(path: str) -> TraceStore:
|
|
111
|
+
"""Heuristic: ``.sqlite`` → SQLiteTraceStore; everything else → JSONL."""
|
|
112
|
+
if path.endswith(('.sqlite', '.db')):
|
|
113
|
+
return SQLiteTraceStore(path)
|
|
114
|
+
return JsonlTraceStore(path)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Single-file HTML console. Plain DOM + fetch — no build step required.
|
|
118
|
+
_INDEX_HTML = """<!doctype html>
|
|
119
|
+
<html lang="en">
|
|
120
|
+
<head>
|
|
121
|
+
<meta charset="utf-8" />
|
|
122
|
+
<title>AgentDebugX Console</title>
|
|
123
|
+
<style>
|
|
124
|
+
:root { color-scheme: dark; --bg:#0e1117; --bg2:#161b22; --fg:#c9d1d9; --muted:#8b949e;
|
|
125
|
+
--acc:#58a6ff; --bad:#f85149; --warn:#d29922; --good:#3fb950; }
|
|
126
|
+
* { box-sizing: border-box; }
|
|
127
|
+
html, body { margin:0; padding:0; height:100%; background:var(--bg); color:var(--fg);
|
|
128
|
+
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
129
|
+
header { padding:10px 14px; border-bottom:1px solid #21262d; display:flex; align-items:center; gap:14px;}
|
|
130
|
+
header h1 { font-size:14px; margin:0; font-weight:600; letter-spacing:0.4px; }
|
|
131
|
+
.pill { font-size:11px; color:var(--muted); border:1px solid #30363d; padding:1px 6px; border-radius:10px; }
|
|
132
|
+
main { display:grid; grid-template-columns: 280px 1fr; height: calc(100% - 44px); }
|
|
133
|
+
aside { background:var(--bg2); border-right:1px solid #21262d; overflow:auto; }
|
|
134
|
+
aside ul { list-style:none; margin:0; padding:0; }
|
|
135
|
+
aside li { padding:8px 12px; border-bottom:1px solid #21262d; cursor:pointer; font-size:12px; }
|
|
136
|
+
aside li:hover { background:#1f2630; }
|
|
137
|
+
aside li.active { background:#1c2530; color:var(--acc); }
|
|
138
|
+
section { padding:14px 18px; overflow:auto; }
|
|
139
|
+
h2 { font-size:15px; margin:8px 0 6px; }
|
|
140
|
+
h3 { font-size:13px; margin:14px 0 6px; color:var(--muted); text-transform:uppercase; letter-spacing:0.7px; }
|
|
141
|
+
table { width:100%; border-collapse: collapse; font-size:12px; }
|
|
142
|
+
th, td { text-align:left; padding:5px 8px; border-bottom:1px solid #21262d; vertical-align: top; }
|
|
143
|
+
th { font-weight:600; color:var(--muted); font-size:11px; }
|
|
144
|
+
.step { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
|
|
145
|
+
.err { color: var(--bad); }
|
|
146
|
+
.warn { color: var(--warn); }
|
|
147
|
+
.good { color: var(--good); }
|
|
148
|
+
.badge { display:inline-block; padding:1px 6px; border-radius:8px; font-size:11px; background:#1f2630; color:var(--acc); margin-right:6px; }
|
|
149
|
+
.empty { color:var(--muted); padding:20px; text-align:center; }
|
|
150
|
+
pre { background:#1c2128; padding:10px; border-radius:6px; overflow:auto; font-size:11px; }
|
|
151
|
+
.summary { background:#1c2128; padding:10px 12px; border-left:3px solid var(--acc);
|
|
152
|
+
border-radius:0 4px 4px 0; font-size:13px; margin-bottom:8px; }
|
|
153
|
+
.root { background:#1c2128; padding:10px 12px; border-left:3px solid var(--warn);
|
|
154
|
+
border-radius:0 4px 4px 0; font-size:12px; margin-bottom:12px; }
|
|
155
|
+
.ev-event_id { color:var(--muted); font-size:10px; font-family:ui-monospace, monospace; }
|
|
156
|
+
</style>
|
|
157
|
+
</head>
|
|
158
|
+
<body>
|
|
159
|
+
<header>
|
|
160
|
+
<h1>AgentDebugX</h1>
|
|
161
|
+
<span class="pill" id="trace-count">…</span>
|
|
162
|
+
<span class="pill">v0.1</span>
|
|
163
|
+
</header>
|
|
164
|
+
<main>
|
|
165
|
+
<aside><ul id="trace-list"></ul></aside>
|
|
166
|
+
<section id="detail"><div class="empty">Select a trace from the left.</div></section>
|
|
167
|
+
</main>
|
|
168
|
+
<script>
|
|
169
|
+
async function api(path) {
|
|
170
|
+
const r = await fetch(path);
|
|
171
|
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
172
|
+
return r.json();
|
|
173
|
+
}
|
|
174
|
+
function fmt(v) {
|
|
175
|
+
if (v === null || v === undefined) return '';
|
|
176
|
+
if (typeof v === 'object') return JSON.stringify(v);
|
|
177
|
+
return String(v);
|
|
178
|
+
}
|
|
179
|
+
function truncate(s, n) { s = fmt(s); return s.length > n ? s.slice(0, n) + '…' : s; }
|
|
180
|
+
function escapeHtml(s) {
|
|
181
|
+
return String(s).replace(/[&<>'"]/g, c => ({'&':'&','<':'<','>':'>',"'":''','"':'"'})[c]);
|
|
182
|
+
}
|
|
183
|
+
async function loadTraceList() {
|
|
184
|
+
const data = await api('/api/v1/traces');
|
|
185
|
+
const ul = document.getElementById('trace-list');
|
|
186
|
+
ul.innerHTML = '';
|
|
187
|
+
document.getElementById('trace-count').textContent = data.traces.length + ' trace' + (data.traces.length === 1 ? '' : 's');
|
|
188
|
+
data.traces.forEach(tid => {
|
|
189
|
+
const li = document.createElement('li');
|
|
190
|
+
li.textContent = tid;
|
|
191
|
+
li.dataset.tid = tid;
|
|
192
|
+
li.onclick = () => { selectTrace(tid, li); };
|
|
193
|
+
ul.appendChild(li);
|
|
194
|
+
});
|
|
195
|
+
if (data.traces.length === 0) {
|
|
196
|
+
document.getElementById('detail').innerHTML = '<div class="empty">No traces in store.</div>';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function selectTrace(tid, li) {
|
|
200
|
+
document.querySelectorAll('aside li').forEach(el => el.classList.remove('active'));
|
|
201
|
+
li.classList.add('active');
|
|
202
|
+
document.getElementById('detail').innerHTML = '<div class="empty">Loading…</div>';
|
|
203
|
+
try {
|
|
204
|
+
const data = await api('/api/v1/traces/' + encodeURIComponent(tid));
|
|
205
|
+
renderTrace(data.trajectory, data.report);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
document.getElementById('detail').innerHTML = '<div class="empty err">' + e + '</div>';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function renderTrace(traj, report) {
|
|
211
|
+
let html = '';
|
|
212
|
+
html += '<h2>' + escapeHtml(traj.trace_id) + '</h2>';
|
|
213
|
+
html += '<div><span class="badge">' + escapeHtml(traj.framework || 'unknown') + '</span>';
|
|
214
|
+
html += '<span class="badge">' + (traj.events?.length || 0) + ' events</span></div>';
|
|
215
|
+
if (traj.goal) html += '<p style="color:var(--muted);margin-top:4px;font-size:12px;">' + escapeHtml(traj.goal) + '</p>';
|
|
216
|
+
html += '<div class="summary"><b>Summary:</b> ' + escapeHtml(report.summary || '—') + '</div>';
|
|
217
|
+
if (report.root_cause_event_id) {
|
|
218
|
+
html += '<div class="root"><b>Root cause:</b> agent=<code>' + escapeHtml(report.root_cause_agent || '') + '</code>';
|
|
219
|
+
html += ', step=<code>' + escapeHtml(String(report.root_cause_step_index)) + '</code>';
|
|
220
|
+
html += ', event=<span class="ev-event_id">' + escapeHtml(report.root_cause_event_id) + '</span></div>';
|
|
221
|
+
}
|
|
222
|
+
html += '<h3>Events</h3><table><thead><tr><th>#</th><th>Agent</th><th>Type</th><th>Module</th><th>Input</th><th>Output</th><th>Error</th></tr></thead><tbody>';
|
|
223
|
+
for (const ev of traj.events || []) {
|
|
224
|
+
html += '<tr>';
|
|
225
|
+
html += '<td class="step">' + (ev.step_index ?? '') + '</td>';
|
|
226
|
+
html += '<td>' + escapeHtml(ev.agent_name || '') + '</td>';
|
|
227
|
+
html += '<td>' + escapeHtml(ev.event_type || '') + '</td>';
|
|
228
|
+
html += '<td>' + escapeHtml(ev.module || '') + '</td>';
|
|
229
|
+
html += '<td>' + escapeHtml(truncate(ev.input, 80)) + '</td>';
|
|
230
|
+
html += '<td>' + escapeHtml(truncate(ev.output, 80)) + '</td>';
|
|
231
|
+
const err = ev.error ? '<span class="err">' + escapeHtml(truncate(ev.error, 80)) + '</span>' : '';
|
|
232
|
+
html += '<td>' + err + '</td>';
|
|
233
|
+
html += '</tr>';
|
|
234
|
+
}
|
|
235
|
+
html += '</tbody></table>';
|
|
236
|
+
html += '<h3>Findings</h3>';
|
|
237
|
+
if ((report.findings || []).length === 0) {
|
|
238
|
+
html += '<div class="empty">No findings.</div>';
|
|
239
|
+
} else {
|
|
240
|
+
html += '<table><thead><tr><th>Mode</th><th>Family</th><th>Step</th><th>Agent</th><th>Confidence</th><th>Evidence</th><th>Suggestion</th></tr></thead><tbody>';
|
|
241
|
+
for (const f of report.findings) {
|
|
242
|
+
html += '<tr>';
|
|
243
|
+
html += '<td><code>' + escapeHtml(f.failure_mode?.mode_id || '') + '</code></td>';
|
|
244
|
+
html += '<td>' + escapeHtml(f.failure_mode?.family || '') + '</td>';
|
|
245
|
+
html += '<td class="step">' + (f.step_index ?? '') + '</td>';
|
|
246
|
+
html += '<td>' + escapeHtml(f.agent_name || '') + '</td>';
|
|
247
|
+
html += '<td>' + (typeof f.confidence === 'number' ? f.confidence.toFixed(2) : '') + '</td>';
|
|
248
|
+
html += '<td>' + escapeHtml((f.evidence || []).join('; ')) + '</td>';
|
|
249
|
+
html += '<td>' + escapeHtml(f.suggestion || '') + '</td>';
|
|
250
|
+
html += '</tr>';
|
|
251
|
+
}
|
|
252
|
+
html += '</tbody></table>';
|
|
253
|
+
}
|
|
254
|
+
document.getElementById('detail').innerHTML = html;
|
|
255
|
+
}
|
|
256
|
+
loadTraceList();
|
|
257
|
+
</script>
|
|
258
|
+
</body>
|
|
259
|
+
</html>
|
|
260
|
+
"""
|