Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/
package org.apache.camel.component.langchain4j.tools;

import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import org.apache.camel.RuntimeCamelException;
Expand All @@ -36,14 +37,15 @@ public class LangChain4jToolsConfiguration implements Cloneable {

public LangChain4jToolsConfiguration() {
}

public ChatMemory getChatMemory() {
return chatMemory;
}

public void setChatMemory(ChatMemory chatMemory) {
this.chatMemory = chatMemory;
}

/**
* Chat Language Model of type dev.langchain4j.model.chat.ChatModel
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.*;
import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.data.message.AiMessage;
Expand All @@ -32,17 +33,22 @@
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.output.FinishReason;
import dev.langchain4j.model.output.Response;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.TypeConverter;
import org.apache.camel.component.langchain4j.tools.spec.CamelToolExecutorCache;
import org.apache.camel.component.langchain4j.tools.spec.CamelToolSpecification;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LangChain4jToolsProducer extends DefaultProducer {
private static final Logger LOG = LoggerFactory.getLogger(LangChain4jToolsProducer.class);

private final LangChain4jToolsEndpoint endpoint;

Expand Down Expand Up @@ -109,15 +115,15 @@ private String toolsChat(List<ChatMessage> chatMessages, Exchange exchange) {
// First talk to the model to get the tools to be called
int i = 0;
do {
// System.out.println("Starting iteration " + i);
// System.out.println("Starting iteration " + i);
final Response<AiMessage> response = chatWithLLM(chatMessages, toolPair, exchange, i);
if (isDoneExecuting(response)) {
return extractAiResponse(response);
}

// Only invoke the tools ... the response will be computed on the next loop
invokeTools(chatMessages, exchange, response, toolPair);
// System.out.println("Finished iteration " + i);
// System.out.println("Finished iteration " + i);
i++;
} while (true);
}
Expand Down Expand Up @@ -151,11 +157,47 @@ private void invokeTools(
.filter(c -> c.getToolSpecification().name().equals(toolName)).findFirst().get();

try {
TypeConverter typeConverter = endpoint.getCamelContext().getTypeConverter();

// Get declared parameters from tool specification to filter incoming fields
Set<String> declaredParams = Set.of();
JsonObjectSchema paramSchema = camelToolSpecification.getToolSpecification().parameters();
if (paramSchema != null && paramSchema.properties() != null) {
declaredParams = paramSchema.properties().keySet();
}
final Set<String> allowedParams = declaredParams;

// Map Json to Header
JsonNode jsonNode = objectMapper.readValue(toolExecutionRequest.arguments(), JsonNode.class);

jsonNode.fieldNames()
.forEachRemaining(name -> exchange.getMessage().setHeader(name, jsonNode.get(name)));
.forEachRemaining(name -> {
if (!allowedParams.contains(name)) {
LOG.warn("Skipping undeclared tool argument '{}' for tool '{}'",
name, toolName);
return;
}
final JsonNode value = jsonNode.get(name);
Object headerValue;

// Try to get values for the known tool parameter types
if (value instanceof TextNode) {
headerValue = typeConverter.convertTo(String.class, value);
} else if (value instanceof IntNode) {
headerValue = typeConverter.convertTo(Integer.class, value);
} else if (value instanceof LongNode) {
headerValue = typeConverter.convertTo(Long.class, value);
} else if (value instanceof DoubleNode) {
headerValue = typeConverter.convertTo(Double.class, value);
} else if (value instanceof BooleanNode) {
headerValue = typeConverter.convertTo(Boolean.class, value);
} else {
// Fallback to JsonNode to enable the value to be extracted elsewhere
headerValue = value;
}

exchange.getMessage().setHeader(name, headerValue);
});

// Execute the consumer route

Expand All @@ -172,7 +214,7 @@ private void invokeTools(
exchange.getIn().getBody(String.class));
if (chatMemory != null) {
chatMemory.add(toolExecutionResultMessage);
}
}
chatMessages.add(toolExecutionResultMessage);
}
}
Expand All @@ -185,14 +227,15 @@ private void invokeTools(
* @param toolPair the toolPair containing the available tools to be called
* @return the response provided by the model
*/
private Response<AiMessage> chatWithLLM(List<ChatMessage> chatMessages, ToolPair toolPair, Exchange exchange, int countNum) {
private Response<AiMessage> chatWithLLM(
List<ChatMessage> chatMessages, ToolPair toolPair, Exchange exchange, int countNum) {

if (chatMemory != null) {
boolean isEmpty = chatMemory.messages().size() == 0;
if (isEmpty) { // first round chat, need to add System and User message.
if (isEmpty) { // first round chat, need to add System and User message.
chatMessages.forEach(chatMemory::add);
}else if (countNum == 0){ // the following rounds only need to add User message.
for (ChatMessage message : chatMessages) {
} else if (countNum == 0) { // the following rounds only need to add User message.
for (ChatMessage message : chatMessages) {
if (message.type() == ChatMessageType.USER) {
chatMemory.add(message);
}
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
<cxf.tesb.version>4.1.0.2</cxf.tesb.version>
<camel-langchain4j-core.tesb.version>4.8.1.20251119</camel-langchain4j-core.tesb.version>
<camel-langchain4j-chat.tesb.version>4.8.1.20251119</camel-langchain4j-chat.tesb.version>
<camel-langchain4j-tools.tesb.version>4.8.1.20251119</camel-langchain4j-tools.tesb.version>
<camel-langchain4j-tools.tesb.version>4.8.1.20260909</camel-langchain4j-tools.tesb.version>
<camel-amqp.tesb.version>4.8.1.20260608</camel-amqp.tesb.version>
<camel-as2-api.tesb.version>4.8.1.20250320</camel-as2-api.tesb.version>
<camel-aws2-kinesis.tesb.version>4.8.1.20250320</camel-aws2-kinesis.tesb.version>
Expand Down
Loading