From 6a1b544545ba2a005a1d6263f3b42aaeeef78bcd Mon Sep 17 00:00:00 2001
From: ageerle <ageerle@163.com>
Date: 星期二, 11 三月 2025 17:32:47 +0800
Subject: [PATCH] feat: 支持插件功能

---
 ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/openai/OpenAiStreamClient.java |  187 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 183 insertions(+), 4 deletions(-)

diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/openai/OpenAiStreamClient.java b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/openai/OpenAiStreamClient.java
index cad1bbb..e799202 100644
--- a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/openai/OpenAiStreamClient.java
+++ b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/openai/OpenAiStreamClient.java
@@ -3,6 +3,7 @@
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.ContentType;
+import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.reactivex.Single;
 import lombok.Getter;
@@ -17,10 +18,7 @@
 import org.ruoyi.common.chat.entity.billing.BillingUsage;
 import org.ruoyi.common.chat.entity.billing.KeyInfo;
 import org.ruoyi.common.chat.entity.billing.Subscription;
-import org.ruoyi.common.chat.entity.chat.BaseChatCompletion;
-import org.ruoyi.common.chat.entity.chat.ChatCompletion;
-import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
-import org.ruoyi.common.chat.entity.chat.ChatCompletionWithPicture;
+import org.ruoyi.common.chat.entity.chat.*;
 import org.ruoyi.common.chat.entity.embeddings.Embedding;
 import org.ruoyi.common.chat.entity.embeddings.EmbeddingResponse;
 import org.ruoyi.common.chat.entity.files.UploadFileResponse;
@@ -29,6 +27,7 @@
 import org.ruoyi.common.chat.entity.models.Model;
 import org.ruoyi.common.chat.entity.models.ModelResponse;
 import org.ruoyi.common.chat.entity.whisper.Transcriptions;
+import org.ruoyi.common.chat.entity.whisper.Translations;
 import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
 import org.ruoyi.common.chat.openai.exception.CommonError;
 import org.ruoyi.common.chat.openai.function.KeyRandomStrategy;
@@ -36,6 +35,10 @@
 import org.ruoyi.common.chat.openai.interceptor.DefaultOpenAiAuthInterceptor;
 import org.ruoyi.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor;
 import org.ruoyi.common.chat.openai.interceptor.OpenAiAuthInterceptor;
+import org.ruoyi.common.chat.openai.plugin.PluginAbstract;
+import org.ruoyi.common.chat.openai.plugin.PluginParam;
+import org.ruoyi.common.chat.sse.DefaultPluginListener;
+import org.ruoyi.common.chat.sse.PluginListener;
 import org.ruoyi.common.core.exception.base.BaseException;
 import org.jetbrains.annotations.NotNull;
 import retrofit2.Call;
@@ -184,6 +187,93 @@
         } catch (Exception e) {
             log.error("璇锋眰鍙傛暟瑙f瀽寮傚父锛歿}", e.getMessage());
         }
+    }
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     * 榛樿妯″瀷锛欳hatCompletion.Model.GPT_3_5_TURBO_16K_0613
+     *
+     * @param chatCompletion            鍙傛暟
+     * @param eventSourceListener       sse鐩戝惉鍣�
+     * @param pluginEventSourceListener 鎻掍欢sse鐩戝惉鍣紝鏀堕泦function call杩斿洖淇℃伅
+     * @param plugin                    鎻掍欢
+     * @param <R>                       鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>                       鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     */
+    public <R extends PluginParam, T> void streamChatCompletionWithPlugin(ChatCompletion chatCompletion, EventSourceListener eventSourceListener, PluginListener pluginEventSourceListener, PluginAbstract<R, T> plugin) {
+        if (Objects.isNull(plugin)) {
+            this.streamChatCompletion(chatCompletion, eventSourceListener);
+            return;
+        }
+        if (CollectionUtil.isEmpty(chatCompletion.getMessages())) {
+            throw new BaseException(CommonError.MESSAGE_NOT_NUL.msg());
+        }
+        Functions functions = Functions.builder()
+                .name(plugin.getFunction())
+                .description(plugin.getDescription())
+                .parameters(plugin.getParameters())
+                .build();
+        //娌℃湁鍊硷紝璁剧疆榛樿鍊�
+        if (Objects.isNull(chatCompletion.getFunctionCall())) {
+            chatCompletion.setFunctionCall("auto");
+        }
+        //tip: 瑕嗙洊鑷繁璁剧疆鐨刦unctions鍙傛暟锛屼娇鐢╬lugin鏋勯�犵殑functions
+        chatCompletion.setFunctions(Collections.singletonList(functions));
+        //璋冪敤OpenAi
+        if (Objects.isNull(pluginEventSourceListener)) {
+            pluginEventSourceListener = new DefaultPluginListener(this, eventSourceListener, plugin, chatCompletion);
+        }
+        this.streamChatCompletion(chatCompletion, pluginEventSourceListener);
+    }
+
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     * 榛樿妯″瀷锛欳hatCompletion.Model.GPT_3_5_TURBO_16K_0613
+     *
+     * @param chatCompletion      鍙傛暟
+     * @param eventSourceListener sse鐩戝惉鍣�
+     * @param plugin              鎻掍欢
+     * @param <R>                 鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>                 鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     */
+    public <R extends PluginParam, T> void streamChatCompletionWithPlugin(ChatCompletion chatCompletion, EventSourceListener eventSourceListener, PluginAbstract<R, T> plugin) {
+        PluginListener pluginEventSourceListener = new DefaultPluginListener(this, eventSourceListener, plugin, chatCompletion);
+        this.streamChatCompletionWithPlugin(chatCompletion, eventSourceListener, pluginEventSourceListener, plugin);
+    }
+
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     * 榛樿妯″瀷锛欳hatCompletion.Model.GPT_3_5_TURBO_16K_0613
+     *
+     * @param messages            闂瓟鍙傛暟
+     * @param eventSourceListener sse鐩戝惉鍣�
+     * @param plugin              鎻掍欢
+     * @param <R>                 鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>                 鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     */
+    public <R extends PluginParam, T> void streamChatCompletionWithPlugin(List<Message> messages, EventSourceListener eventSourceListener, PluginAbstract<R, T> plugin) {
+        this.streamChatCompletionWithPlugin(messages, ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), eventSourceListener, plugin);
+    }
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     *
+     * @param messages            闂瓟鍙傛暟
+     * @param model               妯″瀷
+     * @param eventSourceListener eventSourceListener
+     * @param plugin              鎻掍欢
+     * @param <R>                 鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>                 鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     */
+    public <R extends PluginParam, T> void streamChatCompletionWithPlugin(List<Message> messages, String model, EventSourceListener eventSourceListener, PluginAbstract<R, T> plugin) {
+        ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).model(model).build();
+        this.streamChatCompletionWithPlugin(chatCompletion, eventSourceListener, plugin);
     }
 
 
