admin
2025-06-10 a5a9ac7c2a9fd6026b0d87e1b0eff763eb4512d0
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java
@@ -1,14 +1,6 @@
package tech.aiflowy.ai.controller;
import tech.aiflowy.ai.entity.*;
import tech.aiflowy.ai.service.*;
import tech.aiflowy.common.ai.ChatManager;
import tech.aiflowy.common.ai.MySseEmitter;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.util.StringUtil;
import tech.aiflowy.common.web.controller.BaseCurdController;
import tech.aiflowy.common.web.jsonbody.JsonBody;
import tech.aiflowy.common.satoken.util.SaTokenUtil;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.ObjectUtil;
import com.agentsflex.core.llm.ChatContext;
import com.agentsflex.core.llm.Llm;
@@ -16,25 +8,54 @@
import com.agentsflex.core.llm.functions.Function;
import com.agentsflex.core.llm.response.AiMessageResponse;
import com.agentsflex.core.llm.response.FunctionCaller;
import com.agentsflex.core.message.AiMessage;
import com.agentsflex.core.message.HumanMessage;
import com.agentsflex.core.message.SystemMessage;
import com.agentsflex.core.prompt.HistoriesPrompt;
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.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
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.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
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.common.ai.ChatManager;
import tech.aiflowy.common.ai.MySseEmitter;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.entity.LoginAccount;
import tech.aiflowy.common.satoken.util.SaTokenUtil;
import tech.aiflowy.common.util.StringUtil;
import tech.aiflowy.common.web.controller.BaseCurdController;
import tech.aiflowy.common.web.jsonbody.JsonBody;
import tech.aiflowy.system.entity.SysApiKey;
import tech.aiflowy.system.mapper.SysApiKeyMapper;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
 * 控制层。
@@ -50,6 +71,16 @@
    private final AiBotWorkflowService aiBotWorkflowService;
    private final AiBotKnowledgeService aiBotKnowledgeService;
    private final AiBotMessageService aiBotMessageService;
    @Resource
    private SysApiKeyMapper aiBotApiKeyMapper;
    @Resource
    private AiBotConversationMessageService aiBotConversationMessageService;
    @Resource
    private AiBotConversationMessageMapper aiBotConversationMessageMapper;
    private static final Logger logger = LoggerFactory.getLogger(AiBotController.class);
    @Autowired
    private RestTemplate restTemplate;
    public AiBotController(AiBotService service, AiLlmService aiLlmService, AiBotWorkflowService aiBotWorkflowService, AiBotKnowledgeService aiBotKnowledgeService, AiBotMessageService aiBotMessageService) {
        super(service);
@@ -60,9 +91,9 @@
    }
    @Resource
    private AiPluginsService aiPluginsService;
    @Resource
    private AiBotPluginsService aiBotPluginsService;
    @Resource
    private AiPluginToolService aiPluginToolService;
    @PostMapping("updateOptions")
    public Result updateOptions(@JsonBody("id") BigInteger id, @JsonBody("options") Map<String, Object> options) {
@@ -95,124 +126,675 @@
        return Result.success();
    }
    /**
     * 当前系统用户调用对话
     *
     * @param prompt
     * @param botId
     * @param sessionId
     * @param isExternalMsg
     * @param response
     * @return
     */
    @Autowired
    private ObjectMapper objectMapper;
