提交

提交图

1055 次代码提交

作者 SHA1 备注 提交日期
Simon Willison d8ada58d32 Experimental prompt.display_reasoning mechanism
Test / test (ubuntu-latest, 3.14) (push) Has been cancelled
Test / test (windows-latest, 3.10) (push) Has been cancelled
Test / test (windows-latest, 3.11) (push) Has been cancelled
Test / test (windows-latest, 3.12) (push) Has been cancelled
Test / test (windows-latest, 3.13) (push) Has been cancelled
Test / test (windows-latest, 3.14) (push) Has been cancelled
Test / test (macos-latest, 3.10) (push) Has been cancelled
Test / test (macos-latest, 3.11) (push) Has been cancelled
Test / test (macos-latest, 3.12) (push) Has been cancelled
Test / test (macos-latest, 3.13) (push) Has been cancelled
Test / test (macos-latest, 3.14) (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
So you can turn off display reasoning and compatible plugins can then
opt not to request reasoning summaries from their models, even as
they do actually run reasoning.
2026-05-05 14:34:04 -07:00
Simon Willison 9a5c24e20c Release 0.32a1
Test / test (macos-latest, 3.10) (push) Has been cancelled
Test / test (macos-latest, 3.11) (push) Has been cancelled
Test / test (macos-latest, 3.12) (push) Has been cancelled
Test / test (macos-latest, 3.13) (push) Has been cancelled
Test / test (macos-latest, 3.14) (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
Test / test (ubuntu-latest, 3.14) (push) Has been cancelled
Test / test (windows-latest, 3.10) (push) Has been cancelled
Test / test (windows-latest, 3.11) (push) Has been cancelled
Test / test (windows-latest, 3.12) (push) Has been cancelled
Test / test (windows-latest, 3.13) (push) Has been cancelled
Test / test (windows-latest, 3.14) (push) Has been cancelled
Refs #1426

Refs https://github.com/simonw/llm-anthropic/issues/68
0.32a1
2026-04-29 16:52:09 -07:00
Simon Willison 4d92df12a6 Rebuild prompt.messages chain when loading logged conversations
Each row stores only its current-turn inputs, so a loaded tool-result
response began with an orphan tool_result. `llm -c` then sent a request
with an unexpected tool_use_id. Stitch each response's messages onto the
previous response's chain plus its assistant output during load.

Closes #1426

Refs https://github.com/simonw/llm-anthropic/issues/68

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:39:03 -07:00
Simon Willison cce6ed956a Ran cog 2026-04-29 12:02:36 -07:00
Simon Willison 35c35dac43 Release 0.32a0
Test / test (macos-latest, 3.10) (push) Has been cancelled
Test / test (macos-latest, 3.11) (push) Has been cancelled
Test / test (macos-latest, 3.12) (push) Has been cancelled
Test / test (macos-latest, 3.13) (push) Has been cancelled
Test / test (macos-latest, 3.14) (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
Test / test (ubuntu-latest, 3.14) (push) Has been cancelled
Test / test (windows-latest, 3.10) (push) Has been cancelled
Test / test (windows-latest, 3.11) (push) Has been cancelled
Test / test (windows-latest, 3.12) (push) Has been cancelled
Test / test (windows-latest, 3.13) (push) Has been cancelled
Test / test (windows-latest, 3.14) (push) Has been cancelled
Closes #1314, #506, #1278, #894, #813, #468, #346, #716, #770, #867, #938, #1033, #937

Refs #1067, #1080
0.32a0
2026-04-29 11:55:20 -07:00
Simon Willison 31a4ea73c3 prompt(messages=[]) and response.stream_events() refactor
See https://github.com/simonw/llm/pull/1411 for details.

Closes #506
2026-04-28 17:51:28 -07:00
Simon Willison 926394aecd Tweaked some overly-promotional language
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
Test / test (ubuntu-latest, 3.14) (push) Has been cancelled
Test / test (windows-latest, 3.10) (push) Has been cancelled
Test / test (windows-latest, 3.11) (push) Has been cancelled
Test / test (windows-latest, 3.12) (push) Has been cancelled
Test / test (windows-latest, 3.13) (push) Has been cancelled
Test / test (windows-latest, 3.14) (push) Has been cancelled
Test / test (macos-latest, 3.10) (push) Has been cancelled
Test / test (macos-latest, 3.11) (push) Has been cancelled
Test / test (macos-latest, 3.12) (push) Has been cancelled
Test / test (macos-latest, 3.13) (push) Has been cancelled
Test / test (macos-latest, 3.14) (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
2026-04-28 17:46:07 -07:00
Simon Willison 838d5575e6 Test to_dict() does not emit keys absent from the TypedDict 2026-04-28 17:45:55 -07:00
Simon Willison 02c9af048f A bunch of documentation edits 2026-04-28 17:07:51 -07:00
Simon Willison 5789bc9e36 It's actually the 0.32 alpha 2026-04-28 16:59:11 -07:00
Simon Willison 35c7533c18 Edited the changelog for 0.32a0 2026-04-28 16:49:05 -07:00
Simon Willison 3497c22e8c Black 2026-04-28 16:48:48 -07:00
Simon Willison 7c471496f4 Do not have xPart classes as llm. imports 2026-04-28 16:44:21 -07:00
Simon Willison 29e3787be5 Draft changelog for 0.32a0 2026-04-28 16:32:52 -07:00
Simon Willison 0fa7ccf58f Ran Black 2026-04-28 14:42:39 -07:00
Simon Willison 49dd796264 Strip trailing whitespace from reasoning when rendering markdown logs
Providers (e.g. Gemini) often emit thought text with trailing newlines.
Concatenated and combined with click.echo's own newline, that produced
several blank lines before the `## Response` heading. rstrip() at render
time tightens the gap to a single blank line; the stored reasoning column
keeps the provider's text verbatim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:39:56 -07:00
Simon Willison beaec1e20c Lint fixes: mypy, ruff, black
- Drop the placeholder messages() declaration on _BaseResponse so
  AsyncResponse.messages() (an async coroutine) no longer trips
  the mypy override check. text/json/tool_calls already follow
  this pattern.
- Remove three unused imports flagged by ruff in test_parts.py.
- Apply pending black reformats across the tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 14:27:30 -07:00
Simon Willison b97a6902f5 Persist visible reasoning to logs and render in markdown
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>
2026-04-28 14:27:22 -07:00
Simon Willison 842ab2a93f response.messages is a method, matching .text() / .json() / .tool_calls()
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>
2026-04-28 14:04:35 -07:00
Simon Willison f3a0962162 response.reply() auto-executes pending tool calls
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>
2026-04-28 13:51:13 -07:00
Simon Willison a2547d8183 Drop token_count from ReasoningPart, use redacted marker StreamEvent
ReasoningPart.token_count duplicated info already on response.token_details
(reasoning_tokens), and the side-channel `response._reasoning_token_count`
attribute with its set_usage ordering footgun was the wrong shape. Replaced
with a clean StreamEvent.redacted=True marker that plugins yield like any
other event. The framework hoists redacted reasoning Parts to the start of
the assembled message so UIs render them before content, even though the
opaque count typically arrives at the end of the stream.

Also fix parallel tool calls emitted without tool_call_id (e.g. Gemini):
a fresh tool_call_name now always allocates a new index instead of falling
through to the prior tool-call group, so N parallel calls produce N
distinct ToolCallParts instead of one with concatenated names and args.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 13:28:55 -07:00
Simon Willison 3b0d0fa0a5 part_index is now (mostly) automatically assigned 2026-04-28 09:35:53 -07:00
Simon Willison 6567b60005 Merge remote-tracking branch 'origin/messages-refactor' into messages-refactor
# Conflicts:
#	docs/fragments.md
#	llm/default_plugins/openai_models.py
#	pyproject.toml
2026-04-28 07:34:02 -07:00
Simon Willison 13b10e097c Merge remote-tracking branch 'origin/main' into messages-refactor
# Conflicts:
#	llm/default_plugins/openai_models.py
2026-04-28 07:31:55 -07:00
Simon Willison 5ce40fd703 Release 0.31
Test / test (macos-latest, 3.10) (push) Has been cancelled
Test / test (macos-latest, 3.11) (push) Has been cancelled
Test / test (macos-latest, 3.12) (push) Has been cancelled
Test / test (macos-latest, 3.13) (push) Has been cancelled
Test / test (macos-latest, 3.14) (push) Has been cancelled
Test / test (ubuntu-latest, 3.10) (push) Has been cancelled
Test / test (ubuntu-latest, 3.11) (push) Has been cancelled
Test / test (ubuntu-latest, 3.12) (push) Has been cancelled
Test / test (ubuntu-latest, 3.13) (push) Has been cancelled
Test / test (ubuntu-latest, 3.14) (push) Has been cancelled
Test / test (windows-latest, 3.10) (push) Has been cancelled
Test / test (windows-latest, 3.11) (push) Has been cancelled
Test / test (windows-latest, 3.12) (push) Has been cancelled
Test / test (windows-latest, 3.13) (push) Has been cancelled
Test / test (windows-latest, 3.14) (push) Has been cancelled
Refs #1418
0.31
2026-04-24 16:33:05 -07:00
Simon Willison 706852ecea New image_detail low/high/auto/original option
Refs https://github.com/simonw/llm/issues/1418#issuecomment-4316983472
2026-04-24 16:22:43 -07:00
Simon Willison 87efea179d Cleaner dynamic self.Options building, refs #1418 2026-04-24 16:11:22 -07:00
Simon Willison 021a29d61a OpenAI verbosity option
Refs https://github.com/simonw/llm/issues/1418#issuecomment-4316867527
2026-04-24 16:08:26 -07:00
Simon Willison c9a3ac9fe0 New model: gpt-5.5 - refs #1418 2026-04-24 15:41:12 -07:00
Simon Willison 0cc5a1cdc8 Ignore non-llm folders in pyproject.toml 2026-04-24 12:19:16 -07:00
github-actions[bot] eb45de6bfc Ran cog 2026-04-22 17:18:08 +00:00
Simon Willison 639c2b1309 0.31a0.dev0 release
https://static.simonwillison.net/static/2026/llm-0.31a0.dev0-py3-none-any.whl
https://static.simonwillison.net/static/2026/llm-0.31a0.dev0.tar.gz
2026-04-22 10:16:51 -07:00
Simon Willison de63d8b69e Fixes for ruff 2026-04-22 10:13:39 -07:00
Simon Willison 38cf65adb1 Remove unneccassry exception catch 2026-04-22 10:10:45 -07:00
Simon Willison 5a92cdfc6e Improved some comments
Had a different model review them for accuracy
2026-04-22 08:55:38 -07:00
Simon Willison 211e678e07 Cleaned up tests and comments
Removed all mentions of 'phase'
2026-04-22 08:43:33 -07:00
Simon Willison d11b9a01c5 More documentation tweaks 2026-04-22 08:43:15 -07:00
github-actions[bot] 00e428896f Ran cog 2026-04-22 04:54:40 +00:00
Simon Willison 8c48dccc94 docs: advanced-model-plugins.md covers StreamEvent / prompt.messages / provider_metadata
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>
2026-04-21 21:54:01 -07:00
Simon Willison afc41b0c47 mypy fixes 2026-04-21 21:37:04 -07:00
Simon Willison 92f9359ba3 Documentation improvements 2026-04-21 21:24:50 -07:00
Simon Willison 65b8e37c79 Ran Black 2026-04-21 21:10:13 -07:00
Simon Willison d4f3f242ce Unwrap wrapped text in docs 2026-04-21 20:17:39 -07:00
Simon Willison 5648895125 Remove accidentally committed build/ directory and gitignore it
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 19:58:39 -07:00
github-actions[bot] 1c317ab3fe Ran cog 2026-04-22 00:08:41 +00:00
Simon Willison 3df8e4426b Chain tool-result turns now carry system + system_fragments forward
Bug: ChainResponse.responses() and AsyncChainResponse.responses()
built the follow-up Prompt (for tool-result turns inside the chain
loop) without propagating system= or system_fragments= from the
initial prompt. Adapters that read prompt.system directly — OpenAI's
Chat, for example, which sends system as its own message — saw an
empty system on every turn after the first, silently losing the
caller's instruction.

Fix: pass system=self.prompt._system and
system_fragments=self.prompt.system_fragments when constructing the
next Prompt. Same change on sync and async paths.

_chain_for_tool_results keeps building messages= from the prior
response's prompt.messages + messages, so adapters that read
prompt.messages (the Phase 7 canonical input) continue to work too —
this fix is specifically for the adapters that still use the
prompt.system legacy field.

Three regression tests (sync, sync+system_fragments, async) pin the
new behavior.

670 tests in llm core; llm-anthropic (32) and llm-gemini (50) still
green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:55:36 -07:00
Simon Willison bb5daaf6d8 Add messages= parameter to chain() (sync + async, Conversation + Model)
Parity with prompt(): all four chain() methods —
Conversation.chain, AsyncConversation.chain, _Model.chain,
_AsyncModel.chain — now accept a messages= kwarg and pre-bake the
full chain via _build_full_chain so the first response of the chain
loop satisfies the invariant response.prompt.messages == what was
sent.

Semantics match prompt() exactly: when messages= is passed, it's
authoritative for the first turn. The prompt= kwarg is ignored for
chain construction (it stays available via prompt.prompt / .system /
.attachments for any legacy plugin code). Subsequent tool-result
turns inside the chain loop still extend the chain via
_chain_for_tool_results, which reads from the prior response's
prompt.messages + messages.

Six new tests cover: conv.chain(messages=), model.chain(messages=),
messages= authoritative over prompt= kwarg, explicit messages=
replaces conversation history, and the async variants of all of the
above.

667 tests in llm core (661 before + 6 new); llm-anthropic (32) and
llm-gemini (50) still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:15:39 -07:00
Simon Willison 253fed259b Add llm/serialization.py — TypedDicts for the wire form
Adds a dedicated module describing the exact JSON-safe shape returned
by Part.to_dict() / Message.to_dict() / Response.to_dict() and accepted
by the matching from_dict methods. Every consumer that reads or writes
serialized llm data can now import a specific TypedDict and get proper
autocomplete, static type-checking, and schema generation support.

Module: llm/serialization.py (deliberately not "schema" — that name is
taken by the structured-output feature).

  TextPartDict, ReasoningPartDict, ToolCallPartDict,
  ToolResultPartDict, AttachmentPartDict
    — one per Part subclass, each discriminated by a
      Literal["<type>"] on the `type` field so pydantic/type-checkers
      can narrow cleanly.

  PartDict = Union[...]
    — the discriminated-union form of all Part dicts.

  AttachmentDict — the nested attachment payload (base64 content when
    bytes were supplied).

  MessageDict — {role, parts: list[PartDict], provider_metadata?}

  PromptDict, UsageDict, ResponseDict — full Response.to_dict() shape
    including the input chain, options, messages, and audit fields.

TypedDicts use typing_extensions.NotRequired (available for 3.10+ via
a transitive pydantic dep) so Python 3.10 consumers work.

Type annotations on every .to_dict() / .from_dict() method across
parts.py and models.py now reference the specific TypedDict rather
than Dict[str, Any]. Consumers writing

    def save_messages(msgs: list[MessageDict]) -> None: ...

get autocomplete on msgs[i]["role"], type-errors on typos, and pydantic
TypeAdapter-based validation works out of the box:

    from pydantic import TypeAdapter
    from llm.serialization import MessageDict
    TypeAdapter(MessageDict).validate_python(incoming)       # validate
    TypeAdapter(MessageDict).json_schema()                   # export

Also tidied _response_to_dict to omit usage.details when None so the
serialized UsageDict doesn't carry a null field where pydantic would
reject it during validation.

New test_serialization.py (41 tests):
  - required/optional key sets on every TypedDict
  - actual .to_dict() output conforms to its TypedDict via TypeAdapter
  - PartDict discriminated union accepts all 5 Part variants and
    rejects unknown types
  - Literal discriminator values are correct
  - method annotations point at the right TypedDicts
  - JSON round-trip of Response.to_dict() validates

661 total tests pass (620 before + 41 new). llm-anthropic (32) and
llm-gemini (50) still green against the editable llm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 10:20:29 -07:00
Simon Willison 82b844d7dd Async parity: pin sync/async equivalence for all Phase 1-7 APIs
New tests/test_async_parity.py (18 tests) exercises every new API on
the async path via llm-echo (+ async_mock_model where relevant):

  - AsyncResponse.to_dict() captures chain, output, datetime_utc
  - AsyncResponse.to_dict() raises before await (guard parity)
  - AsyncResponse.from_dict() rehydrates and matches original
  - AsyncResponse.from_dict() + reply() continues correctly
  - model= override on AsyncResponse.from_dict
  - AsyncResponse.from_row fallback (SQLite rehydrate) populates
    response.messages from _chunks so llm -c --async preserves the
    assistant turn
  - load_conversation(async_=True).prompt(...) builds full chain
  - AsyncConversation.chain tool-result turn pre-bakes chain
  - astream_events matches stream_events for text-only output
  - reply chains across 3 async turns
  - Full three-turn save→restore→reply loop under async
  - reply(messages=[...]) kwarg appends to async chain
  - response.messages raises on un-awaited AsyncResponse
  - usage round-trips through async to_dict/from_dict
  - sync/async structurally identical output for same prompts

Plus one test asserting Echo + EchoAsync are both registered.

The tests all passed on first run — Phase 7 async implementation was
already correct. These pin the invariants against future regressions.

620 tests pass (602 before + 18 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 09:06:14 -07:00
Simon Willison c975d4ce7f Fix llm -c regression: rehydrated response.messages preserves text+tools
_build_parts now falls back to synthesizing from self._chunks and
self._tool_calls when self._stream_events is empty. That's the shape
of a Response rehydrated via from_row (SQLite doesn't persist
StreamEvents under Phase 1-7 scope).

Without this, Conversation.prompt's full-chain construction on a
follow-up turn (llm -c, load_conversation().prompt(...)) produced
[user(q1), user(q2)] — dropping the assistant turn entirely — because
prev.messages was []. Now prev.messages yields
[assistant(text + tool calls)] and the chain is correct:
[user(q1), assistant(a1), user(q2)].

Reasoning signatures and structured reasoning parts are still lost
on SQLite rehydrate — that requires Phase 8 (structured parts
persistence) or use of response.to_dict() / from_dict() for
structure-preserving serialization. For the common case (text-only
multi-turn), llm -c works again.

Regression tests pin the fallback + the end-to-end load_conversation
follow-up.

602 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:37:49 -07:00