DBeaver 是一个基于 Java 开发的免费且开源的通用数据库管理工具,广泛支持多种关系型数据库系统,包括 MySQL、PostgreSQL、Oracle、SQL Server、SQLite 等。它提供了一个直观的图形用户界面,使用户能够轻松连接到数据库、浏览数据、执行 SQL 查询和进行数据库管理。
随着人工智能的快速发展DBeaver 也开始支持AI插件功能。界面截图如下:
目前社区版本只支持openai,而商业版支持openai、Azure Openai、Google Gemini、ollama界面截图如下
由于国内网络的限制原因openai、Azure Openai、Google Gemini访问是不方便的,这个社区版或商业版 也不支持第三方代理设置,这样就导致整个DBeaver AI 国内就比较鸡肋几乎很少人使用。其实如果这个DBeaver AI 支持第三方代理openai或者支持兼容openai标准接口的第三方模型比如(阿里通义千问、月之暗面、01万物等)这个插件的市场还是非常巨大的。有没有办法解决以上的问题呢?今天就给大家介绍一下思路和方法来解决适配第三方标准Openai模型接口。
我们改造了 service,增加了第三方接口请求URL,增加了模型手工填写部分,这里我们在DBeaver 24.1.3版本修改效果如下
以上设置完成后接入 AI 聊天界面,当时数据连接状态下,我们输入问题“请将当前数据库表列出来”
点击“translate” 后请求后端模型。模型返回SQL 语句并立即执行
AI 请求返回 SQL 语句“SHOW TABLES” 这个我们知道它是显示当前数据库表。以上就完成语句的表现并执行。
文章后面有网盘链接地址,请看到最后。
接下来我们说一下改造的思路。首先需要下载最新的DBeaver 源码。项目地址
https://github.com/dbeaver/dbeaver
项目中还用到了
,我们同样需要下载源码。使用IntelliJ IDEA 工具将项目导入
这里面我们需要了解DBeaver 用到AI 插件有哪些? 插件包的地址是在DBeaver24.1.3\dbeaver-ce-24.1.3-win32.win32.x86_64\dbeaver\configuration\org.eclipse.osgi\116\data-1751688175\plugins目录下
具体如何查看插件包安装路径,可以通过DBeaver 帮助-安装信息-配置 搜索 AI 找到(详细步骤就不展开)
我们确定插件包是org.jkiss.dbeaver.model.ai_1.0.37.202407211303.jar、org.jkiss.dbeaver.ui.editors.sql.ai_1.0.37.202407211303.jar,找到名字后我们接下来对应名字找源码来修改,对应的源码为
org.jkiss.dbeaver.model.ai和org.jkiss.dbeaver.ui.editors.sql.ai
修改以上2个插件源码即可。重点介绍修改关键代码
org.jkiss.dbeaver.model.ai.AIConstants 我们需要定义gpt.url。修改代码如下
public static final String GPT_URL = "gpt.url";
org.jkiss.dbeaver.model.aiAISettings.java我们需要修改tryMigrateFromPrefStore方法,增加url settings
private void tryMigrateFromPrefStore(String engine, AIEngineSettings settings) {
// migrate from pref store
if (AIConstants.OPENAI_ENGINE.equals(engine) && settings.getProperties().get(AIConstants.GPT_MODEL) == null) {
DBPPreferenceStore preferenceStore = DBWorkbench.getPlatform().getPreferenceStore();
String model = preferenceStore.getString(AIConstants.GPT_MODEL);
if (model != null) {
settings.getProperties().put(AIConstants.GPT_MODEL, model);
}
String url = preferenceStore.getString(AIConstants.GPT_URL);
if (url != null) {
settings.getProperties().put(AIConstants.GPT_URL, model);
}
Double temperature = preferenceStore.getDouble(AIConstants.AI_TEMPERATURE);
settings.getProperties().put(AIConstants.AI_TEMPERATURE, temperature);
Boolean logQuery = preferenceStore.getBoolean(AIConstants.AI_LOG_QUERY);
settings.getProperties().put(AIConstants.AI_LOG_QUERY, logQuery);
}
}
剩下就集中在openai 文件夹下类和接口了
org.jkiss.dbeaver.model.ai.openai.GPTModel 枚举类我们需要增加新增自定义的模型名称,例如:zhipuai/glm4-9B-chat、alibaba/Qwen2-7B-Instruc、internvl2-8b 等。新增代码如下
GLM4_9B_CHAT("zhipuai/glm4-9B-chat", 32768, true),
QWEN2_7B_INSTRUCT("alibaba/Qwen2-7B-Instruc", 32768, true),
YI_15_6B_CHAT("01-ai/Yi-1.5-6B-Chat", 4096, true),
YI_15_9B_CHAT("01-ai/Yi-1.5-9B-Chat", 4096, true),
INTERNVL2_8B("internvl2-8b", 8196, true),
QWEN2_7B_INSTRUCT2("/tmp/pretrainmodel/Qwen2-7B-Instruct", 32768, true),
GEMINI_15_FLASH("gemini-1.5-flash-latest", 32768, true),
这个根据需要增加。
org.jkiss.dbeaver.model.ai.openai.service.AdaptedOpenAiService 抽象类我们需要新增AdaptedOpenAiService 重写方法,主要是增加自定义URL 请求参数
修改的代码如下
package org.jkiss.dbeaver.model.ai.openai.service;
import com.theokanning.openai.service.OpenAiService;
import java.time.Duration;
public class AdaptedOpenAiService extends OpenAiService implements GPTCompletionAdapter {
public AdaptedOpenAiService(String token) {
super(token);
}
public AdaptedOpenAiService(String token, Duration timeout) {
super(token, timeout);
}
public AdaptedOpenAiService(String token, String url,Duration timeout) {
super(token, url,timeout);
}
}
这个地方需要注意的是这里用到了第三方OpenAiService方法,这个第三方JAR 源码我们也一并需要修改。
这个这个第三方JAR 源码地址https://github.com/TheoKanning/openai-java,我们需要修改com.theokanning.openai.service.OpenAiService里面代码支持上面继承AdaptedOpenAiService。这个地方后面章节详细展开。
org.jkiss.dbeaver.model.ai.openai.OpenAICompletionEngine 这个类改的地方会稍微多点。
新增的代码
protected String acquireUrl() {
AIEngineSettings config = getSettings();
Object url = config.getProperties().get(AIConstants.GPT_URL);
if (url != null) {
return url.toString();
}
return DBWorkbench.getPlatform().getPreferenceStore().getString(AIConstants.GPT_URL);
}
修改的代码
protected GPTCompletionAdapter initGPTApiClientInstance() throws DBException {
String token = acquireToken();
if (CommonUtils.isEmpty(token)) {
throw new DBException("Empty API token value");
}
String url = acquireUrl();
if (CommonUtils.isEmpty(url)) {
throw new DBException("Empty URL value");
}
return new AdaptedOpenAiService(token, url,Duration.ofSeconds(30));
}
提示词换成中文
protected String getInstructions(boolean chatCompletion) {
if (GPTModel.QWEN2_7B_INSTRUCT2.equals(getModel())) {
return """
你是 SQL 助手。你必须针对给定的提示生成 SQL 代码。你必须生成有效的 SQL 语句,该语句包含在 Markdown 代码块中并以分号结尾。所有注释必须放在 Markdown 代码块之外的查询之前。请保持礼貌。
""";
}
return super.getInstructions(chatCompletion);
}
另外在项目的plugin.xml 我们需要增加gpt.url 修改的代码如下
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension-point id="com.dbeaver.ai.engine" name="AI integrations" schema="schema/com.dbeaver.ai.engine.exsd"/>
<extension-point id="com.dbeaver.ai.formatter" name="AI formatters" schema="schema/com.dbeaver.ai.formatter.exsd"/>
<extension point="com.dbeaver.ai.engine">
<completionEngine id="openai" label="OpenAI" class="org.jkiss.dbeaver.model.ai.openai.OpenAICompletionEngine" default="true">
<propertyGroup label="Engine settings">
<property id="gpt.url" label="URL" type="string" defaultValue="http://127.0.0.1:8080" hint="自定义openai url请求地址"/>
<property id="gpt.token" label="API token" type="string" hint="Copy-paste API token from https://platform.openai.com/account/api-keys" features="password"/>
<property id="gpt.model" label="Model" type="string" defaultValue="gpt-3.5-turbo-16k" hint="gpt-3.5-turbo-16k model suits the best for SQL code completion"/>
<property id="gpt.model.temperature" label="Temperature" type="integer" hint="Lower temperatures give more precise results"/>
<property id="gpt.log.query" label="Write AI queries to debug log" type="boolean" hint="Write GPT queries with metadata info in debug logs"/>
</propertyGroup>
</completionEngine>
</extension>
<extension point="com.dbeaver.ai.formatter">
<formatter id="core" class="org.jkiss.dbeaver.model.ai.format.DefaultRequestFormatter"/>
</extension>
</plugin>
org.jkiss.dbeaver.ui.editors.sql.ai.internal.AIUIMessages
新增
public static String gpt_preference_page_selector_url;
对应的国际化配置文件
AIUIMessages.properties
gpt_preference_page_selector_url=URL
org.jkiss.dbeaver.ui.editors.sql.ai.openai.OpenAiConfigurator 修改的地方也比较多,下面重点介绍一下。
新增加代码
private String url = "";
protected Text urlText;
protected Text modelText;
protected void createURLParameters(@NotNull Composite parent) {
urlText = UIUtils.createLabelText(
parent,
AIUIMessages.gpt_preference_page_selector_url,
"",
2
);
urlText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
urlText.addModifyListener((e -> url = urlText.getText()));
urlText.setMessage("URL address information such as http://127.0.0.1:8080 ");
}
修改代码1createControl 新增createURLParameters(composite);
public void createControl(
@NotNull Composite parent,
DAICompletionEngine<?> object,
@NotNull Runnable propertyChangeListener
) {
Composite composite = UIUtils.createComposite(parent, 2);
composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createConnectionParameters(composite);
createURLParameters(composite);
createModelParameters(composite);
createAdditionalSettings(composite);
UIUtils.syncExec(this::applySettings);
}
修改代码2createModelParameters 之前官方提供的模型是从下拉列表中获取的,这里我们改成手工填写,修改后代码如下
protected void createModelParameters(@NotNull Composite parent) {
if (isUsesModel()) {
// modelCombo = UIUtils.createLabelCombo(parent, AIUIMessages.gpt_preference_page_combo_engine, SWT.SIMPLE);
// for (GPTModel model : getSupportedGPTModels()) {
// if (model.getDeprecationReplacementModel() == null) {
// modelCombo.add(model.getName());
// }
// }
// modelCombo.addSelectionListener(new SelectionAdapter() {
// @Override
// public void widgetSelected(SelectionEvent e) {
// model = modelCombo.getText();
// }
// });
modelText= UIUtils.createLabelText(
parent,
AIUIMessages.gpt_preference_page_combo_engine,
"",
2
);
modelText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
modelText.addModifyListener((e -> model = modelText.getText()));
modelText.setMessage("model information such as internvl2-8b");
UIUtils.createInfoLabel(parent, NLS.bind(AIUIMessages.gpt_preference_page_info_model, getDefaultModel()), GridData.FILL_HORIZONTAL, 2);
}
temperatureText = UIUtils.createLabelText(parent, AIUIMessages.gpt_preference_page_text_temperature, "0.0");
temperatureText.addVerifyListener(UIUtils.getNumberVerifyListener(Locale.getDefault()));
UIUtils.createInfoLabel(parent, "Lower temperatures give more precise results", GridData.FILL_HORIZONTAL, 2);
temperatureText.addVerifyListener(UIUtils.getNumberVerifyListener(Locale.getDefault()));
temperatureText.addModifyListener((e) -> temperature = temperatureText.getText());
}
修改3 loadSettings 、applySettings、saveSettings url 设置
public void loadSettings(@NotNull AIEngineSettings aiSettings) {
token = CommonUtils.toString(aiSettings.getProperties().get(AIConstants.GPT_API_TOKEN), "");
if (isUsesModel()) {
model = CommonUtils.toString(aiSettings.getProperties().get(AIConstants.GPT_MODEL),
getDefaultModel()
);
}
temperature = CommonUtils.toString(aiSettings.getProperties().get(
AIConstants.AI_TEMPERATURE),
"0.0"
);
logQuery = CommonUtils.toBoolean(aiSettings.getProperties().get(AIConstants.AI_LOG_QUERY)) ;
url=CommonUtils.toString(aiSettings.getProperties().get(AIConstants.GPT_URL), "");
applySettings();
}
protected void applySettings() {
if (tokenText != null) {
tokenText.setText(token);
}
if (isUsesModel()) {
// modelCombo.setText(model);
modelText.setText(model);
}
temperatureText.setText(temperature);
logQueryCheck.setSelection(logQuery);
if (urlText != null) {
urlText.setText(url);
}
}
public void saveSettings(@NotNull AIEngineSettings aiSettings) {
aiSettings.getProperties().put(AIConstants.GPT_API_TOKEN, token);
if (isUsesModel()) {
aiSettings.getProperties().put(AIConstants.GPT_MODEL, model);
}
aiSettings.getProperties().put(AIConstants.AI_TEMPERATURE, temperature);
aiSettings.getProperties().put(AIConstants.AI_LOG_QUERY, logQuery);
aiSettings.getProperties().put(AIConstants.GPT_URL, url);
}
以上完成DBeaver AI 2个插件代码的修改。
我们在前面提到第三方api service-0.18.2.jar 也是需要修改的。我们下载https://github.com/TheoKanning/openai-java 源码导入IntelliJ IDEA 工具
这里我们只需要用到com.theokanning.openai.service.OpenAiService 类,所以我们只需要修改这个OpenAiService 即可。
新增OpenAiService 重写方法
public OpenAiService(final String token, final String url,final Duration timeout) {
ObjectMapper mapper = defaultObjectMapper();
OkHttpClient client = defaultClient(token, timeout);
Retrofit retrofit = defaultRetrofit(client, url,mapper);
System.out.println("OpenAiService" + url);
this.api = retrofit.create(OpenAiApi.class);
this.executorService = client.dispatcher().executorService();
}
新增defaultRetrofit重写方法
public static Retrofit defaultRetrofit(OkHttpClient client, String url,ObjectMapper mapper) {
return new Retrofit.Builder()
.baseUrl(url)
.client(client)
.addConverterFactory(JacksonConverterFactory.create(mapper))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
以上代码修改完成后编译打包生成service-0.18.2.jar,这个编译打包比较简单就不详细展开了。该项目使用gradle ,所以使用gradle编译打包。
上面我们修改DBeaver AI 2个插件。由于DBeaver AI编译打包依赖上面的service-0.18.2.jar所以下面我们重点介绍这个编译打包方式。
dbeaver 生产环境打包依赖的第三方包pom.xml 目录在product\localRepository\pom.xml 这个里面提到上面依赖包service-0.18.2.jar 调用的名称是eclipse-bundle-gpt3
<artifact><id>org.jkiss.bundle:org.jkiss.bundle.gpt3:LATEST</id><transitive>false</transitive><source>false</source><override>false</override></artifact> <!-- repo:eclipse-bundle-gpt3 -->
他其实依赖的是org.jkiss.bundle.gpt3-0.18.3.jar 包 不是直接依赖service-0.18.2.jar 而org.jkiss.bundle.gpt3-0.18.3.jar 包含service-0.18.2.jar 并不是直接依赖service-0.18.2.jar 包。
org.jkiss.bundle.gpt3-0.18.3.jar在本地maven仓库的p2\osgi\bundle\org.jkiss.bundle.gpt3\0.18.3 目录下。
我的maven本地仓库依赖包目录是在D:\develop\maven 目录下
我们打开org.jkiss.bundle.gpt3-0.18.3.jar,里面包含一些依赖包。我们需要替换org.jkiss.bundle.gpt3-0.18.3.jar包里面的service-0.18.2.jar,替换如下图
以上操作才能保证上面修改的DBeaver AI2个插件重新编译打包才不会报错。
以上修改完成后,我们可以利用dbeaver 项目中build.cmd 工具重新完整的项目编译打包。
双击build.cmd 执行。
我们在plugins\org.jkiss.dbeaver.ui.editors.sql.ai\target 和plugins\org.jkiss.dbeaver.model.ai\target 目录下找到2个改造后的JAR
org.jkiss.dbeaver.model.ai-1.0.38-SNAPSHOT.jar 和org.jkiss.dbeaver.ui.editors.sql.ai-1.0.38-SNAPSHOT.jar
将以上3个JAR org.jkiss.dbeaver.model.ai-1.0.38-SNAPSHOT.jar 和 org.jkiss.dbeaver.ui.editors.sql.ai-1.0.38-SNAPSHOT.jar 以及
service-0.18.2.jar 替换DBeaver AI 插件
以我们电脑为例DBeaver AI插件的目录在D:\Program Files\DBeaver24.1.3\dbeaver-ce-24.1.3-win32.win32.x86_64\dbeaver\configuration\org.eclipse.osgi\116\data-1751688175\plugins
service-0.18.2.jar 需要替换目录D:\Program Files\DBeaver24.1.3\dbeaver-ce-24.1.3-win32.win32.x86_64\dbeaver\configuration\org.eclipse.osgi\116\data-1751688175\plugins\org.jkiss.bundle.gpt3_0.18.3\lib目录下的service-0.18.2.jar
org.jkiss.dbeaver.model.ai-1.0.38-SNAPSHOT.jar 和 org.jkiss.dbeaver.ui.editors.sql.ai-1.0.38-SNAPSHOT.jar 这2个插件名称和我们原插件名称org.jkiss.dbeaver.model.ai_1.0.37.202407211303.jar 和org.jkiss.dbeaver.ui.editors.sql.ai_1.0.37.202407211303.jar 名称保持一致,重新修改名称即可。另外jar 里面的META-INF文件夹下MANIFEST.MF 也替换原来老的即可。
以上步骤完成后,重启dbeaver.exe 后生效。
在弹出的对话框中,我们点击右边设置按钮
DBeaver 是一个基于 Java 开发的免费且开源的通用数据库管理工具,广泛支持多种关系型数据库系统。我们借助DBeaver AI功能结合我们修改的代码这样让DBeaver AI 适合国内用户,这样对研发的小伙伴在也不用担心不会写SQL 了。直接使用上连接数据库的AI 了。
附百度网盘DBeaver AI(24.1.3) 插件包
链接:https://pan.baidu.com/s/17iLrpx-uAj-pNFsVcKeqXA?pwd=qhit