|
|
|
@ -1,16 +1,29 @@
|
|
|
|
|
package cn.teammodel.ai.deepseek;
|
|
|
|
|
|
|
|
|
|
import cn.teammodel.ai.SparkGptClient;
|
|
|
|
|
import cn.teammodel.ai.SseHelper;
|
|
|
|
|
import cn.teammodel.ai.cache.HistoryCache;
|
|
|
|
|
import cn.teammodel.ai.domain.SparkChatRequestParam;
|
|
|
|
|
import cn.teammodel.common.ErrorCode;
|
|
|
|
|
import cn.teammodel.common.PK;
|
|
|
|
|
import cn.teammodel.config.exception.ServiceException;
|
|
|
|
|
import cn.teammodel.model.dto.ai.deepseek.ChatRequestOKHttpDto;
|
|
|
|
|
import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto;
|
|
|
|
|
import cn.teammodel.model.dto.ai.ChatCompletionReqDto;
|
|
|
|
|
import cn.teammodel.model.dto.ai.deepseek.DeepSeekChatRequestDto;
|
|
|
|
|
import cn.teammodel.model.dto.ai.deepseek.DeepSeekChatResponse;
|
|
|
|
|
import cn.teammodel.model.dto.ai.deepseek.ChatReqDto;
|
|
|
|
|
import cn.teammodel.model.entity.ai.ChatSession;
|
|
|
|
|
import cn.teammodel.repository.ChatSessionRepository;
|
|
|
|
|
import cn.teammodel.utils.JsonUtil;
|
|
|
|
|
import com.azure.cosmos.models.CosmosPatchOperations;
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
|
|
|
|
import com.google.gson.Gson;
|
|
|
|
|
import com.sun.org.apache.bcel.internal.generic.NEW;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import okhttp3.*;
|
|
|
|
|
import okio.Buffer;
|
|
|
|
|
import okio.BufferedSource;
|
|
|
|
|
import org.apache.http.HttpEntity;
|
|
|
|
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
|
|
|
|
import org.apache.http.client.methods.HttpPost;
|
|
|
|
@ -19,20 +32,35 @@ import org.apache.http.entity.StringEntity;
|
|
|
|
|
import org.apache.http.impl.client.CloseableHttpClient;
|
|
|
|
|
import org.apache.http.impl.client.HttpClients;
|
|
|
|
|
import org.apache.http.util.EntityUtils;
|
|
|
|
|
import org.springframework.util.ObjectUtils;
|
|
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
import java.time.Instant;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
public class DeepSeekClient {
|
|
|
|
|
private static final String API_Key;
|
|
|
|
|
private static final String API_Url;
|
|
|
|
|
public static final String API_Key;
|
|
|
|
|
public static final String API_Url;
|
|
|
|
|
public static String API_Model;
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private static ChatSessionRepository chatSessionRepository;
|
|
|
|
|
|
|
|
|
|
private static final ExecutorService executorService = Executors.newCachedThreadPool();
|
|
|
|
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
/**
|
|
|
|
|
* 读取配置文件 读取key 和url
|
|
|
|
|
*/
|
|
|
|
@ -61,7 +89,7 @@ public class DeepSeekClient {
|
|
|
|
|
msg.add(mssage);
|
|
|
|
|
|
|
|
|
|
//构建请求头
|
|
|
|
|
ChatRequestOKHttpDto requestBody = new ChatRequestOKHttpDto();
|
|
|
|
|
DeepSeekChatRequestDto requestBody = new DeepSeekChatRequestDto();
|
|
|
|
|
requestBody.setModel(API_Model);
|
|
|
|
|
requestBody.setMessages(msg);
|
|
|
|
|
requestBody.setTemperature(0);
|
|
|
|
@ -69,7 +97,7 @@ public class DeepSeekClient {
|
|
|
|
|
|
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
|
//发起请求
|
|
|
|
|
ChatResponseDto response = SendRequests(requestBody);
|
|
|
|
|
DeepSeekChatResponse response = SendRequests(requestBody);
|
|
|
|
|
//Map<String, Object> response = SendRequest(requestBody);
|
|
|
|
|
Long endTime = System.currentTimeMillis();
|
|
|
|
|
//思考耗时 秒
|
|
|
|
@ -85,9 +113,9 @@ public class DeepSeekClient {
|
|
|
|
|
* @param requestBody
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static ChatResponseDto SendRequests(ChatRequestOKHttpDto requestBody)
|
|
|
|
|
public static DeepSeekChatResponse SendRequests(DeepSeekChatRequestDto requestBody)
|
|
|
|
|
{
|
|
|
|
|
ChatResponseDto chatResponse = new ChatResponseDto();
|
|
|
|
|
DeepSeekChatResponse chatResponse = new DeepSeekChatResponse();
|
|
|
|
|
//OkHttpClient client = new OkHttpClient().newBuilder().connectTimeout(60, TimeUnit.SECONDS).build();//设置连接超时时间 1分钟
|
|
|
|
|
OkHttpClient client = new OkHttpClient().newBuilder().build();//设置连接超时时间 1分钟
|
|
|
|
|
|
|
|
|
@ -104,13 +132,36 @@ public class DeepSeekClient {
|
|
|
|
|
.addHeader("Accept", "application/json")
|
|
|
|
|
.addHeader("Authorization", "Bearer "+API_Key)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
try(Response response = client.newCall(request).execute()) {
|
|
|
|
|
if (response.isSuccessful() && response.body() != null) {
|
|
|
|
|
String responseBody = response.body().string();
|
|
|
|
|
StringBuilder responseBody = new StringBuilder();
|
|
|
|
|
try (BufferedSource source = response.body().source()) {
|
|
|
|
|
Buffer buffer = new Buffer();
|
|
|
|
|
while (source.read(buffer, 2048) != -1) {
|
|
|
|
|
// 处理流式数据
|
|
|
|
|
String chunk = buffer.readUtf8();
|
|
|
|
|
if (chunk.startsWith("data:") && !chunk.contains("data: [DONE]")) {
|
|
|
|
|
String[] split = chunk.split("data:");
|
|
|
|
|
for (String result : split) {
|
|
|
|
|
if (StringUtils.hasLength(result) && StringUtils.hasLength(result.trim())) {
|
|
|
|
|
JsonNode jsonNode = objectMapper.readTree(result);
|
|
|
|
|
if (!ObjectUtils.isEmpty(jsonNode.get("choices"))) {
|
|
|
|
|
JsonNode delta = jsonNode.get("choices").get(0).get("delta");
|
|
|
|
|
log.debug("Delta Content: {}", delta.get("content").asText());
|
|
|
|
|
responseBody.append(delta.get("content").asText());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String responseBody1 = response.body().string();
|
|
|
|
|
// 使用 Gson 将 JSON 字符串转换为 MyEntity 对象
|
|
|
|
|
Gson gson = new Gson();
|
|
|
|
|
chatResponse = gson.fromJson(responseBody, ChatResponseDto.class);
|
|
|
|
|
chatResponse = gson.fromJson(responseBody1, DeepSeekChatResponse.class);
|
|
|
|
|
// 确保关闭响应体以释放资源
|
|
|
|
|
response.body().close();
|
|
|
|
|
chatResponse.setCode(200);
|
|
|
|
@ -132,12 +183,145 @@ public class DeepSeekClient {
|
|
|
|
|
return chatResponse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* OkHttpClient 方式请求 流式返回
|
|
|
|
|
* @param requestBody
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static SseEmitter SendRequestsEmitter(DeepSeekChatRequestDto requestBody)
|
|
|
|
|
{
|
|
|
|
|
SseEmitter sseEmitter = new SseEmitter(-1L);
|
|
|
|
|
//OkHttpClient client = new OkHttpClient().newBuilder().connectTimeout(60, TimeUnit.SECONDS).build();//设置连接超时时间 1分钟
|
|
|
|
|
OkHttpClient client = new OkHttpClient().newBuilder().build();//
|
|
|
|
|
|
|
|
|
|
MediaType mediaType = MediaType.parse("application/json");
|
|
|
|
|
//String content = "{\n \"messages\": [\n {\n \"content\": \"You are a helpful assistant\",\n \"role\": \"system\"\n },\n {\n \"content\": \"Hi\",\n \"role\": \"user\"\n }\n ],\n \"model\": \"deepseek-chat\",\n \"frequency_penalty\": 0,\n \"max_tokens\": 2048,\n \"presence_penalty\": 0,\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stop\": null,\n \"stream\": false,\n \"stream_options\": null,\n \"temperature\": 1,\n \"top_p\": 1,\n \"tools\": null,\n \"tool_choice\": \"none\",\n \"logprobs\": false,\n \"top_logprobs\": null\n}";
|
|
|
|
|
String content = JsonUtil.convertToJson(requestBody);
|
|
|
|
|
|
|
|
|
|
RequestBody body = RequestBody.create(mediaType, content);
|
|
|
|
|
Request request = new Request.Builder()
|
|
|
|
|
.url(API_Url)
|
|
|
|
|
.method("POST", body)
|
|
|
|
|
.addHeader("Content-Type", "application/json")
|
|
|
|
|
.addHeader("Accept", "application/json")
|
|
|
|
|
.addHeader("Authorization", "Bearer "+API_Key)
|
|
|
|
|
.build();
|
|
|
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
try(Response response = client.newCall(request).execute()) {
|
|
|
|
|
if (response.isSuccessful() && response.body() != null) {
|
|
|
|
|
StringBuilder responseBody = new StringBuilder();
|
|
|
|
|
try (BufferedSource source = response.body().source()) {
|
|
|
|
|
Buffer buffer = new Buffer();
|
|
|
|
|
while (source.read(buffer, 2048) != -1) {
|
|
|
|
|
// 处理流式数据
|
|
|
|
|
String chunk = buffer.readUtf8();
|
|
|
|
|
if (chunk.startsWith("data:") && !chunk.contains("data: [DONE]")) {
|
|
|
|
|
String[] split = chunk.split("data:");
|
|
|
|
|
for (String result : split) {
|
|
|
|
|
if (StringUtils.hasLength(result) && StringUtils.hasLength(result.trim())) {
|
|
|
|
|
JsonNode jsonNode = objectMapper.readTree(result);
|
|
|
|
|
if (!ObjectUtils.isEmpty(jsonNode.get("choices"))) {
|
|
|
|
|
JsonNode delta = jsonNode.get("choices").get(0).get("delta");
|
|
|
|
|
log.debug("Delta Content: {}", delta.get("content").asText());
|
|
|
|
|
sseEmitter.send(delta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}catch (IOException e) {
|
|
|
|
|
sseEmitter.completeWithError(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
sseEmitter.completeWithError(new Exception("请求DeepSeek服务器失败"));
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
sseEmitter.completeWithError(e);
|
|
|
|
|
}
|
|
|
|
|
return sseEmitter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* HttpClient 方式请求
|
|
|
|
|
* @param chatCompletionReqDto
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static SseEmitter HttpClientSendRequests(ChatCompletionReqDto chatCompletionReqDto){
|
|
|
|
|
SseEmitter emitter = new SseEmitter(-1L);
|
|
|
|
|
List<ChatReqDto> msg = new ArrayList<>();
|
|
|
|
|
msg.add(new ChatReqDto(chatCompletionReqDto.getSessionId(), "user", chatCompletionReqDto.getText()));
|
|
|
|
|
//构建请求头
|
|
|
|
|
DeepSeekChatRequestDto requestBody = new DeepSeekChatRequestDto();
|
|
|
|
|
requestBody.setModel(DeepSeekClient.API_Model);
|
|
|
|
|
requestBody.setMessages(msg);
|
|
|
|
|
requestBody.setTemperature(0);
|
|
|
|
|
requestBody.setStream(true);
|
|
|
|
|
|
|
|
|
|
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
|
|
|
|
// 创建HttpPost对象
|
|
|
|
|
HttpPost httpPost = new HttpPost(API_Url);
|
|
|
|
|
//添加请求头
|
|
|
|
|
httpPost.setHeader("Content-Type", "application/json");
|
|
|
|
|
httpPost.setHeader("Accept", "application/json");
|
|
|
|
|
httpPost.setHeader("Authorization", "Bearer " + API_Key);
|
|
|
|
|
|
|
|
|
|
requestBody.setStream(true);
|
|
|
|
|
// 设置请求体
|
|
|
|
|
String jsonContent = JsonUtil.convertToJson(requestBody);
|
|
|
|
|
httpPost.setEntity(new StringEntity(jsonContent, ContentType.create("application/json", "UTF-8")));
|
|
|
|
|
StringBuilder responseBody = new StringBuilder();
|
|
|
|
|
try (CloseableHttpResponse response = httpClient.execute(httpPost);
|
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))) {
|
|
|
|
|
String line;
|
|
|
|
|
StringBuilder strContent = new StringBuilder();
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
if (line.startsWith("data: ")) {
|
|
|
|
|
String jsonData = line.substring(6);
|
|
|
|
|
if ("[DONE]".equals(jsonData)) {
|
|
|
|
|
//SseHelper.send(emitter, "[DONE]");
|
|
|
|
|
emitter.send("[DONE]");
|
|
|
|
|
// 更新历史会话记录
|
|
|
|
|
ChatSession.Message message = ChatSession.Message.of(chatCompletionReqDto.getText(), strContent.toString(),chatCompletionReqDto.getModel());
|
|
|
|
|
HistoryCache.updateContext(chatCompletionReqDto.getSessionId(), message);
|
|
|
|
|
CosmosPatchOperations options = CosmosPatchOperations.create()
|
|
|
|
|
.replace("/updateTime", Instant.now().toEpochMilli())
|
|
|
|
|
.add("/history/-", message);
|
|
|
|
|
chatSessionRepository.save(chatCompletionReqDto.getSessionId(), PK.of(PK.CHAT_SESSION), ChatSession.class, options);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
JsonNode node = objectMapper.readTree(jsonData);
|
|
|
|
|
String content = node.path("choices")
|
|
|
|
|
.path(0)
|
|
|
|
|
.path("delta")
|
|
|
|
|
.path("content")
|
|
|
|
|
.asText("");
|
|
|
|
|
if (!content.isEmpty()) {
|
|
|
|
|
responseBody.append(content);
|
|
|
|
|
strContent.append(content);
|
|
|
|
|
emitter.send(content);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emitter.complete();
|
|
|
|
|
}catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
emitter.completeWithError(e);
|
|
|
|
|
}
|
|
|
|
|
}catch (Exception e) {
|
|
|
|
|
emitter.completeWithError(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return emitter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***
|
|
|
|
|
* HttpClient 方式请求
|
|
|
|
|
* @param requestBody
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static Map<String, Object> SendRequest(ChatRequestOKHttpDto requestBody) {
|
|
|
|
|
public static Map<String, Object> SendRequest(DeepSeekChatRequestDto requestBody) {
|
|
|
|
|
Map<String, Object> mapper = new HashMap<>();
|
|
|
|
|
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
|
|
|
|
// 创建HttpPost对象
|
|
|
|
@ -175,8 +359,4 @@ public class DeepSeekClient {
|
|
|
|
|
//TODO 请求接口
|
|
|
|
|
return mapper;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|