Commit Graph

3758 Commits

Author SHA1 Message Date
renovate[bot] 1f3b96f84f
Update dependency com.github.docker-java:docker-java-transport-httpclient5 to v3.7.1 2026-07-20 16:00:10 +00:00
Dmytro Liubarskyi 1eb7f7a060
Disable Apache HttpClient's automatic retries by default in ApacheHttpClient (#5811)
## Issue
  Related to #5789

  ## Change
`ApacheHttpClient` builds the underlying Apache HttpClient 5 sync/async
clients from `HttpClients.custom()` / `HttpAsyncClients.custom()`, which
have `automaticRetriesDisabled = false` by default. As a result,
Apache's `DefaultHttpRequestRetryStrategy` transparently retries
requests (e.g. on connection failures and on HTTP `429`/`503`)
**underneath** LangChain4j's own retry logic. This has two consequences:

- `maxRetries=0` does not actually disable retries — Apache still
retries at the transport level.
- For any `maxRetries` value, retries are effectively stacked
(LangChain4j retries × Apache retries).

This PR makes LangChain4j the single source of truth for retry behavior:
when `ApacheHttpClient` creates the client builders itself, it now calls
`disableAutomaticRetries()` on both the sync and async
  builders.

To avoid overriding an explicit user choice, this is only applied to the
**default (LangChain4j-created) builders**. If a user supplies their own
`httpClientBuilder`/`httpAsyncClientBuilder`, their retry
configuration is left untouched — they remain free to configure retries
as they wish (and can call `disableAutomaticRetries()` themselves if
they want LangChain4j to be the only retry layer).

**Note on behavior:** for users on the default builder who were (often
unknowingly) relying on Apache's implicit retries, those transport-level
retries are no longer performed; retries are now governed solely
by LangChain4j's `maxRetries`. No public API changes (verified with
`revapi:check`).

This mirrors the Spring-side fix for `SpringRestClient` in
langchain4j/langchain4j-spring#200.

  ### Tests
Added `ApacheHttpClientRetriesIT` (WireMock, always returns `503`),
covering both cases:
- default builder → exactly **1** request is made (no transport-level
retry);
- user-supplied builder with an explicit
`DefaultHttpRequestRetryStrategy(2, …)` → **3** requests are made (1
initial + 2 retries), i.e. the user's configuration is honored.

No new dependencies were added (WireMock is already a test dependency
across modules).

  ## 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](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/langch 1 new message (ctrl+End) ↓