@@ -418,6 +508,95 @@
         }
     }
 
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     * 榛樿妯″瀷锛欳hatCompletion.Model.GPT_3_5_TURBO_16K_0613
+     *
+     * @param chatCompletion 鍙傛暟
+     * @param plugin         鎻掍欢
+     * @param <R>            鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>            鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     * @return ChatCompletionResponse
+     */
+    public <R extends PluginParam, T> ChatCompletionResponse chatCompletionWithPlugin(ChatCompletion chatCompletion, PluginAbstract<R, T> plugin) {
+        if (Objects.isNull(plugin)) {
+            return this.chatCompletion(chatCompletion);
+        }
+        if (CollectionUtil.isEmpty(chatCompletion.getMessages())) {
+            throw new BaseException(CommonError.MESSAGE_NOT_NUL.msg());
+        }
+        List<Message> messages = chatCompletion.getMessages();
+        Functions functions = Functions.builder()
+                .name(plugin.getFunction())
+                .description(plugin.getDescription())
+                .parameters(plugin.getParameters())
+                .build();
+        //娌℃湁鍊硷紝璁剧疆榛樿鍊�
+        if (Objects.isNull(chatCompletion.getFunctionCall())) {
+            chatCompletion.setFunctionCall("auto");
+        }
+        //tip: 瑕嗙洊鑷繁璁剧疆鐨刦unctions鍙傛暟锛屼娇鐢╬lugin鏋勯�犵殑functions
+        chatCompletion.setFunctions(Collections.singletonList(functions));
+        //璋冪敤OpenAi
+        ChatCompletionResponse functionCallChatCompletionResponse = this.chatCompletion(chatCompletion);
+        ChatChoice chatChoice = functionCallChatCompletionResponse.getChoices().get(0);
+        log.debug("鏋勯�犵殑鏂规硶鍊硷細{}", chatChoice.getMessage().getFunctionCall());
+
+        R realFunctionParam = (R) JSONUtil.toBean(chatChoice.getMessage().getFunctionCall().getArguments(), plugin.getR());
+        T tq = plugin.func(realFunctionParam);
+
+        FunctionCall functionCall = FunctionCall.builder()
+                .arguments(chatChoice.getMessage().getFunctionCall().getArguments())
+                .name(plugin.getFunction())
+                .build();
+        messages.add(Message.builder().role(Message.Role.ASSISTANT).content("function_call").functionCall(functionCall).build());
+        messages.add(Message.builder().role(Message.Role.FUNCTION).name(plugin.getFunction()).content(plugin.content(tq)).build());
+        //璁剧疆绗簩娆★紝璇锋眰鐨勫弬鏁�
+        chatCompletion.setFunctionCall(null);
+        chatCompletion.setFunctions(null);
+
+        ChatCompletionResponse chatCompletionResponse = this.chatCompletion(chatCompletion);
+        log.debug("鑷畾涔夌殑鏂规硶杩斿洖鍊硷細{}", chatCompletionResponse.getChoices());
+        return chatCompletionResponse;
+    }
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     * 榛樿妯″瀷锛欳hatCompletion.Model.GPT_3_5_TURBO_16K_0613
+     *
+     * @param messages 闂瓟鍙傛暟
+     * @param plugin   鎻掍欢
+     * @param <R>      鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>      鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     * @return ChatCompletionResponse
+     */
+    public <R extends PluginParam, T> ChatCompletionResponse chatCompletionWithPlugin(List<Message> messages, PluginAbstract<R, T> plugin) {
+        return chatCompletionWithPlugin(messages, ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), plugin);
+    }
+
+    /**
+     * 鎻掍欢闂瓟绠�鏄撶増
+     * 榛樿鍙杕essages鏈�鍚庝竴涓厓绱犳瀯寤烘彃浠跺璇�
+     *
+     * @param messages 闂瓟鍙傛暟
+     * @param model    妯″瀷
+     * @param plugin   鎻掍欢
+     * @param <R>      鎻掍欢鑷畾涔夊嚱鏁扮殑璇锋眰鍊�
+     * @param <T>      鎻掍欢鑷畾涔夊嚱鏁扮殑杩斿洖鍊�
+     * @return ChatCompletionResponse
+     */
+    public <R extends PluginParam, T> ChatCompletionResponse chatCompletionWithPlugin(List<Message> messages, String model, PluginAbstract<R, T> plugin) {
+        ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).model(model).build();
+        return this.chatCompletionWithPlugin(chatCompletion, plugin);
+    }
+
+
+
+
+
+
 
     /**
      * 鏋勯��

--
Gitblit v1.9.3