This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new ee081b517b Fixes #9237. Add test coverage for OpenAI route-based tools 
via ai-tool
ee081b517b is described below

commit ee081b517bddb6b6e4ce50bfd7894e502143b8fa
Author: James Netherton <[email protected]>
AuthorDate: Fri Sep 25 15:58:19 2026 +0100

    Fixes #9237. Add test coverage for OpenAI route-based tools via ai-tool
    
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 integration-tests/openai/pom.xml                   | 17 ++++++++++
 .../component/openai/it/OpenaiResource.java        | 20 ++++++++++++
 .../quarkus/component/openai/it/OpenaiRoutes.java  | 23 ++++++++++++++
 .../component/openai/it/OpenaiChatTest.java        | 30 ++++++++++++++++++
 ...irect-d4e5f6a7-0000-0000-0000-000000000003.json | 34 ++++++++++++++++++++
 ...step1-d4e5f6a7-0000-0000-0000-000000000001.json | 37 ++++++++++++++++++++++
 ...step2-d4e5f6a7-0000-0000-0000-000000000002.json | 31 ++++++++++++++++++
 7 files changed, 192 insertions(+)

diff --git a/integration-tests/openai/pom.xml b/integration-tests/openai/pom.xml
index 6bbf67069d..05c2fee8b9 100644
--- a/integration-tests/openai/pom.xml
+++ b/integration-tests/openai/pom.xml
@@ -31,6 +31,10 @@
     <description>Integration tests for Camel Quarkus OpenAI 
extension</description>
 
     <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-ai-tool</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-direct</artifactId>
@@ -120,6 +124,19 @@
             </activation>
             <dependencies>
                 <!-- The following dependencies guarantee that this module is 
built after them. You can update them by running `mvn process-resources 
-Pformat -N` from the source tree root directory -->
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-ai-tool-deployment</artifactId>
+                    <version>${project.version}</version>
+                    <type>pom</type>
+                    <scope>test</scope>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>*</groupId>
+                            <artifactId>*</artifactId>
+                        </exclusion>
+                    </exclusions>
+                </dependency>
                 <dependency>
                     <groupId>org.apache.camel.quarkus</groupId>
                     <artifactId>camel-quarkus-direct-deployment</artifactId>
diff --git 
a/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiResource.java
 
b/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiResource.java
index f1a7409323..4cbe16b732 100644
--- 
a/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiResource.java
+++ 
b/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiResource.java
@@ -123,6 +123,26 @@ public class OpenaiResource {
         return producerTemplate.requestBody("direct:chatAgentic", 
chatMessageContent, String.class);
     }
 
+    @Path("/chat/routeTools")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String, Object> chatRouteTools(@QueryParam("returnDirect") 
boolean returnDirect, String chatMessageContent) {
+        String endpointUri = returnDirect ? 
"direct:chatRouteToolsReturnDirect" : "direct:chatRouteTools";
+        Exchange exchange = producerTemplate.request(endpointUri,
+                e -> e.getMessage().setBody(chatMessageContent));
+        if (exchange.getException() != null) {
+            throw new RuntimeException(exchange.getException());
+        }
+
+        Message message = exchange.getMessage();
+        Map<String, Object> result = new HashMap<>();
+        result.put("body", message.getBody(String.class));
+        result.put("toolCalls", 
message.getHeader(OpenAIConstants.MCP_TOOL_CALLS, List.class));
+        result.put("returnDirect", 
message.getHeader(OpenAIConstants.MCP_RETURN_DIRECT, false, Boolean.class));
+        return result;
+    }
+
     @Path("/audio/transcription")
     @POST
     @Consumes(MediaType.TEXT_PLAIN)
diff --git 
a/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiRoutes.java
 
b/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiRoutes.java
index 26672ddce0..dab8f1e0f4 100644
--- 
a/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiRoutes.java
+++ 
b/integration-tests/openai/src/main/java/org/apache/camel/quarkus/component/openai/it/OpenaiRoutes.java
@@ -96,6 +96,29 @@ public class OpenaiRoutes extends RouteBuilder {
                         + "&maxToolIterations=3")
                 .log("Agentic response: ${body}");
 
