| | |
| | | |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.collection.CollectionUtil; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.google.protobuf.ServiceException; |
| | | import com.zhipu.oapi.ClientV4; |
| | | import com.zhipu.oapi.service.v4.tools.*; |
| | | import jakarta.servlet.http.HttpServletRequest; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import okhttp3.*; |
| | | |
| | | import org.ruoyi.chat.config.ChatConfig; |
| | | import org.ruoyi.chat.listener.SSEEventSourceListener; |
| | | import okhttp3.ResponseBody; |
| | | import org.ruoyi.chat.enums.ChatModeType; |
| | | import org.ruoyi.chat.service.chat.IChatCostService; |
| | | import org.ruoyi.chat.service.chat.IChatService; |
| | | import org.ruoyi.chat.service.chat.ISseService; |
| | | import org.ruoyi.chat.util.IpUtil; |
| | | import org.ruoyi.common.chat.config.LocalCache; |
| | | import org.ruoyi.common.chat.request.ChatRequest; |
| | | 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.Message; |
| | | import org.ruoyi.common.chat.entity.files.UploadFileResponse; |
| | | import org.ruoyi.common.chat.entity.whisper.WhisperResponse; |
| | | import org.ruoyi.common.chat.openai.OpenAiStreamClient; |
| | | import org.ruoyi.common.core.service.ConfigService; |
| | | import org.ruoyi.common.chat.request.ChatRequest; |
| | | import org.ruoyi.common.core.utils.DateUtils; |
| | | import org.ruoyi.common.core.utils.StringUtils; |
| | | import org.ruoyi.common.core.utils.file.FileUtils; |
| | | import org.ruoyi.common.core.utils.file.MimeTypeUtils; |
| | | |
| | | import org.ruoyi.common.redis.utils.RedisUtils; |
| | | |
| | | import org.ruoyi.domain.vo.ChatModelVo; |
| | | import org.ruoyi.service.EmbeddingService; |
| | | import org.ruoyi.service.IChatModelService; |
| | |
| | | import java.nio.file.Path; |
| | | import java.time.Duration; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | @Service |
| | | @Slf4j |
| | |
| | | |
| | | private final VectorStoreService vectorStore; |
| | | |
| | | private final ConfigService configService; |
| | | |
| | | private final IChatCostService chatCostService; |
| | | |
| | | private final IChatService chatService; |
| | | |
| | | private final IChatModelService chatModelService; |
| | | |
| | | private static final String requestIdTemplate = "company-%d"; |
| | | private final OpenAIServiceImpl openAIService; |
| | | |
| | | private static final ObjectMapper mapper = new ObjectMapper(); |
| | | private final OllamaServiceImpl ollamaService; |
| | | |
| | | private final ChatConfig chatConfig; |
| | | private ChatModelVo chatModelVo; |
| | | |
| | | |
| | | @Override |
| | | public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) { |
| | |
| | | |
| | | chatRequest.setUserId(chatCostService.getUserId()); |
| | | // ä¿åæ¶æ¯è®°å½ å¹¶æ£é¤è´¹ç¨ |
| | | // chatCostService.deductToken(chatRequest); |
| | | chatCostService.deductToken(chatRequest); |
| | | } |
| | | // æ ¹æ®æ¨¡ååç§°åç¼è°ç¨ä¸åçå¤çé»è¾ |
| | | switchModelAndHandle(chatRequest,sseEmitter); |
| | |
| | | * æ ¹æ®æ¨¡ååç§°åç¼è°ç¨ä¸åçå¤çé»è¾ |
| | | */ |
| | | private void switchModelAndHandle(ChatRequest chatRequest,SseEmitter emitter) { |
| | | SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(emitter); |
| | | String model = chatRequest.getModel(); |
| | | // å¦ææ¨¡åå称以ollamaå¼å¤´ï¼åè°ç¨ollamaä¸é¨ç½²çæ¬å°æ¨¡å |
| | | if (model.startsWith("ollama-")) { |
| | | String[] parts = chatRequest.getModel().split("ollama-", 2); |
| | | if (parts.length > 1) { |
| | | chatRequest.setModel(parts[1]); |
| | | chatService.mcpChat(chatRequest,emitter); |
| | | } else { |
| | | throw new IllegalArgumentException("Invalid ollama model name: " + chatRequest.getModel()); |
| | | } |
| | | // è°ç¨ollamaä¸é¨ç½²çæ¬å°æ¨¡å |
| | | if (ChatModeType.OLLAMA.getCode().equals(chatModelVo.getCategory())) { |
| | | ollamaService.chat(chatRequest,emitter); |
| | | } else { |
| | | |
| | | if (model.startsWith("gpt-4-gizmo")) { |
| | | chatRequest.setModel("gpt-4-gizmo"); |
| | | } |
| | | ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); |
| | | //openAiStreamClient = chatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey()); |
| | | |
| | | ChatCompletion completion = ChatCompletion |
| | | .builder() |
| | | .messages(chatRequest.getMessages()) |
| | | .model(chatRequest.getModel()) |
| | | .temperature(0.2) |
| | | .topP(1.0) |
| | | .stream(true) |
| | | .build(); |
| | | openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener); |
| | | |
| | | openAIService.chat(chatRequest,emitter); |
| | | } |
| | | } |
| | | |
| | |
| | | * æå»ºæ¶æ¯å表 |
| | | */ |
| | | private void buildChatMessageList(ChatRequest chatRequest){ |
| | | ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); |
| | | chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); |
| | | // è·åå¯¹è¯æ¶æ¯å表 |
| | | List<Message> messages = chatRequest.getMessages(); |
| | | String sysPrompt = chatModelVo.getSystemPrompt(); |
| | |
| | | } |
| | | // 设置对è¯ä¿¡æ¯ |
| | | chatRequest.setPrompt(chatString); |
| | | // å è½½èç½ä¿¡æ¯ |
| | | if(chatRequest.getSearch()){ |
| | | Message message = Message.builder().role(Message.Role.ASSISTANT).content("èç½ä¿¡æ¯:"+webSearch(chatString)).build(); |
| | | messages.add(message); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | .build(); |
| | | ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion); |
| | | return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString(); |
| | | } |
| | | |
| | | @Override |
| | | public String webSearch (String prompt) { |
| | | String zpValue = configService.getConfigValue("zhipu", "key"); |
| | | if(StringUtils.isEmpty(zpValue)){ |
| | | throw new IllegalStateException("请å¨chat_configä¸é
ç½®æºè°±keyä¿¡æ¯"); |
| | | }else { |
| | | ClientV4 client = new ClientV4.Builder(zpValue) |
| | | .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(prompt); |
| | | |
| | | 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); |
| | | } |
| | | return choices.get(1).getToolCalls().toString(); |
| | | } |
| | | } |
| | | |
| | | } |