Commit Graph

240 Commits

Author SHA1 Message Date
Farzad Sedaghatbin feae74d397
fix: add null/empty choices guard in AzureOpenAiChatModel (#4815)
## Issue
Closes #4814

## Change
Added a null/empty guard for `getChoices()` in
`AzureOpenAiChatModel.doChat()` before accessing `.get(0)`.

**Before:**
```java
ChatChoice chatChoice = chatCompletions.getChoices().get(0);
```

**After:**
```java
if (isNullOrEmpty(chatCompletions.getChoices())) {
    throw new IllegalArgumentException("Chat completion failed: no choices returned");
}
ChatChoice chatChoice = chatCompletions.getChoices().get(0);
```

This is consistent with the streaming counterpart
`AzureOpenAiStreamingChatModel` which already has this guard (line
287-288).

**Note:** No unit tests are added because the `OpenAIClient` is an Azure
SDK class that requires a real or mock Azure endpoint. The existing
tests in this module are integration tests (IT). The fix is a simple
null/empty guard with clear correctness from code inspection.

## General checklist
- [X] There are no breaking changes (API, behaviour)
- [ ] I have added unit and/or integration tests for my change
- [X] The tests cover both positive and negative cases
- [X] 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
- [ ] 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-07-31 15:44:32 +02:00
renovate[bot] e66f15cab0
fix(deps): update dependency io.netty:netty-codec-http2 to v4.1.136.final [security] (#5831)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [io.netty:netty-codec-http2](https://netty.io/)
([source](https://redirect.github.com/netty/netty)) | `4.1.135.Final` →
`4.1.136.Final` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.netty:netty-codec-http2/4.1.136.Final?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.netty:netty-codec-http2/4.1.135.Final/4.1.136.Final?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/2069) for more information.

---

### Netty: [codec-http2] Lack of Host Header Deduplication in
HTTP/2→HTTP/1.x Translation Leads to Request Routing Bypass
[CVE-2026-59900](https://nvd.nist.gov/vuln/detail/CVE-2026-59900) /
[GHSA-c69g-56f8-xwqj](https://redirect.github.com/advisories/GHSA-c69g-56f8-xwqj)

<details>
<summary>More information</summary>

#### Details
Netty's HTTP/2-to-HTTP/1.x translation layer
(`Http2StreamFrameToHttpObjectCodec` and `InboundHttp2ToHttpAdapter`)
fails to deduplicate or validate `Host` headers when an HTTP/2 client
supplies both the `:authority` pseudo-header and a literal `host` header
in a single HEADERS frame. The translator maps `:authority` to `Host`
and separately copies the literal `host` header, producing an
`HttpRequest` object containing two `Host` headers with
attacker-controlled differing values.

#### Severity
- CVSS Score: 6.9 / 10 (Medium)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N`

#### References
-
[https://github.com/netty/netty/security/advisories/GHSA-c69g-56f8-xwqj](https://redirect.github.com/netty/netty/security/advisories/GHSA-c69g-56f8-xwqj)
-
[https://github.com/netty/netty/releases/tag/netty-4.1.136.Final](https://redirect.github.com/netty/netty/releases/tag/netty-4.1.136.Final)
-
[https://github.com/netty/netty/releases/tag/netty-4.2.16.Final](https://redirect.github.com/netty/netty/releases/tag/netty-4.2.16.Final)
-
[https://github.com/advisories/GHSA-c69g-56f8-xwqj](https://redirect.github.com/advisories/GHSA-c69g-56f8-xwqj)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-c69g-56f8-xwqj)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/langchain4j/langchain4j).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzUuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI3NS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-27 10:26:27 +02:00
github-actions[bot] a39f132b91 Update versions to 1.19.0-SNAPSHOT and 1.19.0-beta29-SNAPSHOT 2026-07-17 13:42:56 +00:00
github-actions[bot] 66ad5ee6d5 Release versions 1.18.0 and 1.18.0-beta28 2026-07-17 12:34:45 +00:00
github-actions[bot] 7d7c3349d7 Update versions to 1.18.0-SNAPSHOT and 1.18.0-beta28-SNAPSHOT 2026-06-26 14:49:40 +00:00
github-actions[bot] 207407aec9 Release versions 1.17.0 and 1.17.0-beta27 2026-06-26 13:13:06 +00:00
Dmytro Liubarskyi c91caf8ee5 fix: throw on unsupported Azure OpenAI service version instead of silently falling back (#5260) 2026-06-26 09:37:16 +02:00
Benamira05 33be3339af
fix: throw on unsupported Azure OpenAI service version instead of silently falling back (#5260)
## Summary

Fixes #5259

`InternalAzureOpenAiHelper.getOpenAIServiceVersion` silently returned
`OpenAIServiceVersion.getLatest()` whenever the user-provided version
string did not match any known enum value. This caused user-supplied
versions (e.g. `2024-10-21`) to be ignored without any warning — the API
call would then be made against a completely different version (the
latest preview), as shown in the issue report.

## Root cause

```java
// before
static OpenAIServiceVersion getOpenAIServiceVersion(String serviceVersion) {
    for (OpenAIServiceVersion version : OpenAIServiceVersion.values()) {
        if (version.getVersion().equals(serviceVersion)) {
            return version;
        }
    }
    return OpenAIServiceVersion.getLatest();  // ← silent fallback
}
```

Anyone who passed a version string not present in the
`com.azure.ai.openai.OpenAIServiceVersion` enum got the latest preview
version with no indication that their value was discarded.

## Fix

- `null` or empty `serviceVersion` continues to return the latest
version (this matches the documented contract: *"if not, the latest
version is used"*)
- A non-matching, non-empty `serviceVersion` now throws
`IllegalArgumentException` with a clear message listing all supported
versions

```java
// after
static OpenAIServiceVersion getOpenAIServiceVersion(String serviceVersion) {
    if (serviceVersion == null || serviceVersion.isEmpty()) {
        return OpenAIServiceVersion.getLatest();
    }
    for (OpenAIServiceVersion version : OpenAIServiceVersion.values()) {
        if (version.getVersion().equals(serviceVersion)) {
            return version;
        }
    }
    List<String> supportedVersions = Arrays.stream(OpenAIServiceVersion.values())
            .map(OpenAIServiceVersion::getVersion)
            .collect(toList());
    throw new IllegalArgumentException("Unsupported Azure OpenAI service version: '" + serviceVersion
            + "'. Supported versions are: " + supportedVersions
            + ". Leave serviceVersion null or empty to use the latest version.");
}
```

## Test plan

- [x] `InternalAzureOpenAiHelperTest` passes (16 tests)
- [x] Existing tests
`setupOpenAIClientShouldReturnClientWithCorrectConfiguration` and
`setupOpenAIAsyncClientShouldReturnClientWithCorrectConfiguration`
updated to use a real supported version (`2024-02-01`) instead of the
previously meaningless `test-service-version` string
- [x] The old
`getOpenAIServiceVersionShouldReturnLatestVersionIfIncorrect` test
(which documented the silent-fallback bug as intended behavior) was
replaced with `getOpenAIServiceVersionShouldThrowIfIncorrect`
- [x] Added `getOpenAIServiceVersionShouldReturnLatestVersionIfNull` and
`getOpenAIServiceVersionShouldReturnLatestVersionIfEmpty` to lock in the
documented fallback behavior

## Breaking change considerations

Code that previously relied on the silent-fallback (passing junk strings
and getting the latest version anyway) will now throw. This is
intentional — the silent fallback was the bug. Callers wanting the
latest version should pass `null` or omit `serviceVersion` entirely.

---------

Co-authored-by: Benamira05 <145583236+Benamira05@users.noreply.github.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-06-25 15:41:16 +02:00
Mawer b763e37224
feat: implement image token estimation for HIGH/AUTO detail levels (#5388)
### PR Description

Image token estimation in AzureOpenAiTokenCountEstimator was hardcoded
to 85 tokens regardless of detail level. This significantly
underestimates costs for HIGH detail images (which actually cost 765+
tokens for a typical 1024x1024 image).

Implement detail-level-aware token estimation:
- LOW: 85 tokens (unchanged, per OpenAI documentation)
- MEDIUM: 400 tokens (conservative estimate)
- HIGH/ULTRA_HIGH/AUTO: 765 tokens (typical for 1024x1024 image)

The exact token count for HIGH detail depends on image dimensions (85 +
170 per 512x512 tile), but since we don't have access to dimensions in
the estimator, we use a conservative default.

References:
- OpenAI Vision documentation:
https://platform.openai.com/docs/guides/vision
- ImageContent.DetailLevel enum in langchain4j-core

---

## Issue
Closes # (discovered via code audit)

## Change

Implement detail-level-aware image token estimation in
`AzureOpenAiTokenCountEstimator`.

**Before:** All images estimated at 85 tokens regardless of detail
level.

**After:**
- LOW: 85 tokens (unchanged, per OpenAI documentation)
- MEDIUM: 400 tokens (conservative estimate)
- HIGH/ULTRA_HIGH/AUTO: 765 tokens (typical for 1024x1024 image)

The exact token count for HIGH detail depends on image dimensions (85 +
170 per 512x512 tile), but since we don't have access to dimensions in
the estimator, we use a conservative default.

The `ImageContent.detailLevel()` method and `DetailLevel` enum already
exist in langchain4j-core but were being ignored.

## General checklist
- [X] There are no breaking changes (API, behaviour)
- [ ] I have added unit and/or integration tests for my change
- [X] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green
- [ ] I have added/updated the documentation
2026-06-17 18:48:38 +02:00
AK 1a15bbd702
docs: fix 'allow(s) to' grammar across docs and Javadocs (#5439)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as a draft initially. Once it is reviewed and
approved, we will ask you to add documentation and examples.
Please note that PRs with breaking changes or without tests will be
rejected.

Please note that PRs will be reviewed based on the priority of the
issues they address.
We ask for your patience. We are doing our best to review your PR as
quickly as possible.
Please refrain from pinging and asking when it will be reviewed. Thank
you for understanding!
-->

## Issue
<!-- Please specify the ID of the issue this PR is addressing. For
example: "Closes #1234" or "Fixes #1234" -->
Closes #

## Change
<!-- Please describe the changes you made. -->


## General checklist
<!-- Please double-check the following points and mark them like this:
[X] -->
- [x] There are no breaking changes (API, behaviour)
- [ ] I have added unit and/or integration tests for my change
- [ ] 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
- [ ] 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
<!-- Before adding documentation and example(s) (below), please wait
until the PR is reviewed and approved. -->
- [ ] 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)


## Checklist for adding new maven module
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added my new module in the root `pom.xml` and
`langchain4j-bom/pom.xml`


## Checklist for adding new embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreIT` that extends
from either `EmbeddingStoreIT` or `EmbeddingStoreWithFilteringIT`
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreRemovalIT` that
extends from `EmbeddingStoreWithRemovalIT`

## Checklist for changing existing embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have manually verified that the
`{NameOfIntegration}EmbeddingStore` works correctly with the data
persisted using the latest released version of LangChain4j

Signed-off-by: Anjali K <anjali.kakkar@ltts.com>
Co-authored-by: Anjali K <anjali.kakkar@ltts.com>
2026-06-17 18:17:14 +02:00
renovate[bot] b8b3139fe2
Update dependency io.netty:netty-codec-http2 to v4.1.135.Final [SECURITY] (#5392)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [io.netty:netty-codec-http2](https://netty.io/)
([source](https://redirect.github.com/netty/netty)) | `4.1.132.Final` →
`4.1.135.Final` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.netty:netty-codec-http2/4.1.135.Final?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.netty:netty-codec-http2/4.1.132.Final/4.1.135.Final?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/2069) for more information.

---

### Netty HTTP/2: Advertised MAX_CONCURRENT_STREAMS are not enforced
[CVE-2026-47244](https://nvd.nist.gov/vuln/detail/CVE-2026-47244) /
[GHSA-5x3r-wrvg-rp6q](https://redirect.github.com/advisories/GHSA-5x3r-wrvg-rp6q)

<details>
<summary>More information</summary>

#### Details
##### Impact
DefaultHttp2Connection.DefaultEndpoint initialises
maxActiveStreams/maxStreams to Integer.MAX_VALUE, and Http2Settings
never inserts SETTINGS_MAX_CONCURRENT_STREAMS by default
(Http2Settings.java:305-307 only clamps a user-supplied value). Unless
the application explicitly calls
initialSettings().maxConcurrentStreams(n), a Netty HTTP/2 server
advertises no limit and enforces none locally. Each open stream
allocates a DefaultStream object, PropertyMap slots, flow-controller
state and IntObjectHashMap entry; with ~2^30 permissible odd stream IDs
a single TCP connection can create hundreds of thousands of long-lived
stream objects. This is also the precondition for CVE-2023-44487-style
Rapid-Reset amplification, where the absence of a low concurrent cap
multiplies backend work.

##### Resources
https://www.rfc-editor.org/rfc/rfc7540.html#section-6.5.2

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`

#### References
-
[https://github.com/netty/netty/security/advisories/GHSA-5x3r-wrvg-rp6q](https://redirect.github.com/netty/netty/security/advisories/GHSA-5x3r-wrvg-rp6q)
-
[https://github.com/netty/netty/releases/tag/netty-4.1.135.Final](https://redirect.github.com/netty/netty/releases/tag/netty-4.1.135.Final)
-
[https://github.com/netty/netty/releases/tag/netty-4.2.15.Final](https://redirect.github.com/netty/netty/releases/tag/netty-4.2.15.Final)
-
[https://github.com/advisories/GHSA-5x3r-wrvg-rp6q](https://redirect.github.com/advisories/GHSA-5x3r-wrvg-rp6q)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-5x3r-wrvg-rp6q)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/langchain4j/langchain4j).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-06-09 10:19:25 +02:00
github-actions[bot] 01de41d641 Update versions to 1.17.0-SNAPSHOT and 1.17.0-beta27-SNAPSHOT 2026-06-06 06:46:38 +00:00
github-actions[bot] cd836845dd Release versions 1.16.0 and 1.16.0-beta26 2026-06-05 15:46:56 +00:00
Thinh Nguyen 18c8397b23
Remove default fallback to Netty when a custom HttpClientProvider is provided for Azure OpenAI integration. (#5355)
## Issue
  Closes #5352

  ## Change

Makes `azure-core-http-netty` optional when a custom
`HttpClientProvider` is supplied via the builder.

  ### Details

- Removed hard constructor reference `NettyAsyncHttpClientProvider::new`
from `InternalAzureOpenAiHelper.setupOpenAIClientBuilder` — previously
baked into the constant pool as a `MethodHandle`, causing
`NoClassDefFoundError` at method invocation time even when a custom
provider
  was set
- Replaced with a lazy `ServiceLoader` lookup that only runs when no
custom provider is supplied
- Added package-private overload
`loadDefaultHttpClientProvider(ClassLoader)` for clean testability
without reflection or thread manipulation
- Added tests covering: ServiceLoader discovery when Netty is on
classpath, custom provider is used and default is skipped, and
`IllegalStateException` when no provider is found

`azure-core-http-netty` already registers `NettyAsyncHttpClientProvider`
via `META-INF/services/com.azure.core.http.HttpClientProvider`, so
default behaviour is unchanged.

  ## 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
- [x] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green
- [ ] I have manually run all the unit and integration tests in the core
and main modules, and they are all green
  - [ ] I have added/updated the documentation
- [ ] I have added an example in the examples repo (only for "big"
features)
  - [ ] I have added/updated Spring Boot starter(s) (if applicable)

  ## Checklist for adding new maven module
- [ ] I have added my new module in the root `pom.xml` and
`langchain4j-bom/pom.xml`

  ## Checklist for adding new embedding store integration
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreIT` that extends
from either `EmbeddingStoreIT` or `EmbeddingStoreWithFilteringIT`
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreRemovalIT` that
extends from `EmbeddingStoreWithRemovalIT`

  ## Checklist for changing existing embedding store integration
- [ ] I have manually verified that the
`{NameOfIntegration}EmbeddingStore` works correctly with the data
persisted using the latest released version of LangChain4j

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-06-03 18:49:37 +02:00
github-actions[bot] 6185599e37 Update versions to 1.16.0-SNAPSHOT and 1.16.0-beta26-SNAPSHOT 2026-05-15 16:21:14 +00:00
github-actions[bot] d0e54aa006 Release versions 1.15.0 and 1.15.0-beta25 2026-05-15 15:55:12 +00:00
github-actions[bot] 628ac34c01 Update versions to 1.15.0-SNAPSHOT and 1.15.0-beta25-SNAPSHOT 2026-04-30 18:43:12 +00:00
github-actions[bot] 4917afa297 Release versions 1.14.0 and 1.14.0-beta24 2026-04-30 18:10:30 +00:00
Dmytro Liubarskyi 634188c469 fixing flaky tests 2026-04-14 09:18:19 +02:00
github-actions[bot] 4798a89d66 Update versions to 1.14.0-SNAPSHOT and 1.14.0-beta24-SNAPSHOT 2026-04-09 14:41:27 +00:00
github-actions[bot] 759cd9a236 Release versions 1.13.0 and 1.13.0-beta23 2026-04-09 13:07:30 +00:00
Dmytro Liubarskyi 8ff282f12c
Support Tools Returning Images (#4851)
## Issue
Closes https://github.com/langchain4j/langchain4j/issues/4652

## Change
- Add support for `@Tool`-annotated methods to return multimodal content
(images) to the LLM, not just text. Tools can now return `Image`,
`ImageContent`, `Content`, `List<Content>`, `Content[]`, etc.
- Refactor `ToolExecutionResultMessage` to store `List<Content>`
internally instead of a plain `String`, with backward-compatible
`text()` accessor and new `contents()` / `hasSingleText()`
  methods.
- Refactor `ToolExecutionResult` to support
`resultContents(List<Content>)` as an alternative to
`resultText(String)` and `resultTextSupplier(Supplier)`.
- Implement multimodal tool result mapping for providers that support
it: Anthropic, Amazon Bedrock, and Google AI Gemini.
- Add `UnsupportedFeatureException` guards in providers that do not
support non-text tool results: Azure OpenAI, GitHub Models, Jlama,
Mistral AI, Ollama, OpenAI (Chat Completions
& Responses), Vertex AI (Anthropic & Gemini), Watsonx, and Workers AI.
- Update `ToolExecutedEvent` / `DefaultToolExecutedEvent` to carry
`resultContents` alongside the existing `resultText` accessor.
- Add comprehensive unit and integration tests
(`should_execute_tool_returning_Image`,
`should_execute_tool_returning_ImageContent`,
`should_execute_tool_returning_ContentList`,

`should_fail_when_tool_returns_image_and_provider_does_not_support_it`).
- Update tools documentation with new "Returning Images and Multimodal
Content" and "Multimodal Tool Results" sections.

## 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
- [X] 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-04-07 13:54:34 +02:00
renovate[bot] ed90147922
Update dependency io.netty:netty-codec-http2 to v4.1.132.Final [SECURITY] (#4790)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [io.netty:netty-codec-http2](https://netty.io/)
([source](https://redirect.github.com/netty/netty)) | `4.1.130.Final` →
`4.1.132.Final` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.netty:netty-codec-http2/4.1.132.Final?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.netty:netty-codec-http2/4.1.130.Final/4.1.132.Final?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/2069) for more information.

### GitHub Vulnerability Alerts

####
[CVE-2026-33871](https://redirect.github.com/netty/netty/security/advisories/GHSA-w9fj-cfpg-grvv)

### Summary
A remote user can trigger a Denial of Service (DoS) against a Netty
HTTP/2 server by sending a flood of `CONTINUATION` frames. The server's
lack of a limit on the number of `CONTINUATION` frames, combined with a
bypass of existing size-based mitigations using zero-byte frames, allows
an user to cause excessive CPU consumption with minimal bandwidth,
rendering the server unresponsive.

### Details
The vulnerability exists in Netty's `DefaultHttp2FrameReader`. When an
HTTP/2 `HEADERS` frame is received without the `END_HEADERS` flag, the
server expects one or more subsequent `CONTINUATION` frames. However,
the implementation does not enforce a limit on the *count* of these
`CONTINUATION` frames.

The key issue is located in
`codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java`.
The `verifyContinuationFrame()` method checks for stream association but
fails to implement a frame count limit.

Any user can exploit this by sending a stream of `CONTINUATION` frames
with a zero-byte payload. While Netty has a `maxHeaderListSize`
protection to limit the total size of headers, this check is never
triggered by zero-byte frames. The logic effectively evaluates to
`maxHeaderListSize - 0 < currentSize`, which will not trigger the limit
until a non-zero byte is added. As a result, the server is forced to
process an unlimited number of frames, consuming a CPU thread and
monopolizing the connection.


`codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java`

**`verifyContinuationFrame()` (lines 381-393)** — No frame count check:
```java
private void verifyContinuationFrame() throws Http2Exception {
    verifyAssociatedWithAStream();
    if (headersContinuation == null) {
        throw connectionError(PROTOCOL_ERROR, "...");
    }
    if (streamId != headersContinuation.getStreamId()) {
        throw connectionError(PROTOCOL_ERROR, "...");
    }
    // NO frame count limit!
}
```

**`HeadersBlockBuilder.addFragment()` (lines 695-723)** — Byte limit
bypassed by 0-byte frames:
```java
// Line 710-711: This check NEVER fires when len=0
if (headersDecoder.configuration().maxHeaderListSizeGoAway() - len <
        headerBlock.readableBytes()) {
    headerSizeExceeded();  // 10240 - 0 < 1 => FALSE always
}
```

When `len=0`: `maxGoAway - 0 < readableBytes` → `10240 < 1` → FALSE. The
byte limit is never triggered.

### Impact
This is a CPU-based Denial of Service (DoS). Any service using Netty's
default HTTP/2 server implementation is impacted. An unauthenticated
user can exhaust server CPU resources and block legitimate users,
leading to service unavailability. The low bandwidth requirement for the
attack makes it highly practical.

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/langchain4j/langchain4j).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My45MS41IiwidXBkYXRlZEluVmVyIjoiNDMuMTAwLjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-04-01 13:25:21 +02:00
Dmytro Liubarskyi d6ee17a7e6 fixing ITs 2026-04-01 09:38:03 +02:00
Dmytro Liubarskyi eed309e4ca fixing ITs 2026-03-31 09:38:08 +02:00
Dmytro Liubarskyi b19d71a74c fixing ITs 2026-03-30 10:49:51 +02:00
Dmytro Liubarskyi e10abf04d0
Update versions to 1.13.0-SNAPSHOT and 1.13.0-beta23-SNAPSHOT (#4710)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-13 11:39:50 +01:00
Dmytro Liubarskyi c92ea033e4
Update versions to 1.13.0-SNAPSHOT and 1.13.0-beta22-SNAPSHOT (#4666)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-05 17:18:22 +01:00
Dmytro Liubarskyi 5d31a3d72f disabling flaky tests 2026-03-03 09:57:32 +01:00
Julien Dubois 606b6aeee3
Bump com.azure:azure-sdk-bom from 1.3.3 to 1.3.4 (#4541)
This dependency update is also updating several transitive dependencies,
and fixing a some security issues.
2026-02-05 10:07:18 +01:00
Dmytro Liubarskyi 336b2accce
Update versions to 1.12.0-SNAPSHOT and 1.12.0-beta20-SNAPSHOT (#4537)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 14:27:44 +01:00
Julien Dubois db2f60b146
Migrate from Azure OpenAi to Microsoft Foundry (#4415)
Microsoft Foundry is the new platform for deploying and operating AI
models (not just OpenAi) on Microsoft Cloud.
2026-02-03 19:04:18 +01:00
Dmytro Liubarskyi 6363fdb80a CI: parallelize tests in main module 2026-01-09 11:11:57 +01:00
Dmytro Liubarskyi c116ec6ec9 cleanup: moved dependency versions to the modules where they are used 2026-01-05 15:52:36 +01:00
Dmytro Liubarskyi 778be1b360
Update versions to 1.11.0-SNAPSHOT and 1.11.0-beta19-SNAPSHOT (#4285)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-24 15:38:05 +01:00
Mohamed AIT ABDERRAHMAN d79209b414
Add OpenAI Transcription support (#4101)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as a draft initially. Once it is reviewed and
approved, we will ask you to add documentation and examples.
Please note that PRs with breaking changes or without tests will be
rejected.

Please note that PRs will be reviewed based on the priority of the
issues they address.
We ask for your patience. We are doing our best to review your PR as
quickly as possible.
Please refrain from pinging and asking when it will be reviewed. Thank
you for understanding!
-->

## Issue
<!-- Please specify the ID of the issue this PR is addressing. For
example: "Closes #1234" or "Fixes #1234" -->
Closes #2985

## Change

This PR adds OpenAI audio transcription support to langchain4j,
implementing the `/audio/transcriptions` API endpoint. The
implementation follows the existing patterns in the codebase and
integrates cleanly with the HTTP client infrastructure.

### Key Changes
- Added `AudioTranscriptionModel` interface in langchain4j-core with
convenience methods
- Implemented `OpenAiAudioModel` with support for Whisper and GPT-4o
transcription models
- Added multipart/form-data support to HTTP client infrastructure
(`addFile()` method)
- Created `MultipartBodyPublisher` for JDK HTTP client to handle file
uploads
- Implemented audio format detection and binary/base64 data handling
- Added comprehensive test coverage including integration tests
- Updated Azure OpenAI implementation to use new interface

### Sequence Diagram

```mermaid
sequenceDiagram
    participant User
    participant OpenAiAudioModel
    participant OpenAiClient
    participant DefaultOpenAiClient
    participant JdkHttpClient
    participant MultipartBodyPublisher
    participant OpenAI API

    User->>OpenAiAudioModel: transcribe(AudioTranscriptionRequest)
    OpenAiAudioModel->>OpenAiAudioModel: validate(audioRequest)
    OpenAiAudioModel->>OpenAiAudioModel: requestBuilder()
    OpenAiAudioModel->>OpenAiClient: audioTranscription(OpenAiAudioTranscriptionRequest)
    OpenAiClient->>DefaultOpenAiClient: audioTranscription()
    DefaultOpenAiClient->>DefaultOpenAiClient: getBinaryDataFromAudio()
    DefaultOpenAiClient->>DefaultOpenAiClient: getAudioExtension()
    DefaultOpenAiClient->>DefaultOpenAiClient: build HttpRequest with multipart data
    DefaultOpenAiClient->>JdkHttpClient: execute(HttpRequest)
    JdkHttpClient->>JdkHttpClient: toJdkRequest()
    JdkHttpClient->>MultipartBodyPublisher: ofMultipartData()
    MultipartBodyPublisher->>MultipartBodyPublisher: addFormField()
    MultipartBodyPublisher->>MultipartBodyPublisher: addFile()
    MultipartBodyPublisher->>MultipartBodyPublisher: build()
    JdkHttpClient->>OpenAI API: POST /audio/transcriptions
    OpenAI API-->>JdkHttpClient: OpenAiAudioTranscriptionResponse
    JdkHttpClient-->>DefaultOpenAiClient: SuccessfulHttpResponse
    DefaultOpenAiClient-->>OpenAiClient: ParsedAndRawResponse
    OpenAiClient-->>OpenAiAudioModel: OpenAiAudioTranscriptionResponse
    OpenAiAudioModel->>OpenAiAudioModel: AudioTranscriptionResponse.from()
    OpenAiAudioModel-->>User: AudioTranscriptionResponse
```

## General checklist
<!-- Please double-check the following points and mark them like this:
[X] -->
- [x] There are no breaking changes (API, behaviour)
- [x] I have added unit and/or integration tests for my change
- [ ] 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
- [ ] 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
<!-- Before adding documentation and example(s) (below), please wait
until the PR is reviewed and approved. -->
- [ ] 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)


## Checklist for adding new maven module
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added my new module in the root `pom.xml` and
`langchain4j-bom/pom.xml`


## Checklist for adding new embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreIT` that extends
from either `EmbeddingStoreIT` or `EmbeddingStoreWithFilteringIT`
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreRemovalIT` that
extends from `EmbeddingStoreWithRemovalIT`

## Checklist for changing existing embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have manually verified that the
`{NameOfIntegration}EmbeddingStore` works correctly with the data
persisted using the latest released version of LangChain4j

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-12-23 15:28:12 +01:00
Dmytro Liubarskyi 25d3b3ec87 cleaned up test dependencies 2025-12-10 10:18:30 +01:00
Dmytro Liubarskyi ca6097e35d
Update versions to 1.10.0-SNAPSHOT and 1.10.0-beta18-SNAPSHOT (#4152)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-28 12:21:30 +01:00
Dmytro Liubarskyi 03fd63a568 Revert "Fixed https://github.com/langchain4j/langchain4j/issues/4125"
This reverts commit 9bb2f45e13.
2025-11-27 11:50:59 +01:00
Dmytro Liubarskyi 9bb2f45e13 Fixed https://github.com/langchain4j/langchain4j/issues/4125 2025-11-27 10:26:33 +01:00
Dmytro Liubarskyi bc0801a4df
Update versions to 1.10.0-SNAPSHOT and 1.10.0-beta17-SNAPSHOT (#4140)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-26 17:38:36 +01:00
Dmytro Liubarskyi 19b40ba70f Azure OpenAI: removed tests for models we can't test anymore (gpt-35-turbo-instruct is deprecated) 2025-11-19 12:06:47 +01:00
Caio Carneiro 59ce706d61
feat: Enable reasoning effort control for GPT-5 (o1 series) (#4052)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as a draft initially. Once it is reviewed and
approved, we will ask you to add documentation and examples.
Please note that PRs with breaking changes or without tests will be
rejected.

Please note that PRs will be reviewed based on the priority of the
issues they address.
We ask for your patience. We are doing our best to review your PR as
quickly as possible.
Please refrain from pinging and asking when it will be reviewed. Thank
you for understanding!
-->

## Issue
<!-- Please specify the ID of the issue this PR is addressing. For
example: "Closes #1234" or "Fixes #1234" -->
Closes #4051

## Change
Add support for the ReasoningEffortValue parameter in the
AzureOpenAiChatModel and AzureOpenAiStreamingChatModel.java class. This
enhancement would:
Extend the AzureOpenAiChatModel.Builder to accept a reasoningEffort
parameter
Store the reasoningEffort value in the model configuration
Pass the reasoningEffort value to the ChatCompletionsOptions when making
API calls
Allow users to specify reasoning effort levels (LOW, MEDIUM, HIGH) for
GPT-5 models
This would enable full compatibility with GPT-5 (o1 series) models and
provide users with control over the reasoning compute effort.


## General checklist
<!-- Please double-check the following points and mark them like this:
[X] -->
- [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
- [ ] 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
<!-- Before adding documentation and example(s) (below), please wait
until the PR is reviewed and approved. -->
- [ ] 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)


## Checklist for adding new maven module
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added my new module in the root `pom.xml` and
`langchain4j-bom/pom.xml`


## Checklist for adding new embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreIT` that extends
from either `EmbeddingStoreIT` or `EmbeddingStoreWithFilteringIT`
- [ ] I have added a `{NameOfIntegration}EmbeddingStoreRemovalIT` that
extends from `EmbeddingStoreWithRemovalIT`

## Checklist for changing existing embedding store integration
<!-- Please double-check the following points and mark them like this:
[X] -->
- [ ] I have manually verified that the
`{NameOfIntegration}EmbeddingStore` works correctly with the data
persisted using the latest released version of LangChain4j
2025-11-14 13:56:08 +01:00
Dmytro Liubarskyi 98d45ac2b4
Fixes https://github.com/langchain4j/langchain4j/issues/3968 (#4026)
## Issue
Fixes https://github.com/langchain4j/langchain4j/issues/3968
Closes https://github.com/langchain4j/langchain4j/pull/3753

## Change


## General checklist
- [X] There are no breaking changes (API, behaviour)
- [ ] I have added unit and/or integration tests for my change
- [ ] The tests cover both positive and negative cases
- [X] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green
- [ ] 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
2025-11-13 12:21:31 +01:00
Dmytro Liubarskyi a473835133
Update versions to 1.9.0-SNAPSHOT and 1.9.0-beta16-SNAPSHOT (#3951)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-10-24 16:51:33 +02:00
Dmytro Liubarskyi ecc754ba1f
Streaming Cancellation (#3910)
## Issue
Closes https://github.com/langchain4j/langchain4j/issues/1146

## Change
Implemented streaming cancellation for the following APIs:
- `StreamingChatModel` + `StreamingChatResponseHandler`
- `TokenStream`

Implemented streaming cancellation for the following modules:
- Anthropic
- Azure OpenAI
- Bedrock
- Google AI Gemini
- Mistral
- Ollama
- OpenAI
- OpenAI Official
- Vertex AI Gemini

## Examples

### `StreamingChatModel` + `StreamingChatResponseHandler` APIs

If you wish to cancel the streaming, you can do so from one of the
following `StreamingChatResponseHandler` methods:
- `onPartialResponse(PartialResponse, PartialResponseContext)`
- `onPartialThinking(PartialThinking, PartialThinkingContext)`
- `onPartialToolCall(PartialToolCall, PartialToolCallContext)`

The context object contains the `StreamingHandle`, which can be used to
cancel the streaming:
```java
model.chat(userMessage, new StreamingChatResponseHandler() {

    @Override
    public void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context) {
        process(partialResponse);
        if (shouldCancel()) {
            context.streamingHandle().cancel();
        }
    }

    @Override
    public void onCompleteResponse(ChatResponse completeResponse) {
        System.out.println("onCompleteResponse: " + completeResponse);
    }

    @Override
    public void onError(Throwable error) {
        error.printStackTrace();
    }
});
```

### `TokenStream` API

If you wish to cancel the streaming, you can do so from one of the
following callbacks:
- `onPartialResponseWithContext(BiConsumer<PartialResponse,
PartialResponseContext>)`
- `onPartialThinkingWithContext(BiConsumer<PartialThinking,
PartialThinkingContext>)`

For example:
```java
tokenStream
    .onPartialResponseWithContext((PartialResponse partialResponse, PartialResponseContext context) -> {
        process(partialResponse);
        if (shouldCancel()) {
            context.streamingHandle().cancel();
        }
    })
    .onCompleteResponse((ChatResponse response) -> futureResponse.complete(response))
    .onError((Throwable error) -> futureResponse.completeExceptionally(error))
    .start();
```

When `StreamingHandle.cancel()` is called, LangChain4j will close the
connection and stop the streaming.
Once `StreamingHandle.cancel()` has been called, `TokenStream` will not
receive any further callbacks.


## 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
- [x] 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)
2025-10-23 20:00:57 +02:00
Dmytro Liubarskyi a592606efe fixed flaky tests 2025-10-22 09:36:53 +02:00
Mathieu Stennier b480cbf9c6
feat: add support for base64 images using OpenAi Azure (#3909)
## Issue
Closes #3908

## Change

As of this implementation, the Azure OpenAI provider in LangChain4j now
supports sending images as base64-encoded data URIs. This allows you to
send images to GPT-4 Vision models without requiring a publicly
accessible URL.

## Background

Azure OpenAI Chat Completions API accepts images in the `image_url`
content block where the URL can be:
- An HTTP(S) URL pointing to an image
- A data URI in the format: `data:image/<mime>;base64,<payload>`

Previously, LangChain4j's Azure OpenAI provider only supported HTTP(S)
URLs and would throw an error when attempting to use base64 images. This
has been fixed.

## Main class that I used for testing

```java
package dev.langchain4j.model.azure;

import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.response.ChatResponse;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;

/**
 * Simple example demonstrating base64 image support with Azure OpenAI.
 * 
 * To run this example:
 * 1. Set your Azure OpenAI endpoint and API key below
 * 2. Set the deployment name (e.g., "gpt-4o", "gpt-4-vision-preview")
 * 3. Set the path to an image file you want to analyze
 * 4. Run the main method
 */
public class Base64ImageExample {

    public static void main(String[] args) {
        // ============================================
        // CONFIGURATION - UPDATE THESE VALUES
        // ============================================
        
        // Your Azure OpenAI endpoint (e.g., "https://your-resource.openai.azure.com/")
        String azureEndpoint = "...";
        
        // Your Azure OpenAI API key
        String azureApiKey = "...";
        
        // Your deployment name (must be a vision-capable model like gpt-4o, gpt-4-vision-preview, etc.)
        String deploymentName = "gpt-4.1";
        
        // Path to the image you want to analyze
        String imagePath = "humour.webp";

        // Question to ask about the image
        String question = "What do you see in this image? Please describe it in detail.";
        
        // ============================================
        // END CONFIGURATION
        // ============================================
        
        try {
            System.out.println("=== Azure OpenAI Base64 Image Example ===\n");
            
            // Validate configuration
            if (azureEndpoint.contains("YOUR_") || azureApiKey.contains("YOUR_")) {
                System.err.println("ERROR: Please update the Azure OpenAI endpoint and API key in the code.");
                System.err.println("Look for the CONFIGURATION section at the top of this file.");
                System.exit(1);
            }
            
            // Read and encode the image
            System.out.println("1. Reading image from: " + imagePath);
            Path imageFile = Paths.get(imagePath);
            if (!Files.exists(imageFile)) {
                System.err.println("ERROR: Image file not found: " + imagePath);
                System.err.println("Please update the 'imagePath' variable with a valid image path.");
                System.exit(1);
            }
            
            byte[] imageBytes = Files.readAllBytes(imageFile);
            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
            System.out.println("   Image size: " + imageBytes.length + " bytes");
            System.out.println("   Base64 length: " + base64Image.length() + " characters");
            
            // Determine MIME type from file extension
            String fileName = imageFile.getFileName().toString().toLowerCase();
            String mimeType;
            if (fileName.endsWith(".png")) {
                mimeType = "image/png";
            } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
                mimeType = "image/jpeg";
            } else if (fileName.endsWith(".gif")) {
                mimeType = "image/gif";
            } else if (fileName.endsWith(".webp")) {
                mimeType = "image/webp";
            } else {
                mimeType = "image/png"; // default
                System.out.println("   Warning: Unknown file extension, assuming PNG");
            }
            System.out.println("   MIME type: " + mimeType);
            
            // Create Azure OpenAI chat model
            System.out.println("\n2. Creating Azure OpenAI chat model...");
            System.out.println("   Endpoint: " + azureEndpoint);
            System.out.println("   Deployment: " + deploymentName);
            
            AzureOpenAiChatModel model = AzureOpenAiChatModel.builder()
                    .endpoint(azureEndpoint)
                    .apiKey(azureApiKey)
                    .deploymentName(deploymentName)
                    .logRequestsAndResponses(false) // Set to true to see the full request/response
                    .build();
            
            // Create message with base64 image
            System.out.println("\n3. Creating message with base64 image...");
            System.out.println("   Question: " + question);
            
            UserMessage message = UserMessage.from(
                    TextContent.from(question),
                    ImageContent.from(base64Image, mimeType)
            );
            
            // Send request and get response
            System.out.println("\n4. Sending request to Azure OpenAI...");
            System.out.println("   (This may take a few seconds)\n");
            
            ChatRequest chatRequest = ChatRequest.builder()
                    .messages(message)
                    .build();
            
            ChatResponse response = model.chat(chatRequest);
            
            // Display the response
            System.out.println("=== RESPONSE ===");
            System.out.println(response.aiMessage().text());
            System.out.println("\n=== METADATA ===");
            if (response.metadata() != null && response.metadata().tokenUsage() != null) {
                System.out.println("Input tokens: " + response.metadata().tokenUsage().inputTokenCount());
                System.out.println("Output tokens: " + response.metadata().tokenUsage().outputTokenCount());
                System.out.println("Total tokens: " + response.metadata().tokenUsage().totalTokenCount());
            }
            if (response.metadata() != null && response.metadata().finishReason() != null) {
                System.out.println("Finish reason: " + response.metadata().finishReason());
            }
            
            System.out.println("\n=== SUCCESS ===");
            System.out.println("✓ Base64 image was successfully processed by Azure OpenAI!");
            
        } catch (Exception e) {
            System.err.println("\n=== ERROR ===");
            System.err.println("Failed to process image: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}
```

that produced the following output using file in copy.

``` 
=== Azure OpenAI Base64 Image Example ===

1. Reading image from: humour.webp
   Image size: 61998 bytes
   Base64 length: 82664 characters
   MIME type: image/webp

2. Creating Azure OpenAI chat model...
   Endpoint: XXX
   Deployment: gpt-4.1
SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@97e1986]
SLF4J(W): Found provider [org.tinylog.slf4j.TinylogSlf4jServiceProvider@26f67b76]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.apache.logging.slf4j.SLF4JServiceProvider@97e1986]

3. Creating message with base64 image...
   Question: What do you see in this image? Please describe it in detail.

4. Sending request to Azure OpenAI...
   (This may take a few seconds)

=== RESPONSE ===
This image consists of two main parts: text at the top and a photo of a car at the bottom.

**Text (in French):**
"Je ne sais plus si je dois inculper le garagiste, l'opticien ou le bistro !!!"
Translation: "I no longer know whether to blame the mechanic, the optician, or the bar!!!"

There are two emojis next to the text: 
- A laughing emoji with tears of joy 
- A thinking face emoji (hand on chin)

**Image (bottom):**
There is a bright red vintage car parked on a street. It looks very unusual and distorted: the windows are misaligned, the wheels seem oddly placed, and the general shape of the car is very awkward, almost comically so. It looks like the car has been badly photoshopped or was constructed incorrectly, making it look absurd and nonfunctional.

**Context and mood:**
The text and the image together create a humorous effect. The joke is that the car looks so weird and dysfunctional that it is hard to know whether the fault lies with the mechanic (garagiste), the optician (who maybe didn't see the errors), or the bar (maybe someone was drunk when designing/repairing it). The emojis reinforce the funny and bewildered reaction.

**Summary:**
The image is a funny meme showing a badly assembled or photoshopped car, with text joking about which professional is to blame for the absurd result.

=== METADATA ===
Input tokens: 648
Output tokens: 297
Total tokens: 945
Finish reason: STOP

=== SUCCESS ===
✓ Base64 image was successfully processed by Azure OpenAI!
```


![humour](https://github.com/user-attachments/assets/18a564e5-7f64-4d6f-b34f-95a72baff519)



## 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
- [x] 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
<!-- Before adding documentation and example(s) (below), please wait
until the PR is reviewed and approved. -->
- [ ] 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)
2025-10-20 13:41:00 +02:00
Dmytro Liubarskyi ad4572c1ac fixing flaky ITs 2025-10-16 09:50:33 +02:00
Dmytro Liubarskyi 9d26e1efad fixed flaky ITs 2025-10-06 15:05:16 +02:00