From 95ae0c83d2aa58d86b4039e3f7f16ee93afb8f00 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 23 十月 2026 17:01:00 +0800
Subject: [PATCH] feat(ai): 完善模型管理和智能体导入功能
---
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java | 186 +++++++++++++++++++++-------------------------
1 files changed, 84 insertions(+), 102 deletions(-)
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
index 235e54a..4b34498 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
@@ -1,93 +1,80 @@
package cn.iocoder.yudao.module.ai.service.model;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelUpdateStatusReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper;
-import com.agentsflex.llm.ollama.OllamaLlm;
-import com.agentsflex.llm.ollama.OllamaLlmConfig;
-import com.agentsflex.llm.qwen.QwenLlm;
-import com.agentsflex.llm.qwen.QwenLlmConfig;
-import dev.tinyflow.core.Tinyflow;
+import cn.iocoder.yudao.module.ai.dal.mysql.model.AiModelMapper;
+import cn.iocoder.yudao.module.ai.enums.model.AiModelCapabilityEnum;
+import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
+import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
+import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.image.ImageModel;
-import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.milvus.MilvusVectorStore;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
-
import java.util.List;
import java.util.Map;
-
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-/**
- * AI 妯″瀷 Service 瀹炵幇绫�
- *
- * @author fansili
- */
@Service
@Validated
public class AiModelServiceImpl implements AiModelService {
@Resource
private AiApiKeyService apiKeyService;
-
@Resource
- private AiChatMapper modelMapper;
-
+ private AiModelMapper modelMapper;
@Resource
private AiModelFactory modelFactory;
+ @Resource
+ private YudaoAiProperties aiProperties;
@Override
+ @Transactional(rollbackFor = Exception.class)
public Long createModel(AiModelSaveReqVO createReqVO) {
- // 1. 鏍¢獙
AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
apiKeyService.validateApiKey(createReqVO.getKeyId());
-
- // 2. 鎻掑叆
AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);
+ model.setCapability(resolveCapability(createReqVO.getCapability()));
+ if (CommonStatusEnum.isEnable(model.getStatus())) {
+ disableOtherEnabledModels(model);
+ }
modelMapper.insert(model);
return model.getId();
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void updateModel(AiModelSaveReqVO updateReqVO) {
- // 1. 鏍¢獙
- validateModelExists(updateReqVO.getId());
+ AiModelDO existing = validateModelExists(updateReqVO.getId());
AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
apiKeyService.validateApiKey(updateReqVO.getKeyId());
-
- // 2. 鏇存柊
- AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);
- modelMapper.updateById(updateObj);
+ AiModelDO model = BeanUtils.toBean(updateReqVO, AiModelDO.class);
+ // 鏈紶鑳藉姏鏃舵部鐢ㄥ師鍊硷紝閬垮厤鑰佸墠绔彁浜ゆ妸瑙嗚妯″瀷闄嶇骇鎴愭枃鏈兘鍔�
+ model.setCapability(resolveCapability(
+ updateReqVO.getCapability() == null || updateReqVO.getCapability().isBlank()
+ ? existing.getCapability() : updateReqVO.getCapability()));
+ if (CommonStatusEnum.isEnable(model.getStatus())) {
+ disableOtherEnabledModels(model);
+ }
+ modelMapper.updateById(model);
}
@Override
public void deleteModel(Long id) {
- // 鏍¢獙瀛樺湪
validateModelExists(id);
- // 鍒犻櫎
modelMapper.deleteById(id);
- }
-
- private AiModelDO validateModelExists(Long id) {
- AiModelDO model = modelMapper.selectById(id);
- if (modelMapper.selectById(id) == null) {
- throw exception(MODEL_NOT_EXISTS);
- }
- return model;
}
@Override
@@ -97,9 +84,16 @@
@Override
public AiModelDO getRequiredDefaultModel(Integer type) {
- AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());
+ return getRequiredDefaultModel(type, AiModelCapabilityEnum.defaultCapability());
+ }
+
+ @Override
+ public AiModelDO getRequiredDefaultModel(Integer type, String capability) {
+ AiModelDO model = modelMapper.selectFirstByTypeAndCapabilityAndStatus(
+ type, resolveCapability(capability), CommonStatusEnum.ENABLE.getStatus());
if (model == null) {
- throw exception(MODEL_DEFAULT_NOT_EXISTS);
+ throw exception(MODEL_NOT_ENABLED,
+ AiModelTypeEnum.getName(type), AiModelCapabilityEnum.getName(resolveCapability(capability)));
}
return model;
}
@@ -110,17 +104,55 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateModelStatus(AiModelUpdateStatusReqVO updateReqVO) {
+ AiModelDO model = validateModelExists(updateReqVO.getId());
+ model.setStatus(updateReqVO.getStatus());
+ if (CommonStatusEnum.isEnable(updateReqVO.getStatus())) {
+ // 鍚敤锛氳嚜鍔ㄥ叧闂悓銆岀被鍨� + 鑳藉姏銆嶄笅鐨勫叾浠栨ā鍨嬶紝淇濊瘉鍚岀粍鍙湁涓�涓惎鐢�
+ disableOtherEnabledModels(model);
+ }
+ modelMapper.updateById(model);
+ }
+
+ @Override
public AiModelDO validateModel(Long id) {
AiModelDO model = validateModelExists(id);
- if (CommonStatusEnum.isDisable(model.getStatus())) {
- throw exception(MODEL_DISABLE);
- }
+ if (CommonStatusEnum.isDisable(model.getStatus())) throw exception(MODEL_DISABLE, model.getName());
return model;
}
@Override
public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) {
return modelMapper.selectListByStatusAndType(status, type, platform);
+ }
+
+ /**
+ * 鍏抽棴鍚屻�岀被鍨� + 鑳藉姏銆嶄笅宸插惎鐢ㄧ殑鍏朵粬妯″瀷锛堜笉鍚嚜韬級
+ */
+ private void disableOtherEnabledModels(AiModelDO model) {
+ List<AiModelDO> sameGroup = modelMapper.selectListByTypeAndCapabilityAndStatus(
+ model.getType(), resolveCapability(model.getCapability()), CommonStatusEnum.ENABLE.getStatus());
+ for (AiModelDO other : sameGroup) {
+ if (other.getId().equals(model.getId())) {
+ continue;
+ }
+ AiModelDO update = new AiModelDO();
+ update.setId(other.getId());
+ update.setStatus(CommonStatusEnum.DISABLE.getStatus());
+ modelMapper.updateById(update);
+ }
+ }
+
+ private static String resolveCapability(String capability) {
+ return capability == null || capability.isBlank()
+ ? AiModelCapabilityEnum.defaultCapability() : capability;
+ }
+
+ private AiModelDO validateModelExists(Long id) {
+ AiModelDO model = modelMapper.selectById(id);
+ if (model == null) throw exception(MODEL_NOT_EXISTS);
+ return model;
}
// ========== 涓� Spring AI 闆嗘垚 ==========
@@ -130,72 +162,22 @@
AiModelDO model = validateModel(id);
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public ImageModel getImageModel(Long id) {
- AiModelDO model = validateModel(id);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public MidjourneyApi getMidjourneyApi(Long id) {
- AiModelDO model = validateModel(id);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public SunoApi getSunoApi() {
- AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
- AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
- return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
+ // temperature / maxTokens 鏄� AI 妯″瀷琛ㄤ笂涓�鐩村瓨鍦ㄣ�佸嵈浠庢病浜鸿杩囩殑涓ゅ垪锛�
+ // 涓嶈 temperature锛屽簱閲岄厤鐨勫�煎璋冪敤鏂规槸鍋囩殑锛涗笉璇� maxTokens锛岄暱杈撳嚭浼氳闈欓粯鎴柇
+ return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel(),
+ model.getTemperature(), model.getMaxTokens(), aiProperties.getTimeout());
}
@Override
public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {
- // 鑾峰彇妯″瀷 + 瀵嗛挜
AiModelDO model = validateModel(id);
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- // 鍒涘缓鎴栬幏鍙� EmbeddingModel 瀵硅薄
EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(
platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
- // 鍒涘缓鎴栬幏鍙� VectorStore 瀵硅薄
- return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
+ return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
}
- // TODO @lesan锛氭槸涓嶆槸杩斿洖 Llm 瀵硅薄浼氬ソ鐐瑰搱锛�
- @Override
- public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) {
- AiModelDO model = validateModel(modelId);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- switch (platform) {
- // TODO @lesan 鑰冭檻鍒版湭鏉ヤ笉闇�瑕佷娇鐢╝gents-flex 鐜板湪浠呮祴璇曢�氫箟鍗冮棶
- // TODO @lesan锛氥�愰噸瑕併�戞槸涓嶆槸鍙互瀹炵幇涓�涓� SpringAiLlm锛岃繖鏍风殑璇濓紝鍐呴儴鍏ㄩ儴鐢ㄥ畠灏卞ソ浜嗐�傚彧瀹炵幇 chat 閮ㄥ垎锛涜繖鏍凤紝灏辨妸 flex 浣滀负涓�涓� agent 妗嗘灦锛屽唴閮ㄨ皟鐢紝杩樻槸 spring ai 鐩稿叧鐨勩�傛垚鏈彲鑳戒綆涓�鐐癸紵锛�
- case TONG_YI:
- QwenLlmConfig qwenLlmConfig = new QwenLlmConfig();
- qwenLlmConfig.setApiKey(apiKey.getApiKey());
- qwenLlmConfig.setModel(model.getModel());
- // TODO @lesan锛氳繖涓湁鐐瑰鎬�傘�傘�傚鏋滀竴涓摼寮忛噷锛屾湁澶氫釜妯″瀷锛屽拫鏁村憖銆傘�傘��
- tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig));
- break;
- case OLLAMA:
- OllamaLlmConfig ollamaLlmConfig = new OllamaLlmConfig();
- ollamaLlmConfig.setEndpoint(apiKey.getUrl());
- ollamaLlmConfig.setModel(model.getModel());
- tinyflow.setLlmProvider(id -> new OllamaLlm(ollamaLlmConfig));
- break;
- }
- }
-
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3