Files
Dmytro Liubarskyi 7405b96d82 Support customHeaders in OpenAI Responses API models (#6101)
## Issue
  Closes #6100

  ## Change
`OpenAiResponsesChatModel` and `OpenAiResponsesStreamingChatModel` had
no way to set custom HTTP headers, so they could not be used behind an
authenticated proxy or a gateway that requires extra headers.

This PR adds the two overloads that `OpenAiChatModel` and
`OpenAiStreamingChatModel` already have, bringing the Responses API
models to parity:

  ```java
  ChatModel model = OpenAiResponsesChatModel.builder()
          .apiKey(System.getenv("OPENAI_API_KEY"))
          .modelName("gpt-4o-mini")
.customHeaders(Map.of("Proxy-Authorization", "Basic dXNlcjpwYXNz"))
          .build();
  ```

  ```java
  ChatModel model = OpenAiResponsesChatModel.builder()
          .apiKey(System.getenv("OPENAI_API_KEY"))
          .modelName("gpt-4o-mini")
.customHeaders(() -> Map.of("Authorization", "Bearer " +
tokenProvider.currentToken()))
          .build();
  ```

  Details:
- `customHeaders(Map<String, String>)` and
`customHeaders(Supplier<Map<String, String>>)` were added to both
builders. The `Supplier` is invoked before every request, so dynamic
values such as expiring OAuth2
  tokens are supported.
- The headers are plumbed through `OpenAiResponsesClient`, which builds
its requests itself rather than going through `DefaultOpenAiClient`. The
default is `Map::of`, following the same pattern as
  `DefaultOpenAiClient`.
- Custom headers are applied after the built-in ones (`Authorization`,
`OpenAI-Organization`, `Content-Type`, `Accept`), so they can
deliberately override them. This matches the precedence in
`DefaultOpenAiClient#buildRequestHeaders` and is what the
authenticated-proxy use case needs.
  - A `null` or empty map returned by the supplier is a no-op.
- No masking work was needed: `HttpRequestLogger` masks by header-name
keyword, so credential-carrying custom headers are already masked when
`logRequests` is enabled.

Tests: `OpenAiResponsesCustomHeadersTest` captures the outgoing
`HttpRequest` through a stub `HttpClient` and asserts the headers that
are actually sent, covering the static map, the supplier being called
before each request, overriding a default header, the streaming model,
and the negative cases (no custom headers configured, supplier returning
`null`).

Documentation: a "Custom HTTP headers" section was added to
`docs/docs/integrations/language-models/open-ai.md`, under the OpenAI
Responses API section.

  ## General checklist
  - [X] There are no breaking changes (API, behaviour)
  - [X] I have added unit and/or integration tests for my change
  - [X] The tests cover both positive and negative cases
- [ ] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green
- [X] I have manually run all the unit and integration tests in the
[core](https://github.com/langchain4j/langchain4j/tree/main/langchain4j-core)
and

[main](https://github.com/langchain4j/langchain4j/tree/main/langchain4j)
modules, and they are all green
- [X] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features)
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)
2026-08-18 15:03:39 +02:00
..