admin
2025-05-29 6ca3d7c61ee8d763f00847d90f4365e348f43078
0529
1个文件已删除
4个文件已修改
2个文件已添加
412 ■■■■■ 已修改文件
aiflowy-commons/aiflowy-common-web/src/main/java/tech/aiflowy/common/web/controller/BaseCurdController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiFirstMenuController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiMenuController.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiSecondMenuController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiFirstMenuBase.java 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiSecondMenuBase.java 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-commons/aiflowy-common-web/src/main/java/tech/aiflowy/common/web/controller/BaseCurdController.java
@@ -135,6 +135,13 @@
     *
     * @return 所有数据
     */
    @GetMapping("sysDept")
    public Result sysDept(M entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        List<M> list = Tree.tryToTree(service.list(queryWrapper), asTree);
        return Result.success(list);
    }
    @GetMapping("list")
    public Result list(M entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java
@@ -18,6 +18,9 @@
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.jfinal.template.stat.ast.Break;
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.http.HttpStatus;
@@ -34,6 +37,7 @@
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;
@@ -139,7 +143,87 @@
        if (aiBot == null) {
            return ChatManager.getInstance().sseEmitterForContent("机器人不存在");
        }
        if (StringUtil.hasText(aiBot.getApiEndpoint())){
            // 情况1:aiBot自带大模型信息
            try {
                // 从aiBot构建自定义LLM实现
                Llm llm = null;
                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();
                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)) {
                                            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);
                    }
                });
                return emitter;
            } catch (Exception e) {
                return ChatManager.getInstance().sseEmitterForContent("自定义LLM配置错误");
            }
        }else{
        Map<String, Object> llmOptions = aiBot.getLlmOptions();
        String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null;
        AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId());
@@ -149,7 +233,6 @@
        }
        Llm llm = aiLlm.toLlm();
        if (llm == null) {
            return ChatManager.getInstance().sseEmitterForContent("LLM获取为空");
        }
@@ -227,6 +310,8 @@
        return emitter;
    }
    }
    /**
     * 外部用户调用智能体进行对话
     * 需要用户传 apiKey 对用户进行身份验证
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiFirstMenuController.java
New file
@@ -0,0 +1,31 @@
package tech.aiflowy.ai.controller;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.aiflowy.ai.entity.AiFirstMenu;
import tech.aiflowy.ai.entity.AiKnowledge;
import tech.aiflowy.ai.service.AiDocumentChunkService;
import tech.aiflowy.ai.service.AiFirstMenuService;
import tech.aiflowy.ai.service.AiKnowledgeService;
import tech.aiflowy.ai.service.AiLlmService;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.tree.Tree;
import tech.aiflowy.common.web.controller.BaseCurdController;
import java.util.List;
@RestController
@RequestMapping("/api/v1/aiMenu/FirstMenu")
public class AiFirstMenuController extends BaseCurdController<AiFirstMenuService, AiFirstMenu> {
    public AiFirstMenuController(AiFirstMenuService service) {
        super(service);
    }
    @Autowired
    AiFirstMenuService aiFirstMenuService;
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiMenuController.java
File was deleted
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiSecondMenuController.java
New file
@@ -0,0 +1,18 @@
package tech.aiflowy.ai.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.aiflowy.ai.entity.AiSecondMenu;
import tech.aiflowy.ai.service.AiSecondMenuService;
import tech.aiflowy.common.web.controller.BaseCurdController;
@RestController
@RequestMapping("/api/v1/aiMenu/SecondMenu")
public class AiSecondMenuController extends BaseCurdController<AiSecondMenuService, AiSecondMenu> {
    public AiSecondMenuController(AiSecondMenuService service) {
        super(service);
    }
    @Autowired
    AiSecondMenuService AiSecondMenuService;
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiFirstMenuBase.java
@@ -6,6 +6,7 @@
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigInteger;
/**
@@ -13,13 +14,13 @@
 * @TableName ai_first_menu
 */
