办学质量监测教学评价系统
ageer
2025-03-12 d8fda1559351c61678738285041da6811b463e53
feat: 增加联网查询功能
已重命名1个文件
已修改6个文件
已删除3个文件
1186 ■■■■ 文件已修改
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/WebSearchToolsTest.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/zhipu/AllToolsTest.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/zhipu/V4Test.java 646 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-fusion/src/main/java/org/ruoyi/fusion/controller/ChatController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-knowledge/src/main/java/org/ruoyi/knowledge/chain/split/CharacterTextSplitter.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-knowledge/src/main/java/org/ruoyi/knowledge/chain/vectorstore/VectorStoreFactory.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/plugin/WebSearchPlugin.java 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/ISysModelService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/impl/SseServiceImpl.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/impl/SysModelServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/WebSearchToolsTest.java
ÎļþÃû´Ó ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/zhipu/WebSearchToolsTest.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package org.ruoyi.common.chat.demo.zhipu;
package org.ruoyi.common.chat.demo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -17,34 +17,11 @@
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.zhipu.oapi.core.response.HttpxBinaryResponseContent;
import com.zhipu.oapi.service.v4.batchs.BatchCreateParams;
import com.zhipu.oapi.service.v4.batchs.BatchResponse;
import com.zhipu.oapi.service.v4.batchs.QueryBatchResponse;
import com.zhipu.oapi.service.v4.embedding.EmbeddingApiResponse;
import com.zhipu.oapi.service.v4.embedding.EmbeddingRequest;
import com.zhipu.oapi.service.v4.file.*;
import com.zhipu.oapi.service.v4.fine_turning.*;
import com.zhipu.oapi.service.v4.image.CreateImageRequest;
import com.zhipu.oapi.service.v4.image.ImageApiResponse;
import com.zhipu.oapi.service.v4.model.*;
import io.reactivex.Flowable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class WebSearchToolsTest {
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/zhipu/AllToolsTest.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/demo/zhipu/V4Test.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-fusion/src/main/java/org/ruoyi/fusion/controller/ChatController.java
@@ -17,7 +17,6 @@
import org.ruoyi.common.mybatis.core.page.PageQuery;
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.knowledge.service.EmbeddingService;
import org.ruoyi.system.domain.bo.ChatMessageBo;
import org.ruoyi.system.domain.request.translation.TranslationRequest;
import org.ruoyi.system.domain.vo.ChatMessageVo;
@@ -48,7 +47,6 @@
    private final IChatMessageService chatMessageService;
    private final EmbeddingService embeddingService;
    /**
     * èŠå¤©æŽ¥å£
     */
ruoyi-modules/ruoyi-knowledge/src/main/java/org/ruoyi/knowledge/chain/split/CharacterTextSplitter.java
@@ -2,6 +2,7 @@
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.knowledge.domain.vo.KnowledgeInfoVo;
import org.ruoyi.knowledge.service.IKnowledgeInfoService;
import org.springframework.context.annotation.Lazy;
@@ -29,7 +30,7 @@
        int textBlockSize = knowledgeInfoVo.getTextBlockSize();
        int overlapChar = knowledgeInfoVo.getOverlapChar();
        List<String> chunkList = new ArrayList<>();
        if (content.contains(knowledgeSeparator)) {
        if (content.contains(knowledgeSeparator) && StringUtils.isNotBlank(knowledgeSeparator)) {
            // æŒ‰è‡ªå®šä¹‰åˆ†éš”符切分
            String[] chunks = content.split(knowledgeSeparator);
            chunkList.addAll(Arrays.asList(chunks));
ruoyi-modules/ruoyi-knowledge/src/main/java/org/ruoyi/knowledge/chain/vectorstore/VectorStoreFactory.java
@@ -1,5 +1,6 @@
package org.ruoyi.knowledge.chain.vectorstore;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.knowledge.domain.vo.KnowledgeInfoVo;
@@ -23,8 +24,13 @@
    }
    public VectorStore getVectorStore(String kid){
        KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoMapper.selectVoById(Long.valueOf(kid));
        String vectorModel = knowledgeInfoVo.getVector();
        String vectorModel = "weaviate";
        if (StrUtil.isNotEmpty(kid)) {
            KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoMapper.selectVoById(Long.valueOf(kid));
            if (knowledgeInfoVo != null && StrUtil.isNotEmpty(knowledgeInfoVo.getVector())) {
                vectorModel = knowledgeInfoVo.getVector();
            }
        }
        if ("weaviate".equals(vectorModel)){
            return weaviateVectorStore;
        }else if ("milvus".equals(vectorModel)){
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/plugin/WebSearchPlugin.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/ISysModelService.java
@@ -2,6 +2,7 @@
import org.ruoyi.common.mybatis.core.page.PageQuery;
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.system.domain.SysModel;
import org.ruoyi.system.domain.bo.SysModelBo;
import org.ruoyi.system.domain.vo.SysModelVo;
@@ -45,4 +46,9 @@
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç³»ç»Ÿæ¨¡åž‹ä¿¡æ¯
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * æ ¹æ®æ¨¡åž‹åç§°æŸ¥è¯¢æ¨¡åž‹
     */
    SysModel selectModelByName(String modelName);
}
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/impl/SseServiceImpl.java
@@ -1,8 +1,10 @@
package org.ruoyi.system.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhipu.oapi.ClientV4;
import com.zhipu.oapi.service.v4.tools.*;
import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.models.chat.OllamaChatMessageRole;
import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
@@ -17,10 +19,7 @@
import org.ruoyi.common.chat.domain.request.ChatRequest;
import org.ruoyi.common.chat.domain.request.Dall3Request;
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
import org.ruoyi.common.chat.entity.chat.Content;
import org.ruoyi.common.chat.entity.chat.Message;
import org.ruoyi.common.chat.entity.chat.*;
import org.ruoyi.common.chat.entity.files.UploadFileResponse;
import org.ruoyi.common.chat.entity.images.Image;
import org.ruoyi.common.chat.entity.images.ImageResponse;
@@ -33,17 +32,15 @@
import org.ruoyi.common.chat.plugin.CmdReq;
import org.ruoyi.common.chat.plugin.SqlPlugin;
import org.ruoyi.common.chat.plugin.SqlReq;
import org.ruoyi.common.chat.sse.ConsoleEventSourceListener;
import org.ruoyi.common.chat.utils.TikTokensUtil;
import org.ruoyi.common.core.domain.model.LoginUser;
import org.ruoyi.common.core.exception.base.BaseException;
import org.ruoyi.common.core.service.ConfigService;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.system.domain.SysModel;
import org.ruoyi.system.domain.bo.ChatMessageBo;
import org.ruoyi.system.domain.bo.SysModelBo;
import org.ruoyi.system.domain.request.translation.TranslationRequest;
import org.ruoyi.system.domain.vo.SysModelVo;
import org.ruoyi.system.listener.SSEEventSourceListener;
import org.ruoyi.system.service.*;
import org.springframework.core.io.InputStreamResource;
@@ -65,6 +62,9 @@
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@Service
@@ -76,17 +76,20 @@
    private final ChatConfig chatConfig;
    private final IChatCostService chatService;
    private final IChatMessageService chatMessageService;
    private final ISysModelService sysModelService;
    private final ISysUserService userService;
    private final ConfigService configService;
    static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build();
    private static final String requestIdTemplate = "mycompany-%d";
    private static final ObjectMapper mapper = new ObjectMapper();
    @Override
    public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
@@ -96,11 +99,10 @@
        // èŽ·å–å¯¹è¯æ¶ˆæ¯åˆ—è¡¨
        List<Message> messages = chatRequest.getMessages();
        try {
            String chatString = null;
            if (StpUtil.isLogin()) {
                LocalCache.CACHE.put("userId", getUserId());
                Object content = messages.get(messages.size() - 1).getContent();
                String chatString = "";
                if (content instanceof List<?> listContent) {
                    if (!listContent.isEmpty() && listContent.get(0) instanceof Content) {
                        chatString = ((Content) listContent.get(0)).getText();
@@ -123,39 +125,89 @@
                        throw new BaseException("文本不合规,请修改!");
                    }
                }
                //根据模型名称查询模型信息
                SysModelBo sysModelBo = new SysModelBo();
                String model = chatRequest.getModel();
                // å¦‚果是gpts系列模型
                if (chatRequest.getModel().startsWith("gpt-4-gizmo")) {
                    sysModelBo.setModelName("gpt-4-gizmo");
                } else {
                    sysModelBo.setModelName(chatRequest.getModel());
                    model = "gpt-4-gizmo";
                }
                List<SysModelVo> sysModelList = sysModelService.queryList(sysModelBo);
                if (CollectionUtil.isEmpty(sysModelList)) {
                SysModel sysModel = sysModelService.selectModelByName(model);
                if (sysModel != null) {
                    // å¦‚果模型不存在默认使用token扣费方式
                    processByToken(chatRequest.getModel(), chatString, chatMessageBo);
                } else {
                    openAiStreamClient = chatConfig.createOpenAiStreamClient(sysModelList.get(0).getApiHost(), sysModelList.get(0).getApiKey());
                    openAiStreamClient = chatConfig.createOpenAiStreamClient(sysModel.getApiHost(), sysModel.getApiKey());
                    // æ¨¡åž‹è®¾ç½®é»˜è®¤æç¤ºè¯
                    SysModelVo firstModel = sysModelList.get(0);
                    if (StringUtils.isNotEmpty(firstModel.getSystemPrompt())) {
                        Message sysMessage = Message.builder().content(firstModel.getSystemPrompt()).role(Message.Role.SYSTEM).build();
                    if (StringUtils.isNotEmpty(sysModel.getSystemPrompt())) {
                        Message sysMessage = Message.builder().content(sysModel.getSystemPrompt()).role(Message.Role.SYSTEM).build();
                        messages.add(sysMessage);
                    }
                    // è®¡è´¹ç±»åž‹: 1 token扣费 2 æ¬¡æ•°æ‰£è´¹
                    if ("2".equals(firstModel.getModelType())) {
                        processByModelPrice(firstModel, chatMessageBo);
                    if ("2".equals(sysModel.getModelType())) {
                        processByModelPrice(sysModel, chatMessageBo);
                    } else {
                       processByToken(chatRequest.getModel(), chatString, chatMessageBo);
                        processByToken(chatRequest.getModel(), chatString, chatMessageBo);
                    }
                }
            }
            if("openCmd".equals(chatRequest.getModel())) {
            String configValue = configService.getConfigValue("zhipu", "key");
            // æ·»åŠ è”ç½‘ä¿¡æ¯
            if(StringUtils.isNotEmpty(configValue)){
                ClientV4 client = new ClientV4.Builder(configValue)
                        .networkConfig(300, 100, 100, 100, TimeUnit.SECONDS)
                        .connectionPool(new okhttp3.ConnectionPool(8, 1, TimeUnit.SECONDS))
                        .build();
                SearchChatMessage jsonNodes = new SearchChatMessage();
                jsonNodes.setRole(Message.Role.USER.getName());
                jsonNodes.setContent(chatString);
                String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
                WebSearchParamsRequest chatCompletionRequest = WebSearchParamsRequest.builder()
                        .model("web-search-pro")
                        .stream(Boolean.TRUE)
                        .messages(Collections.singletonList(jsonNodes))
                        .requestId(requestId)
                        .build();
                WebSearchApiResponse webSearchApiResponse = client.webSearchProStreamingInvoke(chatCompletionRequest);
                List<ChoiceDelta> choices = new ArrayList<>();
                if (webSearchApiResponse.isSuccess()) {
                    AtomicBoolean isFirst = new AtomicBoolean(true);
                    AtomicReference<WebSearchPro> lastAccumulator = new AtomicReference<>();
                    webSearchApiResponse.getFlowable().map(result -> result)
                            .doOnNext(accumulator -> {
                                {
                                    if (isFirst.getAndSet(false)) {
                                        log.info("Response: ");
                                    }
                                    ChoiceDelta delta = accumulator.getChoices().get(0).getDelta();
                                    if (delta != null && delta.getToolCalls() != null) {
                                        log.info("tool_calls: {}", mapper.writeValueAsString(delta.getToolCalls()));
                                    }
                                    choices.add(delta);
                                }
                            })
                            .doOnComplete(() -> System.out.println("Stream completed."))
                            .doOnError(throwable -> System.err.println("Error: " + throwable))
                            .blockingSubscribe();
                    WebSearchPro chatMessageAccumulator = lastAccumulator.get();
                    webSearchApiResponse.setFlowable(null);// æ‰“印前置空
                    webSearchApiResponse.setData(chatMessageAccumulator);
                }
                Message message = Message.builder().role(Message.Role.ASSISTANT).content(choices.get(1).getToolCalls().toString()).build();
                messages.add(message);
            }
            if ("openCmd".equals(chatRequest.getModel())) {
                sseEmitter.send(cmdPlugin(messages));
                sseEmitter.complete();
            }else if ("sqlPlugin".equals(chatRequest.getModel())){
            } else if ("sqlPlugin".equals(chatRequest.getModel())) {
                sseEmitter.send(sqlPlugin(messages));
                sseEmitter.complete();
            } else {
@@ -229,7 +281,7 @@
     * @param model         æ¨¡åž‹ä¿¡æ¯
     * @param chatMessageBo å¯¹è¯ä¿¡æ¯
     */
    private void processByModelPrice(SysModelVo model, ChatMessageBo chatMessageBo) {
    private void processByModelPrice(SysModel model, ChatMessageBo chatMessageBo) {
        double cost = model.getModelPrice();
        chatService.deductUserBalance(getUserId(), cost);
        chatMessageBo.setDeductCost(cost);
@@ -316,16 +368,14 @@
            .style(request.getStyle())
            .build();
        ImageResponse imageResponse = openAiStreamClient.genImages(image);
        SysModelBo sysModelBo = new SysModelBo();
        sysModelBo.setModelName(request.getModel());
        List<SysModelVo> sysModelList = sysModelService.queryList(sysModelBo);
        SysModel sysModel = sysModelService.selectModelByName(request.getModel());
        //chatService.deductUserBalance(getUserId(),sysModelList.get(0).getModelPrice());
        // ä¿å­˜æ¶ˆæ¯è®°å½•
        ChatMessageBo chatMessageBo = new ChatMessageBo();
        chatMessageBo.setUserId(getUserId());
        chatMessageBo.setModelName(Image.Model.DALL_E_3.getName());
        chatMessageBo.setContent(request.getPrompt());
        chatMessageBo.setDeductCost(sysModelList.get(0).getModelPrice());
        chatMessageBo.setDeductCost(sysModel.getModelPrice());
        chatMessageBo.setTotalTokens(0);
        chatMessageService.insertByBo(chatMessageBo);
        return imageResponse.getData();
@@ -342,16 +392,14 @@
            .n(1)
            .build();
        ImageResponse imageResponse = openAiStreamClient.genImages(image);
        SysModelBo sysModelBo = new SysModelBo();
        sysModelBo.setModelName("dall3");
        List<SysModelVo> sysModelList = sysModelService.queryList(sysModelBo);
        SysModel dall3 = sysModelService.selectModelByName("dall3");
        chatService.deductUserBalance(Long.valueOf(userId), 0.3);
        // ä¿å­˜æ¶ˆæ¯è®°å½•
        ChatMessageBo chatMessageBo = new ChatMessageBo();
        chatMessageBo.setUserId(getUserId());
        chatMessageBo.setModelName(Image.Model.DALL_E_3.getName());
        chatMessageBo.setContent(prompt);
        chatMessageBo.setDeductCost(sysModelList.get(0).getModelPrice());
        chatMessageBo.setDeductCost(dall3.getModelPrice());
        chatMessageBo.setTotalTokens(0);
        chatMessageService.insertByBo(chatMessageBo);
        return imageResponse.getData();
@@ -527,12 +575,9 @@
        chatMessageBo.setDeductCost(0.01);
        chatMessageBo.setTotalTokens(0);
        chatMessageService.insertByBo(chatMessageBo);
        openAiStreamClient = chatConfig.getOpenAiStreamClient();
        List<Message> messageList = new ArrayList<>();
        Message sysMessage = Message.builder().role(Message.Role.SYSTEM).content("你是一名翻译老师\n" +
        Message sysMessage = Message.builder().role(Message.Role.SYSTEM).content("你是一位精通各国语言的翻译大师\n" +
            "\n" +
            "请将用户输入词语翻译成{" + translationRequest.getTargetLanguage() + "}\n" +
            "\n" +
@@ -563,25 +608,21 @@
    @Override
    public SseEmitter ollamaChat(ChatRequest chatRequest) {
        String[] parts = chatRequest.getModel().split("ollama-");
        SysModel sysModel = sysModelService.selectModelByName(parts[1]);
        final SseEmitter emitter = new SseEmitter();
        String host = "http://localhost:11434/";
        String host = sysModel.getApiHost();
        List<Message> msgList = chatRequest.getMessages();
        Message message = msgList.get(msgList.size() - 1);
        OllamaAPI ollamaAPI = new OllamaAPI(host);
        ollamaAPI.setRequestTimeoutSeconds(100);
        OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance("qwen2.5:7b");
        OllamaAPI api = new OllamaAPI(host);
        api.setRequestTimeoutSeconds(100);
        OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(sysModel.getModelName());
        OllamaChatRequestModel requestModel = builder
            .withMessage(OllamaChatMessageRole.USER,
                message.getContent().toString())
            .build();
        // å¼‚步执行 Ollama API è°ƒç”¨
        // å¼‚步执行 OllAma API è°ƒç”¨
        CompletableFuture.runAsync(() -> {
            try {
                StringBuilder response = new StringBuilder();
@@ -595,14 +636,12 @@
                        sendErrorEvent(emitter, e.getMessage());
                    }
                };
                ollamaAPI.chat(requestModel, streamHandler);
                api.chat(requestModel, streamHandler);
                emitter.complete();
            } catch (Exception e) {
                sendErrorEvent(emitter, e.getMessage());
            }
        });
        return emitter;
    }
@@ -620,6 +659,4 @@
        ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion);
        return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/impl/SysModelServiceImpl.java
@@ -107,4 +107,11 @@
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
    @Override
    public SysModel selectModelByName(String modelName) {
        return baseMapper.selectOne(
                new LambdaQueryWrapper<SysModel>().eq(SysModel::getModelName, modelName)
        );
    }
}