Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions qitos/qita/_cli_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,61 @@ def _render_run_html(payload: Dict[str, Any], embedded: bool) -> str:
if(!thought) return (summary || '<div class="muted">No explicit thought.</div>');
return '<div style="white-space:pre-wrap;line-height:1.6;background:#0b1220;border:1px solid #1c2b44;border-radius:8px;padding:10px;color:#cfe6ff">'+esc(thought)+'</div>' + summary;
}}
function modelInputPayloads(events){{
const out = [];
const es = Array.isArray(events) ? events : [];
for(const e of es){{
const payload = e && typeof e.payload === 'object' ? e.payload : null;
if(payload && String(payload.stage || '') === 'model_input') out.push(payload);
}}
return out;
}}
function renderModelMessagePreview(messages){{
const msgs = Array.isArray(messages) ? messages : [];
if(!msgs.length) return '<div class="muted">No message list recorded.</div>';
const blocks = [];
for(let i=0;i<msgs.length;i+=1){{
const msg = msgs[i] && typeof msgs[i] === 'object' ? msgs[i] : {{}};
const role = msg.role !== undefined ? String(msg.role) : 'message';
const content = msg.content !== undefined ? msg.content : '';
blocks.push(
'<details class="raw">' +
'<summary class="muted">message ' + i + ' · ' + esc(role) + '</summary>' +
'<pre>' + esc(typeof content === 'string' ? content : JSON.stringify(content, null, 2)) + '</pre>' +
'</details>'
);
}}
return blocks.join('');
}}
function renderModelInput(events){{
const inputs = modelInputPayloads(events);
if(!inputs.length){{
return '<div class="muted">No model input recorded for this step. This usually means the step was handled by the harness or a tool path without calling the model.</div>';
}}
const blocks = [];
for(let i=0;i<inputs.length;i+=1){{
const payload = inputs[i] || {{}};
const ctx = payload.context && typeof payload.context === 'object' ? payload.context : {{}};
const rows = [];
rows.push(kvRow('model input event', i + 1));
if(payload.history_message_count !== undefined) rows.push(kvRow('history messages', payload.history_message_count));
if(Array.isArray(payload.messages)) rows.push(kvRow('messages sent', payload.messages.length));
if(ctx.input_tokens_total !== undefined) rows.push(kvRow('input tokens', ctx.input_tokens_total));
if(ctx.prepared_tokens !== undefined) rows.push(kvRow('prepared tokens', ctx.prepared_tokens));
if(ctx.history_tokens !== undefined) rows.push(kvRow('history tokens', ctx.history_tokens));
if(ctx.occupancy_ratio !== undefined) rows.push(kvRow('context used', ((Number(ctx.occupancy_ratio) || 0) * 100).toFixed(1) + '%'));
const prepared = payload.prepared_full || payload.prepared || '';
const preparedBlock = prepared
? '<div style="margin-top:8px"><div class="muted" style="margin-bottom:4px">prepared text from agent.prepare(state)</div><pre>' + esc(String(prepared)) + '</pre></div>'
: '<div class="muted" style="margin-top:8px">No prepared text recorded.</div>';
const messagesBlock =
'<details class="raw" style="margin-top:8px"><summary class="muted">Actual messages sent to model</summary>' +
renderModelMessagePreview(payload.messages) +
'</details>';
blocks.push('<div class="item">' + kvBlock(rows) + preparedBlock + messagesBlock + '</div>');
}}
return '<div class="list">' + blocks.join('') + '</div>';
}}
function renderAction(actions){{
const label = firstActionLabel(actions);
if(!label) return '<div class="muted">No action.</div>';
Expand Down Expand Up @@ -1870,6 +1925,7 @@ def _render_run_html(payload: Dict[str, Any], embedded: bool) -> str:
let h = '<div class="card-head"><div class="step">STEP ' + it.sid + '</div><div class="muted">events ' + it.events.length + '</div></div>';
if(showObs) h += sectionHtml('State', renderState(obsInput), obsInput, 'state', collapsedAll);
h += sectionHtml('Prompt', renderPromptMetadata(it.step.prompt_metadata || {{}}), it.step.prompt_metadata || {{}}, 'prompt', collapsedAll);
h += sectionHtml('Model Input', renderModelInput(it.events), modelInputPayloads(it.events), 'model_input', collapsedAll);
h += sectionHtml('Visual Assets', renderVisualAssets(it.step), {{visual_assets: it.step.visual_assets || [], observation_modalities: it.step.observation_modalities || [], model_input_modalities: it.step.model_input_modalities || [], model_input_visual_count: it.step.model_input_visual_count || 0}}, 'visual_assets', collapsedAll);
h += sectionHtml('Thought', renderThought(d, it.events, it.step), d, 'thought', collapsedAll);
h += sectionHtml('Parser Diagnostics', renderParserDiagnostics(it.step.parser_diagnostics || {{}}), it.step.parser_diagnostics || {{}}, 'parser', collapsedAll);
Expand Down
4 changes: 4 additions & 0 deletions tests/test_qita_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def _make_run(root: Path, run_id: str) -> Path:
)
(run / "events.jsonl").write_text(
'{"step_id":0,"phase":"INIT","ok":true,"ts":"x"}\n'
'{"step_id":0,"phase":"DECIDE","ok":true,"ts":"x1","payload":{"stage":"model_input","prepared":"Task: inspect the run","prepared_full":"Task: inspect the run\\nStep: 0","history_message_count":1,"messages":[{"role":"system","content":"You are an agent."},{"role":"user","content":"Task: inspect the run"}],"context":{"input_tokens_total":22,"prepared_tokens":6,"history_tokens":8,"occupancy_ratio":0.12}}}\n'
'{"step_id":0,"phase":"DECIDE","ok":true,"ts":"y","payload":{"stage":"model_output","raw_output":"Thought: inspect the run","model_response":{"text":"Thought: inspect the run","usage":{"prompt_tokens":10,"completion_tokens":4,"total_tokens":14},"finish_reason":"stop","tool_calls":[{"id":"call_1","type":"function","function":{"name":"visit_url","arguments":"{\\"url\\":\\"https://example.com\\"}"}}],"model_name":"demo-model","provider":"demo-provider","metadata":{}},"context":{"input_tokens_total":3200,"occupancy_ratio":0.74}}}\n',
encoding="utf-8",
)
Expand Down Expand Up @@ -251,6 +252,9 @@ def test_render_pages(tmp_path: Path):
assert "decision_source" in view
assert "native_tool_call_used" in view
assert "tool_delivery" in view
assert "Model Input" in view
assert "prepared text from agent.prepare(state)" in view
assert "Actual messages sent to model" in view
assert "Visual Assets" in view
assert "grounding metadata" in view
assert "critic retries" in view
Expand Down
Loading