文件历史

154 次代码提交

作者 SHA1 备注 提交日期
Simon Willison ef6fc13631 Handle tool calls with empty arguments better, closes #1521 2026-07-09 09:03:13 -07:00
Simon Willison 5a2e0a4c54 --hide-reasoning and hide_reasoning=True parameters (#1442)
* Rename --no-reasoning flag to --hide-reasoning
* hide_reasoning= Prompt parameter, plus docs
* OpenAI plugin now obeys prompt.hide_reasoning
2026-05-12 09:24:51 -07:00
Simon Willison 6e12258c0b Send prior assistant text as plain string in OpenAI Responses input
When building the `input` list for the OpenAI Responses API from prior
conversation turns, an assistant text-only turn was being serialized as:

    {"role": "assistant",
     "content": [{"type": "output_text", "text": "..."}]}

The openai-python SDK's EasyInputMessage shape uses a plain string for
this case, matching what a direct OpenAI Responses call would send. Use
the same shape so our history matches the SDK exactly, and add tests
covering both _build_responses_input and a two-turn response.reply()
flow.
2026-05-12 08:19:17 -07:00
Simon Willison 3e9e3a30ea Request reasoning summary auto for OpenAI models 2026-05-11 23:06:52 -07:00
Simon Willison 3b5eaedb87 Run latest black 2026-05-11 22:40:03 -07:00
Simon Willison 98e651075f Register more OpenAI models using Responses API 2026-05-11 22:33:28 -07:00
Simon Willison 2838388c31 Ran Black 2026-05-11 21:18:40 -07:00
Claude c7464eeb97 Round-trip encrypted reasoning across tool calls
When the Responses API returns a reasoning item alongside function
calls, capture its opaque id + encrypted_content as provider_metadata
on the resulting ReasoningPart. _build_responses_input already echoed
that metadata back as a reasoning input item on the next turn - now
the output side actually populates it.

This preserves the model's hidden chain of thought across the tool
round-trip. Without it, GPT-5-class models silently lose ~3% on
SWE-bench (per OpenAI) when used with tools.

Adds a dedicated VCR test that asserts the encrypted_content captured
on the first turn appears verbatim in the second turn's outgoing
request body.
2026-05-06 03:15:21 +00:00
Claude 3c747c8b9f Route gpt-5.5 through the /v1/responses endpoint
Adds Responses and AsyncResponses classes that drive the OpenAI
/v1/responses endpoint. The existing Chat / AsyncChat classes are
unchanged because other plugins import them.

gpt-5.5 (and gpt-5.5-2026-04-23) is now registered against Responses
by default. Pass `-o chat_completions 1` to fall back to the older
/v1/chat/completions code path.

This is feature parity with the Chat path (text, tools, streaming,
schema, reasoning_effort, verbosity, attachments, system prompts).
Interleaved reasoning across tool round-trips is not exercised yet -
encrypted reasoning items are accepted on the input side, but the
plugin doesn't yet stash them on outgoing ReasoningParts.
2026-05-06 01:57:09 +00: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 7c471496f4 Do not have xPart classes as llm. imports 2026-04-28 16:44:21 -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 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 de63d8b69e Fixes for ruff 2026-04-22 10:13:39 -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 afc41b0c47 mypy fixes 2026-04-21 21:37:04 -07:00
Simon Willison 65b8e37c79 Ran Black 2026-04-21 21:10:13 -07:00
Simon Willison 63cdf94fbe Phase 7: to_dict/from_dict/reply + full-chain invariant
Three new Response primitives that make conversation persistence
outside SQLite ergonomic — and a clarified invariant that
response.prompt.messages is always exactly what the model was sent.

response.reply(text, **kwargs) -> Response
  Builds next-turn chain = self.prompt.messages + self.messages +
  [user(text)] and calls self.model.prompt(messages=chain, ...).
  Works from any Response, regardless of origin (conversation,
  standalone model.prompt, or from_dict-rehydrated).

response.to_dict() / Response.from_dict(data, *, model=None)
  JSON-safe serialization. to_dict captures model_id, input chain
  (prompt.messages — full), assembled output (response.messages
  including reasoning parts and provider_metadata signatures),
  options, and optional audit fields (id, usage, datetime_utc).
  from_dict rehydrates a _done=True Response where text() returns
  the answer and messages returns the full structured view without
  re-running the assembler. Async variants likewise.

Full-chain invariant: response.prompt.messages == what was sent.
  - Conversation.prompt / AsyncConversation.prompt now pre-compute
    the full chain (last response's prompt.messages + last
    response's messages + new user turn, or tool_results for
    chain loops) and pass it as messages= into the Prompt.
  - Prompt.messages, when _explicit_messages is set, returns that
    list verbatim. The previous "combine with prompt= trailing
    user" behavior is dropped (was Phase 3 scaffolding, no user
    ever wants a partial chain).
  - _BaseConversation._build_full_chain is the shared builder.

