deepseek & sse流式输出

发布于:2025-02-26 ⋅ 阅读:(16) ⋅ 点赞:(0)

链接

semi-ui-vue聊天组件 - 可以用这个组件优化界面

sse服务端消息推送

webflux&webclient

Hi-Dream-Blog - 参考这个博客,可以在后台将markdown语法转为html

效果

在这里插入图片描述
在这里插入图片描述

代码

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <artifactId>spring-boot-dependencies</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.1.8.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>demo-sse</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.10</version>
        </dependency>
    </dependencies>

</project>

DeepSeekController

@RestController
@RequestMapping("/deepseek")
public class DeepSeekController {

    @Autowired
    private DeepSeekClient deepSeekClient;

    @RequestMapping(value = "chatCompletions", produces = "text/event-stream;charset=utf-8")
    public Flux<String> chatCompletions(@RequestParam(required = true, value = "content") String content) {
        return deepSeekClient.chatCompletions(content);
    }

	@RequestMapping(value = "chatCompletions2", produces = "text/event-stream;charset=utf-8")
    public SseEmitter chatCompletions2(@RequestParam(required = true, value = "content2") String content2) {
        return deepSeekClient.chatCompletions2(content2);
    }

}

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                .maxAge(3600)
                .allowCredentials(true)
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("token", "Authorization")
        ;
    }

}

DeepSeekClient

package com.zzhua.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zzhua.pojo.AiChatMessage;
import com.zzhua.pojo.AiChatRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.Arrays;

@Slf4j
@Component
public class DeepSeekClient {

    private static final ObjectMapper mapper = new ObjectMapper();

    public Flux<String> chatCompletions(String content) {

        AiChatMessage chatMsg1 = new AiChatMessage("system", "You are a helpful assistant.");
        AiChatMessage chatMsg2 = new AiChatMessage("user", content);

        AiChatRequest request = new AiChatRequest();
        request.setModel("deepseek-chat");
        request.setMessages(Arrays.asList(chatMsg1, chatMsg2));
        // 流式输出
        request.setStream(true);

        return WebClient.builder()
                .baseUrl("https://api.deepseek.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader("Authorization", "Bearer sk-xxx")
                .build()
                .post()
                .uri("/chat/completions")
                .body(BodyInserters.fromObject(request))
                .retrieve()
                .bodyToFlux(String.class)
                .flatMap(this::handleResult);
    }

    private Flux<String> handleResult(String result) {
        if ("[DONE]".equals(result)) {
            return Flux.empty();
        } else {
            try {
                JsonNode jsonNode = mapper.readTree(result);
                String content = jsonNode.get("choices").get(0).get("delta").get("content").asText();
                System.out.println(content);
                return Flux.just(content);
            } catch (Exception e) {
                log.error("解析失败: {}", result);
            }
        }
        return Flux.empty();
    }

	public SseEmitter chatCompletions2(String content2) {

        SseEmitter sseEmitter = new SseEmitter();

        AiChatMessage chatMsg1 = new AiChatMessage("system", "You are a helpful assistant.");
        AiChatMessage chatMsg2 = new AiChatMessage("user", content2);

        AiChatRequest request = new AiChatRequest();
        request.setModel("deepseek-chat");
        request.setMessages(Arrays.asList(chatMsg1, chatMsg2));
        // 流式输出
        request.setStream(true);

        WebClient.builder()
                .baseUrl("https://api.deepseek.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader("Authorization", "Bearer sk-xxx")
                .build()
                .post()
                .uri("/chat/completions")
                .body(BodyInserters.fromObject(request))
                .retrieve()
                .bodyToFlux(String.class)
                .flatMap(this::handleResult)
                .doOnComplete(()->{
                    sseEmitter.complete();
                    log.warn("结束");
                })
                .subscribe(data->{
                    try {
                        sseEmitter.send(SseEmitter.event().data(data));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });

        return sseEmitter;
    }

    public static void main(String[] args) throws IOException {

        RestTemplate restTemplate = new RestTemplate();
        ObjectMapper mapper = new ObjectMapper();


        AiChatMessage chatMsg1 = new AiChatMessage("system", "You are a helpful assistant.");
        AiChatMessage chatMsg2 = new AiChatMessage("user", "");

        AiChatRequest request = new AiChatRequest();
        request.setModel("deepseek-chat");
        request.setMessages(Arrays.asList(chatMsg1, chatMsg2));
        request.setStream(false);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("Authorization", "Bearer sk-xxx");
        HttpEntity<AiChatRequest> requestEntity = new HttpEntity<>(request, headers);

        String response = restTemplate.exchange("https://api.deepseek.com/chat/completions",
                HttpMethod.POST,
                requestEntity,
                String.class
        ).getBody();

        System.out.println(response);
        System.out.println(mapper.readTree(response).get("choices").get(0).get("message").get("content"));


    }
}

AiChatRequest

@Data
public class AiChatRequest {

    private String model;

    private List<AiChatMessage> messages;

    private boolean stream;



}

AiChatMessage

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AiChatMessage {
    private String role;
    private String content;
}

SseApp

@SpringBootApplication
public class SseApp {
    public static void main(String[] args) {
        SpringApplication.run(SseApp.class, args);
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到