Skip to content

Commit bd1f0d2

Browse files
committed
Remove deprecations in ChatClient and Advisors
* Update remaining Advisors and related classes to use the new APIs. * In AbstractChatMemoryAdvisor, the “doNextWithProtectFromBlockingBefore()” protected method has been changed from accepting AdvisedRequest to ChatClientRequest. It’s a breaking change since the alternative was not part of M8. * MessageAggregator has a new method to aggregate messages from ChatClientRequest. The previous method aggregating messages from AdvisedRequest has been removed. Warning since it wasn’t marked as deprecated in M8. * In SimpleLoggerAdvisor, the “requestToString” input argument needs to be updated to use ChatClientRequest. It’s a breaking change since the alternative was not part of M8. Same thing about the constructor. * Remove deprecations in ChatClient and Advisors, and update tests accordingly. Relates to gh-2655 Signed-off-by: Thomas Vitale <[email protected]>
1 parent 9e71b16 commit bd1f0d2

File tree

59 files changed

+1219
-3185
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1219
-3185
lines changed

advisors/spring-ai-advisors-vector-store/src/main/java/org/springframework/ai/chat/client/advisor/vectorstore/QuestionAnswerAdvisor.java

Lines changed: 44 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,13 @@
2121
import java.util.Map;
2222
import java.util.stream.Collectors;
2323

24-
import reactor.core.publisher.Flux;
25-
import reactor.core.publisher.Mono;
24+
import org.springframework.ai.chat.client.ChatClientRequest;
25+
import org.springframework.ai.chat.client.ChatClientResponse;
26+
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
27+
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
28+
import reactor.core.scheduler.Scheduler;
2629
import reactor.core.scheduler.Schedulers;
2730

28-
import org.springframework.ai.chat.client.advisor.api.AdvisedRequest;
29-
import org.springframework.ai.chat.client.advisor.api.AdvisedResponse;
30-
import org.springframework.ai.chat.client.advisor.api.AdvisedResponseStreamUtils;
31-
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor;
32-
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain;
33-
import org.springframework.ai.chat.client.advisor.api.StreamAroundAdvisor;
34-
import org.springframework.ai.chat.client.advisor.api.StreamAroundAdvisorChain;
3531
import org.springframework.ai.chat.model.ChatResponse;
3632
import org.springframework.ai.chat.prompt.PromptTemplate;
3733
import org.springframework.ai.document.Document;
@@ -53,7 +49,7 @@
5349
* @author Thomas Vitale
5450
* @since 1.0.0
5551
*/
56-
public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
52+
public class QuestionAnswerAdvisor implements BaseAdvisor {
5753

5854
public static final String RETRIEVED_DOCUMENTS = "qa_retrieved_documents";
5955

@@ -80,198 +76,91 @@ public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdv
8076

8177
private final SearchRequest searchRequest;
8278

83-
private final boolean protectFromBlocking;
79+
private final Scheduler scheduler;
8480

8581
private final int order;
8682

87-
/**
88-
* The QuestionAnswerAdvisor retrieves context information from a Vector Store and
89-
* combines it with the user's text.
90-
* @param vectorStore The vector store to use
91-
*/
9283
public QuestionAnswerAdvisor(VectorStore vectorStore) {
93-
this(vectorStore, SearchRequest.builder().build(), DEFAULT_PROMPT_TEMPLATE, true, DEFAULT_ORDER);
94-
}
95-
96-
/**
97-
* The QuestionAnswerAdvisor retrieves context information from a Vector Store and
98-
* combines it with the user's text.
99-
* @param vectorStore The vector store to use
100-
* @param searchRequest The search request defined using the portable filter
101-
* expression syntax
102-
* @deprecated in favor of the builder: {@link #builder(VectorStore)}
103-
*/
104-
@Deprecated
105-
public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest) {
106-
this(vectorStore, searchRequest, DEFAULT_PROMPT_TEMPLATE, true, DEFAULT_ORDER);
107-
}
108-
109-
/**
110-
* The QuestionAnswerAdvisor retrieves context information from a Vector Store and
111-
* combines it with the user's text.
112-
* @param vectorStore The vector store to use
113-
* @param searchRequest The search request defined using the portable filter
114-
* expression syntax
115-
* @param userTextAdvise The user text to append to the existing user prompt. The text
116-
* should contain a placeholder named "question_answer_context".
117-
* @deprecated in favor of the builder: {@link #builder(VectorStore)}
118-
*/
119-
@Deprecated
120-
public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise) {
121-
this(vectorStore, searchRequest, PromptTemplate.builder().template(userTextAdvise).build(), true,
84+
this(vectorStore, SearchRequest.builder().build(), DEFAULT_PROMPT_TEMPLATE, BaseAdvisor.DEFAULT_SCHEDULER,
12285
DEFAULT_ORDER);
12386
}
12487

125-
/**
126-
* The QuestionAnswerAdvisor retrieves context information from a Vector Store and
127-
* combines it with the user's text.
128-
* @param vectorStore The vector store to use
129-
* @param searchRequest The search request defined using the portable filter
130-
* expression syntax
131-
* @param userTextAdvise The user text to append to the existing user prompt. The text
132-
* should contain a placeholder named "question_answer_context".
133-
* @param protectFromBlocking If true the advisor will protect the execution from
134-
* blocking threads. If false the advisor will not protect the execution from blocking
135-
* threads. This is useful when the advisor is used in a non-blocking environment. It
136-
* is true by default.
137-
* @deprecated in favor of the builder: {@link #builder(VectorStore)}
138-
*/
139-
@Deprecated
140-
public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise,
141-
boolean protectFromBlocking) {
142-
this(vectorStore, searchRequest, PromptTemplate.builder().template(userTextAdvise).build(), protectFromBlocking,
143-
DEFAULT_ORDER);
144-
}
145-
146-
/**
147-
* The QuestionAnswerAdvisor retrieves context information from a Vector Store and
148-
* combines it with the user's text.
149-
* @param vectorStore The vector store to use
150-
* @param searchRequest The search request defined using the portable filter
151-
* expression syntax
152-
* @param userTextAdvise The user text to append to the existing user prompt. The text
153-
* should contain a placeholder named "question_answer_context".
154-
* @param protectFromBlocking If true the advisor will protect the execution from
155-
* blocking threads. If false the advisor will not protect the execution from blocking
156-
* threads. This is useful when the advisor is used in a non-blocking environment. It
157-
* is true by default.
158-
* @param order The order of the advisor.
159-
* @deprecated in favor of the builder: {@link #builder(VectorStore)}
160-
*/
161-
@Deprecated
162-
public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise,
163-
boolean protectFromBlocking, int order) {
164-
this(vectorStore, searchRequest, PromptTemplate.builder().template(userTextAdvise).build(), protectFromBlocking,
165-
order);
166-
}
167-
16888
QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, @Nullable PromptTemplate promptTemplate,
169-
boolean protectFromBlocking, int order) {
89+
@Nullable Scheduler scheduler, int order) {
17090
Assert.notNull(vectorStore, "vectorStore cannot be null");
17191
Assert.notNull(searchRequest, "searchRequest cannot be null");
17292

17393
this.vectorStore = vectorStore;
17494
this.searchRequest = searchRequest;
17595
this.promptTemplate = promptTemplate != null ? promptTemplate : DEFAULT_PROMPT_TEMPLATE;
176-
this.protectFromBlocking = protectFromBlocking;
96+
this.scheduler = scheduler != null ? scheduler : BaseAdvisor.DEFAULT_SCHEDULER;
17797
this.order = order;
17898
}
17999

