spring-projects / spring-projects/spring-ai

No way to set per-request HTTP headers on `GoogleGenAiChatModel`

Open Beginner friendly
#6,920 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Expected Behavior

GoogleGenAiChatOptions exposes a header map that GoogleGenAiChatModel applies per request, allowing one shared model bean to send call-specific headers:

chatClient.prompt()
    .options(GoogleGenAiChatOptions.builder()
        .httpHeaders(Map.of("x-some-header-id", "VALUE_123"))
        .build())
    .user("hi")
    .call();

This matches OpenAiChatModel's customHeaders and AnthropicChatModel's httpHeaders.

Current Behavior

On main at bf122ac7f, GoogleGenAiChatOptions has no header field and GoogleGenAiChatModel#createGeminiRequest never configures one.
Neither the model module nor its autoconfiguration references headers in Java source.

The only workaround is to supply a com.google.genai.Client built with HttpOptions.headers(...), which fixes the headers for the client's lifetime rather than per request.

The vendor SDK is not the constraint.
Google GenAI 1.65.0 exposes GenerateContentConfig.httpOptions(), HttpOptions.headers(), and a GenerateContentConfig.Builder.httpOptions(...) setter.
The SDK extracts request httpOptions into the BuiltRequest and passes them to apiClient.request, while its MLDev and Vertex converters keep them out of the request body.

The Spring AI change is therefore a header field plus one config-builder call:

// GoogleGenAiChatModel, around line 667
GenerateContentConfig.Builder configBuilder = GenerateContentConfig.builder();
// ...
if (!CollectionUtils.isEmpty(requestOptions.getHttpHeaders())) {
    configBuilder.httpOptions(HttpOptions.builder().headers(requestOptions.getHttpHeaders()).build());
}

ApiClient.mergeHttpOptions overlays request headers on client defaults, preserving unnamed defaults and concatenating user-agent and x-goog-api-client values.

Context

Gateways and proxies may require a cost-attribution or tenant header derived from each business request.
A shared model bean cannot express that through client-level headers.

#4399 was closed after identifying the client-construction workaround, but that workaround does not support per-request values and no such support is present on main.
#4167 is related, but requests client-level header configuration through ChatClient.Builder; it does not provide per-request values through GoogleGenAiChatOptions.

The example uses httpHeaders to match AnthropicChatOptions; spring-ai-openai calls its equivalent field customHeaders.
Either name would work.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with GoogleGenAiChatOptions.java and GoogleGenAiChatModel#createGeminiRequest in the spring-ai-google-genai model module. Compare the existing per-request header handling in OpenAiChatModel and AnthropicChatModel, then verify that request options reach GenerateContentConfig.httpOptions without changing client-level defaults. Done means a shared model can send call-specific HTTP headers through GoogleGenAiChatOptions.

Written by the indexing model from the issue text.

Assessment

Tech stack
google-cloud, java, spring
Domain
ai, backend
Issue type
Feature
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
85/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.