spring-projects / spring-projects/spring-ai

Per-call `customHeaders` not propagated in `OpenAiEmbeddingOptions`

Open Beginner friendly
#6,574 0 comments 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 10h
Merged PRs (30d)
5

Description

A note before the report: I'm not a native English speaker, so I drafted
this issue with the help of an LLM to describe the problem as precisely as I
could. Apologies for any awkward phrasing, and thank you for your understanding.

Bug description

OpenAiEmbeddingOptions#customHeaders set on a per-EmbeddingRequest basis are
never applied to the outgoing OpenAI embeddings request. OpenAiEmbeddingOptions#toOpenAiCreateParams(...)
builds the EmbeddingCreateParams from only model / input / user /
encodingFormat / dimensions, and never consumes getCustomHeaders():

// OpenAiEmbeddingOptions#toOpenAiCreateParams (main, 2.0.1-SNAPSHOT)
public EmbeddingCreateParams toOpenAiCreateParams(List<String> instructions) {
    EmbeddingCreateParams.Builder builder = EmbeddingCreateParams.builder();
    // ... model / input / user / encodingFormat / dimensions ...
    return builder.build();   // customHeaders never applied
}

The map is only consumed once, when the underlying OpenAIClient is built in
OpenAiSetup.setupSyncClient(...). So when an OpenAIClient is injected
directly into OpenAiEmbeddingModel, headers configured at runtime (per call)
are silently dropped.

This is inconsistent with:

  • the chat side (OpenAiChatModel#createRequest), which propagates per-call
    customHeaders to ChatCompletionCreateParams via putAdditionalHeader
    (added in 2.0.0-M5, commit f36dce3), and
  • the image side, which had the exact same bug and was already fixed in
    #6082 / #6083 (OpenAiImageOptions now calls
    getCustomHeaders().forEach(builder::putAdditionalHeader)).

Embedding is the remaining OpenAI model type where per-call customHeaders are
still dropped.

Use case affected

Calling LLM-gateway proxies that require a per-request header derived from the
business request (e.g. a distributed-tracing / correlation header such as
trace-id). A static client-level header is not sufficient, because a single
embedding model/client is shared across many callers. This is the same
motivation described in #6082 for images.

Environment

  • Spring AI: main (verified on commit ac8b2da26, 2.0.1-SNAPSHOT)
  • module: spring-ai-openai

Steps to reproduce

EmbeddingResponse resp = openAiEmbeddingModel.call(new EmbeddingRequest(
    List.of("hello world"),
    OpenAiEmbeddingOptions.builder()
        .model("text-embedding-3-small")
        .customHeaders(Map.of("trace-id", "TRACE_123"))
        .build()));

Inspect the outgoing HTTP request (OPENAI_LOG=debug). The trace-id header
is not present on the /v1/embeddings call.

Expected behavior

Per-call customHeaders should be propagated as additionalHeaders on the
EmbeddingCreateParams sent to openAiClient.embeddings().create(...),
consistent with chat (OpenAiChatModel#createRequest) and image (#6082/#6083).

Suggested fix

Mirror the image fix (#6083) in OpenAiEmbeddingOptions#toOpenAiCreateParams:

if (!CollectionUtils.isEmpty(this.getCustomHeaders())) {
    this.getCustomHeaders().forEach(builder::putAdditionalHeader);
}

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 in OpenAiEmbeddingOptions#toOpenAiCreateParams in the spring-ai-openai module and compare its builder usage with OpenAiImageOptions and OpenAiChatModel#createRequest. Reproduce the issue with OPENAI_LOG=debug and a per-call trace-id header. Done means the header appears on the /v1/embeddings request when supplied through OpenAiEmbeddingOptions.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.