链接
semi-ui-vue聊天组件 - 可以用这个组件优化界面
sse服务端消息推送
webflux&webclient
Hi-Dream-Blog - 参考这个博客,可以在后台将markdown语法转为html
文章目录
- 链接
- 效果
- 代码
- pom.xml
- DeepSeekController
- WebConfig
- DeepSeekClient
- AiChatRequest
- AiChatMessage
- SseApp
效果
代码
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 {@Autowiredprivate 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 {@Overridepublic 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);}
}