gnodet commented on code in PR #25289:
URL: https://github.com/apache/camel/pull/25289#discussion_r3708109284


##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -633,6 +671,89 @@ private void processNonStreamingAgentic(
                 "Max tool iterations (%d) exceeded. Tools called: 
%s".formatted(maxIterations, toolCallsLog));

Review Comment:
   **Code duplication:** `discoverCamelRouteTools()`, 
`executeCamelRouteTool()`, and the result-handling logic are duplicated nearly 
verbatim between `OpenAIProducer` and `OpenAIToolExecutionProducer` (~80 lines 
each). The duplication is slightly inconsistent — this class extracts result 
handling into `handleCamelToolResult()`, while `OpenAIToolExecutionProducer` 
inlines the same logic.
   
   Consider extracting the shared code into a package-private helper class 
(similar to how `AiToolSpecToOpenAI` is already a shared utility).



##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -633,6 +671,89 @@ private void processNonStreamingAgentic(
                 "Max tool iterations (%d) exceeded. Tools called: 
%s".formatted(maxIterations, toolCallsLog));
     }
 
+    /**
+     * Executes a Camel route tool via {@link AiToolExecutor}, handling the 
exchange lifecycle and error strategies.
+     */
+    private String executeCamelRouteTool(
+            AiToolSpec spec, String argsJson, Exchange exchange, 
OpenAIConfiguration config)
+            throws Exception {
+        LOG.debug("Executing Camel route tool '{}' with args: {}", 
spec.getName(), argsJson);
+
+        Map<String, Object> argsMap;
+        try {
+            if (argsJson == null || argsJson.trim().isEmpty()) {
+                argsMap = Map.of();
+            } else {
+                argsMap = OBJECT_MAPPER.readValue(argsJson, Map.class);
+            }
+        } catch (JsonProcessingException e) {
+            if (config.getToolExecutionErrorStrategy() == 
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+                throw e;
+            }
+            LOG.warn("Invalid tool arguments for Camel route tool '{}': {}", 
spec.getName(), argsJson, e);
+            return "Error: invalid tool arguments: " + e.getMessage();
+        }
+
+        // Isolate tool execution in its own exchange copy
+        Exchange toolExchange = ExchangeHelper.createCopy(exchange, true);
+        try {
+            AiToolResult result = AiToolExecutor.execute(spec, argsMap, 
toolExchange);
+            return handleCamelToolResult(spec.getName(), result, config);
+        } catch (Exception e) {
+            if (config.getToolExecutionErrorStrategy() == 
ToolExecutionErrorStrategy.FAIL_EXCHANGE) {
+                throw e;
+            }
+            LOG.warn("Camel route tool '{}' execution failed: {}", 
spec.getName(), e.getMessage(), e);
+            return "Error: Tool execution failed: " + e.getMessage();

Review Comment:
   **Security nit:** The `AiToolResult` Javadoc warns: "Framework adapters MUST 
NOT return [ExecutionError.message()] verbatim to the LLM without 
sanitization." The `handleCamelToolResult()` method correctly returns the 
generic `"Error: Tool execution failed"` for `ExecutionError`, but this outer 
catch returns `e.getMessage()` which could include internal details. Consider 
using the same sanitized message:
   
   ```suggestion
               return "Error: Tool execution failed";
   ```
   
   The same pattern applies to `OpenAIToolExecutionProducer` at line 291.



##########
components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java:
##########
@@ -474,12 +496,17 @@ private void processNonStreamingSimple(
     }
 
     private void processNonStreamingAgentic(
-            Exchange exchange, ChatCompletionCreateParams params, 
OpenAIConfiguration config)
+            Exchange exchange, ChatCompletionCreateParams params, 
OpenAIConfiguration config,
+            Map<String, AiToolSpec> camelRouteTools)
             throws Exception {
 
         int maxIterations = config.getMaxToolIterations();
+
+        Set<String> availableToolNames = new java.util.LinkedHashSet<>();

Review Comment:
   **FQCN violation:** `new java.util.LinkedHashSet<>()` should use the simple 
class name with an import. The project convention states: "Do NOT use fully 
qualified class names in Java code." Note that `OpenAIToolExecutionProducer` in 
this same PR correctly imports `java.util.LinkedHashSet`.
   
   ```suggestion
           Set<String> availableToolNames = new LinkedHashSet<>();
   ```
   (Also add `import java.util.LinkedHashSet;` alongside the existing `import 
java.util.LinkedHashMap;`)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to