-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Qwen follows the OpenAI-compatible streaming protocol
Environment
Spring AI version: 2.0.0
Java version: 17
Model: qwen3
Mode: stream=true
// //ok
// ChatClient chatClient = ChatClient.builder(chatModel)
// .defaultToolCallbacks(MethodToolCallbackProvider.builder().toolObjects(new MyTools()).build())
// .build();
// var answer = chatClient
// .prompt("What day is tomorrow?")
// .call().content();
// System.out.println(answer);
// error
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultToolCallbacks(MethodToolCallbackProvider.builder().toolObjects(new MyTools()).build())
.build();
var answer = chatClient
.prompt("What day is tomorrow?")
.stream().content();
String content = answer.collectList().block().stream().collect(Collectors.joining());
System.out.println(content);
static class MyTools {
@Tool(description = "Get the current date and time in the user's timezone")
String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
}
logs:
2025-12-18T10:11:40.888+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ COMPLETE
2025-12-18T10:11:40.966+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ: 607B c4
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"content":" call","tool_calls":[]}}]}
c2
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"content":".\n","tool_calls":[]}}]}
c7
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"content":"","tool_calls":[]}}]}
2025-12-18T10:11:40.969+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ COMPLETE
2025-12-18T10:11:41.043+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ: 201B c3
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"content":"\n\n","tool_calls":[]}}]}
2025-12-18T10:11:41.044+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ COMPLETE
2025-12-18T10:11:41.206+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ: 309B 12e
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"tool_calls":[{"id":"chatcmpl-tool-1a81528ba7854bd5af87bcf660b5607a","type":"function","index":0,"function":{"name":"getCurrentDateTime"}}]}}]}
2025-12-18T10:11:41.208+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ COMPLETE
2025-12-18T10:11:41.387+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, READ: 250B dc
data: {"id":"chatcmpl-b3d783b2b520495fb6e3f4f8f4421e3f","object":"chat.completion.chunk","created":1766023897,"model":"Qwen3","choices":[{"index":0,"delta":{"content":"","tool_calls":[]},"finish_reason":"tool_calls"}]}
e
data: [DONE]
0
2025-12-18T10:11:41.388+08:00 DEBUG 27332 --- [reactor-http-nio-2] r.n.http.client.HttpClientOperations : [e2db0bd5-1, Http client inbound receiver cancelled, closing channel.
2025-12-18T10:11:41.389+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5-1, CLOSE
2025-12-18T10:11:41.392+08:00 DEBUG 27332 --- [reactor-http-nio-2] r.n.http.client.HttpClientOperations : [e2db0bd5-1, ] Received last HTTP packet
2025-12-18T10:11:41.393+08:00 WARN 27332 --- [boundedElastic-2] o.s.a.m.tool.DefaultToolCallingManager : Tool call arguments are null or empty for tool: getCurrentDateTime. Using empty JSON object as default.
2025-12-18T10:11:41.393+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5, ] READ COMPLETE
2025-12-18T10:11:41.393+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5, ] INACTIVE
2025-12-18T10:11:41.395+08:00 DEBUG 27332 --- [reactor-http-nio-2] reactor.netty.http.client.HttpClient : [e2db0bd5, ] UNREGISTERED
2025-12-18T10:11:41.404+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a] REGISTERED
2025-12-18T10:11:41.405+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a] CONNECT: /
2025-12-18T10:11:41.434+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a, ] ACTIVE
2025-12-18T10:11:41.434+08:00 DEBUG 27332 --- [reactor-http-nio-3] r.netty.http.client.HttpClientConnect : [40d6b05a-1, ] Handler is being applied: {uri=http:///ai/v1/chat/completions, method=POST}
2025-12-18T10:11:41.437+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a-1, ] WRITE: 194B POST /ai/v1/chat/completions HTTP/1.1
host:
accept: /
Content-Type: application/json
User-Agent: spring-ai
Authorization: Bearer vrv-ai-client
content-length: 739
2025-12-18T10:11:41.438+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a-1, ] WRITE: 739B {"messages":[{"content":"What day is tomorrow?","role":"user"},{"content":"","role":"assistant","tool_calls":[{"id":"chatcmpl-tool-1a81528ba7854bd5af87bcf660b5607a","type":"function","function":{"name":"getCurrentDateTime"}}]},{"content":""2025-12-18T10:11:41.397078900+08:00[Asia/Shanghai]"","role":"tool","name":"getCurrentDateTime","tool_call_id":"chatcmpl-tool-1a81528ba7854bd5af87bcf660b5607a"}],"model":"Qwen3","max_tokens":40960,"stream":true,"tools":[{"function":{"description":"Get the current date and time in the user's timezone","name":"getCurrentDateTime","parameters":{"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false,"type":"object","properties":{},"required":[]}},"type":"function"}]}
2025-12-18T10:11:41.438+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a-1, ] FLUSH
2025-12-18T10:11:41.480+08:00 DEBUG 27332 --- [reactor-http-nio-3] reactor.netty.http.client.HttpClient : [40d6b05a-1, ] READ: 483B HTTP/1.1 400 Bad Request
Server: nginx/1.27.0
Date: Thu, 18 Dec 2025 02:11:41 GMT
Content-Type: application/json
Content-Length: 319
Connection: keep-alive
{"object":"error","message":"1 validation error for ValidatorIterator\n0.function.arguments\n Field required [type=missing, input_value={'name': 'getCurrentDateTime'}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.11/v/missing None","type":"BadRequestError","param":null,"code":400}
2025-12-18T10:11:41.481+08:00 DEBUG 27332 --- [reactor-http-nio-3] r.n.http.client.HttpClientOperations : [40d6b05a-1, ] Received response (auto-read:false) : RESPONSE(decodeResult: success, version: HTTP/1.1)
HTTP/1.1 400 Bad Request
Server:
Date:
Content-Type:
Content-Length:
Connection:
2025-12-18T10:11:41.486+08:00 DEBUG 27332 --- [reactor-http-nio-3] r.n.http.client.HttpClientOperations : [40d6b05a-1, ] Received last HTTP packet
2025-12-18T10:11:41.500+08:00 ERROR 27332 --- [reactor-http-nio-3] o.s.ai.chat.model.MessageAggregator : Aggregation Error
org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest: 400 Bad Request from POST http:///ai/v1/chat/completions
at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:307) ~[spring-webflux-7.0.1.jar:7.0.1]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ 400 BAD_REQUEST from POST http:///ai/v1/chat/completions [DefaultWebClient]
Original Stack Trace:
at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:307) ~[spring-webflux-7.0.1.jar:7.0.1]
at org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:214) ~[spring-webflux-7.0.1.jar:7.0.1]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxOnErrorReturn$ReturnSubscriber.onNext(FluxOnErrorReturn.java:159) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:123) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:130) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:109) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:300) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:338) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2093) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:144) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:263) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.8.0.jar:3.8.0]
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:419) ~[reactor-netty-core-1.3.0.jar:1.3.0]
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:465) ~[reactor-netty-core-1.3.0.jar:1.3.0]
at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:519) ~[reactor-netty-core-1.3.0.jar:1.3.0]
at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:924) ~[reactor-netty-http-1.3.0.jar:1.3.0]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:115) ~[reactor-netty-core-1.3.0.jar:1.3.0]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:356) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-base-4.2.7.Final.jar:4.2.7.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-base-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:354) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) ~[netty-handler-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:354) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:168) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.handle(AbstractNioChannel.java:445) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.NioIoHandler$DefaultNioRegistration.handle(NioIoHandler.java:388) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.NioIoHandler.processSelectedKey(NioIoHandler.java:596) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.NioIoHandler.processSelectedKeysOptimized(NioIoHandler.java:571) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.NioIoHandler.processSelectedKeys(NioIoHandler.java:512) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.nio.NioIoHandler.run(NioIoHandler.java:484) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196) ~[netty-transport-4.2.7.Final.jar:4.2.7.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1193) ~[netty-common-4.2.7.Final.jar:4.2.7.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.2.7.Final.jar:4.2.7.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.2.7.Final.jar:4.2.7.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]