init
This commit is contained in:
36
jeelowcode-framework/jeelowcode-ai/pom.xml
Normal file
36
jeelowcode-framework/jeelowcode-ai/pom.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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>jeelowcode-framework</artifactId>
|
||||
<groupId>com.jeelowcode</groupId>
|
||||
<version>${jeelowcode.version}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jeelowcode-ai</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${jeelowcode.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
<description> JeeLowCode低代码平台 - ai模块 </description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jeelowcode</groupId>
|
||||
<artifactId>jeelowcode-utils</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.jeelowcode.framework.ai;
|
||||
|
||||
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
|
||||
import com.jeelowcode.framework.ai.config.KimiConfig;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
|
||||
import com.jeelowcode.framework.ai.service.Configuration;
|
||||
import com.jeelowcode.framework.ai.service.IChatService;
|
||||
import com.jeelowcode.framework.ai.service.PlatformType;
|
||||
import com.jeelowcode.framework.ai.service.factor.AiService;
|
||||
import com.jeelowcode.framework.ai.template.CreateTableTemplate;
|
||||
import com.jeelowcode.framework.utils.utils.FuncBase;
|
||||
import lombok.Data;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TestUtils {
|
||||
private IChatService chatService;
|
||||
|
||||
@Before
|
||||
public void test_init() throws NoSuchAlgorithmException, KeyManagementException {
|
||||
KimiConfig kimiConfig = new KimiConfig();
|
||||
kimiConfig.setApiKey("sk-EE98wvDeTFvxRcBxDQmWhKQlewkGFIrfyCxvyNxDj8Vaf701");
|
||||
|
||||
|
||||
DeepSeekConfig deepseekConfig = new DeepSeekConfig();
|
||||
deepseekConfig.setApiKey("sk-b31fcf4e27bf45fbb65ace5bbc627d5d");
|
||||
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.setKimiConfig(kimiConfig);
|
||||
configuration.setDeepSeekConfig(deepseekConfig);
|
||||
|
||||
|
||||
OkHttpClient okHttpClient = new OkHttpClient
|
||||
.Builder()
|
||||
.connectTimeout(300, TimeUnit.SECONDS)
|
||||
.writeTimeout(300, TimeUnit.SECONDS)
|
||||
.readTimeout(300, TimeUnit.SECONDS)
|
||||
.build();
|
||||
configuration.setOkHttpClient(okHttpClient);
|
||||
|
||||
AiService aiService = new AiService(configuration);
|
||||
|
||||
chatService = aiService.getChatService(PlatformType.KIMI);
|
||||
//chatService = aiService.getChatService(PlatformType.DEEPSEEK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void test_chatCompletions_common() throws Exception {
|
||||
|
||||
long time1 = System.currentTimeMillis();
|
||||
|
||||
ChatBaseRequest chatBaseRequest = ChatBaseRequest.builder()
|
||||
//.model("deepseek-chat")
|
||||
.messages(CreateTableTemplate.getMessageTemplateList())
|
||||
.message(ChatMessage.withUser("物流信息,其中要有,物流单号,收件人,寄件人"))
|
||||
.build();
|
||||
|
||||
|
||||
ChatBaseResponse chatBaseResponse = chatService.chatCompletion(chatBaseRequest);
|
||||
List<Choice> choices = chatBaseResponse.getChoices();
|
||||
if(FuncBase.isEmpty(choices)){
|
||||
return;
|
||||
}
|
||||
Choice choice = choices.get(0);
|
||||
ChatMessage message = choice.getMessage();
|
||||
if(FuncBase.isEmpty(message)){
|
||||
return;
|
||||
}
|
||||
String content = message.getContent();
|
||||
System.out.println("content===="+content);
|
||||
CreateTableTemplate.RspModel rspModel = FuncBase.json2Bean(content, CreateTableTemplate.RspModel.class);
|
||||
System.out.println(rspModel);
|
||||
long time2 = System.currentTimeMillis();
|
||||
|
||||
System.out.println("time2-time1="+(time2-time1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jeelowcode.framework.ai.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DeepSeekConfig {
|
||||
|
||||
private String apiHost = "https://api.deepseek.com/";
|
||||
private String apiKey = "";
|
||||
|
||||
private String model="deepseek-chat";
|
||||
|
||||
private String chatCompletionUrl = "chat/completions";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.jeelowcode.framework.ai.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class KimiConfig {
|
||||
private String apiHost = "https://api.moonshot.cn/";
|
||||
private String apiKey = "";
|
||||
private String model="moonshot-v1-8k";
|
||||
private String chatCompletionUrl = "v1/chat/completions";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jeelowcode.framework.ai.convert;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
|
||||
|
||||
public interface ParameterConvert<T> {
|
||||
T convertParam(ChatBaseRequest chatBaseRequest);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jeelowcode.framework.ai.convert;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
|
||||
|
||||
public interface ResultConvert<T> {
|
||||
//统一格式输出
|
||||
ChatBaseResponse convertResponse(T t);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
public class ChatBaseRequest {
|
||||
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@Singular
|
||||
private List<ChatMessage> messages;
|
||||
|
||||
/**
|
||||
* 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。
|
||||
*/
|
||||
@Builder.Default
|
||||
@JsonProperty("frequency_penalty")
|
||||
private Float frequencyPenalty = 0f;
|
||||
|
||||
/**
|
||||
* 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。
|
||||
* 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。
|
||||
*/
|
||||
@Builder.Default
|
||||
private Float temperature = 1f;
|
||||
|
||||
/**
|
||||
* 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。
|
||||
* 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。
|
||||
*/
|
||||
@Builder.Default
|
||||
@JsonProperty("top_p")
|
||||
private Float topP = 1f;
|
||||
|
||||
/**
|
||||
* 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。
|
||||
*/
|
||||
@JsonProperty("max_tokens")
|
||||
private Integer maxTokens;
|
||||
|
||||
/**
|
||||
* 辅助属性
|
||||
*/
|
||||
@JsonIgnore
|
||||
private List<String> functions;
|
||||
|
||||
/**
|
||||
* 控制模型调用 tool 的行为。
|
||||
* none 意味着模型不会调用任何 tool,而是生成一条消息。
|
||||
* auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。
|
||||
* 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。
|
||||
*/
|
||||
@JsonProperty("tool_choice")
|
||||
private String toolChoice;
|
||||
|
||||
@Builder.Default
|
||||
@JsonProperty("parallel_tool_calls")
|
||||
private Boolean parallelToolCalls = true;
|
||||
|
||||
/**
|
||||
* 一个 object,指定模型必须输出的格式。
|
||||
*
|
||||
* 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。
|
||||
*
|
||||
* 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。
|
||||
* 否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。
|
||||
* 此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。
|
||||
*/
|
||||
@JsonProperty("response_format")
|
||||
private Object responseFormat;
|
||||
|
||||
private String user;
|
||||
|
||||
@Builder.Default
|
||||
private Integer n = 1;
|
||||
|
||||
/**
|
||||
* 在遇到这些词时,API 将停止生成更多的 token。
|
||||
*/
|
||||
private List<String> stop;
|
||||
|
||||
/**
|
||||
* 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。
|
||||
*/
|
||||
@Builder.Default
|
||||
@JsonProperty("presence_penalty")
|
||||
private Float presencePenalty = 0f;
|
||||
|
||||
@JsonProperty("logit_bias")
|
||||
private Map logitBias;
|
||||
|
||||
/**
|
||||
* 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。
|
||||
*/
|
||||
@Builder.Default
|
||||
private Boolean logprobs = false;
|
||||
|
||||
/**
|
||||
* 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。
|
||||
*/
|
||||
@JsonProperty("top_logprobs")
|
||||
private Integer topLogprobs;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ChatBaseResponse {
|
||||
/**
|
||||
* 该对话的唯一标识符。
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 对象的类型, 其值为 chat.completion 或 chat.completion.chunk
|
||||
*/
|
||||
private String object;
|
||||
|
||||
/**
|
||||
* 创建聊天完成时的 Unix 时间戳(以秒为单位)。
|
||||
*/
|
||||
private Long created;
|
||||
|
||||
/**
|
||||
* 生成该 completion 的模型名。
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 该指纹代表模型运行时使用的后端配置。
|
||||
*/
|
||||
@JsonProperty("system_fingerprint")
|
||||
private String systemFingerprint;
|
||||
|
||||
/**
|
||||
* 模型生成的 completion 的选择列表。
|
||||
*/
|
||||
private List<Choice> choices;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat;
|
||||
|
||||
import com.jeelowcode.framework.utils.utils.FuncBase;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ChatMessageType {
|
||||
SYSTEM("system"),
|
||||
USER("user"),
|
||||
ASSISTANT("assistant"),
|
||||
TOOL("tool"),
|
||||
;
|
||||
|
||||
private final String role;
|
||||
|
||||
public static ChatMessageType getByRole(String role) {
|
||||
// 使用流来查找匹配的
|
||||
Optional<ChatMessageType> matchingEnum = Arrays.stream(ChatMessageType.values())
|
||||
.filter(chatMessageType -> FuncBase.equals(chatMessageType.getRole(), role))
|
||||
.findFirst(); // findFirst()会返回第一个匹配的元素,或者如果找不到则返回一个空的Optional
|
||||
// 检查是否找到了匹配的枚举项
|
||||
if (matchingEnum.isPresent()) {
|
||||
return matchingEnum.get();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.deepseek;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
|
||||
import com.jeelowcode.framework.ai.convert.ParameterConvert;
|
||||
import com.jeelowcode.framework.ai.convert.ResultConvert;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import com.jeelowcode.framework.ai.platform.chat.deepseek.entity.DeepSeekChatRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.deepseek.entity.DeepSeekChatResponse;
|
||||
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatRequest;
|
||||
import com.jeelowcode.framework.ai.service.Configuration;
|
||||
import com.jeelowcode.framework.ai.service.IChatService;
|
||||
import com.jeelowcode.framework.utils.utils.FuncBase;
|
||||
import okhttp3.*;
|
||||
|
||||
|
||||
public class DeepSeekChatService implements IChatService, ParameterConvert<DeepSeekChatRequest>, ResultConvert<DeepSeekChatResponse> {
|
||||
private final DeepSeekConfig deepSeekConfig;
|
||||
private final OkHttpClient okHttpClient;
|
||||
|
||||
|
||||
public DeepSeekChatService(Configuration configuration) {
|
||||
this.deepSeekConfig = configuration.getDeepSeekConfig();
|
||||
this.okHttpClient = configuration.getOkHttpClient();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DeepSeekChatRequest convertParam(ChatBaseRequest chatBaseRequest) {
|
||||
DeepSeekChatRequest deepSeekChatRequest = DeepSeekChatRequest.builder()
|
||||
.messages(chatBaseRequest.getMessages())
|
||||
.frequencyPenalty(chatBaseRequest.getFrequencyPenalty())
|
||||
.maxTokens(chatBaseRequest.getMaxTokens())
|
||||
.presencePenalty(chatBaseRequest.getPresencePenalty())
|
||||
.responseFormat(chatBaseRequest.getResponseFormat())
|
||||
.stop(chatBaseRequest.getStop())
|
||||
.temperature(chatBaseRequest.getTemperature() / 2)
|
||||
.topP(chatBaseRequest.getTopP())
|
||||
.functions(chatBaseRequest.getFunctions())
|
||||
.toolChoice(chatBaseRequest.getToolChoice())
|
||||
.build();
|
||||
return deepSeekChatRequest;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse convertResponse(DeepSeekChatResponse deepSeekChatResponse) {
|
||||
ChatBaseResponse chatBaseResponse = new ChatBaseResponse();
|
||||
chatBaseResponse.setId(deepSeekChatResponse.getId());
|
||||
chatBaseResponse.setObject(deepSeekChatResponse.getObject());
|
||||
chatBaseResponse.setCreated(deepSeekChatResponse.getCreated());
|
||||
chatBaseResponse.setModel(deepSeekChatResponse.getModel());
|
||||
chatBaseResponse.setSystemFingerprint(deepSeekChatResponse.getSystemFingerprint());
|
||||
chatBaseResponse.setChoices(deepSeekChatResponse.getChoices());
|
||||
return chatBaseResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
baseUrl = FuncBase.isEmpty(baseUrl) ? deepSeekConfig.getApiHost() : baseUrl;
|
||||
apiKey = FuncBase.isEmpty(apiKey) ? deepSeekConfig.getApiKey() : apiKey;
|
||||
|
||||
// 转换 请求参数
|
||||
DeepSeekChatRequest deepSeekChatRequest = this.convertParam(chatBaseRequest);
|
||||
deepSeekChatRequest.setModel(deepSeekConfig.getModel());
|
||||
|
||||
// 构造请求
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String requestString = mapper.writeValueAsString(deepSeekChatRequest);
|
||||
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.url(baseUrl + deepSeekConfig.getChatCompletionUrl())
|
||||
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestString))
|
||||
.build();
|
||||
|
||||
Response execute = okHttpClient.newCall(request).execute();
|
||||
if (FuncBase.isEmpty(execute)) {
|
||||
return null;
|
||||
}
|
||||
if (FuncBase.isEmpty(execute.body())) {
|
||||
return null;
|
||||
}
|
||||
//转为统一格式
|
||||
String rsp = execute.body().string();
|
||||
DeepSeekChatResponse moonshotChatCompletionResponse = FuncBase.json2Bean(rsp, DeepSeekChatResponse.class);
|
||||
return this.convertResponse(moonshotChatCompletionResponse);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
return this.chatCompletion(null, null, chatBaseRequest);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.deepseek.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
public class DeepSeekChatRequest extends ChatBaseRequest {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.deepseek.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 聊天返回结果
|
||||
*/
|
||||
@Data
|
||||
public class DeepSeekChatResponse extends ChatBaseResponse {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.kimi;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.config.KimiConfig;
|
||||
import com.jeelowcode.framework.ai.convert.ParameterConvert;
|
||||
import com.jeelowcode.framework.ai.convert.ResultConvert;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatResponse;
|
||||
import com.jeelowcode.framework.ai.service.Configuration;
|
||||
import com.jeelowcode.framework.ai.service.IChatService;
|
||||
import com.jeelowcode.framework.utils.utils.FuncBase;
|
||||
import okhttp3.*;
|
||||
|
||||
|
||||
public class KimiChatService implements IChatService, ParameterConvert<KimiChatRequest>, ResultConvert<KimiChatResponse> {
|
||||
private final KimiConfig kimiConfig;
|
||||
private final OkHttpClient okHttpClient;
|
||||
|
||||
|
||||
public KimiChatService(Configuration configuration) {
|
||||
this.kimiConfig = configuration.getKimiConfig();
|
||||
this.okHttpClient = configuration.getOkHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KimiChatRequest convertParam(ChatBaseRequest chatBaseRequest) {
|
||||
KimiChatRequest kimiChatRequest = KimiChatRequest.builder()
|
||||
.messages(chatBaseRequest.getMessages())
|
||||
.frequencyPenalty(chatBaseRequest.getFrequencyPenalty())
|
||||
.maxTokens(chatBaseRequest.getMaxTokens())
|
||||
.presencePenalty(chatBaseRequest.getPresencePenalty())
|
||||
.responseFormat(chatBaseRequest.getResponseFormat())
|
||||
.stop(chatBaseRequest.getStop())
|
||||
.temperature(chatBaseRequest.getTemperature() / 2)
|
||||
.topP(chatBaseRequest.getTopP())
|
||||
.functions(chatBaseRequest.getFunctions())
|
||||
.toolChoice(chatBaseRequest.getToolChoice())
|
||||
.build();
|
||||
return kimiChatRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复内容参数转换
|
||||
*/
|
||||
@Override
|
||||
public ChatBaseResponse convertResponse(KimiChatResponse kimiChatResponse) {
|
||||
ChatBaseResponse chatBaseResponse = new ChatBaseResponse();
|
||||
chatBaseResponse.setId(kimiChatResponse.getId());
|
||||
chatBaseResponse.setObject(kimiChatResponse.getObject());
|
||||
chatBaseResponse.setCreated(kimiChatResponse.getCreated());
|
||||
chatBaseResponse.setModel(kimiChatResponse.getModel());
|
||||
chatBaseResponse.setChoices(kimiChatResponse.getChoices());
|
||||
return chatBaseResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步请求
|
||||
*/
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
baseUrl = FuncBase.isEmpty(baseUrl) ? kimiConfig.getApiHost() : baseUrl;
|
||||
apiKey = FuncBase.isEmpty(apiKey) ? kimiConfig.getApiKey() : apiKey;
|
||||
// 转换 请求参数
|
||||
KimiChatRequest kimiChatRequest = this.convertParam(chatBaseRequest);
|
||||
kimiChatRequest.setModel(kimiConfig.getModel());
|
||||
|
||||
// 构造请求
|
||||
String requestString = FuncBase.json2Str(kimiChatRequest);
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestString);
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.url(baseUrl+ kimiConfig.getChatCompletionUrl())
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
Response execute = okHttpClient.newCall(request).execute();
|
||||
if (FuncBase.isEmpty(execute)) {
|
||||
return null;
|
||||
}
|
||||
if (FuncBase.isEmpty(execute.body())) {
|
||||
return null;
|
||||
}
|
||||
//转为统一格式
|
||||
String rsp = execute.body().string();
|
||||
KimiChatResponse kimiChatResponse = FuncBase.json2Bean(rsp, KimiChatResponse.class);
|
||||
return this.convertResponse(kimiChatResponse);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
return this.chatCompletion(null, null, chatBaseRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.kimi.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public class KimiChatRequest extends ChatBaseRequest {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.kimi.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class KimiChatResponse extends ChatBaseResponse {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.tool;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatMessageType;
|
||||
import lombok.*;
|
||||
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatMessageType;
|
||||
import lombok.*;
|
||||
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ChatMessage {
|
||||
private String content;
|
||||
private String role;
|
||||
private String name;
|
||||
private String refusal;
|
||||
|
||||
@JsonProperty("reasoning_content")
|
||||
private String reasoningContent;
|
||||
|
||||
@JsonProperty("tool_call_id")
|
||||
private String toolCallId;
|
||||
|
||||
public ChatMessage(String userMessage) {
|
||||
this.role = ChatMessageType.USER.getRole();
|
||||
this.content =userMessage;
|
||||
}
|
||||
public ChatMessage(ChatMessageType role, String message) {
|
||||
this.role = role.getRole();
|
||||
this.content = message;
|
||||
}
|
||||
public ChatMessage(String role, String message) {
|
||||
this.role = role;
|
||||
this.content = message;
|
||||
}
|
||||
|
||||
public static ChatMessage withSystem(String content) {
|
||||
return new ChatMessage(ChatMessageType.SYSTEM, content);
|
||||
}
|
||||
|
||||
public static ChatMessage withUser(String content) {
|
||||
return new ChatMessage(ChatMessageType.USER, content);
|
||||
}
|
||||
|
||||
public static ChatMessage withAssistant(String content) {
|
||||
return new ChatMessage(ChatMessageType.ASSISTANT, content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.tool;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Choice {
|
||||
private Integer index;
|
||||
|
||||
private ChatMessage delta;
|
||||
private ChatMessage message;
|
||||
|
||||
private Object logprobs;
|
||||
|
||||
/**
|
||||
* 模型停止生成 token 的原因。
|
||||
*
|
||||
* [stop, length, content_filter, tool_calls, insufficient_system_resource]
|
||||
*
|
||||
* stop:模型自然停止生成,或遇到 stop 序列中列出的字符串。
|
||||
* length:输出长度达到了模型上下文长度限制,或达到了 max_tokens 的限制。
|
||||
* content_filter:输出内容因触发过滤策略而被过滤。
|
||||
* tool_calls:函数调用。
|
||||
* insufficient_system_resource:系统推理资源不足,生成被打断。
|
||||
*
|
||||
*/
|
||||
@JsonProperty("finish_reason")
|
||||
private String finishReason;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jeelowcode.framework.ai.platform.chat.tool;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ToString
|
||||
public class Content {
|
||||
private String text; // 纯文本时使用
|
||||
private List<MultiModal> multiModals; // 多模态时使用
|
||||
|
||||
// 纯文本构造方法
|
||||
public static Content ofText(String text) {
|
||||
Content instance = new Content();
|
||||
instance.text = text;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 多模态构造方法
|
||||
public static Content ofMultiModals(List<MultiModal> parts) {
|
||||
Content instance = new Content();
|
||||
instance.multiModals = parts;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 序列化逻辑
|
||||
@JsonValue
|
||||
public Object toJson() {
|
||||
if (text != null) {
|
||||
return text; // 直接返回
|
||||
} else if (multiModals != null) {
|
||||
return multiModals;
|
||||
}
|
||||
throw new IllegalStateException("Invalid content state");
|
||||
}
|
||||
|
||||
public String getText() { return text; }
|
||||
public List<MultiModal> getMultiModals() { return multiModals; }
|
||||
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class MultiModal {
|
||||
private String type = Type.TEXT.type;
|
||||
private String text;
|
||||
@JsonProperty("image_url")
|
||||
private ImageUrl imageUrl;
|
||||
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ImageUrl {
|
||||
private String url;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Type {
|
||||
TEXT("text", "文本类型"),
|
||||
IMAGE_URL("image_url", "图片类型,可以为url或者base64"),
|
||||
;
|
||||
private final String type;
|
||||
private final String info;
|
||||
}
|
||||
|
||||
public static List<MultiModal> withMultiModal(String text, String... imageUrl) {
|
||||
List<MultiModal> messages = new ArrayList<>();
|
||||
messages.add(new MultiModal(Type.TEXT.getType(), text, null));
|
||||
for (String url : imageUrl) {
|
||||
messages.add(new MultiModal(Type.IMAGE_URL.getType(), null, new ImageUrl(url)));
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.jeelowcode.framework.ai.properties;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
|
||||
import com.jeelowcode.framework.ai.config.KimiConfig;
|
||||
import com.jeelowcode.framework.ai.service.factor.AiService;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({
|
||||
DeepSeekConfigProperties.class,
|
||||
KimiConfigProperties.class})
|
||||
|
||||
public class AiConfigAutoConfiguration {
|
||||
|
||||
|
||||
private final DeepSeekConfigProperties deepSeekConfigProperties;
|
||||
private final KimiConfigProperties kimiConfigProperties;
|
||||
|
||||
|
||||
private com.jeelowcode.framework.ai.service.Configuration configuration = new com.jeelowcode.framework.ai.service.Configuration();
|
||||
|
||||
public AiConfigAutoConfiguration(DeepSeekConfigProperties deepSeekConfigProperties, KimiConfigProperties kimiConfigProperties) {
|
||||
this.deepSeekConfigProperties = deepSeekConfigProperties;
|
||||
this.kimiConfigProperties = kimiConfigProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AiService aiService() {
|
||||
return new AiService(configuration);
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
initOkHttp();
|
||||
initDeepSeekConfig();
|
||||
initMoonshotConfig();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void initOkHttp() {
|
||||
// 开启 Http 客户端
|
||||
OkHttpClient.Builder okHttpBuilder = new OkHttpClient
|
||||
.Builder()
|
||||
.connectTimeout(300, TimeUnit.SECONDS)
|
||||
.writeTimeout(300,TimeUnit.SECONDS)
|
||||
.readTimeout(300, TimeUnit.SECONDS);
|
||||
OkHttpClient okHttpClient = okHttpBuilder.build();
|
||||
|
||||
configuration.setOkHttpClient(okHttpClient);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化DeepSeek 配置信息
|
||||
*/
|
||||
private void initDeepSeekConfig(){
|
||||
DeepSeekConfig deepSeekConfig = new DeepSeekConfig();
|
||||
deepSeekConfig.setApiHost(deepSeekConfigProperties.getApiHost());
|
||||
deepSeekConfig.setApiKey(deepSeekConfigProperties.getApiKey());
|
||||
deepSeekConfig.setModel(deepSeekConfigProperties.getModel());
|
||||
deepSeekConfig.setChatCompletionUrl(deepSeekConfigProperties.getChatCompletionUrl());
|
||||
|
||||
configuration.setDeepSeekConfig(deepSeekConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Moonshot 配置信息
|
||||
*/
|
||||
private void initMoonshotConfig() {
|
||||
KimiConfig kimiConfig = new KimiConfig();
|
||||
kimiConfig.setApiHost(kimiConfigProperties.getApiHost());
|
||||
kimiConfig.setApiKey(kimiConfigProperties.getApiKey());
|
||||
kimiConfig.setModel(kimiConfigProperties.getModel());
|
||||
kimiConfig.setChatCompletionUrl(kimiConfigProperties.getChatCompletionUrl());
|
||||
|
||||
configuration.setKimiConfig(kimiConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jeelowcode.framework.ai.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "jeelowcode.ai.deepseek")
|
||||
public class DeepSeekConfigProperties {
|
||||
|
||||
private String apiHost = "https://api.deepseek.com/";
|
||||
private String apiKey = "";
|
||||
private String model="deepseek-chat";
|
||||
private String chatCompletionUrl = "chat/completions";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jeelowcode.framework.ai.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @Author cly
|
||||
* @Description Moonshot配置文件
|
||||
* @Date 2024/8/30 15:56
|
||||
*/
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "jeelowcode.ai.kimi")
|
||||
public class KimiConfigProperties {
|
||||
private String apiHost = "https://api.moonshot.cn/";
|
||||
private String apiKey = "";
|
||||
private String model = "moonshot-v1-8k";
|
||||
private String chatCompletionUrl = "v1/chat/completions";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.jeelowcode.framework.ai.service;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
|
||||
import com.jeelowcode.framework.ai.config.KimiConfig;
|
||||
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGConfig;
|
||||
import lombok.Data;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
|
||||
|
||||
@Data
|
||||
public class Configuration {
|
||||
|
||||
private OkHttpClient okHttpClient;
|
||||
|
||||
private DeepSeekConfig deepSeekConfig;
|
||||
|
||||
private KimiConfig kimiConfig;
|
||||
|
||||
private SearXNGConfig searXNGConfig;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jeelowcode.framework.ai.service;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
|
||||
|
||||
public interface IChatService {
|
||||
|
||||
//同步消息
|
||||
ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception;
|
||||
ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.jeelowcode.framework.ai.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PlatformType {
|
||||
DEEPSEEK("deepseek"),
|
||||
KIMI("kimi"),
|
||||
;
|
||||
private final String platform;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.jeelowcode.framework.ai.service.factor;
|
||||
|
||||
|
||||
import com.jeelowcode.framework.ai.platform.chat.deepseek.DeepSeekChatService;
|
||||
import com.jeelowcode.framework.ai.platform.chat.kimi.KimiChatService;
|
||||
import com.jeelowcode.framework.ai.service.Configuration;
|
||||
import com.jeelowcode.framework.ai.service.IChatService;
|
||||
import com.jeelowcode.framework.ai.service.PlatformType;
|
||||
import com.jeelowcode.framework.ai.websearch.ChatWithWebSearchEnhance;
|
||||
|
||||
|
||||
public class AiService {
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
public AiService(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public IChatService getChatService(PlatformType platform) {
|
||||
return createChatService(platform);
|
||||
}
|
||||
|
||||
public IChatService webSearchEnhance(IChatService chatService) {
|
||||
return new ChatWithWebSearchEnhance(chatService, configuration);
|
||||
}
|
||||
|
||||
private IChatService createChatService(PlatformType platform) {
|
||||
switch (platform) {
|
||||
case KIMI:
|
||||
return new KimiChatService(configuration);
|
||||
case DEEPSEEK:
|
||||
return new DeepSeekChatService(configuration);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown platform: " + platform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jeelowcode.framework.ai.template;
|
||||
|
||||
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CreateTableTemplate {
|
||||
//获取模板
|
||||
public static List<ChatMessage> getMessageTemplateList(){
|
||||
List<ChatMessage> sysMessageList=new ArrayList<>();
|
||||
sysMessageList.add(ChatMessage.withSystem("你是一个mysql数据库设计专家,你需要根据用户的需求理解设计出数据表,然后按特定格式返回"));
|
||||
sysMessageList.add(ChatMessage.withSystem("表信息解析:tableName:表名称,其中必须是tbl_ 开头。 tableDescribe:表中文描述"));
|
||||
sysMessageList.add(ChatMessage.withSystem("字段解析:fieldCode:字段编码,fieldName:字段名称,fieldLen:字段长度,fieldDefaultVal:默认值,fieldType:字段类型"));
|
||||
sysMessageList.add(ChatMessage.withSystem("其中fieldType:字段类型的值有:String=字符串类型,Integer=整型,Date=日期(yyyy-MM-dd),DateTime=日期时间(yyyy-MM-dd HH:mm:ss),Time=时间(HH:mm:ss),BigInt=长整型(long),BigDecimal=小数,LongText=长文本,Blob=二进制"));
|
||||
sysMessageList.add(ChatMessage.withSystem("其中必须有的字段未:id,tenant_id,create_user,create_time,create_dept,update_user,update_time,is_deleted"));
|
||||
sysMessageList.add(ChatMessage.withSystem("这个是返回格式,一定要以json格式返回,其他不必要的内容不要返回:{\"tableName\":\"tbl_name\",\"tableDescribe\":\"学生表\",\"fieldModelList\":[{\"fieldCode\":\"name\",\"fieldName\":\"姓名\",\"fieldLen\":10,\"fieldPointLen\":0,\"fieldDefaultVal\":\"\",\"fieldType\":\"String\"},{\"fieldCode\":\"age\",\"fieldName\":\"年龄\",\"fieldLen\":10,\"fieldPointLen\":0,\"fieldDefaultVal\":\"\",\"fieldType\":\"String\"}]}\n"));
|
||||
sysMessageList.add(ChatMessage.withSystem("返回格式[重要]:只需要返回json格式即可 ,其他不必要的内容不要返回"));
|
||||
|
||||
|
||||
return sysMessageList;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RspModel{
|
||||
private String tableName;//表名称
|
||||
private String tableDescribe;//表描述
|
||||
List<FieldModel> fieldModelList;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class FieldModel{
|
||||
private String fieldCode;//字段编号 例如:name
|
||||
private String fieldName;//字段名称 例如:姓名
|
||||
|
||||
private Integer fieldLen;//字段长度
|
||||
private Integer fieldPointLen;//字段小数位
|
||||
private String fieldDefaultVal;//默认值
|
||||
private String fieldType;//字段类型
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.jeelowcode.framework.ai.websearch;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
|
||||
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
|
||||
import com.jeelowcode.framework.ai.service.Configuration;
|
||||
import com.jeelowcode.framework.ai.service.IChatService;
|
||||
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGConfig;
|
||||
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGRequest;
|
||||
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGResponse;
|
||||
import com.jeelowcode.framework.exception.JeeLowCodeException;
|
||||
import com.jeelowcode.framework.utils.utils.FuncBase;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
||||
public class ChatWithWebSearchEnhance implements IChatService {
|
||||
private final IChatService chatService;
|
||||
private final SearXNGConfig searXNGConfig;
|
||||
private final OkHttpClient okHttpClient;
|
||||
public ChatWithWebSearchEnhance(IChatService chatService, Configuration configuration) {
|
||||
this.chatService = chatService;
|
||||
this.searXNGConfig = configuration.getSearXNGConfig();
|
||||
this.okHttpClient = configuration.getOkHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
return chatService.chatCompletion(baseUrl, apiKey, addWebSearchResults(chatBaseRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
|
||||
return chatService.chatCompletion(addWebSearchResults(chatBaseRequest));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ChatBaseRequest addWebSearchResults(ChatBaseRequest chatBaseRequest) {
|
||||
int chatLen = chatBaseRequest.getMessages().size();
|
||||
String prompt = chatBaseRequest.getMessages().get(chatLen - 1).getContent();
|
||||
// 执行联网搜索并将结果附加到提示词中
|
||||
String searchResults = performWebSearch(prompt);
|
||||
chatBaseRequest.getMessages().get(chatLen - 1).setContent("我将提供一段来自互联网的资料信息, 请根据这段资料以及用户提出的问题来给出回答。请确保在回答中使用Markdown格式,并在回答末尾列出参考资料。如果资料中的信息不足以回答用户的问题,可以根据自身知识库进行补充,或者说明无法提供确切的答案。\n" +
|
||||
"网络资料:\n"
|
||||
+ "============\n"
|
||||
+ searchResults
|
||||
+ "============\n"
|
||||
+ "用户问题:\n"
|
||||
+ "============\n"
|
||||
+ prompt
|
||||
+ "============\n");
|
||||
return chatBaseRequest;
|
||||
}
|
||||
|
||||
private String performWebSearch(String query) {
|
||||
|
||||
SearXNGRequest searXNGRequest = SearXNGRequest.builder()
|
||||
.q(query)
|
||||
.engines(searXNGConfig.getEngines())
|
||||
.build();
|
||||
|
||||
|
||||
if(StringUtils.isBlank(searXNGConfig.getUrl())){
|
||||
throw new JeeLowCodeException("SearXNG url is not configured");
|
||||
}
|
||||
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(searXNGConfig.getUrl()+ "?format=json&q=" + query + "&engines=" + searXNGConfig.getEngines())
|
||||
.get()
|
||||
.build();
|
||||
|
||||
|
||||
try(Response execute = okHttpClient.newCall(request).execute()) {
|
||||
if (execute.isSuccessful() && execute.body() != null){
|
||||
SearXNGResponse searXNGResponse = FuncBase.json2Bean(execute.body().string(), SearXNGResponse.class);
|
||||
|
||||
if(searXNGResponse.getResults().size() > searXNGConfig.getNums()) {
|
||||
return FuncBase.json2Str(searXNGResponse.getResults().subList(0, searXNGConfig.getNums()));
|
||||
}
|
||||
return FuncBase.json2Str(searXNGResponse.getResults());
|
||||
|
||||
|
||||
}else{
|
||||
throw new Exception("SearXNG request failed");
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new JeeLowCodeException("SearXNG request failed");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.jeelowcode.framework.ai.websearch.searxng;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class SearXNGConfig {
|
||||
private String url;
|
||||
private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar";
|
||||
private int nums = 20;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jeelowcode.framework.ai.websearch.searxng;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class SearXNGRequest {
|
||||
@Builder.Default
|
||||
private final String format = "json";
|
||||
|
||||
private String q;
|
||||
|
||||
@Builder.Default
|
||||
private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jeelowcode.framework.ai.websearch.searxng;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class SearXNGResponse {
|
||||
private String query;
|
||||
|
||||
@JsonProperty("number_of_results")
|
||||
private String numberOfResults;
|
||||
|
||||
private List<Result> results;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Result {
|
||||
private String url;
|
||||
private String title;
|
||||
private String content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.jeelowcode.framework.ai.properties.AiConfigAutoConfiguration
|
||||
Reference in New Issue
Block a user