Accepts model options as an explicit dict alongside the existing
**kwargs form. The kwargs form continues to work unchanged for
backwards compatibility but is no longer documented. Mixing the two
forms with overlapping keys raises TypeError.
Applies to Model.prompt, Conversation.prompt, Response.reply and
their async equivalents. .chain() already used this pattern.
Co-authored-by: Claude <noreply@anthropic.com>
Adds a `reasoning` column to the responses table (migration m022)
populated from concatenated visible-reasoning text in the assembled
message. `llm logs --md` renders it under a `## Reasoning` heading
above the response when present.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sync: response.messages() forces execution if not drained, so callers
no longer have to remember to call .text() first. Async: `await
response.messages()` awaits the force.
Internal sync paths (_response_to_dict, _chain_for_tool_results,
_build_full_chain, Response.reply, AsyncResponse.reply) use a new
private _messages_now() helper that assumes the response is already
drained, so they don't have to await on async responses.
Drops the now-obsolete "accessing .messages on un-awaited
AsyncResponse raises" parity test — that constraint goes away with
the method form.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Zero-arg sugar: when a response made tool calls and tool_results= is
not passed, reply() runs self.execute_tool_calls() and threads the
results into the next turn. Pass tool_results= explicitly to skip
the auto-execute path (e.g. for mutated or synthetic results). Also
forwards self.prompt.tools to the next turn so the model can call
the same tools again, mirroring Conversation.prompt's tools-or-self
rule.
AsyncResponse.reply() is now an awaitable coroutine — `await
response.reply(...)` — so the auto-execute path can `await
self.execute_tool_calls()` internally. This is a non-shipped API
break: existing async-reply callers in the test suite updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Substantially expanded docs/plugins/advanced-model-plugins.md with
the plugin-author guide to the new machinery. Distilled from the
actual llm-anthropic and llm-gemini implementations so plugin
authors have a recipe that mirrors what real providers do.
New / updated sections (doc grew 317 → 663 lines):
- "Attachments from previous conversations" trimmed to a pointer at
prompt.messages — the old pattern of walking conversation.responses
is replaced by the canonical chain view.
- "Structured messages and streaming events"
- StreamEvent contract, backward compat for plain-str plugins
- Full field reference (type / chunk / part_index / tool_call_id /
provider_metadata / server_executed / tool_name)
- part_index allocation rules with two worked examples:
block-keyed (Anthropic-style content_block_start events) and
kind-tracking (Gemini-style chunk-per-part)
- Reasoning: streamed text + opaque _reasoning_token_count, with
the OpenAI-specific gotcha about reading reasoning_tokens BEFORE
set_usage mutates the dict
- Tool calls — tool_call_name + tool_call_args pattern, reminder
that response.add_tool_call() is separately required for
chain-execution
- Server-side tools — server_executed=True on events, raw payload
in provider_metadata for round-trip, post-stream emission for
providers that don't stream tool-result contents
- Opaque provider_metadata — Anthropic signature, Gemini
thoughtSignature, OpenAI encrypted_content — with namespacing
guidance
- Non-streaming path — one event per content block
- "Consuming prompt.messages in build_messages"
- The invariant: prompt.messages is always the full chain; don't
walk conversation.responses (would double-emit)
- Worked build_messages example that dispatches per Part subtype
- Role mapping across OpenAI / Anthropic / Gemini conventions
- Role-alternation merging
- "Restoring opaque metadata on subsequent requests"
- How to read provider_metadata off prior-turn Parts and fold the
signatures back into the outgoing request body
670 tests still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lock in the "application does its own persistence without SQLite"
story with:
- Five integration tests covering: Message.to_dict / from_dict
round-trip, re-inflating messages and continuing a conversation,
tool calls + results round-trip, redacted reasoning Parts
round-trip, and provider_metadata round-trip.
- A new "Structured messages and streaming events" section in
docs/python-api.md walking users through messages=[...],
stream_events(), response.messages, and the JSON round-trip
pattern.
No new code — the machinery landed in Phases 1-3. This phase is
validation + documentation.
580 tests passing overall.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>