Skip to content

Commit 5840b73

Browse files
authored
fix: incrementalOutput not working (#17)
1. set enable search to false for default chat configuration. 2. Fix incremental output issue and support streaming function call.
1 parent 423142c commit 5840b73

File tree

8 files changed

+60
-29
lines changed

8 files changed

+60
-29
lines changed

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeAutoConfiguration.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ public DashScopeChatModel dashscopeChatModel(DashScopeConnectionProperties commo
112112
retryTemplate);
113113
}
114114

115+
@Bean
116+
@ConditionalOnMissingBean
117+
@ConditionalOnProperty(prefix = DashScopeEmbeddingProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
118+
matchIfMissing = true)
115119
public DashScopeApi dashscopeChatApi(DashScopeConnectionProperties commonProperties,
116120
DashScopeChatProperties chatProperties, RestClient.Builder restClientBuilder,
117121
WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler) {

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/autoconfigure/dashscope/DashScopeChatProperties.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class DashScopeChatProperties extends DashScopeParentProperties {
4040
/**
4141
* Default DashScope Chat model.
4242
*/
43-
public static final String DEFAULT_DEPLOYMENT_NAME = Generation.Models.QWEN_TURBO;
43+
public static final String DEFAULT_DEPLOYMENT_NAME = Generation.Models.QWEN_PLUS;
4444

4545
/**
4646
* Default temperature speed.
@@ -56,7 +56,6 @@ public class DashScopeChatProperties extends DashScopeParentProperties {
5656
private DashScopeChatOptions options = DashScopeChatOptions.builder()
5757
.withModel(DEFAULT_DEPLOYMENT_NAME)
5858
.withTemperature(DEFAULT_TEMPERATURE)
59-
.withEnableSearch(true)
6059
.build();
6160

6261
public DashScopeChatProperties() {

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeAiStreamFunctionCallingHelper.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@
3838
* @author Ken
3939
*/
4040
public class DashScopeAiStreamFunctionCallingHelper {
41+
private Boolean incrementalOutput = false;
42+
43+
public DashScopeAiStreamFunctionCallingHelper() {
44+
}
45+
46+
public DashScopeAiStreamFunctionCallingHelper(Boolean incrementalOutput) {
47+
this.incrementalOutput = incrementalOutput;
48+
}
4149

4250
/**
4351
* Merge the previous and current ChatCompletionChunk into a single one.
@@ -46,7 +54,6 @@ public class DashScopeAiStreamFunctionCallingHelper {
4654
* @return the merged ChatCompletionChunk
4755
*/
4856
public ChatCompletionChunk merge(ChatCompletionChunk previous, ChatCompletionChunk current) {
49-
5057
if (previous == null) {
5158
return current;
5259
}
@@ -57,9 +64,18 @@ public ChatCompletionChunk merge(ChatCompletionChunk previous, ChatCompletionChu
5764
Choice previousChoice0 = previous.output() == null ? null : previous.output().choices().get(0);
5865
Choice currentChoice0 = current.output() == null ? null : current.output().choices().get(0);
5966

67+
//compatibility of incremental_output false for streaming function call
68+
if (!incrementalOutput && isStreamingToolFunctionCall(current)) {
69+
if (!isStreamingToolFunctionCallFinish(current)) {
70+
return new ChatCompletionChunk(id, new ChatCompletionOutput(null, List.of(new Choice(null, null))), usage);
71+
} else {
72+
return new ChatCompletionChunk(id, new ChatCompletionOutput(null, List.of(currentChoice0)), usage);
73+
}
74+
}
75+
6076
Choice choice = merge(previousChoice0, currentChoice0);
6177
List<Choice> chunkChoices = choice == null ? List.of() : List.of(choice);
62-
return new ChatCompletionChunk(id, new ChatCompletionOutput(null, chunkChoices), usage);
78+
return new ChatCompletionChunk(id, new ChatCompletionOutput(null, chunkChoices), usage);
6379
}
6480

6581
private Choice merge(Choice previous, Choice current) {

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/api/DashScopeApi.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,6 @@ public ResponseEntity<ChatCompletion> chatCompletionEntity(ChatCompletionRequest
12371237
.toEntity(ChatCompletion.class);
12381238
}
12391239

1240-
private final DashScopeAiStreamFunctionCallingHelper chunkMerger = new DashScopeAiStreamFunctionCallingHelper();
1241-
12421240
/**
12431241
* Creates a streaming chat response for the given chat conversation.
12441242
* @param chatRequest The chat completion request. Must have the stream property set
@@ -1251,6 +1249,8 @@ public Flux<ChatCompletionChunk> chatCompletionStream(ChatCompletionRequest chat
12511249
Assert.isTrue(chatRequest.stream(), "Request must set the stream property to true.");
12521250

12531251
AtomicBoolean isInsideTool = new AtomicBoolean(false);
1252+
boolean incrementalOutput = chatRequest.parameters() != null && chatRequest.parameters().incrementalOutput != null && chatRequest.parameters().incrementalOutput;
1253+
DashScopeAiStreamFunctionCallingHelper chunkMerger = new DashScopeAiStreamFunctionCallingHelper(incrementalOutput);
12541254

12551255
return this.webClient.post()
12561256
.uri("/api/v1/services/aigc/text-generation/generation")
@@ -1262,21 +1262,21 @@ public Flux<ChatCompletionChunk> chatCompletionStream(ChatCompletionRequest chat
12621262
.filter(SSE_DONE_PREDICATE.negate())
12631263
.map(content -> ModelOptionsUtils.jsonToObject(content, ChatCompletionChunk.class))
12641264
.map(chunk -> {
1265-
if (this.chunkMerger.isStreamingToolFunctionCall(chunk)) {
1265+
if (chunkMerger.isStreamingToolFunctionCall(chunk)) {
12661266
isInsideTool.set(true);
12671267
}
12681268
return chunk;
12691269
})
12701270
.windowUntil(chunk -> {
1271-
if (isInsideTool.get() && this.chunkMerger.isStreamingToolFunctionCallFinish(chunk)) {
1271+
if (isInsideTool.get() && chunkMerger.isStreamingToolFunctionCallFinish(chunk)) {
12721272
isInsideTool.set(false);
12731273
return true;
12741274
}
12751275
return !isInsideTool.get();
12761276
})
12771277
.concatMapIterable(window -> {
12781278
Mono<ChatCompletionChunk> monoChunk = window.reduce(new ChatCompletionChunk(null, null, null),
1279-
this.chunkMerger::merge);
1279+
chunkMerger::merge);
12801280
return List.of(monoChunk);
12811281
})
12821282
.flatMap(mono -> mono);

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatModel.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
248248
options = ModelOptionsUtils.merge(options, this.defaultOptions, DashScopeChatOptions.class);
249249

250250
if (!CollectionUtils.isEmpty(enabledToolsToUse)) {
251-
options = ModelOptionsUtils.merge(
252-
DashScopeChatOptions.builder().withTools(this.getFunctionTools(enabledToolsToUse)).build(), options,
253-
DashScopeChatOptions.class);
251+
options.setTools(this.getFunctionTools(enabledToolsToUse));
254252
}
255253

256254
List<ChatCompletionMessage> chatCompletionMessages = prompt.getInstructions().stream().map(message -> {
@@ -338,7 +336,7 @@ private ChatCompletionRequestParameter toDashScopeRequestParameter(DashScopeChat
338336
return new ChatCompletionRequestParameter();
339337
}
340338

341-
Boolean incrementalOutput = stream || options.getIncrementalOutput();
339+
Boolean incrementalOutput = options.getIncrementalOutput();
342340
return new ChatCompletionRequestParameter("message", options.getSeed(), options.getMaxTokens(),
343341
options.getTopP(), options.getTopK(), options.getRepetitionPenalty(), options.getPresencePenalty(),
344342
options.getTemperature(), options.getStop(), options.getEnableSearch(), incrementalOutput,

spring-ai-alibaba-core/src/main/java/com/alibaba/cloud/ai/dashscope/chat/DashScopeChatOptions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class DashScopeChatOptions implements FunctionCallingOptions, ChatOptions
7474
/**
7575
* 控制在流式输出模式下是否开启增量输出,即后续输出内容是否包含已输出的内容。设置为True时,将开启增量输出模式,后面输出不会包含已经输出的内容,您需要自行拼接整体输出;设置为False则会包含已输出的内容。
7676
*/
77-
private @JsonProperty("incremental_output") Boolean incrementalOutput = false;
77+
private @JsonProperty("incremental_output") Boolean incrementalOutput = true;
7878

7979
/** 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。默认为1.1。 */
8080
private @JsonProperty("repetition_penalty") Float repetitionPenalty;
@@ -341,6 +341,7 @@ public static DashScopeChatOptions fromOptions(DashScopeChatOptions fromOptions)
341341
.withStop(fromOptions.getStop())
342342
.withStream(fromOptions.getStream())
343343
.withEnableSearch(fromOptions.enableSearch)
344+
.withIncrementalOutput(fromOptions.getIncrementalOutput())
344345
.withFunctionCallbacks(fromOptions.getFunctionCallbacks())
345346
.withFunctions(fromOptions.getFunctions())
346347
.withRepetitionPenalty(fromOptions.getRepetitionPenalty())

spring-ai-alibaba-core/src/test/java/com/alibaba/cloud/ai/dashscope/chat/client/DashScopeChatClientIT.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
2222
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi.ChatCompletionFinishReason;
2323
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
24+
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
2425
import com.alibaba.cloud.ai.dashscope.tool.DashScopeFunctionTestConfiguration;
2526
import com.alibaba.cloud.ai.dashscope.chat.tool.MockOrderService;
2627
import com.alibaba.cloud.ai.dashscope.chat.tool.MockWeatherService;
@@ -75,7 +76,7 @@ public class DashScopeChatClientIT {
7576
private DashScopeChatModel dashscopeChatModel;
7677

7778
@Autowired
78-
private DashScopeApi dashscopeApi;
79+
private DashScopeApi dashscopeChatApi;
7980

8081
@Value("classpath:/prompts/rag/system-qa.st")
8182
private Resource systemResource;
@@ -85,7 +86,7 @@ public class DashScopeChatClientIT {
8586

8687
@Test
8788
void callTest() throws IOException {
88-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
89+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
8990
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
9091

9192
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -102,14 +103,20 @@ void callTest() throws IOException {
102103

103104
@Test
104105
void streamTest() throws InterruptedException, IOException {
105-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
106+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
106107
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
107108
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
108109
.defaultAdvisors(
109110
new DocumentRetrievalAdvisor(retriever, systemResource.getContentAsString(StandardCharsets.UTF_8)))
110111
.build();
111112

112-
Flux<ChatResponse> response = chatClient.prompt().user("如何快速开始百炼?").stream().chatResponse();
113+
Flux<ChatResponse> response = chatClient.prompt()
114+
.user("如何快速开始百炼?")
115+
.options(DashScopeChatOptions.builder()
116+
.withIncrementalOutput(true)
117+
.build())
118+
.stream()
119+
.chatResponse();
113120

114121
CountDownLatch cdl = new CountDownLatch(1);
115122
response.subscribe(data -> {
@@ -159,7 +166,7 @@ void callWithFunctionBeanTest() {
159166

160167
@Test
161168
void callWithFunctionAndRagTest() throws IOException {
162-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
169+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
163170
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
164171

165172
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -178,7 +185,7 @@ void callWithFunctionAndRagTest() throws IOException {
178185

179186
@Test
180187
void streamCallWithFunctionAndRagTest() throws InterruptedException, IOException {
181-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
188+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
182189
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
183190

184191
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -187,7 +194,13 @@ void streamCallWithFunctionAndRagTest() throws InterruptedException, IOException
187194
.defaultFunctions("weatherFunction")
188195
.build();
189196

190-
Flux<ChatResponse> response = chatClient.prompt().user("上海今天的天气如何?").stream().chatResponse();
197+
Flux<ChatResponse> response = chatClient.prompt()
198+
.user("上海今天的天气如何?")
199+
.options(DashScopeChatOptions.builder()
200+
.withIncrementalOutput(true)
201+
.build())
202+
.stream()
203+
.chatResponse();
191204

192205
CountDownLatch cdl = new CountDownLatch(1);
193206
response.subscribe(data -> {
@@ -206,7 +219,7 @@ void streamCallWithFunctionAndRagTest() throws InterruptedException, IOException
206219

207220
@Test
208221
void callWithReferencedRagTest() throws IOException {
209-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
222+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
210223
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
211224

212225
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -232,7 +245,7 @@ void callWithReferencedRagTest() throws IOException {
232245

233246
@Test
234247
void streamCallWithReferencedRagTest() throws IOException, InterruptedException {
235-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
248+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
236249
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
237250

238251
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -272,7 +285,7 @@ void streamCallWithReferencedRagTest() throws IOException, InterruptedException
272285

273286
@Test
274287
void callWithMemory() throws IOException {
275-
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeApi,
288+
DocumentRetriever retriever = new DashScopeDocumentRetriever(dashscopeChatApi,
276289
DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());
277290

278291
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
@@ -309,24 +322,24 @@ void callWithMemory() throws IOException {
309322
@Test
310323
void reader() {
311324
String filePath = "/Users/nuocheng.lxm/Desktop/新能源产业有哪些-36氪.pdf";
312-
DashScopeDocumentCloudReader reader = new DashScopeDocumentCloudReader(filePath, dashscopeApi, null);
325+
DashScopeDocumentCloudReader reader = new DashScopeDocumentCloudReader(filePath, dashscopeChatApi, null);
313326
List<Document> documentList = reader.get();
314-
DashScopeDocumentTransformer transformer = new DashScopeDocumentTransformer(dashscopeApi);
327+
DashScopeDocumentTransformer transformer = new DashScopeDocumentTransformer(dashscopeChatApi);
315328
List<Document> transformerList = transformer.apply(documentList);
316329
System.out.println(transformerList.size());
317330
}
318331

319332
@Test
320333
void embed() {
321-
DashScopeEmbeddingModel embeddingModel = new DashScopeEmbeddingModel(dashscopeApi);
334+
DashScopeEmbeddingModel embeddingModel = new DashScopeEmbeddingModel(dashscopeChatApi);
322335
Document document = new Document("你好阿里云");
323336
float[] vectorList = embeddingModel.embed(document);
324337
System.out.println(vectorList.length);
325338
}
326339

327340
@Test
328341
void vectorStore() {
329-
DashScopeCloudStore cloudStore = new DashScopeCloudStore(dashscopeApi, new DashScopeStoreOptions("诺成SpringAI"));
342+
DashScopeCloudStore cloudStore = new DashScopeCloudStore(dashscopeChatApi, new DashScopeStoreOptions("诺成SpringAI"));
330343
List<Document> documentList = Arrays.asList(
331344
new Document("file_f0b6b18b14994ed8a0b45648ce5d0da5_10001", "abc", new HashMap<>()),
332345
new Document("file_d3083d64026d4864b4558d18f9ca2a6d_10001", "abc", new HashMap<>()),

spring-ai-alibaba-examples/playground-flight-booking/frontend/generated/vaadin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ import { Outlet } from 'react-router-dom';
6060
(window as any).Vaadin ??= {};
6161
(window as any).Vaadin.copilot ??= {};
6262
(window as any).Vaadin.copilot._ref ??= {};
63-
(window as any).Vaadin.copilot._ref.Outlet = Outlet;
63+
(window as any).Vaadin.copilot._ref.Outlet = Outlet;

0 commit comments

Comments
 (0)