180100
public static Builder builder(VectorStore vectorStore) {
181101
return new Builder(vectorStore);
182102
}
183103

184-
@Override
185-
public String getName() {
186-
return this.getClass().getSimpleName();
187-
}
188-
189104
@Override
190105
public int getOrder() {
191106
return this.order;
192107
}
193108

194109
@Override
195-
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
196-
197-
AdvisedRequest advisedRequest2 = before(advisedRequest);
198-
199-
AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest2);
200-
201-
return after(advisedResponse);
202-
}
203-
204-
@Override
205-
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
206-
207-
// This can be executed by both blocking and non-blocking Threads
208-
// E.g. a command line or Tomcat blocking Thread implementation
209-
// or by a WebFlux dispatch in a non-blocking manner.
210-
Flux<AdvisedResponse> advisedResponses = (this.protectFromBlocking) ?
211-
// @formatter:off
212-
Mono.just(advisedRequest)
213-
.publishOn(Schedulers.boundedElastic())
214-
.map(this::before)
215-
.flatMapMany(request -> chain.nextAroundStream(request))
216-
: chain.nextAroundStream(before(advisedRequest));
217-
// @formatter:on
218-
219-
return advisedResponses.map(ar -> {
220-
if (AdvisedResponseStreamUtils.onFinishReason().test(ar)) {
221-
ar = after(ar);
222-
}
223-
return ar;
224-
});
225-
}
226-
227-
private AdvisedRequest before(AdvisedRequest request) {
228-
229-
var context = new HashMap<>(request.adviseContext());
230-
110+
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
231111
// 1. Search for similar documents in the vector store.
232112
var searchRequestToUse = SearchRequest.from(this.searchRequest)
233-
.query(request.userText())
234-
.filterExpression(doGetFilterExpression(context))
113+
.query(chatClientRequest.prompt().getUserMessage().getText())
114+
.filterExpression(doGetFilterExpression(chatClientRequest.context()))
235115
.build();
236116

237117
List<Document> documents = this.vectorStore.similaritySearch(searchRequestToUse);
238118

239119
// 2. Create the context from the documents.
120+
Map<String, Object> context = new HashMap<>(chatClientRequest.context());
240121
context.put(RETRIEVED_DOCUMENTS, documents);
241122

242-
String documentContext = documents.stream()
243-
.map(Document::getText)
244-
.collect(Collectors.joining(System.lineSeparator()));
123+
String documentContext = documents == null ? ""
124+
: documents.stream().map(Document::getText).collect(Collectors.joining(System.lineSeparator()));
245125

246126
// 3. Augment the user prompt with the document context.
247127
String augmentedUserText = this.promptTemplate.mutate()
248-
.template(request.userText() + System.lineSeparator() + this.promptTemplate.getTemplate())
128+
.template(chatClientRequest.prompt().getUserMessage().getText() + System.lineSeparator()
129+
+ this.promptTemplate.getTemplate())
249130
.variables(Map.of("question_answer_context", documentContext))
250131
.build()
251132
.render();
252133

253-
AdvisedRequest advisedRequest = AdvisedRequest.from(request)
254-
.userText(augmentedUserText)
255-
.adviseContext(context)
134+
// 4. Update ChatClientRequest with augmented prompt.
135+
return chatClientRequest.mutate()
136+
.prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText))
137+
.context(context)
256138
.build();
257-
258-
return advisedRequest;
259139
}
260140