ig" features)
2026-07-20 11:15:48 +02:00
Eunbin Son 78dca297dc
fix: Read SSE error response body as UTF-8 without reassembling line separators (#5809)
## Issue

Closes #5808

## Change

`JdkHttpClient.readBody(HttpResponse<InputStream>)` builds the
`HttpException` message that streaming (SSE) callers receive through
`listener.onError()`. It read the stream with
`reader.lines().collect(joining(System.lineSeparator()))`, which drops
the original line separators and the trailing one, and its
`InputStreamReader` had no charset, so decoding fell back to the JVM
default.

The synchronous path in the same class (`execute(HttpRequest)`) already
does `new String(body, UTF_8)`. This change makes the streaming path
match:

```java
try (InputStream inputStream = response.body()) {
    return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
}
```

Three now-unused imports (`BufferedReader`, `InputStreamReader`,
`Collectors.joining`) are removed. Nothing else in the file changes.

New unit test `JdkHttpClientErrorBodyTest` (WireMock, no API key) stubs
a `400` with body `line1\r\nline2\r\n` and asserts the exception message
matches exactly. It fails before this change (`line1\nline2`) regardless
of the JVM default charset.

`ApacheHttpClient.readBody` has the same pattern. It is left alone so
this one can be reviewed first.

## 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

Unit tests in the module are green (9 tests). `JdkHttpClientIT` and
`JdkHttpClientTimeoutIT` were not run locally because they need external
services.

- [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
<!-- Will be added after approval, per the contribution guide -->
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
<!-- N/A - internal bug fix, no example needed -->
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features)
<!-- N/A - unrelated to Spring Boot starters -->
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)
2026-07-20 10:15:29 +02:00
Eunbin Son b44723943d
fix: Map connectTimeout to connection establishment in Apache HTTP client (#5807)
## Issue

Closes #5805

## Change

`ApacheHttpClient` passed the SPI `connectTimeout` to
`RequestConfig.Builder.setConnectionRequestTimeout`, which limits
waiting for a pooled connection, not connection establishment.
Establishment reads `RequestConfig.getConnectTimeout()`, never set here,
so the connection manager fell back to the `ConnectionConfig` default of
3 minutes. `JdkHttpClient` and `OkHttpClient` map the same value to the
real connection timeout.

The mapping is replaced; the pool lease timeout returns to the Apache
default.

```java
if (builder.connectTimeout() != null) {
    setConnectTimeout(requestConfigBuilder, builder.connectTimeout());
}
```

The `RequestConfig` is shared by the sync and async client builders, so
both paths are covered.

`RequestConfig.Builder.setConnectTimeout` is deprecated, hence
`@SuppressWarnings("deprecation")` on a small private helper. Its
`ConnectionConfig` replacement can only be applied through a connection
manager, which would override the one a user passes via
`httpClientBuilder(...)`. httpclient5 gives
`RequestConfig.getConnectTimeout()` precedence over `ConnectionConfig`
when it is set.

Testing: a delegating `HttpClientConnectionManager` spy asserts the
`Timeout` passed to `connect(...)` on a real request to WireMock
localhost — null when unset, the configured value when set. Whether the
timeout fires on a slow connection is not covered: WireMock cannot
simulate connection timeouts.

No text conflict with the open draft #5527, which touches the same file
but not this block; I will rebase if it merges first.

## 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
<!-- Unit tests green (8/8, JDK 17). The module integration tests were
not run locally: they need network/external services. -->
- [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
<!-- Unit tests only, via `-pl langchain4j-core,langchain4j clean test`.
-->
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
<!-- N/A — docs are added after review per the template -->
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features)
<!-- N/A — bug fix, not a feature -->
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)
<!-- N/A — no public API change -->
2026-07-20 10:13:20 +02:00
Tim 33b54373d1
Decode gzip/deflate responses in Utils.readBytes (#5801)
## Issue
Closes #5800

## Change
`Utils.readBytes(String url)` sends `Accept-Encoding: gzip, deflate` but
reads the
response body as a **raw** `InputStream`. `HttpURLConnection` only
decompresses gzip
transparently when it adds the `Accept-Encoding` header itself; because
the header is set
explicitly here, the JDK disables transparent decoding and returns the
body exactly as
sent. So when a server responds with `Content-Encoding: gzip` (nginx,
CDNs, S3, most image
hosts), `readBytes` returns the still-compressed bytes.

`readBytes(url)` is used to inline remote images/PDFs as base64 for
several model
integrations (Ollama, Bedrock, Vertex/Gemini image mappers,
`Image`/`PdfFile` helpers), so
a gzip-serving host silently produced a corrupted payload.

This PR decodes the response stream according to the `Content-Encoding`
header via a small
private helper:
- `gzip` / `x-gzip` → `GZIPInputStream`
- `deflate` → `InflaterInputStream` (zlib, per RFC 9110)
- anything else / no header → the raw stream, unchanged

No new dependencies. The uncompressed happy path is byte-for-byte
unchanged (same stream,
same copy loop). The wrapper is created inside the existing
try-with-resources, so closing
it closes the underlying HTTP stream.

Added `/gzip_endpoint` and `/deflate_endpoint` cases to
`UtilsTest.read_bytes` that serve a
compressed body with the matching `Content-Encoding` header and assert
the decoded bytes;
both fail against the previous behaviour.

## General checklist
- [X] There are no breaking changes (API, behaviour) — uncompressed
responses are unchanged
- [X] I have added unit and/or integration tests for my change
- [X] The tests cover both positive and negative cases — decoded
gzip/deflate plus the existing raw/uncompressed and HTTP-error paths
- [X] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green — `langchain4j-core`
`UtilsTest` (46/46) and `spotless:check` pass
- [ ] 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 — n/a, internal utility, no
public API or behaviour visible in docs
- [ ] I have added an example in the examples repo (only for "big"
features) — n/a
- [ ] I have added/updated Spring Boot starter(s) (if applicable) — n/a

---------

Co-authored-by: Timur Rakhmatullin <174210871+TimurRakhmatullin86@users.noreply.github.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-20 09:59:30 +02:00
Eunbin Son 1023c67539
fix: do not consume the response body on the OkHttp streaming path (#5806)
## Issue
Closes #5804

## Change

The streaming path used `fromOkHttpResponse(response)`, the converter
written for the synchronous path. That converter calls
`response.body().bytes()` unless the `Content-Type` is
`text/event-stream`, which reads the body to the end and closes it. The
parser then reads the same body, fails with `IOException: closed`, and
the listener receives no events. Servers that stream with another media
type hit this, for example Ollama with `application/x-ndjson`.

`fromOkHttpResponse` is now split into an overload that takes the body
bytes, and the streaming path passes `null`, the same way
`JdkHttpClient` calls `fromJdkResponse(jdkResponse, null)`.

```java
SuccessfulHttpResponse successResponse = fromOkHttpResponse(response, null);
```

The synchronous path is unchanged, and non-2xx responses are still
handled earlier by `readBody(response)`.

`OkHttpClientStreamingTest` serves responses from
`com.sun.net.httpserver.HttpServer`, so it needs no API key. It covers
the streaming path with `application/x-ndjson`, with
`text/event-stream`, with no `Content-Type`, and with a 500 response,
plus the synchronous path with and without `text/event-stream`.
Reverting the fix makes the two non-SSE streaming tests fail with
`IOException: closed`.

The import reordering and line-wrapping in the diff come from
`spotless:apply` (palantir), which reformats the whole file once it is
touched.

## 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
<!-- The two *IT classes in this module (OkHttpClientIT,
OkHttpClientTimeoutIT) require OPENAI_API_KEY and were not run. -->
- [ ] 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
<!-- langchain4j-core was built and tested as part of the -am build and
is green; the langchain4j main module was not run. -->
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
<!-- Behaviour fix, no documentation change. -->
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features)
<!-- Bug fix. -->
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)
<!-- No starter is affected. -->
2026-07-20 09:53:11 +02:00
Omkar Kathile c27e6e9c2d
Redact sensitive AWS auth headers (Authorization, X-Amz-Security-Toke… (#5799)
## Issue

Closes #5798

## Change

When `logRequests`/`logResponses` are enabled, `AwsLoggingInterceptor`
logs the full HTTP header set. Because `beforeTransmission` runs after
SigV4 signing, this includes the `Authorization` header (access key ID +
signature) and, when temporary/STS credentials are used (assumed roles,
EC2/ECS/EKS instance roles, IAM Identity Center), the
`X-Amz-Security-Token` a live, replayable session token writing
credentials to whatever log sink is configured (CWE-532).

This PR masks the values of these two headers (case-insensitive) with
`[REDACTED]` before logging, via a small package-private `maskHeaders()`
helper. This matches the header redaction already used elsewhere in
langchain4j (core `HttpRequestLogger`, and the cohere/jina/ovh-ai/nomic
request logging interceptors) and the AWS SDK's own wire logging, which
never emits these values.

All other headers, URL, query parameters, and body are logged unchanged.
No public API or request-behavior change only debug log text. Adds
`AwsLoggingInterceptorTest` (7 unit tests covering redaction of both
headers, case variants, non-sensitive header preservation, multi-value
headers, and null/empty maps).

Note: the import re-sort in the diff is mandated by the repo's spotless
config (matches sibling `BedrockCustomHeadersInterceptor`); no other
reformatting was done. `spotless:check` passes.

## 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
- [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


## 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
2026-07-20 09:10:03 +02:00
Andrea Di Maio 73ec8a22cb
Update the watsonx.ai SDK to the latest version (#5710)
## Change
<!-- Please describe the changes you made. -->
Update the watsonx.ai SDK to the latest version

## 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
- [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
- [ ] 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-20 09:04:53 +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] fa7f103e35 docu: update versions to 1.18.0 and 1.18.0-beta28 2026-07-17 12:48:33 +00:00
github-actions[bot] 66ad5ee6d5 Release versions 1.18.0 and 1.18.0-beta28 2026-07-17 12:34:45 +00:00
Dmytro Liubarskyi 3ea186f6e5 fixes after #5735 2026-07-17 13:43:28 +02:00
Mario Fusco 855e06b43c
Apply output function also to supervisor result (#5786)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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 #5774

## 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)
- [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](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)


## 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
2026-07-17 11:27:03 +02:00
Dmytro Liubarskyi 5c6f7cd32c
Updated Jackson to 2.22.1 (#5793)
## Change
Updated Jackson to 2.22.1

## 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
- [ ] 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
- [ ] 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-17 11:26:10 +02:00
Subhash Polisetti abf3fbfb66
feat(google-genai): add generateContentConfigCustomizer to the chat models (#5646)
`GoogleGenAiChatModel` and `GoogleGenAiStreamingChatModel` expose a
fixed set of generation options through their builders. When the
underlying Google Gen AI Java SDK adds a new `GenerateContentConfig`
option, it cannot be used through the integration until a matching
builder method is added and released.

This adds an escape hatch,
`generateContentConfigCustomizer(Consumer<GenerateContentConfig.Builder>)`,
on both chat model builders:

```java
GoogleGenAiChatModel.builder()
    .modelName("gemini-2.5-flash")
    .generateContentConfigCustomizer(config -> config.responseLogprobs(true).logprobs(5))
    .build();
```

The customizer is applied after the integration has populated the config
(generation parameters, tools, system instruction, etc.) and just before
it is built, so a caller can set any SDK option (including ones not yet
exposed here) or override an existing value, while the per-request tools
and system instruction are preserved.

This addresses the configuration side of the suggestion from the
integration review in #4658. The response side of that discussion
already shipped as `GoogleGenAiChatResponseMetadata.rawResponse()`; this
adds the matching request side, which isn't in the module yet.

A note on the chosen shape: the review mentioned accepting a
`GenerateContentConfig` object directly. That does not work well here,
because tools and the system instruction are derived per request, so a
model-level config object would drop them. A customizer applied to the
already-assembled builder keeps them intact and still lets a caller
reach any SDK option. If you'd prefer a different shape (for example
accepting a full config object), or a different method name, I'm happy
to adjust.

Scope: this covers the sync and streaming chat models.
`GoogleGenAiBatchChatModel` is left as is; happy to extend the same
option to it in a follow-up if that's wanted.

Addresses #4658

### Changes
- `GoogleGenAiConfigBuilder`: add a static `applyCustomizer(config,
customizer)` that returns the config unchanged when the customizer is
`null`, otherwise re-opens it via `toBuilder()`, applies the customizer,
and rebuilds. `buildConfig` is left untouched, so current callers
(including `GoogleGenAiBatchChatModel`) are unaffected.
- `GoogleGenAiChatModel` and `GoogleGenAiStreamingChatModel`: add the
`generateContentConfigCustomizer(...)` builder option, and run the
assembled config through `applyCustomizer` just before the request.
- Docs: a "customizing the `GenerateContentConfig`" section in
`google-genai.md`.
- Unit tests: `applyCustomizer` is applied, can override a value set by
the builder, preserves tools, and is a no-op when `null`; plus a
chat-path test (offline, no network) proving the model actually invokes
the customizer while assembling the request.
- Integration tests (`GOOGLE_AI_GEMINI_API_KEY`-gated, like the rest of
this module): a live call on both the sync and streaming model with a
customizer set, asserting the request goes through (the customized
config reaches the transport without breaking the request).

### Testing
`mvn -pl langchain4j-google-genai test` -> 124 passing, 0 failures. The
two new ITs are key-gated and were run against `gemini-2.5-flash` (both
pass). `spotless:check` passes.

### Checklist
- [x] No breaking changes (additive; the new option defaults to no-op).
- [x] Unit tests added (apply, override, preserve tools, null no-op).
- [x] Integration tests added on both models (live call with a
customizer).
- [x] Applied to both the sync and streaming chat models.
- [x] Ran the module build and tests locally (green) + spotless.

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-17 11:11:10 +02:00
Mario Fusco 1f0efd5eac
Add crash-resilient Human-in-the-Loop suspension and resume for agentic systems (#5767)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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

This work has been inspired by the discussion in this issue on the
quarkus-langchain4j extension
https://github.com/quarkiverse/quarkus-langchain4j/issues/2638

However the task of improving the Human-in-the-Loop suspension is
something that we already discussed and that in my opinion belong mostly
to the langchain4j implementation.

This pull request:

- Adds the ability to suspend an agentic system when human input is
required, checkpoint its state to a persistent store, and resume it
later — even after a process restart or crash. Users choose the behavior
by returning either a `SuspendedResponse` (suspend and release the
thread) or a `PendingResponse` (block and wait in-process) from their
human-in-the-loop handler.
- Supports nested and parallel suspension points within the same
workflow, each independently resumable by response ID.
- Provides a `completePendingResponse(value)` convenience method on
`AgenticScope` for the common single-response case.
- Works with both the programmatic API (`HumanInTheLoop.builder()`) and
the declarative `@HumanInTheLoop` annotation.

You can find more details on how this works giving a look to the updated
documentation.

## 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
- [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
- [ ] 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: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-17 10:49:56 +02:00
Subhash Polisetti e59754e998
fix: pass builder skipCreateVectorExtension to PgVectorEmbeddingStore (#5788)
## Issue 
Closes #5784


## Change

`PgVectorEmbeddingStore.builder().skipCreateVectorExtension(true)` had
no effect. The
`PgVectorEmbeddingStoreBuilder` constructor routed through an older
constructor that passes
`skipCreateVectorExtension(null)`, so the value set on the builder never
reached the store and defaulted to `false`.
The store kept running `CREATE EXTENSION IF NOT EXISTS vector` on every
connection. The `datasourceBuilder()` path
was not affected.

Fix: build the store through `DatasourceBuilder` directly and pass
`skipCreateVectorExtension` with the other
settings. No API change.

## Tests

Added `PgVectorEmbeddingStoreBuilderTest` (offline):
`skipCreateVectorExtension(true)` on the builder now reaches
the store (fails on current `main`), and stays `false` when unset. Full
module `verify` is green (unit +
Testcontainers integration tests).

## 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
- [ ] 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-17 10:05:35 +02:00
Guillaume Laforge 55f872c3b0
chore: upgrade google-genai dependency to 1.62.0 (#5748)
Google has released a new version of their Google GenAI Java SDK to
access Gemini models.
This PR upgrades the dependency to the last version.
2026-07-17 09:41:37 +02:00
Dmytro Liubarskyi 9947e2a4c4 Map provider errors to LangChain4j exceptions in Google GenAI, Vertex AI Gemini, Vertex AI Anthropic, Workers AI and Cohere (#5785) 2026-07-17 09:39:30 +02:00
Dmytro Liubarskyi 81b9f64ca2
Map provider errors to LangChain4j exceptions in Google GenAI, Vertex AI Gemini, Vertex AI Anthropic, Workers AI and Cohere (#5785)
## Change

Several providers did not map their native errors to the specific
`dev.langchain4j.exception.*` types (`RateLimitException`,
`AuthenticationException`, `ModelNotFoundException`,
`InvalidRequestException`,
`TimeoutException`, `InternalServerException`), so retry/error-handling
logic keyed on these exception types (as already works for OpenAI,
Anthropic, Ollama, Mistral, Bedrock, watsonx.ai, etc.) silently did
  not work for them. This PR closes that gap:

- **`langchain4j-google-genai`**: added `GoogleGenAiExceptionMapper`
(maps `com.google.genai.errors.ApiException` by its HTTP status code,
same pattern as `BedrockExceptionMapper`/`WatsonxExceptionMapper`).
The module already used `withRetryMappingExceptions(...)`, but with the
default mapper, which only understands lc4j's `HttpException` — the
GenAI SDK exceptions fell through unmapped. The mapper is now passed
to all call sites (chat, streaming, embedding, image, batch models,
token count estimator), the streaming `onError` path, and the
`GoogleGenAiFiles` API calls (which previously threw raw SDK
exceptions).
- **`langchain4j-vertex-ai-gemini`**: added
`VertexAiGeminiExceptionMapper` (maps gax `ApiException` via its HTTP
status code equivalent; `DEADLINE_EXCEEDED` maps to `TimeoutException`).
Same "default mapper
cannot map SDK exceptions" issue as above. Also fixed a double-wrap: the
sync path re-wrapped the already-mapped exception in `new
RuntimeException(e)`, destroying the mapped type. The streaming path now
maps
the error before invoking `handler.onError(...)` and the listener error
context.
- **`langchain4j-vertex-ai-anthropic`**: added
`VertexAiAnthropicExceptionMapper` (gax-based, like the Gemini one, but
walks the whole cause chain because the internal client wraps gax
exceptions into
`IOException`). Replaces `new RuntimeException("Failed to generate
response", e)` in the sync path and maps both streaming error paths.
- **`langchain4j-workers-ai`**: `HttpException` from the HTTP client
passed through unmapped; all requests go through the single
`WorkersAiClient.execute(...)`, which now wraps the call in
  `ExceptionMapper.mappingException(...)`.
- **`langchain4j-cohere`**: `CohereEmbeddingModel` called
`client.embed(...)` / `v2Client.embedV2(...)` without mapping (unlike
`CohereScoringModel`, which already uses `withRetryMappingExceptions`).
Both call
sites are now wrapped in `ExceptionMapper.mappingException(...)`
(mapping only, no retry added, to preserve existing behavior).

Behavior note: exceptions thrown by these modules on provider errors
change from raw `RuntimeException`/SDK exceptions to the specific
`dev.langchain4j.exception.*` types. All of them extend
`RuntimeException`
and carry the original exception as the cause, so existing code catching
`RuntimeException` keeps working; this aligns these providers with the
behavior of the already-migrated ones.

  ## General checklist
- [X] There are no breaking changes (API, behaviour): thrown exception
types become more specific (see behavior note above), matching the
established behavior of the other providers
  - [ ] 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 (unit tests: green;
integration tests requiring provider credentials were not run)
- [ ] 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-16 14:46:08 +02:00
Dmytro Liubarskyi 920276b6d4 Vertex AI Anthropic: default request parameters 2026-07-16 14:03:13 +02:00
Mario Fusco 3e348b615c
Fix error propagation and tracing in MonitoredExecution (#5782)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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

PR #5759 changed `AgentMonitor.onAgentInvocationError` to defer moving
the `MonitoredExecution` from `ongoingExecutions` to `failedExecutions`
until all agents in `ongoingInvocations` have completed. This correctly
fixes the parallel sub-agent race condition described in the PR, but it
breaks the sequential (sequence/loop) error case.

Investigating this issue I also found a secondary bug: `rootCallEnded`
was never called on error, leaking ephemeral `AgenticScope` instances in
the registry. I added one more test case for this.

## 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
- [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
- [ ] 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
2026-07-16 12:20:51 +02:00
Mario Fusco 87d03cbcdc
Allow to dinamically configure the A2A server URL (#5766)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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 #5760

## Change

The `@A2AClientAgent` annotation previously required `a2aServerUrl` as a
compile-time string literal, making it impossible to configure the URL
per environment (dev, staging, production).

This PR adds an `@A2AServerUrlSupplier` companion annotation — a static
method that returns the URL at agent construction time — following the
same supplier pattern already used by `@McpClientSupplier` for
`@McpClientAgent`.

It also bumps the A2A client to the latest `1.1.0.Final` version.

## 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
- [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
- [ ] 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
2026-07-16 10:23:42 +02:00
吴世元 c1635f1fc3
fix: make enum tool-argument coercion locale-independent (#5778)
## Issue

Closes #5777

## Context

When resolving an enum-typed `@Tool` argument,
`DefaultToolExecutor#coerceArgument(...)` uppercases the argument as a
last resort so an LLM value like `"active"` can match the enum constant
`ACTIVE`. It used `String#toUpperCase()` without a `Locale`, making it
depend on the JVM default locale.

Under a locale such as Turkish (`tr-TR`), `"active".toUpperCase()`
yields `"ACTİVE"` (dotted capital I), so `Enum.valueOf` fails and a
valid value is rejected with `IllegalArgumentException`.

## Change

Use `Locale.ROOT` for the fallback uppercasing. This matches
`EnumOutputParser`, which already resolves enums in a locale-independent
way. The change is a one-liner and does not affect ASCII inputs in any
locale.

## Tests

Added
`DefaultToolExecutorTest#coerce_argument_to_enum_should_be_locale_independent`,
which sets the default locale to `tr-TR`, coerces `"active"` into an
enum whose constant is `ACTIVE`, and asserts the result is `ACTIVE`
(restoring the previous default locale afterwards). It fails on `main`
(throws `IllegalArgumentException`) and passes with this fix.

- [x] Unit test added covering the locale-dependent path
- [x] `./mvnw -Pspotless spotless:check` passes

---------

Signed-off-by: wushiyuanmaimob <wushiyuanwork@outlook.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-16 10:19:42 +02:00
Dmytro Liubarskyi 29878c30e3
Migrate Cohere, HuggingFace, and Workers AI from Retrofit/OkHttp to the HttpClient abstraction (#5780)
## Change
Migrates three more integrations off Retrofit/OkHttp and onto
LangChain4j's own `HttpClient` abstraction
(`dev.langchain4j.http.client`), consistent with the already-migrated
modules (OpenAI, Anthropic,
Mistral, Gemini, Voyage). Each module drops `retrofit`, `okhttp`, and
`converter-jackson`, routes HTTP through the pluggable backend (default
JDK client at runtime), and replaces its hand-rolled logging
  interceptors with the abstraction's `LoggingHttpClient`.

  **`langchain4j-cohere`**
- `CohereClient` rewritten on `HttpClient`; added `CohereJsonUtils`
mirroring the exact ObjectMapper config (`SNAKE_CASE` + `NON_NULL` +
lenient — DTOs have no `@JsonProperty`, so the wire format is
preserved). Endpoints unchanged: `POST embed` (v1 + v2), `POST rerank`;
`Authorization: Bearer` preserved.
- Deleted `CohereApi` + both interceptors. Added
`httpClientBuilder(...)` to `CohereClient`, `CohereEmbeddingModel`, and
`CohereScoringModel`.
- `proxy(...)` (public on `CohereScoringModel` + a deprecated
constructor) can't be expressed through the abstraction, so it now
**throws `UnsupportedOperationException`** pointing users to
`httpClientBuilder(...)` rather than silently ignoring the proxy. The
builder method is `@Deprecated`.

  **`langchain4j-hugging-face`**
- `DefaultHuggingFaceClient` rewritten on `HttpClient`; added
`HuggingFaceJsonUtils` (plain mapper — DTOs self-annotate with
`@JsonNaming`). The `Authorization: Bearer` header from
`ApiKeyInsertingInterceptor`
  is replicated on each request. Endpoints unchanged.
- Deleted `HuggingFaceApi` + `ApiKeyInsertingInterceptor`. The
`HuggingFaceClient` interface and the `HuggingFaceClientFactory` SPI
method are unchanged; `HuggingFaceClientFactory.Input` gains a
**`default
HttpClientBuilder httpClientBuilder()`** (backward-compatible — existing
SPI implementors, e.g. quarkus-langchain4j, inherit the `null` default
and are unaffected). Added `httpClientBuilder(...)` to the
  chat/language/embedding model builders.

  **`langchain4j-workers-ai`**
- `WorkersAiClient` rewritten on `HttpClient`; added
`WorkersAiJsonUtils`. Endpoints/paths/`Authorization` preserved. Image
generation returns raw bytes via `SuccessfulHttpResponse.bodyBytes()`
(no String
round-trip). Cloudflare's `HTTP 200` + `{"success":false}` error
envelope is still surfaced (via a restored `checkSuccess()`), alongside
the automatic `HttpException` for non-2xx.
- Deleted `WorkersAiApi`; added `httpClientBuilder(...)` to all four
model builders. Some internal `.client` plumbing was removed
(`WorkersAiClient` no-arg constructor / `createService` /
`AuthInterceptor`,
and `AbstractWorkersAIModel.processErrors`/`workerAiClient`) —
documented in `revapi.json`.

Each module got a `revapi.json` entry for the intentional
`HttpClientBuilder` exposure (and removals), matching the existing
convention in `langchain4j-chroma`, `langchain4j-open-ai`,
`langchain4j-voyage-ai`,
  etc.

**Behavioral note:** on non-2xx responses the clients now throw
`dev.langchain4j.exception.HttpException` (a `RuntimeException`
subclass) instead of a plain `RuntimeException` with a `"status code:
…"` message
  — the same behavior all other migrated modules already exhibit.

## 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
- [ ] 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-16 10:19:19 +02:00
吴世元 707f83ed9e
fix: preserve Ollama-specific parameters set via defaultRequestParameters (#5776)
## Issue

Closes #5775

## Context

`OllamaBaseChatModel#init()` rebuilds the model's default
`OllamaChatRequestParameters` by copying fields one by one, but it
omitted seven Ollama-specific fields:

`numThread`, `numKeep`, `typicalP`, `numBatch`, `numGPU`, `mainGPU`,
`useMmap`.

These fields have no shortcut setter on the model builder, so
`defaultRequestParameters(...)` is the only way to configure them as
model-level defaults. Because `init()` dropped them, the values were
silently lost and never sent to Ollama — even though
`OllamaChatRequestParameters#overrideWith(...)` and the wire mapping in
`InternalOllamaHelper#toOllamaChatRequest(...)` already handle all
seven. They therefore worked as per-request overrides but not as model
defaults.

## Change

Copy the seven missing fields in `init()`, matching the field handling
already present in `overrideWith(...)`. The change is purely additive
and does not alter behavior when the fields are unset (they remain
`null`, as today). Both `OllamaChatModel` and `OllamaStreamingChatModel`
benefit (shared `init()`).

## Tests

Added
`OllamaChatModelTest#default_request_parameters_should_preserve_all_ollama_specific_parameters`,
a pure unit test (no running Ollama server) that builds a model with all
Ollama-specific parameters set via `defaultRequestParameters(...)` and
asserts they are preserved on `model.defaultRequestParameters()`. It
fails on `main` (the seven fields are `null`) and passes with this fix.
Existing `OllamaChatRequestParametersTest` and
`InternalOllamaHelperTest` continue to pass.

- [x] Unit tests added, covering the previously-dropped fields (and
guarding the already-copied ones against regression)
- [x] `./mvnw -Pspotless spotless:check` passes

Signed-off-by: wushiyuanmaimob <wushiyuanwork@outlook.com>
2026-07-16 10:01:16 +02:00
Dmytro Liubarskyi b3da4a12c4
Migrate Nomic, Jina, OVH AI, and Judge0 from Retrofit/OkHttp to the HttpClient abstraction (#5773)
## Change

Migrates four more integrations off Retrofit/OkHttp and onto
LangChain4j's own HttpClient abstraction (dev.langchain4j.http.client),
consistent with the already-migrated modules (OpenAI, Anthropic,
Mistral,
Gemini, Voyage). This removes the retrofit, okhttp, and
converter-jackson dependencies from each module and routes HTTP through
the pluggable, backend-agnostic client (defaulting to the JDK client at
runtime).
Each module's duplicated Request/Response logging interceptors are
deleted in favor of the abstraction's built-in LoggingHttpClient.

  langchain4j-nomic
- NomicClient rewritten on HttpClient; deleted NomicApi + 2
interceptors.
- Added NomicJsonUtils carrying the exact ObjectMapper config the
Retrofit converter used (SNAKE_CASE + NON_NULL + lenient) — the DTOs
have no @JsonProperty, so this preserves the wire format byte-for-byte.
- Exposed httpClientBuilder(...) on NomicEmbeddingModel, and wired the
logger option through to the client (see note below).

  langchain4j-jina
- JinaClient rewritten (endpoints v1/embeddings, multimodal, rerank);
deleted JinaApi + 2 interceptors.
- Added JinaJsonUtils (plain mapper — the DTOs self-annotate with
@JsonNaming(SnakeCase)).
- Exposed httpClientBuilder(...) on both JinaEmbeddingModel and
JinaScoringModel (logger was already wired).

  langchain4j-ovh-ai (module is @Deprecated(forRemoval))
- Minimal transport swap only: DefaultOvhAiClient rewritten on
HttpClient, OvhAiJsonUtils added, OvhAiApi + 2 interceptors deleted. No
new public API — the deprecated surface is left frozen. Preserved the
  @JsonValue request (raw array body) and float[][] response.

  langchain4j-code-execution-engine-judge0
- Judge0JavaScriptEngine (raw OkHttp) rewritten on HttpClient, keeping
its retry loop and per-status friendly messages. Non-2xx now surfaces as
HttpException (caught → mapped to the same messages, no retry);
network/timeout errors are retried; response parsing is kept outside the
retry catch so parse errors propagate exactly as before. Class is
package-private, so no public API change.

Each affected module got a revapi.json entry for the intentional
additions/removals (HttpClientBuilder exposure; removal of the public
JinaApi/OvhAiApi interfaces), matching the existing convention in
  langchain4j-chroma, langchain4j-open-ai, langchain4j-voyage-ai, etc.

Behavioral note: on non-2xx responses the clients now throw
dev.langchain4j.exception.HttpException (a RuntimeException subclass)
instead of a plain RuntimeException with a "status code: …" message —
the same
behavior all other migrated modules already exhibit. Judge0 still
returns its friendly strings.

## 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
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)
2026-07-15 22:25:12 +02:00
Dmytro Liubarskyi e15a3f89b0
Migrate Tavily and SearchApi web search engines from Retrofit/OkHttp to the HttpClient abstraction (#5772)
## Change
Migrates the two web search engine modules off Retrofit/OkHttp and onto
LangChain4j's own HttpClient abstraction (dev.langchain4j.http.client),
consistent with the modules already migrated (OpenAI, Anthropic,
Mistral, Gemini, Voyage, etc.). This removes the retrofit, okhttp, and
converter-jackson dependencies from both modules and routes HTTP through
the pluggable, backend-agnostic client (defaulting to the JDK
  client at runtime).

  langchain4j-web-search-engine-tavily
- TavilyClient now issues a POST via HttpClient instead of a
Retrofit-generated interface; TavilyApi (Retrofit) removed.
- Added TavilyJsonUtils carrying the exact ObjectMapper configuration
the Retrofit converter used (SNAKE_CASE + NON_NULL + lenient
deserialization). This is required because the Tavily DTOs have no
@JsonProperty annotations and relied on Retrofit's snake_case mapper —
the wire format is preserved byte-for-byte.
- pom.xml: replaced retrofit/okhttp/converter-jackson with
langchain4j-http-client (compile) + langchain4j-http-client-jdk
(runtime).

  langchain4j-web-search-engine-searchapi
- SearchApiClient now issues a GET with URL-encoded query params and the
Authorization: Bearer header via HttpClient; SearchApi (Retrofit)
removed.
- Added SearchApiJsonUtils (lenient mapper; the response DTOs already
self-annotate with @JsonNaming/@JsonIgnoreProperties).
  - Same pom.xml dependency swap as Tavily.

  New (additive, non-breaking) public builder options on both engines
- httpClientBuilder(HttpClientBuilder), logRequests(Boolean),
logResponses(Boolean) — bringing these engines in line with other
integrations and enabling custom HTTP client configuration and
request/response
logging. Existing public constructors are retained and delegate to the
new wider ones, so no existing caller breaks.
- Added a revapi.json to each module ignoring
java.class.externalClassExposedInAPI for HttpClientBuilder, matching the
existing convention in langchain4j-chroma, langchain4j-open-ai,
langchain4j-voyage-ai,
  etc.

Behavioral note: on non-2xx responses the client now throws
dev.langchain4j.exception.HttpException (a RuntimeException subclass)
instead of a plain RuntimeException with a "status code: …" message —
the same
  behavior all other migrated modules already exhibit.

## 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
- [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-15 15:22:25 +02:00
gus e85142681c
Add jsonSchema() to Byte/Short/BigInteger/BigDecimal output parsers (#5690)
## Issue
No dedicated issue. This implements the optional follow-up explicitly
suggested by the author of #5465 in the merged commit message:

> "For full parity, these four types could also gain a `jsonSchema()`
(as `Integer`/`Long`/`Float`/`Double` have) to support structured
outputs. I left that out to keep this PR focused on the
parsing-consistency bug ..."

## Change

`IntegerOutputParser`, `LongOutputParser`, `FloatOutputParser` and
`DoubleOutputParser` implement `jsonSchema()`, which lets them
participate in structured outputs (`RESPONSE_FORMAT_JSON_SCHEMA`). Their
sibling numeric parsers `ByteOutputParser`, `ShortOutputParser`,
`BigIntegerOutputParser` and `BigDecimalOutputParser` did not, so they
fell back to the default `Optional.empty()`.

This PR adds `jsonSchema()` to the four remaining numeric parsers,
mirroring the existing implementations:

- `Byte`, `Short`, `BigInteger` → `"integer"` schema with an integer
`value` property (same as `Integer`/`Long`)
- `BigDecimal` → `"number"` schema with a number `value` property (same
as `Float`/`Double`)

New unit tests (`json_schema()`) were added to each of the four parser
tests, following the existing test style. Also updated
`ServiceOutputParserTest.jsonSchema()` to reflect the new support (moved
the four types from "not supported" assertions to "present" assertions).

## General checklist
- [ ] There are no breaking changes (API, behaviour) — purely additive;
the default was `Optional.empty()`, now returns a schema
- [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
and main modules — ran the full `service/output` test suite (including
`ServiceOutputParserTest`) + `spotless:check` passes
- [x] I have added/updated the documentation — not applicable (internal
parser behaviour)
- [ ] I have added an example in the examples repo — not applicable
- [ ] I have added/updated Spring Boot starter(s) — not applicable

## Verification
```
mvn -pl langchain4j test -Dtest='ByteOutputParserTest,ShortOutputParserTest,BigIntegerOutputParserTest,BigDecimalOutputParserTest,ServiceOutputParserTest'
# all tests pass
mvn -pl langchain4j spotless:check
# BUILD SUCCESS
```

---------

Co-authored-by: gus.guo <gus.guo@tec-do.com>
Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-15 10:46:19 +02:00
Mario Fusco b9e3f27200
Introduce Belief-Desire-Intention (BDI) agentic pattern (#5730)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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)
- [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](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)


## 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: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-14 11:00:31 +02:00
Mario Fusco 6e6cf9fec4
Allow to declaratively define beforeCall method on agentic patterns (#5741)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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

Agentic patterns define a `beforeCall` method that didn't have a
counterpart in the declarative API. With this pull request I also added
a new `writeStateIfAbsent` method on the `AgenticScope` and tried to
clarify a bit the documentation of the declarative API itself.

## 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
- [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
- [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)


## 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: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-14 10:10:40 +02:00
Mario Fusco 41f9a385fd
Fix race condition in AgentMonitor.onAgentInvocationError with parallel sub-agents (#5759)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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
`AgentMonitor.onAgentInvocationError` unconditionally removed the entire
`MonitoredExecution` from `ongoingExecutions` when any single agent
errored. In a `ParallelAgent` scenario, if one sub-agent failed (e.g. a
`SocketTimeoutException`) while siblings were still running, those
siblings would hit an `NPE` in `afterAgentInvocation` because the shared
execution had already been removed.

Closes #

## Change

The fix makes `onAgentInvocationError` mirror the lifecycle of
`afterAgentInvocation`: it removes only the erroring agent from the
execution's `ongoingInvocations` and defers removal of the execution
itself until all agents have completed.

## 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
- [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
- [ ] 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
2026-07-14 09:43:16 +02:00
Farzad Sedaghatbin 378b6dd52c
fix: preserve precision of long/BigInteger/BigDecimal tool arguments (#5755)
## Issue
Closes #5754

## Change

`DefaultToolExecutor.coerceArgument(...)` converted
`long`/`int`/`short`/`byte`/`BigInteger`/`BigDecimal` tool arguments by
routing them through a `double`, which silently corrupts values that
can't be represented exactly as a `double`:

- `getBoundedLongValue(...)` did `(long) ((Number)
argument).doubleValue()`.
- The `BigInteger` branch did
`BigDecimal.valueOf(getNonFractionalDoubleValue(...)).toBigInteger()`.
- The `BigDecimal` branch did `BigDecimal.valueOf(getDoubleValue(...))`.

Since tool-call argument JSON is deserialized into a `Map<String,
Object>`, a JSON integer above 2^53 arrives as a `Long` (or a
`BigInteger` above `Long.MAX_VALUE`), so this is reachable on a normal
path. Examples of the corruption:

- `long` param, arg `9007199254740993` (2^53+1) → delivered as
`9007199254740992`.
- `BigInteger` param, arg `9223372036854775817` → `9223372036854776000`.
- `BigDecimal` param, arg `1234567890.123456789` → `1234567890.1234567`.

This is the same class of bug as the already-merged #5503
(AwsDocumentConverter), but in the shared `DefaultToolExecutor` path.

The fix introduces a `getBigDecimalValue(...)` helper that converts the
argument to a `BigDecimal` preserving its exact value, and:
- `long`/`int`/`short`/`byte` go through
`BigDecimal.toBigIntegerExact()` + a `BigInteger` range check (no
`double`).
- `BigInteger` uses `toBigIntegerExact()`.
- `BigDecimal` is built from the argument's exact value / string.

Care was taken to preserve existing behavior:
- The existing \"has non-integer value\" and \"is out of range\" error
messages are kept.
- String input is trimmed before parsing, matching the whitespace
leniency of the previous `Double.parseDouble` path — this path is also
used by the numeric `*OutputParser`s (e.g. `LongOutputParser`), which
call `getBoundedLongValue` as a fallback for whitespace-padded input.

One intentional, more-correct behavior change: `BigDecimal` arguments
now preserve their exact scale — a JSON integer `2` becomes
`BigDecimal(\"2\")` instead of `BigDecimal(\"2.0\")`.
`ToolExecutorTest.should_execute_tool_with_parameters_of_type_BigDecimal`
is updated to assert the corrected per-input results (`2 + 2 = 4`, `2.0
+ 2.0 = 4.0`).

## General checklist
- [X] There are no breaking changes (API); the only behavior change is
that numeric arguments are now converted *correctly* instead of being
silently corrupted (plus BigDecimal scale is now preserved)
- [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
- [ ] 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)

## Test plan
- Added `coerce_argument_preserves_precision_of_large_long`,
`coerce_argument_preserves_precision_of_large_big_integer`,
`coerce_argument_preserves_precision_of_big_decimal`, and
`coerce_argument_rejects_fractional_value_for_integer_types` to
`DefaultToolExecutorTest`.
- Verified the three precision tests fail without the fix
(`9007199254740993` → `...992`, `9223372036854775817` → `...776000`,
`1234567890.123456789` → `...1234567`) and pass with it.
- Verified the existing comprehensive `coerce_argument` test and the
numeric `*OutputParser` tests (which share `getBoundedLongValue`) still
pass — the whitespace-tolerance fix was needed to keep them green.
- Ran the full `langchain4j` module suite (1262 tests) and
`langchain4j-core` — all green.
- Ran `./mvnw spotless:check` — clean.

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-13 11:54:18 +02:00
Eunbin Son 8cce321f0d
fix: Skills: Declare shell tool timeout_seconds as integer and route bad values through typed exception (#5752)
## Issue
Closes #5751

## Change

Two complementary changes so the run_shell_command tool handles
timeout_seconds robustly — prevention at the schema level, containment
at the parse level.

1. Schema (prevention). ShellSkills registered timeout_seconds via
addStringProperty, even though the value is semantically an integer
(ShellCommandRunner.run(..., Integer timeoutSeconds, ...), described as
"The command timeout in seconds"). Advertising it as a string invites
the LLM to return non-integer text like "30 seconds", "1.5", or "". This
changes it to addIntegerProperty, so the schema communicates the
constraint to the model. RunShellCommandToolExecutor.resolveTimeout(...)
already had a first-class Integer branch, so no executor change is
needed for the happy path.

2. Parse guard (containment). JSON-schema adherence is not guaranteed
across providers — a model can still emit a string (or free text) for an
integer property. resolveTimeout(...) parsed the value with
Integer.valueOf(timeoutSeconds.toString()) and no guard, so a bad value
produced a raw NumberFormatException that propagated out of
executeWithContext, bypassing the tool's argument-error contract that
every
other argument error in this class honors. This wraps the parse and
routes NumberFormatException through the existing throwException(...)
path, mirroring parseArguments. A malformed timeout_seconds now yields
ToolExecutionException by default (message returned to the LLM), or
ToolArgumentsException when throwToolArgumentsExceptions(true).

The string-parse fallback in resolveTimeout is intentionally kept — it
remains the containment layer for providers that don't emit a strict
integer. The two layers together mean bad timeout values become rare
  at the source and fail gracefully when they still occur.

Signature, public Java API, and the normal path (null, Integer, valid
numeric string) are unchanged. The schema type change affects only the
tool specification advertised to the LLM, in an experimental module.

Tests: two negative tests added — non-numeric timeout_seconds throws
ToolExecutionException under the default config and
ToolArgumentsException under throwToolArgumentsExceptions(true). The
existing positive
test_resolveTimeout still passes and covers both Integer (1) and String
("1") inputs, exercising the retained string fallback.


## 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](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 <!-- N/A — change is isolated to
experimental/langchain4j-experimental-skills-shell; core/main untouched
-->
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
<!-- N/A — bug fix, no doc change -->
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features) <!-- N/A -->
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-13 11:40:51 +02:00
Subhash Polisetti 3b94250b58
Add `MistralAiBatchChatModel` for the Mistral Batch API (#5750)
## Issue

Progresses #3916

## Change
Adds `MistralAiBatchChatModel`, an implementation of the core
`BatchChatModel` interface for the
[Mistral Batch API](https://docs.mistral.ai/capabilities/batch/), which
processes many chat requests
asynchronously at 50% of the standard per-token price. It follows the
existing
`GoogleAiGeminiBatchChatModel`, so both providers expose the same
`submit` / `retrieve` / `cancel` /
`list` surface.

Each `ChatRequest` is built through the same path `MistralAiChatModel`
uses (`createMistralAiRequest`),
so per-request parameters (temperature, tools, response format, etc.)
behave identically in a batch and
in a single call. Requests are submitted inline, so no separate file
upload is needed; once a job
completes, its results are downloaded from the `output_file` /
`error_file` and re-sorted back to
submission order using generated `custom_id`s. Mistral's job statuses
map onto the core `BatchState`:

| Mistral status | `BatchState` |
|---|---|
| `QUEUED` | `PENDING` |
| `RUNNING`, `CANCELLATION_REQUESTED` | `RUNNING` |
| `SUCCESS` | `SUCCEEDED` |
| `FAILED` | `FAILED` |
| `TIMEOUT_EXCEEDED` | `EXPIRED` |
| `CANCELLED` | `CANCELLED` |

The batch operations (create / retrieve / cancel / list jobs, and
downloading a result file) are added
to the existing hand-rolled `MistralAiClient`. They are non-abstract
methods that throw
`UnsupportedFeatureException` by default, so existing `MistralAiClient`
implementations keep compiling
and are unaffected. No new dependency is introduced.

Scope:
- Chat requests only.
- One model per job (a Mistral constraint): the model configured on the
batch model applies to every
  request in the batch.
- Inline submission (the documented path for batches under 10,000
requests); the file-upload submission
  path is not included.

Verified with unit tests against a mock HTTP server custom-id ordering,
success/error mapping,
`output_file` + `error_file` merge, the running-state short-circuit (no
results fetched until the job
produces them), status-code failures, and pagination and with a
key-gated integration test
(`MistralAiBatchChatModelIT`) run end-to-end against the live Batch API
(submit → poll to completion →
read results, plus cancel and list).

## 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
- [X] 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)

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-13 11:22:10 +02:00
Arvind Akula f16130b989
feat(guardrails): add PromptInjectionGuardrail (#5619)
Pattern-based InputGuardrail that detects prompt injection attempts
using OWASP LLM01 categories (instruction override, role hijacking,
jailbreaks, system prompt leakage, delimiter injection, encoded
payloads). Zero external dependencies, sub-millisecond latency. Designed
to run as the first (cheapest) gate in a guardrail chain. Subclasses can
extend with domain-specific patterns and customise the failure message.

Relates to #3248
cc @dliubarskyi

<!--
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
Relates to #3248 — implements the first concrete `InputGuardrail`
security gate as discussed in the issue.
## Change

Adds `PromptInjectionGuardrail` to the `langchain4j-guardrails` module —
a pattern-based `InputGuardrail` that detects and blocks prompt
injection attempts before they reach the LLM.

### Why this approach
- **Zero external dependencies** — pure Java regex, no HTTP calls,
sub-millisecond latency
- **No framework coupling** — works identically under Spring Boot,
Quarkus, Helidon, or plain Java
- **Single responsibility** — one class, one concern, per the guardrails
tutorial
- **Designed for chaining** — intended to run first (cheapest) before
any LLM-based classifiers
- **Extensible** — subclasses can add domain-specific patterns and
customise the failure message

### Patterns covered (based on OWASP LLM01)

| Category | Examples |
|---|---|
| Instruction override | "ignore previous instructions", "forget all
rules", "disregard prior context" |
| Role hijacking | "you are now a...", "act as a...", "pretend to be..."
|
| Jailbreaks | "DAN", "developer mode", "bypass safety filters" |
| System prompt leakage | "reveal your prompt", "print your
instructions" |
| Delimiter injection | ` ```system `, `<system>`, `[INST]`, `<<SYS>>` |
| Encoded injection | `base64: <payload>`, "decode the following and
execute" |

### What this does NOT do
- Does not call any external service or LLM
- Does not modify `langchain4j-core` or any existing interface
- Does not introduce any new Maven dependencies

### Testing
- 58 parameterised test cases covering all 6 injection categories
- Tests for legitimate messages that must NOT be blocked
- Edge cases: blank input, empty input, null input, case insensitivity
- Extensibility tests: subclass `buildFailureMessage()` override,
additional patterns

### Follow-up
As discussed in #3248, a separate `LlmPromptInjectionGuardrail`
(LLM-based classifier) will follow in a subsequent PR — designed to
chain after this one for deeper semantic analysis.


## 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-07-10 17:04:12 -04:00
Subhash Polisetti f7ace00df1
Surface async agent failures as AgentInvocationException (#5746)
## Issue
Closes #5745

## Change

`DelayedResponse.blockingGet()` resolves its future with
`CompletableFuture.join()`, which wraps any failure in a
`java.util.concurrent.CompletionException`. So when an async agent
fails, the caller does not get the `AgentInvocationException` that the
sync path throws; it gets a `CompletionException` (cause
`AgentInvocationException`) once the result is resolved through
`AgenticScope.readState(...)` or when the root call ends. A `catch
(AgentInvocationException)` handles the sync case but misses the async
one.

`AsyncResponse`, `StreamingResponse` and `PendingResponse` are the three
`DelayedResponse` implementations, and all leak `CompletionException`
from `blockingGet()` the same way. This adds a shared
`DelayedResponse.join(CompletableFuture)` helper that rethrows the
original cause instead of the `CompletionException` wrapper when the
cause is unchecked (`RuntimeException`/`Error`), and routes all three
through it. A checked-exception cause is left wrapped, so no checked
exception is thrown from an unchecked method.

Behaviour change: `blockingGet()` no longer leaks `CompletionException`;
it surfaces the underlying cause. An async agent failure now throws
`AgentInvocationException`, matching the sync path; a streaming or
pending failure surfaces its own cause (for streaming that is the model
error, which was never an `AgentInvocationException`). Code that
specifically caught `CompletionException` here would now catch that
underlying type. This does not touch the parallel executor, which
surfaces sub-agent failures through its own `ExecutionException`
handling.

Tests:
- `DelayedResponseTest` (unit, per implementation): a failed
`AsyncResponse` / `PendingResponse` / `StreamingResponse` rethrows the
original `AgentInvocationException` (same instance), a checked cause
stays wrapped in `CompletionException`, and a successful response is
returned unchanged.
- `AsyncAgentExceptionParityTest` (end-to-end through
`AgenticServices`): a failing agent throws `AgentInvocationException` on
both the sync and async paths, and the two exceptions have the same
type.

Both fail on current main (async throws `CompletionException`) and pass
with the change.

```
mvn -pl langchain4j-agentic test
```

Module suite: 78 tests, 0 failures (7 new).

## 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
- [ ] 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-10 16:22:05 +02:00
agent 22e1c046df fix ITs 2026-07-10 09:46:09 +02:00
Dmytro Liubarskyi 2101288ad7
EmbeddingModel: request/response API with per-call parameters, multimodal inputs, and observability (#5735)
## Issue
Closes #1153 — distinguish APIs for embedding queries vs. documents/keys
(adds `EmbeddingInputType.QUERY`/`DOCUMENT` as a per-call parameter,
plus opt-in `embeddingInputType(...)` on
  `EmbeddingStoreContentRetriever` / `EmbeddingStoreIngestor`).

Partially addresses #4019 — adds the multimodal image-embedding API at
the core level (`EmbeddingInput` of `Content` parts) and wires Cohere,
Voyage, Jina, Google (Gemini Embedding 2), and Bedrock Titan; does
  not implement it for `OnnxEmbeddingModel`.

Relates to #5142 — provider-specific / per-call parameters for OpenAI
embeddings (`OpenAiEmbeddingRequestParameters`: `user`,
`encodingFormat`, `customParameters`; e.g. NVIDIA NIM `input_type` via
custom
  parameters).

Relates to #4273 — observability for `EmbeddingModel` via listeners
(`EmbeddingModelListener` + request/response/error contexts, wired
across providers).

  ## Change

Introduces an `EmbeddingModel.embed(EmbeddingRequest) →
EmbeddingResponse` API, structured like `ChatModel`'s request/response
API, so embeddings can carry **per-call parameters** and **multimodal
inputs** and
participate in **observability**. Everything is additive and
`@Experimental`; the existing `embed(String)` / `embed(TextSegment)` /
`embedAll(List)` methods keep working unchanged.

  ### Core (`langchain4j-core`)
- New request/response types: `EmbeddingRequest`, `EmbeddingResponse`,
`EmbeddingResponseMetadata`, `EmbeddingRequestParameters` (+
`DefaultEmbeddingRequestParameters` and typed `EmbeddingParameter<T>`
  tokens), `EmbeddingInput`, `EmbeddingInputType`.
- New default methods on `EmbeddingModel`: `embed(EmbeddingRequest)`,
`doEmbed(...)`, `defaultRequestParameters()`, `supportedParameters()`,
`supportedContentTypes()`, `provider()`, `listeners()`.
- **Strict opt-in / fail-fast:** per-call parameters and content types
are token/type-checked; a request that uses something the model doesn't
declare is rejected with `UnsupportedFeatureException` instead of
being silently ignored. `overrideWith` preserves the provider-specific
parameters subtype (as on the chat side).
- **Multimodal:** an `EmbeddingInput` is an ordered list of `Content`
parts (text/image); models fuse them into one embedding (or
one-per-item, per provider). Modality is auto-detected — no manual flag.
- **Observability:** `EmbeddingModelListener` + request/response/error
contexts (same shape as `ChatModelListener`), fired inline from
`embed(EmbeddingRequest)`. `addListener(...)` still works.
- **RAG opt-in:** `EmbeddingStoreContentRetriever` and
`EmbeddingStoreIngestor` gain an optional `embeddingInputType(...)`
(QUERY / DOCUMENT). Default behavior is unchanged (no input type sent).
- `ModelProvider`: added `COHERE`, `VOYAGE_AI`, `JINA`, with matching
OpenTelemetry `gen_ai.provider.name` mappings (`cohere` is a well-known
OTel value; `voyage_ai` / `jina` are custom, as permitted by the
  spec).

### Providers
- **OpenAI** (dimensions, `user`/`encodingFormat`/custom params),
**Cohere** (Embed v4 multimodal + input types), **Voyage** (multimodal +
input types), **Jina** (CLIP multimodal), **Google AI Gemini** (input
types; **Gemini Embedding 2** multimodal), **Amazon Bedrock Titan**
(multimodal).
- **Google Gen AI** (`langchain4j-google-genai`): input type → SDK
`task_type`, per-call dimensions → `outputDimensionality`, `provider()`,
listeners.
- **Ollama**: text-only — `provider()` + listeners (per-call params
correctly fail fast).
- **In-process models** (ONNX / `AbstractInProcessEmbeddingModel`):
already work via the default `doEmbed→embedAll` bridge (text-only,
image/param requests fail fast); observability via `addListener(...)`.
No
code change (no builders to wire listeners into, no dedicated
`ModelProvider`).
- **Gemini Embedding 2** dropped the `task_type` parameter, so input
types are applied as prompt instructions (`task: search result | query:
…` / `title: none | text: …`) automatically; `gemini-embedding-001`
  still uses `task_type`.
- `modelName` in the response metadata reflects the API-reported model
where the provider returns one (OpenAI/Voyage/Jina), falling back to the
configured name.

  ### Tests
- `AbstractEmbeddingModelIT` — a shared IT base (like
`AbstractChatModelIT`) covering the new API, convenience methods,
listeners, and fail-fast; each provider adds a small
`common/…EmbeddingModelIT` that
parameterizes it and declares its capabilities via `supports*()`
overrides.
- Mock-based unit tests per provider for wire format / routing /
fail-fast (run in CI without keys), plus core value-type and listener
tests.

### Docs
- Embedding-model section in the RAG tutorial (request/response,
multimodal, query-vs-document opt-in), the EmbeddingModel listener
section in the Observability tutorial, the embedding contribution
guidance in
  `CONTRIBUTING.md`, and the six provider integration pages.

  ### Notes
- `EmbeddingResponseMetadata` intentionally has no `finishReason`
(embeddings have no finish reason). No real provider is affected: the
only provider that emits `STOP` (Cloudflare WorkersAI) overrides the
convenience methods directly, and every other provider always returned
`null` here.
- `revapi.json` suppressions were added where the new (non-breaking)
types are exposed in provider APIs.

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

---------

Co-authored-by: agent <agent@langchain4j.dev>
2026-07-09 22:09:34 +02:00
agent ef7ea76e3c Fix https://github.com/langchain4j/langchain4j/issues/5740 2026-07-09 10:50:23 +02:00
agent b9af540cf3 fix ITs 2026-07-09 10:13:05 +02:00
agent 4490ea08d9 fix ITs 2026-07-09 09:58:50 +02:00
代码不加冰 765b564740
fix: Paginate ListObjects in TencentCosDocumentLoader to load all obj… (#5709)
…ects

The loadDocuments() method only fetched the first page of results (max
1000 objects) from Tencent COS. This is a follow-up to #5662 which fixed
the same issue in AmazonS3DocumentLoader.

Use a while-loop with isTruncated() and listNextBatchOfObjects() to
iterate through all pages, matching the pattern already used by
AmazonS3DocumentLoader and GoogleCloudStorageDocumentLoader.

<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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] -->
- [ ] 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
- [ ] 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)
- [ ] 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
2026-07-09 09:52:22 +02:00
Mario Fusco 032065f1e6
Allow a zero-args non-AI agent to also produce an output (#5734)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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

Zero-args non-AI agents can be useful to initialize the `AgenticScope`,
but at the moment their output is ignored, so they actually has no
effect. This pull request fixes this issue.

## 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
- [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
- [ ] 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
2026-07-09 09:50:13 +02:00
Mario Fusco 196bc5dc1b
Follow up on AgentsRegistry implementation (#5719)
<!--
Thank you so much for your contribution!

Please fill in all the sections below.
Please open the PR as ready for review (not as a draft), with tests and
documentation already included.
Please note that PRs with breaking changes, or without tests and
documentation, 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" -->
Follow-up to #5653 addressing review feedback on AgentsRegistry.

## Change

  CompositeAgentsRegistry.allAgents() returns an unmodifiable map
The interface javadoc promises "an unmodifiable map of agent names to
AgentInstances" and EmptyAgentsRegistry honors this with Map.of(), but
CompositeAgentsRegistry was returning the internal HashMap directly.
Callers could
put()/clear() and silently corrupt the shared registry. Now wrapped with
Collections.unmodifiableMap().

  AgentsRegistryIT calls refresh() after classloader swap
The integration test was setting the context classloader but not calling
AgentsRegistry.refresh(), so it only worked if no prior code in the JVM
had triggered AgentsRegistry.get(). If any earlier test initialized the
holder
(e.g. AgentsRegistryTest leaving it as the empty registry), the IT would
see a stale empty registry. Added refresh() in both @BeforeAll and
@AfterAll.

  Evaluated but not changed

  Static initializer poisoning (LazyHolder.INSTANCE = discover())
The reviewer noted that if discover() throws during the static field
initializer (e.g. duplicate agent names), the holder class is
permanently poisoned with ExceptionInInitializerError, and every
subsequent get()/refresh()
throws NoClassDefFoundError. Moving discovery out of the static
initializer was considered, but the only alternatives either break
thread safety (multiple threads calling discover() concurrently on first
access) or require
explicit synchronization (double-checked locking), which adds complexity
disproportionate to the risk. The poisoning scenario only occurs with a
misconfigured SPI provider, which is a deployment-time error rather than
a
runtime race. The holder idiom's thread safety guarantee via the JVM
class loading mechanism was preserved as-is.

  Registry override via builder (.registry(myOwnRegistry))
Allowing builders to accept an explicit registry instead of relying
solely on SPI was acknowledged as a good idea but deferred to a
follow-up PR to keep this change scoped.

@RegistryAgent ignoring @V parameters / exception types / package
placement
These were flagged as minor/non-blocking in the review and are not
addressed here.


## 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
- [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
- [ ] 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
2026-07-09 09:48:39 +02:00
Benamira05 cfd90e52f3
fix: Avoid StringIndexOutOfBoundsException when cleaning unclosed code fence in SqlDatabaseContentRetriever (#5737)
## Issue
Closes #5736

## Change

`SqlDatabaseContentRetriever.clean()` strips a markdown code fence from
the generated SQL before executing it. When the response has an opening
fence (```sql```/``````) but no closing fence, `substring(start,
lastIndexOf("```"))` gets `end < start` (`lastIndexOf` matches the
opening fence's own backticks) and throws
`StringIndexOutOfBoundsException`. `clean()` runs outside `retrieve()`'s
`try/catch`, so the exception escapes the retry / `emptyList()` fallback
the method is designed around.

This extracts the shared boundary logic into a `stripCodeFence` helper:
it slices to the closing fence only when one follows the opening tag,
otherwise returns the text after the opening tag. Behaviour for
correctly closed fences is unchanged.

Same underlying bug as #5731, fixed for `HibernateContentRetriever` in
#5732 (both classes independently implement the same fence-stripping
logic; `SqlDatabaseContentRetriever` was missed in that fix).

Added `SqlDatabaseContentRetrieverTest` (the module's first unit test —
`clean()` is `protected` and pure, so no live database is needed)
covering closed fences (regression), unclosed fences for both fence
types, and plain text.

## 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

---------

Co-authored-by: Benamira05 <145583236+Benamira05@users.noreply.github.com>
2026-07-09 09:42:59 +02:00
Dmytro Liubarskyi 8f58d26d53 fix ITs 2026-07-09 09:34:29 +02:00
Eunbin Son c2aa57844e
fix: Avoid StringIndexOutOfBoundsException when cleaning unclosed code fence in HibernateContentRetriever (#5732)
## Issue
Closes #5731

## Change

`HibernateContentRetriever.clean()` strips a markdown code fence from
the generated response before executing the HQL. When the response has
an opening fence (```` ```hql ````/```` ```sql ````/```` ``` ````) but
no closing fence, `substring(start, lastIndexOf("```"))` gets `end <
start` (`lastIndexOf` matches the opening fence's own backticks) and
throws `StringIndexOutOfBoundsException`. `clean()` runs outside
`retrieve()`'s `try/catch`, so the exception escapes the retry /
`emptyList()` fallback the method is designed around.

This extracts the shared boundary logic into a `stripCodeFence` helper:
it slices to the closing fence only when one follows the opening tag,
otherwise returns the text after the opening tag. Behaviour for
correctly closed fences is unchanged.

Added `HibernateContentRetrieverTest` (the module's first unit test —
`clean()` is `protected` and pure, so no live database is needed)
covering closed fences (regression), unclosed fences for all three fence
types, and plain text.

## 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](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 <!-- change confined to the
experimental-hibernate module; core/main not exercised -->
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
<!-- wait until reviewed/approved -->
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features) <!-- N/A — small bug fix -->
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable) <!-- N/A -->

<!-- "new maven module" and "embedding store integration" checklists
omitted — not applicable to this bug fix. -->

<!-- Testing done: JDK 17, `./mvnw -pl
experimental/langchain4j-experimental-hibernate test
-Dtest=HibernateContentRetrieverTest` → 7 tests green. Spotless verified
clean. IntegrationTests (HibernateContentRetrieverIT,
Testcontainers/Postgres) not run locally. -->

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-08 09:34:44 +02:00
Johannes Edmeier 20827c067a
fix: Anthropic honors cache_control on AiMessage and ToolExecutionResultMessage (#5729)
## Issue
Closes #5727

## Change
`AnthropicMapper` already honored a `cache_control: "ephemeral"`
attribute on `UserMessage` (#4487). In an agentic tool loop,
`AiServices` resends the growing conversation tail (`AiMessage` +
`ToolExecutionResultMessage`) on every call, but neither of those
message types could be marked for caching, so every accumulated tool
result was rebilled at full price on each turn.

This extends the same `cache_control` handling to `AiMessage` and
`ToolExecutionResultMessage`, mirroring the existing `UserMessage`
behavior:
```java
AiMessage aiMessage = someAiMessage.toBuilder()
        .attributes(Map.of("cache_control", "ephemeral"))
        .build();
```
The cache marker is applied to the last content block of the message
(for `ToolExecutionResultMessage`, the `tool_result` block itself).
`AnthropicToolResultContent` and `AnthropicToolUseContent` gained
`cacheControl`-aware constructor/builder overloads so the marker is set
at construction time, consistent with how `AnthropicTextContent` already
does it for `UserMessage`.

## 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](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-07-08 09:28:57 +02:00
Ayomipo Solaja 7fe5d68761
fix: avoid IllegalArgumentException in RetryPolicy jitter when bound is 0 (#5722)
## Issue
Closes #5721

## Change

`RetryPolicy.jitterDelayMillis(int)` passed `(int) jitter` straight to
`Random.nextInt(bound)`, which requires a **strictly positive** bound.
The bound rounds down to `0` in two realistic configurations:

1. **Jitter disabled** — `jitterScale == 0` (the natural way to turn
jitter off; nothing validates against it).
2. **Small base delay** — when `rawDelayMs(retry) * jitterScale < 1`
(e.g. `delayMillis(1)` with the default `jitterScale = 0.2`).

In both cases `Random.nextInt(0)` throws `IllegalArgumentException:
bound must be positive`. Since `sleep(retry)` calls
`jitterDelayMillis(retry)` on every retry, such a policy throws on the
first retriable failure instead of retrying, masking the original error.
The default policy (`jitterScale = 0.2`, `delayMillis = 500`) is
unaffected, which is why the existing `jitter()` test didn't catch it.

**Fix:** when the jitter bound is `<= 0` there is nothing to add, so
return the base delay unchanged.

Added two unit tests (disabled jitter, and base delay too small for
jitter). Both fail on `main` with `IllegalArgumentException` and pass
with this change; the existing `jitter()` test for the default policy 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)

<!-- Note: ran the affected `langchain4j-core` unit tests
(`RetryUtilsTest`, 12/12 green) and `spotless:apply`/`check` on the
changed files. Did not run the full core+main integration suites, which
require provider credentials; this change is a self-contained internal
utility fix with no documentation or API surface impact. -->

---------

Co-authored-by: Dmytro Liubarskyi <ljubarskij@gmail.com>
2026-07-07 09:45:56 +02:00