From 8ba206e4c0dd15c3086e748b9631027e5416a310 Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Sun, 10 Aug 2025 20:15:47 +0800 Subject: [PATCH 1/6] [Enhancement] - Add requestMetadata to converse request Signed-off-by: Stuart Loxton Signed-off-by: Stuart Loxton --- .../ai/bedrock/converse/BedrockChatOptions.java | 15 +++++++++++++++ .../bedrock/converse/BedrockProxyChatModel.java | 9 ++++----- .../bedrock/converse/BedrockChatOptionsTests.java | 7 ++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java index 776cba66d58..07e0ed66c62 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java @@ -53,6 +53,9 @@ public class BedrockChatOptions implements ToolCallingChatOptions { @JsonProperty("presencePenalty") private Double presencePenalty; + @JsonIgnore + private Map requestParameters = new HashMap<>(); + @JsonProperty("stopSequences") private List stopSequences; @@ -87,6 +90,7 @@ public static BedrockChatOptions fromOptions(BedrockChatOptions fromOptions) { .frequencyPenalty(fromOptions.getFrequencyPenalty()) .maxTokens(fromOptions.getMaxTokens()) .presencePenalty(fromOptions.getPresencePenalty()) + .requestParameters(new HashMap<>(fromOptions.getRequestParameters())) .stopSequences( fromOptions.getStopSequences() != null ? new ArrayList<>(fromOptions.getStopSequences()) : null) .temperature(fromOptions.getTemperature()) @@ -126,6 +130,12 @@ public void setMaxTokens(Integer maxTokens) { this.maxTokens = maxTokens; } + public Map getRequestParameters() { return this.requestParameters; } + + public void setRequestParameters(Map requestParameters) { + this.requestParameters = requestParameters; + } + @Override public Double getPresencePenalty() { return this.presencePenalty; @@ -279,6 +289,11 @@ public Builder presencePenalty(Double presencePenalty) { return this; } + public Builder requestParameters(Map requestParameters) { + this.options.requestParameters = requestParameters; + return this; + } + public Builder stopSequences(List stopSequences) { this.options.stopSequences = stopSequences; return this; diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java index 071e77a78cb..2132912849e 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java @@ -22,11 +22,7 @@ import java.net.URL; import java.net.URLConnection; import java.time.Duration; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; @@ -425,6 +421,8 @@ else if (message.getMessageType() == MessageType.TOOL) { Document additionalModelRequestFields = ConverseApiUtils .getChatOptionsAdditionalModelRequestFields(this.defaultOptions, prompt.getOptions()); + HashMap requestMetadata = new HashMap<>(); + return ConverseRequest.builder() .modelId(updatedRuntimeOptions.getModel()) .inferenceConfig(inferenceConfiguration) @@ -432,6 +430,7 @@ else if (message.getMessageType() == MessageType.TOOL) { .system(systemMessages) .additionalModelRequestFields(additionalModelRequestFields) .toolConfig(toolConfiguration) + .requestMetadata(requestMetadata) .build(); } diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockChatOptionsTests.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockChatOptionsTests.java index aed48c1a3b5..e34ba9a84a0 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockChatOptionsTests.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockChatOptionsTests.java @@ -37,6 +37,7 @@ void testBuilderWithAllFields() { .frequencyPenalty(0.0) .maxTokens(100) .presencePenalty(0.0) + .requestParameters(Map.of("requestId", "1234")) .stopSequences(List.of("stop1", "stop2")) .temperature(0.7) .topP(0.8) @@ -44,9 +45,9 @@ void testBuilderWithAllFields() { .build(); assertThat(options) - .extracting("model", "frequencyPenalty", "maxTokens", "presencePenalty", "stopSequences", "temperature", - "topP", "topK") - .containsExactly("test-model", 0.0, 100, 0.0, List.of("stop1", "stop2"), 0.7, 0.8, 50); + .extracting("model", "frequencyPenalty", "maxTokens", "presencePenalty", "requestParameters", + "stopSequences", "temperature", "topP", "topK") + .containsExactly("test-model", 0.0, 100, 0.0, Map.of("requestId", "1234"), List.of("stop1", "stop2"), 0.7, 0.8, 50); } @Test From 07fa29d95c2fc27992e869b51d54490b9e8188cb Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Sun, 10 Aug 2025 21:04:01 +0800 Subject: [PATCH 2/6] [Enhancement] - Derrive requestMetadata from user params Signed-off-by: Stuart Loxton Signed-off-by: Stuart Loxton --- .../converse/BedrockProxyChatModel.java | 8 +++++-- .../converse/api/ConverseApiUtils.java | 22 +++++++++++++++++++ .../converse/BedrockConverseChatClientIT.java | 4 +++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java index 2132912849e..f2603e0f230 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java @@ -22,7 +22,11 @@ import java.net.URL; import java.net.URLConnection; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Set; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; @@ -421,7 +425,7 @@ else if (message.getMessageType() == MessageType.TOOL) { Document additionalModelRequestFields = ConverseApiUtils .getChatOptionsAdditionalModelRequestFields(this.defaultOptions, prompt.getOptions()); - HashMap requestMetadata = new HashMap<>(); + Map requestMetadata = ConverseApiUtils.getRequestMetadata(prompt); return ConverseRequest.builder() .modelId(updatedRuntimeOptions.getModel()) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java index d58fdbad8cf..8abe0654d7b 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import org.springframework.ai.chat.prompt.Prompt; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import software.amazon.awssdk.core.SdkField; @@ -384,6 +385,27 @@ else if (value instanceof Map mapValue) { } } + @SuppressWarnings("unchecked") + public static Map getRequestMetadata(Prompt prompt) { + Map metadata = prompt.getUserMessage().getMetadata(); + + if (metadata.isEmpty()) { + return Map.of(); + } + + Map result = new HashMap<>(); + for (Map.Entry entry : metadata.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + + if (key != null && value != null) { + result.put(key, value.toString()); + } + } + + return result; + } + private static Document convertMapToDocument(Map value) { Map attr = value.entrySet() .stream() diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java index 6980b6b2859..520337e0fd8 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java @@ -70,7 +70,9 @@ void call() { .system(s -> s.text(this.systemTextResource) .param("name", "Bob") .param("voice", "pirate")) - .user("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did") + .user(u -> u.text("Tell me about 3 famous pirates from the Golden Age of Piracy and what they did") + .param("requestId", "1234") + ) .call() .chatResponse(); // @formatter:on From 83bc0bbed262e60a210663ae546efd6e5978b6d4 Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Sun, 10 Aug 2025 21:16:23 +0800 Subject: [PATCH 3/6] [Enhancement] - Change argument type in getRequestMetada function Signed-off-by: Stuart Loxton Signed-off-by: Stuart Loxton --- .../ai/bedrock/converse/BedrockProxyChatModel.java | 2 +- .../ai/bedrock/converse/api/ConverseApiUtils.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java index f2603e0f230..a089676dda3 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java @@ -425,7 +425,7 @@ else if (message.getMessageType() == MessageType.TOOL) { Document additionalModelRequestFields = ConverseApiUtils .getChatOptionsAdditionalModelRequestFields(this.defaultOptions, prompt.getOptions()); - Map requestMetadata = ConverseApiUtils.getRequestMetadata(prompt); + Map requestMetadata = ConverseApiUtils.getRequestMetadata(prompt.getUserMessage().getMetadata()); return ConverseRequest.builder() .modelId(updatedRuntimeOptions.getModel()) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java index 8abe0654d7b..dd49bb5e006 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/ConverseApiUtils.java @@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import org.springframework.ai.chat.prompt.Prompt; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import software.amazon.awssdk.core.SdkField; @@ -386,8 +385,7 @@ else if (value instanceof Map mapValue) { } @SuppressWarnings("unchecked") - public static Map getRequestMetadata(Prompt prompt) { - Map metadata = prompt.getUserMessage().getMetadata(); + public static Map getRequestMetadata(Map metadata) { if (metadata.isEmpty()) { return Map.of(); From b2db844b3d78ee6623185b7bc562985b5c0e16af Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Mon, 11 Aug 2025 19:06:22 +0800 Subject: [PATCH 4/6] [Enhancement] - Remove redundant default value Signed-off-by: Stuart Loxton --- .../springframework/ai/bedrock/converse/BedrockChatOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java index 07e0ed66c62..3ead065d32b 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java @@ -54,7 +54,7 @@ public class BedrockChatOptions implements ToolCallingChatOptions { private Double presencePenalty; @JsonIgnore - private Map requestParameters = new HashMap<>(); + private Map requestParameters; @JsonProperty("stopSequences") private List stopSequences; From bb22ff3f3b8f725f16a9f4afeab2f8e98b932b8d Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Mon, 11 Aug 2025 19:11:52 +0800 Subject: [PATCH 5/6] [Enhancement] - Add equals and hashCode logic Signed-off-by: Stuart Loxton --- .../ai/bedrock/converse/BedrockChatOptions.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java index 3ead065d32b..8e93172b858 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java @@ -251,6 +251,7 @@ public boolean equals(Object o) { return Objects.equals(this.model, that.model) && Objects.equals(this.frequencyPenalty, that.frequencyPenalty) && Objects.equals(this.maxTokens, that.maxTokens) && Objects.equals(this.presencePenalty, that.presencePenalty) + && Objects.equals(this.requestParameters, that.requestParameters) && Objects.equals(this.stopSequences, that.stopSequences) && Objects.equals(this.temperature, that.temperature) && Objects.equals(this.topK, that.topK) && Objects.equals(this.topP, that.topP) && Objects.equals(this.toolCallbacks, that.toolCallbacks) @@ -260,8 +261,9 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(this.model, this.frequencyPenalty, this.maxTokens, this.presencePenalty, this.stopSequences, - this.temperature, this.topK, this.topP, this.toolCallbacks, this.toolNames, this.toolContext, + return Objects.hash(this.model, this.frequencyPenalty, this.maxTokens, this.presencePenalty, + this.requestParameters, this.stopSequences, this.temperature, this.topK, this.topP, + this.toolCallbacks, this.toolNames, this.toolContext, this.internalToolExecutionEnabled); } From ff85ea1902775c671823f65d6905030f960823d2 Mon Sep 17 00:00:00 2001 From: Stuart Loxton Date: Mon, 11 Aug 2025 19:29:27 +0800 Subject: [PATCH 6/6] Initialise with hashmap to avoid null pointer exception Signed-off-by: Stuart Loxton --- .../springframework/ai/bedrock/converse/BedrockChatOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java index 8e93172b858..37b82a0fcee 100644 --- a/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java +++ b/models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockChatOptions.java @@ -54,7 +54,7 @@ public class BedrockChatOptions implements ToolCallingChatOptions { private Double presencePenalty; @JsonIgnore - private Map requestParameters; + private Map requestParameters = new HashMap<>(); @JsonProperty("stopSequences") private List stopSequences;