Skip to content

feat: add Claude models to the google_cloud provider - #483

Draft
cloetzi wants to merge 4 commits into
feat/google-cloud-providerfrom
feat/google-cloud-claude
Draft

cloetzi wants to merge 4 commits into
feat/google-cloud-providerfrom
feat/google-cloud-claude

Conversation

@cloetzi

@cloetzi cloetzi commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Stacked on #482. Adds Claude models on Vertex AI to the google_cloud provider, and a shared Riffer::Wire::Anthropic library for building Anthropic requests and parsing responses without the anthropic gem.

Why our own transport

Anthropic::VertexClient only picks up ambient Application Default Credentials and has no way to pass in credentials, so it can't use the fiber-safe token refresh from #482. Claude requests go through the same GoogleCloud::Client as Gemini instead.

Routing

Model ids starting with claude- go to publishers/anthropic/models/{model}:rawPredict and :streamRawPredict, using the Anthropic Messages body plus anthropic_version: "vertex-2023-10-16". Every other model id goes to Gemini. Model ids may contain @ (e.g. claude-haiku-4-5@20251001).

Riffer::Wire::Anthropic

None of these classes depend on the google_cloud provider, so they can be reused if we replace the anthropic gem:

  • Request: the hash-building code moved out of the Anthropic provider, which now calls it. Existing Anthropic cassettes pass unchanged.
  • Response / Stream: parse raw JSON and SSE events into riffer messages and stream events. Stream raises IncompleteStreamError when there's no message_stop. Replaying all the existing Anthropic cassettes through these classes gives the same results as the SDK path (19 streams, 31 responses).
  • Out of scope here: the Anthropic provider still uses the SDK for responses and streaming. Moving it onto Response / Stream is a follow-up.

Other changes

  • Client#handle_api_error! handles streamRawPredict errors, which come back wrapped in a JSON array.
  • Streamed Claude tool-call deltas carry the real id and name. The SDK path sends "pending" and nil.

Tests

Cassettes are recorded against claude-haiku-5-5: generate, stream, tool calling (generate and stream), and reasoning replay with adaptive thinking.

Commented out: the structured output and web search tests. Our org policy constraints/vertexai.allowedPartnerModelFeatures blocks those features on the recording project. They need an org-policy admin to allow publishers/anthropic/models/claude-haiku-5-5:structured_outputs and :web_search, then the cassettes can be recorded.

Commits

  1. Extract Riffer::Wire::Anthropic::Request
  2. Add Riffer::Wire::Anthropic response and stream parsers
  3. Add Claude models to the google_cloud provider
  4. Docs

🤖 Generated with Claude Code

cloetzi and others added 4 commits October 8, 2026 13:58
…opic::Request

Move message partitioning, assistant/reasoning/file/tool conversion and
the Messages API body assembly out of the Anthropic provider into a
transport-independent builder that returns the body without a model,
so a transport that carries the model in the URL can reuse it. The
provider prepends the model, producing the same hash in the same key
order; the recorded cassettes still match.

REASONING_FORMAT moves to Riffer::Wire::Anthropic and the provider
constant aliases it. The two provider tests that called the moved
private converters now go through build_request_params.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Riffer::Wire::Anthropic::Response reads a raw Messages API response
hash into content, tool calls, reasoning parts, token usage and finish
reason. Riffer::Wire::Anthropic::Stream turns raw stream event hashes
into riffer stream events (text, reasoning, tool calls, web search),
accumulates the final message, and raises IncompleteStreamError from
finish! when message_stop never arrived. Both mirror what the Anthropic
provider extracts from SDK objects; replaying every recorded Anthropic
cassette through both paths yields identical results. Streamed tool
call deltas carry the real id and name, which the SDK path does not
know until the block ends.

FINISH_REASONS and the tool-name encoding move to
Riffer::Wire::Anthropic so the request builder, these parsers and the
provider share them. The Anthropic provider itself keeps reading SDK
objects for now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
google_cloud/claude-* models route to Vertex AI's Anthropic endpoint:
rawPredict and streamRawPredict under publishers/anthropic, with the
Messages body from Riffer::Wire::Anthropic::Request minus the model
plus anthropic_version "vertex-2023-10-16". Responses and SSE streams
are parsed by Riffer::Wire::Anthropic::Response and ::Stream, so a
stream that ends without message_stop raises IncompleteStreamError as
the Anthropic provider does. Claude models get the XML skills adapter;
everything else keeps the Gemini path.

The VCR tests await cassettes; Claude is not yet enabled on the
recording project.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Describe the claude-* routing, the rawPredict body (anthropic_version
in place of the model), and the behaviour shared with the Anthropic
provider: reasoning replay, web search, structured output, user_id
metadata and IncompleteStreamError. List Claude in the provider
overview and the tag mapping table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 8, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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