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,92 +143,173 @@ 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获取为空"); } Map<String, Object> llmOptions = aiBot.getLlmOptions(); String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null; AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId()); AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(), sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper, aiBotConversationMessageService); if (aiLlm == null) { return ChatManager.getInstance().sseEmitterForContent("LLM不存在"); } final HistoriesPrompt historiesPrompt = new HistoriesPrompt(); Llm llm = aiLlm.toLlm(); historiesPrompt.setMemory(memory); if (llm == null) { return ChatManager.getInstance().sseEmitterForContent("LLM获取为空"); } HumanMessage humanMessage = new HumanMessage(prompt); AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(), sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper, aiBotConversationMessageService); // 添加插件相关的function calling appendPluginToolFunction(botId, humanMessage); final HistoriesPrompt historiesPrompt = new HistoriesPrompt(); if (systemPrompt != null) { historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt)); } //添加工作流相关的 Function Calling appendWorkflowFunctions(botId, humanMessage); historiesPrompt.setMemory(memory); //添加知识库相关的 Function Calling appendKnowledgeFunctions(botId, humanMessage); HumanMessage humanMessage = new HumanMessage(prompt); historiesPrompt.addMessage(humanMessage); // 添加插件相关的function calling appendPluginToolFunction(botId, humanMessage); MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2)); //添加工作流相关的 Function Calling appendWorkflowFunctions(botId, humanMessage); final Boolean[] needClose = {true}; //添加知识库相关的 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())); 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(); } } } catch (Exception e) { emitter.completeWithError(e); @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()); 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)) { 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 onStop(ChatContext context) { if (needClose[0]) { emitter.complete(); } } } @Override public void onFailure(ChatContext context, Throwable throwable) { emitter.completeWithError(throwable); } }); @Override public void onFailure(ChatContext context, Throwable throwable) { emitter.completeWithError(throwable); } }); return emitter; return emitter; } } /** 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(); } }