+        from("direct:chatRouteTools")
+                
.to("openai:chat-completion?autoToolExecution=true&tags=weather&maxToolIterations=3")
+                .log("Route tools response: ${body}");
+
+        from("direct:chatRouteToolsReturnDirect")
+                
.to("openai:chat-completion?autoToolExecution=true&tags=orders&maxToolIterations=3")
+                .log("Route tools return direct response: ${body}");
+
+        from("ai-tool:get_weather?tags=weather"
+                + "&description=Get the current weather for a city"
+                + "&parameter.city=string"
+                + "&parameter.city.description=The city name"
+                + "&parameter.city.required=true")
+                .setBody(simple("Sunny, 22 degrees celsius in 
${header.city}"));
+
+        from("ai-tool:get_order_status?tags=orders"
+                + "&description=Get the status of an order"
+                + "&parameter.orderId=string"
+                + "&parameter.orderId.description=The order identifier"
+                + "&parameter.orderId.required=true"
+                + "&returnDirect=true")
+                .setBody(simple("Order ${header.orderId} has been shipped"));
+
         from("direct:audioTranscription")
                 .to("openai:audio-transcription?audioModel=whisper-1")
                 .log("Audio transcription response: ${body}");
diff --git 
a/integration-tests/openai/src/test/java/org/apache/camel/quarkus/component/openai/it/OpenaiChatTest.java
 
b/integration-tests/openai/src/test/java/org/apache/camel/quarkus/component/openai/it/OpenaiChatTest.java
index 22eb6b71a5..ab7905036c 100644
--- 
a/integration-tests/openai/src/test/java/org/apache/camel/quarkus/component/openai/it/OpenaiChatTest.java
+++ 
b/integration-tests/openai/src/test/java/org/apache/camel/quarkus/component/openai/it/OpenaiChatTest.java
@@ -43,7 +43,9 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInfo;
 
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsStringIgnoringCase;
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -257,4 +259,32 @@ class OpenaiChatTest {
                         containsStringIgnoringCase("sum"));
     }
 
+    @Test
+    void chatWithRouteTools() {
+        RestAssured.given()
+                .contentType(ContentType.TEXT)
+                .body("What is the weather in London? Use the get_weather 
tool.")
+                .post("/openai/chat/routeTools")
+                .then()
+                .statusCode(200)
+                .body(
+                        "body", containsStringIgnoringCase("sunny"),
+                        "toolCalls", contains("get_weather"),
+                        "returnDirect", equalTo(false));
+    }
+
+    @Test
+    void chatWithRouteToolsReturnDirect() {
+        RestAssured.given()
+                .queryParam("returnDirect", true)
+                .contentType(ContentType.TEXT)
+                .body("What is the status of order 12345? Use the 
get_order_status tool.")
+                .post("/openai/chat/routeTools")
+                .then()
+                .statusCode(200)
+                .body(
+                        "body", equalTo("Order 12345 has been shipped"),
+                        "toolCalls", contains("get_order_status"),
+                        "returnDirect", equalTo(true));
+    }
 }
diff --git 
a/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-return-direct-d4e5f6a7-0000-0000-0000-000000000003.json
 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-return-direct-d4e5f6a7-0000-0000-0000-000000000003.json
new file mode 100644
index 0000000000..17c1fb3a57
--- /dev/null
+++ 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-return-direct-d4e5f6a7-0000-0000-0000-000000000003.json
@@ -0,0 +1,34 @@
+{
+  "id" : "d4e5f6a7-0000-0000-0000-000000000003",
+  "name" : "chat_completions_route_tools_return_direct",
+  "request" : {
+    "url" : "/chat/completions",
+    "method" : "POST",
+    "bodyPatterns" : [
+      {
+        "equalToJson" : "{\"messages\":[{\"content\":\"What is the status of 
order 12345? Use the get_order_status 
tool.\",\"role\":\"user\"}],\"model\":\"gpt-5\"}",
+        "ignoreArrayOrder" : true,
+        "ignoreExtraElements" : true
+      },
+      {
+        "matchesJsonPath" : "$.tools[?(@.function.name == 'get_order_status')]"
+      },
+      {
+        "matchesJsonPath" : {
+          "expression" : "$.tools[?(@.function.name == 'get_weather')]",
+          "absent" : true
+        }
+      }
+    ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : "{\n  \"id\": \"chatcmpl-route-tools-3\",\n  \"object\": 
\"chat.completion\",\n  \"created\": 1770382702,\n  \"model\": \"gpt-5\",\n  
\"choices\": [\n    {\n      \"index\": 0,\n      \"message\": {\n        
\"role\": \"assistant\",\n        \"content\": null,\n        \"tool_calls\": 
[\n          {\n            \"id\": \"call_route-tools-3\",\n            
\"type\": \"function\",\n            \"function\": {\n              \"name\": 
\"get_order_status\",\n              \"arg [...]
+    "headers" : {
+      "Content-Type" : "application/json"
+    }
+  },
+  "uuid" : "d4e5f6a7-0000-0000-0000-000000000003",
+  "persistent" : true,
+  "insertionIndex" : 302
+}
diff --git 
a/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step1-d4e5f6a7-0000-0000-0000-000000000001.json
 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step1-d4e5f6a7-0000-0000-0000-000000000001.json
new file mode 100644
index 0000000000..193697b549
--- /dev/null
+++ 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step1-d4e5f6a7-0000-0000-0000-000000000001.json
@@ -0,0 +1,37 @@
+{
+  "id" : "d4e5f6a7-0000-0000-0000-000000000001",
+  "name" : "chat_completions_route_tools_step1",
+  "scenarioName" : "route-tools-weather",
+  "requiredScenarioState" : "Started",
+  "newScenarioState" : "tool-executed",
+  "request" : {
+    "url" : "/chat/completions",
+    "method" : "POST",
+    "bodyPatterns" : [
+      {
+        "equalToJson" : "{\"messages\":[{\"content\":\"What is the weather in 
London? Use the get_weather tool.\",\"role\":\"user\"}],\"model\":\"gpt-5\"}",
+        "ignoreArrayOrder" : true,
+        "ignoreExtraElements" : true
+      },
+      {
+        "matchesJsonPath" : "$.tools[?(@.function.name == 'get_weather')]"
+      },
+      {
+        "matchesJsonPath" : {
+          "expression" : "$.tools[?(@.function.name == 'get_order_status')]",
+          "absent" : true
+        }
+      }
+    ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : "{\n  \"id\": \"chatcmpl-route-tools-1\",\n  \"object\": 
\"chat.completion\",\n  \"created\": 1770382700,\n  \"model\": \"gpt-5\",\n  
\"choices\": [\n    {\n      \"index\": 0,\n      \"message\": {\n        
\"role\": \"assistant\",\n        \"content\": null,\n        \"tool_calls\": 
[\n          {\n            \"id\": \"call_route-tools-1\",\n            
\"type\": \"function\",\n            \"function\": {\n              \"name\": 
\"get_weather\",\n              \"argument [...]
+    "headers" : {
+      "Content-Type" : "application/json"
+    }
+  },
+  "uuid" : "d4e5f6a7-0000-0000-0000-000000000001",
+  "persistent" : true,
+  "insertionIndex" : 300
+}
diff --git 
a/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step2-d4e5f6a7-0000-0000-0000-000000000002.json
 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step2-d4e5f6a7-0000-0000-0000-000000000002.json
new file mode 100644
index 0000000000..12dc416977
--- /dev/null
+++ 
b/integration-tests/openai/src/test/resources/mappings/chat_completions-route-tools-step2-d4e5f6a7-0000-0000-0000-000000000002.json
@@ -0,0 +1,31 @@
+{
+  "id" : "d4e5f6a7-0000-0000-0000-000000000002",
+  "name" : "chat_completions_route_tools_step2",
+  "scenarioName" : "route-tools-weather",
+  "requiredScenarioState" : "tool-executed",
+  "newScenarioState" : "Started",
+  "request" : {
+    "url" : "/chat/completions",
+    "method" : "POST",
+    "bodyPatterns" : [
+      {
+        "equalToJson" : "{\"messages\":[{\"content\":\"What is the weather in 
London? Use the get_weather tool.\",\"role\":\"user\"}],\"model\":\"gpt-5\"}",
+        "ignoreArrayOrder" : true,
+        "ignoreExtraElements" : true
+      },
+      {
+        "matchesJsonPath" : "$.messages[?(@.content == 'Sunny, 22 degrees 
celsius in London')]"
+      }
+    ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : "{\n  \"id\": \"chatcmpl-route-tools-2\",\n  \"object\": 
\"chat.completion\",\n  \"created\": 1770382701,\n  \"model\": \"gpt-5\",\n  
\"choices\": [\n    {\n      \"index\": 0,\n      \"message\": {\n        
\"role\": \"assistant\",\n        \"content\": \"The weather in London is sunny 
with a temperature of 22 degrees celsius.\",\n        \"refusal\": null\n      
},\n      \"finish_reason\": \"stop\"\n    }\n  ],\n  \"usage\": {\n    
\"prompt_tokens\": 60,\n    \"completion [...]
+    "headers" : {
+      "Content-Type" : "application/json"
+    }
+  },
+  "uuid" : "d4e5f6a7-0000-0000-0000-000000000002",
+  "persistent" : true,
+  "insertionIndex" : 301
+}

Reply via email to