diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java index 36aa1192..96a4d014 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java @@ -41,6 +41,7 @@ import xiaozhi.modules.agent.service.AgentTemplateService; import xiaozhi.modules.agent.vo.AgentInfoVO; import xiaozhi.modules.device.service.DeviceService; import xiaozhi.modules.model.dto.ModelProviderDTO; +import xiaozhi.modules.model.entity.ModelConfigEntity; import xiaozhi.modules.model.service.ModelConfigService; import xiaozhi.modules.model.service.ModelProviderService; import xiaozhi.modules.security.user.SecurityUser; @@ -324,9 +325,32 @@ public class AgentServiceImpl extends BaseServiceImpl imp // 删除音频数据 agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false); } + + boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId()); + if (!b) { + throw new RenException("LLM大模型和Intent意图识别,选择参数不匹配"); + } this.updateById(existingEntity); } + /** + * 验证大语言模型和意图识别的参数是否符合匹配 + * + * @param llmModelId 大语言模型id + * @param intentModelId 意图识别id + * @return T 匹配 : F 不匹配 + */ + private boolean validateLLMIntentParams(String llmModelId, String intentModelId) { + ModelConfigEntity llmModelData = modelConfigService.selectById(llmModelId); + String type = llmModelData.getConfigJson().get("type").toString(); + // 如果查询大语言模型是openai或者ollama,意图识别选参数都可以 + if ("openai".equals(type) || "ollama".equals(type)) { + return true; + } + // 除了openai和ollama的类型,不可以选择id为Intent_function_call(函数调用)的意图识别 + return !"Intent_function_call".equals(intentModelId); + } + @Override @Transactional(rollbackFor = Exception.class) public String createAgent(AgentCreateDTO dto) { diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java index 8baff29d..32a5c0cc 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/controller/ModelController.java @@ -21,11 +21,7 @@ import xiaozhi.common.utils.ConvertUtils; import xiaozhi.common.utils.Result; import xiaozhi.modules.agent.service.AgentTemplateService; import xiaozhi.modules.config.service.ConfigService; -import xiaozhi.modules.model.dto.ModelBasicInfoDTO; -import xiaozhi.modules.model.dto.ModelConfigBodyDTO; -import xiaozhi.modules.model.dto.ModelConfigDTO; -import xiaozhi.modules.model.dto.ModelProviderDTO; -import xiaozhi.modules.model.dto.VoiceDTO; +import xiaozhi.modules.model.dto.*; import xiaozhi.modules.model.entity.ModelConfigEntity; import xiaozhi.modules.model.service.ModelConfigService; import xiaozhi.modules.model.service.ModelProviderService; @@ -52,6 +48,14 @@ public class ModelController { return new Result>().ok(modelList); } + @GetMapping("/llm/names") + @Operation(summary = "获取LLM模型信息") + @RequiresPermissions("sys:role:normal") + public Result> getLlmModelCodeList(@RequestParam(required = false) String modelName) { + List llmModelCodeList = modelConfigService.getLlmModelCodeList(modelName); + return new Result>().ok(llmModelCodeList); + } + @GetMapping("/{modelType}/provideTypes") @Operation(summary = "获取模型供应器列表") @RequiresPermissions("sys:role:superAdmin") diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java new file mode 100644 index 00000000..3995ceb8 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/dto/LlmModelBasicInfoDTO.java @@ -0,0 +1,13 @@ +package xiaozhi.modules.model.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * LLM的模型的基础展示数据 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class LlmModelBasicInfoDTO extends ModelBasicInfoDTO{ + private String type; +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java index 9d15ac6d..634101e3 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java @@ -4,6 +4,7 @@ import java.util.List; import xiaozhi.common.page.PageData; import xiaozhi.common.service.BaseService; +import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO; import xiaozhi.modules.model.dto.ModelBasicInfoDTO; import xiaozhi.modules.model.dto.ModelConfigBodyDTO; import xiaozhi.modules.model.dto.ModelConfigDTO; @@ -13,6 +14,8 @@ public interface ModelConfigService extends BaseService { List getModelCodeList(String modelType, String modelName); + List getLlmModelCodeList(String modelName); + PageData getPageList(String modelType, String modelName, String page, String limit); ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java index 1f006d93..c156b408 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -23,6 +24,7 @@ import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.agent.dao.AgentDao; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.model.dao.ModelConfigDao; +import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO; import xiaozhi.modules.model.dto.ModelBasicInfoDTO; import xiaozhi.modules.model.dto.ModelConfigBodyDTO; import xiaozhi.modules.model.dto.ModelConfigDTO; @@ -52,6 +54,25 @@ public class ModelConfigServiceImpl extends BaseServiceImpl getLlmModelCodeList(String modelName) { + List entities = modelConfigDao.selectList( + new QueryWrapper() + .eq("model_type", "llm") + .eq("is_enabled", 1) + .like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%") + .select("id", "model_name", "config_json")); + // 处理获取到的内容 + return entities.stream().map(item -> { + LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO(); + dto.setId(item.getId()); + dto.setModelName(item.getModelName()); + String type = item.getConfigJson().get("type").toString(); + dto.setType(type); + return dto; + }).toList(); + } + @Override public PageData getPageList(String modelType, String modelName, String page, String limit) { Map params = new HashMap(); @@ -94,6 +115,21 @@ public class ModelConfigServiceImpl extends BaseServiceImpl() + .eq(ModelConfigEntity::getId, llm)); + String selectModelType = (modelConfigEntity == null || modelConfigEntity.getModelType() == null) ? null + : modelConfigEntity.getModelType().toUpperCase(); + if (modelConfigEntity == null || !"LLM".equals(selectModelType)) { + throw new RenException("设置的LLM不存在"); + } + String type = modelConfigEntity.getConfigJson().get("type").toString(); + // 如果查询大语言模型是openai或者ollama,意图识别选参数都可以 + if (!"openai".equals(type) && !"ollama".equals(type)) { + throw new RenException("设置的LLM不是openai和ollama"); + } + } // 再更新供应器提供的模型 ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class); @@ -137,6 +173,8 @@ public class ModelConfigServiceImpl extends BaseServiceImpl { + RequestService.clearRequestTime(); + callback(res); + }) + .networkFail(() => { + RequestService.reAjaxFun(() => { + this.getLlmModelCodeList(modelName, callback); + }); + }).send(); + }, // 获取模型音色列表 getModelVoices(modelId, voiceName, callback) { const queryParams = new URLSearchParams({ diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index ccd96e1b..bbba01bd 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -89,7 +89,7 @@
-
@@ -130,7 +130,6 @@
- @@ -173,8 +172,9 @@ export default { { label: '视觉大模型(VLLM)', key: 'vllmModelId', type: 'VLLM' }, { label: '意图识别(Intent)', key: 'intentModelId', type: 'Intent' }, { label: '记忆(Memory)', key: 'memModelId', type: 'Memory' }, - { label: '语音合成(TTS)', key: 'ttsModelId', type: 'TTS' }, + { label: '语音合成(TTS)', key: 'ttsModelId', type: 'TTS' } ], + llmModeTypeMap: new Map(), modelOptions: {}, templates: [], loadingTemplate: false, @@ -356,6 +356,9 @@ export default { }); // 备份原始,以备取消时恢复 this.originalFunctions = JSON.parse(JSON.stringify(this.currentFunctions)); + + // 确保意图识别选项的可见性正确 + this.updateIntentOptionsVisibility(); }); } else { this.$message.error(data.msg || '获取配置失败'); @@ -364,16 +367,41 @@ export default { }, fetchModelOptions() { this.models.forEach(model => { - Api.model.getModelNames(model.type, '', ({ data }) => { - if (data.code === 0) { - this.$set(this.modelOptions, model.type, data.data.map(item => ({ - value: item.id, - label: item.modelName - }))); - } else { - this.$message.error(data.msg || '获取模型列表失败'); - } - }); + if (model.type != "LLM") { + Api.model.getModelNames(model.type, '', ({ data }) => { + if (data.code === 0) { + this.$set(this.modelOptions, model.type, data.data.map(item => ({ + value: item.id, + label: item.modelName, + isHidden: false + }))); + + // 如果是意图识别选项,需要根据当前LLM类型更新可见性 + if (model.type === 'Intent') { + this.updateIntentOptionsVisibility(); + } + } else { + this.$message.error(data.msg || '获取模型列表失败'); + } + }); + } else { + Api.model.getLlmModelCodeList('', ({ data }) => { + if (data.code === 0) { + let LLMdata = [] + data.data.forEach(item => { + LLMdata.push({ + value: item.id, + label: item.modelName, + isHidden: false + }) + this.llmModeTypeMap.set(item.id, item.type) + }) + this.$set(this.modelOptions, model.type, LLMdata); + } else { + this.$message.error(data.msg || '获取LLM模型列表失败'); + } + }); + } }); }, fetchVoiceOptions(modelId) { @@ -410,6 +438,10 @@ export default { if (type === 'Memory' && value !== 'Memory_nomem' && (this.form.chatHistoryConf === 0 || this.form.chatHistoryConf === null)) { this.form.chatHistoryConf = 2; } + if (type === 'LLM') { + // 当LLM类型改变时,更新意图识别选项的可见性 + this.updateIntentOptionsVisibility(); + } }, fetchAllFunctions() { return new Promise((resolve, reject) => { @@ -450,6 +482,42 @@ export default { } this.showFunctionDialog = false; }, + updateIntentOptionsVisibility() { + // 根据当前选择的LLM类型更新意图识别选项的可见性 + const currentLlmId = this.form.model.llmModelId; + if (!currentLlmId || !this.modelOptions['Intent']) return; + + const llmType = this.llmModeTypeMap.get(currentLlmId); + if (!llmType) return; + + this.modelOptions['Intent'].forEach(item => { + if (item.value === "Intent_function_call") { + // 如果llmType是openai或ollama,允许选择function_call + // 否则隐藏function_call选项 + if (llmType === "openai" || llmType === "ollama") { + item.isHidden = false; + } else { + item.isHidden = true; + } + } else { + // 其他意图识别选项始终可见 + item.isHidden = false; + } + }); + + // 如果当前选择的意图识别是function_call,但LLM类型不支持,则设置为可选的第一项 + if (this.form.model.intentModelId === "Intent_function_call" && + llmType !== "openai" && llmType !== "ollama") { + // 找到第一个可见的选项 + const firstVisibleOption = this.modelOptions['Intent'].find(item => !item.isHidden); + if (firstVisibleOption) { + this.form.model.intentModelId = firstVisibleOption.value; + } else { + // 如果没有可见选项,设置为Intent_nointent + this.form.model.intentModelId = 'Intent_nointent'; + } + } + }, updateChatHistoryConf() { if (this.form.model.memModelId === 'Memory_nomem') { this.form.chatHistoryConf = 0;