Skip to content

Represent reasoning as transcript entries - #290

Merged
mattt merged 9 commits into
mainfrom
mattt/reasoning-transcript-entries
Oct 3, 2026
Merged

mattt merged 9 commits into
mainfrom
mattt/reasoning-transcript-entries

Conversation

@mattt

@mattt mattt commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator

This PR carries #264 by @qoli forward, with main merged in. It represents reasoning as transcript entries, following Foundation Models 27: Transcript.Entry.reasoning(Transcript.Reasoning), with a stable ID, display segments, an opaque signature, and metadata.

The built-in Anthropic provider fills in these entries for thinking and redacted thinking, in streaming and nonstreaming responses, and can replay its own reasoning after a transcript is restored. Providers that can't replay reasoning leave it out of their requests, and the entries stay in the transcript for display and persistence.

On top of @qoli's commits, this PR resolves the conflict with #271 in toAnthropicMessages() and marks the reasoning types as following Foundation Models 27, as #287 did for other APIs.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Anthropic replay can reorder assistant content blocks, and the new public replay error case is misleadingly named.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

Adds persistent reasoning transcript entries, including Anthropic thinking capture and replay.

Changes:

  • Introduces Codable reasoning entries with stable IDs, metadata, and opaque signatures.
  • Adds Anthropic streaming/nonstreaming reasoning support and cross-provider omission.
  • Documents and tests persistence, replay, and tool-flow behavior.
File Description
README.md Documents reasoning entries and replay behavior.
Transcript.swift Defines reasoning entries and replay errors.
LanguageModelSession.swift Updates transcript-entry documentation.
AnthropicLanguageModel.swift Captures and replays Anthropic reasoning.
GeminiLanguageModel.swift Omits unsupported reasoning from requests.
LlamaLanguageModel.swift Omits unsupported reasoning from prompts.
MLXLanguageModel.swift Omits unsupported reasoning from chat history.
OllamaLanguageModel.swift Filters reasoning during message conversion.
OpenAILanguageModel.swift Omits reasoning from OpenAI requests.
OpenResponsesLanguageModel.swift Omits reasoning from Responses requests.
SystemLanguageModel.swift Omits reasoning from Foundation Models conversion.
ReasoningTests.swift Tests core reasoning persistence and streaming.
AnthropicReasoningTests.swift Tests Anthropic capture, replay, tools, and portability.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +473 to +477
var entries: [Transcript.Entry] = message.content.compactMap { block in
switch block {
case .thinking(let thinking): return .reasoning(thinking.transcriptReasoning())
case .redactedThinking(let redacted): return .reasoning(redacted.transcriptReasoning())
default: return nil
Comment thread Sources/AnyLanguageModel/Transcript.swift Outdated
@mattt
mattt requested a balanced review from Copilot October 3, 2026 12:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation and coverage are coherent; only a minor documentation wording issue remains.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Low severity Update documentation for reasoning entries in non-streamed responses

Sources/​AnyLanguageModel/​LanguageModelSession.swift:1384

This still says the collection is empty whenever tool activity is not streamed, but Anthropic now fills it with reasoning entries even for ordinary no-tool responses. Update the condition so the public documentation matches the new behavior.

@mattt
mattt merged commit 8d1879e into main Oct 3, 2026
7 checks passed
@mattt
mattt deleted the mattt/reasoning-transcript-entries branch October 3, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants