链接
semi-ui-vue聊天组件 - 可以用这个组件优化界面
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);
}
}