admin
2025-06-07 3732bde983f34f5cb729ac1724b9795fb97d8941
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java
@@ -2,8 +2,6 @@
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.agentsflex.core.llm.ChatContext;
import com.agentsflex.core.llm.Llm;
import com.agentsflex.core.llm.StreamResponseListener;
@@ -17,32 +15,32 @@
import com.agentsflex.core.prompt.ToolPrompt;
import com.agentsflex.core.util.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jfinal.template.stat.ast.Break;
import com.mybatisflex.core.paginate.Page;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import io.milvus.param.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.aiflowy.ai.config.DifyStreamClient;
import tech.aiflowy.ai.config.FileReference;
import tech.aiflowy.ai.entity.*;
import tech.aiflowy.ai.mapper.AiBotConversationMessageMapper;
import tech.aiflowy.ai.service.*;
import tech.aiflowy.ai.vo.ModelConfig;
import tech.aiflowy.common.ai.ChatManager;
import tech.aiflowy.common.ai.MySseEmitter;
import tech.aiflowy.common.domain.Result;
@@ -57,14 +55,8 @@
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigInteger;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
 * 控制层。
@@ -149,91 +141,139 @@
    @Autowired
    private ObjectMapper objectMapper;
    @PostMapping("chat")
    public SseEmitter chat(@JsonBody(value = "prompt", required = true) String prompt,
                           @JsonBody(value = "botId", required = true) BigInteger botId,
                           @JsonBody(value = "sessionId", required = true) String sessionId,
                           @JsonBody(value = "isExternalMsg") int isExternalMsg,
                           @JsonBody(value = "file") String file,
                           HttpServletResponse response) {
        response.setContentType("text/event-stream");
        AiBot aiBot = service.getById(botId);
        if (aiBot == null) {
            return ChatManager.getInstance().sseEmitterForContent("机器人不存在");
        }
        Map<String, Object> llmOptions = aiBot.getLlmOptions();
        String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null;
        if (StringUtil.hasText(aiBot.getModelAPI())){
            // 使用Dify模型的逻辑
            String apiUrl = aiBot.getModelAPI();
            String bearerToken = aiBot.getModelKey();
            if (aiBot.getBotTypeId() == 2) {
            // 构建请求JSON
            JSONObject jsonBody = new JSONObject();
            jsonBody.put("inputs", new JSONObject());
            jsonBody.put("query", prompt);
            jsonBody.put("response_mode", "blocking");
            jsonBody.put("conversation_id", "");
            jsonBody.put("user", userId.toString());
                String apiUrl = aiBot.getModelAPI()+"/workflows/run"; // 替换为实际API URL
                String apiKey = aiBot.getModelKEY(); // 替换为实际API Key
            JSONArray files = new JSONArray();
            JSONObject fileObj = new JSONObject();
            fileObj.put("type", "");
            fileObj.put("transfer_method", "");
            fileObj.put("url", "");
            files.put(fileObj);
            jsonBody.put("files", files);
                DifyStreamClient client = new DifyStreamClient(apiUrl, apiKey, aiBotMessageService);
                DifyStreamClient uploadClient = new DifyStreamClient(aiBot.getModelAPI()+"/files/upload", apiKey, aiBotMessageService);
                String test = uploadClient.fileUpload(SaTokenUtil.getLoginAccount().getId() + "", file);
                System.out.println(test);
                Gson gson = new GsonBuilder().setPrettyPrinting().create();
                JsonObject fileJson = gson.fromJson(test, JsonObject.class);
                String fileId = fileJson.get("id").getAsString();
            // 发送HTTP请求
            HttpClient client = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofSeconds(30))
                    .build();
                // 2. 构建文件参数对象
                Map<String, Object> fileParam = new HashMap<>();
                fileParam.put("transfer_method", "local_file");
                fileParam.put("upload_file_id", fileId);
                String[] split = file.split("\\.");
//                fileParam.put("type", fileJson.get("extension").getAsString()); // 例如 "excel"、"pdf" 等
                fileParam.put("type", "document"); // 例如 "excel"、"pdf" 等
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(apiUrl))
                    .header("Content-Type", "application/json")
                    .header("Authorization", "Bearer " + bearerToken)
                    .POST(HttpRequest.BodyPublishers.ofString(jsonBody.toString()))
                    .build();
                // 3. 组装 inputs 参数
                Map<String, Object> inputs = new HashMap<>();
                inputs.put("w", fileParam); // 添加文件参数,variableName 如 "document"
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            // 处理成功响应
            JSONObject responseJson = new JSONObject(response.body());
            // 保存用户消息到历史记录
            ChatHistory userChat = new ChatHistory();
            userChat.setContent(prompt);
            userChat.setRole("user");
            userChat.setUserId(userId);
            userChat.setBotId(botId.intValue());
            userChat.setModel("dify");
            userChat.setChatId(responseJson.optString("conversation_id", ""));
            Integer userHistoryId = chatHistoryService.saveChatHistory(userChat);
                AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(),
                        sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper,
                        aiBotConversationMessageService);
            // 保存AI回复到历史记录
            ChatHistory assistantChat = new ChatHistory();
            assistantChat.setContent(responseJson.getString("answer"));
            assistantChat.setRole("assistant");
            assistantChat.setUserId(userId);
            assistantChat.setBotId(botId.intValue());
            assistantChat.setModel("dify");
            assistantChat.setChatId(userChat.getChatId());
            Integer assistantHistoryId = chatHistoryService.saveChatHistory(assistantChat);
                final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
                if (systemPrompt != null) {
                    historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
                }
            // 构建响应数据
            int count = chatHistoryService.getChatHistoryCount(userId, botId.intValue());
            JSONObject result = new JSONObject();
            result.put("Count", count);
            result.put("Data", responseJson.getString("answer"));
            result.put("UserId", userHistoryId);
            result.put("AssistantId", assistantHistoryId);
                historiesPrompt.setMemory(memory);
            // 发送SSE响应
            emitter.send(SseEmitter.event()
                    .name("message")
                    .data(result.toString()));
            emitter.complete();
        }else{
            Map<String, Object> llmOptions = aiBot.getLlmOptions();
            String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null;
                HumanMessage humanMessage = new HumanMessage(prompt);
                // 添加插件相关的function calling
                appendPluginToolFunction(botId, humanMessage);
                //添加工作流相关的 Function Calling
                appendWorkflowFunctions(botId, humanMessage);
                //添加知识库相关的 Function Calling
                appendKnowledgeFunctions(botId, humanMessage);
                historiesPrompt.addMessage(humanMessage);
                final Boolean[] needClose = {true};
                ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                MySseEmitter emitter = new MySseEmitter(1000L * 60 * 2); // 2分钟超时
                try {
                    String userId = SaTokenUtil.getLoginAccount().getId() + "";
                    client.runWorkflow(inputs, prompt, userId, emitter, sessionId, botId);
                } catch (Exception e) {
                    emitter.completeWithError(e);
                }
//                System.out.println(emitter.toString());
                return emitter;
            }
            aiBot.setModelAPI(aiBot.getModelAPI()+"/chat-messages");
            String apiUrl = aiBot.getModelAPI(); // 替换为实际API URL
            String apiKey = aiBot.getModelKEY(); // 替换为实际API Key
            DifyStreamClient client = new DifyStreamClient(apiUrl, apiKey, aiBotMessageService);
            AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(),
                    sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper,
                    aiBotConversationMessageService);
            final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
            if (systemPrompt != null) {
                historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
            }
            historiesPrompt.setMemory(memory);
            HumanMessage humanMessage = new HumanMessage(prompt);
            // 添加插件相关的function calling
            appendPluginToolFunction(botId, humanMessage);
            //添加工作流相关的 Function Calling
            appendWorkflowFunctions(botId, humanMessage);
            //添加知识库相关的 Function Calling
            appendKnowledgeFunctions(botId, humanMessage);
            historiesPrompt.addMessage(humanMessage);
            final Boolean[] needClose = {true};
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            MySseEmitter emitter = new MySseEmitter(1000L * 60 * 2); // 2分钟超时
            try {
                String userId = SaTokenUtil.getLoginAccount().getId() + "";
                client.chatStream(prompt, userId, emitter, sessionId, botId);
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
            System.out.println(emitter.toString());
            return emitter;
        }
        else{
            AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId());
            if (aiLlm == null) {
@@ -290,6 +330,7 @@
                                if (response.getMessage() != null) {
                                    String content = response.getMessage().getContent();
                                    if (StringUtil.hasText(content)) {
                                        System.out.println(response);
                                        emitter.send(JSON.toJSONString(response.getMessage()));
                                    }
                                }
@@ -315,10 +356,73 @@
                }
            });
            System.out.println(emitter.toString());
            return emitter;
        }
    }
    @PostMapping("files/upload")
    public Result filesUpload(@JsonBody(value = "botId", required = true) BigInteger botId,
                           String file,
                           HttpServletResponse response){
        try{
            String userId = SaTokenUtil.getLoginAccount().getId() + "";
            response.setContentType("text/event-stream");
            AiBot aiBot = service.getById(botId);
            aiBot.setModelAPI(aiBot.getModelAPI()+"/files/upload");
            String apiUrl = aiBot.getModelAPI(); // 替换为实际API URL
            String apiKey = aiBot.getModelKEY(); // 替换为实际API Key
            DifyStreamClient client = new DifyStreamClient(apiUrl, apiKey, aiBotMessageService);
            client.fileUpload(userId,file);
            return Result.success("成功!");
        }catch (Exception e){
            return Result.fail(400, String.valueOf(e));
        }
    }
    public Result save(@RequestBody String jsonStr) {
        // 解析JSON
        JSONObject json = JSONObject.parseObject(jsonStr);
        // 合并所有secondMenuId*字段
        List<Integer> menuIds = new ArrayList<>();
        for (String key : json.keySet()) {
            if (key.startsWith("secondMenuId")) {
                Object value = json.get(key);
                if (value instanceof Integer) {
                    menuIds.add((Integer) value);
                }
            }
        }
        // 保留第一个ID(根据需要调整)
        if (!menuIds.isEmpty()) {
            json.put("secondMenuId", menuIds.get(0));
        }
        // 转换为实体类
        AiBot entity = json.toJavaObject(AiBot.class);
        // 后续处理保持不变
        Result result = onSaveOrUpdateBefore(entity, true);
        if (result != null) return result;
        if (entity == null) {
            throw new NullPointerException("entity is null");
        }
        LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
        commonFiled(entity,loginAccount.getId(),loginAccount.getTenantId(),loginAccount.getDeptId());
        boolean success = service.save(entity);
        onSaveOrUpdateAfter(entity, true);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass());
        Object[] pkArgs = tableInfo.buildPkSqlArgs(entity);
        return Result.create(success).set("id", pkArgs);
    }
    /**
     * 外部用户调用智能体进行对话
@@ -641,4 +745,4 @@
    }
}
}