Repository navigation
feat: add GPT-6 mantle support, fix item/cost loss in Responses - #33
Conversation
Handle GPT-6 Responses output items (reasoning replay, compaction, cache usage) via a new OutputItems module, register GPT-6 models, and add Mantle/Bedrock specs with recorded cassettes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Sam Boland <sam.boland@appfolio.com>
|
🤖 Supernova Code Review — View trace — via automation "Code Review Opus 5.5 Medium" (run), installed by Sam Boland (sam.boland@appfolio.com) |
There was a problem hiding this comment.
Adversarial review of the GPT-6 mantle routing, the Responses output-item preservation, the compaction API, the streaming changes, the cache-token accounting and ActiveRecord storage. I left 2 inline findings:
lib/ruby_llm/chat.rb:230:Chat#compactshadowsEnumerable#compact, whichChatgets frominclude Enumerable. A call that used to return an Array of messages now makes a network request or raises.lib/ruby_llm/protocols/responses/chat.rb:121: when a reply comes back asOutputItems,with_schemaparsing is skipped, becauseChat#normalize_schema_responseonly parses String content. Structured-output replies that include compaction or commentary items reach the caller unparsed. This happens on both the sync and streaming paths.
I checked the rest (signing paths for /compact, ActiveRecord round trip through content_raw → Content::Raw, format/replay of reasoning blocks, stream accumulation of blocks and cache tokens, VCR body stripping, Bedrock routing) and found nothing else actionable.
— via automation "Code Review Opus 5.5 Medium" (run), installed by Sam Boland (sam.boland@appfolio.com)
| # Compacts the conversation through the provider's standalone compaction endpoint and | ||
| # returns the compaction Message without adding it to #messages; the caller decides | ||
| # whether to add it. Raises UnsupportedFeatureError for protocols without one. | ||
| def compact |
There was a problem hiding this comment.
Chat#compact overrides Enumerable#compact. Chat does include Enumerable (line 8), and since Ruby 3.1 Enumerable#compact returns the non-nil elements. Any existing chat.compact call, or library code that treats a Chat as a generic Enumerable, used to get back an Array of messages. After this change the same call sends a billable network request, or raises UnsupportedFeatureError on Converse, OpenAI Chat Completions and other non-Responses protocols. Please give it a name that doesn't collide, e.g. compact_context or compact_conversation (and on Provider too, for consistency).
| end | ||
|
|
||
| def parse_reply_content(output) | ||
| output_items?(output) ? OutputItems.new(output) : parse_output_text(output) |
There was a problem hiding this comment.
When a reply becomes OutputItems, with_schema parsing is skipped without any error. Chat#normalize_schema_response only parses when response.content.is_a?(String) (chat.rb:371). So when a structured-output reply has a compaction item (server-side context_management) or a phase: "commentary" message, the caller gets an OutputItems object instead of the parsed Hash. The streaming path has the same problem, because Streaming#stream_response swaps in OutputItems before the chat normalizes the reply. The fix could live in normalize_schema_response: parse response.content.text when the content responds to :text. That keeps the raw items for replay, e.g. by checking for Protocols::Responses::OutputItems and parsing .text. Alternatively, document that schema replies may come back as OutputItems. A spec covering schema plus compaction would lock this in.
Full output-item handling (OutputItems replay, compaction, all reasoning items, cache-write usage accounting) now applies only to GPT-6 models; other models keep their existing Responses behaviour. Standalone compaction raises UnsupportedFeatureError for non-GPT-6 models. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Sam Boland <sam.boland@appfolio.com>
|
Review of a94ecff. Three changes, then this merges.
Verified and fine:
|
I've queued all three changes as one task, and I'm working on them now.
|
|
Change to item 1 of the previous comment: do not record those two cassettes, and make no more live Bedrock calls. Delete the two examples instead:
Update the PR body's test summary to match (no failures, and say these two were removed rather than recorded). Items 2 and 3 are unchanged. Neither needs a cassette; use unit specs only. |
I queued the amended task, and I'm working on it now. It replaces item 1 of the earlier task. I'll record no cassettes and make no more live Bedrock calls.
The earlier queued task had already been dispatched before this amendment, so I queued the amended one separately rather than editing it. If the first run recorded cassettes or made Bedrock calls, that work will be reverted here. |
…as OutputItems Chat includes Enumerable, so Chat#compact replaced Enumerable#compact for every model. with_schema now parses the final answer text of an OutputItems reply. Removes the two live examples that have no cassette (6.1 Sol tool loop and the GPT-5.6 Sol round trip) instead of recording them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Sam Boland <sam.boland@appfolio.com>
samuelboland
left a comment
There was a problem hiding this comment.
Verified: GPT-6 routing and models.json entries, GPT-6-only gating with GPT-5.6 unchanged, compact_context rename, OutputItems schema parsing, CI green.
What this does
Adds support for GPT-6 "mantle" models and fixes a bug in the Responses API handling where items and cost data were being lost.
Type of change
Scope check
Required for new features
Quality check
overcommit --installand all hooks passbundle exec rake vcr:record[provider_name]bundle exec rspecmodels.json,aliases.json)AI-generated code
API changes