Quarkus LangChain4j ChatMemoryProvider silently leaks into AgentWithoutMemory when default model is configured
- Dominant language
- Java
- Stars
- 302
- Forks
- 232
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 114
Description
## Description
When Quarkus LangChain4j (QL4J) is on the classpath and a default `ChatModel` is configured via `quarkus.langchain4j.*` properties, QL4J's `ChatMemoryProvider` silently leaks into Camel's `AgentWithoutMemory`, making it stateful despite the user's explicit intent for stateless behavior.
## Root Cause
QL4J replaces `AiServices.builder()` via SPI (`AiServicesFactory`) in JVM mode and via GraalVM `@Substitute` in native mode. When Camel calls `AiServices.builder(AiAgentWithoutMemoryService.class)`:
1. QL4J's `AiServicesProcessor.determinedImpliedRegisterAiService()` discovers `AiAgentWithoutMemoryService` because it has `@UserMessage` and `@SystemMessage` annotations. The `AGENTIC_PACKAGE_PREFIX` exclusion (`dev.langchain4j.agentic.*`) does not match Camel's package (`org.apache.camel.component.langchain4j.agent.api`).
2. QL4J creates a synthetic `QuarkusAiServiceContext` CDI bean for this interface. Since no explicit `chatMemoryProviderSupplier` is set on the implied registration, it defaults to `BEAN_CHAT_MEMORY_PROVIDER_SUPPLIER` — meaning "inject `ChatMemoryProvider` from CDI."
3. `ChatMemoryProcessor` always creates a default `@ApplicationScoped` `ChatMemoryProvider` (`MessageWindowChatMemory`). This gets injected into the synthetic context.
4. `QuarkusAiServiceContextFactory.create()` returns this pre-configured context (with `ChatMemoryProvider` already set) to Camel's builder.
5. Camel's `AgentWithoutMemory.createAiAgentService()` only calls `.chatModel(config.getChatModel())` — it never resets `chatMemoryProvider`. The QL4J-injected `ChatMemoryProvider` survives into the built proxy.
## Impact
Since `AiAgentWithoutMemoryService` has no `@MemoryId` parameter, the memory ID falls back to either a request-scope hash (HTTP routes) or the literal string `"default"` (non-HTTP routes like timer, file, kafka). This causes:
- **Non-HTTP routes**: All `AgentWithoutMemory` instances across the application share a single global chat memory. Messages from unrelated conversations contaminate each other.
- **HTTP routes**: Per-request isolation, but `InMemoryChatMemoryStore` entries are never evicted (memory leak).
- **All routes**: Each call sends up to 9 prior unrelated messages to the LLM (default window size = 10), increasing token cost, latency, and producing incorrect responses.
No error or warning is logged. The agent silently behaves differently than its name and contract imply.
## Steps to Reproduce
1. Add `quarkus-langchain4j-ollama` (or another QL4J model provider) as a dependency
2. Configure a default model:
```properties
quarkus.langchain4j.ollama.base-url=http://localhost:11434
quarkus.langchain4j.ollama.chat-model.model-id=llama3.2
```
3. Create an `AgentWithoutMemory` with a Camel-managed `ChatModel`:
```java
new AgentWithoutMemory(new AgentConfiguration().withChatModel(chatModel))
```
4. Send multiple unrelated messages through the agent
5. Observe that responses reference content from previous, unrelated messages
## Expected Behavior
`AgentWithoutMemory` should be stateless — each call processed independently with no conversation history, regardless of whether QL4J is on the classpath.
## Why Current Tests Don't Catch This
The `langchain4j-agent-ql4j` integration test uses `@Identifier`-qualified `ChatModel` beans and does not configure a default QL4J model via properties. Without a default `ChatModel`, the CDI context bean creation fails and `QuarkusAiServiceContextFactory` falls back to a clean empty context. The `ChatMemoryProvider` doesn't leak — but only by accident, not by design.
## Additional Context
The `langchain4j-agent-ql4j` test README acknowledges related interference: `RetrievalAugmentor` had to be disabled (`cq-test.retrieval.augmentor.disabled=true`) because "Testing with quarkus-langchain4j does not work when the RetrievalAugmentor bean is present with scope ."
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.