x
This commit is contained in:
commit
12aa452792
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
91
pom.xml
Normal file
91
pom.xml
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.hxy</groupId>
|
||||||
|
<artifactId>demoDeepSeek</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>demoDeepSeek</name>
|
||||||
|
<description>demoDeepSeek</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<spring-boot.version>3.0.2</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>17</source>
|
||||||
|
<target>17</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.hxy.demodeepseek.DemoDeepSeekApplication</mainClass>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>repackage</id>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.hxy.demodeepseek;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class DemoDeepSeekApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DemoDeepSeekApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.hxy.demodeepseek.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "deepseek")
|
||||||
|
@Data
|
||||||
|
public class DeepSeekConfig {
|
||||||
|
private String apiUrl;
|
||||||
|
private String apiKey;
|
||||||
|
}
|
25
src/main/java/com/hxy/demodeepseek/config/OpenAPIConfig.java
Normal file
25
src/main/java/com/hxy/demodeepseek/config/OpenAPIConfig.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.hxy.demodeepseek.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://localhost:8080/swagger-ui/index.html
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class OpenAPIConfig {
|
||||||
|
@Bean
|
||||||
|
public OpenAPI openAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(new Info()
|
||||||
|
.title("接口文档标题")
|
||||||
|
.description("SpringBoot3 集成 Swagger3接口文档")
|
||||||
|
.version("v1"))
|
||||||
|
.externalDocs(new ExternalDocumentation()
|
||||||
|
.description("项目API文档")
|
||||||
|
.url("/"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.hxy.demodeepseek.controller;
|
||||||
|
import com.hxy.demodeepseek.service.DeepSeekService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class DeepSeekController {
|
||||||
|
@Autowired
|
||||||
|
private DeepSeekService deepSeekService;
|
||||||
|
|
||||||
|
@GetMapping("/chat")
|
||||||
|
public Mono<String> sendChatRequest(String question) {
|
||||||
|
Flux<String> response = deepSeekService.sendChatRequest(question);
|
||||||
|
// 将 Flux 中的元素收集到一个列表中
|
||||||
|
Mono<List<String>> listMono = response.collectList();
|
||||||
|
|
||||||
|
// 将列表中的元素拼接成一个字符串
|
||||||
|
Mono<String> resultMono = listMono.map(list -> {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String str : list) {
|
||||||
|
sb.append(str);
|
||||||
|
}
|
||||||
|
System.out.println("resultMono = " + sb);
|
||||||
|
return sb.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
return resultMono;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
17
src/main/java/com/hxy/demodeepseek/model/ChatRequest.java
Normal file
17
src/main/java/com/hxy/demodeepseek/model/ChatRequest.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.hxy.demodeepseek.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
@Data
|
||||||
|
public class ChatRequest {
|
||||||
|
private String model = "deepseek-ai/DeepSeek-V3";
|
||||||
|
private List<Message> messages;
|
||||||
|
private boolean stream = true;
|
||||||
|
private int max_tokens = 2048;
|
||||||
|
private double temperature = 0.7;
|
||||||
|
private double top_p = 0.7;
|
||||||
|
private int top_k = 50;
|
||||||
|
private double frequency_penalty = 0.5;
|
||||||
|
private int n = 1;
|
||||||
|
private ResponseFormat response_format = new ResponseFormat("text");
|
||||||
|
}
|
7
src/main/java/com/hxy/demodeepseek/model/Message.java
Normal file
7
src/main/java/com/hxy/demodeepseek/model/Message.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.hxy.demodeepseek.model;
|
||||||
|
import lombok.Data;
|
||||||
|
@Data
|
||||||
|
public class Message {
|
||||||
|
private String role;
|
||||||
|
private String content;
|
||||||
|
}
|
17
src/main/java/com/hxy/demodeepseek/model/ResponseFormat.java
Normal file
17
src/main/java/com/hxy/demodeepseek/model/ResponseFormat.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.hxy.demodeepseek.model;
|
||||||
|
|
||||||
|
public class ResponseFormat {
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public ResponseFormat(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.hxy.demodeepseek.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.hxy.demodeepseek.config.DeepSeekConfig;
|
||||||
|
import com.hxy.demodeepseek.model.ChatRequest;
|
||||||
|
import com.hxy.demodeepseek.model.Message;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.netty.http.client.PrematureCloseException;
|
||||||
|
import reactor.util.retry.Retry;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DeepSeekService {
|
||||||
|
@Autowired
|
||||||
|
private DeepSeekConfig config;
|
||||||
|
@Autowired
|
||||||
|
private WebClient.Builder webClientBuilder;
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
|
||||||
|
public Flux<String> sendChatRequest(String question) {
|
||||||
|
ChatRequest request = new ChatRequest();
|
||||||
|
Message userMessage = new Message();
|
||||||
|
userMessage.setRole("user");
|
||||||
|
userMessage.setContent(question);
|
||||||
|
request.setMessages(Collections.singletonList(userMessage));
|
||||||
|
|
||||||
|
System.out.println("发送请求到: "+ config.getApiUrl());
|
||||||
|
|
||||||
|
return webClientBuilder.build()
|
||||||
|
.post()
|
||||||
|
.uri(config.getApiUrl())
|
||||||
|
.header("Authorization", "Bearer " + config.getApiKey())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.bodyValue(request)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToFlux(String.class)
|
||||||
|
.timeout(Duration.ofSeconds(300))
|
||||||
|
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
|
||||||
|
.filter(throwable ->
|
||||||
|
throwable instanceof PrematureCloseException
|
||||||
|
|| throwable instanceof TimeoutException
|
||||||
|
|| throwable instanceof RuntimeException)
|
||||||
|
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
|
||||||
|
System.out.println("重试次数已用完,最后一次错误: "+ retrySignal.failure().getMessage());
|
||||||
|
return new RuntimeException("服务暂时不可用,请稍后重试");
|
||||||
|
}))
|
||||||
|
.onErrorResume(e -> {
|
||||||
|
System.out.println("请求处理错误: "+ e.getMessage());
|
||||||
|
return Flux.just("抱歉,服务器处理请求时发生错误: " + e.getMessage());
|
||||||
|
})
|
||||||
|
.map(response -> {
|
||||||
|
try {
|
||||||
|
if ("[DONE]".equals(response)) {
|
||||||
|
return "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode jsonNode = objectMapper.readTree(response);
|
||||||
|
JsonNode choices = jsonNode.get("choices");
|
||||||
|
if (choices != null && choices.isArray() && choices.size() > 0) {
|
||||||
|
JsonNode choice = choices.get(0);
|
||||||
|
JsonNode delta = choice.get("delta");
|
||||||
|
if (delta != null && delta.has("content")) {
|
||||||
|
String content = delta.get("content").asText();
|
||||||
|
return content != null ? content : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("解析响应时出错: "+ e.getMessage());
|
||||||
|
System.out.println("原始响应: "+ response);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(content -> !content.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
10
src/main/resources/application.yml
Normal file
10
src/main/resources/application.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: demoDeepSeek
|
||||||
|
mvc:
|
||||||
|
async:
|
||||||
|
request-timeout: 360000
|
||||||
|
deepseek:
|
||||||
|
api-url: https://api.siliconflow.cn/v1/chat/completions
|
||||||
|
api-key: sk-ldfhfwuwgskxzrskydasvjvqemctwonwuvqqfljaoomcjoro
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.hxy.demodeepseek;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class DemoDeepSeekApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user