261-
private AdvisedResponse after(AdvisedResponse advisedResponse) {
262-
ChatResponse.Builder chatResponseBuilder = ChatResponse.builder().from(advisedResponse.response());
263-
chatResponseBuilder.metadata(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
264-
return new AdvisedResponse(chatResponseBuilder.build(), advisedResponse.adviseContext());
141+
@Override
142+
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
143+
ChatResponse.Builder chatResponseBuilder;
144+
if (chatClientResponse.chatResponse() == null) {
145+
chatResponseBuilder = ChatResponse.builder();
146+
}
147+
else {
148+
chatResponseBuilder = ChatResponse.builder().from(chatClientResponse.chatResponse());
149+
}
150+
chatResponseBuilder.metadata(RETRIEVED_DOCUMENTS, chatClientResponse.context().get(RETRIEVED_DOCUMENTS));
151+
return ChatClientResponse.builder()
152+
.chatResponse(chatResponseBuilder.build())
153+
.context(chatClientResponse.context())
154+
.build();
265155
}
266156

157+
@Nullable
267158
protected Filter.Expression doGetFilterExpression(Map<String, Object> context) {
268-
269159
if (!context.containsKey(FILTER_EXPRESSION)
270160
|| !StringUtils.hasText(context.get(FILTER_EXPRESSION).toString())) {
271161
return this.searchRequest.getFilterExpression();
272162
}
273163
return new FilterExpressionTextParser().parse(context.get(FILTER_EXPRESSION).toString());
274-
275164
}
276165

277166
public static final class Builder {
@@ -282,7 +171,7 @@ public static final class Builder {
282171

283172
private PromptTemplate promptTemplate;
284173

285-
private boolean protectFromBlocking = true;
174+
private Scheduler scheduler;
286175

287176
private int order = DEFAULT_ORDER;
288177

@@ -303,18 +192,13 @@ public Builder searchRequest(SearchRequest searchRequest) {
303192
return this;
304193
}
305194

306-
/**
307-
* @deprecated in favour of {@link #promptTemplate(PromptTemplate)}
308-
*/
309-
@Deprecated
310-
public Builder userTextAdvise(String userTextAdvise) {
311-
Assert.hasText(userTextAdvise, "The userTextAdvise must not be empty!");
312-
this.promptTemplate = PromptTemplate.builder().template(userTextAdvise).build();
195+
public Builder protectFromBlocking(boolean protectFromBlocking) {
196+
this.scheduler = protectFromBlocking ? BaseAdvisor.DEFAULT_SCHEDULER : Schedulers.immediate();
313197
return this;
314198
}
315199

316-
public Builder protectFromBlocking(boolean protectFromBlocking) {
317-
this.protectFromBlocking = protectFromBlocking;
200+
public Builder scheduler(Scheduler scheduler) {
201+
this.scheduler = scheduler;
318202
return this;
319203
}
320204

@@ -324,8 +208,8 @@ public Builder order(int order) {
324208
}
325209

326210
public QuestionAnswerAdvisor build() {
327-
return new QuestionAnswerAdvisor(this.vectorStore, this.searchRequest, this.promptTemplate,
328-
this.protectFromBlocking, this.order);
211+
return new QuestionAnswerAdvisor(this.vectorStore, this.searchRequest, this.promptTemplate, this.scheduler,
212+
this.order);
329213
}
330214

331215
}

0 commit comments

Comments
 (0)