spring-projects / spring-projects/spring-ai
No way to set per-request HTTP headers on `GoogleGenAiChatModel`
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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