当前位置:首页|资讯|AI大模型|人工智能|OpenAI

全网首创,dbeaver AI 插件功能实现自定义适配AI大模型

作者:wwwzhouhui发布时间:2024-07-26

1、背景

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模型接口。

2、实现效果

我们改造了 service,增加了第三方接口请求URL,增加了模型手工填写部分,这里我们在DBeaver 24.1.3版本修改效果如下

以上设置完成后接入 AI 聊天界面,当时数据连接状态下,我们输入问题“请将当前数据库表列出来”

      点击“translate”  后请求后端模型。模型返回SQL 语句并立即执行

  AI 请求返回 SQL 语句“SHOW TABLES”  这个我们知道它是显示当前数据库表。以上就完成语句的表现并执行。

  文章后面有网盘链接地址,请看到最后。

3.如何改造

接下来我们说一下改造的思路。首先需要下载最新的DBeaver 源码。项目地址

https://github.com/dbeaver/dbeaver

项目中还用到了dbeaver-common,我们同样需要下载源码。

使用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个插件源码即可。重点介绍修改关键代码

3.1DBeaver AI插件代码

3.1.1 org.jkiss.dbeaver.model.ai代码修改

 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>

3.1.2 org.jkiss.dbeaver.ui.editors.sql.ai代码修改

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个插件代码的修改。

3.2 openai-java 依赖包修改

我们在前面提到第三方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编译打包。

3.3  DBeaver AI 编译打包

上面我们修改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 执行。


dbeaver 项目会重新编译打包。这个时间会有点长,等待他完成编译打包后。

我们在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


4.如何使用

将以上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.exe  点击 "AI smart completion"



在弹出的对话框中,我们点击右边设置按钮


DBeaver 是一个基于 Java 开发的免费且开源的通用数据库管理工具,广泛支持多种关系型数据库系统。我们借助DBeaver AI功能结合我们修改的代码这样让DBeaver AI 适合国内用户,这样对研发的小伙伴在也不用担心不会写SQL 了。直接使用上连接数据库的AI 了。

附百度网盘DBeaver AI(24.1.3) 插件包

链接:https://pan.baidu.com/s/17iLrpx-uAj-pNFsVcKeqXA?pwd=qhit  提取码:qhit



Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1