From 3763e9a1b89ef9e0c17335db69194bbed06c0338 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Mon, 31 Mar 2025 17:21:02 +0900 Subject: [PATCH] Encapsulate Advisor Parameters with ChatMemoryAdvisorOptions Signed-off-by: jonghoonpark --- .../advisor/ChatMemoryAdvisorOptions.java | 70 +++++++++++++++++++ .../ChatMemoryAdvisorOptionsTests.java | 49 +++++++++++++ .../modules/ROOT/pages/api/advisors.adoc | 10 ++- .../modules/ROOT/pages/api/chatclient.adoc | 12 ++-- .../RetrievalAugmentationAdvisorIT.java | 11 ++- 5 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptions.java create mode 100644 spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptionsTests.java diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptions.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptions.java new file mode 100644 index 00000000000..e40fce3f9fa --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptions.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.chat.client.advisor; + +import org.springframework.ai.chat.client.ChatClient.AdvisorSpec; +import org.springframework.util.Assert; + +import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.*; + +/** + * Provides a way to configure an AdvisorSpec with a conversation ID and retrieval size. + * + * @author Jonghoon Park + */ +public class ChatMemoryAdvisorOptions { + + private Object conversationId = DEFAULT_CHAT_MEMORY_CONVERSATION_ID; + + private Integer retrieveSize = DEFAULT_CHAT_MEMORY_RESPONSE_SIZE; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final ChatMemoryAdvisorOptions options; + + public Builder() { + this.options = new ChatMemoryAdvisorOptions(); + } + + public Builder conversationId(Object conversationId) { + this.options.conversationId = conversationId; + return this; + } + + public Builder retrieveSize(int retrieveSize) { + this.options.retrieveSize = retrieveSize; + return this; + } + + public ChatMemoryAdvisorOptions build() { + return this.options; + } + + } + + public void applyTo(AdvisorSpec advisorSpec) { + Assert.notNull(advisorSpec, "advisorSpec must not be null"); + + advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId); + advisorSpec.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, retrieveSize); + } + +} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptionsTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptionsTests.java new file mode 100644 index 00000000000..50252c26b6b --- /dev/null +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/ChatMemoryAdvisorOptionsTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.chat.client.advisor; + +import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.client.DefaultChatClient; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.*; + +/** + * @author Jonghoon Park + */ +public class ChatMemoryAdvisorOptionsTests { + + @Test + public void testChatMemoryAdvisorConfigurator() { + DefaultChatClient.DefaultAdvisorSpec spec = new DefaultChatClient.DefaultAdvisorSpec(); + spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, 1); + spec.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100); + + DefaultChatClient.DefaultAdvisorSpec spec2 = new DefaultChatClient.DefaultAdvisorSpec(); + ChatMemoryAdvisorOptions options = ChatMemoryAdvisorOptions.builder() + .conversationId(1) + .retrieveSize(100) + .build(); + options.applyTo(spec2); + + assertEquals(spec.getParams().get(CHAT_MEMORY_CONVERSATION_ID_KEY), + spec2.getParams().get(CHAT_MEMORY_CONVERSATION_ID_KEY)); + assertEquals(spec.getParams().get(CHAT_MEMORY_RETRIEVE_SIZE_KEY), + spec2.getParams().get(CHAT_MEMORY_RETRIEVE_SIZE_KEY)); + } + +} diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/advisors.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/advisors.adoc index 405eb60883a..7f82b38a929 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/advisors.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/advisors.adoc @@ -18,10 +18,14 @@ var chatClient = ChatClient.builder(chatModel) ) .build(); +// Set advisor parameters at runtime +ChatMemoryAdvisorOptions options = ChatMemoryAdvisorOptions.builder() + .conversationId("678") + .retrieveSize(100) + .build(); + String response = this.chatClient.prompt() - // Set advisor parameters at runtime - .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678") - .param("chat_memory_response_size", 100)) + .advisors(options::applyTo) .user(userText) .call() .content(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chatclient.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chatclient.adoc index 3fd13c44c89..0050eb9da29 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chatclient.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chatclient.adoc @@ -420,9 +420,6 @@ A sample `@Service` implementation that uses several advisors is shown below. [source,java] ---- -import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY; -import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY; - @Service public class CustomerSupportAssistant { @@ -452,11 +449,14 @@ public class CustomerSupportAssistant { public Flux chat(String chatId, String userMessageContent) { + ChatMemoryAdvisorOptions options = ChatMemoryAdvisorOptions.builder() + .conversationId(chatId) + .retrieveSize(100) + .build(); + return this.chatClient.prompt() .user(userMessageContent) - .advisors(a -> a - .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) - .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)) + .advisors(options::applyTo) .stream().content(); } diff --git a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/client/advisor/RetrievalAugmentationAdvisorIT.java b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/client/advisor/RetrievalAugmentationAdvisorIT.java index e7170c68b75..191c068bf2f 100644 --- a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/client/advisor/RetrievalAugmentationAdvisorIT.java +++ b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/client/advisor/RetrievalAugmentationAdvisorIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.ChatMemoryAdvisorOptions; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.RetrievalAugmentationAdvisor; import org.springframework.ai.chat.memory.InMemoryChatMemory; @@ -147,11 +147,11 @@ void ragWithCompression() { .build(); String conversationId = "007"; + ChatMemoryAdvisorOptions options = ChatMemoryAdvisorOptions.builder().conversationId(conversationId).build(); ChatResponse chatResponse1 = chatClient.prompt() .user("Where does the adventure of Anacletus and Birba take place?") - .advisors(advisors -> advisors.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, - conversationId)) + .advisors(options::applyTo) .call() .chatResponse(); @@ -161,8 +161,7 @@ void ragWithCompression() { ChatResponse chatResponse2 = chatClient.prompt() .user("Did they meet any cow?") - .advisors(advisors -> advisors.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, - conversationId)) + .advisors(options::applyTo) .call() .chatResponse();