From dfcfe1c521be0106aec4cf67c3b9e5d0a4048ec9 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Wed, 8 Jul 2026 10:47:13 +0200 Subject: [PATCH 1/2] chore: [SpringAI] Improve e2e test stability --- .../sap/ai/sdk/app/services/OpenAiService.java | 7 +++++-- .../app/services/OpenAiServiceDeprecated.java | 11 +++++++---- .../sap/ai/sdk/app/services/WeatherMethod.java | 16 +++++----------- .../sdk/app/controllers/SpringAiOpenAiTest.java | 4 ++-- .../controllers/SpringAiOrchestrationTest.java | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java index 1b84e56f6..726cea766 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java @@ -120,11 +120,14 @@ public OpenAiChatCompletionResponse chatCompletionToolExecution( final var messages = new ArrayList(); messages.add(OpenAiMessage.user("What's the weather in %s in %s?".formatted(location, unit))); + record WeatherRequest(String location, WeatherMethod.Unit unit) {} + // 1. Define the function final List tools = List.of( - OpenAiTool.forFunction(WeatherMethod::getCurrentWeather) - .withArgument(WeatherMethod.Request.class) + OpenAiTool.forFunction( + (WeatherRequest r) -> WeatherMethod.getCurrentWeather(r.location(), r.unit())) + .withArgument(WeatherRequest.class) .withName("weather") .withDescription("Get the weather for the given location")); diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java index ceccf95da..b81df3efb 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java @@ -107,8 +107,10 @@ public OpenAiChatCompletionOutput chatCompletionImage(@Nonnull final String link public OpenAiChatCompletionOutput chatCompletionToolExecution( @Nonnull final String location, @Nonnull final String unit) { + record WeatherRequest(String location, WeatherMethod.Unit unit) {} + // 1. Define the function - final Map schemaMap = generateSchema(WeatherMethod.Request.class); + final Map schemaMap = generateSchema(WeatherRequest.class); final var function = new OpenAiChatCompletionFunction() .setName("weather") @@ -133,9 +135,10 @@ public OpenAiChatCompletionOutput chatCompletionToolExecution( // 2. Optionally, execute the function. final OpenAiChatToolCall toolCall = response.getChoices().get(0).getMessage().getToolCalls().get(0); - final WeatherMethod.Request arguments = - parseJson(toolCall.getFunction().getArguments(), WeatherMethod.Request.class); - final WeatherMethod.Response currentWeather = WeatherMethod.getCurrentWeather(arguments); + final WeatherRequest arguments = + parseJson(toolCall.getFunction().getArguments(), WeatherRequest.class); + final WeatherMethod.Response currentWeather = + WeatherMethod.getCurrentWeather(arguments.location(), arguments.unit()); final OpenAiChatMessage.OpenAiChatAssistantMessage assistantMessage = response.getChoices().get(0).getMessage(); diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/WeatherMethod.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/WeatherMethod.java index 9e1a7decd..cadc7b0c0 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/WeatherMethod.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/WeatherMethod.java @@ -16,14 +16,6 @@ enum Unit { F } - /** - * Request for the weather - * - * @param location the city - * @param unit the unit of temperature - */ - record Request(String location, Unit unit) {} - /** * Response for the weather * @@ -35,8 +27,10 @@ record Response(double temp, Unit unit) {} @Nonnull @SuppressWarnings("unused") @Tool(description = "Get the weather in location") - static Response getCurrentWeather(@ToolParam @Nonnull final Request request) { - final int temperature = request.location.hashCode() % 30; - return new Response(temperature, request.unit); + static Response getCurrentWeather( + @ToolParam(description = "the city") @Nonnull final String location, + @ToolParam(description = "the unit of temperature") @Nonnull final Unit unit) { + final int temperature = location.hashCode() % 30; + return new Response(temperature, unit); } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOpenAiTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOpenAiTest.java index 46c6f199b..f4134a5a3 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOpenAiTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOpenAiTest.java @@ -76,9 +76,9 @@ void testToolCallingWithoutExecution() { assertThat(toolCall1.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall2.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall1.arguments()) - .matches("\\{\"arg0\": \\{\"location\": \"[^\"]*Potsdam[^\"]*\", \"unit\": \"C\"}}"); + .matches("\\{\"arg0\": \"[^\"]*Potsdam[^\"]*\", \"arg1\": \"C\"}"); assertThat(toolCall2.arguments()) - .matches("\\{\"arg0\": \\{\"location\": \"[^\"]*Toulouse[^\"]*\", \"unit\": \"C\"}}"); + .matches("\\{\"arg0\": \"[^\"]*Toulouse[^\"]*\", \"arg1\": \"C\"}"); } @Test diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 4727d0ad4..e080ccfc6 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -139,9 +139,9 @@ void testToolCallingWithoutExecution() { assertThat(toolCall1.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall2.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall1.arguments()) - .matches("\\{\"arg0\": \\{\"location\": \"[^\"]*Potsdam[^\"]*\", \"unit\": \"C\"}}"); + .matches("\\{\"arg0\": \"[^\"]*Potsdam[^\"]*\", \"arg1\": \"C\"}"); assertThat(toolCall2.arguments()) - .matches("\\{\"arg0\": \\{\"location\": \"[^\"]*Toulouse[^\"]*\", \"unit\": \"C\"}}"); + .matches("\\{\"arg0\": \"[^\"]*Toulouse[^\"]*\", \"arg1\": \"C\"}"); } @Test From bc55ee41d5f2daccc48a4017755b2e0fb27a5243 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Mon, 13 Jul 2026 17:00:21 +0200 Subject: [PATCH 2/2] add changes to unit tests --- .../openai/spring/OpenAiChatModelTest.java | 6 ++-- .../openai/spring/WeatherMethod.java | 14 +++----- .../__files/weatherToolResponse.json | 4 +-- .../src/test/resources/toolCallsRequest.json | 28 +++++++--------- .../src/test/resources/toolCallsRequest2.json | 32 ++++++++----------- .../spring/OrchestrationChatModelTest.java | 6 ++-- .../orchestration/spring/WeatherMethod.java | 14 +++----- .../resources/__files/toolCallsResponse.json | 8 ++--- .../resources/__files/toolCallsResponse2.json | 4 +-- .../src/test/resources/toolCallsRequest.json | 28 +++++++--------- .../src/test/resources/toolCallsRequest2.json | 32 ++++++++----------- 11 files changed, 68 insertions(+), 108 deletions(-) diff --git a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/OpenAiChatModelTest.java b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/OpenAiChatModelTest.java index 72ee11c47..3f92e35ee 100644 --- a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/OpenAiChatModelTest.java +++ b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/OpenAiChatModelTest.java @@ -148,10 +148,8 @@ void testToolCallsWithoutExecution() throws IOException { assertThat(toolCall2.type()).isEqualTo("function"); assertThat(toolCall1.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall2.name()).isEqualTo("getCurrentWeather"); - assertThat(toolCall1.arguments()) - .isEqualTo("{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}"); - assertThat(toolCall2.arguments()) - .isEqualTo("{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}"); + assertThat(toolCall1.arguments()).isEqualTo("{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}"); + assertThat(toolCall2.arguments()).isEqualTo("{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}"); try (var request1InputStream = fileLoader.apply("toolCallsRequest.json")) { final String request1 = new String(request1InputStream.readAllBytes()); diff --git a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/WeatherMethod.java b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/WeatherMethod.java index d2cd25649..1f2272191 100644 --- a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/WeatherMethod.java +++ b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/spring/WeatherMethod.java @@ -16,14 +16,6 @@ public enum Unit { F } - /** - * Request for the weather - * - * @param location the city - * @param unit the unit of temperature - */ - public record Request(String location, Unit unit) {} - /** * Response for the weather * @@ -35,7 +27,9 @@ public record Response(double temp, Unit unit) {} @Nonnull @SuppressWarnings("unused") @Tool(description = "Get the weather in location") - Response getCurrentWeather(@ToolParam @Nonnull Request request) { - return new Response(30, request.unit); + Response getCurrentWeather( + @ToolParam(description = "the city") @Nonnull final String location, + @ToolParam(description = "the unit of temperature") @Nonnull final Unit unit) { + return new Response(30, unit); } } diff --git a/foundation-models/openai/src/test/resources/__files/weatherToolResponse.json b/foundation-models/openai/src/test/resources/__files/weatherToolResponse.json index 14382b9d8..1eb8a0928 100644 --- a/foundation-models/openai/src/test/resources/__files/weatherToolResponse.json +++ b/foundation-models/openai/src/test/resources/__files/weatherToolResponse.json @@ -12,7 +12,7 @@ "tool_calls": [ { "function": { - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}", + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}", "name": "getCurrentWeather" }, "id": "call_MQ7MyYGmoP5TpMSv6AfeWCg5", @@ -20,7 +20,7 @@ }, { "function": { - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}", + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}", "name": "getCurrentWeather" }, "id": "call_BQpUfvkUUqx7e3yZv7Rmpnxy", diff --git a/foundation-models/openai/src/test/resources/toolCallsRequest.json b/foundation-models/openai/src/test/resources/toolCallsRequest.json index 761322b7f..70ded745e 100644 --- a/foundation-models/openai/src/test/resources/toolCallsRequest.json +++ b/foundation-models/openai/src/test/resources/toolCallsRequest.json @@ -16,27 +16,21 @@ "type": "object", "properties": { "arg0": { - "type": "object", - "properties": { - "location": { - "type": "string" - }, - "unit": { - "type": "string", - "enum": [ - "C", - "F" - ] - } - }, - "required": [ - "location", - "unit" + "type": "string", + "description": "the city" + }, + "arg1": { + "type": "string", + "description": "the unit of temperature", + "enum": [ + "C", + "F" ] } }, "required": [ - "arg0" + "arg0", + "arg1" ], "additionalProperties": false }, diff --git a/foundation-models/openai/src/test/resources/toolCallsRequest2.json b/foundation-models/openai/src/test/resources/toolCallsRequest2.json index 783536354..d52bf0df7 100644 --- a/foundation-models/openai/src/test/resources/toolCallsRequest2.json +++ b/foundation-models/openai/src/test/resources/toolCallsRequest2.json @@ -12,7 +12,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}" } }, { @@ -20,7 +20,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}" } } ] @@ -47,27 +47,21 @@ "type": "object", "properties": { "arg0": { - "type": "object", - "properties": { - "location": { - "type": "string" - }, - "unit": { - "type": "string", - "enum": [ - "C", - "F" - ] - } - }, - "required": [ - "location", - "unit" + "type": "string", + "description": "the city" + }, + "arg1": { + "type": "string", + "description": "the unit of temperature", + "enum": [ + "C", + "F" ] } }, "required": [ - "arg0" + "arg0", + "arg1" ], "additionalProperties": false }, diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModelTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModelTest.java index 2d511f9cf..17b1eaa82 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModelTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModelTest.java @@ -169,10 +169,8 @@ void testToolCallsWithoutExecution() throws IOException { assertThat(toolCall2.type()).isEqualTo("function"); assertThat(toolCall1.name()).isEqualTo("getCurrentWeather"); assertThat(toolCall2.name()).isEqualTo("getCurrentWeather"); - assertThat(toolCall1.arguments()) - .isEqualTo("{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}"); - assertThat(toolCall2.arguments()) - .isEqualTo("{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}"); + assertThat(toolCall1.arguments()).isEqualTo("{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}"); + assertThat(toolCall2.arguments()).isEqualTo("{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}"); try (var request1InputStream = fileLoader.apply("toolCallsRequest.json")) { final String request1 = new String(request1InputStream.readAllBytes()); diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/WeatherMethod.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/WeatherMethod.java index e2444bef0..b85b7e496 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/WeatherMethod.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/spring/WeatherMethod.java @@ -16,14 +16,6 @@ public enum Unit { F } - /** - * Request for the weather - * - * @param location the city - * @param unit the unit of temperature - */ - public record Request(String location, Unit unit) {} - /** * Response for the weather * @@ -35,7 +27,9 @@ public record Response(double temp, Unit unit) {} @Nonnull @SuppressWarnings("unused") @Tool(description = "Get the weather in location") - Response getCurrentWeather(@ToolParam @Nonnull Request request) { - return new Response(30, request.unit); + Response getCurrentWeather( + @ToolParam(description = "the city") @Nonnull final String location, + @ToolParam(description = "the unit of temperature") @Nonnull final Unit unit) { + return new Response(30, unit); } } diff --git a/orchestration/src/test/resources/__files/toolCallsResponse.json b/orchestration/src/test/resources/__files/toolCallsResponse.json index a85577b4d..31f02235e 100644 --- a/orchestration/src/test/resources/__files/toolCallsResponse.json +++ b/orchestration/src/test/resources/__files/toolCallsResponse.json @@ -25,7 +25,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}" } }, { @@ -33,7 +33,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}" } } ] @@ -66,7 +66,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}" } }, { @@ -74,7 +74,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}" } } ] diff --git a/orchestration/src/test/resources/__files/toolCallsResponse2.json b/orchestration/src/test/resources/__files/toolCallsResponse2.json index 076687786..19a53d8b0 100644 --- a/orchestration/src/test/resources/__files/toolCallsResponse2.json +++ b/orchestration/src/test/resources/__files/toolCallsResponse2.json @@ -15,7 +15,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}" } }, { @@ -23,7 +23,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}" } } ] diff --git a/orchestration/src/test/resources/toolCallsRequest.json b/orchestration/src/test/resources/toolCallsRequest.json index 208400e95..b6f7f639d 100644 --- a/orchestration/src/test/resources/toolCallsRequest.json +++ b/orchestration/src/test/resources/toolCallsRequest.json @@ -29,27 +29,21 @@ "type": "object", "properties": { "arg0": { - "type": "object", - "properties": { - "location": { - "type": "string" - }, - "unit": { - "type": "string", - "enum": [ - "C", - "F" - ] - } - }, - "required": [ - "location", - "unit" + "type": "string", + "description": "the city" + }, + "arg1": { + "type": "string", + "description": "the unit of temperature", + "enum": [ + "C", + "F" ] } }, "required": [ - "arg0" + "arg0", + "arg1" ] }, "strict": false diff --git a/orchestration/src/test/resources/toolCallsRequest2.json b/orchestration/src/test/resources/toolCallsRequest2.json index 1697ba0c0..5961d12d1 100644 --- a/orchestration/src/test/resources/toolCallsRequest2.json +++ b/orchestration/src/test/resources/toolCallsRequest2.json @@ -24,7 +24,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Potsdam\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Potsdam\", \"arg1\": \"C\"}" } }, { @@ -32,7 +32,7 @@ "type": "function", "function": { "name": "getCurrentWeather", - "arguments": "{\"arg0\": {\"location\": \"Toulouse\", \"unit\": \"C\"}}" + "arguments": "{\"arg0\": \"Toulouse\", \"arg1\": \"C\"}" } } ], @@ -62,27 +62,21 @@ "type": "object", "properties": { "arg0": { - "type": "object", - "properties": { - "location": { - "type": "string" - }, - "unit": { - "type": "string", - "enum": [ - "C", - "F" - ] - } - }, - "required": [ - "location", - "unit" + "type": "string", + "description": "the city" + }, + "arg1": { + "type": "string", + "description": "the unit of temperature", + "enum": [ + "C", + "F" ] } }, "required": [ - "arg0" + "arg0", + "arg1" ] }, "strict": false