//    @PostMapping("chat")
//    public SseEmitter chat2(@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("file") String file,//上传文件
//                           HttpServletResponse response){
//        File file =
//    }
    @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 = "files") List<String> files,//上传文件
                           @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())){
            if (aiBot.getBotTypeId() == 2 || aiBot.getBotTypeId() == 3) {
                String apiUrl = aiBot.getModelAPI()+"/workflows/run"; // 替换为实际API URL
                String apiKey = aiBot.getModelKEY(); // 替换为实际API Key
                DifyStreamClient client = new DifyStreamClient(apiUrl, apiKey, aiBotMessageService);
//                DifyStreamClient uploadClient = new DifyStreamClient(aiBot.getModelAPI()+"/files/upload", apiKey, aiBotMessageService);
                Map<String, Object> inputs = new HashMap<>();
                if(aiBot.getBotTypeId() == 3){
                    // 2. 构建文件参数对象
                    HttpHeaders headers = new HttpHeaders();
                    headers.set("Authorization", apiKey);
                    HttpEntity<Object> requestEntity = new HttpEntity<>(null, headers);
                    ResponseEntity<String> parameters = restTemplate.exchange(
                            aiBot.getModelAPI()+"/parameters",
                            HttpMethod.GET,
                            requestEntity, // 请求体(GET 无请求体)
                            String.class // 响应结果类型
                    );
                    org.json.JSONObject jsonObj = new org.json.JSONObject(parameters.getBody());
                    // 先获取 user_input_form 数组
                    org.json.JSONArray userInputFormArr = jsonObj.getJSONArray("user_input_form");
                    String variable = null;
                    String type = "";
                    // 遍历数组,找到 file - list 结构
                    for (int i = 0; i < userInputFormArr.length(); i++) {
                        org.json.JSONObject formItem = userInputFormArr.getJSONObject(i);
                        if (formItem.has("file-list")) {
                            org.json.JSONObject fileListObj = formItem.getJSONObject("file-list");
                            // 提取 variable 参数
                            variable = fileListObj.getString("variable");
                            // 提取 allowed_file_types 参数(数组形式)
                            org.json.JSONArray allowedFileTypesArr = fileListObj.getJSONArray("allowed_file_types");
                            System.out.println("variable: " + variable);
                            System.out.println("allowed_file_types: " + allowedFileTypesArr.toString());
                            type = allowedFileTypesArr.toString().replace("[\"", "").replace("\"]", "");
                            break; // 这里假设只有一个符合的 file - list,找到就退出循环,可根据实际情况调整
                        }
                    }
                    List fileList = new ArrayList<>();
                    Map<String, Object> fileParam = new HashMap<>();
                    for (String fileId : files) {
                        fileParam.put("transfer_method", "local_file");
                        fileParam.put("upload_file_id", fileId);
                        fileParam.put("type", type); // 例如 "excel"、"pdf" 等
                        fileList.add(fileParam);
                    }
                    // 3. 组装 inputs 参数
                    inputs.put(variable, fileList); // 添加文件参数,variableName 如 "document"
                }
                else {
                    HttpHeaders headers = new HttpHeaders();
                    headers.set("Authorization", apiKey);
                    Map<String, Object> fileParam = new HashMap<>();
                    fileParam.put("transfer_method", "local_file");
                    fileParam.put("upload_file_id", file);
                    HttpEntity<Object> requestEntity = new HttpEntity<>(null, headers);
                    ResponseEntity<String> parameters = restTemplate.exchange(
                            aiBot.getModelAPI()+"/parameters",
                            HttpMethod.GET,
                            requestEntity, // 请求体(GET 无请求体)
                            String.class // 响应结果类型
                    );
//                    System.out.println("========================\n"+parameters.getBody()+"\n=====================");
                    String variable = null;
                    String type = "";
                    try {
                        // 解析 JSON 字符串
                        org.json.JSONObject configJson = new org.json.JSONObject(parameters.getBody());
                        // 获取 user_input_form 数组
                        org.json.JSONArray userInputFormArray = configJson.getJSONArray("user_input_form");
                        // 遍历 user_input_form 数组中的每个元素
                        for (int i = 0; i < userInputFormArray.length(); i++) {
                            org.json.JSONObject formElement = userInputFormArray.getJSONObject(i);
                            // 检查是否包含 "file" 对象
                            if (formElement.has("file")) {
                                org.json.JSONObject fileObject = formElement.getJSONObject("file");
                                // 提取 variable
                                variable = fileObject.getString("variable");
                                // 提取 allowed_file_types 数组
                                org.json.JSONArray allowedFileTypesArray = fileObject.getJSONArray("allowed_file_types");
                                // 打印结果
                                System.out.println("Variable: " + variable);
                                System.out.print("Document Types: ");
                                for (int j = 0; j < allowedFileTypesArray.length(); j++) {
                                    type = type + allowedFileTypesArray.getString(j);
                                    System.out.print(allowedFileTypesArray.getString(j) + " ");
                                }
                                System.out.println();
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    fileParam.put("type", type); // 例如 "excel"、"pdf" 等
                    // 3. 组装 inputs 参数
                    inputs.put(variable, fileParam); // 添加文件参数,variableName 如 "document"
                }
                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.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) {
                return ChatManager.getInstance().sseEmitterForContent("LLM不存在");
            }
            Llm llm = aiLlm.toLlm();
            if (llm == null) {
                return ChatManager.getInstance().sseEmitterForContent("LLM获取为空");
            }
            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);
            MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
            final Boolean[] needClose = {true};
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            // 统一使用流式处理,无论是否有 Function Calling
            llm.chatStream(historiesPrompt, new StreamResponseListener() {
                @Override
                public void onMessage(ChatContext context, AiMessageResponse response) {
                    try {
                        RequestContextHolder.setRequestAttributes(sra, true);
                        if (response != null) {
                            // 检查是否需要触发 Function Calling
                            if (response.getFunctionCallers() != null && CollectionUtil.hasItems(response.getFunctionCallers())) {
                                needClose[0] = false;
                                function_call(response, emitter, needClose, historiesPrompt, llm, prompt, false);
                            } else {
                                // 强制流式返回,即使有 Function Calling 也先返回部分结果
                                if (response.getMessage() != null) {
                                    String content = response.getMessage().getContent();
                                    if (StringUtil.hasText(content)) {
                                        System.out.println(response);
                                        emitter.send(JSON.toJSONString(response.getMessage()));
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                }
                @Override
                public void onStop(ChatContext context) {
                    if (needClose[0]) {
                        emitter.complete();
                    }
                }
                @Override
                public void onFailure(ChatContext context, Throwable throwable) {
                    emitter.completeWithError(throwable);
                }
            });
            System.out.println(emitter.toString());
            return emitter;
        }
    }
    @PostMapping("files/upload")
    public Result filesUpload(@RequestParam("botId") BigInteger botId,
                              @RequestParam("file") MultipartFile 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);
            String s = client.fileUpload(userId, file);
            return Result.success(s);
        }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);
    }
    /**
     * 外部用户调用智能体进行对话
     * 需要用户传 apiKey 对用户进行身份验证
     *
     * @param stream [true: 返回sse false: 返回json
     * @return
     */
    @SaIgnore
    @PostMapping("externalChat")
    public Object externalChat(
            @JsonBody(value = "messages", required = true) List<AiBotMessage> messages,
            @JsonBody(value = "botId", required = true) BigInteger botId,
            @JsonBody(value = "stream", required = false) boolean stream,
            HttpServletResponse response,
            HttpServletRequest request
    ) {
        // 设置响应类型
        if (stream) {
            response.setContentType("text/event-stream");
        } else {
            response.setContentType("application/json");
        }
        // 获取 API Key 和 Bot 信息
        String apiKey = request.getHeader("Authorization");
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select("api_key", "status", "expired_at")
                .from("tb_sys_api_key")
                .where("api_key = ? ", apiKey);
        SysApiKey aiBotApiKey = aiBotApiKeyMapper.selectOneByQuery(queryWrapper);
        if (aiBotApiKey == null) {
            return createResponse(stream, JSON.toJSONString(errorRespnseMsg(1, "该apiKey不存在")));
        }
        if (aiBotApiKey.getStatus() == 0) {
            return createResponse(stream, JSON.toJSONString(errorRespnseMsg(2, "该apiKey未启用")));
        }
        if (aiBotApiKey.getExpiredAt().getTime() < new Date().getTime()) {
            return createResponse(stream, JSON.toJSONString(errorRespnseMsg(3, "该apiKey已失效")));
        }
        AiBot aiBot = service.getById(botId);
        if (aiBot == null) {
            return createResponse(stream, JSON.toJSONString(errorRespnseMsg(4, "机器人不存在")));
        }
        Map<String, Object> llmOptions = aiBot.getLlmOptions();
        String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null;
        AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId());
        if (aiLlm == null) {
            return ChatManager.getInstance().sseEmitterForContent("LLM不存在");
            return createResponse(stream, JSON.toJSONString(errorRespnseMsg(5, "LLM不存在")));
        }
        Llm llm = aiLlm.toLlm();
        AiBotExternalMessageMemory messageMemory = new AiBotExternalMessageMemory(messages);
        HistoriesPrompt historiesPrompt = new HistoriesPrompt();
        if (systemPrompt != null) {
            historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
        }
        historiesPrompt.setMemory(messageMemory);
        AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(), sessionId, aiBotMessageService);
        String prompt = messages.get(messages.size() - 1).getContent();
        HumanMessage humanMessage = new HumanMessage();
        final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
        historiesPrompt.setSystemMessage(SystemMessage.of((String) llmOptions.get("systemPrompt")));
        historiesPrompt.setMemory(memory);
        HumanMessage humanMessage = new HumanMessage(prompt);
        // 添加插件相关的function calling
        appendPluginFunctions(botId, humanMessage);
        //添加工作流相关的 Function Calling
        // 添加插件、工作流、知识库相关的 Function Calling
        appendPluginToolFunction(botId, humanMessage);
        appendWorkflowFunctions(botId, humanMessage);
        //添加知识库相关的 Function Calling
        appendKnowledgeFunctions(botId, humanMessage);
        historiesPrompt.addMessage(humanMessage);
        MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
        // 根据 responseType 返回不同的响应
        if (stream) {
            MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
            final Boolean[] needClose = {true};
        final Boolean[] needClose = {true};
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        llm.chatStream(historiesPrompt, new StreamResponseListener() {
            @Override
            public void onMessage(ChatContext context, AiMessageResponse response) {
            if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) {
                try {
                    RequestContextHolder.setRequestAttributes(sra, true);
                    String content = response.getMessage().getContent();
                    Object messageContent = response.getMessage();
                    if (StringUtil.hasText(content)) {
                        String jsonResult = JSON.toJSONString(messageContent);
                        emitter.send(jsonResult);
                    }
                    List<FunctionCaller> functionCallers = response.getFunctionCallers();
                    if (CollectionUtil.hasItems(functionCallers)) {
                        needClose[0] = false;
                        for (FunctionCaller functionCaller : functionCallers) {
                            Object result = functionCaller.call();
                            if (ObjectUtil.isNotEmpty(result)) {
                                String newPrompt = "请根据以下内容回答用户,内容是:\n" + result + "\n 用户的问题是:" + prompt;
                                historiesPrompt.addMessageTemporary(new HumanMessage(newPrompt));
                                llm.chatStream(historiesPrompt, new StreamResponseListener() {
                                    @Override
                                    public void onMessage(ChatContext context, AiMessageResponse response) {
                                        needClose[0] = true;
                                        String content = response.getMessage().getContent();
                                        Object messageContent = response.getMessage();
                                        if (StringUtil.hasText(content)) {
                                            String jsonResult = JSON.toJSONString(messageContent);
                                            emitter.send(jsonResult);
                                        }
                                    }
                                    @Override
                                    public void onStop(ChatContext context) {
                                        if (needClose[0]) {
                                            System.out.println("function chat complete");
                                            emitter.complete();
                                        }
                                        historiesPrompt.clearTemporaryMessages();
                                    }
                                    @Override
                                    public void onFailure(ChatContext context, Throwable throwable) {
                                        emitter.completeWithError(throwable);
                                    }
                                });
                            }
                        }
                    }
                    AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt);
                    function_call(aiMessageResponse, emitter, needClose, historiesPrompt, llm, prompt, true);
                } catch (Exception e) {
                    emitter.completeWithError(e);
                }
                if (needClose[0]) {
                    System.out.println("function chat complete");
                    emitter.complete();
                }
            } else {
                llm.chatStream(historiesPrompt, new StreamResponseListener() {
                    @Override
                    public void onMessage(ChatContext context, AiMessageResponse response) {
                        try {
                            function_call(response, emitter, needClose, historiesPrompt, llm, prompt, true);
                        } catch (Exception e) {
                            emitter.completeWithError(e);
                        }
                    }
                    @Override
                    public void onStop(ChatContext context) {
                        if (needClose[0]) {
                            System.out.println("normal chat complete");
                            emitter.complete();
                        }
                    }
                    @Override
                    public void onFailure(ChatContext context, Throwable throwable) {
                        emitter.completeWithError(throwable);
                    }
                });
            }
            return emitter;
        } else {
            AiMessageResponse resultFunctionCall;
            if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) {
                try {
                    AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt);
                    resultFunctionCall = jsonResultJsonFunctionCall(aiMessageResponse, historiesPrompt, llm, prompt);
                    return JSON.toJSONString(resultFunctionCall.getMessage(), new SerializeConfig());
                } catch (Exception e) {
                    return createErrorResponse(e);
                }
            } else {
                AiMessageResponse messageResponse = llm.chat(historiesPrompt);
                resultFunctionCall = jsonResultJsonFunctionCall(messageResponse, historiesPrompt, llm, prompt);
                AiBotExternalMsgJsonResult result = handleMessageResult(resultFunctionCall.getMessage());
                return JSON.toJSONString(result, new SerializeConfig());
            }
        }
    }
    private AiBotExternalMsgJsonResult handleMessageResult(AiMessage aiMessage) {
        AiBotExternalMsgJsonResult messageResult = new AiBotExternalMsgJsonResult();
        messageResult.setCreated(new Date().getTime());
        AiBotExternalMsgJsonResult.Usage usage = new AiBotExternalMsgJsonResult.Usage();
        if (aiMessage.getTotalTokens() != null){
            usage.setTotalTokens(aiMessage.getTotalTokens());
        }
        if (aiMessage.getCompletionTokens() != null){
            usage.setCompletionTokens(aiMessage.getCompletionTokens());
        }
        if (aiMessage.getPromptTokens() != null){
            usage.setPromptTokens(aiMessage.getPromptTokens());
        }
        messageResult.setUsage(usage);
        AiBotExternalMsgJsonResult.Choice choice = new AiBotExternalMsgJsonResult.Choice();
        AiBotExternalMsgJsonResult.Message message = new AiBotExternalMsgJsonResult.Message();
        message.setContent(aiMessage.getContent());
        message.setRole("assistant");
        choice.setMessage(message);
        messageResult.setChoices(choice);
        messageResult.setStatus(aiMessage.getStatus().name());
        return messageResult;
    }
    // 辅助方法:创建响应
    private Object createResponse(boolean stream, String content) {
        if (stream) {
            MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
            emitter.send(content);
            emitter.complete();
            return emitter;
        } else {
            return ResponseEntity.ok(content);
        }
    }
    // 辅助方法:创建错误响应
    private Object createErrorResponse(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
    }
    /**
     * @param aiMessageResponse 大模型返回的消息
     * @param emitter
     * @param needClose         是否需要关闭流
     * @param historiesPrompt   消息历史记录
     * @param llm               大模型
     * @param prompt            提示词
     * @param isExternalChatApi true 外部系统调用bot  false 内部系统调用bot
     */
    private String function_call(AiMessageResponse aiMessageResponse, MySseEmitter emitter, Boolean[] needClose, HistoriesPrompt historiesPrompt, Llm llm, String prompt, boolean isExternalChatApi) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        RequestContextHolder.setRequestAttributes(sra, true);
        String content = aiMessageResponse.getMessage().getContent();
        Object messageContent = aiMessageResponse.getMessage();
        if (StringUtil.hasText(content)) {
            // 如果是外部系统调用chat
            if (isExternalChatApi) {
                AiBotExternalMsgJsonResult result = handleMessageStreamJsonResult(aiMessageResponse.getMessage());
                emitter.send(JSON.toJSONString(result, new SerializeConfig()));
            } else {
                emitter.send(JSON.toJSONString(messageContent));
            }
        }
        System.out.println("function call 接收到的参数message:" + aiMessageResponse);
        llm.chatStream(ToolPrompt.of(aiMessageResponse), new StreamResponseListener() {
            @Override
            public void onMessage(ChatContext context, AiMessageResponse response) {
                System.out.println("function call <UNK>message<UNK>" + aiMessageResponse);
                String content = response.getMessage().getContent();
                if (StringUtil.hasText(content)) {
                    System.out.println("if content"  + content);
                    emitter.send(JSON.toJSONString(response.getMessage()));
                }
            }
            @Override
            public void onStop(ChatContext context) {
                if (needClose[0]) {
                    System.out.println("normal chat complete");
                    emitter.complete();
                }
                System.out.println("function call complete");
                emitter.complete();
            }
            @Override
            public void onFailure(ChatContext context, Throwable throwable) {
                emitter.completeWithError(throwable);
                logger.error("function_call报错:",throwable);
                AiMessage aiMessage = new AiMessage();
                aiMessage.setContent("未查询到相关信息...");
                emitter.send(JSON.toJSONString(aiMessage));
                System.out.println("function call complete with error");
            }
        });
        return emitter;
        return JSON.toJSONString(messageContent);
    }
    private Map<String, Object> errorRespnseMsg(int errorCode, String message) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("error", errorCode);
        result.put("message", message);
        return result;
    }
    private AiBotExternalMsgJsonResult handleMessageStreamJsonResult(AiMessage message) {
        AiBotExternalMsgJsonResult result = new AiBotExternalMsgJsonResult();
        AiBotExternalMsgJsonResult.Choice choice = new AiBotExternalMsgJsonResult.Choice();
        AiBotExternalMsgJsonResult.Delta delta = new AiBotExternalMsgJsonResult.Delta();
        delta.setRole("assistant");
        delta.setContent(message.getContent());
        choice.setDelta(delta);
        result.setCreated(new Date().getTime());
        result.setChoices(choice);
        result.setStatus(message.getStatus().name());
        return result;
    }
    private AiMessageResponse jsonResultJsonFunctionCall(AiMessageResponse aiMessageResponse, HistoriesPrompt historiesPrompt, Llm llm, String prompt) {
        List<FunctionCaller> functionCallers = aiMessageResponse.getFunctionCallers();
        if (CollectionUtil.hasItems(functionCallers)) {
            for (FunctionCaller functionCaller : functionCallers) {
                Object result = functionCaller.call();
                if (ObjectUtil.isNotEmpty(result)) {
                    String newPrompt = "请根据以下内容回答用户,内容是:\n" + result + "\n 用户的问题是:" + prompt;
                    historiesPrompt.addMessageTemporary(new HumanMessage(newPrompt));
                    return llm.chat(historiesPrompt);
                }
            }
        }
        return aiMessageResponse;
    }
    private void appendWorkflowFunctions(BigInteger botId, HumanMessage humanMessage) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(AiBotWorkflow::getBotId, botId);