OpenAI adapter simplified: build_messages now reads only
prompt.messages and ignores conversation.responses. Under the
invariant, prompt.messages already has the full history baked in;
walking conversation would double-emit.

response.messages now short-circuits to _loaded_messages when set
(rehydrated responses don't re-run the assembler).

The persistence pattern becomes:

    response = model.prompt("Hi", thinking=True)
    response.text()
    Path("chat.json").write_text(json.dumps(response.to_dict()))

    # Later, any process:
    data = json.loads(Path("chat.json").read_text())
    response = llm.Response.from_dict(data)
    response = response.reply("Follow up")
    print(response.text())

Reasoning signatures (Anthropic extended thinking, Gemini
thoughtSignature) round-trip via provider_metadata on the
ReasoningPart / ToolCallPart — so multi-turn extended thinking
works across process boundaries for free.

19 new tests + 2 tightened existing tests; 600 total pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:17:31 -07:00
Simon Willison 9f0d72ac03 Phase 4c: OpenAI reasoning token count → redacted ReasoningPart
Capture usage.completion_tokens_details.reasoning_tokens and store it
on response._reasoning_token_count BEFORE set_usage runs — set_usage
pops top-level keys and then simplify_usage_dict strips zero-valued
entries, both of which would lose the count.

Phase 2's _build_parts() picks up _reasoning_token_count and prepends
a ReasoningPart(redacted=True, token_count=N, text="") to the
assembled output messages. That gives CLI and client code a hook to
render "GPT-5 used N reasoning tokens here" without the model
actually streaming any reasoning text (which OpenAI's reasoning
models don't expose).

Both Chat.execute (sync) and AsyncChat.execute do this.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 09:40:28 -07:00
Simon Willison 8ae9644016 Phase 4b: OpenAI execute() yields StreamEvent objects
Replace string yields with typed StreamEvent yields in both sync
Chat.execute and async AsyncChat.execute, for both streaming and
non-streaming code paths.

Event shape:
  - Text chunks emit StreamEvent(type="text", part_index=0).
    Empty-string content (OpenAI's first role=assistant delta) is
    now skipped as noise.
  - Each tool call gets its own part_index past any text that
    preceded it (text is always part_index=0, tool calls start at
    1). A new tool call emits tool_call_name with name+id, and each
    tool-call-args delta emits tool_call_args with the partial
    JSON. Callers see arguments build up live via stream_events().
  - Non-streaming path emits one StreamEvent per content block:
    tool calls first (each with name + args events), then the final
    text if present.

response.add_tool_call() is still invoked for every tool call so
existing code paths (response.tool_calls(), chain execution) keep
working. The new event stream is additive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 09:39:00 -07:00
Simon Willison 94837936df Phase 4a: OpenAI build_messages reads prompt.messages
Rewrite the OpenAI _Shared.build_messages() to consume prompt.messages
(the canonical structured input) and dispatch per Part subtype into
OpenAI's wire format. Prior-turn input history also comes from
prev_response.prompt.messages; prior-turn output continues to use
the flat text_or_raise() / tool_calls_or_raise() accessors since
those tolerate plugin-specific quirks that _build_parts rejects.

Per-Part translation:
  - TextPart        → message content (string) or {"type": "text", ...}
                      entry inside attachment-bearing array content
  - AttachmentPart  → passed through _attachment() for image/audio/pdf
  - ToolCallPart    → tool_calls[] entry on an assistant message
                      (content=null when only tool_calls, no text)
  - ToolResultPart  → one {"role": "tool", tool_call_id, content} per
                      result (Message role="tool" can carry several)

System dedup: consecutive identical system prompts emit once. The
legacy prompt=/system=/attachments=/tool_results= path continues to
work unchanged because Prompt.messages synthesizes those into the
same Message list the new code reads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 09:36:23 -07:00
Simon Willison cad03fb4f4 Register async models for extra-openai-models.yaml, closes #1395
Note that Completion models do not have an async class so will not be registered as async.
2026-04-04 07:07:14 -07:00
Simon Willison 07dccc00ef GPT-5.4, 5.4-mini, 5.4-nano
Closes #1376
2026-03-17 11:24:52 -07:00
Claude 947feaa0c9 Add gpt-5.4 and gpt-5.4-2026-03-05 model support
https://claude.ai/code/session_01HwqZ4WeDCrspfF8E7STiPA
2026-03-06 00:42:46 +00:00
Arjan Mossel 73548c479a Add type annotations for OpenAI Chat/AsyncChat/Completion execute methods (#1315)
* 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>
2025-12-11 14:17:17 -08:00
Simon Willison 1753eb74ee gpt-5.2, gpt-5.2-chat-latest - refs #1317 2025-12-11 11:50:08 -08:00
Simon Willison 0526abeeea gpt-5.1 and gpt-5.1-chat-latest, refs #1300 2025-11-13 11:35:14 -08:00
James Sanford 2a54939951 Fix streaming tool calls with tests for many variants. (#1218)
* Recorded instance of streaming tool response variant "a".

This is the typical response, where "arguments":"" arrives
first in the stream, followed by "arguments":"{}"

The response data is a real capture from the OpenRouter API,
however some request and header data may be from other test fixtures.

* Recorded instance of streaming tool response variant "b".

This is a streaming response where the first arguments
you get is a fully formed "arguments":"{}"

The response data is a real capture from the OpenRouter API,
however some request and header data may be from other test fixtures.

* Test cases for streaming tool responses.

Note that the replays are marked as "read-only", as they are variants
seen in the wild where the streaming tool call argument fragments
arrive in a specific order.

* Fix streaming tool response variant "b", where "arguments":"{}" is what arrives first.

The previous code erroneously caused the first "arguments" to be duplicated,
by using "+=" even when being initially set.

This went unnoticed as many models stream "arguments":"" first.

When a more fully formed "arguments" fragment arrived first, it was causing
"Error: Extra data: line 1 column 3 (char 2)"

* Recorded instance of streaming tool response variant "c".

This was failing with "Error: unsupported operand type(s) for +=: 'NoneType' and 'str'"

The response data is a real capture from the OpenRouter API,
however some request and header data may be from other test fixtures.

* Test case for streaming tool response variant "c".

* Fix streaming tool response variant "c".

However, I'm not sure why arguments was initially not present or seen as None.
2025-08-11 14:17:52 -07:00
Simon Willison cbd3aab511 GPT-5 model IDs, refs #1229 2025-08-11 13:23:36 -07:00
Simon Willison b5d1c5ee90 Tools can now return attachments
Closes #1014

- llm.ToolOutput(output='...', attachments=[...]) for tools to return attachments
- New table: `tool_results_attachments`
- Table is populated when tools return attachments
- llm --tools-debug shows attachments returned by tools
- llm logs shows attachments returned by tools
2025-06-01 10:08:36 -07:00
Simon Willison 2bc6d7679c New default tool, llm_time, closes #1103 2025-05-26 23:00:18 -07:00
Simon Willison b4365ceb35 Move llm_version into llm.tools namespace 2025-05-26 21:44:33 -07:00
Simon Willison 9bbb37fae0 New default llm_version tool, closes #1096
Refs https://github.com/simonw/llm/issues/1095#issuecomment-2910574597
2025-05-26 13:30:47 -07:00
Mahesh Hegde d5f7bf9073 Support supports_tools parameters in openai compatible models (#1068) 2025-05-23 22:30:51 -07:00
Simon Willison 3cb875fa3d Async tool support (#1063)
* Sync models can now call async tools, refs #987
* Test for async tool functions in sync context, refs #987
* Test for asyncio tools, plus test that they run in parallel
* Docs for async tool usage
2025-05-21 21:42:19 -07:00
Simon Willison ad7dc2ef71 Enable vision for o3 and o4-mini, closes #1037 2025-05-15 10:22:13 -04:00
Simon Willison bed617cd62 Enable tools for o3, o3-mini, o4-mini, refs #988 2025-05-13 17:19:30 -07:00
Simon Willison 88b806ae1a Got multi-tool OpenAI chat working, in no-stream mode too
Refs #1017, #1019
2025-05-13 17:19:30 -07:00
Simon Willison 0ff24b34c4 dict() is now model_dump() 2025-05-13 17:19:30 -07:00
Simon Willison f994196b32 tool_calls_or_raise()
Refs #992, #998, #999
2025-05-13 17:19:30 -07:00
Simon Willison 7dbf0b8586 Got a tool call to run through OpenAI
Refs https://github.com/simonw/llm/issues/937#issuecomment-2870434157
2025-05-13 17:19:30 -07:00
Simon Willison c990578934 ToolCall.tool_call_id property, refs #937 2025-05-13 17:19:30 -07:00
Simon Willison 7bc2f78156 Capture tool calls from OpenAI streaming sync responses
Refs https://github.com/simonw/llm/issues/988#issuecomment-2869079084
2025-05-13 17:19:30 -07:00