Skip to content

fix(ai): use the first text block of an Anthropic response - #1332

Merged
debba merged 2 commits into
TabularisDB:mainfrom
harshbhx:fix/1330-anthropic-thinking-block
Oct 29, 2026
Merged

debba merged 2 commits into
TabularisDB:mainfrom
harshbhx:fix/1330-anthropic-thinking-block

Conversation

@harshbhx

@harshbhx harshbhx commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

The Anthropic provider failed with "Invalid response format from Anthropic" whenever the model put a thinking block ahead of its answer, even though the API call returned 200. generate_anthropic now uses the first content block whose type is text. Other block types are skipped, and a reply with no text block still returns the same error.

Cause

generate_anthropic read the reply from content[0].text. With extended thinking, content[0] is a thinking block that has no text field, so the parse failed.

Changes

  • src-tauri/src/ai.rs: pick the first text block in content instead of indexing content[0].
  • src-tauri/src/ai_provider_tests.rs: two tests on the existing local fixture. One returns a thinking block followed by a text block and expects the parsed SQL. The other returns only a thinking block and expects the invalid-format error.

Only the built-in Anthropic provider is touched. Other providers and plugin drivers do not go through this code.

Verification

  • Not run locally: a Rust build is too heavy for my development machine. This PR's CI is green, including test (cargo test, which runs the two new tests), web-e2e, Postgres and both MySQL jobs: https://github.com/TabularisDB/tabularis/actions/runs/40770839575
  • By reading the code, the thinking-first test fails on the old content[0] read; I did not run it against the old code.
  • Not tried against the live Anthropic API. The reply shape comes from the raw response in the issue.

Closes #1330

Models that return a thinking block before the answer made the provider fail with "Invalid response format from Anthropic", because the reply was read from content[0]. Pick the first block whose type is text instead, and cover it with fixture tests for a thinking-first reply and a reply with no text block.

Closes TabularisDB#1330
@kilo-code-bot

kilo-code-bot Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • src-tauri/src/ai.rs
  • src-tauri/src/ai_provider_tests.rs

Notes: generate_anthropic selects the first type == "text" block. When none exists it now returns a specific error if stop_reason == "max_tokens" and otherwise keeps the existing "Invalid response format from Anthropic" error. New fixture tests cover thinking-first, thinking-only, and thinking-only with max_tokens. Tests were not executed in this read-only review.

Previous Review Summary (commit a89d196)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a89d196)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • src-tauri/src/ai.rs
  • src-tauri/src/ai_provider_tests.rs

Notes: generate_anthropic now selects the first type == "text" block and keeps the same error when none exists. The two new fixture tests (thinking-first and thinking-only) follow the existing Fixture/generate patterns. Tests were not executed in this read-only review.


Reviewed by claude-sonnet-5.5 · Input: 246 · Output: 1.2K · Cached: 163.1K

Review guidance: REVIEW.md from base branch main

@HongTin2104 HongTin2104 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The fix looks right to me. With a leading thinking block, json["content"][0]["text"] is Null, so the old as_str() returned None and produced exactly the error in #1330. The thinking-first test covers that case, and filtering on type == "text" also skips redacted_thinking blocks. The two tests follow the existing Fixture pattern.

Two non-blocking notes:

  1. A thinking-only reply will still show the generic error. anthropic_request_body sends "max_tokens": 1024, and thinking tokens count toward that limit. A longer Explain can run out of budget during thinking and come back with stop_reason: "max_tokens" and no text block. The user would then see "Invalid response format from Anthropic" again, which points at the wrong cause. It could be worth checking json["stop_reason"] when no text block is found and returning something like "Anthropic stopped at the max_tokens limit before producing an answer". Raising max_tokens for the explain paths could also be a follow-up. Either is fine outside this PR.

  2. Only the first text block is used. For this request shape (no tools, no documents), the answer comes back as a single text block, so this is fine today. If the API ever splits the answer into several text blocks, joining them would be a little more robust and costs nothing:

    let text: String = blocks
        .iter()
        .filter(|block| block["type"].as_str() == Some("text"))
        .filter_map(|block| block["text"].as_str())
        .collect();

    This is optional, not a request.

I don't have a Rust toolchain on this machine, so I reviewed by reading only and haven't run cargo test. The CI run linked in the description covers the new tests.

@harshbhx

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review, @HongTin2104.

I took note 1: when there is no text block and stop_reason is max_tokens, the error now says Anthropic stopped at the max_tokens limit before producing an answer. Added a test for it in a separate commit. I left note 2 out since the response is a single text block for this request shape. Raising max_tokens can be a separate follow-up if @debba wants it.

@debba debba left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for fixing Anthropic responses that put a thinking block before the text answer, and for adding regression tests for missing text and max_tokens exhaustion. This is a useful fix for the existing provider.

We are evaluating moving AI assistant integrations into plugins in #1337, but that does not need to hold up this bug fix. I will verify it on this branch and merge it if the checks pass.

@debba

debba commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

I tested Anthropic SQL generation in the real macOS app with claude-haiku-4-5 against a SQLite fixture, then ran the generated query: 255 orders and a total of 34972.5. The three regression tests and existing provider fixtures pass. Full checks reproduce only the same two Rust theme-path failures and one frontend workspace-layout failure seen on main; scoped ESLint, formatting, theme checks and typecheck pass. No console errors appeared during generation. This is ready to merge.

@debba
debba merged commit 2f2bb3c into TabularisDB:main Oct 29, 2026
9 checks passed
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.

[Bug]: Anthropic provider fails with "Invalid response format" when the model returns a thinking block first

3 participants