Skip to content

Claude 5.x goals survive long thinking with an Anthropic key - #38

Merged
mattbaconz merged 2 commits into
mainfrom
fix/anthropic-thinking-max-tokens
Oct 22, 2026
Merged

mattbaconz merged 2 commits into
mainfrom
fix/anthropic-thinking-max-tokens

Conversation

@mattbaconz

Copy link
Copy Markdown
Contributor

With an Anthropic key, a hard goal on Claude 5.x could end at once with anthropic response has no text block (stop_reason: max_tokens). Claude 5.5 models think by default and thinking counts toward max_tokens. The provider sent a fixed max_tokens: 8192, so on harder tasks the thinking used the whole cap, the reply held only a thinking block, and the goal failed with a dispatch error and no retry. In the 51-exercise Aider Polyglot benchmark on claude-haiku-5-5 this was 2 of Phonton's 8 failures (Rust forth and bowling; forth also failed this way in an earlier run).

Changes

  • Claude 5 and later models (ids claude-<family>-<major>… with a major version of 5 or more) get max_tokens: 64000. Older models, which don't think unless asked, keep 8192. 64K is the starting point Anthropic's docs give for agentic work, is within every Claude 5.x model's 128K output limit, and max_tokens does not count against output-token rate limits.
  • Every Anthropic request streams ("stream": true) and the reply is rebuilt from its events: text deltas, stop_reason, and the cumulative usage; a mid-stream error event (such as overloaded_error) becomes the error. Anthropic's docs advise against a large max_tokens without streaming because networks can drop idle connections. The client's 600 s total timeout is now a 600 s read (idle) timeout, so a long stream is not cut off.
  • If a reply still stops at max_tokens before any text, the provider asks once more with output_config.effort: "low" (the docs' other lever: less thinking) instead of failing the goal. Both attempts' tokens count in the receipt.
  • Regression test anthropic_thinking_only_max_tokens_reply_is_retried_at_low_effort: a local stand-in API streams a thinking-only reply that stops at max_tokens, then a reply with text. The test checks that the request streams with the 64K cap, that the retry asks for low effort, the returned diff, and the summed tokens. claude_5_and_later_think_by_default pins the model-id check. The live-tests smoke now also covers claude-haiku-5-5.
  • CHANGELOG entry under Unreleased. No version bump or release.

Verified on Windows

  • cargo fmt --check and cargo clippy --workspace --all-targets -D warnings pass.
  • cargo test --workspace --no-fail-fast: every suite passes except phonton-verify browser_check_passes_on_valid_html, which needs the Playwright install that CI performs (Cannot find module 'playwright' locally). The new regression test failed on main before the fix.
  • Live API (cargo test -p phonton-providers --features live-tests anthropic_live_smoke): claude-haiku-4-5 and claude-haiku-5-5 answered through the streamed path with text and token counts.
  • Not rerun: the benchmark's forth and bowling goals.

Docs checked: Effort (Claude Haiku 5.5: thinking counts toward max_tokens, leave room for it), Troubleshooting thinking (a max_tokens stop: raise max_tokens or lower effort), Errors > Long requests (stream when max_tokens is large), Rate limits (max_tokens is not counted toward OTPM).

Claude 5 and later models think by default, and thinking counts toward
max_tokens. With a fixed 8192 cap, hard goals (Aider Polyglot Rust
forth and bowling on claude-haiku-5-5) spent the whole cap thinking,
the reply had no text block, and the goal failed with a dispatch error.

- Send max_tokens 64000 to Claude 5+ models; older models keep 8192.
- Stream every Anthropic request and rebuild the reply from its events,
  so a long reply is not cut off; the 600 s total client timeout is now
  a 600 s read (idle) timeout.
- If a reply still stops at max_tokens before any text, ask once more
  with output_config.effort "low" and count both attempts' tokens.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T17:18:44.237975Z d6010e3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6010e3168

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +785 to +786
message.insert("content".into(), Value::Array(content));
Ok(Value::Object(message))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject streams that end before message_stop

If Anthropic or an intermediary closes the SSE response cleanly after one or more text deltas but before the required message_delta/message_stop, this returns a seemingly valid message. Because message_start already supplies usage, call_with_attachments then accepts the partial text and reports the initial output count, silently treating a truncated model response as complete. Track the terminal event and reject incomplete streams so the call can fail or be retried instead.

Useful? React with 👍 / 👎.

@mattbaconz
mattbaconz merged commit 5745e76 into main Oct 22, 2026
2 checks passed
mattbaconz added a commit that referenced this pull request Oct 23, 2026
* Release 0.26.0: Cloud goals run your own checks

Bump the version in phonton-cli/Cargo.toml, Cargo.lock, package.json and
the README badge, date the Unreleased CHANGELOG entry as 0.26.0, and add
release-notes/v0.26.0.md. The release carries #37 (cloud `--check`,
`pin_model`, Claude 5.5 prices, linked node_modules, missing test runner
stops as unavailable) and #38 (64K reply cap with streaming for Claude 5+,
retry of a thinking-only reply at low effort).

* Release notes: forth and bowling pass on the release binary
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.

1 participant