Commit 8b958cdc9b77d7b4f2fc67967329491c006951e1
1 parent
04b8aa04
feat:【AI 大模型】工作流增加 OLLAMA 的接入
Showing
5 changed files
with
74 additions
and
30 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http
0 → 100644
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | -import jakarta.validation.constraints.NotEmpty; | 5 | +import jakarta.validation.constraints.AssertTrue; |
| 5 | import lombok.Data; | 6 | import lombok.Data; |
| 6 | 7 | ||
| 7 | import java.util.Map; | 8 | import java.util.Map; |
| @@ -10,11 +11,18 @@ import java.util.Map; | @@ -10,11 +11,18 @@ import java.util.Map; | ||
| 10 | @Data | 11 | @Data |
| 11 | public class AiWorkflowTestReqVO { | 12 | public class AiWorkflowTestReqVO { |
| 12 | 13 | ||
| 13 | - @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") | ||
| 14 | - @NotEmpty(message = "工作流模型不能为空") | 14 | + @Schema(description = "工作流编号", example = "1024") |
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @Schema(description = "工作流模型", example = "{}") | ||
| 15 | private String graph; | 18 | private String graph; |
| 16 | 19 | ||
| 17 | @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") | 20 | @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") |
| 18 | private Map<String, Object> params; | 21 | private Map<String, Object> params; |
| 19 | 22 | ||
| 23 | + @AssertTrue(message = "工作流或模型,必须传递一个") | ||
| 24 | + public boolean isGraphValid() { | ||
| 25 | + return id != null || StrUtil.isNotEmpty(graph); | ||
| 26 | + } | ||
| 27 | + | ||
| 20 | } | 28 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
| @@ -133,7 +133,7 @@ public interface AiModelService { | @@ -133,7 +133,7 @@ public interface AiModelService { | ||
| 133 | VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields); | 133 | VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields); |
| 134 | 134 | ||
| 135 | /** | 135 | /** |
| 136 | - * 获取 Tinyflow 所需 LLm Provider | 136 | + * 获取 TinyFlow 所需 LLm Provider |
| 137 | * | 137 | * |
| 138 | * @param tinyflow tinyflow | 138 | * @param tinyflow tinyflow |
| 139 | * @param modelId AI 模型 ID | 139 | * @param modelId AI 模型 ID |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
| @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReq | @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReq | ||
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; |
| 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 14 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper; | 14 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper; |
| 15 | +import com.agentsflex.llm.ollama.OllamaLlm; | ||
| 16 | +import com.agentsflex.llm.ollama.OllamaLlmConfig; | ||
| 15 | import com.agentsflex.llm.qwen.QwenLlm; | 17 | import com.agentsflex.llm.qwen.QwenLlm; |
| 16 | import com.agentsflex.llm.qwen.QwenLlmConfig; | 18 | import com.agentsflex.llm.qwen.QwenLlmConfig; |
| 17 | import dev.tinyflow.core.Tinyflow; | 19 | import dev.tinyflow.core.Tinyflow; |
| @@ -171,6 +173,7 @@ public class AiModelServiceImpl implements AiModelService { | @@ -171,6 +173,7 @@ public class AiModelServiceImpl implements AiModelService { | ||
| 171 | // return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); | 173 | // return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); |
| 172 | } | 174 | } |
| 173 | 175 | ||
| 176 | + // TODO @lesan:是不是返回 Llm 对象会好点哈? | ||
| 174 | @Override | 177 | @Override |
| 175 | public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) { | 178 | public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) { |
| 176 | AiModelDO model = validateModel(modelId); | 179 | AiModelDO model = validateModel(modelId); |
| @@ -178,12 +181,20 @@ public class AiModelServiceImpl implements AiModelService { | @@ -178,12 +181,20 @@ public class AiModelServiceImpl implements AiModelService { | ||
| 178 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | 181 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); |
| 179 | switch (platform) { | 182 | switch (platform) { |
| 180 | // TODO @lesan 考虑到未来不需要使用agents-flex 现在仅测试通义千问 | 183 | // TODO @lesan 考虑到未来不需要使用agents-flex 现在仅测试通义千问 |
| 184 | + // TODO @lesan:【重要】是不是可以实现一个 SpringAiLlm,这样的话,内部全部用它就好了。只实现 chat 部分;这样,就把 flex 作为一个 agent 框架,内部调用,还是 spring ai 相关的。成本可能低一点?! | ||
| 181 | case TONG_YI: | 185 | case TONG_YI: |
| 182 | QwenLlmConfig qwenLlmConfig = new QwenLlmConfig(); | 186 | QwenLlmConfig qwenLlmConfig = new QwenLlmConfig(); |
| 183 | qwenLlmConfig.setApiKey(apiKey.getApiKey()); | 187 | qwenLlmConfig.setApiKey(apiKey.getApiKey()); |
| 184 | qwenLlmConfig.setModel(model.getModel()); | 188 | qwenLlmConfig.setModel(model.getModel()); |
| 189 | + // TODO @lesan:这个有点奇怪。。。如果一个链式里,有多个模型,咋整呀。。。 | ||
| 185 | tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig)); | 190 | tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig)); |
| 186 | break; | 191 | break; |
| 192 | + case OLLAMA: | ||
| 193 | + OllamaLlmConfig ollamaLlmConfig = new OllamaLlmConfig(); | ||
| 194 | + ollamaLlmConfig.setEndpoint(apiKey.getUrl()); | ||
| 195 | + ollamaLlmConfig.setModel(model.getModel()); | ||
| 196 | + tinyflow.setLlmProvider(id -> new OllamaLlm(ollamaLlmConfig)); | ||
| 197 | + break; | ||
| 187 | } | 198 | } |
| 188 | } | 199 | } |
| 189 | 200 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java
| @@ -40,7 +40,10 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | @@ -40,7 +40,10 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | ||
| 40 | 40 | ||
| 41 | @Override | 41 | @Override |
| 42 | public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) { | 42 | public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) { |
| 43 | - validateWorkflowForCreateOrUpdate(null, createReqVO.getCode()); | 43 | + // 1. 参数校验 |
| 44 | + validateCodeUnique(null, createReqVO.getCode()); | ||
| 45 | + | ||
| 46 | + // 2. 插入工作流配置 | ||
| 44 | AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class); | 47 | AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class); |
| 45 | workflowMapper.insert(workflow); | 48 | workflowMapper.insert(workflow); |
| 46 | return workflow.getId(); | 49 | return workflow.getId(); |
| @@ -48,47 +51,33 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | @@ -48,47 +51,33 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | ||
| 48 | 51 | ||
| 49 | @Override | 52 | @Override |
| 50 | public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) { | 53 | public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) { |
| 51 | - validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode()); | 54 | + // 1. 参数校验 |
| 55 | + validateWorkflowExists(updateReqVO.getId()); | ||
| 56 | + validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); | ||
| 57 | + | ||
| 58 | + // 2. 更新工作流配置 | ||
| 52 | AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class); | 59 | AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class); |
| 53 | workflowMapper.updateById(workflow); | 60 | workflowMapper.updateById(workflow); |
| 54 | } | 61 | } |
| 55 | 62 | ||
| 56 | @Override | 63 | @Override |
| 57 | public void deleteWorkflow(Long id) { | 64 | public void deleteWorkflow(Long id) { |
| 65 | + // 1. 校验存在 | ||
| 58 | validateWorkflowExists(id); | 66 | validateWorkflowExists(id); |
| 59 | - workflowMapper.deleteById(id); | ||
| 60 | - } | ||
| 61 | 67 | ||
| 62 | - @Override | ||
| 63 | - public AiWorkflowDO getWorkflow(Long id) { | ||
| 64 | - return workflowMapper.selectById(id); | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - @Override | ||
| 68 | - public PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO) { | ||
| 69 | - return workflowMapper.selectPage(pageReqVO); | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - @Override | ||
| 73 | - public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { | ||
| 74 | - Map<String, Object> variables = testReqVO.getParams(); | ||
| 75 | - Tinyflow tinyflow = parseFlowParam(testReqVO.getGraph()); | ||
| 76 | - return tinyflow.toChain().executeForResult(variables); | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - private void validateWorkflowForCreateOrUpdate(Long id, String code) { | ||
| 80 | - validateWorkflowExists(id); | ||
| 81 | - validateCodeUnique(id, code); | 68 | + // 2. 删除工作流配置 |
| 69 | + workflowMapper.deleteById(id); | ||
| 82 | } | 70 | } |
| 83 | 71 | ||
| 84 | - private void validateWorkflowExists(Long id) { | 72 | + private AiWorkflowDO validateWorkflowExists(Long id) { |
| 85 | if (ObjUtil.isNull(id)) { | 73 | if (ObjUtil.isNull(id)) { |
| 86 | - return; | 74 | + throw exception(WORKFLOW_NOT_EXISTS); |
| 87 | } | 75 | } |
| 88 | AiWorkflowDO workflow = workflowMapper.selectById(id); | 76 | AiWorkflowDO workflow = workflowMapper.selectById(id); |
| 89 | if (ObjUtil.isNull(workflow)) { | 77 | if (ObjUtil.isNull(workflow)) { |
| 90 | throw exception(WORKFLOW_NOT_EXISTS); | 78 | throw exception(WORKFLOW_NOT_EXISTS); |
| 91 | } | 79 | } |
| 80 | + return workflow; | ||
| 92 | } | 81 | } |
| 93 | 82 | ||
| 94 | private void validateCodeUnique(Long id, String code) { | 83 | private void validateCodeUnique(Long id, String code) { |
| @@ -107,6 +96,30 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | @@ -107,6 +96,30 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { | ||
| 107 | } | 96 | } |
| 108 | } | 97 | } |
| 109 | 98 | ||
| 99 | + @Override | ||
| 100 | + public AiWorkflowDO getWorkflow(Long id) { | ||
| 101 | + return workflowMapper.selectById(id); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + @Override | ||
| 105 | + public PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO) { | ||
| 106 | + return workflowMapper.selectPage(pageReqVO); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Override | ||
| 110 | + public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { | ||
| 111 | + // 加载 graph | ||
| 112 | + String graph = testReqVO.getGraph() != null ? testReqVO.getGraph() | ||
| 113 | + : validateWorkflowExists(testReqVO.getId()).getGraph(); | ||
| 114 | + | ||
| 115 | + // 构建 TinyFlow 执行链 | ||
| 116 | + Tinyflow tinyflow = parseFlowParam(graph); | ||
| 117 | + | ||
| 118 | + // 执行 | ||
| 119 | + Map<String, Object> variables = testReqVO.getParams(); | ||
| 120 | + return tinyflow.toChain().executeForResult(variables); | ||
| 121 | + } | ||
| 122 | + | ||
| 110 | private Tinyflow parseFlowParam(String graph) { | 123 | private Tinyflow parseFlowParam(String graph) { |
| 111 | // TODO @lesan:可以使用 jackson 哇? | 124 | // TODO @lesan:可以使用 jackson 哇? |
| 112 | JSONObject json = JSONObject.parseObject(graph); | 125 | JSONObject json = JSONObject.parseObject(graph); |