Skip to content

带有toolcall的流式请求报错 #84

@kennard520

Description

@kennard520

implementation 'io.github.pig-mesh.ai:deepseek-spring-boot-starter:1.4.6'
代码如下:
` @GetMapping(value = "/chat/advanced", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux chatAdvanced(String prompt, String cacheCode) {

    ChatCompletionRequest request = ChatCompletionRequest.builder().model(model)
            .messages(UserMessage.from(prompt), SystemMessage.from("You are a device maintenance and repair assistant. When the user asks about device maintenance or repair, call the get_knowledge_from_es function to retrieve relevant information from Elasticsearch. If the function is called, respond based on the function's return results; if the function is not called, respond directly. If the response references knowledge from the knowledge base and sourceUrl has a value, add a footnote marker (e.g., [1]) in the text and append the footnote content in the format `[fileName](sourceUrl)` at the end of the response. If there is no sourceUrl, do not add any footnotes. Always provide accurate and helpful responses"))
            .tools(WEATHER_TOOL).build();
    // 只保留上一次回答内容
    cache.remove(cacheCode);
    StringBuffer sb = new StringBuffer();
    AtomicBoolean isToolCallInProgress = new AtomicBoolean(false);
    return deepSeekClient.chatFluxCompletion(request)
            .filter(r->{
                List<ChatCompletionChoice> choices = r.choices();
                if (choices.isEmpty()) {
                    return false;
                }
                ChatCompletionChoice chatCompletionChoice = choices.get(0);
                Delta delta = chatCompletionChoice.delta();
                if (Objects.isNull(delta)) return false;
                String content = delta.content();
                if (!StringUtils.isNotBlank(content)) return false;
                return true;
            })
            .concatMap(response -> {
                log.info("response {}", response);
                Delta delta = response.choices().get(0).delta();
                // 情况2:工具调用进行中(忽略所有中间文本)
                if (isToolCallInProgress.get()) {
                    List<ToolCall> toolCalls = delta.toolCalls();
                    if (!CollectionUtils.isEmpty(toolCalls)){
                        String arguments = toolCalls.get(0).function().arguments();
                        sb.append(arguments);
                    }
                    return Flux.empty();
                }

                // 情况1:检测到工具调用开始
                if (delta.toolCalls() != null) {
                    isToolCallInProgress.set(true);
                    return Flux.empty();
                }
                // 情况3:正常返回最终结果
                return Flux.just(response);

            }).doFinally(r -> log.info("arguments {}", sb.toString()))
            .doOnError(e -> log.error("/chat/advanced error:{}", e.getMessage()));
}`

报错如下:
2025-04-12T10:58:00.799+08:00 ERROR 25152 --- [ppinfra.com/...] org.ai.aidemo.controller.AiController : /chat/advanced error:com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot coerce empty String ("") to io.github.pigmesh.ai.deepseek.core.chat.ToolTypevalue (but could if coercion was enabled usingCoercionConfig) at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 238] (through reference chain: io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionResponse$Builder["choices"]->java.util.ArrayList[0]->io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionChoice$Builder["delta"]->io.github.pigmesh.ai.deepseek.core.chat.Delta$Builder["tool_calls"]->java.util.ArrayList[0]->io.github.pigmesh.ai.deepseek.core.chat.ToolCall$Builder["type"])
2025-04-12T10:58:00.799+08:00 INFO 25152 --- [ppinfra.com/...] org.ai.aidemo.controller.AiController : arguments
2025-04-12T10:58:01.381+08:00 ERROR 25152 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot coerce empty String ("") to io.github.pigmesh.ai.deepseek.core.chat.ToolType value (but could if coercion was enabled using CoercionConfig)
at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 238] (through reference chain: io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionResponse$Builder["choices"]->java.util.ArrayList[0]->io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionChoice$Builder["delta"]->io.github.pigmesh.ai.deepseek.core.chat.Delta$Builder["tool_calls"]->java.util.ArrayList[0]->io.github.pigmesh.ai.deepseek.core.chat.ToolCall$Builder["type"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadCoercion(DeserializationContext.java:1832) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._checkCoercionFail(StdDeserializer.java:1683) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._deserializeAltString(EnumDeserializer.java:389) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._fromString(EnumDeserializer.java:304) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:273) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:294) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:218) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:294) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:218) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:294) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:218) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:294) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:218) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4931) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3868) ~[jackson-databind-2.18.3.jar:2.18.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3836) ~[jackson-databind-2.18.3.jar:2.18.3]
at io.github.pigmesh.ai.deepseek.core.Json.fromJson(Json.java:25) ~[deepseek4j-core-1.4.3.jar:na]
at io.github.pigmesh.ai.deepseek.core.StreamingRequestExecutor$2.onEvent(StreamingRequestExecutor.java:157) ~[deepseek4j-core-1.4.3.jar:na]
at okhttp3.internal.sse.RealEventSource.onEvent(RealEventSource.kt:101) ~[okhttp-sse-4.12.0.jar:na]
at okhttp3.internal.sse.ServerSentEventReader.completeEvent(ServerSentEventReader.kt:108) ~[okhttp-sse-4.12.0.jar:na]
at okhttp3.internal.sse.ServerSentEventReader.processNextEvent(ServerSentEventReader.kt:52) ~[okhttp-sse-4.12.0.jar:na]
at okhttp3.internal.sse.RealEventSource.processResponse(RealEventSource.kt:75) ~[okhttp-sse-4.12.0.jar:na]
at okhttp3.internal.sse.RealEventSource.onResponse(RealEventSource.kt:46) ~[okhttp-sse-4.12.0.jar:na]
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519) ~[okhttp-4.12.0.jar:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]`

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis doesn't seem right

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions