anthropics / anthropics/claude-cookbooks

[BUG] claude_agent_sdk/05_Building_a_session_browser.ipynb, cell 14: message renderer prints the user's prompt as blank whenever `content` is a string

未关闭
#863 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
bug
主要语言
Jupyter Notebook
星标
52.7k
派生
6.3k
平均合并
25 分钟
30 天内合并 PR
6

描述

### Affected Notebook/File

claude_agent_sdk/05_Building_a_session_browser.ipynb

### Bug Description

Cell 14 renders each message like this:

for m in messages:
role = m.type
# content is a list of blocks; pull out the text ones
text_parts = [
b.get("text", "")
for b in m.message.get("content", [])
if isinstance(b, dict) and b.get("type") == "text"
]
text = " ".join(text_parts).strip()
print(f"[{role:>9}] {text[:100]}")

`m.message` is whatever `get_session_messages()` read back from the session's JSONL transcript,
completely unchanged: `SessionMessage.message` is typed `Any` and documented "Raw Anthropic API
message dict (role, content, etc.)" (`claude_agent_sdk/types.py`), and the function that builds
it does no normalization at all (`claude_agent_sdk/_internal/sessions.py`:
`message=entry.get("message")`).

The Anthropic Messages API's `content` field is documented as string-or-block-array, and a
plain, interactively-typed prompt — i.e. the normal case — is stored as a plain string. When
`content` is a string, the list comprehension iterates it character by character;
`isinstance(b, dict)` is `False` for every character, so `text_parts` is always `[]`. Nothing
raises — the cell just prints an empty line where the prompt should be.

The notebook's own markdown even names the shape this breaks on: cell 13 describes `message` as
"a `message` dict in the same shape as the Anthropic Messages API (`role`, `content`)" — and the
Messages API's own `content` field is string-or-array — while cell 14's inline comment asserts
the opposite ("content is a list of blocks").

I checked every Claude Code session transcript on my machine — 388 files — and every single one
renders the opening user turn as an empty string: 384 because that message's `content` is a
plain string, and the remaining 4 because it happens to be an array whose blocks are all
`tool_result` with no `text` block either. Neither shape survives the filter.

Expected: the user's actual prompt is printed.
Actual: an empty string, silently, on effectively every real session.

Suggested fix — branch on the type before filtering:

content = m.message.get("content", [])
if isinstance(content, str):
text = content.strip()
else:
text = " ".join(
b.get("text", "") for b in content if isinstance(b, dict) and b.get("type") == "text"
).strip()

### Steps to Reproduce

No SDK install and no API key needed — get_session_messages() just replays the raw JSONL, so
applying cell 14's own filter directly to any transcript your own Claude Code CLI has already
written reproduces it exactly:

import glob, json, os

paths = glob.glob(os.path.join(os.path.expanduser("~"), ".claude", "projects", "*", "*.jsonl"))
with open(paths[0], encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
entry = json.loads(line)
if entry.get("type") != "user" or entry.get("isMeta") or entry.get("isSidechain"):
continue
content = entry["message"].get("content")
print("typeof content:", type(content).__name__)
text_parts = [b.get("text", "") for b in (content or []) if isinstance(b, dict) and b.get("type") == "text"]
print("cell 14 would print:", repr(" ".join(text_parts).strip()))
break

On my machine this printed `typeof content: str` / `cell 14 would print: ''` for all 388
transcripts checked (script above tested and run, not just described).

Or with the notebook itself: run cells 1-12 to create the demo sessions, then run cell 14 and
compare its printed `[ user]` line against the prompt you actually typed into `query()` two
cells earlier.

### Error Message

```shell
N/A — nothing raises. That's what makes this one easy to miss: the notebook ships with every
cell's output cleared, so a reader only sees the blank line once they run it themselves, and it
reads as a formatting quirk rather than a bug.
```

### Environment

- Python version: 3.11+ (this recipe's own prerequisite); the reproduction above needs nothing
beyond the standard library
- OS: not version-sensitive — reproduces on any machine that has used the Claude Code CLI
- claude-agent-sdk version: reproduces at this notebook's own pinned floor (>=0.1.51) through
the current release (0.2.152) — the file and line that hand back the raw value are unchanged
across that whole range

### Would you be willing to submit a PR to fix this?

No, I'm just reporting

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。