This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 7ff1cb117732 CAMEL-23929: Expose langchain4j-agent tool-calling
options as @UriParam
7ff1cb117732 is described below
commit 7ff1cb117732b7cc1ef202358873f827d1fc7155
Author: Omar Atie <[email protected]>
AuthorDate: Tue Aug 25 05:35:11 2026 -0700
CAMEL-23929: Expose langchain4j-agent tool-calling options as @UriParam
Exposes the simple scalar tool-calling options from AgentConfiguration
as @UriParam fields on LangChain4jAgentConfiguration, so users can
configure them directly in the endpoint URI without creating a separate
bean: maxToolCallingRoundTrips (0 = unset), compensateOnToolErrors, and
executeToolsConcurrently. The producer merges these URI values onto the
duplicated AgentConfiguration (URI overrides bean) and restricts them to
inline agent creation mode, rejecting use with a user-provided agent
bean or agentFactory. AgentConfiguration gains
withExecuteToolsConcurrentlyEnabled(Boolean) so a URI
executeToolsConcurrently=false can disable concurrency configured on the
bean. Includes unit tests, component docs with URI/YAML examples, and
regenerated catalog and DSL metadata.
Closes #25512
Co-authored-by: Cursor <[email protected]>
---
.../catalog/components/langchain4j-agent.json | 34 ++-
.../catalog/docs/langchain4j-agent-component.adoc | 21 ++
.../langchain4j/agent/api/AgentConfiguration.java | 15 +
.../agent/api/AgentConfigurationTest.java | 5 +
.../agent/LangChain4jAgentComponentConfigurer.java | 18 ++
.../LangChain4jAgentConfigurationConfigurer.java | 18 ++
.../agent/LangChain4jAgentEndpointConfigurer.java | 18 ++
.../agent/LangChain4jAgentEndpointUriFactory.java | 5 +-
.../langchain4j/agent/langchain4j-agent.json | 34 ++-
.../src/main/docs/langchain4j-agent-component.adoc | 21 ++
.../agent/LangChain4jAgentConfiguration.java | 61 ++++
.../agent/LangChain4jAgentProducer.java | 39 +++
.../LangChain4jAgentToolCallingUriParamsTest.java | 334 +++++++++++++++++++++
.../Langchain4jAgentComponentBuilderFactory.java | 64 ++++
.../LangChain4jAgentEndpointBuilderFactory.java | 116 +++++++
15 files changed, 774 insertions(+), 29 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-agent.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-agent.json
index a9a55884f1d5..82fbe66b7ed2 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-agent.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-agent.json
@@ -27,14 +27,17 @@
"agent": { "index": 0, "kind": "property", "displayName": "Agent",
"group": "producer", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.langchain4j.agent.api.Agent",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent to use for the
component" },
"agentConfiguration": { "index": 1, "kind": "property", "displayName":
"Agent Configuration", "group": "producer", "label": "", "required": false,
"type": "object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentConfiguration",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Agent [...]
"agentFactory": { "index": 2, "kind": "property", "displayName": "Agent
Factory", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentFactory", "deprecated":
false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent factory to us
[...]
- "configuration": { "index": 3, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description": "The
configuration" },
- "jsonSchema": { "index": 4, "kind": "property", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validat [...]
- "lazyStartProducer": { "index": 5, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
- "outputClass": { "index": 6, "kind": "property", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deriv [...]
- "tags": { "index": 7, "kind": "property", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
- "autowiredEnabled": { "index": 8, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
- "mcpClients": { "index": 9, "kind": "property", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client insta [...]
- "mcpServer": { "index": 10, "kind": "property", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
+ "compensateOnToolErrors": { "index": 3, "kind": "property", "displayName":
"Compensate On Tool Errors", "group": "producer", "label": "producer",
"required": false, "type": "boolean", "javaType": "java.lang.Boolean",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether Lang [...]
+ "configuration": { "index": 4, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description": "The
configuration" },
+ "executeToolsConcurrently": { "index": 5, "kind": "property",
"displayName": "Execute Tools Concurrently", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether m [...]
+ "jsonSchema": { "index": 6, "kind": "property", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validat [...]
+ "lazyStartProducer": { "index": 7, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
+ "maxToolCallingRoundTrips": { "index": 8, "kind": "property",
"displayName": "Max Tool Calling Round Trips", "group": "producer", "label":
"producer", "required": false, "type": "integer", "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Maximum number of
tool-ca [...]
+ "outputClass": { "index": 9, "kind": "property", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deriv [...]
+ "tags": { "index": 10, "kind": "property", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
+ "autowiredEnabled": { "index": 11, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching [...]
+ "mcpClients": { "index": 12, "kind": "property", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client inst [...]
+ "mcpServer": { "index": 13, "kind": "property", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
},
"headers": {
"CamelLangChain4jAgentSystemMessage": { "index": 0, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The system prompt.", "constantName":
"org.apache.camel.component.langchain4j.agent.api.Headers#SYSTEM_MESSAGE" },
@@ -57,11 +60,14 @@
"agent": { "index": 1, "kind": "parameter", "displayName": "Agent",
"group": "producer", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.langchain4j.agent.api.Agent",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent to use for the
component" },
"agentConfiguration": { "index": 2, "kind": "parameter", "displayName":
"Agent Configuration", "group": "producer", "label": "", "required": false,
"type": "object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentConfiguration",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Agen [...]
"agentFactory": { "index": 3, "kind": "parameter", "displayName": "Agent
Factory", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentFactory", "deprecated":
false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent factory to u
[...]
- "jsonSchema": { "index": 4, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output valida [...]
- "outputClass": { "index": 5, "kind": "parameter", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deri [...]
- "tags": { "index": 6, "kind": "parameter", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
- "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produc [...]
- "mcpClients": { "index": 8, "kind": "parameter", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client inst [...]
- "mcpServer": { "index": 9, "kind": "parameter", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
+ "compensateOnToolErrors": { "index": 4, "kind": "parameter",
"displayName": "Compensate On Tool Errors", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether Lan [...]
+ "executeToolsConcurrently": { "index": 5, "kind": "parameter",
"displayName": "Execute Tools Concurrently", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether [...]
+ "jsonSchema": { "index": 6, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output valida [...]
+ "maxToolCallingRoundTrips": { "index": 7, "kind": "parameter",
"displayName": "Max Tool Calling Round Trips", "group": "producer", "label":
"producer", "required": false, "type": "integer", "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool-c
[...]
+ "outputClass": { "index": 8, "kind": "parameter", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deri [...]
+ "tags": { "index": 9, "kind": "parameter", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
+ "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
+ "mcpClients": { "index": 11, "kind": "parameter", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client ins [...]
+ "mcpServer": { "index": 12, "kind": "parameter", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP serve [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/langchain4j-agent-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/langchain4j-agent-component.adoc
index 4afe1aa5b07b..542581d694ff 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/langchain4j-agent-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/langchain4j-agent-component.adoc
@@ -178,6 +178,27 @@ AgentConfiguration configuration = new AgentConfiguration()
Tool-route header side-effects are not merged back onto the main exchange when
tools run concurrently; only the tool result text is returned to the LLM.
+The same tool-calling options can also be set directly on the endpoint URI
when using inline agent creation mode (`agentConfiguration` without `agent` or
`agentFactory`). URI values override the same options on the
`AgentConfiguration` bean:
+
+[source,yaml]
+----
+- to:
+ uri: "langchain4j-agent:assistant"
+ parameters:
+ agentConfiguration: "#myConfig"
+ tags: "orders"
+ maxToolCallingRoundTrips: 5
+ compensateOnToolErrors: true
+ executeToolsConcurrently: true
+----
+
+[source,java]
+----
+from("direct:start")
+ .to("langchain4j-agent:assistant?agentConfiguration=#myConfig&tags=orders"
+ +
"&maxToolCallingRoundTrips=5&compensateOnToolErrors=true&executeToolsConcurrently=true");
+----
+
==== Creating an Agent without Memory
._Java-only: LangChain4j `AgentConfiguration` and programmatic bean
registration_
diff --git
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
index 7e02ae1fa94a..d7f300c7f2bd 100644
---
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
+++
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
@@ -514,6 +514,21 @@ public class AgentConfiguration {
return this;
}
+ /**
+ * Sets whether concurrent tool execution is enabled.
+ *
+ * @param executeToolsConcurrently {@code true} to enable, {@code false}
to disable, or {@code null} to leave unset
+ * @return this configuration instance for method
chaining
+ * @since 4.23
+ */
+ public AgentConfiguration withExecuteToolsConcurrentlyEnabled(Boolean
executeToolsConcurrently) {
+ this.executeToolsConcurrently = executeToolsConcurrently;
+ if (!Boolean.TRUE.equals(executeToolsConcurrently)) {
+ this.executeToolsExecutor = null;
+ }
+ return this;
+ }
+
/**
* Creates a shallow copy of this configuration. Used by the
langchain4j-agent producer to attach a managed tool
* executor without mutating registry-held configuration beans.
diff --git
a/components/camel-ai/camel-langchain4j-agent-api/src/test/java/org/apache/camel/component/langchain4j/agent/api/AgentConfigurationTest.java
b/components/camel-ai/camel-langchain4j-agent-api/src/test/java/org/apache/camel/component/langchain4j/agent/api/AgentConfigurationTest.java
index 5f9c45e10e69..d75633725beb 100644
---
a/components/camel-ai/camel-langchain4j-agent-api/src/test/java/org/apache/camel/component/langchain4j/agent/api/AgentConfigurationTest.java
+++
b/components/camel-ai/camel-langchain4j-agent-api/src/test/java/org/apache/camel/component/langchain4j/agent/api/AgentConfigurationTest.java
@@ -433,6 +433,11 @@ class AgentConfigurationTest {
} finally {
((ExecutorService) executor).shutdownNow();
}
+
+ AgentConfiguration disabled =
config.withExecuteToolsConcurrentlyEnabled(false);
+ assertThat(disabled).isSameAs(config);
+ assertThat(config.getExecuteToolsConcurrently()).isFalse();
+ assertThat(config.getExecuteToolsExecutor()).isNull();
}
@Test
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentComponentConfigurer.java
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentComponentConfigurer.java
index 3cee6170962e..0e85d03f7494 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentComponentConfigurer.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentComponentConfigurer.java
@@ -37,11 +37,17 @@ public class LangChain4jAgentComponentConfigurer extends
PropertyConfigurerSuppo
case "agentFactory":
getOrCreateConfiguration(target).setAgentFactory(property(camelContext,
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class, value));
return true;
case "autowiredenabled":
case "autowiredEnabled":
target.setAutowiredEnabled(property(camelContext, boolean.class, value));
return true;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors":
getOrCreateConfiguration(target).setCompensateOnToolErrors(property(camelContext,
java.lang.Boolean.class, value)); return true;
case "configuration": target.setConfiguration(property(camelContext,
org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration.class,
value)); return true;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently":
getOrCreateConfiguration(target).setExecuteToolsConcurrently(property(camelContext,
java.lang.Boolean.class, value)); return true;
case "jsonschema":
case "jsonSchema":
getOrCreateConfiguration(target).setJsonSchema(property(camelContext,
java.lang.String.class, value)); return true;
case "lazystartproducer":
case "lazyStartProducer":
target.setLazyStartProducer(property(camelContext, boolean.class, value));
return true;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips":
getOrCreateConfiguration(target).setMaxToolCallingRoundTrips(property(camelContext,
int.class, value)); return true;
case "mcpclients":
case "mcpClients":
getOrCreateConfiguration(target).setMcpClients(property(camelContext,
java.util.List.class, value)); return true;
case "mcpserver":
@@ -68,11 +74,17 @@ public class LangChain4jAgentComponentConfigurer extends
PropertyConfigurerSuppo
case "agentFactory": return
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class;
case "autowiredenabled":
case "autowiredEnabled": return boolean.class;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return java.lang.Boolean.class;
case "configuration": return
org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration.class;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return java.lang.Boolean.class;
case "jsonschema":
case "jsonSchema": return java.lang.String.class;
case "lazystartproducer":
case "lazyStartProducer": return boolean.class;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return int.class;
case "mcpclients":
case "mcpClients": return java.util.List.class;
case "mcpserver":
@@ -95,11 +107,17 @@ public class LangChain4jAgentComponentConfigurer extends
PropertyConfigurerSuppo
case "agentFactory": return
getOrCreateConfiguration(target).getAgentFactory();
case "autowiredenabled":
case "autowiredEnabled": return target.isAutowiredEnabled();
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return
getOrCreateConfiguration(target).getCompensateOnToolErrors();
case "configuration": return target.getConfiguration();
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return
getOrCreateConfiguration(target).getExecuteToolsConcurrently();
case "jsonschema":
case "jsonSchema": return
getOrCreateConfiguration(target).getJsonSchema();
case "lazystartproducer":
case "lazyStartProducer": return target.isLazyStartProducer();
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return
getOrCreateConfiguration(target).getMaxToolCallingRoundTrips();
case "mcpclients":
case "mcpClients": return
getOrCreateConfiguration(target).getMcpClients();
case "mcpserver":
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfigurationConfigurer.java
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfigurationConfigurer.java
index 240f9fdd9fe1..fd067b85c129 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfigurationConfigurer.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfigurationConfigurer.java
@@ -28,8 +28,14 @@ public class LangChain4jAgentConfigurationConfigurer extends
org.apache.camel.su
case "agentConfiguration":
target.setAgentConfiguration(property(camelContext,
org.apache.camel.component.langchain4j.agent.api.AgentConfiguration.class,
value)); return true;
case "agentfactory":
case "agentFactory": target.setAgentFactory(property(camelContext,
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class, value));
return true;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors":
target.setCompensateOnToolErrors(property(camelContext,
java.lang.Boolean.class, value)); return true;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently":
target.setExecuteToolsConcurrently(property(camelContext,
java.lang.Boolean.class, value)); return true;
case "jsonschema":
case "jsonSchema": target.setJsonSchema(property(camelContext,
java.lang.String.class, value)); return true;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips":
target.setMaxToolCallingRoundTrips(property(camelContext, int.class, value));
return true;
case "mcpclients":
case "mcpClients": target.setMcpClients(property(camelContext,
java.util.List.class, value)); return true;
case "mcpserver":
@@ -49,8 +55,14 @@ public class LangChain4jAgentConfigurationConfigurer extends
org.apache.camel.su
case "agentConfiguration": return
org.apache.camel.component.langchain4j.agent.api.AgentConfiguration.class;
case "agentfactory":
case "agentFactory": return
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return java.lang.Boolean.class;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return java.lang.Boolean.class;
case "jsonschema":
case "jsonSchema": return java.lang.String.class;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return int.class;
case "mcpclients":
case "mcpClients": return java.util.List.class;
case "mcpserver":
@@ -71,8 +83,14 @@ public class LangChain4jAgentConfigurationConfigurer extends
org.apache.camel.su
case "agentConfiguration": return target.getAgentConfiguration();
case "agentfactory":
case "agentFactory": return target.getAgentFactory();
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return
target.getCompensateOnToolErrors();
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return
target.getExecuteToolsConcurrently();
case "jsonschema":
case "jsonSchema": return target.getJsonSchema();
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return
target.getMaxToolCallingRoundTrips();
case "mcpclients":
case "mcpClients": return target.getMcpClients();
case "mcpserver":
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointConfigurer.java
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointConfigurer.java
index 00a641d41cea..549a5b3a016b 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointConfigurer.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointConfigurer.java
@@ -28,10 +28,16 @@ public class LangChain4jAgentEndpointConfigurer extends
PropertyConfigurerSuppor
case "agentConfiguration":
target.getConfiguration().setAgentConfiguration(property(camelContext,
org.apache.camel.component.langchain4j.agent.api.AgentConfiguration.class,
value)); return true;
case "agentfactory":
case "agentFactory":
target.getConfiguration().setAgentFactory(property(camelContext,
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class, value));
return true;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors":
target.getConfiguration().setCompensateOnToolErrors(property(camelContext,
java.lang.Boolean.class, value)); return true;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently":
target.getConfiguration().setExecuteToolsConcurrently(property(camelContext,
java.lang.Boolean.class, value)); return true;
case "jsonschema":
case "jsonSchema":
target.getConfiguration().setJsonSchema(property(camelContext,
java.lang.String.class, value)); return true;
case "lazystartproducer":
case "lazyStartProducer":
target.setLazyStartProducer(property(camelContext, boolean.class, value));
return true;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips":
target.getConfiguration().setMaxToolCallingRoundTrips(property(camelContext,
int.class, value)); return true;
case "mcpclients":
case "mcpClients":
target.getConfiguration().setMcpClients(property(camelContext,
java.util.List.class, value)); return true;
case "mcpserver":
@@ -56,10 +62,16 @@ public class LangChain4jAgentEndpointConfigurer extends
PropertyConfigurerSuppor
case "agentConfiguration": return
org.apache.camel.component.langchain4j.agent.api.AgentConfiguration.class;
case "agentfactory":
case "agentFactory": return
org.apache.camel.component.langchain4j.agent.api.AgentFactory.class;
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return java.lang.Boolean.class;
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return java.lang.Boolean.class;
case "jsonschema":
case "jsonSchema": return java.lang.String.class;
case "lazystartproducer":
case "lazyStartProducer": return boolean.class;
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return int.class;
case "mcpclients":
case "mcpClients": return java.util.List.class;
case "mcpserver":
@@ -80,10 +92,16 @@ public class LangChain4jAgentEndpointConfigurer extends
PropertyConfigurerSuppor
case "agentConfiguration": return
target.getConfiguration().getAgentConfiguration();
case "agentfactory":
case "agentFactory": return
target.getConfiguration().getAgentFactory();
+ case "compensateontoolerrors":
+ case "compensateOnToolErrors": return
target.getConfiguration().getCompensateOnToolErrors();
+ case "executetoolsconcurrently":
+ case "executeToolsConcurrently": return
target.getConfiguration().getExecuteToolsConcurrently();
case "jsonschema":
case "jsonSchema": return target.getConfiguration().getJsonSchema();
case "lazystartproducer":
case "lazyStartProducer": return target.isLazyStartProducer();
+ case "maxtoolcallingroundtrips":
+ case "maxToolCallingRoundTrips": return
target.getConfiguration().getMaxToolCallingRoundTrips();
case "mcpclients":
case "mcpClients": return target.getConfiguration().getMcpClients();
case "mcpserver":
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointUriFactory.java
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointUriFactory.java
index d145280ea180..03e90ee3a02f 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointUriFactory.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentEndpointUriFactory.java
@@ -24,13 +24,16 @@ public class LangChain4jAgentEndpointUriFactory extends
org.apache.camel.support
private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(10);
+ Set<String> props = new HashSet<>(13);
props.add("agent");
props.add("agentConfiguration");
props.add("agentFactory");
props.add("agentId");
+ props.add("compensateOnToolErrors");
+ props.add("executeToolsConcurrently");
props.add("jsonSchema");
props.add("lazyStartProducer");
+ props.add("maxToolCallingRoundTrips");
props.add("mcpClients");
props.add("mcpServer");
props.add("outputClass");
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/agent/langchain4j-agent.json
b/components/camel-ai/camel-langchain4j-agent/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/agent/langchain4j-agent.json
index a9a55884f1d5..82fbe66b7ed2 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/agent/langchain4j-agent.json
+++
b/components/camel-ai/camel-langchain4j-agent/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/agent/langchain4j-agent.json
@@ -27,14 +27,17 @@
"agent": { "index": 0, "kind": "property", "displayName": "Agent",
"group": "producer", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.langchain4j.agent.api.Agent",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent to use for the
component" },
"agentConfiguration": { "index": 1, "kind": "property", "displayName":
"Agent Configuration", "group": "producer", "label": "", "required": false,
"type": "object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentConfiguration",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Agent [...]
"agentFactory": { "index": 2, "kind": "property", "displayName": "Agent
Factory", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentFactory", "deprecated":
false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent factory to us
[...]
- "configuration": { "index": 3, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description": "The
configuration" },
- "jsonSchema": { "index": 4, "kind": "property", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validat [...]
- "lazyStartProducer": { "index": 5, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
- "outputClass": { "index": 6, "kind": "property", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deriv [...]
- "tags": { "index": 7, "kind": "property", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
- "autowiredEnabled": { "index": 8, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
- "mcpClients": { "index": 9, "kind": "property", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client insta [...]
- "mcpServer": { "index": 10, "kind": "property", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
+ "compensateOnToolErrors": { "index": 3, "kind": "property", "displayName":
"Compensate On Tool Errors", "group": "producer", "label": "producer",
"required": false, "type": "boolean", "javaType": "java.lang.Boolean",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether Lang [...]
+ "configuration": { "index": 4, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description": "The
configuration" },
+ "executeToolsConcurrently": { "index": 5, "kind": "property",
"displayName": "Execute Tools Concurrently", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether m [...]
+ "jsonSchema": { "index": 6, "kind": "property", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output validat [...]
+ "lazyStartProducer": { "index": 7, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
+ "maxToolCallingRoundTrips": { "index": 8, "kind": "property",
"displayName": "Max Tool Calling Round Trips", "group": "producer", "label":
"producer", "required": false, "type": "integer", "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Maximum number of
tool-ca [...]
+ "outputClass": { "index": 9, "kind": "property", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deriv [...]
+ "tags": { "index": 10, "kind": "property", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
+ "autowiredEnabled": { "index": 11, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching [...]
+ "mcpClients": { "index": 12, "kind": "property", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client inst [...]
+ "mcpServer": { "index": 13, "kind": "property", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
},
"headers": {
"CamelLangChain4jAgentSystemMessage": { "index": 0, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The system prompt.", "constantName":
"org.apache.camel.component.langchain4j.agent.api.Headers#SYSTEM_MESSAGE" },
@@ -57,11 +60,14 @@
"agent": { "index": 1, "kind": "parameter", "displayName": "Agent",
"group": "producer", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.langchain4j.agent.api.Agent",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent to use for the
component" },
"agentConfiguration": { "index": 2, "kind": "parameter", "displayName":
"Agent Configuration", "group": "producer", "label": "", "required": false,
"type": "object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentConfiguration",
"deprecated": false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Agen [...]
"agentFactory": { "index": 3, "kind": "parameter", "displayName": "Agent
Factory", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.langchain4j.agent.api.AgentFactory", "deprecated":
false, "deprecationNote": "", "autowired": true, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "The agent factory to u
[...]
- "jsonSchema": { "index": 4, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output valida [...]
- "outputClass": { "index": 5, "kind": "parameter", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deri [...]
- "tags": { "index": 6, "kind": "parameter", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
- "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produc [...]
- "mcpClients": { "index": 8, "kind": "parameter", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client inst [...]
- "mcpServer": { "index": 9, "kind": "parameter", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP server [...]
+ "compensateOnToolErrors": { "index": 4, "kind": "parameter",
"displayName": "Compensate On Tool Errors", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether Lan [...]
+ "executeToolsConcurrently": { "index": 5, "kind": "parameter",
"displayName": "Execute Tools Concurrently", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Whether [...]
+ "jsonSchema": { "index": 6, "kind": "parameter", "displayName": "Json
Schema", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "JSON schema for
structured output valida [...]
+ "maxToolCallingRoundTrips": { "index": 7, "kind": "parameter",
"displayName": "Max Tool Calling Round Trips", "group": "producer", "label":
"producer", "required": false, "type": "integer", "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": 0, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Maximum number of tool-c
[...]
+ "outputClass": { "index": 8, "kind": "parameter", "displayName": "Output
Class", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.Class<java.lang.Object>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Java class to use for
structured output. Camel deri [...]
+ "tags": { "index": 9, "kind": "parameter", "displayName": "Tags", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Tags for discovering and
calling Camel route tools" },
+ "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
+ "mcpClients": { "index": 11, "kind": "parameter", "displayName": "Mcp
Clients", "group": "advanced", "label": "advanced", "required": false, "type":
"array", "javaType": "java.util.List<dev.langchain4j.mcp.client.McpClient>",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "Pre-built MCP (Model
Context Protocol) client ins [...]
+ "mcpServer": { "index": 12, "kind": "parameter", "displayName": "Mcp
Server", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>",
"prefix": "mcpServer.", "multiValue": true, "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration",
"configurationField": "configuration", "description": "MCP serve [...]
}
}
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
b/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
index 4afe1aa5b07b..542581d694ff 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
+++
b/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
@@ -178,6 +178,27 @@ AgentConfiguration configuration = new AgentConfiguration()
Tool-route header side-effects are not merged back onto the main exchange when
tools run concurrently; only the tool result text is returned to the LLM.
+The same tool-calling options can also be set directly on the endpoint URI
when using inline agent creation mode (`agentConfiguration` without `agent` or
`agentFactory`). URI values override the same options on the
`AgentConfiguration` bean:
+
+[source,yaml]
+----
+- to:
+ uri: "langchain4j-agent:assistant"
+ parameters:
+ agentConfiguration: "#myConfig"
+ tags: "orders"
+ maxToolCallingRoundTrips: 5
+ compensateOnToolErrors: true
+ executeToolsConcurrently: true
+----
+
+[source,java]
+----
+from("direct:start")
+ .to("langchain4j-agent:assistant?agentConfiguration=#myConfig&tags=orders"
+ +
"&maxToolCallingRoundTrips=5&compensateOnToolErrors=true&executeToolsConcurrently=true");
+----
+
==== Creating an Agent without Memory
._Java-only: LangChain4j `AgentConfiguration` and programmatic bean
registration_
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfiguration.java
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfiguration.java
index a8f0b0101239..33da6202993f 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfiguration.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConfiguration.java
@@ -80,6 +80,30 @@ public class LangChain4jAgentConfiguration implements
Cloneable {
prefix = "mcpServer.", multiValue = true, label = "advanced")
private Map<String, Object> mcpServer;
+ @UriParam
+ @Metadata(label = "producer", defaultValue = "0",
+ description = "Maximum number of tool-calling round trips
allowed per request."
+ + " Each round trip is one LLM call plus execution
of the tools requested in that call."
+ + " Set to 0 to leave unset and use the
LangChain4j default."
+ + " Only supported in inline agent creation mode
(agentConfiguration without agent or agentFactory)."
+ + " URI value overrides the same option on the
agentConfiguration bean.")
+ private int maxToolCallingRoundTrips;
+
+ @UriParam
+ @Metadata(label = "producer",
+ description = "Whether LangChain4j should compensate when a tool
execution fails."
+ + " Only supported in inline agent creation mode
(agentConfiguration without agent or agentFactory)."
+ + " URI value overrides the same option on the
agentConfiguration bean.")
+ private Boolean compensateOnToolErrors;
+
+ @UriParam
+ @Metadata(label = "producer",
+ description = "Whether multiple tools requested in a single LLM
turn are executed concurrently."
+ + " Camel route tools run on isolated exchange
copies."
+ + " Only supported in inline agent creation mode
(agentConfiguration without agent or agentFactory)."
+ + " URI value overrides the same option on the
agentConfiguration bean.")
+ private Boolean executeToolsConcurrently;
+
public LangChain4jAgentConfiguration() {
}
@@ -202,4 +226,41 @@ public class LangChain4jAgentConfiguration implements
Cloneable {
public void setOutputClass(Class<?> outputClass) {
this.outputClass = outputClass;
}
+
+ /**
+ * Maximum number of tool-calling round trips allowed per request.
+ */
+ public int getMaxToolCallingRoundTrips() {
+ return maxToolCallingRoundTrips;
+ }
+
+ public void setMaxToolCallingRoundTrips(int maxToolCallingRoundTrips) {
+ if (maxToolCallingRoundTrips < 0) {
+ throw new IllegalArgumentException(
+ "maxToolCallingRoundTrips must be zero (unset) or
positive, but was: " + maxToolCallingRoundTrips);
+ }
+ this.maxToolCallingRoundTrips = maxToolCallingRoundTrips;
+ }
+
+ /**
+ * Whether LangChain4j should compensate when a tool execution fails.
+ */
+ public Boolean getCompensateOnToolErrors() {
+ return compensateOnToolErrors;
+ }
+
+ public void setCompensateOnToolErrors(Boolean compensateOnToolErrors) {
+ this.compensateOnToolErrors = compensateOnToolErrors;
+ }
+
+ /**
+ * Whether multiple tools requested in a single LLM turn are executed
concurrently.
+ */
+ public Boolean getExecuteToolsConcurrently() {
+ return executeToolsConcurrently;
+ }
+
+ public void setExecuteToolsConcurrently(Boolean executeToolsConcurrently) {
+ this.executeToolsConcurrently = executeToolsConcurrently;
+ }
}
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
index 989996a504ff..a021f007c1bd 100644
---
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
+++
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
@@ -120,10 +120,30 @@ public class LangChain4jAgentProducer extends
DefaultProducer {
}
}
+ if (hasToolCallingEndpointOptions(endpoint.getConfiguration())) {
+ if (endpoint.getConfiguration().getAgentConfiguration() == null) {
+ throw new IllegalArgumentException(
+ "maxToolCallingRoundTrips, compensateOnToolErrors, and
executeToolsConcurrently require "
+ + "agentConfiguration to be
set (inline agent creation mode). "
+ + "They cannot be used with
a user-provided agent bean or agentFactory.");
+ }
+ if (endpoint.getConfiguration().getAgent() != null) {
+ throw new IllegalArgumentException(
+ "Tool-calling endpoint options cannot be combined with
a user-provided agent bean. "
+ + "They only work in inline
agent creation mode (agentConfiguration without agent or agentFactory).");
+ }
+ if (endpoint.getConfiguration().getAgentFactory() != null) {
+ throw new IllegalArgumentException(
+ "Tool-calling endpoint options cannot be combined with
agentFactory. "
+ + "They only work in inline
agent creation mode (agentConfiguration without agent or agentFactory).");
+ }
+ }
+
if (endpoint.getConfiguration().getAgent() != null) {
agent = endpoint.getConfiguration().getAgent();
} else if (endpoint.getConfiguration().getAgentConfiguration() !=
null) {
AgentConfiguration agentConfiguration =
endpoint.getConfiguration().getAgentConfiguration().duplicate();
+ applyEndpointToolCallingOptions(agentConfiguration,
endpoint.getConfiguration());
resolveExecuteToolsConcurrentlyExecutor(agentConfiguration);
agent = agentConfiguration.getChatMemoryProvider() != null
? new AgentWithMemory(agentConfiguration)
@@ -226,6 +246,25 @@ public class LangChain4jAgentProducer extends
DefaultProducer {
LOG.debug("Registered Camel-managed executor for concurrent
LangChain4j tool execution");
}
+ private static boolean
hasToolCallingEndpointOptions(LangChain4jAgentConfiguration configuration) {
+ return configuration.getMaxToolCallingRoundTrips() > 0
+ || configuration.getCompensateOnToolErrors() != null
+ || configuration.getExecuteToolsConcurrently() != null;
+ }
+
+ private static void applyEndpointToolCallingOptions(
+ AgentConfiguration agentConfiguration,
LangChain4jAgentConfiguration endpointConfiguration) {
+ if (endpointConfiguration.getMaxToolCallingRoundTrips() > 0) {
+
agentConfiguration.withMaxToolCallingRoundTrips(endpointConfiguration.getMaxToolCallingRoundTrips());
+ }
+ if (endpointConfiguration.getCompensateOnToolErrors() != null) {
+
agentConfiguration.withCompensateOnToolErrors(endpointConfiguration.getCompensateOnToolErrors());
+ }
+ if (endpointConfiguration.getExecuteToolsConcurrently() != null) {
+
agentConfiguration.withExecuteToolsConcurrentlyEnabled(endpointConfiguration.getExecuteToolsConcurrently());
+ }
+ }
+
/**
* Creates a composed tool provider that aggregates tools from all
configured sources: Camel route tools (via tags)
* and MCP tools (via endpoint-level mcpClients and mcpServers
configuration).
diff --git
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentToolCallingUriParamsTest.java
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentToolCallingUriParamsTest.java
new file mode 100644
index 000000000000..3d6c7fb0bab3
--- /dev/null
+++
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentToolCallingUriParamsTest.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.langchain4j.agent;
+
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import dev.langchain4j.agent.tool.ToolExecutionRequest;
+import dev.langchain4j.data.message.AiMessage;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.chat.request.ChatRequest;
+import dev.langchain4j.model.chat.response.ChatResponse;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.langchain4j.agent.api.Agent;
+import org.apache.camel.component.langchain4j.agent.api.AgentConfiguration;
+import org.apache.camel.component.langchain4j.agent.api.AgentFactory;
+import org.apache.camel.component.langchain4j.agent.api.AiAgentBody;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Tests for CAMEL-23929: tool-calling options exposed as {@code @UriParam} on
the langchain4j-agent endpoint.
+ */
+class LangChain4jAgentToolCallingUriParamsTest extends CamelTestSupport {
+
+ private static final String TAG = "uri-tool-calling";
+
+ private final TwoToolRoundTripChatModel twoToolChatModel = new
TwoToolRoundTripChatModel();
+ private final SingleToolRoundTripChatModel singleToolChatModel = new
SingleToolRoundTripChatModel();
+ private final AlwaysToolChatModel alwaysToolChatModel = new
AlwaysToolChatModel();
+ private final FailingToolRoundTripChatModel failingToolChatModel = new
FailingToolRoundTripChatModel();
+ private final AtomicInteger chatRound = new AtomicInteger();
+ private final AtomicInteger inFlight = new AtomicInteger();
+ private final AtomicInteger maxConcurrent = new AtomicInteger();
+ private CyclicBarrier overlapBarrier = new CyclicBarrier(2);
+
+ @BeforeEach
+ void resetState() {
+ chatRound.set(0);
+ maxConcurrent.set(0);
+ inFlight.set(0);
+ overlapBarrier = new CyclicBarrier(2);
+ failingToolChatModel.reset();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:concurrent-uri")
+
.to("langchain4j-agent:test?agentConfiguration=#concurrentConfig&tags=" + TAG
+ + "&executeToolsConcurrently=true");
+
+ from("direct:disable-concurrent-uri")
+
.to("langchain4j-agent:test?agentConfiguration=#singleToolConfig&tags=" + TAG
+ + "&executeToolsConcurrently=false");
+
+ from("direct:limited-uri")
+
.to("langchain4j-agent:test?agentConfiguration=#singleToolConfig&tags=" + TAG
+ + "&maxToolCallingRoundTrips=2");
+
+ from("direct:override-uri")
+
.to("langchain4j-agent:test?agentConfiguration=#highLimitConfig&tags=" + TAG
+ + "&maxToolCallingRoundTrips=1");
+
+ from("direct:compensate-uri")
+
.to("langchain4j-agent:test?agentConfiguration=#failingToolConfig&tags=" + TAG
+ +
"&compensateOnToolErrors=true&maxToolCallingRoundTrips=2");
+
+ from("ai-tool:countTool?tags=" + TAG + "&description=Counting
tool")
+ .setBody(constant("ok"));
+
+ from("ai-tool:slowToolA?tags=" + TAG + "&description=Slow tool
A")
+ .process(exchange -> recordConcurrentEntry("A",
exchange));
+
+ from("ai-tool:slowToolB?tags=" + TAG + "&description=Slow tool
B")
+ .process(exchange -> recordConcurrentEntry("B",
exchange));
+
+ from("ai-tool:failingTool?tags=" + TAG + "&description=A tool
that always fails")
+ .throwException(new RuntimeException("Simulated tool
failure"));
+ }
+ };
+ }
+
+ @Override
+ protected void bindToRegistry(Registry registry) {
+ registry.bind("agentConfig", new AgentConfiguration()
+ .withChatModel(twoToolChatModel)
+ .withExecuteToolsConcurrently()
+ .withMaxToolCallingRoundTrips(5));
+ registry.bind("concurrentConfig", new AgentConfiguration()
+ .withChatModel(twoToolChatModel)
+ .withMaxToolCallingRoundTrips(5));
+ registry.bind("singleToolConfig", new AgentConfiguration()
+ .withChatModel(singleToolChatModel)
+ .withExecuteToolsConcurrently()
+ .withMaxToolCallingRoundTrips(5));
+ registry.bind("highLimitConfig", new AgentConfiguration()
+ .withChatModel(alwaysToolChatModel)
+ .withMaxToolCallingRoundTrips(10));
+ registry.bind("failingToolConfig", new AgentConfiguration()
+ .withChatModel(failingToolChatModel)
+ .withMaxToolCallingRoundTrips(5));
+ }
+
+ @Test
+ void executeToolsConcurrentlyCanBeEnabledViaEndpointUri() {
+ String response = template.requestBody("direct:concurrent-uri", new
AiAgentBody<>("run tools"), String.class);
+
+ assertThat(response).isEqualTo("done");
+ assertThat(maxConcurrent.get()).isGreaterThanOrEqualTo(2);
+ }
+
+ @Test
+ void executeToolsConcurrentlyFalseViaEndpointUriOverridesBean() {
+ String response =
template.requestBody("direct:disable-concurrent-uri", new AiAgentBody<>("run
tools"), String.class);
+
+ assertThat(response).isEqualTo("done");
+ }
+
+ @Test
+ void maxToolCallingRoundTripsViaEndpointUriIsApplied() {
+ String response = template.requestBody("direct:limited-uri", new
AiAgentBody<>("run tools"), String.class);
+
+ assertThat(response).isEqualTo("done");
+ }
+
+ @Test
+ void endpointUriOverridesAgentConfigurationToolCallingLimit() {
+ assertThatThrownBy(() -> template.requestBody("direct:override-uri",
new AiAgentBody<>("run tools"), String.class))
+ .rootCause()
+ .hasMessageContaining("exceeded 1 tool calling round trips");
+ }
+
+ @Test
+ void negativeMaxToolCallingRoundTripsIsRejected() {
+ assertThatThrownBy(() -> new
LangChain4jAgentConfiguration().setMaxToolCallingRoundTrips(-1))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("maxToolCallingRoundTrips");
+ }
+
+ @Test
+ void toolCallingUriParamsRequireAgentConfiguration() throws Exception {
+ try (DefaultCamelContext ctx = new DefaultCamelContext()) {
+ ctx.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:x").to("langchain4j-agent:test?maxToolCallingRoundTrips=3");
+ }
+ });
+
+ assertThatThrownBy(ctx::start)
+ .isInstanceOf(Exception.class)
+ .hasMessageContaining("agentConfiguration");
+ }
+ }
+
+ @Test
+ void compensateOnToolErrorsCanBeEnabledViaEndpointUri() {
+ String response = template.requestBody("direct:compensate-uri", new
AiAgentBody<>("run tools"), String.class);
+
+ assertThat(response).isEqualTo("recovered");
+ assertThat(failingToolChatModel.getRoundCount()).isEqualTo(2);
+ }
+
+ @Test
+ void toolCallingUriParamsCannotBeCombinedWithAgentFactory() throws
Exception {
+ try (DefaultCamelContext ctx = new DefaultCamelContext()) {
+ ctx.getRegistry().bind("agentConfig", new
AgentConfiguration().withChatModel(twoToolChatModel));
+ ctx.getRegistry().bind("myFactory", new StubAgentFactory());
+ ctx.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:x").to(
+
"langchain4j-agent:test?agentConfiguration=#agentConfig&agentFactory=#myFactory&maxToolCallingRoundTrips=3");
+ }
+ });
+
+ assertThatThrownBy(ctx::start)
+ .isInstanceOf(Exception.class)
+ .hasMessageContaining("agentFactory");
+ }
+ }
+
+ @Test
+ void toolCallingUriParamsCannotBeCombinedWithAgentBean() throws Exception {
+ try (DefaultCamelContext ctx = new DefaultCamelContext()) {
+ ctx.getRegistry()
+ .bind("myAgent",
+
(org.apache.camel.component.langchain4j.agent.api.Agent) (
+ body,
+ toolProvider) ->
dev.langchain4j.service.Result.<String> builder().content("ok").build());
+ ctx.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:x").to("langchain4j-agent:test?agent=#myAgent&maxToolCallingRoundTrips=3");
+ }
+ });
+
+ assertThatThrownBy(ctx::start)
+ .isInstanceOf(Exception.class)
+ .hasMessageContaining("agentConfiguration");
+ }
+ }
+
+ private void recordConcurrentEntry(String label, Exchange exchange) {
+ int concurrent = inFlight.incrementAndGet();
+ maxConcurrent.updateAndGet(current -> Math.max(current, concurrent));
+ try {
+ overlapBarrier.await(10, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ inFlight.decrementAndGet();
+ exchange.getMessage().setBody(label);
+ }
+
+ private final class SingleToolRoundTripChatModel implements ChatModel {
+ @Override
+ public ChatResponse doChat(ChatRequest request) {
+ if (chatRound.getAndIncrement() == 0) {
+ ToolExecutionRequest toolRequest =
ToolExecutionRequest.builder()
+ .id("t1")
+ .name("countTool")
+ .arguments("{}")
+ .build();
+ return ChatResponse.builder()
+
.aiMessage(AiMessage.builder().toolExecutionRequests(List.of(toolRequest)).build())
+ .build();
+ }
+ return
ChatResponse.builder().aiMessage(AiMessage.from("done")).build();
+ }
+ }
+
+ private final class TwoToolRoundTripChatModel implements ChatModel {
+ @Override
+ public ChatResponse doChat(ChatRequest request) {
+ if (chatRound.getAndIncrement() == 0) {
+ List<ToolExecutionRequest> requests = List.of(
+
ToolExecutionRequest.builder().id("a").name("slowToolA").arguments("{}").build(),
+
ToolExecutionRequest.builder().id("b").name("slowToolB").arguments("{}").build());
+ return ChatResponse.builder()
+
.aiMessage(AiMessage.builder().toolExecutionRequests(requests).build())
+ .build();
+ }
+ return
ChatResponse.builder().aiMessage(AiMessage.from("done")).build();
+ }
+ }
+
+ private final class AlwaysToolChatModel implements ChatModel {
+ @Override
+ public ChatResponse doChat(ChatRequest request) {
+ ToolExecutionRequest toolRequest = ToolExecutionRequest.builder()
+ .id("t1")
+ .name("countTool")
+ .arguments("{}")
+ .build();
+ return ChatResponse.builder()
+
.aiMessage(AiMessage.builder().toolExecutionRequests(List.of(toolRequest)).build())
+ .build();
+ }
+ }
+
+ private final class FailingToolRoundTripChatModel implements ChatModel {
+ private final AtomicInteger round = new AtomicInteger();
+
+ @Override
+ public ChatResponse doChat(ChatRequest request) {
+ if (round.getAndIncrement() == 0) {
+ ToolExecutionRequest toolRequest =
ToolExecutionRequest.builder()
+ .id("fail1")
+ .name("failingTool")
+ .arguments("{}")
+ .build();
+ return ChatResponse.builder()
+
.aiMessage(AiMessage.builder().toolExecutionRequests(List.of(toolRequest)).build())
+ .build();
+ }
+ return
ChatResponse.builder().aiMessage(AiMessage.from("recovered")).build();
+ }
+
+ int getRoundCount() {
+ return round.get();
+ }
+
+ void reset() {
+ round.set(0);
+ }
+ }
+
+ private static final class StubAgentFactory implements AgentFactory {
+ private CamelContext camelContext;
+
+ @Override
+ public Agent createAgent(Exchange exchange) {
+ return (body, toolProvider) ->
dev.langchain4j.service.Result.<String> builder().content("ok").build();
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+ }
+}
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Langchain4jAgentComponentBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Langchain4jAgentComponentBuilderFactory.java
index 5b02e643e350..9d5dbca350d3 100644
---
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Langchain4jAgentComponentBuilderFactory.java
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Langchain4jAgentComponentBuilderFactory.java
@@ -101,6 +101,26 @@ public interface Langchain4jAgentComponentBuilderFactory {
return this;
}
+
+ /**
+ * Whether LangChain4j should compensate when a tool execution fails.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option is a: <code>java.lang.Boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param compensateOnToolErrors the value to set
+ * @return the dsl builder
+ */
+ default Langchain4jAgentComponentBuilder
compensateOnToolErrors(java.lang.Boolean compensateOnToolErrors) {
+ doSetProperty("compensateOnToolErrors", compensateOnToolErrors);
+ return this;
+ }
+
/**
* The configuration.
*
@@ -117,6 +137,27 @@ public interface Langchain4jAgentComponentBuilderFactory {
return this;
}
+
+ /**
+ * Whether multiple tools requested in a single LLM turn are executed
+ * concurrently. Camel route tools run on isolated exchange copies.
Only
+ * supported in inline agent creation mode (agentConfiguration without
+ * agent or agentFactory). URI value overrides the same option on the
+ * agentConfiguration bean.
+ *
+ * The option is a: <code>java.lang.Boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param executeToolsConcurrently the value to set
+ * @return the dsl builder
+ */
+ default Langchain4jAgentComponentBuilder
executeToolsConcurrently(java.lang.Boolean executeToolsConcurrently) {
+ doSetProperty("executeToolsConcurrently",
executeToolsConcurrently);
+ return this;
+ }
+
/**
* JSON schema for structured output validation. Only supported in
* inline agent creation mode: agentConfiguration must be set and
@@ -163,6 +204,26 @@ public interface Langchain4jAgentComponentBuilderFactory {
return this;
}
+ /**
+ * Maximum number of tool-calling round trips allowed per request. Each
+ * round trip is one LLM call plus execution of the tools requested in
+ * that call. Set to 0 to leave unset and use the LangChain4j default.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option is a: <code>int</code> type.
+ *
+ * Group: producer
+ *
+ * @param maxToolCallingRoundTrips the value to set
+ * @return the dsl builder
+ */
+ default Langchain4jAgentComponentBuilder maxToolCallingRoundTrips(int
maxToolCallingRoundTrips) {
+ doSetProperty("maxToolCallingRoundTrips",
maxToolCallingRoundTrips);
+ return this;
+ }
+
/**
* Java class to use for structured output. Camel derives the JSON
* schema from the class and instructs the model to produce matching
@@ -285,9 +346,12 @@ public interface Langchain4jAgentComponentBuilderFactory {
case "agent": getOrCreateConfiguration((LangChain4jAgentComponent)
component).setAgent((org.apache.camel.component.langchain4j.agent.api.Agent)
value); return true;
case "agentConfiguration":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setAgentConfiguration((org.apache.camel.component.langchain4j.agent.api.AgentConfiguration)
value); return true;
case "agentFactory":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setAgentFactory((org.apache.camel.component.langchain4j.agent.api.AgentFactory)
value); return true;
+ case "compensateOnToolErrors":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setCompensateOnToolErrors((java.lang.Boolean) value); return true;
case "configuration": ((LangChain4jAgentComponent)
component).setConfiguration((org.apache.camel.component.langchain4j.agent.LangChain4jAgentConfiguration)
value); return true;
+ case "executeToolsConcurrently":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setExecuteToolsConcurrently((java.lang.Boolean) value); return true;
case "jsonSchema":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setJsonSchema((java.lang.String) value); return true;
case "lazyStartProducer": ((LangChain4jAgentComponent)
component).setLazyStartProducer((boolean) value); return true;
+ case "maxToolCallingRoundTrips":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setMaxToolCallingRoundTrips((int) value); return true;
case "outputClass":
getOrCreateConfiguration((LangChain4jAgentComponent)
component).setOutputClass((java.lang.Class) value); return true;
case "tags": getOrCreateConfiguration((LangChain4jAgentComponent)
component).setTags((java.lang.String) value); return true;
case "autowiredEnabled": ((LangChain4jAgentComponent)
component).setAutowiredEnabled((boolean) value); return true;
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/LangChain4jAgentEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/LangChain4jAgentEndpointBuilderFactory.java
index a0e6ad818223..9acf041cfb2f 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/LangChain4jAgentEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/LangChain4jAgentEndpointBuilderFactory.java
@@ -142,6 +142,82 @@ public interface LangChain4jAgentEndpointBuilderFactory {
doSetProperty("agentFactory", agentFactory);
return this;
}
+ /**
+ * Whether LangChain4j should compensate when a tool execution fails.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option is a: <code>java.lang.Boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param compensateOnToolErrors the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder compensateOnToolErrors(Boolean
compensateOnToolErrors) {
+ doSetProperty("compensateOnToolErrors", compensateOnToolErrors);
+ return this;
+ }
+ /**
+ * Whether LangChain4j should compensate when a tool execution fails.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option will be converted to a <code>java.lang.Boolean</code>
+ * type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param compensateOnToolErrors the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder compensateOnToolErrors(String
compensateOnToolErrors) {
+ doSetProperty("compensateOnToolErrors", compensateOnToolErrors);
+ return this;
+ }
+ /**
+ * Whether multiple tools requested in a single LLM turn are executed
+ * concurrently. Camel route tools run on isolated exchange copies.
Only
+ * supported in inline agent creation mode (agentConfiguration without
+ * agent or agentFactory). URI value overrides the same option on the
+ * agentConfiguration bean.
+ *
+ * The option is a: <code>java.lang.Boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param executeToolsConcurrently the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder
executeToolsConcurrently(Boolean executeToolsConcurrently) {
+ doSetProperty("executeToolsConcurrently",
executeToolsConcurrently);
+ return this;
+ }
+ /**
+ * Whether multiple tools requested in a single LLM turn are executed
+ * concurrently. Camel route tools run on isolated exchange copies.
Only
+ * supported in inline agent creation mode (agentConfiguration without
+ * agent or agentFactory). URI value overrides the same option on the
+ * agentConfiguration bean.
+ *
+ * The option will be converted to a <code>java.lang.Boolean</code>
+ * type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param executeToolsConcurrently the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder
executeToolsConcurrently(String executeToolsConcurrently) {
+ doSetProperty("executeToolsConcurrently",
executeToolsConcurrently);
+ return this;
+ }
/**
* JSON schema for structured output validation. Only supported in
* inline agent creation mode: agentConfiguration must be set and
@@ -162,6 +238,46 @@ public interface LangChain4jAgentEndpointBuilderFactory {
doSetProperty("jsonSchema", jsonSchema);
return this;
}
+ /**
+ * Maximum number of tool-calling round trips allowed per request. Each
+ * round trip is one LLM call plus execution of the tools requested in
+ * that call. Set to 0 to leave unset and use the LangChain4j default.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option is a: <code>int</code> type.
+ *
+ * Default: 0
+ * Group: producer
+ *
+ * @param maxToolCallingRoundTrips the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder maxToolCallingRoundTrips(int
maxToolCallingRoundTrips) {
+ doSetProperty("maxToolCallingRoundTrips",
maxToolCallingRoundTrips);
+ return this;
+ }
+ /**
+ * Maximum number of tool-calling round trips allowed per request. Each
+ * round trip is one LLM call plus execution of the tools requested in
+ * that call. Set to 0 to leave unset and use the LangChain4j default.
+ * Only supported in inline agent creation mode (agentConfiguration
+ * without agent or agentFactory). URI value overrides the same option
+ * on the agentConfiguration bean.
+ *
+ * The option will be converted to a <code>int</code> type.
+ *
+ * Default: 0
+ * Group: producer
+ *
+ * @param maxToolCallingRoundTrips the value to set
+ * @return the dsl builder
+ */
+ default LangChain4jAgentEndpointBuilder
maxToolCallingRoundTrips(String maxToolCallingRoundTrips) {
+ doSetProperty("maxToolCallingRoundTrips",
maxToolCallingRoundTrips);
+ return this;
+ }
/**
* Java class to use for structured output. Camel derives the JSON
* schema from the class and instructs the model to produce matching