From 0b05cc958729bc76f4513b97a4bf8616b38dc270 Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Sun, 4 Feb 2024 23:13:13 +0800
Subject: [PATCH 001/350] =?UTF-8?q?=E4=BF=AE=E6=94=B9openai=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=EF=BC=8C=E8=AE=A9openai=E5=8F=AF=E4=BB=A5=E4=BD=BF?=
=?UTF-8?q?=E7=94=A8function=E7=9A=84=E6=96=B9=E5=BC=8F=E8=8E=B7=E5=8F=96?=
=?UTF-8?q?=E8=A1=A8=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/ChatInput/index.tsx | 7 +-
.../components/SelectBoundInfo/index.tsx | 2 +-
.../web/api/controller/ai/ChatController.java | 105 ++++++++++-
.../chat2db/client/Chat2DBAIStreamClient.java | 6 -
.../ai/openai/client/OpenAIClient.java | 13 +-
.../listener/OpenAIEventSourceListener.java | 166 +++++++++++++++---
.../ai/chat2db/spi/sql/Chat2DBContext.java | 1 +
chat2db-server/pom.xml | 2 +-
8 files changed, 255 insertions(+), 47 deletions(-)
diff --git a/chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.tsx b/chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.tsx
index 592755979..997b8bd3d 100644
--- a/chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.tsx
+++ b/chat2db-client/src/components/ConsoleEditor/components/ChatInput/index.tsx
@@ -42,18 +42,17 @@ const ChatInput = (props: IProps) => {
};
const renderSelectTable = () => {
- const { tables, onSelectTableSyncModel, selectedTables, onSelectTables } = props;
+ const { tables, onSelectTableSyncModel, selectedTables, onSelectTables,syncTableModel } = props;
const options = (tables || []).map((t) => ({ value: t, label: t }));
return (
onSelectTableSyncModel(v.target.value)}
- // value={syncTableModel}
- value={SyncModelType.MANUAL}
+ value={syncTableModel}
style={{ marginBottom: '8px' }}
>
- {/* 自动 */}
+ 自动
手动
diff --git a/chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.tsx b/chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.tsx
index e4318a2a9..4be3b4b20 100644
--- a/chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.tsx
+++ b/chat2db-client/src/components/ConsoleEditor/components/SelectBoundInfo/index.tsx
@@ -186,7 +186,7 @@ const SelectBoundInfo = memo((props: IProps) => {
boundInfo.databaseName,
boundInfo.schemaName,
);
- setSelectedTables(tableNameListTemp.slice(0, 1));
+ //setSelectedTables(tableNameListTemp.slice(0, 1));
}
}, [allTableList, isActive]);
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index c9e77806f..aab53bdaf 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -54,11 +54,20 @@
import ai.chat2db.server.web.api.http.response.EsTableSchemaResponse;
import ai.chat2db.server.web.api.http.response.TableSchemaResponse;
import ai.chat2db.server.web.api.util.ApplicationContextUtil;
+import ai.chat2db.spi.MetaData;
+import ai.chat2db.spi.model.Table;
+import ai.chat2db.spi.sql.Chat2DBContext;
+import ai.chat2db.spi.sql.ConnectInfo;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
+import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.Message;
+import com.unfbx.chatgpt.entity.chat.Parameters;
+import com.unfbx.chatgpt.entity.chat.tool.Tools;
+import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -171,7 +180,7 @@ public SseEmitter customChat(@RequestBody ChatRequest queryRequest) throws IOExc
/**
* 自定义模型非流式输出接口DEMO
*
- * Note:使用自己本地的飞流式输出自定义AI,接口输入和输出需与该样例保持一致
+ * Note:使用自己本地的飞流式输出自定义AI,接口输入和输出需与该样例保持一致
*
*
* @param queryRequest
@@ -276,11 +285,11 @@ private SseEmitter chatWithRestAi(ChatQueryRequest prompt, SseEmitter sseEmitter
* @throws IOException
*/
private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)
- throws IOException {
- String prompt = buildPrompt(queryRequest);
+ throws IOException {
+ String prompt = buildPrompt2(queryRequest);
if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
- prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
+ prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
throw new ParamBusinessException();
}
@@ -290,9 +299,28 @@ private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseE
Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
messages.add(currentMessage);
buildSseEmitter(sseEmitter, uid);
-
- OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter);
- OpenAIClient.getInstance().streamChatCompletion(messages, openAIEventSourceListener);
+ ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();
+ OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter, messages, connectInfo, queryRequest);
+ ToolsFunction function = ToolsFunction.builder()
+ .name("get_table_columns")
+ .description("获取指定表的字段名,类型")
+ .parameters(Parameters.builder()
+ .type("object")
+ .properties(ImmutableMap.builder()
+ .put("table_name", ImmutableMap.builder()
+ .put("type", "string")
+ .put("description", "表名,例如```User```")
+ .build())
+ .build())
+ .required(List.of("table_name"))
+ .build())
+ .build();
+ ChatCompletion chatCompletion = ChatCompletion.builder()
+ .model("gpt-3.5-turbo-1106")
+ .tools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)))
+ .toolChoice("auto")
+ .messages(messages).stream(true).build();
+ OpenAIClient.getInstance().streamChatCompletion(chatCompletion, openAIEventSourceListener);
LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
return sseEmitter;
}
@@ -630,6 +658,47 @@ private String buildPrompt(ChatQueryRequest queryRequest) {
return cleanedInput;
}
+ /**
+ * 构建prompt
+ *
+ * @param queryRequest
+ * @return
+ */
+ private String buildPrompt2(ChatQueryRequest queryRequest) {
+ if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
+ return queryRequest.getMessage();
+ }
+
+ // 查询schema信息
+ String dataSourceType = queryDatabaseType(queryRequest);
+ String properties = "";
+ if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
+ properties = queryRequest.getTableNames().stream().collect(Collectors.joining(","));
+ } else {
+ properties = queryDatabaseSchema2(queryRequest);
+ }
+ String prompt = queryRequest.getMessage();
+ String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
+ : queryRequest.getPromptType();
+ PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
+ String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
+ String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
+ "### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables:\n#\n# "
+ + "%s\n#\n#\n### SQL input: %s", pType.getDescription(), ext, dataSourceType,
+ properties, prompt) : String.format("### 请根据以下SQL input%s. %s\n#\n### SQL input: %s",
+ pType.getDescription(), ext, prompt);
+ switch (pType) {
+ case SQL_2_SQL:
+ schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, queryRequest.getDestSqlType()) : String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, dataSourceType);
+ default:
+ break;
+ }
+ String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
+ return cleanedInput;
+ }
+
/**
* query chat2db apikey
*
@@ -727,6 +796,28 @@ public String queryDatabaseSchema(ChatQueryRequest queryRequest) {
}
}
+
+ /**
+ * query database schema
+ *
+ * @param queryRequest
+ * @return
+ * @throws IOException
+ */
+ public String queryDatabaseSchema2(ChatQueryRequest queryRequest) {
+ MetaData metaSchema = Chat2DBContext.getMetaData();
+ try {
+ List
tables = metaSchema.tables(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), null);
+ return tables.stream()
+ .map(table -> StringUtils.isBlank(table.getComment()) ? table.getName()
+ : table.getName() + "(" + table.getComment() + ")")
+ .collect(Collectors.joining(","));
+ } catch (Exception e) {
+ log.error("query table error:{}, do nothing", e.getMessage());
+ return "";
+ }
+ }
+
/**
* query database schema
*
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java
index 0f0b6d84f..295d39cff 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java
@@ -1,21 +1,15 @@
package ai.chat2db.server.web.api.controller.ai.chat2db.client;
-import ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;
-import ai.chat2db.server.domain.api.model.Config;
-import ai.chat2db.server.domain.api.service.ConfigService;
-import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
import ai.chat2db.server.web.api.controller.ai.chat2db.interceptor.Chat2dbHeaderAuthorizationInterceptor;
import ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatOpenAiApi;
import ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbedding;
import ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;
-import ai.chat2db.server.web.api.util.ApplicationContextUtil;
import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.Message;
-import com.unfbx.chatgpt.interceptor.HeaderAuthorizationInterceptor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java
index 9ebf711c2..1d3de3bc7 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java
@@ -4,6 +4,7 @@
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import ai.chat2db.server.domain.api.model.Config;
import ai.chat2db.server.domain.api.service.ConfigService;
@@ -93,7 +94,17 @@ public static void refresh() {
log.info("refresh openai apikey:{}", maskApiKey(apikey));
if (Objects.nonNull(host) && Objects.nonNull(port)) {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
- OkHttpClient okHttpClient = new OkHttpClient.Builder().proxy(proxy).build();
+ OkHttpClient okHttpClient = new OkHttpClient.Builder()
+ // 设置连接超时为10秒
+ .connectTimeout(10, TimeUnit.SECONDS)
+ // 设置读取超时为30秒
+ .readTimeout(30, TimeUnit.SECONDS)
+ // 设置写入超时为15秒
+ .writeTimeout(15, TimeUnit.SECONDS)
+ // 设置整个调用的超时为1分钟
+ .callTimeout(1, TimeUnit.MINUTES)
+ .proxy(proxy)
+ .build();
OPEN_AI_STREAM_CLIENT = OpenAiStreamClient.builder().apiHost(apiHost).apiKey(
Lists.newArrayList(apikey)).okHttpClient(okHttpClient).build();
} else {
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index ccadf6d68..099fd76b5 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -1,12 +1,18 @@
package ai.chat2db.server.web.api.controller.ai.openai.listener;
-import java.util.Objects;
-
+import ai.chat2db.server.web.api.controller.ai.openai.client.OpenAIClient;
+import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.ai.response.ChatCompletionResponse;
-
+import ai.chat2db.spi.MetaData;
+import ai.chat2db.spi.sql.Chat2DBContext;
+import ai.chat2db.spi.sql.ConnectInfo;
+import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.unfbx.chatgpt.entity.chat.BaseMessage;
import com.unfbx.chatgpt.entity.chat.Message;
+import com.unfbx.chatgpt.entity.chat.tool.ToolCallFunction;
+import com.unfbx.chatgpt.entity.chat.tool.ToolCalls;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
@@ -15,6 +21,10 @@
import okhttp3.sse.EventSourceListener;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
/**
* 描述:OpenAIEventSourceListener
*
@@ -24,10 +34,78 @@
@Slf4j
public class OpenAIEventSourceListener extends EventSourceListener {
- private SseEmitter sseEmitter;
+ private final SseEmitter sseEmitter;
+
+ private final List messages;
- public OpenAIEventSourceListener(SseEmitter sseEmitter) {
+ private final ConnectInfo connectInfo;
+
+ private final ChatQueryRequest queryRequest;
+
+ private List toolCalls = new ArrayList<>();
+
+
+ public OpenAIEventSourceListener(SseEmitter sseEmitter, List messages, ConnectInfo connectInfo, ChatQueryRequest queryRequest) {
this.sseEmitter = sseEmitter;
+ this.messages = messages;
+ this.connectInfo = connectInfo;
+ this.queryRequest = queryRequest;
+ }
+
+ public static List mergeToolCallsLists(List list1, List list2) {
+ List mergedList = new ArrayList<>(list1);
+ if (list2.isEmpty()) {
+ return mergedList;
+ }
+ ToolCalls item2 = list2.get(0);
+ boolean isMerged = false;
+ // 反向遍历
+ for (int i = list1.size() - 1; i >= 0; i--) {
+ ToolCalls item1 = list1.get(i);
+ if (item2.getId() == null || Objects.equals(item1.getId(), item2.getId())) {
+ mergedList.set(i, mergeToolCalls(item1, item2));
+ isMerged = true;
+ break;
+ }
+ }
+ if (!isMerged) {
+ // 如果 list2 中的对象与 list1 中的任何对象都不匹配,则作为新对象添加
+ mergedList.add(item2);
+ }
+ return mergedList;
+ }
+
+ private static ToolCalls mergeToolCalls(ToolCalls tc1, ToolCalls tc2) {
+ if (tc1 == null) return tc2;
+ if (tc2 == null) return tc1;
+
+ // 相同的逻辑,只是当 id 为 null 时进行合并
+ String id = tc1.getId() != null ? tc1.getId() : tc2.getId();
+ String type = mergeStrings(tc1.getType(), tc2.getType());
+ ToolCallFunction function = mergeToolCallFunctions(tc1.getFunction(), tc2.getFunction());
+
+ return new ToolCalls(id, type, function);
+ }
+
+ private static ToolCallFunction mergeToolCallFunctions(ToolCallFunction f1, ToolCallFunction f2) {
+ if (f1 == null) return f2;
+ if (f2 == null) return f1;
+
+ String name = mergeStrings(f1.getName(), f2.getName());
+ String arguments = mergeStrings(f1.getArguments(), f2.getArguments());
+
+ return new ToolCallFunction(name, arguments);
+ }
+
+ private static String mergeStrings(String str1, String str2) {
+ if (str1 != null && str2 != null) {
+ // Concatenate both strings
+ return str1 + str2;
+ } else if (str1 != null) {
+ return str1;
+ } else {
+ return str2;
+ }
}
/**
@@ -46,35 +124,69 @@ public void onOpen(EventSource eventSource, Response response) {
public void onEvent(EventSource eventSource, String id, String type, String data) {
log.info("OpenAI返回数据:{}", data);
if (data.equals("[DONE]")) {
- log.info("OpenAI返回数据结束了");
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]")
- .reconnectTime(3000));
- sseEmitter.complete();
+ if (toolCalls.isEmpty()) {
+ log.info("OpenAI返回数据结束了");
+ sseEmitter.send(SseEmitter.event()
+ .id("[DONE]")
+ .data("[DONE]")
+ .reconnectTime(3000));
+ sseEmitter.complete();
+ return;
+ }
+ messages.add(Message.builder()
+ .toolCalls(toolCalls)
+ .role(BaseMessage.Role.ASSISTANT).build());
+ Chat2DBContext.putContext(connectInfo);
+ try {
+ for (ToolCalls toolCall : toolCalls) {
+ String callId = toolCall.getId();
+ ToolCallFunction function = toolCall.getFunction();
+ if (function != null && Objects.nonNull(function.getArguments())) {
+ String functionName = function.getName();
+ JSONObject arguments = JSONObject.parse(function.getArguments());
+ if ("get_table_columns".equals(functionName)) {
+ MetaData metaSchema = Chat2DBContext.getMetaData();
+ String ddl = metaSchema.tableDDL(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), arguments.getString("table_name"));
+ messages.add(Message.builder().role(BaseMessage.Role.TOOL)
+ .toolCallId(callId)
+ .name(functionName)
+ .content(ddl)
+ .build());
+ }
+ }
+ }
+ } finally {
+ Chat2DBContext.removeContext();
+ }
+ OpenAIClient.getInstance().streamChatCompletion(messages, this);
+ toolCalls.clear();
return;
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 读取Json
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);
- String text = completionResponse.getChoices().get(0).getDelta() == null
- ? completionResponse.getChoices().get(0).getText()
- : completionResponse.getChoices().get(0).getDelta().getContent();
+ Message delta = completionResponse.getChoices().get(0).getDelta();
+ if (delta != null && delta.getToolCalls() != null) {
+ this.toolCalls = mergeToolCallsLists(this.toolCalls, delta.getToolCalls());
+ }
+ String text = delta == null
+ ? completionResponse.getChoices().get(0).getText()
+ : delta.getContent();
Message message = new Message();
if (text != null) {
message.setContent(text);
sseEmitter.send(SseEmitter.event()
- .id(completionResponse.getId())
- .data(message)
- .reconnectTime(3000));
+ .id(completionResponse.getId())
+ .data(message)
+ .reconnectTime(3000));
}
}
@Override
public void onClosed(EventSource eventSource) {
- sseEmitter.complete();
- log.info("OpenAI关闭sse连接...");
+// sseEmitter.complete();
+// log.info("OpenAI关闭sse连接...");
}
@Override
@@ -88,11 +200,11 @@ public void onFailure(EventSource eventSource, Throwable t, Response response) {
Message sseMessage = new Message();
sseMessage.setContent(message);
sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(sseMessage));
+ .id("[ERROR]")
+ .data(sseMessage));
sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
+ .id("[DONE]")
+ .data("[DONE]"));
sseEmitter.complete();
return;
}
@@ -108,11 +220,11 @@ public void onFailure(EventSource eventSource, Throwable t, Response response) {
Message message = new Message();
message.setContent("出现异常,请在帮助中查看详细日志:" + bodyString);
sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(message));
+ .id("[ERROR]")
+ .data(message));
sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
+ .id("[DONE]")
+ .data("[DONE]"));
sseEmitter.complete();
} catch (Exception exception) {
log.error("发送数据异常:", exception);
diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java
index 9e6fce81a..88183d9ad 100644
--- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java
+++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/Chat2DBContext.java
@@ -142,6 +142,7 @@ public static void removeContext() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
+ connectInfo.setConnection(null);
}
} catch (SQLException e) {
log.error("close connection error", e);
diff --git a/chat2db-server/pom.xml b/chat2db-server/pom.xml
index 16c693477..b5b0cf43a 100644
--- a/chat2db-server/pom.xml
+++ b/chat2db-server/pom.xml
@@ -222,7 +222,7 @@
com.unfbx
chatgpt-java
- 1.0.8
+ 1.1.5
org.slf4j
From 1b84afbd4814e0088b9bcce4e89fbc3e4df0091f Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Mon, 5 Feb 2024 09:05:49 +0800
Subject: [PATCH 002/350] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=B9=E9=94=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/ai/chat2db/plugin/mysql/MysqlMetaData.java | 9 +++++++--
.../ai/openai/listener/OpenAIEventSourceListener.java | 11 +++++++++--
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlMetaData.java b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlMetaData.java
index 40a291955..d08cc4a6a 100644
--- a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlMetaData.java
+++ b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlMetaData.java
@@ -33,8 +33,13 @@ public List databases(Connection connection) {
@Override
public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
@NotEmpty String tableName) {
- String sql = "SHOW CREATE TABLE " + format(databaseName) + "."
- + format(tableName);
+ String sql;
+ if(StringUtils.isEmpty(databaseName)) {
+ sql = "SHOW CREATE TABLE " + format(tableName);
+ }else{
+ sql = "SHOW CREATE TABLE " + format(databaseName) + "."
+ + format(tableName);
+ }
return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
if (resultSet.next()) {
return resultSet.getString("Create Table");
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index 099fd76b5..e30ff1c21 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -19,6 +19,7 @@
import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.ArrayList;
@@ -146,11 +147,17 @@ public void onEvent(EventSource eventSource, String id, String type, String data
JSONObject arguments = JSONObject.parse(function.getArguments());
if ("get_table_columns".equals(functionName)) {
MetaData metaSchema = Chat2DBContext.getMetaData();
- String ddl = metaSchema.tableDDL(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), arguments.getString("table_name"));
+ String content;
+ try {
+ content = metaSchema.tableDDL(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), arguments.getString("table_name"));
+ }catch (Exception e){
+ log.error("OpenAI查询表结构失败",e);
+ content = StringUtils.defaultString(e.getMessage(), "OpenAI查询表结构失败");
+ }
messages.add(Message.builder().role(BaseMessage.Role.TOOL)
.toolCallId(callId)
.name(functionName)
- .content(ddl)
+ .content(content)
.build());
}
}
From 16f3dc24e6572ae8d5dfa98e2a80b93d970d3a0d Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Wed, 7 Feb 2024 15:20:37 +0800
Subject: [PATCH 003/350] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=91=98=E8=A6=81?=
=?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F=EF=BC=8C=E4=B9=8B=E5=89=8D?=
=?UTF-8?q?=E7=94=A8=E5=AE=98=E7=BD=91=E7=9A=84=E5=A4=AA=E6=B5=AA=E8=B4=B9?=
=?UTF-8?q?token=E4=BA=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/api/controller/ai/ChatController.java | 384 ++----------------
.../controller/ai/EmbeddingController.java | 6 +-
.../controller/ai/KnowledgeController.java | 4 +-
.../ai/TextGenerationController.java | 4 +-
.../listener/OpenAIEventSourceListener.java | 69 ++--
.../controller/ai/utils/PromptService.java | 364 +++++++++++++++++
6 files changed, 432 insertions(+), 399 deletions(-)
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index aab53bdaf..ff93fcbd4 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -1,5 +1,7 @@
package ai.chat2db.server.web.api.controller.ai;
+
+
import ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;
import ai.chat2db.server.domain.api.model.Config;
import ai.chat2db.server.domain.api.model.DataSource;
@@ -11,6 +13,8 @@
import ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
+import ai.chat2db.server.tools.common.model.LoginUser;
+import ai.chat2db.server.tools.common.util.ContextUtils;
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;
@@ -41,6 +45,7 @@
import ai.chat2db.server.web.api.controller.ai.rest.listener.RestAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.tongyi.client.TongyiChatAIClient;
import ai.chat2db.server.web.api.controller.ai.tongyi.listener.TongyiChatAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.utils.PromptService;
import ai.chat2db.server.web.api.controller.ai.wenxin.client.WenxinAIClient;
import ai.chat2db.server.web.api.controller.ai.wenxin.listener.WenxinAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;
@@ -68,6 +73,7 @@
import com.unfbx.chatgpt.entity.chat.Parameters;
import com.unfbx.chatgpt.entity.chat.tool.Tools;
import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
+import com.unfbx.chatgpt.entity.chat.BaseChatCompletion.Model;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -99,14 +105,6 @@
@Slf4j
public class ChatController {
- @Autowired
- private TableService tableService;
-
- @Autowired
- private ChatConverter chatConverter;
-
- @Autowired
- private DataSourceService dataSourceService;
@Value("${chatgpt.context.length}")
private Integer contextLength;
@@ -117,6 +115,10 @@ public class ChatController {
@Resource
private GatewayClientService gatewayClientService;
+
+ @Resource
+ protected PromptService promptService;
+
/**
* chat的超时时间
*/
@@ -271,7 +273,7 @@ public SseEmitter distributeAISql(ChatQueryRequest queryRequest, SseEmitter sseE
*/
private SseEmitter chatWithRestAi(ChatQueryRequest prompt, SseEmitter sseEmitter) {
RestAIEventSourceListener eventSourceListener = new RestAIEventSourceListener(sseEmitter);
- RestAIClient.getInstance().restCompletions(buildPrompt(prompt), eventSourceListener);
+ RestAIClient.getInstance().restCompletions(promptService.buildPrompt(prompt), eventSourceListener);
return sseEmitter;
}
@@ -286,7 +288,7 @@ private SseEmitter chatWithRestAi(ChatQueryRequest prompt, SseEmitter sseEmitter
*/
private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)
throws IOException {
- String prompt = buildPrompt2(queryRequest);
+ String prompt = promptService.buildAutoPrompt(queryRequest);
if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
@@ -299,9 +301,12 @@ private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseE
Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
messages.add(currentMessage);
buildSseEmitter(sseEmitter, uid);
- ConnectInfo connectInfo = Chat2DBContext.getConnectInfo();
- OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter, messages, connectInfo, queryRequest);
- ToolsFunction function = ToolsFunction.builder()
+ LoginUser loginUser = ContextUtils.getLoginUser();
+ OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter, promptService, queryRequest,loginUser);
+ ChatCompletion chatCompletion = ChatCompletion.builder()
+ .messages(messages).stream(true).build();
+ if(queryRequest.getDatabaseName()!=null){
+ ToolsFunction function = ToolsFunction.builder()
.name("get_table_columns")
.description("获取指定表的字段名,类型")
.parameters(Parameters.builder()
@@ -315,11 +320,10 @@ private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseE
.required(List.of("table_name"))
.build())
.build();
- ChatCompletion chatCompletion = ChatCompletion.builder()
- .model("gpt-3.5-turbo-1106")
- .tools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)))
- .toolChoice("auto")
- .messages(messages).stream(true).build();
+ chatCompletion.setModel("gpt-3.5-turbo-0125");
+ chatCompletion.setTools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)));
+ chatCompletion.setToolChoice("auto");
+ }
OpenAIClient.getInstance().streamChatCompletion(chatCompletion, openAIEventSourceListener);
LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
return sseEmitter;
@@ -336,7 +340,7 @@ private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseE
*/
private SseEmitter chatWithChat2dbAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)
throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
log.error("exceed max token length:{},input length:{}", MAX_PROMPT_LENGTH,
prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
@@ -366,7 +370,7 @@ private SseEmitter chatWithChat2dbAi(ChatQueryRequest queryRequest, SseEmitter s
* @throws IOException
*/
private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
@@ -401,7 +405,7 @@ private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sse
* @throws IOException
*/
private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -422,7 +426,7 @@ private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter
* @throws IOException
*/
private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -443,7 +447,7 @@ private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter
* @throws IOException
*/
private SseEmitter chatWithTongyiChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -464,7 +468,7 @@ private SseEmitter chatWithTongyiChatAi(ChatQueryRequest queryRequest, SseEmitte
* @throws IOException
*/
private SseEmitter chatWithBaichuanAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -506,7 +510,7 @@ private List getFastChatMessage(String uid, String prompt) {
* @throws IOException
*/
private SseEmitter chatWithWenxinAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
if (messages.size() >= 2 && messages.size() % 2 == 0) {
messages.remove(messages.size() - 1);
@@ -531,7 +535,7 @@ private SseEmitter chatWithWenxinAi(ChatQueryRequest queryRequest, SseEmitter ss
* @throws IOException
*/
private SseEmitter chatWithClaudeAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = buildPrompt(queryRequest);
+ String prompt = promptService.buildPrompt(queryRequest);
ClaudeChatMessage claudeChatMessage = new ClaudeChatMessage();
claudeChatMessage.setText(prompt);
ClaudeChatCompletionsOptions chatCompletionsOptions = new ClaudeChatCompletionsOptions();
@@ -574,333 +578,5 @@ private SseEmitter buildSseEmitter(SseEmitter sseEmitter, String uid) throws IOE
return sseEmitter;
}
- /**
- * 构建schema参数
- *
- * @param tableQueryParam
- * @param tableNames
- * @return
- */
- private String buildTableColumn(TableQueryParam tableQueryParam,
- List tableNames) {
- if (CollectionUtils.isEmpty(tableNames)) {
- return "";
- }
- List schemaContent = Lists.newArrayList();
- try {
- schemaContent = tableNames.stream().map(tableName -> {
- tableQueryParam.setTableName(tableName);
- return queryTableDdl(tableName, tableQueryParam);
- }).collect(Collectors.toList());
- } catch (Exception exception) {
- log.error("query table error, do nothing");
- }
-
- return JSON.toJSONString(schemaContent);
- }
-
- /**
- * query table schema
- *
- * @param tableName
- * @param request
- * @return
- */
- private String queryTableDdl(String tableName, TableQueryParam request) {
- ShowCreateTableParam param = new ShowCreateTableParam();
- param.setTableName(tableName);
- param.setDataSourceId(request.getDataSourceId());
- param.setDatabaseName(request.getDatabaseName());
- param.setSchemaName(request.getSchemaName());
- DataResult tableSchema = tableService.showCreateTable(param);
- return tableSchema.getData();
- }
-
- /**
- * 构建prompt
- *
- * @param queryRequest
- * @return
- */
- private String buildPrompt(ChatQueryRequest queryRequest) {
- if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
- return queryRequest.getMessage();
- }
-
- // 查询schema信息
- String dataSourceType = queryDatabaseType(queryRequest);
- String properties = "";
- if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
- TableQueryParam queryParam = chatConverter.chat2tableQuery(queryRequest);
- properties = buildTableColumn(queryParam, queryRequest.getTableNames());
- } else {
- properties = mappingDatabaseSchema(queryRequest);
- }
- String prompt = queryRequest.getMessage();
- String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
- : queryRequest.getPromptType();
- PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
- String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
- String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
- "### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables, with their properties:\n#\n# "
- + "%s\n#\n#\n### SQL input: %s", pType.getDescription(), ext, dataSourceType,
- properties, prompt) : String.format("### 请根据以下SQL input%s. %s\n#\n### SQL input: %s",
- pType.getDescription(), ext, prompt);
- switch (pType) {
- case SQL_2_SQL:
- schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(
- "%s\n#\n### 目标SQL类型: %s", schemaProperty, queryRequest.getDestSqlType()) : String.format(
- "%s\n#\n### 目标SQL类型: %s", schemaProperty, dataSourceType);
- default:
- break;
- }
- String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
- return cleanedInput;
- }
-
- /**
- * 构建prompt
- *
- * @param queryRequest
- * @return
- */
- private String buildPrompt2(ChatQueryRequest queryRequest) {
- if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
- return queryRequest.getMessage();
- }
-
- // 查询schema信息
- String dataSourceType = queryDatabaseType(queryRequest);
- String properties = "";
- if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
- properties = queryRequest.getTableNames().stream().collect(Collectors.joining(","));
- } else {
- properties = queryDatabaseSchema2(queryRequest);
- }
- String prompt = queryRequest.getMessage();
- String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
- : queryRequest.getPromptType();
- PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
- String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
- String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
- "### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables:\n#\n# "
- + "%s\n#\n#\n### SQL input: %s", pType.getDescription(), ext, dataSourceType,
- properties, prompt) : String.format("### 请根据以下SQL input%s. %s\n#\n### SQL input: %s",
- pType.getDescription(), ext, prompt);
- switch (pType) {
- case SQL_2_SQL:
- schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(
- "%s\n#\n### 目标SQL类型: %s", schemaProperty, queryRequest.getDestSqlType()) : String.format(
- "%s\n#\n### 目标SQL类型: %s", schemaProperty, dataSourceType);
- default:
- break;
- }
- String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
- return cleanedInput;
- }
-
- /**
- * query chat2db apikey
- *
- * @return
- */
- public String getApiKey() {
- ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
- Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();
- String aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();
- // only sync for chat2db ai
- if (Objects.isNull(config) || !aiSqlSource.equals(config.getContent())) {
- return null;
- }
- Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();
- if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {
- return null;
- }
- return keyConfig.getContent();
- }
-
- /**
- * query database type
- *
- * @param queryRequest
- * @return
- */
- public String queryDatabaseType(ChatQueryRequest queryRequest) {
- // 查询schema信息
- DataResult dataResult = dataSourceService.queryById(queryRequest.getDataSourceId());
- String dataSourceType = dataResult.getData().getType();
- if (StringUtils.isBlank(dataSourceType)) {
- dataSourceType = "MYSQL";
- }
- return dataSourceType;
- }
-
- public String mappingDatabaseSchema(ChatQueryRequest queryRequest) {
- String properties = "";
- String apiKey = getApiKey();
- if (StringUtils.isNotBlank(apiKey)) {
- boolean res = gatewayClientService.checkInWhite(new WhiteListRequest(apiKey, WhiteListTypeEnum.VECTOR.getCode())).getData();
- if (res) {
-// properties = queryDatabaseSchema(queryRequest) + querySchemaByEs(queryRequest);
- properties = queryDatabaseSchema(queryRequest);
- }
- }
- return properties;
- }
-
- /**
- * query database schema
- *
- * @param queryRequest
- * @return
- * @throws IOException
- */
- public String queryDatabaseSchema(ChatQueryRequest queryRequest) {
- // request embedding
- FastChatEmbeddingResponse response = distributeAIEmbedding(queryRequest.getMessage());
- List> contentVector = new ArrayList<>();
- if (Objects.isNull(response) || CollectionUtils.isEmpty(response.getData())) {
- return "";
- }
- contentVector.add(response.getData().get(0).getEmbedding());
-
- // search embedding
- TableSchemaRequest tableSchemaRequest = new TableSchemaRequest();
- tableSchemaRequest.setSchemaVector(contentVector);
- tableSchemaRequest.setDataSourceId(queryRequest.getDataSourceId());
- tableSchemaRequest.setDatabaseName(queryRequest.getDatabaseName());
- tableSchemaRequest.setDataSourceSchema(queryRequest.getSchemaName());
- ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
- Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();
- if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {
- return "";
- }
- tableSchemaRequest.setApiKey(keyConfig.getContent());
- try {
- DataResult result = gatewayClientService.schemaVectorSearch(tableSchemaRequest);
- List schemas = Lists.newArrayList();
- if (Objects.nonNull(result.getData()) && CollectionUtils.isNotEmpty(result.getData().getTableSchemas())) {
- for(TableSchema data: result.getData().getTableSchemas()){
- schemas.add(data.getTableSchema());
- }
- }
- if (CollectionUtils.isEmpty(schemas)) {
- return "";
- }
- String res = JSON.toJSONString(schemas);
- log.info("search vector result:{}", res);
- return res;
- } catch (Exception exception) {
- log.error("query table error, do nothing");
- return "";
- }
- }
-
-
- /**
- * query database schema
- *
- * @param queryRequest
- * @return
- * @throws IOException
- */
- public String queryDatabaseSchema2(ChatQueryRequest queryRequest) {
- MetaData metaSchema = Chat2DBContext.getMetaData();
- try {
- List tables = metaSchema.tables(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), null);
- return tables.stream()
- .map(table -> StringUtils.isBlank(table.getComment()) ? table.getName()
- : table.getName() + "(" + table.getComment() + ")")
- .collect(Collectors.joining(","));
- } catch (Exception e) {
- log.error("query table error:{}, do nothing", e.getMessage());
- return "";
- }
- }
-
- /**
- * query database schema
- *
- * @param queryRequest
- * @return
- * @throws IOException
- */
- public String querySchemaByEs(ChatQueryRequest queryRequest) {
- // search embedding
- EsTableSchemaRequest tableSchemaRequest = new EsTableSchemaRequest();
- tableSchemaRequest.setSearchKey(queryRequest.getMessage());
- tableSchemaRequest.setDataSourceId(queryRequest.getDataSourceId());
- tableSchemaRequest.setDatabaseName(queryRequest.getDatabaseName());
- tableSchemaRequest.setSchemaName(queryRequest.getSchemaName());
- ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
- Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();
- if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {
- return "";
- }
- tableSchemaRequest.setApiKey(keyConfig.getContent());
- try {
- DataResult result = gatewayClientService.schemaEsSearch(tableSchemaRequest);
- List schemas = Lists.newArrayList();
- if (Objects.nonNull(result.getData()) && CollectionUtils.isNotEmpty(result.getData().getTableSchemas())) {
- for(EsTableSchema data: result.getData().getTableSchemas()){
- schemas.add(data.getTableSchemaContent());
- }
- }
- if (CollectionUtils.isEmpty(schemas)) {
- return "";
- }
- String res = JSON.toJSONString(schemas);
- log.info("search es result:{}", res);
- return res;
- } catch (Exception exception) {
- log.error("query es table error, do nothing");
- return "";
- }
- }
-
- /**
- * distribute embedding with different AI
- *
- * @return
- */
- public FastChatEmbeddingResponse distributeAIEmbedding(String input) {
- ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
- Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();
- String aiSqlSource = config.getContent();
- if (Objects.isNull(aiSqlSource)) {
- return null;
- }
- AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);
- switch (Objects.requireNonNull(aiSqlSourceEnum)) {
- case CHAT2DBAI:
- return embeddingWithChat2dbAi(input);
- case FASTCHATAI:
- return embeddingWithFastChatAi(input);
- }
- return null;
- }
-
- /**
- * embedding with fast chat openai
- *
- * @param input
- * @return
- * @throws IOException
- */
- private FastChatEmbeddingResponse embeddingWithFastChatAi(String input) {
- FastChatEmbeddingResponse response = FastChatAIClient.getInstance().embeddings(input);
- return response;
- }
-
- /**
- * embedding with open ai
- *
- * @param input
- * @return
- */
- private FastChatEmbeddingResponse embeddingWithChat2dbAi(String input) {
- FastChatEmbeddingResponse embeddings = Chat2dbAIClient.getInstance().embeddings(input);
- return embeddings;
- }
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java
index c8c694309..70df31c6f 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java
@@ -242,7 +242,7 @@ public void syncTableVector(TableBriefQueryRequest param) throws Exception {
return;
}
- String apiKey = getApiKey();
+ String apiKey = promptService.getApiKey();
if (StringUtils.isBlank(apiKey)) {
return;
}
@@ -281,7 +281,7 @@ private void saveTableEmbedding(String tableSchema, TableSchemaRequest tableSche
List> contentVector = new ArrayList<>();
for(String str : schemaList){
// request embedding
- FastChatEmbeddingResponse response = distributeAIEmbedding(str);
+ FastChatEmbeddingResponse response = promptService.distributeAIEmbedding(str);
if(response == null){
throw new ParamBusinessException();
}
@@ -310,7 +310,7 @@ public void syncTableEs(TableBriefQueryRequest param) throws Exception {
return;
}
- String apiKey = getApiKey();
+ String apiKey = promptService.getApiKey();
if (StringUtils.isBlank(apiKey)) {
return;
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java
index 6ff16ee09..6ef0731ac 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java
@@ -70,7 +70,7 @@ public ActionResult embeddings(MultipartFile file, HttpServletRequest request)
contentWordCount.add(str.length());
// request embedding
- FastChatEmbeddingResponse response = distributeAIEmbedding(str);
+ FastChatEmbeddingResponse response = promptService.distributeAIEmbedding(str);
if(response == null){
continue;
}
@@ -97,7 +97,7 @@ public ActionResult embeddings(MultipartFile file, HttpServletRequest request)
public SseEmitter search(ChatQueryRequest queryRequest, @RequestHeader Map headers)
throws Exception {
// request embedding
- FastChatEmbeddingResponse response = distributeAIEmbedding(queryRequest.getMessage());
+ FastChatEmbeddingResponse response = promptService.distributeAIEmbedding(queryRequest.getMessage());
List> contentVector = new ArrayList<>();
contentVector.add(response.getData().get(0).getEmbedding());
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java
index 0c6180667..94caf7d4f 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java
@@ -63,8 +63,8 @@ public SseEmitter prompt(ChatQueryRequest queryRequest, @RequestHeader Map messages;
-
- private final ConnectInfo connectInfo;
+ private final PromptService promptService;;
private final ChatQueryRequest queryRequest;
+ private final LoginUser loginUser;
+
private List toolCalls = new ArrayList<>();
- public OpenAIEventSourceListener(SseEmitter sseEmitter, List messages, ConnectInfo connectInfo, ChatQueryRequest queryRequest) {
+ public OpenAIEventSourceListener(SseEmitter sseEmitter, PromptService promptService, ChatQueryRequest queryRequest, LoginUser loginUser) {
this.sseEmitter = sseEmitter;
- this.messages = messages;
- this.connectInfo = connectInfo;
+ this.promptService = promptService;
this.queryRequest = queryRequest;
+ this.loginUser = loginUser;
}
public static List mergeToolCallsLists(List list1, List list2) {
@@ -134,37 +134,30 @@ public void onEvent(EventSource eventSource, String id, String type, String data
sseEmitter.complete();
return;
}
- messages.add(Message.builder()
- .toolCalls(toolCalls)
- .role(BaseMessage.Role.ASSISTANT).build());
- Chat2DBContext.putContext(connectInfo);
- try {
- for (ToolCalls toolCall : toolCalls) {
- String callId = toolCall.getId();
- ToolCallFunction function = toolCall.getFunction();
- if (function != null && Objects.nonNull(function.getArguments())) {
- String functionName = function.getName();
+ List tableNames = new ArrayList<>();
+ for (ToolCalls toolCall : toolCalls) {
+ String callId = toolCall.getId();
+ ToolCallFunction function = toolCall.getFunction();
+ if (function != null && Objects.nonNull(function.getArguments())) {
+ String functionName = function.getName();
+ if ("get_table_columns".equals(functionName)) {
JSONObject arguments = JSONObject.parse(function.getArguments());
- if ("get_table_columns".equals(functionName)) {
- MetaData metaSchema = Chat2DBContext.getMetaData();
- String content;
- try {
- content = metaSchema.tableDDL(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), arguments.getString("table_name"));
- }catch (Exception e){
- log.error("OpenAI查询表结构失败",e);
- content = StringUtils.defaultString(e.getMessage(), "OpenAI查询表结构失败");
- }
- messages.add(Message.builder().role(BaseMessage.Role.TOOL)
- .toolCallId(callId)
- .name(functionName)
- .content(content)
- .build());
- }
+ tableNames.add(arguments.getString("table_name"));
}
}
- } finally {
- Chat2DBContext.removeContext();
}
+ List messages = new ArrayList<>();
+ queryRequest.setTableNames(tableNames);
+ ContextUtils.setContext(Context.builder()
+ .loginUser(loginUser)
+ .build());
+ Dbutils.setSession();
+ String prompt = promptService.buildPrompt(queryRequest);
+ Dbutils.removeSession();
+ prompt = prompt.replaceAll("#", "");
+ log.info(prompt);
+ Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
+ messages.add(currentMessage);
OpenAIClient.getInstance().streamChatCompletion(messages, this);
toolCalls.clear();
return;
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
new file mode 100644
index 000000000..3e9ba940e
--- /dev/null
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -0,0 +1,364 @@
+package ai.chat2db.server.web.api.controller.ai.utils;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson2.JSON;
+import com.google.common.collect.Lists;
+
+import ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;
+import ai.chat2db.server.domain.api.model.Config;
+import ai.chat2db.server.domain.api.model.DataSource;
+import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
+import ai.chat2db.server.domain.api.param.TableQueryParam;
+import ai.chat2db.server.domain.api.service.ConfigService;
+import ai.chat2db.server.domain.api.service.DataSourceService;
+import ai.chat2db.server.domain.api.service.TableService;
+import ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;
+import ai.chat2db.server.tools.base.wrapper.result.DataResult;
+import ai.chat2db.server.tools.common.util.EasyEnumUtils;
+import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
+import ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;
+import ai.chat2db.server.web.api.controller.ai.converter.ChatConverter;
+import ai.chat2db.server.web.api.controller.ai.enums.PromptType;
+import ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;
+import ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;
+import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
+import ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;
+import ai.chat2db.server.web.api.http.GatewayClientService;
+import ai.chat2db.server.web.api.http.model.EsTableSchema;
+import ai.chat2db.server.web.api.http.model.TableSchema;
+import ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;
+import ai.chat2db.server.web.api.http.request.TableSchemaRequest;
+import ai.chat2db.server.web.api.http.request.WhiteListRequest;
+import ai.chat2db.server.web.api.http.response.EsTableSchemaResponse;
+import ai.chat2db.server.web.api.http.response.TableSchemaResponse;
+import ai.chat2db.server.web.api.util.ApplicationContextUtil;
+import ai.chat2db.spi.MetaData;
+import ai.chat2db.spi.model.Table;
+import ai.chat2db.spi.sql.Chat2DBContext;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+@ConnectionInfoAspect
+@Service
+public class PromptService {
+
+
+ @Autowired
+ private TableService tableService;
+
+ @Autowired
+ private DataSourceService dataSourceService;
+
+
+ @Autowired
+ private ChatConverter chatConverter;
+
+
+ @Resource
+ private GatewayClientService gatewayClientService;
+
+
+ /**
+ * 构建prompt
+ *
+ * @param queryRequest
+ * @return
+ */
+ public String buildPrompt(ChatQueryRequest queryRequest) {
+ if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
+ return queryRequest.getMessage();
+ }
+
+ // 查询schema信息
+ String dataSourceType = queryDatabaseType(queryRequest);
+ String properties = "";
+ if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
+ TableQueryParam queryParam = chatConverter.chat2tableQuery(queryRequest);
+ properties = buildTableColumn(queryParam, queryRequest.getTableNames());
+ } else {
+ properties = mappingDatabaseSchema(queryRequest);
+ }
+ String prompt = queryRequest.getMessage();
+ String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
+ : queryRequest.getPromptType();
+ PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
+ String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
+ String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
+ "### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables, with their properties:\n#\n# "
+ + "%s\n#\n#\n### SQL input: %s", pType.getDescription(), ext, dataSourceType,
+ properties, prompt) : String.format("### 请根据以下SQL input%s. %s\n#\n### SQL input: %s",
+ pType.getDescription(), ext, prompt);
+ switch (pType) {
+ case SQL_2_SQL:
+ schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, queryRequest.getDestSqlType()) : String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, dataSourceType);
+ default:
+ break;
+ }
+ String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
+ return cleanedInput;
+ }
+
+ public String mappingDatabaseSchema(ChatQueryRequest queryRequest) {
+ String properties = "";
+ String apiKey = getApiKey();
+ if (StringUtils.isNotBlank(apiKey)) {
+ boolean res = gatewayClientService.checkInWhite(new WhiteListRequest(apiKey, WhiteListTypeEnum.VECTOR.getCode())).getData();
+ if (res) {
+// properties = queryDatabaseSchema(queryRequest) + querySchemaByEs(queryRequest);
+ properties = queryDatabaseSchema(queryRequest);
+ }
+ }
+ return properties;
+ }
+
+
+ /**
+ * query chat2db apikey
+ *
+ * @return
+ */
+ public String getApiKey() {
+ ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
+ Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();
+ String aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();
+ // only sync for chat2db ai
+ if (Objects.isNull(config) || !aiSqlSource.equals(config.getContent())) {
+ return null;
+ }
+ Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();
+ if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {
+ return null;
+ }
+ return keyConfig.getContent();
+ }
+
+ /**
+ * 构建schema参数
+ *
+ * @param tableQueryParam
+ * @param tableNames
+ * @return
+ */
+ public String buildTableColumn(TableQueryParam tableQueryParam,
+ List tableNames) {
+ if (CollectionUtils.isEmpty(tableNames)) {
+ return "";
+ }
+ List schemaContent = Lists.newArrayList();
+ try {
+ schemaContent = tableNames.stream().map(tableName -> {
+ tableQueryParam.setTableName(tableName);
+ return queryTableDdl(tableName, tableQueryParam);
+ }).collect(Collectors.toList());
+ } catch (Exception exception) {
+ log.error("query table error, do nothing");
+ }
+
+ return JSON.toJSONString(schemaContent);
+ }
+
+ /**
+ * query table schema
+ *
+ * @param tableName
+ * @param request
+ * @return
+ */
+ public String queryTableDdl(String tableName, TableQueryParam request) {
+ ShowCreateTableParam param = new ShowCreateTableParam();
+ param.setTableName(tableName);
+ param.setDataSourceId(request.getDataSourceId());
+ param.setDatabaseName(request.getDatabaseName());
+ param.setSchemaName(request.getSchemaName());
+ DataResult tableSchema = tableService.showCreateTable(param);
+ return tableSchema.getData();
+ }
+
+ /**
+ * query database schema
+ *
+ * @param queryRequest
+ * @return
+ * @throws IOException
+ */
+ public String queryDatabaseSchema(ChatQueryRequest queryRequest) {
+ // request embedding
+ FastChatEmbeddingResponse response = distributeAIEmbedding(queryRequest.getMessage());
+ List> contentVector = new ArrayList<>();
+ if (Objects.isNull(response) || CollectionUtils.isEmpty(response.getData())) {
+ return "";
+ }
+ contentVector.add(response.getData().get(0).getEmbedding());
+
+ // search embedding
+ TableSchemaRequest tableSchemaRequest = new TableSchemaRequest();
+ tableSchemaRequest.setSchemaVector(contentVector);
+ tableSchemaRequest.setDataSourceId(queryRequest.getDataSourceId());
+ tableSchemaRequest.setDatabaseName(queryRequest.getDatabaseName());
+ tableSchemaRequest.setDataSourceSchema(queryRequest.getSchemaName());
+ ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
+ Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData();
+ if (Objects.isNull(keyConfig) || StringUtils.isBlank(keyConfig.getContent())) {
+ return "";
+ }
+ tableSchemaRequest.setApiKey(keyConfig.getContent());
+ try {
+ DataResult result = gatewayClientService.schemaVectorSearch(tableSchemaRequest);
+ List schemas = Lists.newArrayList();
+ if (Objects.nonNull(result.getData()) && CollectionUtils.isNotEmpty(result.getData().getTableSchemas())) {
+ for(TableSchema data: result.getData().getTableSchemas()){
+ schemas.add(data.getTableSchema());
+ }
+ }
+ if (CollectionUtils.isEmpty(schemas)) {
+ return "";
+ }
+ String res = JSON.toJSONString(schemas);
+ log.info("search vector result:{}", res);
+ return res;
+ } catch (Exception exception) {
+ log.error("query table error, do nothing");
+ return "";
+ }
+ }
+
+ /**
+ * distribute embedding with different AI
+ *
+ * @return
+ */
+ public FastChatEmbeddingResponse distributeAIEmbedding(String input) {
+ ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
+ Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();
+ String aiSqlSource = config.getContent();
+ if (Objects.isNull(aiSqlSource)) {
+ return null;
+ }
+ AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);
+ switch (Objects.requireNonNull(aiSqlSourceEnum)) {
+ case CHAT2DBAI:
+ return embeddingWithChat2dbAi(input);
+ case FASTCHATAI:
+ return embeddingWithFastChatAi(input);
+ }
+ return null;
+ }
+
+ /**
+ * embedding with fast chat openai
+ *
+ * @param input
+ * @return
+ * @throws IOException
+ */
+ public FastChatEmbeddingResponse embeddingWithFastChatAi(String input) {
+ FastChatEmbeddingResponse response = FastChatAIClient.getInstance().embeddings(input);
+ return response;
+ }
+
+ /**
+ * embedding with open ai
+ *
+ * @param input
+ * @return
+ */
+ public FastChatEmbeddingResponse embeddingWithChat2dbAi(String input) {
+ FastChatEmbeddingResponse embeddings = Chat2dbAIClient.getInstance().embeddings(input);
+ return embeddings;
+ }
+
+ /**
+ * 构建prompt
+ *
+ * @param queryRequest
+ * @return
+ */
+ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
+ if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
+ return queryRequest.getMessage();
+ }
+
+ // 查询schema信息
+ String dataSourceType = queryDatabaseType(queryRequest);
+ String properties = "";
+ if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
+ properties = queryRequest.getTableNames().stream().collect(Collectors.joining(","));
+ } else {
+ properties = queryDatabaseTables(queryRequest);
+ }
+ String prompt = queryRequest.getMessage();
+ String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
+ : queryRequest.getPromptType();
+ PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
+ String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
+ String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
+ "### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables:\n#\n# "
+ + "%s\n#\n#\n### SQL input: %s", pType.getDescription(), ext, dataSourceType,
+ properties, prompt) : String.format("### 请根据以下SQL input%s. %s\n#\n### SQL input: %s",
+ pType.getDescription(), ext, prompt);
+ switch (pType) {
+ case SQL_2_SQL:
+ schemaProperty = StringUtils.isNotBlank(queryRequest.getDestSqlType()) ? String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, queryRequest.getDestSqlType()) : String.format(
+ "%s\n#\n### 目标SQL类型: %s", schemaProperty, dataSourceType);
+ default:
+ break;
+ }
+ String cleanedInput = schemaProperty.replaceAll("[\r\t]", "");
+ return cleanedInput;
+ }
+
+
+ /**
+ * query database type
+ *
+ * @param queryRequest
+ * @return
+ */
+ public String queryDatabaseType(ChatQueryRequest queryRequest) {
+ // 查询schema信息
+ DataResult dataResult = dataSourceService.queryById(queryRequest.getDataSourceId());
+ String dataSourceType = dataResult.getData().getType();
+ if (StringUtils.isBlank(dataSourceType)) {
+ dataSourceType = "MYSQL";
+ }
+ return dataSourceType;
+ }
+
+ /**
+ * query database schema
+ *
+ * @param queryRequest
+ * @return
+ * @throws IOException
+ */
+ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
+ MetaData metaSchema = Chat2DBContext.getMetaData();
+ try {
+ List tables = metaSchema.tables(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), null);
+ return tables.stream()
+ .map(table -> StringUtils.isBlank(table.getComment()) ? table.getName()
+ : table.getName() + "(" + table.getComment() + ")")
+ .collect(Collectors.joining(","));
+ } catch (Exception e) {
+ log.error("query table error:{}, do nothing", e.getMessage());
+ return "";
+ }
+ }
+
+}
From d5a8216a67454dd537c2f5a54564ac0c36ec8ddc Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Wed, 14 Feb 2024 23:35:30 +0800
Subject: [PATCH 004/350] =?UTF-8?q?=E6=99=BA=E8=B0=B1ai=E4=BF=AE=E6=94=B9?=
=?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=9B=9E=E8=B0=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/api/controller/ai/ChatController.java | 2 +-
.../zhipu/client/ZhipuChatAIStreamClient.java | 61 +++++++++++++------
.../model/ZhipuChatCompletionsOptions.java | 60 ++++++++++++++++++
3 files changed, 105 insertions(+), 18 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index ff93fcbd4..5150b3db9 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -426,7 +426,7 @@ private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter
* @throws IOException
*/
private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = promptService.buildPrompt(queryRequest);
+ String prompt = promptService.buildAutoPrompt(queryRequest);
List messages = getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
index 550c929eb..896e180b3 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
@@ -4,9 +4,15 @@
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import ai.chat2db.server.web.api.controller.ai.zhipu.interceptor.ZhipuChatHeaderAuthorizationInterceptor;
import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function.Parameters;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function.Property;
import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
@@ -19,6 +25,7 @@
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -69,7 +76,6 @@ public class ZhipuChatAIStreamClient {
@Getter
private OkHttpClient okHttpClient;
-
/**
* @param builder
*/
@@ -90,13 +96,12 @@ private ZhipuChatAIStreamClient(Builder builder) {
* okhttpclient
*/
private OkHttpClient okHttpClient() {
- OkHttpClient okHttpClient = new OkHttpClient
- .Builder()
- .addInterceptor(new ZhipuChatHeaderAuthorizationInterceptor(this.key, this.secret))
- .connectTimeout(10, TimeUnit.SECONDS)
- .writeTimeout(50, TimeUnit.SECONDS)
- .readTimeout(50, TimeUnit.SECONDS)
- .build();
+ OkHttpClient okHttpClient = new OkHttpClient.Builder()
+ .addInterceptor(new ZhipuChatHeaderAuthorizationInterceptor(this.key, this.secret))
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .writeTimeout(50, TimeUnit.SECONDS)
+ .readTimeout(50, TimeUnit.SECONDS)
+ .build();
return okHttpClient;
}
@@ -195,12 +200,34 @@ public void streamCompletions(List chatMessages, EventSourceLis
}
log.info("Zhipu Chat AI, prompt:{}", chatMessages.get(chatMessages.size() - 1).getContent());
try {
- // 建议直接查看demo包代码,这里更新可能不及时
- ZhipuChatCompletionsOptions completionsOptions = new ZhipuChatCompletionsOptions();
- completionsOptions.setPrompt(chatMessages);
- completionsOptions.setModel(this.model);
String requestId = String.valueOf(System.currentTimeMillis());
- completionsOptions.setRequestId(requestId);
+ // 建议直接查看demo包代码,这里更新可能不及时
+ ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
+ .requestId(requestId)
+ .stream(true)
+ .sseFormat("data")
+ .model(this.model)
+ .toolChoice("auto")
+ .prompt(chatMessages)
+ .tools(Arrays.asList(
+ Tool.builder()
+ .type("function")
+ .function(Function.builder()
+ .name("get_table_columns")
+ .description("获取指定表的字段名,类型")
+ .parameters(Parameters.builder()
+ .type("object")
+ .properties(ImmutableMap.builder()
+ .put("table_name", Property.builder()
+ .type("string")
+ .description("表名,例如```User```")
+ .build())
+ .build())
+ .required(Arrays.asList("table_name"))
+ .build())
+ .build())
+ .build()))
+ .build();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String requestBody = mapper.writeValueAsString(completionsOptions);
@@ -208,10 +235,10 @@ public void streamCompletions(List chatMessages, EventSourceLis
String url = this.apiHost + "/" + this.model + "/" + "sse-invoke";
EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
Request request = new Request.Builder()
- .url(url)
- .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
- .build();
- //创建事件
+ .url(url)
+ .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
+ .build();
+ // 创建事件
EventSource eventSource = factory.newEventSource(request, eventSourceListener);
log.info("finish invoking zhipu chat ai");
} catch (Exception e) {
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
index 4b6359cc2..4bcc82d4b 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
@@ -5,15 +5,19 @@
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.Builder;
import lombok.Data;
import java.util.List;
+import java.util.Map;
/**
* The configuration information for a chat completions request. Completions support a wide variety of tasks and
* generate text that continues from or "completes" provided prompt data.
*/
@Data
+@Builder
public final class ZhipuChatCompletionsOptions {
@JsonProperty(value = "request_id")
@@ -45,4 +49,60 @@ public final class ZhipuChatCompletionsOptions {
*/
@JsonProperty(value = "model")
private String model;
+
+
+
+ // 新添加的参数
+ @JsonProperty(value = "tool_choice")
+ private String toolChoice; // 工具选择策略
+
+ @JsonProperty(value = "tools")
+ private List tools; // 工具列表
+
+ // 工具类
+ @Data
+ @Builder
+ public static class Tool {
+ @JsonProperty(value = "type")
+ private String type;
+
+ @JsonProperty(value = "function")
+ private Function function;
+
+ @Data
+ @Builder
+ public static class Function {
+ @JsonProperty(value = "name")
+ private String name;
+
+ @JsonProperty(value = "description")
+ private String description;
+
+ @JsonProperty(value = "parameters")
+ private Parameters parameters;
+
+ @Data
+ @Builder
+ public static class Parameters {
+ @JsonProperty(value = "type")
+ private String type;
+
+ @JsonProperty(value = "properties")
+ private Map properties;
+
+ @JsonProperty(value = "required")
+ private List required;
+ }
+
+ @Data
+ @Builder
+ public static class Property {
+ @JsonProperty(value = "type")
+ private String type;
+
+ @JsonProperty(value = "description")
+ private String description;
+ }
+ }
+ }
}
From d8ac27db770ee6ce537048bb8eb16f85d572e28b Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Tue, 20 Feb 2024 11:40:43 +0800
Subject: [PATCH 005/350] =?UTF-8?q?=E6=99=BA=E8=B0=B1=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E5=8D=87=E7=BA=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/api/controller/ai/ChatController.java | 95 +++---------
.../api/controller/ai/enums/PromptType.java | 6 +
.../listener/OpenAIEventSourceListener.java | 17 ++-
.../controller/ai/utils/PromptService.java | 57 ++++++-
.../ai/zhipu/client/ZhipuChatAIClient.java | 4 +-
.../zhipu/client/ZhipuChatAIStreamClient.java | 50 +------
.../ZhipuChatAIEventSourceListener.java | 139 ++++--------------
.../model/ZhipuChatCompletionsOptions.java | 59 +-------
8 files changed, 131 insertions(+), 296 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index 5150b3db9..0222b3b05 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -4,18 +4,10 @@
import ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;
import ai.chat2db.server.domain.api.model.Config;
-import ai.chat2db.server.domain.api.model.DataSource;
-import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
-import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.domain.api.service.ConfigService;
-import ai.chat2db.server.domain.api.service.DataSourceService;
-import ai.chat2db.server.domain.api.service.TableService;
-import ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;
-import ai.chat2db.server.tools.base.wrapper.result.DataResult;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.tools.common.util.ContextUtils;
-import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;
import ai.chat2db.server.web.api.controller.ai.azure.listener.AzureOpenAIEventSourceListener;
@@ -30,10 +22,7 @@
import ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeChatCompletionsOptions;
import ai.chat2db.server.web.api.controller.ai.claude.model.ClaudeChatMessage;
import ai.chat2db.server.web.api.controller.ai.config.LocalCache;
-import ai.chat2db.server.web.api.controller.ai.converter.ChatConverter;
-import ai.chat2db.server.web.api.controller.ai.enums.PromptType;
import ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;
-import ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;
import ai.chat2db.server.web.api.controller.ai.fastchat.listener.FastChatAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
@@ -50,48 +39,31 @@
import ai.chat2db.server.web.api.controller.ai.wenxin.listener.WenxinAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;
import ai.chat2db.server.web.api.controller.ai.zhipu.listener.ZhipuChatAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
import ai.chat2db.server.web.api.http.GatewayClientService;
-import ai.chat2db.server.web.api.http.model.EsTableSchema;
-import ai.chat2db.server.web.api.http.model.TableSchema;
-import ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;
-import ai.chat2db.server.web.api.http.request.TableSchemaRequest;
-import ai.chat2db.server.web.api.http.request.WhiteListRequest;
-import ai.chat2db.server.web.api.http.response.EsTableSchemaResponse;
-import ai.chat2db.server.web.api.http.response.TableSchemaResponse;
import ai.chat2db.server.web.api.util.ApplicationContextUtil;
-import ai.chat2db.spi.MetaData;
-import ai.chat2db.spi.model.Table;
-import ai.chat2db.spi.sql.Chat2DBContext;
-import ai.chat2db.spi.sql.ConnectInfo;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson2.JSON;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.Message;
-import com.unfbx.chatgpt.entity.chat.Parameters;
import com.unfbx.chatgpt.entity.chat.tool.Tools;
import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
-import com.unfbx.chatgpt.entity.chat.BaseChatCompletion.Model;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
-import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.stream.Collectors;
/**
* 描述:
@@ -306,20 +278,7 @@ private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseE
ChatCompletion chatCompletion = ChatCompletion.builder()
.messages(messages).stream(true).build();
if(queryRequest.getDatabaseName()!=null){
- ToolsFunction function = ToolsFunction.builder()
- .name("get_table_columns")
- .description("获取指定表的字段名,类型")
- .parameters(Parameters.builder()
- .type("object")
- .properties(ImmutableMap.builder()
- .put("table_name", ImmutableMap.builder()
- .put("type", "string")
- .put("description", "表名,例如```User```")
- .build())
- .build())
- .required(List.of("table_name"))
- .build())
- .build();
+ ToolsFunction function = PromptService.getToolsFunction();
chatCompletion.setModel("gpt-3.5-turbo-0125");
chatCompletion.setTools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)));
chatCompletion.setToolChoice("auto");
@@ -406,7 +365,7 @@ private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sse
*/
private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildPrompt(queryRequest);
- List messages = getFastChatMessage(uid, prompt);
+ List messages = promptService.getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -427,12 +386,25 @@ private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter
*/
private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildAutoPrompt(queryRequest);
- List messages = getFastChatMessage(uid, prompt);
+ List messages = promptService.getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
-
- ZhipuChatAIEventSourceListener sourceListener = new ZhipuChatAIEventSourceListener(sseEmitter);
- ZhipuChatAIClient.getInstance().streamCompletions(messages, sourceListener);
+ LoginUser loginUser = ContextUtils.getLoginUser();
+ ZhipuChatAIEventSourceListener sourceListener = new ZhipuChatAIEventSourceListener(sseEmitter,promptService,queryRequest,loginUser);
+ String requestId = String.valueOf(System.currentTimeMillis());
+ // 建议直接查看demo包代码,这里更新可能不及时
+ ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
+ .requestId(requestId)
+ .stream(true)
+ .toolChoice("auto")
+ .messages(messages)
+ .build();
+ if(queryRequest.getDatabaseName()!=null){
+ ToolsFunction function = PromptService.getToolsFunction();
+ completionsOptions.setTools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)));
+ completionsOptions.setToolChoice("auto");
+ }
+ ZhipuChatAIClient.getInstance().streamCompletions(completionsOptions, sourceListener);
LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);
return sseEmitter;
}
@@ -448,7 +420,7 @@ private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter
*/
private SseEmitter chatWithTongyiChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildPrompt(queryRequest);
- List messages = getFastChatMessage(uid, prompt);
+ List messages = promptService.getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -469,7 +441,7 @@ private SseEmitter chatWithTongyiChatAi(ChatQueryRequest queryRequest, SseEmitte
*/
private SseEmitter chatWithBaichuanAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildPrompt(queryRequest);
- List messages = getFastChatMessage(uid, prompt);
+ List messages = promptService.getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
@@ -479,26 +451,7 @@ private SseEmitter chatWithBaichuanAi(ChatQueryRequest queryRequest, SseEmitter
return sseEmitter;
}
- /**
- * get fast chat message
- *
- * @param uid
- * @param prompt
- * @return
- */
- private List getFastChatMessage(String uid, String prompt) {
- List messages = (List)LocalCache.CACHE.get(uid);
- if (CollectionUtils.isNotEmpty(messages)) {
- if (messages.size() >= contextLength) {
- messages = messages.subList(1, contextLength);
- }
- } else {
- messages = Lists.newArrayList();
- }
- FastChatMessage currentMessage = new FastChatMessage(FastChatRole.USER).setContent(prompt);
- messages.add(currentMessage);
- return messages;
- }
+
/**
* chat with wenxin chat openai
@@ -511,7 +464,7 @@ private List getFastChatMessage(String uid, String prompt) {
*/
private SseEmitter chatWithWenxinAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildPrompt(queryRequest);
- List messages = getFastChatMessage(uid, prompt);
+ List messages = promptService.getFastChatMessage(uid, prompt);
if (messages.size() >= 2 && messages.size() % 2 == 0) {
messages.remove(messages.size() - 1);
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
index 9e9745c75..0135e5ea6 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
@@ -38,6 +38,12 @@ public enum PromptType implements BaseEnum {
* text generation
*/
TEXT_GENERATION("文本生成"),
+
+
+ /**
+ * function call
+ */
+ FUNCTION_CALL("获取指定表的字段名,类型"),
;
final String description;
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index 7f3e6b4f5..6bd49a387 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -37,11 +37,11 @@ public class OpenAIEventSourceListener extends EventSourceListener {
private final SseEmitter sseEmitter;
- private final PromptService promptService;;
+ protected final PromptService promptService;;
private final ChatQueryRequest queryRequest;
- private final LoginUser loginUser;
+ public final LoginUser loginUser;
private List toolCalls = new ArrayList<>();
@@ -117,6 +117,13 @@ public void onOpen(EventSource eventSource, Response response) {
log.info("OpenAI建立sse连接...");
}
+
+ public void functionCall(String prompt){
+ List messages = new ArrayList<>();
+ Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
+ messages.add(currentMessage);
+ OpenAIClient.getInstance().streamChatCompletion(messages, this);
+ }
/**
* {@inheritDoc}
*/
@@ -146,7 +153,7 @@ public void onEvent(EventSource eventSource, String id, String type, String data
}
}
}
- List messages = new ArrayList<>();
+
queryRequest.setTableNames(tableNames);
ContextUtils.setContext(Context.builder()
.loginUser(loginUser)
@@ -156,9 +163,7 @@ public void onEvent(EventSource eventSource, String id, String type, String data
Dbutils.removeSession();
prompt = prompt.replaceAll("#", "");
log.info(prompt);
- Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
- messages.add(currentMessage);
- OpenAIClient.getInstance().streamChatCompletion(messages, this);
+ functionCall(prompt);
toolCalls.clear();
return;
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index 3e9ba940e..5a1ae63d2 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -10,10 +10,14 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSON;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
+import com.unfbx.chatgpt.entity.chat.Parameters;
+import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
import ai.chat2db.server.domain.api.enums.AiSqlSourceEnum;
import ai.chat2db.server.domain.api.model.Config;
@@ -28,19 +32,19 @@
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;
+import ai.chat2db.server.web.api.controller.ai.config.LocalCache;
import ai.chat2db.server.web.api.controller.ai.converter.ChatConverter;
import ai.chat2db.server.web.api.controller.ai.enums.PromptType;
import ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;
import ai.chat2db.server.web.api.controller.ai.fastchat.embeddings.FastChatEmbeddingResponse;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;
import ai.chat2db.server.web.api.http.GatewayClientService;
-import ai.chat2db.server.web.api.http.model.EsTableSchema;
import ai.chat2db.server.web.api.http.model.TableSchema;
-import ai.chat2db.server.web.api.http.request.EsTableSchemaRequest;
import ai.chat2db.server.web.api.http.request.TableSchemaRequest;
import ai.chat2db.server.web.api.http.request.WhiteListRequest;
-import ai.chat2db.server.web.api.http.response.EsTableSchemaResponse;
import ai.chat2db.server.web.api.http.response.TableSchemaResponse;
import ai.chat2db.server.web.api.util.ApplicationContextUtil;
import ai.chat2db.spi.MetaData;
@@ -56,6 +60,10 @@
public class PromptService {
+ @Value("${chatgpt.context.length}")
+ private Integer contextLength;
+
+
@Autowired
private TableService tableService;
@@ -292,7 +300,6 @@ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
if (PromptType.TEXT_GENERATION.getCode().equals(queryRequest.getPromptType())) {
return queryRequest.getMessage();
}
-
// 查询schema信息
String dataSourceType = queryDatabaseType(queryRequest);
String properties = "";
@@ -305,6 +312,10 @@ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
: queryRequest.getPromptType();
PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
+ if (pType.equals(PromptType.NL_2_SQL)) {
+ pType = PromptType.FUNCTION_CALL;
+ }
+
String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
"### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables:\n#\n# "
@@ -361,4 +372,42 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
}
}
+ public static ToolsFunction getToolsFunction(){
+ return ToolsFunction.builder()
+ .name("get_table_columns")
+ .description("获取指定表的字段名,类型")
+ .parameters(Parameters.builder()
+ .type("object")
+ .properties(ImmutableMap.builder()
+ .put("table_name", ImmutableMap.builder()
+ .put("type", "string")
+ .put("description", "表名,例如```User```")
+ .build())
+ .build())
+ .required(List.of("table_name"))
+ .build())
+ .build();
+ }
+
+
+ /**
+ * get fast chat message
+ *
+ * @param uid
+ * @param prompt
+ * @return
+ */
+ public List getFastChatMessage(String uid, String prompt) {
+ List messages = (List)LocalCache.CACHE.get(uid);
+ if (CollectionUtils.isNotEmpty(messages)) {
+ if (messages.size() >= contextLength) {
+ messages = messages.subList(1, contextLength);
+ }
+ } else {
+ messages = Lists.newArrayList();
+ }
+ FastChatMessage currentMessage = new FastChatMessage(FastChatRole.USER).setContent(prompt);
+ messages.add(currentMessage);
+ return messages;
+ }
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java
index f205f17f5..db0d35fa6 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java
@@ -58,8 +58,8 @@ private static ZhipuChatAIStreamClient singleton() {
public static void refresh() {
String apiKey = "";
- String apiHost = "https://open.bigmodel.cn/api/paas/v3/model-api/";
- String model = "chatglm_turbo";
+ String apiHost = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
+ String model = "glm-4";
ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
Config apiHostConfig = configService.find(ZHIPU_HOST).getData();
if (apiHostConfig != null && StringUtils.isNotBlank(apiHostConfig.getContent())) {
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
index 896e180b3..ef0ec8071 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
@@ -1,18 +1,11 @@
package ai.chat2db.server.web.api.controller.ai.zhipu.client;
import ai.chat2db.server.tools.common.exception.ParamBusinessException;
-import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import ai.chat2db.server.web.api.controller.ai.zhipu.interceptor.ZhipuChatHeaderAuthorizationInterceptor;
import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
-import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool;
-import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function;
-import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function.Parameters;
-import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions.Tool.Function.Property;
import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableMap;
-
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
@@ -22,11 +15,8 @@
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;
-import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
-import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -189,50 +179,20 @@ public ZhipuChatAIStreamClient build() {
* @param chatMessages
* @param eventSourceListener
*/
- public void streamCompletions(List chatMessages, EventSourceListener eventSourceListener) {
- if (CollectionUtils.isEmpty(chatMessages)) {
- log.error("param error:Zhipu Chat Prompt cannot be empty");
- throw new ParamBusinessException("prompt");
- }
+ public void streamCompletions(ZhipuChatCompletionsOptions completionsOptions, EventSourceListener eventSourceListener) {
+
if (Objects.isNull(eventSourceListener)) {
log.error("param error:Zhipu ChatEventSourceListener cannot be empty");
throw new ParamBusinessException();
}
- log.info("Zhipu Chat AI, prompt:{}", chatMessages.get(chatMessages.size() - 1).getContent());
+ completionsOptions.setModel(this.model);
try {
- String requestId = String.valueOf(System.currentTimeMillis());
- // 建议直接查看demo包代码,这里更新可能不及时
- ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
- .requestId(requestId)
- .stream(true)
- .sseFormat("data")
- .model(this.model)
- .toolChoice("auto")
- .prompt(chatMessages)
- .tools(Arrays.asList(
- Tool.builder()
- .type("function")
- .function(Function.builder()
- .name("get_table_columns")
- .description("获取指定表的字段名,类型")
- .parameters(Parameters.builder()
- .type("object")
- .properties(ImmutableMap.builder()
- .put("table_name", Property.builder()
- .type("string")
- .description("表名,例如```User```")
- .build())
- .build())
- .required(Arrays.asList("table_name"))
- .build())
- .build())
- .build()))
- .build();
+
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String requestBody = mapper.writeValueAsString(completionsOptions);
- String url = this.apiHost + "/" + this.model + "/" + "sse-invoke";
+ String url = this.apiHost;
EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
Request request = new Request.Builder()
.url(url)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
index a8b1ae016..5fd65b128 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
@@ -1,22 +1,19 @@
package ai.chat2db.server.web.api.controller.ai.zhipu.listener;
+import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
-import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletions;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.unfbx.chatgpt.entity.chat.Message;
-import lombok.SneakyThrows;
+import ai.chat2db.server.web.api.controller.ai.openai.listener.OpenAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
+import ai.chat2db.server.web.api.controller.ai.utils.PromptService;
+import ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
import lombok.extern.slf4j.Slf4j;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-import okhttp3.sse.EventSource;
-import okhttp3.sse.EventSourceListener;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import java.io.IOException;
+import java.util.List;
import java.util.Objects;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
/**
* 描述:OpenAIEventSourceListener
*
@@ -24,111 +21,25 @@
* @date 2023-02-22
*/
@Slf4j
-public class ZhipuChatAIEventSourceListener extends EventSourceListener {
-
- private SseEmitter sseEmitter;
-
- private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-
- public ZhipuChatAIEventSourceListener(SseEmitter sseEmitter) {
- this.sseEmitter = sseEmitter;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onOpen(EventSource eventSource, Response response) {
- log.info("Zhipu Chat Sse connecting...");
- }
-
- /**
- * {@inheritDoc}
- */
- @SneakyThrows
- @Override
- public void onEvent(EventSource eventSource, String id, String type, String data) {
- log.info("Zhipu Chat AI response data:{}", data);
- if (data.equals("[DONE]")) {
- log.info("Zhipu Chat AI closed");
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]")
- .reconnectTime(3000));
- sseEmitter.complete();
- return;
- }
-
- ZhipuChatCompletions chatCompletions = mapper.readValue(data, ZhipuChatCompletions.class);
- String text = chatCompletions.getData();
- if (Objects.isNull(text)) {
- for (FastChatMessage message : chatCompletions.getBody().getChoices()) {
- if (message != null && message.getContent() != null) {
- text = message.getContent();
- }
- }
- }
-
- Message message = new Message();
- message.setContent(text);
- sseEmitter.send(SseEmitter.event()
- .id(null)
- .data(message)
- .reconnectTime(3000));
+public class ZhipuChatAIEventSourceListener extends OpenAIEventSourceListener {
+
+ public ZhipuChatAIEventSourceListener(SseEmitter sseEmitter, PromptService promptService,
+ ChatQueryRequest queryRequest, LoginUser loginUser) {
+ super(sseEmitter, promptService, queryRequest, loginUser);
}
- @Override
- public void onClosed(EventSource eventSource) {
- try {
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- sseEmitter.complete();
- log.info("ZhipuChatAI close sse connection...");
- }
@Override
- public void onFailure(EventSource eventSource, Throwable t, Response response) {
- try {
- if (Objects.isNull(response)) {
- String message = t.getMessage();
- Message sseMessage = new Message();
- sseMessage.setContent(message);
- sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(sseMessage));
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- sseEmitter.complete();
- return;
- }
- ResponseBody body = response.body();
- String bodyString = Objects.nonNull(t) ? t.getMessage() : "";
- if (Objects.nonNull(body)) {
- bodyString = body.string();
- if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {
- bodyString = t.getMessage();
- }
- log.error("Zhipu Chat AI sse response:{}", bodyString);
- } else {
- log.error("Zhipu Chat AI sse response:{},error:{}", response, t);
- }
- eventSource.cancel();
- Message message = new Message();
- message.setContent("Zhipu Chat AI error:" + bodyString);
- sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(message));
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- sseEmitter.complete();
- } catch (Exception exception) {
- log.error("Zhipu Chat AI send data error:", exception);
- }
+ public void functionCall(String prompt){
+ Long uid = loginUser.getId();
+ List messages = promptService.getFastChatMessage(Objects.toString(uid), prompt);
+ String requestId = String.valueOf(System.currentTimeMillis());
+ ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
+ .requestId(requestId)
+ .stream(true)
+ .toolChoice("auto")
+ .messages(messages)
+ .build();
+ ZhipuChatAIClient.getInstance().streamCompletions(completionsOptions, this);
}
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
index 4bcc82d4b..06c16bd07 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
@@ -5,12 +5,12 @@
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.unfbx.chatgpt.entity.chat.tool.Tools;
import lombok.Builder;
import lombok.Data;
import java.util.List;
-import java.util.Map;
/**
* The configuration information for a chat completions request. Completions support a wide variety of tasks and
@@ -24,12 +24,9 @@ public final class ZhipuChatCompletionsOptions {
private String requestId;
// sse-params
- @JsonProperty(value = "incremental")
+ @JsonProperty(value = "stream")
private Boolean stream = true;
- @JsonProperty(value = "sseFormat")
- private String sseFormat = "data";
-
/*
* The collection of context messages associated with this chat completions request.
@@ -37,8 +34,8 @@ public final class ZhipuChatCompletionsOptions {
* the behavior of the assistant, followed by alternating messages between the User and
* Assistant roles.
*/
- @JsonProperty(value = "prompt")
- private List prompt;
+ @JsonProperty(value = "messages")
+ private List messages;
//
@@ -57,52 +54,6 @@ public final class ZhipuChatCompletionsOptions {
private String toolChoice; // 工具选择策略
@JsonProperty(value = "tools")
- private List tools; // 工具列表
-
- // 工具类
- @Data
- @Builder
- public static class Tool {
- @JsonProperty(value = "type")
- private String type;
-
- @JsonProperty(value = "function")
- private Function function;
-
- @Data
- @Builder
- public static class Function {
- @JsonProperty(value = "name")
- private String name;
-
- @JsonProperty(value = "description")
- private String description;
-
- @JsonProperty(value = "parameters")
- private Parameters parameters;
-
- @Data
- @Builder
- public static class Parameters {
- @JsonProperty(value = "type")
- private String type;
-
- @JsonProperty(value = "properties")
- private Map properties;
-
- @JsonProperty(value = "required")
- private List required;
- }
-
- @Data
- @Builder
- public static class Property {
- @JsonProperty(value = "type")
- private String type;
+ private List tools; // 工具列表
- @JsonProperty(value = "description")
- private String description;
- }
- }
- }
}
From c330315881d9870a02519bd1047a7135fe39391c Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Wed, 21 Feb 2024 14:44:01 +0800
Subject: [PATCH 006/350] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?=
=?UTF-8?q?=E6=89=93=E5=8D=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/api/controller/ai/ChatController.java | 1 +
.../listener/OpenAIEventSourceListener.java | 39 +++++++++++++++----
.../controller/ai/utils/PromptService.java | 11 ++++--
.../ZhipuChatAIEventSourceListener.java | 11 +++++-
4 files changed, 49 insertions(+), 13 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index 0222b3b05..2a106fc10 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -386,6 +386,7 @@ private SseEmitter chatWithFastChatAi(ChatQueryRequest queryRequest, SseEmitter
*/
private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
String prompt = promptService.buildAutoPrompt(queryRequest);
+ log.info("原始提示词{}",prompt);
List messages = promptService.getFastChatMessage(uid, prompt);
buildSseEmitter(sseEmitter, uid);
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index 6bd49a387..2d63e9f4c 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -8,6 +8,8 @@
import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.ai.response.ChatCompletionResponse;
import ai.chat2db.server.web.api.controller.ai.utils.PromptService;
+
+import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -109,12 +111,16 @@ private static String mergeStrings(String str1, String str2) {
}
}
+
+ public String getName() {
+ return "OpenAI";
+ }
/**
* {@inheritDoc}
*/
@Override
public void onOpen(EventSource eventSource, Response response) {
- log.info("OpenAI建立sse连接...");
+ log.info("{}建立sse连接...",getName());
}
@@ -124,16 +130,32 @@ public void functionCall(String prompt){
messages.add(currentMessage);
OpenAIClient.getInstance().streamChatCompletion(messages, this);
}
+
+
+ public void handleTableNames(List tableNames,Object instance){
+ if(instance instanceof JSONArray){
+ ((JSONArray)instance).forEach(tableName->{
+ handleTableNames(tableNames,tableName);
+ });
+ }else if (instance instanceof JSONObject) {
+ ((JSONObject)instance).entrySet().forEach(entrySet->{
+ handleTableNames(tableNames,entrySet.getValue());
+ });
+ }else if (instance instanceof String) {
+ tableNames.add((String)instance);
+ }
+ }
/**
* {@inheritDoc}
*/
@SneakyThrows
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
- log.info("OpenAI返回数据:{}", data);
+ String scheme = getName();
+ log.info("{}返回数据:{}",scheme,data);
if (data.equals("[DONE]")) {
if (toolCalls.isEmpty()) {
- log.info("OpenAI返回数据结束了");
+ log.info("{}返回数据结束了",scheme);
sseEmitter.send(SseEmitter.event()
.id("[DONE]")
.data("[DONE]")
@@ -149,7 +171,7 @@ public void onEvent(EventSource eventSource, String id, String type, String data
String functionName = function.getName();
if ("get_table_columns".equals(functionName)) {
JSONObject arguments = JSONObject.parse(function.getArguments());
- tableNames.add(arguments.getString("table_name"));
+ handleTableNames(tableNames,arguments.get("table_names"));
}
}
}
@@ -162,7 +184,7 @@ public void onEvent(EventSource eventSource, String id, String type, String data
String prompt = promptService.buildPrompt(queryRequest);
Dbutils.removeSession();
prompt = prompt.replaceAll("#", "");
- log.info(prompt);
+ log.info("{} 新提示词 :{}",scheme,prompt);
functionCall(prompt);
toolCalls.clear();
return;
@@ -196,6 +218,7 @@ public void onClosed(EventSource eventSource) {
@Override
public void onFailure(EventSource eventSource, Throwable t, Response response) {
+ String scheme = getName();
try {
if (Objects.isNull(response)) {
String message = t.getMessage();
@@ -217,9 +240,9 @@ public void onFailure(EventSource eventSource, Throwable t, Response response) {
String bodyString = null;
if (Objects.nonNull(body)) {
bodyString = body.string();
- log.error("OpenAI sse连接异常data:{}", bodyString, t);
+ log.error("{} sse连接异常data:{}",scheme, bodyString, t);
} else {
- log.error("OpenAI sse连接异常data:{}", response, t);
+ log.error("{} sse连接异常data:{}",scheme, response, t);
}
eventSource.cancel();
Message message = new Message();
@@ -232,7 +255,7 @@ public void onFailure(EventSource eventSource, Throwable t, Response response) {
.data("[DONE]"));
sseEmitter.complete();
} catch (Exception exception) {
- log.error("发送数据异常:", exception);
+ log.error("{}发送数据异常:", scheme,exception);
}
}
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index 5a1ae63d2..d3ac972a9 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -304,7 +304,8 @@ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
String dataSourceType = queryDatabaseType(queryRequest);
String properties = "";
if (CollectionUtils.isNotEmpty(queryRequest.getTableNames())) {
- properties = queryRequest.getTableNames().stream().collect(Collectors.joining(","));
+ TableQueryParam queryParam = chatConverter.chat2tableQuery(queryRequest);
+ properties = buildTableColumn(queryParam, queryRequest.getTableNames());
} else {
properties = queryDatabaseTables(queryRequest);
}
@@ -375,13 +376,15 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
public static ToolsFunction getToolsFunction(){
return ToolsFunction.builder()
.name("get_table_columns")
- .description("获取指定表的字段名,类型")
+ .description("获取指定表的属性")
.parameters(Parameters.builder()
.type("object")
.properties(ImmutableMap.builder()
- .put("table_name", ImmutableMap.builder()
- .put("type", "string")
+ .put("table_names", ImmutableMap.builder()
.put("description", "表名,例如```User```")
+ .put("type", "array")
+ .put("items", ImmutableMap.of("type", "string"))
+ .put("uniqueItems", true)
.build())
.build())
.required(List.of("table_name"))
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
index 5fd65b128..abc07a8e1 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
@@ -14,6 +14,9 @@
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import com.unfbx.chatgpt.entity.chat.tool.Tools;
+import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
+
/**
* 描述:OpenAIEventSourceListener
*
@@ -28,18 +31,24 @@ public ZhipuChatAIEventSourceListener(SseEmitter sseEmitter, PromptService promp
super(sseEmitter, promptService, queryRequest, loginUser);
}
+ @Override
+ public String getName(){
+ return "Zhipu";
+ }
@Override
public void functionCall(String prompt){
Long uid = loginUser.getId();
List messages = promptService.getFastChatMessage(Objects.toString(uid), prompt);
String requestId = String.valueOf(System.currentTimeMillis());
+ ToolsFunction function = PromptService.getToolsFunction();
ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
.requestId(requestId)
.stream(true)
.toolChoice("auto")
+ .tools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)))
.messages(messages)
- .build();
+ .build();
ZhipuChatAIClient.getInstance().streamCompletions(completionsOptions, this);
}
}
From e5bbcc2115c30f0d2d549405d568c626953b70cb Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Thu, 22 Feb 2024 09:59:02 +0800
Subject: [PATCH 007/350] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9F=A5=E8=AF=A2?=
=?UTF-8?q?=E6=89=80=E6=9C=89=E8=A1=A8=E4=B8=8D=E6=98=BE=E7=A4=BA=E6=B3=A8?=
=?UTF-8?q?=E9=87=8A=E5=90=8D=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../domain/core/impl/TableServiceImpl.java | 62 +++++++++----------
.../api/controller/ai/enums/PromptType.java | 4 +-
.../controller/ai/utils/PromptService.java | 4 +-
3 files changed, 33 insertions(+), 37 deletions(-)
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
index 1454772d8..a5aaea130 100644
--- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
@@ -419,44 +419,40 @@ public ListResult queryTables(TablePageQueryParam param) {
private long addDBCache(Long dataSourceId, String databaseName, String schemaName, long version) {
String key = getTableKey(dataSourceId, databaseName, schemaName);
-
Connection connection = Chat2DBContext.getConnection();
long n = 0;
- try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, null,
- new String[]{"TABLE", "SYSTEM TABLE"})) {
- List cacheDOS = new ArrayList<>();
- while (resultSet.next()) {
- TableCacheDO tableCacheDO = new TableCacheDO();
- tableCacheDO.setDatabaseName(databaseName);
- tableCacheDO.setSchemaName(schemaName);
- tableCacheDO.setTableName(resultSet.getString("TABLE_NAME"));
- tableCacheDO.setExtendInfo(resultSet.getString("REMARKS"));
- tableCacheDO.setDataSourceId(dataSourceId);
- tableCacheDO.setVersion(version);
- tableCacheDO.setKey(key);
- cacheDOS.add(tableCacheDO);
- if (cacheDOS.size() >= 500) {
- getTableCacheMapper().batchInsert(cacheDOS);
- cacheDOS = new ArrayList<>();
- }
- n++;
- }
- if (!CollectionUtils.isEmpty(cacheDOS)) {
+ MetaData metaSchema = Chat2DBContext.getMetaData();
+ List tables = metaSchema.tables(connection, databaseName, schemaName, null);
+ List cacheDOS = new ArrayList<>();
+ for(Table table : tables){
+ TableCacheDO tableCacheDO = new TableCacheDO();
+ tableCacheDO.setDatabaseName(databaseName);
+ tableCacheDO.setSchemaName(schemaName);
+ tableCacheDO.setTableName(table.getName());
+ tableCacheDO.setExtendInfo(table.getComment());
+ tableCacheDO.setDataSourceId(dataSourceId);
+ tableCacheDO.setVersion(version);
+ tableCacheDO.setKey(key);
+ cacheDOS.add(tableCacheDO);
+ if (cacheDOS.size() >= 500) {
getTableCacheMapper().batchInsert(cacheDOS);
+ cacheDOS = new ArrayList<>();
}
- LambdaQueryWrapper q = new LambdaQueryWrapper();
- q.eq(TableCacheDO::getDataSourceId, dataSourceId);
- q.lt(TableCacheDO::getVersion, version);
- if (StringUtils.isNotBlank(databaseName)) {
- q.eq(TableCacheDO::getDatabaseName, databaseName);
- }
- if (StringUtils.isNotBlank(schemaName)) {
- q.eq(TableCacheDO::getSchemaName, schemaName);
- }
- getTableCacheMapper().delete(q);
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ n++;
+ }
+ if (!CollectionUtils.isEmpty(cacheDOS)) {
+ getTableCacheMapper().batchInsert(cacheDOS);
+ }
+ LambdaQueryWrapper q = new LambdaQueryWrapper();
+ q.eq(TableCacheDO::getDataSourceId, dataSourceId);
+ q.lt(TableCacheDO::getVersion, version);
+ if (StringUtils.isNotBlank(databaseName)) {
+ q.eq(TableCacheDO::getDatabaseName, databaseName);
+ }
+ if (StringUtils.isNotBlank(schemaName)) {
+ q.eq(TableCacheDO::getSchemaName, schemaName);
}
+ getTableCacheMapper().delete(q);
return n;
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
index 0135e5ea6..f6e833a01 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/enums/PromptType.java
@@ -41,9 +41,9 @@ public enum PromptType implements BaseEnum {
/**
- * function call
+ * GET_TABLE_COLUMNS
*/
- FUNCTION_CALL("获取指定表的字段名,类型"),
+ GET_TABLE_COLUMNS("获取指定表的属性"),
;
final String description;
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index d3ac972a9..3cb89d1f0 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -314,7 +314,7 @@ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
: queryRequest.getPromptType();
PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
if (pType.equals(PromptType.NL_2_SQL)) {
- pType = PromptType.FUNCTION_CALL;
+ pType = PromptType.GET_TABLE_COLUMNS;
}
String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
@@ -376,7 +376,7 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
public static ToolsFunction getToolsFunction(){
return ToolsFunction.builder()
.name("get_table_columns")
- .description("获取指定表的属性")
+ .description(PromptType.GET_TABLE_COLUMNS.getDescription())
.parameters(Parameters.builder()
.type("object")
.properties(ImmutableMap.builder()
From 1a721f085e8810cd6640fd4095ceb4338f8e40c5 Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Thu, 22 Feb 2024 15:19:06 +0800
Subject: [PATCH 008/350] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=96=E9=94=AE?=
=?UTF-8?q?=E6=98=A0=E5=B0=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/ai/utils/PromptService.java | 69 ++++++++++++++++---
1 file changed, 59 insertions(+), 10 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index 3cb89d1f0..b0ba984b1 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -3,10 +3,14 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
+import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -49,6 +53,7 @@
import ai.chat2db.server.web.api.util.ApplicationContextUtil;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.model.Table;
+import ai.chat2db.spi.model.TableColumn;
import ai.chat2db.spi.sql.Chat2DBContext;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -167,17 +172,16 @@ public String buildTableColumn(TableQueryParam tableQueryParam,
if (CollectionUtils.isEmpty(tableNames)) {
return "";
}
- List schemaContent = Lists.newArrayList();
try {
- schemaContent = tableNames.stream().map(tableName -> {
+ return tableNames.stream().map(tableName -> {
tableQueryParam.setTableName(tableName);
return queryTableDdl(tableName, tableQueryParam);
- }).collect(Collectors.toList());
+ }).collect(Collectors.joining(";\n"));
} catch (Exception exception) {
log.error("query table error, do nothing");
}
- return JSON.toJSONString(schemaContent);
+ return "";
}
/**
@@ -313,10 +317,9 @@ public String buildAutoPrompt(ChatQueryRequest queryRequest) {
String promptType = StringUtils.isBlank(queryRequest.getPromptType()) ? PromptType.NL_2_SQL.getCode()
: queryRequest.getPromptType();
PromptType pType = EasyEnumUtils.getEnum(PromptType.class, promptType);
- if (pType.equals(PromptType.NL_2_SQL)) {
+ if (StringUtils.isNotEmpty(properties)) {
pType = PromptType.GET_TABLE_COLUMNS;
}
-
String ext = StringUtils.isNotBlank(queryRequest.getExt()) ? queryRequest.getExt() : "";
String schemaProperty = StringUtils.isNotEmpty(properties) ? String.format(
"### 请根据以下table properties和SQL input%s. %s\n#\n### %s SQL tables:\n#\n# "
@@ -352,6 +355,32 @@ public String queryDatabaseType(ChatQueryRequest queryRequest) {
return dataSourceType;
}
+
+ /**
+ * 根据给定的表对象找出所有可能的外键列
+ * @return 外键列名列表
+ */
+ public static List findPossibleForeignKeys(List columns) {
+ List foreignKeys = new ArrayList<>();
+ for (TableColumn column : columns) {
+ String columnName = column.getName();
+ // 假设TableColumn类有一个getTableName方法可以获取列所属的表名
+ String tableName = column.getTableName();
+ Boolean primaryKey = column.getPrimaryKey();
+
+ // 检查列名是否符合`关联表_id`的格式,并且列名前半部分不等于表名
+ if (columnName != null && columnName.matches(".+_id") && Boolean.FALSE.equals(primaryKey)) {
+ // 从列名中移除"_id"以获取可能的关联表名
+ String potentialForeignKeyTable = columnName.substring(0, columnName.length() - 3);
+
+ if (!potentialForeignKeyTable.equals(tableName)) {
+ foreignKeys.add(columnName);
+ }
+ }
+ }
+ return foreignKeys;
+ }
+
/**
* query database schema
*
@@ -363,10 +392,30 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
MetaData metaSchema = Chat2DBContext.getMetaData();
try {
List tables = metaSchema.tables(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), null);
- return tables.stream()
- .map(table -> StringUtils.isBlank(table.getComment()) ? table.getName()
- : table.getName() + "(" + table.getComment() + ")")
- .collect(Collectors.joining(","));
+
+ return tables.stream().map(table -> {
+ StringBuilder sb = new StringBuilder(table.getName()); // 直接在初始化时加入表名
+ String comment = table.getComment();
+ List columns = metaSchema.columns(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), table.getName());
+ List foreignKeys = findPossibleForeignKeys(columns); // 假设这个方法已经被定义
+
+ // 只有当有注释或外键时才添加额外信息
+ if(StringUtils.isNotEmpty(comment) || !foreignKeys.isEmpty()){
+ sb.append("(").append(comment);
+
+ // 如果存在外键,添加外键信息
+ if(!foreignKeys.isEmpty()){
+ // 如果注释和外键都存在,先添加一个分隔符
+ if(StringUtils.isNotEmpty(comment)) {
+ sb.append("; ");
+ }
+ sb.append("外键:").append(String.join(", ", foreignKeys)); // 优化外键的展示
+ }
+ sb.append(")");
+ }
+ return sb.toString(); // 在映射阶段直接转换为字符串
+ })
+ .collect(Collectors.joining(","));
} catch (Exception e) {
log.error("query table error:{}, do nothing", e.getMessage());
return "";
From f6b4c36430244ebb61fb729f2c011f410122f662 Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Sun, 25 Feb 2024 11:05:26 +0800
Subject: [PATCH 009/350] =?UTF-8?q?=E5=8A=A0=E5=88=97=E7=BC=93=E5=AD=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../domain/core/impl/TableServiceImpl.java | 13 +++++++++++-
.../controller/ai/utils/PromptService.java | 21 ++++++++++++++-----
.../rdb/converter/RdbWebConverter.java | 9 ++++++++
3 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
index a5aaea130..46cf1e033 100644
--- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
@@ -339,6 +339,16 @@ public PageResult pageQuery(TablePageQueryParam param, TableSelector sele
t.setComment(tableCacheDO.getExtendInfo());
t.setSchemaName(tableCacheDO.getSchemaName());
t.setDatabaseName(tableCacheDO.getDatabaseName());
+ if(Boolean.TRUE.equals(selector.getColumnList())){
+ TableQueryParam tableQueryParam = new TableQueryParam();
+ tableQueryParam.setDataSourceId(param.getDataSourceId());
+ tableQueryParam.setDatabaseName(param.getDatabaseName());
+ tableQueryParam.setSchemaName(param.getSchemaName());
+ tableQueryParam.setTableName(tableCacheDO.getTableName());
+ tableQueryParam.setRefresh(false);
+ List columns = queryColumns(tableQueryParam);
+ t.setColumnList(columns);
+ }
tables.add(t);
}
}
@@ -433,6 +443,7 @@ private long addDBCache(Long dataSourceId, String databaseName, String schemaNam
tableCacheDO.setDataSourceId(dataSourceId);
tableCacheDO.setVersion(version);
tableCacheDO.setKey(key);
+ metaSchema.columns(connection, databaseName, schemaName, table.getName());
cacheDOS.add(tableCacheDO);
if (cacheDOS.size() >= 500) {
getTableCacheMapper().batchInsert(cacheDOS);
@@ -476,7 +487,7 @@ private Long getLock(Long dataSourceId, String databaseName, String schemaName,
}
} else {
long version = versionDO.getVersion() + 1;
- LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TableCacheVersionDO::getId, versionDO.getId());
queryWrapper.eq(TableCacheVersionDO::getVersion, versionDO.getVersion());
versionDO.setVersion(version);
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index b0ba984b1..47c1e1e0b 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -27,12 +27,15 @@
import ai.chat2db.server.domain.api.model.Config;
import ai.chat2db.server.domain.api.model.DataSource;
import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
+import ai.chat2db.server.domain.api.param.TablePageQueryParam;
import ai.chat2db.server.domain.api.param.TableQueryParam;
+import ai.chat2db.server.domain.api.param.TableSelector;
import ai.chat2db.server.domain.api.service.ConfigService;
import ai.chat2db.server.domain.api.service.DataSourceService;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.tools.base.enums.WhiteListTypeEnum;
import ai.chat2db.server.tools.base.wrapper.result.DataResult;
+import ai.chat2db.server.tools.base.wrapper.result.PageResult;
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient;
@@ -45,6 +48,7 @@
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.ai.rest.client.RestAIClient;
+import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;
import ai.chat2db.server.web.api.http.GatewayClientService;
import ai.chat2db.server.web.api.http.model.TableSchema;
import ai.chat2db.server.web.api.http.request.TableSchemaRequest;
@@ -84,6 +88,10 @@ public class PromptService {
private GatewayClientService gatewayClientService;
+ @Autowired
+ private RdbWebConverter rdbWebConverter;
+
+
/**
* 构建prompt
*
@@ -391,13 +399,16 @@ public static List findPossibleForeignKeys(List columns) {
public String queryDatabaseTables(ChatQueryRequest queryRequest) {
MetaData metaSchema = Chat2DBContext.getMetaData();
try {
- List tables = metaSchema.tables(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), null);
-
- return tables.stream().map(table -> {
+ TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(queryRequest);
+ TableSelector tableSelector = new TableSelector();
+ tableSelector.setColumnList(true);
+ tableSelector.setIndexList(false);
+ PageResult tables = tableService.pageQuery(queryParam,tableSelector);
+ return tables.getData().stream().map(table -> {
StringBuilder sb = new StringBuilder(table.getName()); // 直接在初始化时加入表名
String comment = table.getComment();
- List columns = metaSchema.columns(Chat2DBContext.getConnection(), queryRequest.getDatabaseName(), queryRequest.getSchemaName(), table.getName());
- List foreignKeys = findPossibleForeignKeys(columns); // 假设这个方法已经被定义
+ List columns = table.getColumnList();
+ List foreignKeys = findPossibleForeignKeys(columns);
// 只有当有注释或外键时才添加额外信息
if(StringUtils.isNotEmpty(comment) || !foreignKeys.isEmpty()){
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
index b04663dc9..5a37c352a 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
@@ -3,6 +3,7 @@
import java.util.List;
import ai.chat2db.server.domain.api.param.*;
+import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;
import ai.chat2db.server.web.api.controller.rdb.request.*;
import ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;
@@ -99,6 +100,14 @@ public abstract class RdbWebConverter {
* @return
*/
public abstract SqlVO dto2vo(Sql dto);
+
+ /**
+ * 参数转换
+ *
+ * @param request
+ * @return
+ */
+ public abstract TablePageQueryParam tablePageRequest2param(ChatQueryRequest request);
/**
* 参数转换
*
From a00f94ffeca5380db660fc893afe662837dbf71d Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Sun, 25 Feb 2024 16:49:09 +0800
Subject: [PATCH 010/350] =?UTF-8?q?er=E5=9B=BE=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/ai/utils/PromptService.java | 1 -
.../api/controller/rdb/TableController.java | 37 ++++++++++++++++
.../java/ai/chat2db/spi/model/ErDiagram.java | 42 +++++++++++++++++++
3 files changed, 79 insertions(+), 1 deletion(-)
create mode 100644 chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ErDiagram.java
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index 47c1e1e0b..b70f8b058 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -397,7 +397,6 @@ public static List findPossibleForeignKeys(List columns) {
* @throws IOException
*/
public String queryDatabaseTables(ChatQueryRequest queryRequest) {
- MetaData metaSchema = Chat2DBContext.getMetaData();
try {
TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(queryRequest);
TableSelector tableSelector = new TableSelector();
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
index 131a6bf6c..016f3bdad 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
@@ -250,4 +250,41 @@ public ActionResult delete(@Valid @RequestBody TableDeleteRequest request) {
DropParam dropParam = rdbWebConverter.tableDelete2dropParam(request);
return tableService.drop(dropParam);
}
+
+
+ /**
+ * 查询ER图
+ *
+ * @param request
+ * @return
+ */
+ @GetMapping("/er-diagram")
+ public DataResult erDiagram(@Valid TableBriefQueryRequest request) {
+ TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);
+ TableSelector tableSelector = new TableSelector();
+ tableSelector.setColumnList(true);
+ tableSelector.setIndexList(false);
+ PageResult tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);
+ new ArrayList<>();
+ List entityList = tableDTOPageResult.getData().stream().map(table -> {
+ ErDiagram.Node entity = new ErDiagram.Node(table.getName(),
+ StringUtils.defaultIfBlank(table.getComment(), table.getName()));
+ return entity;
+ }).collect(Collectors.toList());
+ List relationList = tableDTOPageResult.getData().stream().flatMap(table -> {
+ return table.getColumnList().stream().filter(column -> {
+ String columnName = column.getName();
+ Boolean primaryKey = column.getPrimaryKey();
+ return columnName != null && columnName.matches(".+_id") && Boolean.FALSE.equals(primaryKey);
+ }).map(column -> {
+ String columnName = column.getName();
+ String tableName = column.getTableName();
+ // 从列名中移除"_id"以获取可能的关联表名
+ String potentialForeignKeyTable = columnName.substring(0, columnName.length() - 3);
+ ErDiagram.Edge relation = new ErDiagram.Edge(columnName,tableName, potentialForeignKeyTable,column.getComment());
+ return relation;
+ });
+ }).collect(Collectors.toList());
+ return DataResult.of(new ErDiagram(entityList, relationList));
+ }
}
diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ErDiagram.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ErDiagram.java
new file mode 100644
index 000000000..67a2f9920
--- /dev/null
+++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ErDiagram.java
@@ -0,0 +1,42 @@
+package ai.chat2db.spi.model;
+
+import java.util.List;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * er图
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ErDiagram {
+
+ private List nodes;
+ private List edges;
+
+ @Data
+ @SuperBuilder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class Node {
+ private String id;
+ private String label;
+ }
+
+ @Data
+ @SuperBuilder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class Edge {
+ private String id;
+ private String source;
+ private String target;
+ private String label;
+ }
+
+}
From 63cbd75f034b2e275c3102529ff4b526949d6258 Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Wed, 28 Feb 2024 10:40:56 +0800
Subject: [PATCH 011/350] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../server/web/api/controller/rdb/TableController.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
index 016f3bdad..f2ce64dfd 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/TableController.java
@@ -18,17 +18,18 @@
import ai.chat2db.server.web.api.controller.rdb.vo.SqlVO;
import ai.chat2db.server.web.api.controller.rdb.vo.TableVO;
import ai.chat2db.spi.model.*;
-import ai.chat2db.spi.sql.Chat2DBContext;
-import ai.chat2db.spi.sql.ConnectInfo;
import com.google.common.collect.Lists;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
@Slf4j
@ConnectionInfoAspect
@@ -265,7 +266,6 @@ public DataResult erDiagram(@Valid TableBriefQueryRequest request) {
tableSelector.setColumnList(true);
tableSelector.setIndexList(false);
PageResult tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);
- new ArrayList<>();
List entityList = tableDTOPageResult.getData().stream().map(table -> {
ErDiagram.Node entity = new ErDiagram.Node(table.getName(),
StringUtils.defaultIfBlank(table.getComment(), table.getName()));
From 79d6ef3771375c4205582ac30dcf67d1454990e0 Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Wed, 6 Mar 2024 17:34:12 +0800
Subject: [PATCH 012/350] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BD=91=E5=85=B3?=
=?UTF-8?q?=E5=92=8Cazure=E6=8E=A5=E5=8F=A3=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 3 +-
chat2db-gateway/pom.xml | 80 ++++++++++
.../main/java/com/hejianjun/Application.java | 18 +++
.../hejianjun/ElasticsearchClientConfig.java | 41 +++++
.../java/com/hejianjun/SchemaDocument.java | 14 ++
.../com/hejianjun/TableSchemaController.java | 56 +++++++
.../com/hejianjun/TableSchemaRequest.java | 34 +++++
.../com/hejianjun/TableSchemaService.java | 107 +++++++++++++
.../aspect/GatewayClientServiceAspect.java | 33 ++++
.../web/api/controller/ai/ChatController.java | 19 ++-
.../azure/client/AzureOpenAiStreamClient.java | 12 +-
.../AzureOpenAIEventSourceListener.java | 142 ++++--------------
.../model/AzureChatCompletionsOptions.java | 9 ++
.../listener/OpenAIEventSourceListener.java | 5 +
.../controller/ai/utils/PromptService.java | 1 +
.../ZhipuChatAIEventSourceListener.java | 7 +-
16 files changed, 452 insertions(+), 129 deletions(-)
create mode 100644 chat2db-gateway/pom.xml
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/Application.java
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/ElasticsearchClientConfig.java
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/SchemaDocument.java
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaController.java
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
create mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaService.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/GatewayClientServiceAspect.java
diff --git a/.gitignore b/.gitignore
index f8a263aaa..793134d36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,5 @@ package-lock.json
/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/src/main/resources/lib/*
/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/lib/*
/lib
-/out/*
\ No newline at end of file
+/out/*
+/chat2db-gateway/target
diff --git a/chat2db-gateway/pom.xml b/chat2db-gateway/pom.xml
new file mode 100644
index 000000000..1e3e4b89a
--- /dev/null
+++ b/chat2db-gateway/pom.xml
@@ -0,0 +1,80 @@
+
+
+ 4.0.0
+
+ com.hejianjun
+ chat2db-gateway
+ 0.0.1-SNAPSHOT
+ jar
+
+ chat2db-gateway
+ Project for chat2db-gateway
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.7
+
+
+
+
+ 11
+ 8.12.2
+ 2.0.1
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ co.elastic.clients
+ elasticsearch-java
+ 8.12.2
+
+
+ jakarta.json
+ jakarta.json-api
+ ${jakarta-json.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.12.3
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/Application.java b/chat2db-gateway/src/main/java/com/hejianjun/Application.java
new file mode 100644
index 000000000..23f0f58f3
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/Application.java
@@ -0,0 +1,18 @@
+package com.hejianjun;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@Slf4j
+@SpringBootApplication
+public class Application {
+ /**
+ * 主程序入口
+ * @param args 命令行参数
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/ElasticsearchClientConfig.java b/chat2db-gateway/src/main/java/com/hejianjun/ElasticsearchClientConfig.java
new file mode 100644
index 000000000..e3d997bc1
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/ElasticsearchClientConfig.java
@@ -0,0 +1,41 @@
+package com.hejianjun;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.message.BasicHeader;
+import org.elasticsearch.client.RestClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ElasticsearchClientConfig {
+
+ String apiKey = "DVaOd3B6Rl*9sWUeTIHO";
+
+ /**
+ * 创建ElasticsearchClient实例
+ *
+ * @return ElasticsearchClient实例
+ */
+ @Bean
+ public ElasticsearchClient elasticsearchClient() {
+ // 初始化低级客户端
+ RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200))
+ .setDefaultHeaders(new Header[]{
+ new BasicHeader("Authorization", "ApiKey " + apiKey)
+ })
+ .build();
+
+ // 使用低级客户端创建传输层
+ ElasticsearchTransport transport = new RestClientTransport(
+ restClient, new JacksonJsonpMapper());
+
+ // 创建ElasticsearchClient实例
+ return new ElasticsearchClient(transport);
+ }
+
+}
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/SchemaDocument.java b/chat2db-gateway/src/main/java/com/hejianjun/SchemaDocument.java
new file mode 100644
index 000000000..b077e6508
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/SchemaDocument.java
@@ -0,0 +1,14 @@
+package com.hejianjun;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+public class SchemaDocument {
+ private String schema;
+ private List vector;
+}
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaController.java b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaController.java
new file mode 100644
index 000000000..93883b9d6
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaController.java
@@ -0,0 +1,56 @@
+package com.hejianjun;
+
+import co.elastic.clients.json.JsonData;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/api/client/milvus")
+public class TableSchemaController {
+
+ private final TableSchemaService service;
+
+
+ /**
+ * 保存表结构
+ * @param request 表结构请求对象
+ * @return 保存成功的文档ID
+ */
+ @PostMapping("/schema/save")
+ public ResponseEntity> saveSchema(@RequestBody TableSchemaRequest request) {
+ try {
+ List documentId = service.saveSchemaBatch(request);
+ return ResponseEntity.ok(documentId);
+ } catch (IOException e) {
+ log.error("保存表结构时发生错误", e);
+ return ResponseEntity.internalServerError().build();
+ }
+ }
+
+ /**
+ * 通过向量搜索表结构
+ * @param request 表结构搜索请求
+ * @return 搜索结果列表
+ */
+ @PostMapping("/schema/search")
+ public ResponseEntity searchByVector(@RequestBody TableSchemaRequest request) {
+ try {
+ TableSchemaRequest tableSchemaRequest = service.searchByVector(request);
+ return ResponseEntity.ok(tableSchemaRequest);
+ } catch (IOException e) {
+ log.error("Error searching schema", e);
+ return ResponseEntity.internalServerError().build();
+ }
+ }
+}
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
new file mode 100644
index 000000000..a3c72acf8
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
@@ -0,0 +1,34 @@
+package com.hejianjun;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 表结构请求
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TableSchemaRequest {
+
+ // 数据源ID
+ private Long dataSourceId;
+ // 数据库名称
+ private String databaseName;
+ // API密钥
+ private String apiKey;
+ // 数据源模式
+ private String dataSourceSchema;
+ // 模式向量
+ private List> schemaVector;
+ // 模式列表
+ private List schemaList;
+ // 插入前删除
+ private Boolean deleteBeforeInsert = false;
+}
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaService.java b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaService.java
new file mode 100644
index 000000000..0bf7c31bc
--- /dev/null
+++ b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaService.java
@@ -0,0 +1,107 @@
+package com.hejianjun;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch.core.BulkRequest;
+import co.elastic.clients.elasticsearch.core.BulkResponse;
+import co.elastic.clients.elasticsearch.core.IndexResponse;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.json.JsonData;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * TableSchemaService类用于处理表结构相关的操作。
+ */
+@Service
+@AllArgsConstructor
+public class TableSchemaService {
+
+ private final ElasticsearchClient client;
+
+ /**
+ * 批量保存表结构。
+ *
+ * @param request 表结构请求对象
+ * @return 保存成功后的每个文档的ID列表
+ * @throws IOException IO异常
+ */
+ public List saveSchemaBatch(TableSchemaRequest request) throws IOException {
+ List documentIds = new ArrayList<>();
+
+ // 构建批量请求
+ BulkRequest.Builder bulkBuilder = new BulkRequest.Builder();
+
+ String indexName = request.getDataSourceId() + request.getDatabaseName() + request.getDataSourceSchema();
+
+ for (int i = 0; i < request.getSchemaVector().size(); i++) {
+ // 假设schemaVector和schemaList的长度相同,并且一一对应
+ List vector = request.getSchemaVector().get(i);
+ String schema = request.getSchemaList().get(i);
+
+ // 创建文档内容,这里简化为Map,具体结构根据需求定义
+ SchemaDocument document = new SchemaDocument(schema,vector);
+
+ // 添加到批量请求
+ bulkBuilder.operations(op -> op
+ .index(idx -> idx
+ .index(indexName)
+ .document(document)
+ )
+ );
+ }
+
+ // 执行批量请求
+ BulkResponse bulkResponse = client.bulk(bulkBuilder.build());
+
+ // 收集文档ID
+ for (BulkResponseItem item : bulkResponse.items()) {
+ if (item.error()!=null) {
+ throw new IOException("Error indexing document: " + item.error().reason());
+ }
+ documentIds.add(item.id());
+ }
+
+ return documentIds;
+ }
+
+ /**
+ * 根据向量搜索表结构。
+ *
+ * @param request 表结构请求对象
+ * @return 搜索结果列表
+ * @throws IOException IO异常
+ */
+ public TableSchemaRequest searchByVector(TableSchemaRequest request) throws IOException {
+ String indexName = request.getDataSourceId() + request.getDatabaseName() + request.getDataSourceSchema();
+ List vector = request.getSchemaVector().get(0);
+ // 假设schemaVector已转换为适合Elasticsearch的格式
+ // 执行k-NN搜索
+ SearchResponse response = client.search(s -> s
+ .index(indexName)
+ // 这里添加k-NN查询逻辑,具体实现根据实际需求
+ , SchemaDocument.class
+ );
+ List> schemaVector = new ArrayList<>();
+ List schemaList = new ArrayList<>();
+ List> hits = response.hits().hits();
+ for (Hit hit: hits) {
+ SchemaDocument document = hit.source();
+ if(document!=null) {
+ schemaVector.add(document.getVector());
+ schemaList.add(document.getSchema());
+ }
+ }
+ request.setSchemaVector(schemaVector);
+ request.setSchemaList(schemaList);
+ return request;
+ }
+}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/GatewayClientServiceAspect.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/GatewayClientServiceAspect.java
new file mode 100644
index 000000000..01e4c0eef
--- /dev/null
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/GatewayClientServiceAspect.java
@@ -0,0 +1,33 @@
+package ai.chat2db.server.web.api.aspect;
+
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+public class GatewayClientServiceAspect {
+ /**
+ * 定义切点,匹配 GatewayClientService 类中的所有方法
+ */
+ @Pointcut("execution(* ai.chat2db.server.web.api.http.GatewayClientService.*(..)) && !execution(* ai.chat2db.server.web.api.http.GatewayClientService.checkInWhite(..))")
+ public void gatewayClientServiceMethods() {}
+
+
+
+ /**
+ * 环绕通知:在切点方法执行时触发
+ * @param joinPoint
+ * @return
+ * @throws Throwable
+ */
+ @Around("gatewayClientServiceMethods()")
+ public Object aroundGatewayClientServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
+ // 这里你可以执行一些自定义的逻辑,如果需要的话
+ // 然后返回 null 或其他默认值
+ return null;
+ }
+}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index 2a106fc10..973b890e7 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -11,6 +11,7 @@
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;
import ai.chat2db.server.web.api.controller.ai.azure.listener.AzureOpenAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatCompletionsOptions;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatMessage;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatRole;
import ai.chat2db.server.web.api.controller.ai.baichuan.client.BaichuanAIClient;
@@ -24,6 +25,7 @@
import ai.chat2db.server.web.api.controller.ai.config.LocalCache;
import ai.chat2db.server.web.api.controller.ai.fastchat.client.FastChatAIClient;
import ai.chat2db.server.web.api.controller.ai.fastchat.listener.FastChatAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatCompletionsOptions;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
import ai.chat2db.server.web.api.controller.ai.openai.client.OpenAIClient;
@@ -329,7 +331,7 @@ private SseEmitter chatWithChat2dbAi(ChatQueryRequest queryRequest, SseEmitter s
* @throws IOException
*/
private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
- String prompt = promptService.buildPrompt(queryRequest);
+ String prompt = promptService.buildAutoPrompt(queryRequest);
if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
@@ -347,9 +349,16 @@ private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sse
messages.add(currentMessage);
buildSseEmitter(sseEmitter, uid);
-
- AzureOpenAIEventSourceListener sourceListener = new AzureOpenAIEventSourceListener(sseEmitter);
- AzureOpenAIClient.getInstance().streamCompletions(messages, sourceListener);
+ LoginUser loginUser = ContextUtils.getLoginUser();
+ AzureOpenAIEventSourceListener sourceListener = new AzureOpenAIEventSourceListener(sseEmitter,promptService,queryRequest,loginUser);
+ AzureChatCompletionsOptions chatCompletionsOptions = new AzureChatCompletionsOptions(messages);
+ chatCompletionsOptions.setStream(true);
+ if(queryRequest.getDatabaseName()!=null){
+ ToolsFunction function = PromptService.getToolsFunction();
+ chatCompletionsOptions.setTools(List.of(new Tools(Tools.Type.FUNCTION.getName(), function)));
+ chatCompletionsOptions.setToolChoice("auto");
+ }
+ AzureOpenAIClient.getInstance().streamCompletions(chatCompletionsOptions, sourceListener);
LocalCache.CACHE.put(uid, messages, LocalCache.TIMEOUT);
return sseEmitter;
}
@@ -397,7 +406,7 @@ private SseEmitter chatWithZhipuChatAi(ChatQueryRequest queryRequest, SseEmitter
ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
.requestId(requestId)
.stream(true)
- .toolChoice("auto")
+
.messages(messages)
.build();
if(queryRequest.getDatabaseName()!=null){
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java
index 338f5b1c1..6ae245590 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java
@@ -149,22 +149,14 @@ public AzureOpenAiStreamClient build() {
* @param chatMessages
* @param eventSourceListener
*/
- public void streamCompletions(List chatMessages, EventSourceListener eventSourceListener) {
- if (CollectionUtils.isEmpty(chatMessages)) {
- log.error("param error:Azure Prompt cannot be empty");
- throw new ParamBusinessException("prompt");
- }
+ public void streamCompletions(AzureChatCompletionsOptions chatCompletionsOptions, EventSourceListener eventSourceListener) {
if (Objects.isNull(eventSourceListener)) {
log.error("param error:AzureEventSourceListener cannot be empty");
throw new ParamBusinessException();
}
- log.info("Azure Open AI, prompt:{}", chatMessages.get(chatMessages.size() - 1).getContent());
try {
-
- AzureChatCompletionsOptions chatCompletionsOptions = new AzureChatCompletionsOptions(chatMessages);
chatCompletionsOptions.setStream(true);
chatCompletionsOptions.setModel(this.deployId);
-
EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -172,7 +164,7 @@ public void streamCompletions(List chatMessages, EventSourceLi
if (!endpoint.endsWith("/")) {
endpoint = endpoint + "/";
}
- String url = this.endpoint + "openai/deployments/"+ deployId + "/chat/completions?api-version=2023-05-15";
+ String url = this.endpoint + "openai/deployments/"+ deployId + "/chat/completions?api-version=2024-02-15-preview";
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java
index 4488bd6b8..2b9ab4a99 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java
@@ -1,15 +1,32 @@
package ai.chat2db.server.web.api.controller.ai.azure.listener;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
+import ai.chat2db.server.tools.common.model.LoginUser;
+import ai.chat2db.server.web.api.controller.ai.azure.client.AzureOpenAIClient;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatChoice;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatCompletions;
+import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatCompletionsOptions;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatMessage;
+import ai.chat2db.server.web.api.controller.ai.azure.model.AzureChatRole;
import ai.chat2db.server.web.api.controller.ai.azure.model.AzureCompletionsUsage;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
+import ai.chat2db.server.web.api.controller.ai.openai.listener.OpenAIEventSourceListener;
+import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
+import ai.chat2db.server.web.api.controller.ai.utils.PromptService;
+import ai.chat2db.server.web.api.controller.ai.zhipu.client.ZhipuChatAIClient;
+import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
+
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unfbx.chatgpt.entity.chat.Message;
+import com.unfbx.chatgpt.entity.chat.tool.Tools;
+import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction;
+
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
@@ -26,123 +43,26 @@
* @date 2023-02-22
*/
@Slf4j
-public class AzureOpenAIEventSourceListener extends EventSourceListener {
+public class AzureOpenAIEventSourceListener extends OpenAIEventSourceListener {
- private SseEmitter sseEmitter;
- private ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-
- public AzureOpenAIEventSourceListener(SseEmitter sseEmitter) {
- this.sseEmitter = sseEmitter;
+ public AzureOpenAIEventSourceListener(SseEmitter sseEmitter, PromptService promptService,
+ ChatQueryRequest queryRequest, LoginUser loginUser) {
+ super(sseEmitter, promptService, queryRequest, loginUser);
}
- /**
- * {@inheritDoc}
- */
@Override
- public void onOpen(EventSource eventSource, Response response) {
- log.info("AzureOpenAI建立sse连接...");
+ public String getName(){
+ return "AzureOpenAI";
}
- /**
- * {@inheritDoc}
- */
- @SneakyThrows
- @Override
- public void onEvent(EventSource eventSource, String id, String type, String data) {
- log.info("AzureOpenAI返回数据:{}", data);
- if (data.equals("[DONE]")) {
- log.info("AzureOpenAI返回数据结束了");
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]")
- .reconnectTime(3000));
- sseEmitter.complete();
- return;
- }
-
- AzureChatCompletions chatCompletions = mapper.readValue(data, AzureChatCompletions.class);
- String text = "";
- log.info("Model ID={} is created at {}.", chatCompletions.getId(),
- chatCompletions.getCreated());
- for (AzureChatChoice choice : chatCompletions.getChoices()) {
- AzureChatMessage message = choice.getDelta();
- if (message != null) {
- log.info("Index: {}, Chat Role: {}", choice.getIndex(), message.getRole());
- if (message.getContent() != null) {
- text = message.getContent();
- }
- }
- }
-
- AzureCompletionsUsage usage = chatCompletions.getUsage();
- if (usage != null) {
- log.info(
- "Usage: number of prompt token is {}, number of completion token is {}, and number of total "
- + "tokens in request and response is {}.%n", usage.getPromptTokens(),
- usage.getCompletionTokens(), usage.getTotalTokens());
- }
-
- Message message = new Message();
- message.setContent(text);
- sseEmitter.send(SseEmitter.event()
- .id(null)
- .data(message)
- .reconnectTime(3000));
- }
-
- @Override
- public void onClosed(EventSource eventSource) {
- try {
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- sseEmitter.complete();
- log.info("AzureOpenAI close sse connection...");
- }
-
- @Override
- public void onFailure(EventSource eventSource, Throwable t, Response response) {
- try {
- if (Objects.isNull(response)) {
- String message = t.getMessage();
- Message sseMessage = new Message();
- sseMessage.setContent(message);
- sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(sseMessage));
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- sseEmitter.complete();
- return;
- }
- ResponseBody body = response.body();
- String bodyString = Objects.nonNull(t) ? t.getMessage() : "";
- if (Objects.nonNull(body)) {
- bodyString = body.string();
- if (StringUtils.isBlank(bodyString) && Objects.nonNull(t)) {
- bodyString = t.getMessage();
- }
- log.error("Azure OpenAI sse response:{}", bodyString);
- } else {
- log.error("Azure OpenAI sse response:{},error:{}", response, t);
- }
- eventSource.cancel();
- Message message = new Message();
- message.setContent("Azure OpenAI error:" + bodyString);
- sseEmitter.send(SseEmitter.event()
- .id("[ERROR]")
- .data(message));
- sseEmitter.send(SseEmitter.event()
- .id("[DONE]")
- .data("[DONE]"));
- sseEmitter.complete();
- } catch (Exception exception) {
- log.error("Azure OpenAI发送数据异常:", exception);
- }
+ @Override
+ public void functionCall(String prompt){
+ AzureChatMessage currentMessage = new AzureChatMessage(AzureChatRole.USER).setContent(prompt);
+ List messages = new ArrayList<>();
+ messages.add(currentMessage);
+ AzureChatCompletionsOptions chatCompletionsOptions = new AzureChatCompletionsOptions(messages);
+ chatCompletionsOptions.setStream(true);
+ AzureOpenAIClient.getInstance().streamCompletions(chatCompletionsOptions, this);
}
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java
index 1d6198e57..8d33166b3 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java
@@ -7,6 +7,8 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.unfbx.chatgpt.entity.chat.tool.Tools;
+
import lombok.Data;
/**
@@ -391,4 +393,11 @@ public AzureChatCompletionsOptions setModel(String model) {
this.model = model;
return this;
}
+
+ // 新添加的参数
+ @JsonProperty(value = "tool_choice")
+ private String toolChoice; // 工具选择策略
+
+ @JsonProperty(value = "tools")
+ private List tools; // 工具列表
}
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index 2d63e9f4c..36afe4e02 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -22,6 +22,8 @@
import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
+
+import org.apache.commons.collections4.CollectionUtils;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.ArrayList;
@@ -193,6 +195,9 @@ public void onEvent(EventSource eventSource, String id, String type, String data
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 读取Json
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);
+ if(CollectionUtils.isEmpty(completionResponse.getChoices())){
+ return;
+ }
Message delta = completionResponse.getChoices().get(0).getDelta();
if (delta != null && delta.getToolCalls() != null) {
this.toolCalls = mergeToolCallsLists(this.toolCalls, delta.getToolCalls());
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index b70f8b058..c84831224 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -13,6 +13,7 @@
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
index abc07a8e1..a02668775 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
@@ -2,6 +2,7 @@
import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatMessage;
+import ai.chat2db.server.web.api.controller.ai.fastchat.model.FastChatRole;
import ai.chat2db.server.web.api.controller.ai.openai.listener.OpenAIEventSourceListener;
import ai.chat2db.server.web.api.controller.ai.request.ChatQueryRequest;
import ai.chat2db.server.web.api.controller.ai.utils.PromptService;
@@ -9,6 +10,7 @@
import ai.chat2db.server.web.api.controller.ai.zhipu.model.ZhipuChatCompletionsOptions;
import lombok.extern.slf4j.Slf4j;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -38,8 +40,9 @@ public String getName(){
@Override
public void functionCall(String prompt){
- Long uid = loginUser.getId();
- List messages = promptService.getFastChatMessage(Objects.toString(uid), prompt);
+ FastChatMessage currentMessage = new FastChatMessage(FastChatRole.USER).setContent(prompt);
+ List messages = new ArrayList<>();
+ messages.add(currentMessage);
String requestId = String.valueOf(System.currentTimeMillis());
ToolsFunction function = PromptService.getToolsFunction();
ZhipuChatCompletionsOptions completionsOptions = ZhipuChatCompletionsOptions.builder()
From b2b41480600599eaca402762ab5af91d0a7d469e Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Sat, 30 Mar 2024 21:48:33 +0800
Subject: [PATCH 013/350] =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=9F=A5=E8=AF=A2?=
=?UTF-8?q?=E8=A1=A8=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/com/hejianjun/TableSchemaRequest.java | 4 ++++
.../src/main/resources/mapper/TableCacheMapper.xml | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
index a3c72acf8..6e28ca0ea 100644
--- a/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
+++ b/chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
@@ -18,16 +18,20 @@
public class TableSchemaRequest {
// 数据源ID
+ @NotNull
private Long dataSourceId;
// 数据库名称
+ @NotNull
private String databaseName;
// API密钥
private String apiKey;
// 数据源模式
private String dataSourceSchema;
// 模式向量
+ @NotNull
private List> schemaVector;
// 模式列表
+ @NotNull
private List schemaList;
// 插入前删除
private Boolean deleteBeforeInsert = false;
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml b/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml
index c367e2605..37efbd21c 100644
--- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml
@@ -25,7 +25,7 @@
and tc.schema_name = #{schemaName}
- and LOWER(tc.table_name) like LOWER(concat('%',#{searchKey},'%'))
+ and (LOWER(tc.table_name) like LOWER(concat('%',#{searchKey},'%')) or tc.extend_info like concat('%',#{searchKey},'%'))
From 6bcdea72d507f80c48c8edc3a3821cac70d7e92b Mon Sep 17 00:00:00 2001
From: hejianjun <942156265@qq.com>
Date: Mon, 1 Apr 2024 16:11:03 +0800
Subject: [PATCH 014/350] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A1=A8=E9=80=89?=
=?UTF-8?q?=E6=8B=A9=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../web/api/controller/ai/ChatController.java | 2 +
.../listener/OpenAIEventSourceListener.java | 43 +++++++++++++------
.../controller/ai/utils/PromptService.java | 6 ++-
3 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
index 973b890e7..8a4ca74eb 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/ChatController.java
@@ -336,6 +336,8 @@ private SseEmitter chatWithAzureAi(ChatQueryRequest queryRequest, SseEmitter sse
log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
throw new ParamBusinessException();
+ }else{
+ log.info("提示词 :{}",prompt);
}
List messages = (List)LocalCache.CACHE.get(uid);
if (CollectionUtils.isNotEmpty(messages)) {
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
index 36afe4e02..de39b8bcb 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
@@ -24,9 +24,13 @@
import okhttp3.sse.EventSourceListener;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
import java.util.List;
import java.util.Objects;
@@ -134,17 +138,24 @@ public void functionCall(String prompt){
}
- public void handleTableNames(List tableNames,Object instance){
- if(instance instanceof JSONArray){
- ((JSONArray)instance).forEach(tableName->{
- handleTableNames(tableNames,tableName);
- });
- }else if (instance instanceof JSONObject) {
- ((JSONObject)instance).entrySet().forEach(entrySet->{
- handleTableNames(tableNames,entrySet.getValue());
- });
- }else if (instance instanceof String) {
- tableNames.add((String)instance);
+ public void handleTableNames(Set tableNames, Object instance) {
+ if (instance instanceof JSONArray) {
+ ((JSONArray) instance).forEach(item -> handleTableNames(tableNames, item));
+ } else if (instance instanceof JSONObject) {
+ ((JSONObject) instance).forEach((key, value) -> handleTableNames(tableNames, value));
+ } else if (instance instanceof String) {
+ String tableName = (String) instance;
+ List queryTableNames = queryRequest.getTableNames();
+ if (queryTableNames != null) {
+ String mostSimilarTableName = queryTableNames.stream()
+ // 根据相似度排序
+ .min(Comparator.comparingInt(existingTableName -> StringUtils.getLevenshteinDistance(existingTableName, tableName)))
+ .orElse(tableName);
+ tableNames.add(mostSimilarTableName);
+ }else{
+ tableNames.add(tableName);
+ }
+
}
}
/**
@@ -165,7 +176,7 @@ public void onEvent(EventSource eventSource, String id, String type, String data
sseEmitter.complete();
return;
}
- List tableNames = new ArrayList<>();
+ Set tableNames = new HashSet<>();
for (ToolCalls toolCall : toolCalls) {
String callId = toolCall.getId();
ToolCallFunction function = toolCall.getFunction();
@@ -177,8 +188,12 @@ public void onEvent(EventSource eventSource, String id, String type, String data
}
}
}
-
- queryRequest.setTableNames(tableNames);
+ Message message = new Message();
+ message.setContent("选择表" + tableNames);
+ sseEmitter.send(SseEmitter.event()
+ .data(message)
+ .reconnectTime(3000));
+ queryRequest.setTableNames(new ArrayList<>(tableNames));
ContextUtils.setContext(Context.builder()
.loginUser(loginUser)
.build());
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
index c84831224..9b52411bf 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/utils/PromptService.java
@@ -404,7 +404,9 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
tableSelector.setColumnList(true);
tableSelector.setIndexList(false);
PageResult tables = tableService.pageQuery(queryParam,tableSelector);
- return tables.getData().stream().map(table -> {
+ List tableNames = new ArrayList<>();
+ String properties = tables.getData().stream().map(table -> {
+ tableNames.add(table.getName());
StringBuilder sb = new StringBuilder(table.getName()); // 直接在初始化时加入表名
String comment = table.getComment();
List columns = table.getColumnList();
@@ -427,6 +429,8 @@ public String queryDatabaseTables(ChatQueryRequest queryRequest) {
return sb.toString(); // 在映射阶段直接转换为字符串
})
.collect(Collectors.joining(","));
+ queryRequest.setTableNames(tableNames);
+ return properties;
} catch (Exception e) {
log.error("query table error:{}, do nothing", e.getMessage());
return "";
From fb86ddbf320eedde10d73a598ac3750dd2004f8d Mon Sep 17 00:00:00 2001
From: hejianjun
Date: Thu, 9 Apr 2026 17:25:39 +0800
Subject: [PATCH 015/350] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=95=B4=E4=B8=AA?=
=?UTF-8?q?=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.opencode/skills/build/SKILL.md | 51 +
.vscode/launch.json | 21 +
.vscode/settings.json | 4 +-
AGENTS.md | 268 +
INITIALIZATION_GUIDE.md | 131 +
StateMachine.md | 142 +
chat2db-client/.gitignore | 4 +-
chat2db-client/.umirc.ts | 8 +-
chat2db-client/.yarnrc.yml | 1 +
chat2db-client/package.json | 17 +-
chat2db-client/src/assets/font/demo.css | 539 -
.../src/assets/font/demo_index.html | 4880 ---
.../src/assets/font/iconfont-he.ttf | Bin 0 -> 2316 bytes
.../src/assets/font/iconfont-he.woff | Bin 0 -> 1600 bytes
.../src/assets/font/iconfont-he.woff2 | Bin 0 -> 1192 bytes
chat2db-client/src/assets/font/iconfont.css | 7 +
.../assets/img/databaseImg/phoenixLogo.png | Bin 0 -> 4037 bytes
.../DatabaseTableEditor/BaseInfo/index.tsx | 17 +-
.../DatabaseTableEditor/ColumnList/index.tsx | 37 +-
.../ForeignKeyList/index.less | 162 +
.../ForeignKeyList/index.tsx | 406 +
.../src/blocks/DatabaseTableEditor/index.tsx | 144 +-
.../blocks/Setting/AiSetting/aiTypeConfig.ts | 71 +-
.../src/blocks/Setting/AiSetting/index.tsx | 107 +-
.../src/blocks/Tree/functions/openAsyncSql.ts | 3 +
.../blocks/Tree/functions/truncateTable.tsx | 66 +
.../blocks/Tree/hooks/useGetRightClickMenu.ts | 119 +-
chat2db-client/src/blocks/Tree/index.tsx | 3 +-
chat2db-client/src/blocks/Tree/treeConfig.tsx | 50 +-
.../src/components/AiChat/index.less | 154 +
.../src/components/AiChat/index.tsx | 294 +
.../src/components/CascaderDB/index.tsx | 6 +-
.../ConnectionEdit/config/dataSource.ts | 118 +
.../components/ChatInput/index.less | 12 +-
.../components/ChatInput/index.tsx | 67 +-
.../components/OperationLine/index.less | 9 +-
.../components/SelectBoundInfo/index.less | 6 +-
.../components/SelectBoundInfo/index.tsx | 31 +-
.../src/components/ConsoleEditor/index.less | 85 +-
.../src/components/ConsoleEditor/index.tsx | 338 +-
.../src/components/Iconfont/index.less | 14 +-
chat2db-client/src/components/Tabs/index.tsx | 25 +-
chat2db-client/src/constants/common.ts | 1 +
chat2db-client/src/constants/database.ts | 22 +-
chat2db-client/src/constants/tree.ts | 5 +
chat2db-client/src/constants/workspace.ts | 4 +
chat2db-client/src/i18n/en-us/common.ts | 11 +-
chat2db-client/src/i18n/en-us/editTable.ts | 11 +
chat2db-client/src/i18n/en-us/setting.ts | 4 -
chat2db-client/src/i18n/en-us/workspace.ts | 5 +
chat2db-client/src/i18n/zh-cn/common.ts | 16 +-
chat2db-client/src/i18n/zh-cn/editTable.ts | 11 +
chat2db-client/src/i18n/zh-cn/setting.ts | 4 -
chat2db-client/src/i18n/zh-cn/workspace.ts | 8 +
.../src/layouts/GlobalLayout/index.tsx | 18 +-
chat2db-client/src/main/package.json | 1 +
chat2db-client/src/main/yarn.lock | 26 +-
.../pages/main/dashboard/chart-item/index.tsx | 1 -
.../workspace/components/ERDiagram/index.less | 32 +
.../workspace/components/ERDiagram/index.tsx | 120 +
.../workspace/components/SQLExecute/index.tsx | 1 -
.../components/ViewAllTable/index.tsx | 224 +-
.../components/WorkspaceExtend/config.tsx | 13 +-
.../components/WorkspaceTabs/index.tsx | 132 +-
.../pages/main/workspace/store/aiChatStore.ts | 166 +
.../src/pages/main/workspace/store/common.ts | 14 +
chat2db-client/src/service/sql.ts | 124 +-
chat2db-client/src/typings/ai.ts | 8 +-
chat2db-client/src/typings/database.ts | 5 +
chat2db-client/src/typings/editTable.ts | 17 +
chat2db-client/src/typings/setting.ts | 34 +-
chat2db-client/src/utils/database.ts | 4 +-
chat2db-client/src/utils/eventSource.ts | 116 +-
chat2db-client/yarn.lock | 28213 ++++++++++------
chat2db-gateway/pom.xml | 80 -
.../main/java/com/hejianjun/Application.java | 18 -
.../hejianjun/ElasticsearchClientConfig.java | 41 -
.../java/com/hejianjun/SchemaDocument.java | 14 -
.../com/hejianjun/TableSchemaController.java | 56 -
.../com/hejianjun/TableSchemaRequest.java | 38 -
.../com/hejianjun/TableSchemaService.java | 107 -
.../ai/chat2db/plugin/db2/DB2MetaData.java | 2 +-
.../java/ai/chat2db/plugin/dm/DMMetaData.java | 3 +-
.../java/ai/chat2db/plugin/hive/hive.json | 4 +-
.../plugin/kingbase/KingBaseMetaData.java | 2 +-
.../chat2db/plugin/mysql/MysqlMetaData.java | 15 +-
.../plugin/mysql/builder/MysqlSqlBuilder.java | 312 +-
.../chat2db/plugin/oracle/OracleMetaData.java | 2 +-
.../oracle/type/OracleColumnTypeEnum.java | 2 +-
.../chat2db-plugins/chat2db-phoenix/pom.xml | 35 +
.../plugin/phoenix/PhoenixDBManage.java | 17 +
.../plugin/phoenix/PhoenixMetaData.java | 42 +
.../chat2db/plugin/phoenix/PhoenixPlugin.java | 24 +
.../phoenix/builder/PhoenixSqlBuilder.java | 139 +
.../ai/chat2db/plugin/phoenix/phoenix.json | 28 +
.../phoenix/type/PhoenixColumnTypeEnum.java | 198 +
.../phoenix/type/PhoenixDefaultValueEnum.java | 27 +
.../phoenix/type/PhoenixIndexTypeEnum.java | 181 +
.../META-INF/services/ai.chat2db.spi.Plugin | 1 +
.../plugin/postgresql/PostgreSQLMetaData.java | 2 +-
.../chat2db/plugin/redis/RedisMetaData.java | 112 +-
.../java/ai/chat2db/plugin/redis/redis.json | 6 +-
.../chat2db/plugin/sqlite/SqliteMetaData.java | 2 +-
.../sqlserver/SqlServerCommandExecutor.java | 1 +
.../plugin/sqlserver/SqlServerMetaData.java | 2 +-
chat2db-server/chat2db-plugins/pom.xml | 1 +
.../domain/api/constant/AiConfigKeys.java | 29 +
.../domain/api/enums/AiSqlSourceEnum.java | 47 +-
.../server/domain/api/model/AIConfig.java | 59 +-
.../server/domain/api/param/DropKeyParam.java | 49 +
.../server/domain/api/param/DropParam.java | 5 +-
.../domain/api/param/TablePageQueryParam.java | 11 +-
.../domain/api/param/TableQueryParam.java | 12 +-
.../domain/api/param/TableSelector.java | 4 +
.../domain/api/service/DataSourceService.java | 3 +-
.../api/service/LoginAttemptService.java | 8 +
.../domain/api/service/TableService.java | 52 +-
.../domain/api/service/UserService.java | 4 +-
.../chat2db-server-domain-core/pom.xml | 29 +-
.../server/domain/core/cache/CacheKey.java | 14 +-
.../domain/core/cache/LuceneIndexManager.java | 474 +
.../core/cache/LuceneIndexManagerFactory.java | 43 +
.../domain/core/converter/TableConverter.java | 17 -
.../core/impl/LoginAttemptServiceImpl.java | 77 +
.../domain/core/impl/TableServiceImpl.java | 632 +-
.../server/domain/repository/Dbutils.java | 2 +-
.../repository/entity/LoginAttempt.java | 20 +
.../repository/entity/TableCacheDO.java | 83 -
.../entity/TableCacheVersionDO.java | 78 -
.../entity/TableVectorMappingDO.java | 55 -
.../repository/mapper/LoginAttemptMapper.java | 18 +
.../repository/mapper/TableCacheMapper.java | 24 -
.../mapper/TableCacheVersionMapper.java | 16 -
.../mapper/TableVectorMappingMapper.java | 16 -
.../resources/db/migration/V2_1_10__user.sql | 1 +
.../db/migration/V2_1_11__login_attempt.sql | 7 +
...ppingMapper.xml => LoginAttemptMapper.xml} | 2 +-
.../resources/mapper/TableCacheMapper.xml | 32 -
...ation.java => Chat2dbLiteApplication.java} | 17 +-
.../start/config/config/ThreadPoolConfig.java | 43 +
.../controller/oauth/OauthController.java | 14 +-
.../thymeleaf/ThymeleafController.java | 6 -
.../src/main/resources/application.yml | 5 +
.../src/main/resources/logback-spring.xml | 3 +-
.../src/main/resources/thymeleaf/index.html | 52 +
.../server/start/test/TestApplication.java | 6 +-
.../server/start/test/common/BaseTest.java | 4 +-
.../chat2db/server/test/common/BaseTest.java | 4 +-
.../tools/base/enums/DataSourceTypeEnum.java | 5 +-
.../base/wrapper/param/PageQueryParam.java | 6 +
.../wrapper/request/PageQueryRequest.java | 6 +
.../tools/base/wrapper/result/PageResult.java | 34 +-
.../wrapper/result/web/WebPageResult.java | 38 +-
.../tools/common/util/EasyBooleanUtils.java | 4 +-
.../tools/common/util/EasyIntegerUtils.java | 4 +-
.../chat2db-server-web-start/pom.xml | 4 +
...cation.java => Chat2dbWebApplication.java} | 27 +-
.../start/config/config/ThreadPoolConfig.java | 43 +
.../controller/oauth/OauthController.java | 57 +-
.../thymeleaf/ThymeleafController.java | 5 -
.../src/main/resources/application.yml | 7 +-
.../static/front/1065.fe92f3dd.async.js | 1 +
.../converter/EnvironmentCommonConverter.java | 4 +-
.../chat2db-server-web-api/pom.xml | 21 +-
.../aspect/GatewayClientServiceAspect.java | 33 -
.../server/web/api/config/AiChatConfig.java | 164 +
.../api/controller/ai/AiConfigController.java | 131 -
.../web/api/controller/ai/ChatController.java | 587 +-
.../controller/ai/EmbeddingController.java | 367 -
.../controller/ai/KnowledgeController.java | 134 -
.../ai/TextGenerationController.java | 92 -
.../ai/azure/client/AzureOpenAIClient.java | 89 -
.../azure/client/AzureOpenAiStreamClient.java | 182 -
.../AzureHeaderAuthorizationInterceptor.java | 42 -
.../AzureOpenAIEventSourceListener.java | 68 -
.../ai/azure/model/AzureChatChoice.java | 91 -
.../ai/azure/model/AzureChatCompletions.java | 96 -
.../model/AzureChatCompletionsOptions.java | 403 -
.../ai/azure/model/AzureChatMessage.java | 61 -
.../ai/azure/model/AzureChatRole.java | 46 -
.../ai/azure/model/AzureChoice.java | 97 -
.../ai/azure/model/AzureCompletions.java | 98 -
.../model/AzureCompletionsFinishReason.java | 51 -
.../AzureCompletionsLogProbabilityModel.java | 96 -
.../ai/azure/model/AzureCompletionsUsage.java | 78 -
.../model/AzureExpandableStringEnum.java | 147 -
.../ai/azure/util/AzureReflectionUtils.java | 190 -
.../ai/baichuan/client/BaichuanAIClient.java | 93 -
.../client/BaichuanAIStreamClient.java | 227 -
...aichuanHeaderAuthorizationInterceptor.java | 109 -
.../BaichuanChatAIEventSourceListener.java | 141 -
.../model/BaichuanChatCompletions.java | 52 -
.../model/BaichuanChatCompletionsOptions.java | 39 -
.../model/BaichuanChatCompletionsUsage.java | 53 -
.../ai/baichuan/model/BaichuanChatData.java | 39 -
.../baichuan/model/BaichuanChatMessage.java | 30 -
.../chat2db/client/Chat2DBAIStreamClient.java | 261 -
.../ai/chat2db/client/Chat2dbAIClient.java | 97 -
...Chat2dbHeaderAuthorizationInterceptor.java | 46 -
.../Chat2dbAIEventSourceListener.java | 129 -
.../ai/claude/client/ClaudeAIClient.java | 87 -
.../claude/client/ClaudeAiStreamClient.java | 188 -
.../ClaudeHeaderAuthorizationInterceptor.java | 40 -
.../listener/ClaudeAIEventSourceListener.java | 112 -
.../model/ClaudeChatCompletionsOptions.java | 38 -
.../ai/claude/model/ClaudeChatMessage.java | 15 -
.../model/ClaudeCompletionResponse.java | 25 -
.../ai/claude/model/ClaudeMessageLimit.java | 9 -
.../ai/converter/ChatConverter.java | 26 -
.../api/controller/ai/enums/PromptType.java | 13 +-
.../ai/fastchat/client/FastChatAIClient.java | 80 -
.../client/FastChatAIStreamClient.java | 243 -
.../ai/fastchat/client/FastChatOpenAiApi.java | 54 -
.../embeddings/FastChatEmbedding.java | 73 -
.../embeddings/FastChatEmbeddingResponse.java | 22 -
.../ai/fastchat/embeddings/FastChatItem.java | 14 -
...astChatHeaderAuthorizationInterceptor.java | 40 -
.../FastChatAIEventSourceListener.java | 148 -
.../ai/fastchat/model/FastChatChoice.java | 97 -
.../fastchat/model/FastChatCompletions.java | 110 -
.../FastChatCompletionsFinishReason.java | 51 -
.../model/FastChatCompletionsOptions.java | 92 -
.../model/FastChatCompletionsUsage.java | 80 -
.../model/FastChatExpandableStringEnum.java | 147 -
.../ai/fastchat/model/FastChatMessage.java | 61 -
.../ai/fastchat/model/FastChatRole.java | 46 -
.../ai/openai/client/OpenAIClient.java | 128 -
.../listener/OpenAIEventSourceListener.java | 281 -
.../ai/rest/client/RestAIClient.java | 71 -
.../ai/rest/client/RestAiStreamClient.java | 166 -
.../listener/RestAIEventSourceListener.java | 118 -
.../ai/rest/model/RestAiCompletion.java | 26 -
.../ai/statemachine/ChatContext.java | 24 +
.../controller/ai/statemachine/ChatEvent.java | 44 +
.../controller/ai/statemachine/ChatState.java | 27 +
.../statemachine/ChatStateMachineConfig.java | 97 +
.../actions/AutoSelectTablesAction.java | 118 +
.../statemachine/actions/BaseChatAction.java | 72 +
.../actions/BuildPromptAction.java | 98 +
.../actions/FetchSchemaAction.java | 81 +
.../ai/statemachine/actions/StreamAction.java | 102 +
.../ai/tongyi/client/TongyiChatAIClient.java | 80 -
.../client/TongyiChatAIStreamClient.java | 212 -
.../TongyiChatAIEventSourceListener.java | 131 -
.../tongyi/model/TongyiChatCompletions.java | 46 -
.../model/TongyiChatCompletionsOptions.java | 41 -
.../model/TongyiChatCompletionsUsage.java | 45 -
.../ai/tongyi/model/TongyiChatMessage.java | 13 -
.../ai/tongyi/model/TongyiChatOutput.java | 43 -
.../controller/ai/utils/PromptService.java | 463 +-
.../ai/wenxin/client/WenxinAIClient.java | 78 -
.../wenxin/client/WenxinAIStreamClient.java | 200 -
.../interceptor/AccessTokenInterceptor.java | 35 -
.../listener/WenxinAIEventSourceListener.java | 128 -
.../wenxin/model/WenxinChatCompletions.java | 74 -
.../ai/zhipu/client/ZhipuChatAIClient.java | 80 -
.../zhipu/client/ZhipuChatAIStreamClient.java | 211 -
...ipuChatHeaderAuthorizationInterceptor.java | 43 -
.../ZhipuChatAIEventSourceListener.java | 57 -
.../ai/zhipu/model/ZhipuChatBody.java | 32 -
.../ai/zhipu/model/ZhipuChatCompletions.java | 44 -
.../model/ZhipuChatCompletionsOptions.java | 59 -
.../controller/ai/zhipu/util/ZhipuUtils.java | 41 -
.../controller/config/ConfigController.java | 381 +-
.../config/request/AIConfigCreateRequest.java | 59 +-
.../controller/rdb/DatabaseController.java | 20 +-
.../api/controller/rdb/RdbDdlController.java | 112 +-
.../api/controller/rdb/RdbDmlController.java | 82 +-
.../rdb/RdbDmlExportController.java | 20 +-
.../api/controller/rdb/TableController.java | 131 +-
.../rdb/converter/RdbWebConverter.java | 60 +-
.../request/BatchTableModifySqlRequest.java | 27 +
.../rdb/request/KeyDeleteRequest.java | 18 +
.../rdb/request/TableDeleteRequest.java | 5 +-
.../controller/rdb/request/TableRequest.java | 7 +
.../web/api/controller/rdb/vo/SchemaVO.java | 4 +
.../web/api/controller/rdb/vo/TableVO.java | 12 +
.../controller/system/SystemController.java | 5 -
.../controller/system/util/SystemUtils.java | 11 +-
.../api/controller/task/TaskController.java | 2 +-
.../web/api/http/GatewayClientService.java | 219 -
.../api/http/request/TableSchemaRequest.java | 1 +
.../chat2db/server/web/api/ws/WsService.java | 13 -
.../main/java/ai/chat2db/spi/DBManage.java | 8 +
.../main/java/ai/chat2db/spi/MetaData.java | 16 +-
.../ai/chat2db/spi/jdbc/DefaultDBManage.java | 10 +
.../chat2db/spi/jdbc/DefaultMetaService.java | 73 +-
.../chat2db/spi/jdbc/DefaultSqlBuilder.java | 224 +-
.../java/ai/chat2db/spi/model/BaseModel.java | 49 +
.../java/ai/chat2db/spi/model/ColumnType.java | 18 +-
.../java/ai/chat2db/spi/model/ErDiagram.java | 4 +-
.../java/ai/chat2db/spi/model/ForeignKey.java | 80 +
.../java/ai/chat2db/spi/model/IndexModel.java | 64 +
.../java/ai/chat2db/spi/model/Schema.java | 4 +
.../main/java/ai/chat2db/spi/model/Table.java | 60 +-
.../ai/chat2db/spi/model/TableColumn.java | 29 +-
.../java/ai/chat2db/spi/model/TableMeta.java | 19 +
.../main/java/ai/chat2db/spi/model/Type.java | 60 +-
.../chat2db/spi/model/VirtualForeignKey.java | 18 +
.../ai/chat2db/spi/sql/IDriverManager.java | 72 +-
.../java/ai/chat2db/spi/sql/SQLExecutor.java | 207 +-
.../java/ai/chat2db/spi/util/SqlUtils.java | 74 +-
chat2db-server/pom.xml | 23 +-
docker/Dockerfile | 2 +-
docker/docker-compose.yml | 18 +-
305 files changed, 25605 insertions(+), 28540 deletions(-)
create mode 100644 .opencode/skills/build/SKILL.md
create mode 100644 .vscode/launch.json
create mode 100644 AGENTS.md
create mode 100644 INITIALIZATION_GUIDE.md
create mode 100644 StateMachine.md
create mode 100644 chat2db-client/.yarnrc.yml
delete mode 100644 chat2db-client/src/assets/font/demo.css
delete mode 100644 chat2db-client/src/assets/font/demo_index.html
create mode 100644 chat2db-client/src/assets/font/iconfont-he.ttf
create mode 100644 chat2db-client/src/assets/font/iconfont-he.woff
create mode 100644 chat2db-client/src/assets/font/iconfont-he.woff2
create mode 100644 chat2db-client/src/assets/img/databaseImg/phoenixLogo.png
create mode 100644 chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less
create mode 100644 chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx
create mode 100644 chat2db-client/src/blocks/Tree/functions/truncateTable.tsx
create mode 100644 chat2db-client/src/components/AiChat/index.less
create mode 100644 chat2db-client/src/components/AiChat/index.tsx
create mode 100644 chat2db-client/src/pages/main/workspace/components/ERDiagram/index.less
create mode 100644 chat2db-client/src/pages/main/workspace/components/ERDiagram/index.tsx
create mode 100644 chat2db-client/src/pages/main/workspace/store/aiChatStore.ts
delete mode 100644 chat2db-gateway/pom.xml
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/Application.java
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/ElasticsearchClientConfig.java
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/SchemaDocument.java
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaController.java
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaRequest.java
delete mode 100644 chat2db-gateway/src/main/java/com/hejianjun/TableSchemaService.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/pom.xml
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/PhoenixDBManage.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/PhoenixMetaData.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/PhoenixPlugin.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/builder/PhoenixSqlBuilder.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/phoenix.json
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/type/PhoenixColumnTypeEnum.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/type/PhoenixDefaultValueEnum.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/java/ai/chat2db/plugin/phoenix/type/PhoenixIndexTypeEnum.java
create mode 100644 chat2db-server/chat2db-plugins/chat2db-phoenix/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/constant/AiConfigKeys.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/DropKeyParam.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/LoginAttemptService.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/cache/LuceneIndexManager.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/cache/LuceneIndexManagerFactory.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/converter/TableConverter.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/LoginAttemptServiceImpl.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/LoginAttempt.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableCacheDO.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableCacheVersionDO.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/entity/TableVectorMappingDO.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/LoginAttemptMapper.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableCacheMapper.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableCacheVersionMapper.java
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/java/ai/chat2db/server/domain/repository/mapper/TableVectorMappingMapper.java
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__user.sql
create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_11__login_attempt.sql
rename chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/{TableVectorMappingMapper.xml => LoginAttemptMapper.xml} (63%)
delete mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/mapper/TableCacheMapper.xml
rename chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/{Application.java => Chat2dbLiteApplication.java} (66%)
create mode 100644 chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/ThreadPoolConfig.java
create mode 100644 chat2db-server/chat2db-server-start/src/main/resources/thymeleaf/index.html
rename chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/{Application.java => Chat2dbWebApplication.java} (81%)
create mode 100644 chat2db-server/chat2db-server-web-start/src/main/java/ai/chat2db/server/web/start/config/config/ThreadPoolConfig.java
create mode 100644 chat2db-server/chat2db-server-web-start/src/main/resources/static/front/1065.fe92f3dd.async.js
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/aspect/GatewayClientServiceAspect.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/config/AiChatConfig.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/AiConfigController.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/EmbeddingController.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/KnowledgeController.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/TextGenerationController.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/client/AzureOpenAiStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/interceptor/AzureHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/listener/AzureOpenAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatChoice.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatMessage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChatRole.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureChoice.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsFinishReason.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsLogProbabilityModel.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureCompletionsUsage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/model/AzureExpandableStringEnum.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/azure/util/AzureReflectionUtils.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/client/BaichuanAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/client/BaichuanAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/interceptor/BaichuanHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/listener/BaichuanChatAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatCompletionsUsage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatData.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/baichuan/model/BaichuanChatMessage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2DBAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/client/Chat2dbAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/interceptor/Chat2dbHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/chat2db/listener/Chat2dbAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/client/ClaudeAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/client/ClaudeAiStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/interceptor/ClaudeHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/listener/ClaudeAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeChatMessage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeCompletionResponse.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/claude/model/ClaudeMessageLimit.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/client/FastChatOpenAiApi.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatEmbedding.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatEmbeddingResponse.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/embeddings/FastChatItem.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/interceptor/FastChatHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/listener/FastChatAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatChoice.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsFinishReason.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatCompletionsUsage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatExpandableStringEnum.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatMessage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/fastchat/model/FastChatRole.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/client/OpenAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/openai/listener/OpenAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/client/RestAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/client/RestAiStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/listener/RestAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/rest/model/RestAiCompletion.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/ChatContext.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/ChatEvent.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/ChatState.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/ChatStateMachineConfig.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/actions/AutoSelectTablesAction.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/actions/BaseChatAction.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/actions/BuildPromptAction.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/actions/FetchSchemaAction.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/statemachine/actions/StreamAction.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/client/TongyiChatAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/client/TongyiChatAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/listener/TongyiChatAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatCompletionsUsage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatMessage.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/tongyi/model/TongyiChatOutput.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/client/WenxinAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/client/WenxinAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/interceptor/AccessTokenInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/listener/WenxinAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/wenxin/model/WenxinChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/client/ZhipuChatAIStreamClient.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/interceptor/ZhipuChatHeaderAuthorizationInterceptor.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/listener/ZhipuChatAIEventSourceListener.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatBody.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/model/ZhipuChatCompletionsOptions.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/ai/zhipu/util/ZhipuUtils.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/BatchTableModifySqlRequest.java
create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/KeyDeleteRequest.java
delete mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/http/GatewayClientService.java
create mode 100644 chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/BaseModel.java
create mode 100644 chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/ForeignKey.java
create mode 100644 chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/IndexModel.java
create mode 100644 chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/model/VirtualForeignKey.java
diff --git a/.opencode/skills/build/SKILL.md b/.opencode/skills/build/SKILL.md
new file mode 100644
index 000000000..a9e952391
--- /dev/null
+++ b/.opencode/skills/build/SKILL.md
@@ -0,0 +1,51 @@
+---
+name: build
+description: 编译当前项目
+license: MIT
+compatibility: opencode
+---
+
+## Build Backend JAR
+
+编译后端 JAR 包:
+
+```powershell
+$env:JAVA_HOME="D:\tool\Java\jdk-17"; cd chat2db-server; mvn clean package -DskipTests
+```
+
+生成的 JAR 文件:
+- `chat2db-server/chat2db-server-start/target/chat2db-server-start.jar`
+
+运行方式:
+```powershell
+java -jar chat2db-server/chat2db-server-start/target/chat2db-server-start.jar
+```
+
+## Build Backend (Compile Only)
+
+仅编译不打包:
+
+```powershell
+$env:JAVA_HOME="D:\tool\Java\jdk-17"; cd chat2db-server; mvn clean compile -DskipTests
+```
+
+## Build Frontend
+
+编译前端:
+
+```powershell
+nvm use 21; cd chat2db-client; yarn install; yarn run build:web
+```
+
+开发模式运行前端:
+```powershell
+cd chat2db-client; yarn install; yarn run start:web
+```
+
+```powershell
+# Requirements
+# - Java 17 (设置 JAVA_HOME 环境变量)
+# - Node.js 16+
+# - Maven 3.6.1+
+# - Yarn 4.x
+```
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..dd418b355
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,21 @@
+{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "Current File",
+ "request": "launch",
+ "mainClass": "${file}"
+ },
+ {
+ "type": "java",
+ "name": "Application",
+ "request": "launch",
+ "mainClass": "ai.chat2db.server.start.Application",
+ "projectName": "chat2db-server-start"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b2967e218..78f86b2a2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -87,5 +87,7 @@
"yizhoumo",
"zustand"
],
- "java.compile.nullAnalysis.mode": "automatic"
+ "java.compile.nullAnalysis.mode": "automatic",
+ "java.debug.settings.onBuildFailureProceed": true,
+ "java.configuration.updateBuildConfiguration": "interactive"
}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..8f8faa432
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,268 @@
+# AGENTS.md - Chat2DB Development Guide
+
+This document provides essential information for AI coding agents working on the Chat2DB codebase.
+
+## Project Overview
+
+Chat2DB is a multi-database client tool with AI capabilities. It consists of:
+- **chat2db-client**: Frontend React/Electron application (TypeScript)
+- **chat2db-server**: Backend Spring Boot application (Java 17)
+
+## Build/Lint/Test Commands
+
+### Frontend (chat2db-client)
+
+```powershell
+# Install dependencies (must use yarn)
+cd chat2db-client
+yarn install
+
+# Development server
+yarn run start:web # Web version
+yarn run start # Desktop version (Electron + Web)
+
+# Build
+yarn run build:web # Build for web
+yarn run build:desktop # Build for desktop
+yarn run build:prod # Production build
+
+# Linting
+yarn run lint # Run ESLint
+
+# Package manager: yarn 4.9.1 (REQUIRED - do not use npm)
+```
+
+### Backend (chat2db-server)
+
+```powershell
+# Build (from chat2db-server directory)
+cd chat2db-server
+mvn clean install -DskipTests # Build without tests
+mvn clean install # Build with tests
+
+# Run a single test class
+mvn test -Dtest=TableOperationsTest -pl chat2db-server-test
+
+# Run a single test method
+mvn test -Dtest=TableOperationsTest#table -pl chat2db-server-test
+
+# Run application
+mvn spring-boot:run -pl chat2db-server-start
+
+# Run packaged JAR
+java -jar chat2db-server-start/target/chat2db-server-start.jar
+```
+
+## Requirements
+
+- **Java**: 17 or higher
+ - **Environment Setup**: `$env:JAVA_HOME = "D:\tool\Java\jdk-17"` before compiling Java code
+- **Node.js**: 16 or higher
+ - **Environment Setup**: `nvm use 20` before using yarn to compile frontend code
+- **Maven**: 3.6.1 or higher
+- **Yarn**: 4.x (required for frontend)
+
+## Code Style Guidelines
+
+### TypeScript/React (Frontend)
+
+#### Imports
+```typescript
+// React imports first
+import React, { memo, useCallback, useEffect, useRef } from 'react';
+
+// Third-party libraries
+import classnames from 'classnames';
+import { message } from 'antd';
+
+// Internal imports (use @ alias for src)
+import { useWorkspaceStore } from '@/pages/main/workspace/store';
+import DraggableContainer from '@/components/DraggableContainer';
+
+// Styles last
+import styles from './index.less';
+```
+
+#### Formatting (Prettier)
+- Print width: 120 characters
+- Single quotes
+- Trailing commas: 'all'
+- Use `prettier-plugin-organize-imports` for import ordering
+
+#### Naming Conventions
+- Components: PascalCase (`WorkspaceLeft`, `TableController`)
+- Files: camelCase for utilities, PascalCase for components
+- Hooks: camelCase with `use` prefix (`useWorkspaceStore`)
+- Stores: camelCase with `Store` suffix (`IWorkspaceStore`)
+- Interfaces: PascalCase with `I` prefix (`IConfigStore`, `IStore`)
+- Types: PascalCase with `Type` suffix or descriptive names
+
+#### React Patterns
+- Use `memo()` for component exports: `const Component = memo(() => { ... })`
+- Use functional components with hooks
+- Use Zustand for state management
+- Use `@/` alias for src imports
+
+#### ESLint Rules
+- Max line length: 120 characters
+- Prefer arrow functions
+- Prefer const
+- React hooks rules enforced
+- TypeScript strict mode (noImplicitAny: false in tsconfig)
+
+### Java (Backend)
+
+#### Package Structure
+```
+ai.chat2db.server.{module}
+├── controller/ # REST controllers
+├── service/ # Business logic interfaces and implementations
+├── param/ # Request parameters
+├── converter/ # Object converters/mappers
+├── vo/ # View Objects (response DTOs)
+└── model/ # Domain models
+```
+
+#### Naming Conventions
+- Classes: PascalCase (`TableServiceImpl`, `TableController`)
+- Methods: camelCase (`queryColumns`, `buildSql`)
+- Constants: UPPER_SNAKE_CASE (`TABLE_NAME`)
+- Parameters: camelCase with `Param` suffix (`TableQueryParam`)
+- Services: Interface without suffix, impl with `Impl` suffix
+- Converters: `*Converter` suffix
+- VOs: `*VO` suffix
+
+#### Code Style
+- Use Lombok annotations (`@Data`, `@Builder`, `@Slf4j`, `@AllArgsConstructor`)
+- Use Spring annotations (`@Service`, `@RestController`, `@Autowired`)
+- Use Jakarta validation (`@Valid`)
+- 4-space indentation
+- Opening braces on same line
+
+#### Service Layer Pattern
+```java
+public interface TableService {
+ DataResult query(TableQueryParam param, TableSelector selector);
+ ActionResult drop(DropParam param);
+}
+
+@Service
+@Slf4j
+public class TableServiceImpl implements TableService {
+ @Autowired
+ private PinService pinService;
+
+ @Override
+ public DataResult query(TableQueryParam param, TableSelector selector) {
+ // Implementation
+ }
+}
+```
+
+#### Controller Pattern
+```java
+@Slf4j
+@ConnectionInfoAspect
+@RequestMapping("/api/rdb/table")
+@RestController
+public class TableController {
+ @Autowired
+ private TableService tableService;
+
+ @GetMapping("/list")
+ public WebPageResult list(@Valid TableBriefQueryRequest request) {
+ // Implementation
+ }
+}
+```
+
+#### Error Handling
+- Use `ActionResult` for operations without return data
+- Use `DataResult` for single object returns
+- Use `ListResult` for list returns
+- Use `PageResult` for paginated results
+- Log errors with `@Slf4j` and `log.error()`/`log.warn()`
+
+## Project Structure
+
+```
+Chat2DB/
+├── chat2db-client/ # Frontend
+│ ├── src/
+│ │ ├── blocks/ # Reusable UI blocks
+│ │ ├── components/ # React components
+│ │ ├── pages/ # Page components
+│ │ ├── service/ # API service layer
+│ │ ├── hooks/ # Custom React hooks
+│ │ ├── store/ # Zustand stores
+│ │ ├── typings/ # TypeScript types
+│ │ ├── utils/ # Utility functions
+│ │ └── i18n/ # Internationalization
+│ ├── .eslintrc.js
+│ ├── .prettierrc
+│ └── package.json
+│
+├── chat2db-server/ # Backend
+│ ├── chat2db-server-domain/ # Domain layer
+│ │ ├── chat2db-server-domain-api/ # Service interfaces
+│ │ └── chat2db-server-domain-core/ # Service implementations
+│ ├── chat2db-server-web/ # Web/API layer
+│ │ ├── chat2db-server-web-api/ # API controllers
+│ │ └── chat2db-server-common-api/ # Common APIs
+│ ├── chat2db-server-tools/ # Utilities
+│ ├── chat2db-spi/ # Service Provider Interface
+│ ├── chat2db-plugins/ # Database plugins (MySQL, PostgreSQL, etc.)
+│ ├── chat2db-server-start/ # Main application
+│ └── chat2db-server-test/ # Test module
+│
+└── docker/ # Docker configuration
+```
+
+## Testing
+
+### Backend Tests
+- Located in `chat2db-server-test/src/test/java/`
+- Use JUnit 5 (`@Test`, `@Order`)
+- Extend `BaseTest` for integration tests
+- Use `@SpringBootTest` for integration tests
+
+### Running Tests
+```powershell
+# All tests
+mvn test
+
+# Single test class
+mvn test -Dtest=ClassName
+
+# Single test method
+mvn test -Dtest=ClassName#methodName
+```
+
+## Key Technologies
+
+### Frontend
+- React 18 with TypeScript
+- UmiJS 4 framework
+- Ant Design 5
+- Zustand for state management
+- Monaco Editor for SQL editing
+- Electron for desktop app
+
+### Backend
+- Spring Boot 3.1
+- Java 17
+- MyBatis Plus for database access
+- Sa-Token for authentication
+- Lombok for boilerplate reduction
+- MapStruct for object mapping
+- H2 as embedded database
+
+## Important Notes
+
+1. **Always use yarn** for frontend package management, not npm
+2. **Java 17** is required for the backend
+3. **Node 16+** is required for the frontend
+4. Tests in the server use `@TestMethodOrder(OrderAnnotation.class)` for execution order
+5. Database plugins follow the SPI pattern in `chat2db-spi`
+6. Use `@Valid` annotation for request validation in controllers
+7. Follow existing patterns when adding new controllers or services
\ No newline at end of file
diff --git a/INITIALIZATION_GUIDE.md b/INITIALIZATION_GUIDE.md
new file mode 100644
index 000000000..6b473e681
--- /dev/null
+++ b/INITIALIZATION_GUIDE.md
@@ -0,0 +1,131 @@
+# Chat2DB Project Initialization Guide
+
+This document outlines the steps required to initialize and build the Chat2DB project from scratch.
+
+## Prerequisites
+
+Before starting, ensure you have the following software installed:
+
+- **Java**: Version 17 or higher (Java 17.0.9 was used in this initialization)
+- **Node.js**: Version 16 or higher (Node.js 20.19.1 was used in this initialization)
+- **Yarn**: Version 4.9.1 or higher (Yarn 4.9.1 was used in this initialization)
+- **Maven**: Version 3.6.1 or higher (Maven 3.6.1 was used in this initialization)
+
+## Project Structure
+
+Chat2DB is a full-stack application with the following main components:
+
+- `chat2db-client`: The frontend React/Electron application
+- `chat2db-server`: The backend Spring Boot application
+
+## Step-by-step Initialization Process
+
+### 1. Clone the Repository
+
+```bash
+git clone
+cd Chat2DB
+```
+
+### 2. Initialize Client Application
+
+#### Navigate to the client directory:
+```bash
+cd chat2db-client
+```
+
+#### Install client dependencies:
+```bash
+yarn install
+```
+
+This will install all necessary JavaScript/TypeScript dependencies for the frontend.
+
+#### Build the client application:
+```bash
+yarn run build:web
+```
+
+This compiles the React application for production use.
+
+### 3. Initialize Server Application
+
+#### Navigate to the server directory:
+```bash
+cd ../chat2db-server
+```
+
+#### Install server dependencies and build the project:
+```bash
+mvn clean install -DskipTests
+```
+
+This command:
+- Downloads all required Maven dependencies
+- Compiles all Java components
+- Builds executable JAR files for the server
+- Skips running tests to speed up the build process
+
+### 4. Verify Build Output
+
+After successful initialization, you should find these executable JAR files:
+
+- **Main Server**: `chat2db-server\chat2db-server-start\target\chat2db-server-start.jar`
+- **Web Server**: `chat2db-server\chat2db-server-web-start\target\chat2db-server-web-start.jar`
+
+## Running the Application
+
+### For Development
+
+#### Frontend development server:
+```bash
+cd chat2db-client
+yarn run start:web
+```
+
+#### Backend development:
+```bash
+cd chat2db-server
+mvn spring-boot:run -pl chat2db-server-start
+```
+
+### For Production
+
+Run the server application:
+```bash
+java -jar chat2db-server\chat2db-server-start\target\chat2db-server-start.jar
+```
+
+The application will be accessible via the web interface or through the Electron desktop application.
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Maven Build Fails with Memory Issues**
+ - Ensure you have sufficient memory allocated to Maven
+ - You may need to set `MAVEN_OPTS` environment variable with increased heap size
+
+2. **Yarn Install Fails**
+ - Clear the yarn cache: `yarn cache clean`
+ - Try installing with network timeout tolerance: `yarn install --network-timeout 100000`
+
+3. **JDK Version Issues**
+ - Ensure you're using Java 17 or higher
+ - Verify with: `java -version`
+
+## Additional Notes
+
+- The project uses a multi-module Maven structure for server components
+- Database plugins are built as separate modules (MySQL, PostgreSQL, Oracle, etc.)
+- Client-side uses Umi framework with React and Ant Design
+- The application supports various databases through plugin architecture
+- AI integration features are available using ChatGPT-like APIs
+
+## Development Workflow
+
+After initialization, developers can:
+- Modify frontend components in the `chat2db-client` directory
+- Modify backend logic in the `chat2db-server` directory
+- Test with the development servers before building for production
+- Add new database plugins following the existing plugin architecture
\ No newline at end of file
diff --git a/StateMachine.md b/StateMachine.md
new file mode 100644
index 000000000..efc8c6f5d
--- /dev/null
+++ b/StateMachine.md
@@ -0,0 +1,142 @@
+# Prompt处理状态机:事件分支逻辑与完整流程图
+
+## 一、设计理念
+
+基于“前端触发事件,后端状态自动流转”的架构,事件(Event)代表用户或系统的明确**动作或指令**,状态(State)代表系统当前所处的**处理阶段**。状态机根据接收到的事件和当前状态,结合守卫条件,自动决定下一个状态并执行业务逻辑。
+
+## 二、事件(Event)体系完整定义
+
+### 2.1 用户发起的主要指令事件
+
+这些事件对应前端可触发的各类`PromptType`请求。
+
+| 事件 | 对应PromptType | 触发方 | 含义与携带信息 |
+| ----------------------------- | ------------------ | ------ | ------------------------------------------------------------ |
+| **`REQUEST_NL_TO_SQL`** | `NL_2_SQL` | 前端 | 请求将自然语言转换为SQL。需携带`message`(自然语言问题)。 |
+| **`REQUEST_EXPLAIN_SQL`** | `SQL_EXPLAIN` | 前端 | 请求解释SQL语句。需携带`message`(SQL语句)。 |
+| **`REQUEST_OPTIMIZE_SQL`** | `SQL_OPTIMIZER` | 前端 | 请求优化SQL语句。需携带`message`(SQL语句)。 |
+| **`REQUEST_CONVERT_SQL`** | `SQL_2_SQL` | 前端 | 请求转换SQL方言。需携带`message`(SQL语句)和`destSqlType`(目标数据库类型)。 |
+| **`REQUEST_TEXT_GENERATION`** | `TEXT_GENERATION` | 前端 | 请求直接进行文本生成。需携带`message`(任何文本)。 |
+| **`REQUEST_GENERATE_TITLE`** | `TITLE_GENERATION` | 前端 | 请求为查询生成标题。需携带`message`(查询语句或描述)。 |
+| **`REQUEST_GUESS_COMMENT`** | `NL_2_COMMENT` | 前端 | 请求猜测表/字段注释。需携带`message`(表名、字段名或SQL)。 |
+
+**关键属性**:所有上述事件均隐含一个由前端通过`tables`请求参数传递的**`tableNames`列表**。此属性是驱动核心分支逻辑的关键。
+
+### 2.2 系统内部决策与处理事件
+
+这些事件通常由状态机在特定状态的**动作(Action)** 或**守卫(Guard)** 中自动触发,而非前端直接调用。
+
+| 事件 | 触发条件 | 含义与作用 |
+| ------------------------- | ------------------------------------------------------------ | -------------------------------------------- |
+| **`TABLES_PROVIDED`** | 守卫`hasTablesGuard()`检测到`tableNames`列表有效(非空)。 | 表示用户已明确提供表名,流程应跳过自动选表。 |
+| **`TABLES_NOT_PROVIDED`** | 守卫`hasTablesGuard()`检测到`tableNames`列表无效(空或未提供)。 | 表示用户未提供表名,流程需进入自动选表阶段。 |
+| **`AUTO_SELECT_DONE`** | 在`AUTO_SELECTING_TABLES`状态中,AI自动选表逻辑完成。 | 驱动状态离开“自动选表”状态,进入下一阶段。 |
+| **`SCHEMA_FETCHED`** | 在`FETCHING_TABLE_SCHEMA`状态中,成功获取到指定表的DDL信息。 | 驱动状态进入Prompt构建阶段。 |
+| **`PROMPT_BUILT`** | 在`BUILDING_PROMPT`状态中,最终Prompt构建完成。 | 驱动状态进入AI流式调用阶段。 |
+| **`STREAM_FINISHED`** | 在`STREAMING`状态中,AI流式响应全部完成。 | 驱动状态进入完成终态。 |
+| **`PROMPT_BUILD_FAILED`** | 在`BUILDING_PROMPT`等状态中,构建Prompt时发生错误。 | 驱动状态进入错误终态。 |
+| **`AI_CALL_FAILED`** | 在`STREAMING`状态中,调用AI服务失败。 | 驱动状态进入错误终态。 |
+
+## 三、核心分支逻辑详解
+
+状态机的分支逻辑主要由**初始路由决策**和**后续自动流转**两部分构成。
+
+### 3.1 初始路由决策(基于`tables`参数)
+
+这是最主要的分支点,发生在状态机从`IDLE`状态接收到任意一个`REQUEST_*`事件时。
+
+```mermaid
+flowchart TD
+A["IDLE (初始状态)"] --> B{"收到前端事件
如: REQUEST_NL_TO_SQL"}
+B --> C["检查请求参数中的
tableNames (tables参数)"]
+C --> D{"tableNames是否有效?"}
+D -- 是 --> E["触发内部事件: TABLES_PROVIDED"]
+E --> F["进入状态: FETCHING_TABLE_SCHEMA
(跳过自动选表)"]
+D -- 否 --> G["触发内部事件: TABLES_NOT_PROVIDED"]
+G --> H["进入状态: AUTO_SELECTING_TABLES
(需自动选表)"]
+```
+
+**逻辑说明**:
+
+1. **分支条件**:取决于前端请求中`tables`参数是否提供了有效的表名列表。
+2. **分支动作**:此决策由一个**守卫(Guard)** 实现。守卫检查`tableNames`属性,并自动触发相应的内部事件(`TABLES_PROVIDED`或`TABLES_NOT_PROVIDED`),从而驱动状态机进入不同的下一个状态。
+3. **结果**:
+ - **有效**:直接进入`FETCHING_TABLE_SCHEMA`,流程最短。
+ - **无效**:进入`AUTO_SELECTING_TABLES`,启动AI选表子流程。
+
+### 3.2 后续自动流转路径
+
+初始分支之后,状态机将沿着预设路径自动运行,直至完成。
+
+```mermaid
+flowchart TD
+subgraph 主流程
+ direction TB
+ S1["AUTO_SELECTING_TABLES
(自动选表中)"] -- "AI选表完成
触发: AUTO_SELECT_DONE" --> S2["FETCHING_TABLE_SCHEMA
(获取表DDL)"]
+ S2 -- "DDL获取完成
触发: SCHEMA_FETCHED" --> S3["BUILDING_PROMPT
(构建最终Prompt)"]
+ S3 -- "Prompt构建完成
触发: PROMPT_BUILT" --> S4["STREAMING
(流式调用AI)"]
+ S4 -- "流式响应完成
触发: STREAM_FINISHED" --> S5["COMPLETED
(成功完成)"]
+end
+
+subgraph 初始分支
+ direction LR
+ IDLE -- "REQUEST_*事件 + tables有效
(隐含TABLES_PROVIDED)" --> S2
+ IDLE -- "REQUEST_*事件 + tables无效
(隐含TABLES_NOT_PROVIDED)" --> S1
+end
+
+subgraph 异常分支
+ S1 -. "选表失败
触发: PROMPT_BUILD_FAILED" .-> ERR["FAILED"]
+ S2 -. "获取DDL失败
触发: PROMPT_BUILD_FAILED" .-> ERR
+ S3 -. "构建Prompt失败
触发: PROMPT_BUILD_FAILED" .-> ERR
+ S4 -. "AI调用失败
触发: AI_CALL_FAILED" .-> ERR
+end
+```
+
+**路径说明**:
+
+1. **自动选表路径**:`IDLE`-> `AUTO_SELECTING_TABLES`-> `FETCHING_TABLE_SCHEMA`-> ...
+2. **直连路径**:`IDLE`-> `FETCHING_TABLE_SCHEMA`-> ...
+3. **共同路径**:两条路径在`FETCHING_TABLE_SCHEMA`状态汇合,之后的流程完全一致:获取DDL -> 构建Prompt -> 流式输出 -> 完成。
+4. **异常路径**:在任何非终态,如果对应的业务操作失败,应触发相应的失败事件(如`PROMPT_BUILD_FAILED`),使状态机跳转到`FAILED`终态,便于统一错误处理。
+
+## 四、完整状态转换图 (Mermaid)
+
+以下图表综合了所有状态、事件和分支逻辑。
+
+```mermaid
+stateDiagram-v2
+ [*] --> IDLE
+
+ note right of IDLE
+ 前端触发请求(如REQUEST_NL_TO_SQL)
+ 根据tables参数有效性内部触发不同事件
+ end note
+
+ IDLE --> FETCHING_TABLE_SCHEMA: TABLES_PROVIDED
+ IDLE --> AUTO_SELECTING_TABLES: TABLES_NOT_PROVIDED
+
+ state 自动选表子流程 {
+ AUTO_SELECTING_TABLES --> FETCHING_TABLE_SCHEMA: AUTO_SELECT_DONE
+ }
+
+ state 核心处理流程 {
+ FETCHING_TABLE_SCHEMA --> BUILDING_PROMPT: SCHEMA_FETCHED
+ BUILDING_PROMPT --> STREAMING: PROMPT_BUILT
+ STREAMING --> COMPLETED: STREAM_FINISHED
+ }
+
+ AUTO_SELECTING_TABLES --> FAILED: AUTO_SELECT_FAILED
+ FETCHING_TABLE_SCHEMA --> FAILED: FETCH_SCHEMA_FAILED
+ BUILDING_PROMPT --> FAILED: PROMPT_BUILD_FAILED
+ STREAMING --> FAILED: AI_CALL_FAILED
+
+ COMPLETED --> [*]
+ FAILED --> [*]
+```
+
+**图例解读**:
+
+1. **实线箭头**:代表正常的状态转换,由对应的事件触发。
+2. **虚线箭头**:代表异常的状态转换,由失败事件触发。
+3. **注释框**:解释了在`IDLE`状态,同一个前端事件如何通过守卫产生不同的内部事件,进而实现分支。
+4. **状态分组**:将“自动选表”和“核心处理”分别用子状态框表示,突出了流程的模块化。
\ No newline at end of file
diff --git a/chat2db-client/.gitignore b/chat2db-client/.gitignore
index c5fcd3df3..6ffb61b89 100644
--- a/chat2db-client/.gitignore
+++ b/chat2db-client/.gitignore
@@ -10,8 +10,10 @@
/dist
.swc
./yarn-error.log
+/.yarn/*
/release
/static
-/versions
\ No newline at end of file
+/versions
+/.yarn
diff --git a/chat2db-client/.umirc.ts b/chat2db-client/.umirc.ts
index 6ea951156..4c4cebcf0 100644
--- a/chat2db-client/.umirc.ts
+++ b/chat2db-client/.umirc.ts
@@ -100,10 +100,10 @@ export default defineConfig({
// window.addEventListener("appinstalled", () => {
// deferredPrompt = null;
// })`,
- {
- src: 'https://www.googletagmanager.com/gtag/js?id=G-V8M4E5SF61',
- async: true,
- },
+ // {
+ // src: 'https://www.googletagmanager.com/gtag/js?id=G-V8M4E5SF61',
+ // async: true,
+ // },
// `window.dataLayer = window.dataLayer || [];
// function gtag() {
// window.dataLayer.push(arguments);
diff --git a/chat2db-client/.yarnrc.yml b/chat2db-client/.yarnrc.yml
new file mode 100644
index 000000000..3186f3f07
--- /dev/null
+++ b/chat2db-client/.yarnrc.yml
@@ -0,0 +1 @@
+nodeLinker: node-modules
diff --git a/chat2db-client/package.json b/chat2db-client/package.json
index b0b8afaa6..3b7614786 100644
--- a/chat2db-client/package.json
+++ b/chat2db-client/package.json
@@ -1,13 +1,13 @@
{
"name": "chat2db",
- "version": "1.0.0",
+ "version": "2.0.0",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/chat2db/Chat2DB"
},
"author": "fjy, hexi",
- "main": "src/main/main.js",
+ "main": "src/main/index.js",
"scripts": {
"build": "npm run build:web && npm run build:main",
"build:desktop": "npm run build:web:desktop && npm run build:main:prod",
@@ -15,15 +15,15 @@
"build:main:prod": "cross-env NODE_ENV=production electron-builder",
"build:prod": "npm run build:web:prod && npm run build:main:prod",
"build:web": "umi build",
- "build:web:desktop": "cross-env UMI_ENV=desktop cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} umi build",
- "build:web:prod": "cross-env UMI_ENV=prod cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} cross-env UMI_PublicPath=${npm_config_public_path} umi build",
+ "build:web:desktop": "cross-env UMI_ENV=desktop umi build",
+ "build:web:prod": "cross-env UMI_ENV=prod umi build",
"postinstall": "umi setup",
"lint": "umi lint",
"start": "concurrently \"npm run start:web\" \"npm run start:main\"",
"start:main": "cross-env NODE_ENV=development electron .",
"start:main:prod": "cross-env NODE_ENV=production electron .",
- "start:web": "cross-env UMI_ENV=local HMR=none cross-env APP_VERSION=${npm_config_app_version} umi dev",
- "start:web:hot": "cross-env UMI_ENV=local cross-env APP_VERSION=${npm_config_app_version} umi dev"
+ "start:web": "cross-env UMI_ENV=local HMR=none umi dev",
+ "start:web:hot": "cross-env UMI_ENV=local umi dev"
},
"dependencies": {
"@dnd-kit/modifiers": "^6.0.1",
@@ -40,8 +40,10 @@
"monaco-editor": "^0.44.0",
"monaco-editor-esm-webpack-plugin": "^2.1.0",
"monaco-editor-webpack-plugin": "^7.0.1",
+ "react-markdown": "^8.0.7",
"react-monaco-editor": "^0.54.0",
"react-sortablejs": "^6.1.4",
+ "remark-gfm": "3",
"sql-formatter": "^13.0.4",
"styled-components": "^6.0.1",
"umi": "^4.0.87",
@@ -133,5 +135,6 @@
"AppImage"
]
}
- }
+ },
+ "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538"
}
diff --git a/chat2db-client/src/assets/font/demo.css b/chat2db-client/src/assets/font/demo.css
deleted file mode 100644
index a67054a0a..000000000
--- a/chat2db-client/src/assets/font/demo.css
+++ /dev/null
@@ -1,539 +0,0 @@
-/* Logo 字体 */
-@font-face {
- font-family: "iconfont logo";
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
-}
-
-.logo {
- font-family: "iconfont logo";
- font-size: 160px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-/* tabs */
-.nav-tabs {
- position: relative;
-}
-
-.nav-tabs .nav-more {
- position: absolute;
- right: 0;
- bottom: 0;
- height: 42px;
- line-height: 42px;
- color: #666;
-}
-
-#tabs {
- border-bottom: 1px solid #eee;
-}
-
-#tabs li {
- cursor: pointer;
- width: 100px;
- height: 40px;
- line-height: 40px;
- text-align: center;
- font-size: 16px;
- border-bottom: 2px solid transparent;
- position: relative;
- z-index: 1;
- margin-bottom: -1px;
- color: #666;
-}
-
-
-#tabs .active {
- border-bottom-color: #f00;
- color: #222;
-}
-
-.tab-container .content {
- display: none;
-}
-
-/* 页面布局 */
-.main {
- padding: 30px 100px;
- width: 960px;
- margin: 0 auto;
-}
-
-.main .logo {
- color: #333;
- text-align: left;
- margin-bottom: 30px;
- line-height: 1;
- height: 110px;
- margin-top: -50px;
- overflow: hidden;
- *zoom: 1;
-}
-
-.main .logo a {
- font-size: 160px;
- color: #333;
-}
-
-.helps {
- margin-top: 40px;
-}
-
-.helps pre {
- padding: 20px;
- margin: 10px 0;
- border: solid 1px #e7e1cd;
- background-color: #fffdef;
- overflow: auto;
-}
-
-.icon_lists {
- width: 100% !important;
- overflow: hidden;
- *zoom: 1;
-}
-
-.icon_lists li {
- width: 100px;
- margin-bottom: 10px;
- margin-right: 20px;
- text-align: center;
- list-style: none !important;
- cursor: default;
-}
-
-.icon_lists li .code-name {
- line-height: 1.2;
-}
-
-.icon_lists .icon {
- display: block;
- height: 100px;
- line-height: 100px;
- font-size: 42px;
- margin: 10px auto;
- color: #333;
- -webkit-transition: font-size 0.25s linear, width 0.25s linear;
- -moz-transition: font-size 0.25s linear, width 0.25s linear;
- transition: font-size 0.25s linear, width 0.25s linear;
-}
-
-.icon_lists .icon:hover {
- font-size: 100px;
-}
-
-.icon_lists .svg-icon {
- /* 通过设置 font-size 来改变图标大小 */
- width: 1em;
- /* 图标和文字相邻时,垂直对齐 */
- vertical-align: -0.15em;
- /* 通过设置 color 来改变 SVG 的颜色/fill */
- fill: currentColor;
- /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
- normalize.css 中也包含这行 */
- overflow: hidden;
-}
-
-.icon_lists li .name,
-.icon_lists li .code-name {
- color: #666;
-}
-
-/* markdown 样式 */
-.markdown {
- color: #666;
- font-size: 14px;
- line-height: 1.8;
-}
-
-.highlight {
- line-height: 1.5;
-}
-
-.markdown img {
- vertical-align: middle;
- max-width: 100%;
-}
-
-.markdown h1 {
- color: #404040;
- font-weight: 500;
- line-height: 40px;
- margin-bottom: 24px;
-}
-
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
- color: #404040;
- margin: 1.6em 0 0.6em 0;
- font-weight: 500;
- clear: both;
-}
-
-.markdown h1 {
- font-size: 28px;
-}
-
-.markdown h2 {
- font-size: 22px;
-}
-
-.markdown h3 {
- font-size: 16px;
-}
-
-.markdown h4 {
- font-size: 14px;
-}
-
-.markdown h5 {
- font-size: 12px;
-}
-
-.markdown h6 {
- font-size: 12px;
-}
-
-.markdown hr {
- height: 1px;
- border: 0;
- background: #e9e9e9;
- margin: 16px 0;
- clear: both;
-}
-
-.markdown p {
- margin: 1em 0;
-}
-
-.markdown>p,
-.markdown>blockquote,
-.markdown>.highlight,
-.markdown>ol,
-.markdown>ul {
- width: 80%;
-}
-
-.markdown ul>li {
- list-style: circle;
-}
-
-.markdown>ul li,
-.markdown blockquote ul>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown>ul li p,
-.markdown>ol li p {
- margin: 0.6em 0;
-}
-
-.markdown ol>li {
- list-style: decimal;
-}
-
-.markdown>ol li,
-.markdown blockquote ol>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown code {
- margin: 0 3px;
- padding: 0 5px;
- background: #eee;
- border-radius: 3px;
-}
-
-.markdown strong,
-.markdown b {
- font-weight: 600;
-}
-
-.markdown>table {
- border-collapse: collapse;
- border-spacing: 0px;
- empty-cells: show;
- border: 1px solid #e9e9e9;
- width: 95%;
- margin-bottom: 24px;
-}
-
-.markdown>table th {
- white-space: nowrap;
- color: #333;
- font-weight: 600;
-}
-
-.markdown>table th,
-.markdown>table td {
- border: 1px solid #e9e9e9;
- padding: 8px 16px;
- text-align: left;
-}
-
-.markdown>table th {
- background: #F7F7F7;
-}
-
-.markdown blockquote {
- font-size: 90%;
- color: #999;
- border-left: 4px solid #e9e9e9;
- padding-left: 0.8em;
- margin: 1em 0;
-}
-
-.markdown blockquote p {
- margin: 0;
-}
-
-.markdown .anchor {
- opacity: 0;
- transition: opacity 0.3s ease;
- margin-left: 8px;
-}
-
-.markdown .waiting {
- color: #ccc;
-}
-
-.markdown h1:hover .anchor,
-.markdown h2:hover .anchor,
-.markdown h3:hover .anchor,
-.markdown h4:hover .anchor,
-.markdown h5:hover .anchor,
-.markdown h6:hover .anchor {
- opacity: 1;
- display: inline-block;
-}
-
-.markdown>br,
-.markdown>p>br {
- clear: both;
-}
-
-
-.hljs {
- display: block;
- background: white;
- padding: 0.5em;
- color: #333333;
- overflow-x: auto;
-}
-
-.hljs-comment,
-.hljs-meta {
- color: #969896;
-}
-
-.hljs-string,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-strong,
-.hljs-emphasis,
-.hljs-quote {
- color: #df5000;
-}
-
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-type {
- color: #a71d5d;
-}
-
-.hljs-literal,
-.hljs-symbol,
-.hljs-bullet,
-.hljs-attribute {
- color: #0086b3;
-}
-
-.hljs-section,
-.hljs-name {
- color: #63a35c;
-}
-
-.hljs-tag {
- color: #333333;
-}
-
-.hljs-title,
-.hljs-attr,
-.hljs-selector-id,
-.hljs-selector-class,
-.hljs-selector-attr,
-.hljs-selector-pseudo {
- color: #795da3;
-}
-
-.hljs-addition {
- color: #55a532;
- background-color: #eaffea;
-}
-
-.hljs-deletion {
- color: #bd2c00;
- background-color: #ffecec;
-}
-
-.hljs-link {
- text-decoration: underline;
-}
-
-/* 代码高亮 */
-/* PrismJS 1.15.0
-https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
-/**
- * prism.js default theme for JavaScript, CSS and HTML
- * Based on dabblet (http://dabblet.com)
- * @author Lea Verou
- */
-code[class*="language-"],
-pre[class*="language-"] {
- color: black;
- background: none;
- text-shadow: 0 1px white;
- font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-}
-
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-@media print {
-
- code[class*="language-"],
- pre[class*="language-"] {
- text-shadow: none;
- }
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: .5em 0;
- overflow: auto;
-}
-
-:not(pre)>code[class*="language-"],
-pre[class*="language-"] {
- background: #f5f2f0;
-}
-
-/* Inline code */
-:not(pre)>code[class*="language-"] {
- padding: .1em;
- border-radius: .3em;
- white-space: normal;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
- color: slategray;
-}
-
-.token.punctuation {
- color: #999;
-}
-
-.namespace {
- opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol,
-.token.deleted {
- color: #905;
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
- color: #690;
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string {
- color: #9a6e3a;
- background: hsla(0, 0%, 100%, .5);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
- color: #07a;
-}
-
-.token.function,
-.token.class-name {
- color: #DD4A68;
-}
-
-.token.regex,
-.token.important,
-.token.variable {
- color: #e90;
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic {
- font-style: italic;
-}
-
-.token.entity {
- cursor: help;
-}
diff --git a/chat2db-client/src/assets/font/demo_index.html b/chat2db-client/src/assets/font/demo_index.html
deleted file mode 100644
index 6a0d556a8..000000000
--- a/chat2db-client/src/assets/font/demo_index.html
+++ /dev/null
@@ -1,4880 +0,0 @@
-
-
-
-
- iconfont Demo
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Unicode
- - Font class
- - Symbol
-
-
-
查看项目
-
-
-
-
-
-
-
Unicode 引用
-
-
-
Unicode 是字体在网页端最原始的应用方式,特点是:
-
- - 支持按字体的方式去动态调整图标大小,颜色等等。
- - 默认情况下不支持多色,直接添加多色图标会自动去色。
-
-
- 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
-
-
Unicode 使用步骤如下:
-
第一步:拷贝项目下面生成的 @font-face
-
@font-face {
- font-family: 'iconfont';
- src: url('iconfont.woff2?t=1704794525154') format('woff2'),
- url('iconfont.woff?t=1704794525154') format('woff'),
- url('iconfont.ttf?t=1704794525154') format('truetype');
-}
-
-
第二步:定义使用 iconfont 的样式
-
.iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-
第三步:挑选相应图标并获取字体编码,应用于页面
-
-<span class="iconfont">3</span>
-
-
- "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
- -
-
-
- right_on_5
-
- .icon-right_on_5
-
-
-
- -
-
-
- right_off_5-01
-
- .icon-right_off_5-01
-
-
-
- -
-
-
- left_on_2
-
- .icon-a-left_on_huaban11
-
-
-
- -
-
-
- left_off
-
- .icon-a-left_off_huaban1
-
-
-
- -
-
-
- minimize21
-
- .icon-minimize21
-
-
-
- -
-
-
- restore
-
- .icon-restore_button2
-
-
-
- -
-
-
- resize
-
- .icon-resize_button2
-
-
-
- -
-
-
- close
-
- .icon-close_button2
-
-
-
- -
-
-
- 筛选
-
- .icon-shaixuan
-
-
-
- -
-
-
- 排序
-
- .icon-a-44tubiao-122
-
-
-
- -
-
-
- 305信息-线性圆框
-
- .icon-xinxi-xianxingyuankuang
-
-
-
- -
-
-
- 加号
-
- .icon-jiahao
-
-
-
- -
-
-
- 列表
-
- .icon-liebiao
-
-
-
- -
-
-
- 减去
-
- .icon-jianqu
-
-
-
- -
-
-
- database
-
- .icon-database
-
-
-
- -
-
-
- 筛选
-
- .icon-shaixuan1
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin2
-
-
-
- -
-
-
- 加号_o
-
- .icon-jiahao_o
-
-
-
- -
-
-
- 数据库_jurassic
-
- .icon-jurassic_data
-
-
-
- -
-
-
- 权限
-
- .icon-quanxian
-
-
-
- -
-
-
- sharpicons_add-database
-
- .icon-sharpicons_add-database
-
-
-
- -
-
-
- 组织管理
-
- .icon-zuzhiguanli-
-
-
-
- -
-
-
- 空间
-
- .icon-moxing-miaobian
-
-
-
- -
-
-
- 下箭头-copy
-
- .icon-xiajiantou1-copy
-
-
-
- -
-
-
- 查看
-
- .icon-chakan2
-
-
-
- -
-
-
- clone
-
- .icon-clone
-
-
-
- -
-
-
- 提交
-
- .icon-tijiao
-
-
-
- -
-
-
- 查看
-
- .icon-chakan1
-
-
-
- -
-
-
- 复制
-
- .icon-fuzhi
-
-
-
- -
-
-
- icon_answer
-
- .icon-icon_answer
-
-
-
- -
-
-
- icon_question
-
- .icon-icon_question
-
-
-
- -
-
-
- 发送
-
- .icon-fasong
-
-
-
- -
-
-
- 重启
-
- .icon-zhongqi
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing2
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing3
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing1
-
-
-
- -
-
-
- 升级
-
- .icon-shengji
-
-
-
- -
-
-
- 全局_升级
-
- .icon-quanju_shengji
-
-
-
- -
-
-
- 关于我们
-
- .icon-guanyuwomen1
-
-
-
- -
-
-
- ico版本更新
-
- .icon-icobanbengengxin
-
-
-
- -
-
-
- 对话气泡
-
- .icon-duihuaqipao
-
-
-
- -
-
-
- 角色权限
-
- .icon-jiaosequanxian
-
-
-
- -
-
-
- preview
-
- .icon-preview1
-
-
-
- -
-
-
- 导入
-
- .icon-daoru
-
-
-
- -
-
-
- 终止
-
- .icon-zhongzhi
-
-
-
- -
-
-
- 退出
-
- .icon-tuichu
-
-
-
- -
-
-
- 控桩终端
-
- .icon-kongzhuangzhongduan
-
-
-
- -
-
-
- 撤销
-
- .icon-chexiao1
-
-
-
- -
-
-
- 向上
-
- .icon-xiangshang
-
-
-
- -
-
-
- 查看
-
- .icon-chakan-copy
-
-
-
- -
-
-
- 编辑数据_编辑录入操作_jurassic
-
- .icon-jurassic_edit-data
-
-
-
- -
-
-
- 编辑表格_编辑录入操作_jurassic
-
- .icon-jurassic_edit-table
-
-
-
- -
-
-
- 报表数据录入
-
- .icon-baobiaoshujuluru
-
-
-
- -
-
-
- 播放5
-
- .icon-bofang5
-
-
-
- -
-
-
- 清空@3x
-
- .icon-a-qingkong3x
-
-
-
- -
-
-
- 删除
-
- .icon-shanchu
-
-
-
- -
-
-
- new-document-worksheet
-
- .icon-newdocumentworksheet
-
-
-
- -
-
-
- file-excel
-
- .icon-file-excel
-
-
-
- -
-
-
- file-markdown
-
- .icon-file-markdown
-
-
-
- -
-
-
- file-word
-
- .icon-file-word
-
-
-
- -
-
-
- HTML5
-
- .icon-HTML
-
-
-
- -
-
-
- HTML
-
- .icon-HTML1
-
-
-
- -
-
-
- pdf
-
- .icon-pdf
-
-
-
- -
-
-
- 个人用户
-
- .icon-gerenyonghu
-
-
-
- -
-
-
- 后台管理
-
- .icon-houtaiguanli
-
-
-
- -
-
-
- 字体代码
-
- .icon-zitidaima
-
-
-
- -
-
-
- 版本
-
- .icon-banben
-
-
-
- -
-
-
- 车位管理
-
- .icon-cheweiguanli
-
-
-
- -
-
-
- dictate
-
- .icon-dianzhelidaochu
-
-
-
- -
-
-
- circle-f
-
- .icon-circle-f
-
-
-
- -
-
-
- 图表-函数
-
- .icon-tubiao-hanshu
-
-
-
- -
-
-
- 视图管理器
-
- .icon-shituguanliqi
-
-
-
- -
-
-
- 回车
-
- .icon-huiche
-
-
-
- -
-
-
- 缺省
-
- .icon-quesheng
-
-
-
- -
-
-
- 进入箭头
-
- .icon-jinrujiantou
-
-
-
- -
-
-
- 右箭头
-
- .icon-youjiantou_huaban
-
-
-
- -
-
-
- 向右箭头
-
- .icon-xiangyoujiantou1
-
-
-
- -
-
-
- 数据源
-
- .icon-shujuyuan
-
-
-
- -
-
-
- question
-
- .icon-question
-
-
-
- -
-
-
- 星星-copy
-
- .icon-xingxing
-
-
-
- -
-
-
- 控制台
-
- .icon-kongzhitai
-
-
-
- -
-
-
- 星系
-
- .icon-xingxi
-
-
-
- -
-
-
- 暂无数据 (1)
-
- .icon-a-zanwushuju1
-
-
-
- -
-
-
- 开始
-
- .icon-kaishi
-
-
-
- -
-
-
- 关闭
-
- .icon-guanbi
-
-
-
- -
-
-
- 下箭头
-
- .icon-xiajiantou
-
-
-
- -
-
-
- more
-
- .icon-gengduo
-
-
-
- -
-
-
- 设置
-
- .icon-shezhi
-
-
-
- -
-
-
- 对话-未选
-
- .icon-duihua-weixuan
-
-
-
- -
-
-
- 图表-未选
-
- .icon-tubiao-weixuan
-
-
-
- -
-
-
- 编组 13备份 3
-
- .icon-a-bianzu13beifen3
-
-
-
- -
-
-
- 编组备份
-
- .icon-bianzubeifen
-
-
-
- -
-
-
- 表格
-
- .icon-biaoge1
-
-
-
- -
-
-
- 收藏 (1)
-
- .icon-a-shoucang1
-
-
-
- -
-
-
- guthub-未选
-
- .icon-guthub-weixuan1
-
-
-
- -
-
-
- 数据-未选
-
- .icon-shuju-weixuan
-
-
-
- -
-
-
- 编组 4
-
- .icon-a-bianzu4
-
-
-
- -
-
-
- 编组 14备份
-
- .icon-a-bianzu14beifen
-
-
-
- -
-
-
- guthub-未选
-
- .icon-guthub-weixuan
-
-
-
- -
-
-
- 24gl-folderMinus
-
- .icon-24gl-folderMinus
-
-
-
- -
-
-
- 24gl-folderOpen
-
- .icon-24gl-folderOpen
-
-
-
- -
-
-
- 24gf-folderOpen
-
- .icon-24gf-folderOpen
-
-
-
- -
-
-
- 云数据库
-
- .icon-yunshujuku
-
-
-
- -
-
-
- 报表
-
- .icon-baobiao
-
-
-
- -
-
-
- 工作台
-
- .icon-gongzuotai
-
-
-
- -
-
-
- mongodb
-
- .icon-mongodb
-
-
-
- -
-
-
- Redis
-
- .icon-Redis
-
-
-
- -
-
-
- HIVE_2
-
- .icon-HIVE
-
-
-
- -
-
-
- Kingbase
-
- .icon-Kingbase
-
-
-
- -
-
-
- 仪表盘
-
- .icon-yibiaopan
-
-
-
- -
-
-
- presto
-
- .icon-presto_sql
-
-
-
- -
-
-
- DB2
-
- .icon-shujukuleixingtubiao-kuozhan-
-
-
-
- -
-
-
- oceanbase
-
- .icon-oceanbase
-
-
-
- -
-
-
- 达梦
-
- .icon-dameng1
-
-
-
- -
-
-
- proxy
-
- .icon-proxy
-
-
-
- -
-
-
- openai
-
- .icon-openai
-
-
-
- -
-
-
- 关于
-
- .icon-guanyu
-
-
-
- -
-
-
- 衣服
-
- .icon-yifu
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku4
-
-
-
- -
-
-
- 数据源配置
-
- .icon-shujuyuanpeizhi
-
-
-
- -
-
-
- 服务器_数据库_jurassic
-
- .icon-jurassic_server
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku2
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku3
-
-
-
- -
-
-
- 数据库数据
-
- .icon-shujukushuju
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku1
-
-
-
- -
-
-
- 配置数据源
-
- .icon-peizhishujuyuan
-
-
-
- -
-
-
- SQL历史查询
-
- .icon-SQLlishichaxun
-
-
-
- -
-
-
- 重命名
-
- .icon-zhongmingming
-
-
-
- -
-
-
- ico_数据查询与统计_预约情况查询
-
- .icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun
-
-
-
- -
-
-
- clickhouse-云数据库ClickHouse
-
- .icon-clickhouse-yunshujukuClickHouse
-
-
-
- -
-
-
- rds_mariadb
-
- .icon-rds_mariadb
-
-
-
- -
-
-
- 减少减去减号
-
- .icon-jianshaojianqujianhao
-
-
-
- -
-
-
- sqlserver
-
- .icon-sqlserver
-
-
-
- -
-
-
- sqlite
-
- .icon-sqlite
-
-
-
- -
-
-
- 缺省页_暂无数据
-
- .icon-queshengye_zanwushuju
-
-
-
- -
-
-
- 未完成
-
- .icon-weiwancheng
-
-
-
- -
-
-
- 完成-01
-
- .icon-wancheng-
-
-
-
- -
-
-
- 成功
-
- .icon-chenggong1
-
-
-
- -
-
-
- 机器人
-
- .icon-jiqiren
-
-
-
- -
-
-
- 换一换
-
- .icon-huanyihuan
-
-
-
- -
-
-
- icon_infomation
-
- .icon-icon_infomation
-
-
-
- -
-
-
- key
-
- .icon-key1
-
-
-
- -
-
-
- mysql
-
- .icon-mysql
-
-
-
- -
-
-
- oracle
-
- .icon-oracle
-
-
-
- -
-
-
- postgresql
-
- .icon-postgresql
-
-
-
- -
-
-
- h2
-
- .icon-h2
-
-
-
- -
-
-
- cc-schema
-
- .icon-cc-schema
-
-
-
- -
-
-
- 新建表格
-
- .icon-xinjianbiaoge
-
-
-
- -
-
-
- export
-
- .icon-export
-
-
-
- -
-
-
- 角色管理
-
- .icon-jiaoseguanli
-
-
-
- -
-
-
- console
-
- .icon-console
-
-
-
- -
-
-
- 24gf-folderMinus
-
- .icon-24gf-folderMinus
-
-
-
- -
-
-
- 查看
-
- .icon-chakan
-
-
-
- -
-
-
- 复制_o
-
- .icon-fuzhi_o
-
-
-
- -
-
-
- 执行
-
- .icon-zhihang
-
-
-
- -
-
-
- m-格式化文字
-
- .icon-m-geshihuawenzi
-
-
-
- -
-
-
- github-fill
-
- .icon-github-fill
-
-
-
- -
-
-
- 保存
-
- .icon-baocun2
-
-
-
- -
-
-
- 箭头_向左两次_o
-
- .icon-jiantou_xiangzuoliangci_o
-
-
-
- -
-
-
- 新建窗口
-
- .icon-xinjianchuangkou
-
-
-
- -
-
-
- loading
-
- .icon-loading2
-
-
-
- -
-
-
- 链接克隆
-
- .icon-lianjiekelong
-
-
-
- -
-
-
- SQL升级文件
-
- .icon-SQLshengjiwenjian
-
-
-
- -
-
-
- sql
-
- .icon-sql
-
-
-
- -
-
-
- 连接流
-
- .icon-lianjieliu
-
-
-
- -
-
-
- 跳转/退出
-
- .icon-tiaozhuan
-
-
-
- -
-
-
- key
-
- .icon-key
-
-
-
- -
-
-
- 播放记录
-
- .icon-bofangjilu
-
-
-
- -
-
-
- 成功
-
- .icon-chenggong
-
-
-
- -
-
-
- 失败
-
- .icon-shibai
-
-
-
- -
-
-
- 收回 上下
-
- .icon-shouhuishangxia
-
-
-
- -
-
-
- 展开 上下
-
- .icon-zhankaishangxia
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku
-
-
-
- -
-
-
- 保存
-
- .icon-baocun
-
-
-
- -
-
-
- 查询
-
- .icon-chaxun
-
-
-
- -
-
-
- 对勾
-
- .icon-duigou11
-
-
-
- -
-
-
- check
-
- .icon-check1
-
-
-
- -
-
-
- 概览
-
- .icon-gailan
-
-
-
- -
-
-
- 概览
-
- .icon-huaban2
-
-
-
- -
-
-
- 编辑
-
- .icon-bianji
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin1
-
-
-
- -
-
-
- 菜单/列表
-
- .icon-caidan
-
-
-
- -
-
-
- 表格
-
- .icon-biaoge
-
-
-
- -
-
-
- 展开
-
- .icon-zhankai
-
-
-
- -
-
-
- 收起
-
- .icon-shouqi
-
-
-
- -
-
-
- 主题_o
-
- .icon-zhuti_o
-
-
-
- -
-
-
- 断开连接
-
- .icon-duankailianjie
-
-
-
- -
-
-
- 修改
-
- .icon-xiugai
-
-
-
- -
-
-
- 删除
-
- .icon-delete
-
-
-
- -
-
-
- 更多
-
- .icon-gengduo1
-
-
-
- -
-
-
- 减少
-
- .icon-jianshao
-
-
-
- -
-
-
- 加
-
- .icon-jia
-
-
-
- -
-
-
- 加号
-
- .icon-hao
-
-
-
- -
-
-
- arrow drop down
-
- .icon-right
-
-
-
- -
-
-
- search
-
- .icon-search1
-
-
-
- -
-
-
- download
-
- .icon-download1
-
-
-
- -
-
-
- 向右箭头
-
- .icon-xiangyoujiantou
-
-
-
- -
-
-
- 删除线型
-
- .icon-shanchuxianxing
-
-
-
- -
-
-
- cross
-
- .icon-cross-copy
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing
-
-
-
- -
-
-
- 138设置、系统设置、功能设置、属性
-
- .icon-shezhixitongshezhigongnengshezhishuxing
-
-
-
- -
-
-
- 执行sql脚本
-
- .icon-zhihangsqljiaoben
-
-
-
- -
-
-
- 虚拟数据库管理
-
- .icon-xunishujukuguanli
-
-
-
-
-
-
font-class 引用
-
-
-
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
-
与 Unicode 使用方式相比,具有如下特点:
-
- - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
- - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 fontclass 代码:
-
<link rel="stylesheet" href="./iconfont.css">
-
-
第二步:挑选相应图标并获取类名,应用于页面:
-
<span class="iconfont icon-xxx"></span>
-
-
- "
- iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
- -
-
-
right_on_5
- #icon-right_on_5
-
-
- -
-
-
right_off_5-01
- #icon-right_off_5-01
-
-
- -
-
-
left_on_2
- #icon-a-left_on_huaban11
-
-
- -
-
-
left_off
- #icon-a-left_off_huaban1
-
-
- -
-
-
minimize21
- #icon-minimize21
-
-
- -
-
-
restore
- #icon-restore_button2
-
-
- -
-
-
resize
- #icon-resize_button2
-
-
- -
-
-
close
- #icon-close_button2
-
-
- -
-
-
筛选
- #icon-shaixuan
-
-
- -
-
-
排序
- #icon-a-44tubiao-122
-
-
- -
-
-
305信息-线性圆框
- #icon-xinxi-xianxingyuankuang
-
-
- -
-
-
加号
- #icon-jiahao
-
-
- -
-
-
列表
- #icon-liebiao
-
-
- -
-
-
减去
- #icon-jianqu
-
-
- -
-
-
database
- #icon-database
-
-
- -
-
-
筛选
- #icon-shaixuan1
-
-
- -
-
-
刷新
- #icon-shuaxin2
-
-
- -
-
-
加号_o
- #icon-jiahao_o
-
-
- -
-
-
数据库_jurassic
- #icon-jurassic_data
-
-
- -
-
-
权限
- #icon-quanxian
-
-
- -
-
-
sharpicons_add-database
- #icon-sharpicons_add-database
-
-
- -
-
-
组织管理
- #icon-zuzhiguanli-
-
-
- -
-
-
空间
- #icon-moxing-miaobian
-
-
- -
-
-
下箭头-copy
- #icon-xiajiantou1-copy
-
-
- -
-
-
查看
- #icon-chakan2
-
-
- -
-
-
clone
- #icon-clone
-
-
- -
-
-
提交
- #icon-tijiao
-
-
- -
-
-
查看
- #icon-chakan1
-
-
- -
-
-
复制
- #icon-fuzhi
-
-
- -
-
-
icon_answer
- #icon-icon_answer
-
-
- -
-
-
icon_question
- #icon-icon_question
-
-
- -
-
-
发送
- #icon-fasong
-
-
- -
-
-
重启
- #icon-zhongqi
-
-
- -
-
-
提醒
- #icon-tixing2
-
-
- -
-
-
提醒
- #icon-tixing3
-
-
- -
-
-
提醒
- #icon-tixing1
-
-
- -
-
-
升级
- #icon-shengji
-
-
- -
-
-
全局_升级
- #icon-quanju_shengji
-
-
- -
-
-
关于我们
- #icon-guanyuwomen1
-
-
- -
-
-
ico版本更新
- #icon-icobanbengengxin
-
-
- -
-
-
对话气泡
- #icon-duihuaqipao
-
-
- -
-
-
角色权限
- #icon-jiaosequanxian
-
-
- -
-
-
preview
- #icon-preview1
-
-
- -
-
-
导入
- #icon-daoru
-
-
- -
-
-
终止
- #icon-zhongzhi
-
-
- -
-
-
退出
- #icon-tuichu
-
-
- -
-
-
控桩终端
- #icon-kongzhuangzhongduan
-
-
- -
-
-
撤销
- #icon-chexiao1
-
-
- -
-
-
向上
- #icon-xiangshang
-
-
- -
-
-
查看
- #icon-chakan-copy
-
-
- -
-
-
编辑数据_编辑录入操作_jurassic
- #icon-jurassic_edit-data
-
-
- -
-
-
编辑表格_编辑录入操作_jurassic
- #icon-jurassic_edit-table
-
-
- -
-
-
报表数据录入
- #icon-baobiaoshujuluru
-
-
- -
-
-
播放5
- #icon-bofang5
-
-
- -
-
-
清空@3x
- #icon-a-qingkong3x
-
-
- -
-
-
删除
- #icon-shanchu
-
-
- -
-
-
new-document-worksheet
- #icon-newdocumentworksheet
-
-
- -
-
-
file-excel
- #icon-file-excel
-
-
- -
-
-
file-markdown
- #icon-file-markdown
-
-
- -
-
-
file-word
- #icon-file-word
-
-
- -
-
-
HTML5
- #icon-HTML
-
-
- -
-
-
HTML
- #icon-HTML1
-
-
- -
-
-
pdf
- #icon-pdf
-
-
- -
-
-
个人用户
- #icon-gerenyonghu
-
-
- -
-
-
后台管理
- #icon-houtaiguanli
-
-
- -
-
-
字体代码
- #icon-zitidaima
-
-
- -
-
-
版本
- #icon-banben
-
-
- -
-
-
车位管理
- #icon-cheweiguanli
-
-
- -
-
-
dictate
- #icon-dianzhelidaochu
-
-
- -
-
-
circle-f
- #icon-circle-f
-
-
- -
-
-
图表-函数
- #icon-tubiao-hanshu
-
-
- -
-
-
视图管理器
- #icon-shituguanliqi
-
-
- -
-
-
回车
- #icon-huiche
-
-
- -
-
-
缺省
- #icon-quesheng
-
-
- -
-
-
进入箭头
- #icon-jinrujiantou
-
-
- -
-
-
右箭头
- #icon-youjiantou_huaban
-
-
- -
-
-
向右箭头
- #icon-xiangyoujiantou1
-
-
- -
-
-
数据源
- #icon-shujuyuan
-
-
- -
-
-
question
- #icon-question
-
-
- -
-
-
星星-copy
- #icon-xingxing
-
-
- -
-
-
控制台
- #icon-kongzhitai
-
-
- -
-
-
星系
- #icon-xingxi
-
-
- -
-
-
暂无数据 (1)
- #icon-a-zanwushuju1
-
-
- -
-
-
开始
- #icon-kaishi
-
-
- -
-
-
关闭
- #icon-guanbi
-
-
- -
-
-
下箭头
- #icon-xiajiantou
-
-
- -
-
-
more
- #icon-gengduo
-
-
- -
-
-
设置
- #icon-shezhi
-
-
- -
-
-
对话-未选
- #icon-duihua-weixuan
-
-
- -
-
-
图表-未选
- #icon-tubiao-weixuan
-
-
- -
-
-
编组 13备份 3
- #icon-a-bianzu13beifen3
-
-
- -
-
-
编组备份
- #icon-bianzubeifen
-
-
- -
-
-
表格
- #icon-biaoge1
-
-
- -
-
-
收藏 (1)
- #icon-a-shoucang1
-
-
- -
-
-
guthub-未选
- #icon-guthub-weixuan1
-
-
- -
-
-
数据-未选
- #icon-shuju-weixuan
-
-
- -
-
-
编组 4
- #icon-a-bianzu4
-
-
- -
-
-
编组 14备份
- #icon-a-bianzu14beifen
-
-
- -
-
-
guthub-未选
- #icon-guthub-weixuan
-
-
- -
-
-
24gl-folderMinus
- #icon-24gl-folderMinus
-
-
- -
-
-
24gl-folderOpen
- #icon-24gl-folderOpen
-
-
- -
-
-
24gf-folderOpen
- #icon-24gf-folderOpen
-
-
- -
-
-
云数据库
- #icon-yunshujuku
-
-
- -
-
-
报表
- #icon-baobiao
-
-
- -
-
-
工作台
- #icon-gongzuotai
-
-
- -
-
-
mongodb
- #icon-mongodb
-
-
- -
-
-
Redis
- #icon-Redis
-
-
- -
-
-
HIVE_2
- #icon-HIVE
-
-
- -
-
-
Kingbase
- #icon-Kingbase
-
-
- -
-
-
仪表盘
- #icon-yibiaopan
-
-
- -
-
-
presto
- #icon-presto_sql
-
-
- -
-
-
DB2
- #icon-shujukuleixingtubiao-kuozhan-
-
-
- -
-
-
oceanbase
- #icon-oceanbase
-
-
- -
-
-
达梦
- #icon-dameng1
-
-
- -
-
-
proxy
- #icon-proxy
-
-
- -
-
-
openai
- #icon-openai
-
-
- -
-
-
关于
- #icon-guanyu
-
-
- -
-
-
衣服
- #icon-yifu
-
-
- -
-
-
数据库
- #icon-shujuku4
-
-
- -
-
-
数据源配置
- #icon-shujuyuanpeizhi
-
-
- -
-
-
服务器_数据库_jurassic
- #icon-jurassic_server
-
-
- -
-
-
数据库
- #icon-shujuku2
-
-
- -
-
-
数据库
- #icon-shujuku3
-
-
- -
-
-
数据库数据
- #icon-shujukushuju
-
-
- -
-
-
数据库
- #icon-shujuku1
-
-
- -
-
-
配置数据源
- #icon-peizhishujuyuan
-
-
- -
-
-
SQL历史查询
- #icon-SQLlishichaxun
-
-
- -
-
-
重命名
- #icon-zhongmingming
-
-
- -
-
-
ico_数据查询与统计_预约情况查询
- #icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun
-
-
- -
-
-
clickhouse-云数据库ClickHouse
- #icon-clickhouse-yunshujukuClickHouse
-
-
- -
-
-
rds_mariadb
- #icon-rds_mariadb
-
-
- -
-
-
减少减去减号
- #icon-jianshaojianqujianhao
-
-
- -
-
-
sqlserver
- #icon-sqlserver
-
-
- -
-
-
sqlite
- #icon-sqlite
-
-
- -
-
-
缺省页_暂无数据
- #icon-queshengye_zanwushuju
-
-
- -
-
-
未完成
- #icon-weiwancheng
-
-
- -
-
-
完成-01
- #icon-wancheng-
-
-
- -
-
-
成功
- #icon-chenggong1
-
-
- -
-
-
机器人
- #icon-jiqiren
-
-
- -
-
-
换一换
- #icon-huanyihuan
-
-
- -
-
-
icon_infomation
- #icon-icon_infomation
-
-
- -
-
-
key
- #icon-key1
-
-
- -
-
-
mysql
- #icon-mysql
-
-
- -
-
-
oracle
- #icon-oracle
-
-
- -
-
-
postgresql
- #icon-postgresql
-
-
- -
-
-
h2
- #icon-h2
-
-
- -
-
-
cc-schema
- #icon-cc-schema
-
-
- -
-
-
新建表格
- #icon-xinjianbiaoge
-
-
- -
-
-
export
- #icon-export
-
-
- -
-
-
角色管理
- #icon-jiaoseguanli
-
-
- -
-
-
console
- #icon-console
-
-
- -
-
-
24gf-folderMinus
- #icon-24gf-folderMinus
-
-
- -
-
-
查看
- #icon-chakan
-
-
- -
-
-
复制_o
- #icon-fuzhi_o
-
-
- -
-
-
执行
- #icon-zhihang
-
-
- -
-
-
m-格式化文字
- #icon-m-geshihuawenzi
-
-
- -
-
-
github-fill
- #icon-github-fill
-
-
- -
-
-
保存
- #icon-baocun2
-
-
- -
-
-
箭头_向左两次_o
- #icon-jiantou_xiangzuoliangci_o
-
-
- -
-
-
新建窗口
- #icon-xinjianchuangkou
-
-
- -
-
-
loading
- #icon-loading2
-
-
- -
-
-
链接克隆
- #icon-lianjiekelong
-
-
- -
-
-
SQL升级文件
- #icon-SQLshengjiwenjian
-
-
- -
-
-
sql
- #icon-sql
-
-
- -
-
-
连接流
- #icon-lianjieliu
-
-
- -
-
-
跳转/退出
- #icon-tiaozhuan
-
-
- -
-
-
key
- #icon-key
-
-
- -
-
-
播放记录
- #icon-bofangjilu
-
-
- -
-
-
成功
- #icon-chenggong
-
-
- -
-
-
失败
- #icon-shibai
-
-
- -
-
-
收回 上下
- #icon-shouhuishangxia
-
-
- -
-
-
展开 上下
- #icon-zhankaishangxia
-
-
- -
-
-
数据库
- #icon-shujuku
-
-
- -
-
-
保存
- #icon-baocun
-
-
- -
-
-
查询
- #icon-chaxun
-
-
- -
-
-
对勾
- #icon-duigou11
-
-
- -
-
-
check
- #icon-check1
-
-
- -
-
-
概览
- #icon-gailan
-
-
- -
-
-
概览
- #icon-huaban2
-
-
- -
-
-
编辑
- #icon-bianji
-
-
- -
-
-
刷新
- #icon-shuaxin1
-
-
- -
-
-
菜单/列表
- #icon-caidan
-
-
- -
-
-
表格
- #icon-biaoge
-
-
- -
-
-
展开
- #icon-zhankai
-
-
- -
-
-
收起
- #icon-shouqi
-
-
- -
-
-
主题_o
- #icon-zhuti_o
-
-
- -
-
-
断开连接
- #icon-duankailianjie
-
-
- -
-
-
修改
- #icon-xiugai
-
-
- -
-
-
删除
- #icon-delete
-
-
- -
-
-
更多
- #icon-gengduo1
-
-
- -
-
-
减少
- #icon-jianshao
-
-
- -
-
-
加
- #icon-jia
-
-
- -
-
-
加号
- #icon-hao
-
-
- -
-
-
arrow drop down
- #icon-right
-
-
- -
-
-
search
- #icon-search1
-
-
- -
-
-
download
- #icon-download1
-
-
- -
-
-
向右箭头
- #icon-xiangyoujiantou
-
-
- -
-
-
删除线型
- #icon-shanchuxianxing
-
-
- -
-
-
cross
- #icon-cross-copy
-
-
- -
-
-
刷新
- #icon-shuaxin
-
-
- -
-
-
提醒
- #icon-tixing
-
-
- -
-
-
138设置、系统设置、功能设置、属性
- #icon-shezhixitongshezhigongnengshezhishuxing
-
-
- -
-
-
执行sql脚本
- #icon-zhihangsqljiaoben
-
-
- -
-
-
虚拟数据库管理
- #icon-xunishujukuguanli
-
-
-
-
-
Symbol 引用
-
-
-
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
- 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
-
- - 支持多色图标了,不再受单色限制。
- - 通过一些技巧,支持像字体那样,通过
font-size, color 来调整样式。
- - 兼容性较差,支持 IE9+,及现代浏览器。
- - 浏览器渲染 SVG 的性能一般,还不如 png。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 symbol 代码:
-
<script src="./iconfont.js"></script>
-
-
第二步:加入通用 CSS 代码(引入一次就行):
-
<style>
-.icon {
- width: 1em;
- height: 1em;
- vertical-align: -0.15em;
- fill: currentColor;
- overflow: hidden;
-}
-</style>
-
-
第三步:挑选相应图标并获取类名,应用于页面:
-
<svg class="icon" aria-hidden="true">
- <use xlink:href="#icon-xxx"></use>
-</svg>
-
-
-
-
-
-
-
-
-
diff --git a/chat2db-client/src/assets/font/iconfont-he.ttf b/chat2db-client/src/assets/font/iconfont-he.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..6c5de7a91fa3d7f190cbca8110029f488e01f2cb
GIT binary patch
literal 2316
zcmd^A&2Jk;6o0ed_WE<}cx|Uix3;rR8rP0Dj@_nBkZ_YoB_d6l^h;G6$8qdb{=#uu
zmjeXxAy7deacK{z5?nyyfVfrS0HkuDDP9269*XLrzwxuxt13aKNsjD*=NeZ8Q@LuhXR1C~L%LqM=tjsx-*@VMM-
z%;(6x*|p>+FbGC6z_ZByM?1YSvksp=R32S1>jCVyu!FLHSA(Go_Bujb2Tur)=+fk}
zr9Y1>jZC1$6C=-=8UZZ_jH6$8{1B`n;y=&FEs12qG&ClI#;?9CgzqMRF?n
zeQkMhEG>5w+xGrmEikQJ_;GjjCOH4DEhUR01h}}s9-Y@z6iNpvO2zz`3+RAx-
zM3w|892j%(Jj>fyhd<_3!>+8`tMIzw@`~z!>XaFN?e^RCkcZq>4#XUTvI}FJgt&(y
z!GvV9OOlJ1B(EZf9Osg7K{=`_T7nl`f};3?qMacuZrmxyxzYYbvf7D{_xp!JQ%aoZ
z!{_Qxtvs`!MbuDa$cRQq@{{YlWM>7L$DA@F4%p-X<7FN0*i>fZ$Vm9~bH~)nr^c7h
zb85nHYHr2r89bU#P9=lNu+welB-gp@(Pa8yB%&FEhl3tt<&Jje5IhmrCXBYN6G0d-iOpwYAx(Yw6@zkD4phORZj-t!rhY
z-QLhPT8)}Ei}^~`YC~(b8dpojcCylLHz!jmx(?-YPSC(1H>appD}?Xs`x1aNL9<
zG@uR}=GcDBUqW>mwxNoVt;c5fN1sJ?3${>4b83)=B)$UotL9KuHZaGp{Y=p9UQh|7E^TGFv69#(!nXGPD|F(
zL1?u(g_6>gLot*@Ryw>z-n`GQxBhsa>-zkj`}@7_=f0oc?|0q%iR;%xFbFJl0yJN>
z&(r!(tK$DJzpWu&5Cqczt~-Eakz;d!xE1RJJRhJ{kUi{(SXvx~1-xTGZwGtE(ebXS
znAntP5Gx=Ez89eQXw@g9M^PdnNR0&YumBf_<6#zbU;-~4=*0kQ)Bqx#9>-1wUMbL8
zDpZfyc$zBj8VFVQ(SXjuSaFnO&{ypi(B=RO*aGMrlM)vNJUQr10DWV0E7piv@ri8k
zz6K!29^lr!>DtreA{sf8Ojb~FWHF7Lu=mS6isDhg@-`ZuN~Uli{u-x3yxj^90Tpe>@)j=>>Yccee0I!mFDi=@(asZ`#^&fs#(%)M6JkH%5oK-o@Wrxj!*j3Khu
zLY<#4$%Rk5V%$0t3n+GqX
zKlf>iQPqVyw%~5QL7U^B6Li1z4cs2N-jq9X`?YP-WMr{(3!|t>nQ4kHBWW7iTnq_M
z4u6D
zuRofa@&qR}=5v%@sAb5Pd@MoA
zFmd^5#}&JU>t1yr8>ed0DwS7$_?+~Jk2jJ}$YM`K%WwX(*S&!FXndBYOf{Z%+|WK-
zIBa13_S21EzFE}1!yQj$!IC2^qZs`6{O=^pOgF48!(wArDR(E<$Dz%pHQ1CGd~#8$
zzX`&!GxWVf_LiMA`$P@37152z%ij2v9AnHo-W+CnzSEs;zmUdRmO0G&Cd-ImSn4Hu
zL6Cgdp@t+n@bK~ln;d_3RT9z*ZM3@i+C)!xS2>eFrPpyPcbpDQKJsO#%bvT4Rx6T6
z9#0@a+1i7-O{vz|rUt&R3GmK6e@Oz-;vrvMMk{VpIDd!qS!QKIYt30(vfKQF8>1V0
z@m_=iyR$mE5lfOi_sy=?Q>%LR0%>4_ZsWhm3oQ%dJ8XNGM?HLbusvAzo&|@#
zywuSDldqZ!-Npt8PBUZa57}j6zx;dAx
z|3>3IJu%xqrN*Wq2N~$eWU3Hlk^%73|~7j+s8EM7kUcWy~cJ$L_QW
z%@cTzaQ2g(R&`wsDQ=r{9U>-zlGqsLqk?qjys(Y{YW1+rnpaiAbU9*q&Q-0$w`$Av
zj|M*G&%%06#cx-AUn&Q3RR6%q7i03X9qeP#s^VUO`>E!3gfmVY#%pix0I?8
zyA=yRs_6@ULK6>9%iQ}jvzy*rtvYduKb2Mi6^}g&-N@2(ghxm$ib&8qkf)i+;B-Pd
zkZC(mD0Tp!+L*H;%c%%8lCw;D{F}oO@Zf|B8S681#8tKLL_Wx^
zqtsobE0F415mK+oroodbn%eloBMG9W>w1Y`S(|oBZiz@dc`No>eujpc6#fyLnqk!I
zT%^P1NM0>&r`V+>5>xqDX(49+y78m<{^
z4!U`b+Vi0g!FV5D(<5E$&7
zfhPnycnSJdBoeNlgyf7(?SW%p8($M2gmm<7YL0FKjg74?AVGZ!b9`OG?Wp$dvJtKw
zJOND*6@}UqrVI^V-4_#X?{9D6!4q^x3Bvw9e$KKEGK!Y4wJR!4DK_%&Z&5)-^CNr!
zABInp8De&2;o#$7(d30>>QF#5TES>b($!L{n$_Gc*$6U0^_gN<97fEUAwF0a*jmxj
zQN~#o?rx}2#
z8C3>Ncm9-bJooDi8j2t0JMJD2SZVM5@9Zhyk9tp!M4RpcUZ+z+$bJv~1&~;jIJp$b
zELnv=dmF2e&}}4X$n?;#=j{U{$RbbZlyZ!u!wj-cvs4kRqS9j*wNCr!w#IaMj}bBQ
zqI6+VK0+8YilpBovO&jG5s*+B_W`v*|IzIz`Znva7{-iI6Es2p1lOR*GL7Q#y`r54
z8t0{-$cc-SFjj!m#T7=#T}(+{_C&-2iz$R9KMsR{AZGH=kbxr-Plt*@MNb(R6lP`^
z(g_yaTrFo0U=B2dejbdUF9n@AumIxGky
zAz-F>P+;T;h$-&pK?X4p(jpU^QY54xsBSR6A-uS4&`sYxM~WOp{W7&vEh@Jh!pQx|
GWB>qmq98&5
literal 0
HcmV?d00001
diff --git a/chat2db-client/src/assets/font/iconfont.css b/chat2db-client/src/assets/font/iconfont.css
index 52ce3cf9e..ce32ae206 100644
--- a/chat2db-client/src/assets/font/iconfont.css
+++ b/chat2db-client/src/assets/font/iconfont.css
@@ -829,3 +829,10 @@
content: "\e61a";
}
+.icon-Phoenix:before {
+ content: "\e712";
+}
+
+.icon-wuguan:before {
+ content: "\ec5f";
+}
diff --git a/chat2db-client/src/assets/img/databaseImg/phoenixLogo.png b/chat2db-client/src/assets/img/databaseImg/phoenixLogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..50bd4a59d5964af0dab2b777a62f5861afb4dedc
GIT binary patch
literal 4037
zcmV;$4?6IPP)cCo%rjL
zHyLe>lBp9_c+}L1Dhz?r^4(erzB>Sv&$S@O6k(GyXTqiI{|RZ(WbR2_=DxPwtV#4kUFJY<<8EOW_q3emVgQ^8*NuXH
zJ2gNNuXp836;8oxbcrrTjvZ%!CUb9O{xhM;To|?-#k(5-!z-bk>}1#>O%+GuxuCSP
z7KEG*e6>u)HCB_kVaBn{*;5mmM4x3dfL%Zbv@ksb6rsO}5n@@(?kvzHy1L%agznI(
zpo;M#qyKBYgPy5Lbg+Qmr|rVa-2K@LSd%y{xiR#Xd&kTO2J{H0lbrxsJDsIVT;&^{
zk&>?nT2YQ36W%~B3%S)KdatcBUE(h{89?S>a9MM*Xy@6P&>iZir{Y%V5}nOPm_&a?
z>ICR%38%e4aefAu29s7^QC?ezM(eZ_trrel8z@eG-UumB#ih_i#*jKfmjlOYGWVOT
zyoOA#tkW)cSw`oQ+{CX5n8{~yHlASX_`j_!6E&GjnJ%2k0dx!H86h1VZH3+-S4#qR
zv<6x&N-w;fc3}k1!~KJqCts6ORNgnM!#bOlvj}JvaJJ)
zn<_Ivel~0eMevLi=pth@iTl{*PxFz*2Ro#xhj{h8+;i2?kC`r2a4>jl&@QXQMO`1J
zcknZ8{_1?bGB;$O5wGB;Zlpx?OZ-u8l?n@V+w-iI<-*C*#$!2NE-#P=L|G^oOFN2+h)
z{$>Xm>ANKb&?RoJ^~U&>Wky4#Tax8VdZ$k*B}ac7Jw=!4j*_F^+_qOue7ATgsNyP6
zgkdjqjnA`3Kf2s_S!qGe3XRt!`lNC9DA@Y&A-oo<$UE)?E=Q#MyqdlFocrf0^2XM2X
zN!;tEj>0@7ANJ4{sacmK41ywFREjh`N>&vbfEi)_gpUgM17(q~h%<{P(5h?6^#bT&
zd)yCaOr8)wmutciUziud{1ws8ZJKP2Fj(4D;$F}sdb=)B(^QYm<{|Z1EsP-I)_zVg
z;^s)Z3^c(CnAV3>^6BP8i$1Y^!YOfFaQx3qjaohI?v?W_~PxUe$r
zGVP|k0mA$gwh&tu-1s$3#XAY}Fm!tj7G_0!2R8EiSV`>bA_wbHl5#Y_qA{SK*9hOV
z@dw(--^lNiD>U;?Mi{z1X!H5jh8!Z7HWwe}&3mEDVR7!F!NtL}h1Ux#r%}wx7jlS9Gv^&Hp-UV
zIw8y^nmU=uLF(}WQWX)B4~1apwq_Vu5McX)*d;t$cyQM@*coKkcsRlm&=Wc-M`9P*
z+lZ3C6*T~^2bUdeszSp)JoWpE$aF=WQFq3{oB-owXjzbPRLBDcEY_4XW_2C?q$bf<
zOtawTDl?(pDf=(z4NZ?l2yaBl?@N)-TBHH=Yxa9#$NY&R=AKv|qo~8+%|UxunkoIU
z8=###-DIhDj%}+)Re=W%EKRyg8(uqkRAp!90Rzitm4^q=D)-~2M1H_h%tKF#ID6l<%bH0arHCys;$oxrIeOS@}
zy3F00S+QT&WV$d@uw`>c+09x_@n*I4|x0
zF}%XvU9C50@jLeR%mCO|tv6f)H#ISCkR#;E!3ax8xxm9?U83(5b}-bHPR`Y<4ct4?
zyG8ikiFK77xw4SIy`qH}feUf=g#uGFq=EHS^8vFWWIK{%+_h2mepuDeBzgl@8#v~?
z7Y8A^CIrx38>I&B-f!gY=dKC0A-!8V&$Xj_mxmqsSSsMEi_3daftr=Zw%wr|L4NIL!uI
zczT0J8AwHd*ll}_Yy+lv6DA}1rSxXpNOX%XbB@Jko}j;-KN1UOt!KzN*EEY~IT6o%
z>zc!=AN^|%_e>7Vfw^tESdo=P@5Pr#NUecITe?iQxshM-3mt0lmR#2iMrRdiiO#W*
zeAruAP&dbXPulHlMQ&AbLctI}_4f!sn*9(N1qS
zBINC|$&ZkCHJRR#b(676`E=;)RkWsH{eQ?Y@1LFx+*gb^z*+Nb;eFUwram(t!JUUC
zmH{RM%7QW#$~e5dnaPeiVCtR88FnTdxj;^ko4PE-%ox(tLon9WWV!;+3<_Vxue52b
zRGa$?ekcTAOLBXr5g}WPl8@DK;c4j|Xa_&T@s+1G?iPHOTNGEOyn{?!Ydv9o#do)57seNT_PaD8ZreJwC%xxc{e!v)qaqog*MS6qeXz%~P1
z#``m%Qs`0gpGp?t!e%X$8n`L2qhbN}bQlrxMxgC-np%776i
zx7laG_>!{#Un-B!&lCtN!jnGPRU#fCO?s3hY_s4|TJ)DD;ENKuS7=LopVUVQ7!mTg
zsp>6C7K19za@~ln4YA6#4EY-LPIvxPypQq_?;;^RN`^89=!1D=FX)}Vx7#;A@f&XW
zpM{5fo;`pOB`Y#Tw_CIS2CA^8L}D%hz0Ku)oy13Rz>Q>r9wG1RQSwyo-_+2V#iJo%
zLy2yERD48PI0Mpd$nAsM-@vBe;T1i=M`;+H#Pm^DP=x254bZZnjP87l6zq4^*nGe6
z8vf3g5qIvR%wHjBUU>VUTL_kMs}C;#_*u$4`6>K1w9jlE0h>dU3ZfqNT9S`?8URQ`
r=Bv1?eBwt5poq(T@_qOqyD0t-v-dT?Bw4kc00000NkvXXu0mjf!-KUl
literal 0
HcmV?d00001
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
index c82f2063c..82bc05d02 100644
--- a/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
@@ -1,14 +1,15 @@
-import React, { useContext, useEffect, useImperativeHandle, ForwardedRef, forwardRef } from 'react';
-import styles from './index.less';
-import classnames from 'classnames';
-import { Form, Input } from 'antd';
-import { Context } from '../index';
-import { IBaseInfo } from '@/typings';
import { DatabaseTypeCode } from '@/constants';
import i18n from '@/i18n';
+import { IBaseInfo } from '@/typings';
+import { Form, Input } from 'antd';
+import classnames from 'classnames';
+import { ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle } from 'react';
+import { Context } from '../index';
+import styles from './index.less';
export interface IBaseInfoRef {
getBaseInfo: () => IBaseInfo;
+ setTableComment: (comment: string) => void;
}
interface IProps {
@@ -24,6 +25,7 @@ const BaseInfo = forwardRef((props: IProps, ref: ForwardedRef) =>
form.setFieldsValue({
name: tableDetails.name,
comment: tableDetails.comment,
+ aiComment: tableDetails.aiComment,
charset: tableDetails.charset,
engine: tableDetails.engine,
incrementValue: tableDetails.incrementValue,
@@ -36,6 +38,9 @@ const BaseInfo = forwardRef((props: IProps, ref: ForwardedRef) =>
useImperativeHandle(ref, () => ({
getBaseInfo,
+ setTableComment: (comment: string) => {
+ form.setFieldsValue({ comment:comment,aiComment:comment });
+ },
}));
return (
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
index ad6b6718f..d732b976f 100644
--- a/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
@@ -1,19 +1,19 @@
-import React, { useContext, useEffect, useState, useRef, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
-import styles from './index.less';
-import classnames from 'classnames';
+import CustomSelect from '@/components/CustomSelect';
+import Iconfont from '@/components/Iconfont';
+import { DatabaseTypeCode, EditColumnOperationType, NullableType } from '@/constants';
+import i18n from '@/i18n';
+import { IColumnItemNew, IColumnTypes } from '@/typings';
import { MenuOutlined } from '@ant-design/icons';
import { DndContext, type DragEndEvent } from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
-import { Table, InputNumber, Input, Form, Select, Checkbox } from 'antd';
-import { v4 as uuidv4 } from 'uuid';
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
+import { Checkbox, Form, Input, InputNumber, Select, Table } from 'antd';
+import classnames from 'classnames';
+import React, { ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
+import { v4 as uuidv4 } from 'uuid';
import { Context } from '../index';
-import { IColumnItemNew, IColumnTypes } from '@/typings';
-import i18n from '@/i18n';
-import { EditColumnOperationType, DatabaseTypeCode, NullableType } from '@/constants';
-import CustomSelect from '@/components/CustomSelect';
-import Iconfont from '@/components/Iconfont';
+import styles from './index.less';
interface RowProps extends React.HTMLAttributes {
'data-row-key': string;
@@ -29,6 +29,7 @@ interface IEditingConfig extends IColumnTypes {
// 本组件暴露给父组件的方法
export interface IColumnListRef {
getColumnListInfo: () => IColumnItemNew[];
+ setColumnComment: (columnName: string, comment: string) => void;
}
const Row = ({ children, ...props }: RowProps) => {
@@ -71,6 +72,7 @@ const createInitialData = () => {
defaultValue: null,
autoIncrement: null,
comment: null,
+ aiComment: null,
primaryKey: null,
primaryKeyOrder: null,
schemaName: null,
@@ -477,6 +479,21 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
useImperativeHandle(ref, () => ({
getColumnListInfo,
+ setColumnComment: (columnName: string, comment: string) => {
+ setDataSource((prevDataSource) =>
+ prevDataSource.map((column) => {
+ if (column.name === columnName) {
+ return {
+ ...column,
+ comment,
+ aiComment: comment,
+ editStatus:EditColumnOperationType.Modify,
+ };
+ }
+ return column;
+ })
+ );
+ },
}));
const renderOtherInfoForm = () => {
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less
new file mode 100644
index 000000000..a1b1efeb0
--- /dev/null
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less
@@ -0,0 +1,162 @@
+@import '../../../styles/var.less';
+
+.foreignKeyList {
+ height: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+}
+
+.foreignKeyListHeader {
+ margin: 0px -5px 10px;
+ flex-shrink: 0;
+ button {
+ margin: 0px 5px;
+ }
+}
+
+.formBox {
+ height: 0px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+.tableBox {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ border-radius: 8px 8px 0px 0px;
+ overflow: hidden;
+}
+
+.addColumnButton {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px dashed var(--color-border);
+ line-height: 30px;
+ margin-top: 10px;
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ i {
+ margin-right: 5px;
+ }
+ &:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+ }
+}
+
+.otherInfo {
+ flex-shrink: 0;
+ margin: 10px -10px 0px;
+ padding: 10px;
+ height: 200px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-top: 1px solid var(--color-border);
+}
+
+.otherInfoFormBox {
+ min-height: 140px;
+ width: 400px;
+}
+
+.editableCell {
+ height: 28px;
+ line-height: 26px;
+ padding: 0px 7px;
+ box-sizing: border-box;
+ border: 1px solid transparent;
+ .f-single-line();
+ width: 100%;
+ cursor: pointer;
+}
+
+// .cellContent {
+// border: 1px solid transparent;
+// margin: -1px;
+// &:hover {
+// border: 1px solid var(--color-primary);
+// }
+// }
+
+.keyBox {
+ width: 26px;
+ height: 26px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ position: relative;
+ i {
+ color: var(--color-warning);
+ }
+ span {
+ position: absolute;
+ font-weight: bold;
+ right: 4px;
+ bottom: -2px;
+ transform: scale(0.8);
+ }
+}
+
+.disabledKeyBox {
+ cursor: default;
+}
+
+.operationBar {
+ display: flex;
+ justify-content: end;
+ .deleteIconBox {
+ height: 26px;
+ width: 26px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ &:hover {
+ color: var(--color-primary);
+ }
+ }
+}
+.foreignKeyList {
+ :global {
+ .ant-table-body {
+ border: 1px solid var(--color-border);
+ border-top: 0px;
+ border-bottom: 0px;
+ }
+ .ant-table-header {
+ border: 1px solid var(--color-border);
+ border-bottom: 0px;
+ }
+ .ant-table {
+ border-radius: 10px;
+ border-bottom: 0px;
+ }
+ .ant-table-wrapper .ant-table-tbody > tr > td {
+ // border: 0px;
+ padding: 4px 2px;
+ }
+ .ant-table-wrapper .ant-table-thead > tr > th {
+ padding: 8px 4px;
+ &::before {
+ display: none;
+ }
+ }
+ .ant-table-wrapper .ant-table-thead > tr > td {
+ &::before {
+ display: none;
+ }
+ }
+ // antd无法设置最小宽度,所以在这里设置最小列宽为100px
+ colgroup col:nth-last-child(2) {
+ min-width: 100px;
+ }
+ }
+}
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx
new file mode 100644
index 000000000..d205b6503
--- /dev/null
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx
@@ -0,0 +1,406 @@
+import React, { useContext, useEffect, useState, useRef, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
+import styles from './index.less';
+import classnames from 'classnames';
+import { MenuOutlined } from '@ant-design/icons';
+import { DndContext, type DragEndEvent } from '@dnd-kit/core';
+import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
+import { Table, Input, Form, Select, Checkbox } from 'antd';
+import { v4 as uuidv4 } from 'uuid';
+import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
+import { Context } from '../index';
+import { IForeignKeyItemNew, IForeignKey } from '@/typings'; // 假设你有一个外键的类型定义
+import i18n from '@/i18n';
+import { EditColumnOperationType } from '@/constants';
+import Iconfont from '@/components/Iconfont';
+
+interface RowProps extends React.HTMLAttributes {
+ 'data-row-key': string;
+}
+
+interface IProps { }
+
+// 编辑配置
+interface IEditingConfig extends IForeignKey {
+ editKey: string;
+}
+
+export type IForeignKeyListInfo = IForeignKeyItemNew[]; // 外键信息列表类型
+
+export interface IForeignKeyListRef {
+ getForeignKeyListInfo: () => IForeignKeyListInfo; // 获取外键信息列表的方法
+}
+
+// 创建一个空的外键数据结构
+const createInitialData = () => {
+ return {
+ key: uuidv4(),
+ name: null,
+ tableName: null,
+ schemaName: null,
+ databaseName: null,
+ column: null,
+ referencedTable: null,
+ referencedColumn: null,
+ updateRule: 0,
+ deleteRule: 0,
+ comment: null,
+ editStatus: EditColumnOperationType.Add,
+ };
+};
+
+const Row = ({ children, ...props }: RowProps) => {
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
+ id: props['data-row-key'],
+ });
+
+ const style: React.CSSProperties = {
+ ...props.style,
+ transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
+ transition,
+ ...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
+ };
+
+ return (
+
+ {React.Children.map(children, (child) => {
+ if ((child as React.ReactElement).key === 'sort') {
+ return React.cloneElement(child as React.ReactElement, {
+ children: (
+
+ ),
+ });
+ }
+ return child;
+ })}
+
+ );
+};
+
+
+const ForeignKeyList = forwardRef((props: IProps, ref: ForwardedRef) => {
+ const { tableDetails } = useContext(Context);
+ const [dataSource, setDataSource] = useState([createInitialData()]);
+ const [form] = Form.useForm();
+ const [editingData, setEditingData] = useState(null);
+ const [editingConfig, setEditingConfig] = useState(null);
+ const tableRef = useRef(null);
+
+ const isEditing = (record: IForeignKeyItemNew) => record.key === editingData?.key;
+ const handleFieldsChange = (field: any) => {
+ let { value } = field[0];
+ const { name: nameList } = field[0];
+ const name = nameList[0];
+ const newData = dataSource.map((item) => {
+ if (item.key === editingData?.key) {
+ // 判断当前数据是新增的数据还是编辑后的数据
+ let editStatus = item.editStatus;
+ if (editStatus !== EditColumnOperationType.Add) {
+ editStatus = EditColumnOperationType.Modify;
+ }
+ const editingDataItem = {
+ ...item,
+ [name]: value,
+ editStatus,
+ };
+ return editingDataItem;
+ }
+ return item;
+ });
+ setDataSource(newData);
+ };
+
+ const edit = (record: IForeignKeyItemNew) => {
+ if (record.key) {
+ form.setFieldsValue({ ...record });
+ setEditingData(record);
+ }
+ };
+
+ // 整理服务端返回的数据,构造为前端需要的数据结构
+ useEffect(() => {
+ if (tableDetails) {
+ const list =
+ tableDetails?.foreignKeyList?.map((t) => {
+ return {
+ ...t,
+ key: uuidv4(),
+ };
+ }) || [];
+ setDataSource(list);
+ }
+ }, [tableDetails]);
+
+ const columns = [
+ {
+ key: 'sort',
+ width: '40px',
+ align: 'center',
+ fixed: 'left',
+ },
+ {
+ title: i18n('editTable.label.foreignKeyName'),
+ dataIndex: 'name',
+ width: '160px',
+ fixed: 'left',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.column'),
+ dataIndex: 'column',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.referencedTable'),
+ dataIndex: 'referencedTable',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.referencedColumn'),
+ dataIndex: 'referencedColumn',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.updateRule'),
+ dataIndex: 'updateRule',
+ width: '160px',
+ render: (text: number, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
+ {text === 0 ? 'CASCADE' :
+ text === 1 ? 'SET NULL' :
+ text === 2 ? 'NO ACTION' :
+ text === 3 ? 'RESTRICT' :
+ text === 4 ? 'SET DEFAULT' : 'UNKNOWN'}
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.deleteRule'),
+ dataIndex: 'deleteRule',
+ width: '160px',
+ render: (text: number, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
+ {text === 0 ? 'CASCADE' :
+ text === 1 ? 'SET NULL' :
+ text === 2 ? 'NO ACTION' :
+ text === 3 ? 'RESTRICT' :
+ text === 4 ? 'SET DEFAULT' : 'UNKNOWN'}
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.comment'),
+ dataIndex: 'comment',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return editable ? (
+
+
+
+ ) : (
+ {text}
+ );
+ },
+ },
+ {
+ width: '40px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ return (
+ {
+ deleteData(record);
+ }}
+ >
+
+
+
+
+ );
+ },
+ },
+ ];
+
+ const onDragEnd = ({ active, over }: DragEndEvent) => {
+ if (active.id !== over?.id) {
+ setDataSource((previous) => {
+ const activeIndex = previous.findIndex((i) => i.key === active.id);
+ const overIndex = previous.findIndex((i) => i.key === over?.id);
+ return arrayMove(previous, activeIndex, overIndex);
+ });
+ }
+ };
+
+ const addData = () => {
+ const newData = {
+ ...createInitialData(),
+ };
+ setDataSource([...dataSource, newData]);
+ edit(newData);
+ setTimeout(() => {
+ tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);
+ }, 0);
+ };
+
+ const deleteData = (record) => {
+ let list: any = [];
+ if (record?.editStatus === EditColumnOperationType.Add) {
+ list = dataSource.filter((i) => i.key !== record?.key);
+ } else {
+ list = dataSource.map((i) => {
+ if (i.key === record?.key) {
+ setEditingData(null);
+ setEditingConfig(null);
+ return {
+ ...i,
+ editStatus: EditColumnOperationType.Delete,
+ };
+ }
+ return i;
+ });
+ }
+ setDataSource(list);
+ };
+
+ useImperativeHandle(ref, () => ({
+ getForeignKeyListInfo: () => {
+ return dataSource.map((i) => {
+ const data = {
+ ...i,
+ };
+ delete data.key;
+ return data;
+ });
+ },
+ }));
+
+ return (
+