New option: from llm.parts import Text then Text.system("..."),
Text.user("..."). Combined with role classmethods this is very
concise. Notes the Attachment/ToolCall name collision issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New sections cover:
- response.parts for structured access to text, reasoning, tool calls
- ReasoningPart with both streamed (Anthropic) and redacted (OpenAI) examples
- ToolCallPart and ToolResultPart
- stream_events() / astream_events() for rich real-time streaming
- parts= parameter for constructing prompts with typed parts
- prompt.input_parts for unified view of all input parts
- All part types listed with serialization methods
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add notes about: requiring response.add_tool_call() alongside StreamEvents
for tool calls, yield from limitation in async generators, filtering empty
text chunks, and astream_events() vs stream_events() for async.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers: importing StreamEvent, converting yield str to yield
StreamEvent for text/reasoning/tool calls/server-side tools, part
index tracking, handling opaque reasoning tokens, testing patterns,
and the special case of plugins that inherit from built-in models.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents the " " space character that gets persisted as a real
TextPart when the Anthropic plugin works around a text-joining bug
in tool call chains. Lists four options to fix, recommends moving
the spacing concern to the display layer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
display_stream_events() helper handles writing text to stdout and
reasoning to stderr with proper newlines at each reasoning-to-text
transition. Used by sync prompt and chat streaming loops. Async prompt
loop has the same logic inline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Streaming loops in prompt and chat commands now use stream_events()
instead of __iter__. Reasoning events are displayed on stderr in
dim text. Text events go to stdout as before.
New flags:
-R / --no-reasoning Suppress reasoning output on stderr
-S / --no-stream (shortcut for existing --no-stream)
-L (shortcut for existing -n/--no-log)
ChainResponse.stream_events() and AsyncChainResponse.astream_events()
added so tool-calling flows also surface reasoning events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New m022_parts_table migration creates a parts table with direction
(input/output), role, part_type, content, content_json, tool_call_id,
and server_executed columns.
log_to_db() writes both input parts (from prompt.input_parts) and
output parts (from response.parts) to the table. from_row() loads
output parts and makes them available via the parts property.
Tested live: parts table created, input/output parts written and
loaded correctly with gpt-5.4-mini via CLI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
model.prompt() and conversation.prompt() now accept a parts= parameter
for passing explicit Part objects. Prompt.input_parts synthesizes a
unified list of input Parts from prompt=, system=, attachments=, and
parts= parameters.
prompt= remains sugar for a TextPart(role="user"). system= becomes
a TextPart(role="system"). attachments= become AttachmentParts.
All parameters combine (parts first, then system, then prompt, then
attachments).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents findings from all three plugin implementations: OpenAI
(set_usage mutation, opaque reasoning), Anthropic (streamed thinking,
server-side tools), Gemini (opaque thinking, complete parts per chunk).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chat.execute() and AsyncChat.execute() now yield StreamEvent instead
of bare strings. Text chunks, tool call names/args are all emitted
as typed events. Reasoning token counts from usage data are stored
on the response and _build_parts() prepends a redacted ReasoningPart.
StreamEvent gains server_executed and tool_name fields for use by
plugins with server-side tool execution.
Tested live against gpt-5.4-mini: text streaming, tool calls, and
reasoning tokens (with reasoning_effort='high') all work correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents design decisions, live testing observations against
gpt-5.4-mini, and things to watch for in Phase 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Response.__iter__ now handles str | StreamEvent from execute().
Plain str yields are backward compatible. StreamEvent yields are
processed by the assembler: text events yield as str to consumers,
reasoning/tool_call/tool_result events are filtered from __iter__
but available via stream_events(). Parts are assembled from events
after completion. Same changes for AsyncResponse.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Response.stream_events() yields StreamEvents wrapping text chunks.
AsyncResponse.astream_events() is the async equivalent.
response.parts returns a list of Part objects after completion.
Currently only handles plain str chunks (Phase 1 baseline).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 of the parts project: define the Part dataclass hierarchy
(TextPart, ReasoningPart, ToolCallPart, ToolResultPart, AttachmentPart)
and StreamEvent in a new llm/parts.py module. All Part types have
to_dict()/from_dict() for JSON roundtripping. 14 tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add type annotations for OpenAI Chat/AsyncChat/Completion execute methods
* Add type hint for OpenAI _Shared class
* cast(Response) to make mypy happy
Co-authored-by: Simon Willison <swillison@gmail.com>