@@ -236,14 +818,33 @@
        }
    }
    private void appendPluginFunctions(BigInteger botId, HumanMessage humanMessage) {
        QueryWrapper queryWrapper = QueryWrapper.create().eq(AiBotPlugins::getBotId, botId);
        List<AiBotPlugins> aiBotPlugins = aiBotPluginsService.getMapper().selectListWithRelationsByQuery(queryWrapper);
        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(aiBotPlugins)) {
            for (AiBotPlugins aiBotPlugin : aiBotPlugins) {
                Function function = aiBotPlugin.getAiPlugins().toFunction();
                humanMessage.addFunction(function);
            }
    //    private void appendPluginFunctions(BigInteger botId, HumanMessage humanMessage) {
//        QueryWrapper queryWrapper = QueryWrapper.create().eq(AiBotPlugins::getBotId, botId);
//        List<AiBotPlugins> aiBotPlugins = aiBotPluginsService.getMapper().selectListWithRelationsByQuery(queryWrapper);
//        if (cn.hutool.core.collection.CollectionUtil.isNotEmpty(aiBotPlugins)) {
//            for (AiBotPlugins aiBotPlugin : aiBotPlugins) {
//                Function function = aiBotPlugin.getAiPlugins().toFunction();
//                humanMessage.addFunction(function);
//            }
//        }
//    }
    private void appendPluginToolFunction(BigInteger botId, HumanMessage humanMessage) {
        QueryWrapper queryWrapper = QueryWrapper.create().select("plugin_tool_id").eq(AiBotPlugins::getBotId, botId);
        List<BigInteger> pluginToolIds = aiBotPluginsService.getMapper().selectListWithRelationsByQueryAs(queryWrapper, BigInteger.class);
        if (pluginToolIds == null || pluginToolIds.isEmpty()) {
            return;
        }
        QueryWrapper queryTool = QueryWrapper.create()
                .select("*")
                .from("tb_ai_plugin_tool")
                .in("id", pluginToolIds);
        List<AiPluginTool> aiPluginTools = aiPluginToolService.getMapper().selectListWithRelationsByQuery(queryTool);
        for (AiPluginTool item : aiPluginTools) {
            humanMessage.addFunction(item.toFunction());
        }
    }
}