feat: inspect the full prompt in the live log

The "context built" event now carries the fully-rendered prompt (persona, gists,
recalled details, recent turns, the new message) plus a total char count. The
log panel renders it as a collapsed "view full prompt" block — clean by default,
one click to see exactly what hit the model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 23:52:35 +00:00
parent d7c258eba0
commit 236a16b331
3 changed files with 38 additions and 6 deletions
+11 -5
View File
@@ -30,6 +30,11 @@ def _detail_note(exchanges: list[memory.Exchange]) -> Message:
return {"role": "system", "content": body}
def _render(messages: list[Message]) -> str:
"""Human-readable dump of the exact prompt, for the live-log inspector."""
return "\n\n".join(f"[{m['role']}]\n{m['content']}" for m in messages)
def build_messages(session_id: str, user_msg: str) -> list[Message]:
"""Assemble the full, tiered message list for one turn."""
messages: list[Message] = [{"role": "system", "content": persona.system_prompt()}]
@@ -51,16 +56,17 @@ def build_messages(session_id: str, user_msg: str) -> list[Message]:
if recalled:
messages.append(_detail_note(recalled))
logbus.log(
"debug", "context built",
recent=len(recent), summaries=len(summaries), details=len(recalled),
)
# Tier 3: current session, full fidelity.
for ex in recent:
messages.append({"role": ex.role, "content": ex.content})
messages.append({"role": "user", "content": user_msg})
logbus.log(
"debug", "context built",
recent=len(recent), summaries=len(summaries), details=len(recalled),
chars=sum(len(m["content"]) for m in messages), detail=_render(messages),
)
return messages
+5 -1
View File
@@ -734,7 +734,10 @@
const level = event.level || 'info';
const time = new Date((event.ts || 0) * 1000).toLocaleTimeString();
const fields = event.fields || {};
const fields = Object.assign({}, event.fields || {});
// `detail` is rendered as an expandable block, not an inline field.
const detail = fields.detail;
delete fields.detail;
const fieldStr = Object.keys(fields).length
? Object.entries(fields).map(([k, v]) => `${k}=${v}`).join(' ')
: '';
@@ -746,6 +749,7 @@
<span class="log-level log-level-${level}">${escapeHtml(level)}</span>
<span class="log-msg">${escapeHtml(event.msg || '')}</span>
${fieldStr ? `<span class="log-fields">${escapeHtml(fieldStr)}</span>` : ''}
${detail ? `<details class="log-detail"><summary>view full prompt</summary><pre>${escapeHtml(detail)}</pre></details>` : ''}
`;
thinkingContent.appendChild(eventDiv);
+22
View File
@@ -941,3 +941,25 @@ select:hover {
.log-error .log-level, .log-error .log-msg { color: #fca5a5; }
.log-system { border-left-color: #00ff66; }
.log-system .log-level { color: #00ff66; }
.log-detail { width: 100%; margin-top: 4px; }
.log-detail summary {
cursor: pointer;
color: var(--accent);
font-size: 0.72rem;
user-select: none;
}
.log-detail pre {
margin: 6px 0 0;
padding: 8px;
max-height: 340px;
overflow: auto;
background: rgba(0,0,0,0.25);
border-left: 2px solid var(--accent);
border-radius: 4px;
font-size: 0.72rem;
line-height: 1.4;
white-space: pre-wrap;
word-break: break-word;
color: var(--text);
}