This is an automated email from the ASF dual-hosted git repository. orpiske pushed a commit to branch camel-4.10.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit a95be3262743667ca9e51fd457a459a3c515219d Author: Otavio Rodolfo Piske <angusyo...@gmail.com> AuthorDate: Tue Apr 1 16:46:32 2025 +0200 CAMEL-21923: LLM call should include all tools at the end of the flow --- .../tools/LangChain4jToolsProducer.java | 48 ++++++++++++++++------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java index 13aa4a1b42e..6a68d09b371 100644 --- a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java +++ b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java @@ -29,6 +29,7 @@ import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.data.message.ToolExecutionResultMessage; import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.output.FinishReason; import dev.langchain4j.model.output.Response; import org.apache.camel.Exchange; import org.apache.camel.InvalidPayloadException; @@ -36,8 +37,11 @@ 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; @@ -99,25 +103,45 @@ public class LangChain4jToolsProducer extends DefaultProducer { } // First talk to the model to get the tools to be called + int i = 0; do { - final Response<AiMessage> response = chatWithLLMForTools(chatMessages, toolPair, exchange); - if (!response.content().hasToolExecutionRequests()) { + LOG.debug("Starting iteration {}", i); + final Response<AiMessage> response = chatWithLLM(chatMessages, toolPair, exchange); + if (isDoneExecuting(response)) { return extractAiResponse(response); } - // Then, talk again to call the tools and compute the final response - final Response<AiMessage> toolsCallResponse = chatWithLLMForToolCalling(chatMessages, exchange, response, toolPair); - if (!toolsCallResponse.content().hasToolExecutionRequests()) { - return extractAiResponse(toolsCallResponse); + // Only invoke the tools ... the response will be computed on the next loop + invokeTools(chatMessages, exchange, response, toolPair); + LOG.debug("Finished iteration {}", i); + i++; + } while (true); + } + + private boolean isDoneExecuting(Response<AiMessage> response) { + if (!response.content().hasToolExecutionRequests()) { + LOG.info("Finished executing tools because of there are no more execution requests"); + return true; + } + + if (response.finishReason() != null) { + LOG.info("Finished executing tools because of {}", response.finishReason()); + + if (response.finishReason() == FinishReason.STOP) { + return true; } + } - } while (true); + return false; } - private Response<AiMessage> chatWithLLMForToolCalling( + private void invokeTools( List<ChatMessage> chatMessages, Exchange exchange, Response<AiMessage> response, ToolPair toolPair) { - for (ToolExecutionRequest toolExecutionRequest : response.content().toolExecutionRequests()) { + int i = 0; + List<ToolExecutionRequest> toolExecutionRequests = response.content().toolExecutionRequests(); + for (ToolExecutionRequest toolExecutionRequest : toolExecutionRequests) { String toolName = toolExecutionRequest.name(); + LOG.info("Invoking tool {} ({}) of {}", i, toolName, toolExecutionRequests.size()); final CamelToolSpecification camelToolSpecification = toolPair.callableTools().stream() .filter(c -> c.getToolSpecification().name().equals(toolName)).findFirst().get(); @@ -130,7 +154,9 @@ public class LangChain4jToolsProducer extends DefaultProducer { .forEachRemaining(name -> exchange.getMessage().setHeader(name, jsonNode.get(name))); // Execute the consumer route + camelToolSpecification.getConsumer().getProcessor().process(exchange); + i++; } catch (Exception e) { // How to handle this exception? exchange.setException(e); @@ -141,8 +167,6 @@ public class LangChain4jToolsProducer extends DefaultProducer { toolExecutionRequest.name(), exchange.getIn().getBody(String.class))); } - - return this.chatLanguageModel.generate(chatMessages); } /** @@ -153,7 +177,7 @@ public class LangChain4jToolsProducer extends DefaultProducer { * @param toolPair the toolPair containing the available tools to be called * @return the response provided by the model */ - private Response<AiMessage> chatWithLLMForTools(List<ChatMessage> chatMessages, ToolPair toolPair, Exchange exchange) { + private Response<AiMessage> chatWithLLM(List<ChatMessage> chatMessages, ToolPair toolPair, Exchange exchange) { Response<AiMessage> response = this.chatLanguageModel.generate(chatMessages, toolPair.toolSpecifications()); if (!response.content().hasToolExecutionRequests()) {