Commit 8b958cdc9b77d7b4f2fc67967329491c006951e1

Authored by YunaiV
1 parent 04b8aa04

feat:【AI 大模型】工作流增加 OLLAMA 的接入

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http 0 → 100644
  1 +### 测试 AI 工作流
  2 +POST {{baseUrl}}/ai/workflow/test
  3 +Content-Type: application/json
  4 +Authorization: {{token}}
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +{
  8 + "id": 4,
  9 + "params": {
  10 + "message": "1 + 1 = ?"
  11 + }
  12 +}
0 \ No newline at end of file 13 \ No newline at end of file
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);