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 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 1 package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4   -import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.AssertTrue;
5 6 import lombok.Data;
6 7  
7 8 import java.util.Map;
... ... @@ -10,11 +11,18 @@ import java.util.Map;
10 11 @Data
11 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 18 private String graph;
16 19  
17 20 @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
18 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 133 VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);
134 134  
135 135 /**
136   - * 获取 Tinyflow 所需 LLm Provider
  136 + * 获取 TinyFlow 所需 LLm Provider
137 137 *
138 138 * @param tinyflow tinyflow
139 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 12 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
13 13 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
14 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 17 import com.agentsflex.llm.qwen.QwenLlm;
16 18 import com.agentsflex.llm.qwen.QwenLlmConfig;
17 19 import dev.tinyflow.core.Tinyflow;
... ... @@ -171,6 +173,7 @@ public class AiModelServiceImpl implements AiModelService {
171 173 // return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
172 174 }
173 175  
  176 + // TODO @lesan:是不是返回 Llm 对象会好点哈?
174 177 @Override
175 178 public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) {
176 179 AiModelDO model = validateModel(modelId);
... ... @@ -178,12 +181,20 @@ public class AiModelServiceImpl implements AiModelService {
178 181 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
179 182 switch (platform) {
180 183 // TODO @lesan 考虑到未来不需要使用agents-flex 现在仅测试通义千问
  184 + // TODO @lesan:【重要】是不是可以实现一个 SpringAiLlm,这样的话,内部全部用它就好了。只实现 chat 部分;这样,就把 flex 作为一个 agent 框架,内部调用,还是 spring ai 相关的。成本可能低一点?!
181 185 case TONG_YI:
182 186 QwenLlmConfig qwenLlmConfig = new QwenLlmConfig();
183 187 qwenLlmConfig.setApiKey(apiKey.getApiKey());
184 188 qwenLlmConfig.setModel(model.getModel());
  189 + // TODO @lesan:这个有点奇怪。。。如果一个链式里,有多个模型,咋整呀。。。
185 190 tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig));
186 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 40  
41 41 @Override
42 42 public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) {
43   - validateWorkflowForCreateOrUpdate(null, createReqVO.getCode());
  43 + // 1. 参数校验
  44 + validateCodeUnique(null, createReqVO.getCode());
  45 +
  46 + // 2. 插入工作流配置
44 47 AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class);
45 48 workflowMapper.insert(workflow);
46 49 return workflow.getId();
... ... @@ -48,47 +51,33 @@ public class AiWorkflowServiceImpl implements AiWorkflowService {
48 51  
49 52 @Override
50 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 59 AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class);
53 60 workflowMapper.updateById(workflow);
54 61 }
55 62  
56 63 @Override
57 64 public void deleteWorkflow(Long id) {
  65 + // 1. 校验存在
58 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 73 if (ObjUtil.isNull(id)) {
86   - return;
  74 + throw exception(WORKFLOW_NOT_EXISTS);
87 75 }
88 76 AiWorkflowDO workflow = workflowMapper.selectById(id);
89 77 if (ObjUtil.isNull(workflow)) {
90 78 throw exception(WORKFLOW_NOT_EXISTS);
91 79 }
  80 + return workflow;
92 81 }
93 82  
94 83 private void validateCodeUnique(Long id, String code) {
... ... @@ -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 123 private Tinyflow parseFlowParam(String graph) {
111 124 // TODO @lesan:可以使用 jackson 哇?
112 125 JSONObject json = JSONObject.parseObject(graph);
... ...