public class AiFirstMenuBase {
public class AiFirstMenuBase  implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 一级菜单编号
     */
    @Id(keyType = KeyType.Auto, value = "firstMenuId", comment = "一级菜单编号")
    private BigInteger firstMenuId;
    private BigInteger id;
    /**
     * 一级菜单名称
@@ -27,61 +28,19 @@
    @Column(comment = "一级菜单名称")
    private String firstMenuName;
    public BigInteger getFirstMenuId() {
        return firstMenuId;
    public BigInteger getId() {
        return id;
    }
    public void setFirstMenuId(BigInteger firstMenuId) {
        this.firstMenuId = firstMenuId;
    public void setId(BigInteger id) {
        this.id = id;
    }
    /**
     * 一级菜单名称
     */
    public String getFirstMenuName() {
        return firstMenuName;
    }
    /**
     * 一级菜单名称
     */
    public void setFirstMenuName(String firstMenuName) {
        this.firstMenuName = firstMenuName;
    }
    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        AiFirstMenuBase other = (AiFirstMenuBase) that;
        return (this.getFirstMenuId() == null ? other.getFirstMenuId() == null : this.getFirstMenuId().equals(other.getFirstMenuId()))
            && (this.getFirstMenuName() == null ? other.getFirstMenuName() == null : this.getFirstMenuName().equals(other.getFirstMenuName()));
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getFirstMenuId() == null) ? 0 : getFirstMenuId().hashCode());
        result = prime * result + ((getFirstMenuName() == null) ? 0 : getFirstMenuName().hashCode());
        return result;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", firstMenuId=").append(firstMenuId);
        sb.append(", firstMenuName=").append(firstMenuName);
        sb.append("]");
        return sb.toString();
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/base/AiSecondMenuBase.java
@@ -5,18 +5,22 @@
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigInteger;
/**
 * ai机器人二级菜单表
 * @TableName ai_second_menu
 */
public class AiSecondMenuBase {
public class AiSecondMenuBase implements Serializable {
    /**
     * 二级菜单编号
     */
    private static final long serialVersionUID = 1L;
    @Id(keyType = KeyType.Auto, value = "secondMenuId", comment = "二级菜单编号")
    private BigInteger secondMenuId;
    private BigInteger id;
    /**
     * 一级菜单编号
@@ -30,6 +34,14 @@
    @Column(comment = "二级菜单名称")
    private String secondMenuName;
    public BigInteger getId() {
        return id;
    }
    public void setId(BigInteger id) {
        this.id = id;
    }
    public BigInteger getFirstMenuId() {
        return firstMenuId;
    }
@@ -38,65 +50,11 @@
        this.firstMenuId = firstMenuId;
    }
    public BigInteger getSecondMenuId() {
        return secondMenuId;
    }
    public void setSecondMenuId(BigInteger secondMenuId) {
        this.secondMenuId = secondMenuId;
    }
    /**
     * 二级菜单名称
     */
    public String getSecondMenuName() {
        return secondMenuName;
    }
    /**
     * 二级菜单名称
     */
    public void setSecondMenuName(String secondMenuName) {
        this.secondMenuName = secondMenuName;
    }
    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        AiSecondMenuBase other = (AiSecondMenuBase) that;
        return (this.getSecondMenuId() == null ? other.getSecondMenuId() == null : this.getSecondMenuId().equals(other.getSecondMenuId()))
            && (this.getFirstMenuId() == null ? other.getFirstMenuId() == null : this.getFirstMenuId().equals(other.getFirstMenuId()))
            && (this.getSecondMenuName() == null ? other.getSecondMenuName() == null : this.getSecondMenuName().equals(other.getSecondMenuName()));
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getSecondMenuId() == null) ? 0 : getSecondMenuId().hashCode());
        result = prime * result + ((getFirstMenuId() == null) ? 0 : getFirstMenuId().hashCode());
        result = prime * result + ((getSecondMenuName() == null) ? 0 : getSecondMenuName().hashCode());
        return result;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", secondMenuId=").append(secondMenuId);
        sb.append(", firstMenuId=").append(firstMenuId);
        sb.append(", secondMenuName=").append(secondMenuName);
        sb.append("]");
        return sb.toString();
    }
}