fix(anthropic): one structured-output path for both payload builders - #52
Open
rezaho wants to merge 2 commits into
Open
fix(anthropic): one structured-output path for both payload builders#52rezaho wants to merge 2 commits into
rezaho wants to merge 2 commits into
Conversation
The api-key adapter and the OAuth adapter are deliberately parallel — the OAuth one is not a subclass, so it builds its own payload — and the structured-output branch is where that parallelism drifted twice. Both drifts landed on the OAuth leg: it ASSIGNED `output_config`, so a request carrying both a reasoning effort and a response schema silently lost the effort (the api-key twin had already been fixed and test-pinned to merge, never assign); and it had no `supports_structured_output` gate, so a schema request on an endpoint that cannot enforce one would have degraded to a bare "please emit JSON" nudge instead of putting the schema in the prompt. Both branches are now one module-level `apply_structured_output` that each builder calls, so the shape is identical by construction rather than by two people remembering to cut the same line twice. The OAuth adapter declares `supports_structured_output = True` — an endpoint capability, like the api-key one, and true here: this IS the first-party Messages API, verified live 2026-08-07 with haiku 4.5 under the Claude Code prefix returning schema-conformant output. A leg that turns out not to enforce it flips the flag and inherits the fallback, the way Bedrock does. Tests drive BOTH builders over identical inputs and compare, for the native and the fallback arms alike, plus the clamp parity the repair path depends on: a background model at max_tokens=4096 with the default 8192 budget clamps to 3072 on both legs.
…ot __new__ Every OAuth payload assertion ran against an object built with __new__ and five hand-set attributes, so anything __init__ does that shapes a request was invisible to it — and the parity arms compared that hand-assembled object against a real AnthropicAdapter. The constructor's only obstacle was reading credentials from disk, so the tests give it a credentials file at the path it already reads from the environment and turn auto_refresh off (the one step that would leave the machine). Nothing reaches the network.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #51 (merge that first; GitHub will retarget this to main).
Problem. The OAuth payload builder carried a hand-copied twin of the api-key leg's structured-output logic. The twin ASSIGNED
output_config(clobbering a reasoning-effort value already in it) and had nosupports_structured_outputgate, so a schema request on a non-enforcing endpoint degraded to a bare 'reply with JSON' nudge instead of the schema-in-prompt fallback. This is the S246 leg of the Spren-side structured-output integrity repair: identical shape by construction, not by discipline.Change.
apply_structured_outputinanthropic.py; both payload builders call it; the OAuth twin is deleted.AnthropicOAuthAdapterdeclaressupports_structured_output = True— an endpoint capability, live-verified against the real OAuth endpoint (schema honored, conformant output, one call).output_configon both legs: a request carrying reasoning effort AND a response schema emits one config object with both.auto_refresh=Falseas the one suppressed step) instead of a__new__hand-assembly; parity arms compare both legs on identical inputs.Tests.
tests/models: 221 passed, 39 skipped. Full suite: 1468 passed, 5 failed, 53 skipped — all 5 pre-existing intests/coordination/**, none referencing anthropic or structured output.After merge: Spren bumps its framework pin (its gitlink still points at a commit predating both this and #51 — until the bump, a fresh Spren checkout ships the old OAuth payload shape).