diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java index c9275f92..bfd82e41 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java @@ -1,6 +1,5 @@ package xiaozhi.modules.agent.controller; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.UUID; @@ -33,7 +32,6 @@ import xiaozhi.common.page.PageData; import xiaozhi.common.redis.RedisKeys; import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.user.UserDetail; -import xiaozhi.common.utils.ConvertUtils; import xiaozhi.common.utils.Result; import xiaozhi.common.utils.ResultUtils; import xiaozhi.modules.agent.dto.AgentChatHistoryDTO; @@ -46,6 +44,7 @@ import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentTemplateEntity; import xiaozhi.modules.agent.service.AgentChatAudioService; import xiaozhi.modules.agent.service.AgentChatHistoryService; +import xiaozhi.modules.agent.service.AgentPluginMappingService; import xiaozhi.modules.agent.service.AgentService; import xiaozhi.modules.agent.service.AgentTemplateService; import xiaozhi.modules.agent.vo.AgentInfoVO; @@ -63,6 +62,7 @@ public class AgentController { private final DeviceService deviceService; private final AgentChatHistoryService agentChatHistoryService; private final AgentChatAudioService agentChatAudioService; + private final AgentPluginMappingService agentPluginMappingService; private final RedisUtils redisUtils; @GetMapping("/list") @@ -99,37 +99,8 @@ public class AgentController { @Operation(summary = "创建智能体") @RequiresPermissions("sys:role:normal") public Result save(@RequestBody @Valid AgentCreateDTO dto) { - AgentEntity entity = ConvertUtils.sourceToTarget(dto, AgentEntity.class); - - // 获取默认模板 - AgentTemplateEntity template = agentTemplateService.getDefaultTemplate(); - if (template != null) { - // 设置模板中的默认值 - entity.setAsrModelId(template.getAsrModelId()); - entity.setVadModelId(template.getVadModelId()); - entity.setLlmModelId(template.getLlmModelId()); - entity.setVllmModelId(template.getVllmModelId()); - entity.setTtsModelId(template.getTtsModelId()); - entity.setTtsVoiceId(template.getTtsVoiceId()); - entity.setMemModelId(template.getMemModelId()); - entity.setIntentModelId(template.getIntentModelId()); - entity.setSystemPrompt(template.getSystemPrompt()); - entity.setSummaryMemory(template.getSummaryMemory()); - entity.setChatHistoryConf(template.getChatHistoryConf()); - entity.setLangCode(template.getLangCode()); - entity.setLanguage(template.getLanguage()); - } - - // 设置用户ID和创建者信息 - UserDetail user = SecurityUser.getUser(); - entity.setUserId(user.getId()); - entity.setCreator(user.getId()); - entity.setCreatedAt(new Date()); - - // ID、智能体编码和排序会在Service层自动生成 - agentService.insert(entity); - - return new Result().ok(entity.getId()); + String agentId = agentService.createAgent(dto); + return new Result().ok(agentId); } @PutMapping("/saveMemory/{macAddress}") @@ -161,6 +132,8 @@ public class AgentController { deviceService.deleteByAgentId(id); // 删除关联的聊天记录 agentChatHistoryService.deleteByAgentId(id, true, true); + // 删除关联的插件 + agentPluginMappingService.deleteByAgentId(id); // 再删除智能体 agentService.deleteById(id); return new Result<>(); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java index d0597590..ae99b0eb 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentPluginMapping.java @@ -1,20 +1,22 @@ package xiaozhi.modules.agent.entity; +import java.io.Serializable; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import java.io.Serializable; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** * Agent与插件的唯一映射表 + * * @TableName ai_agent_plugin_mapping */ @Data -@TableName(value ="ai_agent_plugin_mapping") +@TableName(value = "ai_agent_plugin_mapping") @Schema(description = "Agent与插件的唯一映射表") public class AgentPluginMapping implements Serializable { /** @@ -37,12 +39,11 @@ public class AgentPluginMapping implements Serializable { private String pluginId; /** - * 插件参数(Json)格式 - */ + * 插件参数(Json)格式 + */ @Schema(description = "插件参数(Json)格式") private String paramInfo; - // 冗余字段,用于方便在根据id查询插件时,对照查出插件的Provider_code,详见dao层xml文件 @TableField(exist = false) @Schema(description = "插件provider_code, 对应表ai_model_provider") diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java index c2de4ea6..79d97d2e 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentPluginMappingService.java @@ -1,15 +1,29 @@ package xiaozhi.modules.agent.service; -import xiaozhi.modules.agent.entity.AgentPluginMapping; -import com.baomidou.mybatisplus.extension.service.IService; - import java.util.List; +import com.baomidou.mybatisplus.extension.service.IService; + +import xiaozhi.modules.agent.entity.AgentPluginMapping; + /** -* @description 针对表【ai_agent_plugin_mapping(Agent与插件的唯一映射表)】的数据库操作Service -* @createDate 2025-05-25 22:33:17 -*/ + * @description 针对表【ai_agent_plugin_mapping(Agent与插件的唯一映射表)】的数据库操作Service + * @createDate 2025-05-25 22:33:17 + */ public interface AgentPluginMappingService extends IService { + /** + * 根据智能体id获取插件参数 + * + * @param agentId + * @return + */ List agentPluginParamsByAgentId(String agentId); + + /** + * 根据智能体id删除插件参数 + * + * @param agentId + */ + void deleteByAgentId(String agentId); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java index 990ac61e..a9d4e5bb 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java @@ -5,6 +5,7 @@ import java.util.Map; import xiaozhi.common.page.PageData; import xiaozhi.common.service.BaseService; +import xiaozhi.modules.agent.dto.AgentCreateDTO; import xiaozhi.modules.agent.dto.AgentDTO; import xiaozhi.modules.agent.dto.AgentUpdateDTO; import xiaozhi.modules.agent.entity.AgentEntity; @@ -82,5 +83,19 @@ public interface AgentService extends BaseService { */ boolean checkAgentPermission(String agentId, Long userId); + /** + * 更新智能体 + * + * @param agentId 智能体ID + * @param dto 更新智能体所需的信息 + */ void updateAgentById(String agentId, AgentUpdateDTO dto); + + /** + * 创建智能体 + * + * @param dto 创建智能体所需的信息 + * @return 创建的智能体ID + */ + String createAgent(AgentCreateDTO dto); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java index 5f62e71f..2a53d474 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentPluginMappingServiceImpl.java @@ -4,6 +4,7 @@ import java.util.List; import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; @@ -26,4 +27,11 @@ public class AgentPluginMappingServiceImpl extends ServiceImpl updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("agent_id", agentId); + agentPluginMappingMapper.delete(updateWrapper); + } + } 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 003c3eb3..36aa1192 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 @@ -1,6 +1,8 @@ package xiaozhi.modules.agent.service.impl; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -23,18 +25,24 @@ import xiaozhi.common.redis.RedisKeys; import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.ConvertUtils; import xiaozhi.common.utils.JsonUtils; import xiaozhi.modules.agent.dao.AgentDao; +import xiaozhi.modules.agent.dto.AgentCreateDTO; import xiaozhi.modules.agent.dto.AgentDTO; import xiaozhi.modules.agent.dto.AgentUpdateDTO; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentPluginMapping; +import xiaozhi.modules.agent.entity.AgentTemplateEntity; import xiaozhi.modules.agent.service.AgentChatHistoryService; import xiaozhi.modules.agent.service.AgentPluginMappingService; import xiaozhi.modules.agent.service.AgentService; +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.service.ModelConfigService; +import xiaozhi.modules.model.service.ModelProviderService; import xiaozhi.modules.security.user.SecurityUser; import xiaozhi.modules.sys.enums.SuperAdminEnum; import xiaozhi.modules.timbre.service.TimbreService; @@ -49,6 +57,8 @@ public class AgentServiceImpl extends BaseServiceImpl imp private final DeviceService deviceService; private final AgentPluginMappingService agentPluginMappingService; private final AgentChatHistoryService agentChatHistoryService; + private final AgentTemplateService agentTemplateService; + private final ModelProviderService modelProviderService; @Override public PageData adminAgentList(Map params) { @@ -250,7 +260,6 @@ public class AgentServiceImpl extends BaseServiceImpl imp List functions = dto.getFunctions(); if (functions != null) { // 1. 收集本次提交的 pluginId - // TODO: 提交的参数信息里,这里是允许为空,后续可以扩展required字段限制避免为空 List newPluginIds = functions.stream() .map(AgentUpdateDTO.FunctionInfo::getPluginId) .toList(); @@ -317,4 +326,67 @@ public class AgentServiceImpl extends BaseServiceImpl imp } this.updateById(existingEntity); } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createAgent(AgentCreateDTO dto) { + // 转换为实体 + AgentEntity entity = ConvertUtils.sourceToTarget(dto, AgentEntity.class); + + // 获取默认模板 + AgentTemplateEntity template = agentTemplateService.getDefaultTemplate(); + if (template != null) { + // 设置模板中的默认值 + entity.setAsrModelId(template.getAsrModelId()); + entity.setVadModelId(template.getVadModelId()); + entity.setLlmModelId(template.getLlmModelId()); + entity.setVllmModelId(template.getVllmModelId()); + entity.setTtsModelId(template.getTtsModelId()); + entity.setTtsVoiceId(template.getTtsVoiceId()); + entity.setMemModelId(template.getMemModelId()); + entity.setIntentModelId(template.getIntentModelId()); + entity.setSystemPrompt(template.getSystemPrompt()); + entity.setSummaryMemory(template.getSummaryMemory()); + entity.setChatHistoryConf(template.getChatHistoryConf()); + entity.setLangCode(template.getLangCode()); + entity.setLanguage(template.getLanguage()); + } + + // 设置用户ID和创建者信息 + UserDetail user = SecurityUser.getUser(); + entity.setUserId(user.getId()); + entity.setCreator(user.getId()); + entity.setCreatedAt(new Date()); + + // 保存智能体 + insert(entity); + + // 设置默认插件 + List toInsert = new ArrayList<>(); + // 播放音乐、查天气、查新闻 + String[] pluginIds = new String[] { "SYSTEM_PLUGIN_MUSIC", "SYSTEM_PLUGIN_WEATHER", + "SYSTEM_PLUGIN_NEWS_NEWSNOW" }; + for (String pluginId : pluginIds) { + ModelProviderDTO provider = modelProviderService.getById(pluginId); + if (provider == null) { + continue; + } + AgentPluginMapping mapping = new AgentPluginMapping(); + mapping.setPluginId(pluginId); + + Map paramInfo = new HashMap<>(); + List> fields = JsonUtils.parseObject(provider.getFields(), List.class); + if (fields != null) { + for (Map field : fields) { + paramInfo.put((String) field.get("key"), field.get("default")); + } + } + mapping.setParamInfo(JsonUtils.toJsonString(paramInfo)); + mapping.setAgentId(entity.getId()); + toInsert.add(mapping); + } + // 保存默认插件 + agentPluginMappingService.saveBatch(toInsert); + return entity.getId(); + } } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java index 92a8da80..829f86a8 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelProviderService.java @@ -8,10 +8,10 @@ import xiaozhi.modules.model.dto.ModelProviderDTO; public interface ModelProviderService { - // List getModelNames(String modelType, String modelName); - List getPluginList(); + ModelProviderDTO getById(String id); + List getPluginListByIds(Collection ids); List getListByModelType(String modelType); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java index 392454a0..ccab6256 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelProviderServiceImpl.java @@ -1,11 +1,15 @@ package xiaozhi.modules.model.service.impl; -import java.util.*; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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; @@ -38,6 +42,12 @@ public class ModelProviderServiceImpl extends BaseServiceImpl getPluginListByIds(Collection ids) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -47,7 +57,6 @@ public class ModelProviderServiceImpl extends BaseServiceImpl getListByModelType(String modelType) { diff --git a/main/manager-api/src/main/resources/db/changelog/202505292203.sql b/main/manager-api/src/main/resources/db/changelog/202505292203.sql index c61fa24a..4102c7fa 100644 --- a/main/manager-api/src/main/resources/db/changelog/202505292203.sql +++ b/main/manager-api/src/main/resources/db/changelog/202505292203.sql @@ -43,6 +43,16 @@ VALUES ('SYSTEM_PLUGIN_WEATHER', ), 10, 0, NOW(), 0, NOW()); +-- 6. 本地播放音乐 +INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, + sort, creator, create_date, updater, update_date) +VALUES ('SYSTEM_PLUGIN_MUSIC', + 'Plugin', + 'play_music', + '服务器音乐播放', + JSON_ARRAY(), + 20, 0, NOW(), 0, NOW()); + -- 2. 新闻订阅 INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, sort, creator, create_date, updater, update_date) @@ -80,7 +90,7 @@ VALUES ('SYSTEM_PLUGIN_NEWS_CHINANEWS', 'https://www.chinanews.com.cn/rss/finance.xml' ) ), - 20, 0, NOW(), 0, NOW()); + 30, 0, NOW(), 0, NOW()); -- 3. 新闻订阅 INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, @@ -98,7 +108,8 @@ VALUES ('SYSTEM_PLUGIN_NEWS_NEWSNOW', 'https://newsnow.busiyi.world/api/s?id=' ) ), - 20, 0, NOW(), 0, NOW()); + 40, 0, NOW(), 0, NOW()); + -- 4. HomeAssistant 状态查询 INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, @@ -106,7 +117,7 @@ INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, VALUES ('SYSTEM_PLUGIN_HA_GET_STATE', 'Plugin', 'hass_get_state', - 'HomeAssistant 状态查询', + 'HomeAssistant设备状态查询', JSON_ARRAY( JSON_OBJECT( 'key', 'base_url', @@ -130,7 +141,7 @@ VALUES ('SYSTEM_PLUGIN_HA_GET_STATE', (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.devices') ) ), - 30, 0, NOW(), 0, NOW()); + 50, 0, NOW(), 0, NOW()); -- 5. HomeAssistant 状态写入 INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, @@ -138,41 +149,19 @@ INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, VALUES ('SYSTEM_PLUGIN_HA_SET_STATE', 'Plugin', 'hass_set_state', - 'HomeAssistant 状态写入', - JSON_ARRAY( - JSON_OBJECT( - 'key', 'base_url', - 'type', 'string', - 'label', 'HA 服务器地址', - 'default', - (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.base_url') - ), - JSON_OBJECT( - 'key', 'api_key', - 'type', 'string', - 'label', 'HA API 访问令牌', - 'default', - (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.api_key') - ), - JSON_OBJECT( - 'key', 'devices', - 'type', 'array', - 'label', '设备列表(名称,实体ID;…)', - 'default', - (SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.devices') - ) - ), - 40, 0, NOW(), 0, NOW()); + 'HomeAssistant设备状态修改', + JSON_ARRAY(), + 60, 0, NOW(), 0, NOW()); --- 6. 本地播放音乐 +-- 5. HomeAssistant 音乐播放 INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields, sort, creator, create_date, updater, update_date) -VALUES ('SYSTEM_PLUGIN_MUSIC', +VALUES ('SYSTEM_PLUGIN_HA_PLAY_MUSIC', 'Plugin', - 'play_music', - '本地音乐播放', + 'hass_play_music', + 'HomeAssistant音乐播放', JSON_ARRAY(), - 50, 0, NOW(), 0, NOW()); + 70, 0, NOW(), 0, NOW()); -- =============================== diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index a641960e..8c669864 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -445,14 +445,17 @@ class ConnectionHandler: if private_config.get("Intent", None) is not None: init_intent = True self.config["Intent"] = private_config["Intent"] - model_intent = private_config.get('selected_module', {}).get('Intent', {}) + model_intent = private_config.get("selected_module", {}).get("Intent", {}) self.config["selected_module"]["Intent"] = model_intent # 加载插件配置 - if model_intent != 'Intent_nointent': + if model_intent != "Intent_nointent": plugin_from_server = private_config.get("plugins", {}) for plugin, config_str in plugin_from_server.items(): plugin_from_server[plugin] = json.loads(config_str) - self.config['plugins'] = plugin_from_server + self.config["plugins"] = plugin_from_server + self.config["Intent"][self.config["selected_module"]["Intent"]][ + "functions" + ] = plugin_from_server.keys() if private_config.get("prompt", None) is not None: self.config["prompt"] = private_config["prompt"] if private_config.get("summaryMemory", None) is not None: diff --git a/main/xiaozhi-server/core/handle/functionHandler.py b/main/xiaozhi-server/core/handle/functionHandler.py index c5e438ad..bb58ead9 100644 --- a/main/xiaozhi-server/core/handle/functionHandler.py +++ b/main/xiaozhi-server/core/handle/functionHandler.py @@ -61,7 +61,6 @@ class FunctionHandler: self.function_registry.register_function("plugin_loader") self.function_registry.register_function("get_time") self.function_registry.register_function("get_lunar") - # self.function_registry.register_function("handle_speaker_volume_or_screen_brightness") def register_config_functions(self): """注册配置中的函数,可以不同客户端使用不同的配置""" diff --git a/main/xiaozhi-server/core/providers/memory/base.py b/main/xiaozhi-server/core/providers/memory/base.py index f404f15e..2ced898d 100644 --- a/main/xiaozhi-server/core/providers/memory/base.py +++ b/main/xiaozhi-server/core/providers/memory/base.py @@ -12,10 +12,6 @@ class MemoryProviderBase(ABC): def set_llm(self, llm): self.llm = llm - # 获取模型名称和类型信息 - model_name = getattr(llm, "model_name", str(llm.__class__.__name__)) - # 记录更详细的日志 - logger.bind(tag=TAG).info(f"记忆总结设置LLM: {model_name}") @abstractmethod async def save_memory(self, msgs): diff --git a/main/xiaozhi-server/plugins_func/functions/handle_speaker_or_screen.py b/main/xiaozhi-server/plugins_func/functions/handle_speaker_or_screen.py deleted file mode 100644 index f89f30b0..00000000 --- a/main/xiaozhi-server/plugins_func/functions/handle_speaker_or_screen.py +++ /dev/null @@ -1,157 +0,0 @@ -from config.logger import setup_logging -from plugins_func.register import register_function, ToolType, ActionResponse, Action -from core.handle.iotHandle import get_iot_status, send_iot_conn -import asyncio - -TAG = __name__ -logger = setup_logging() - - -async def _get_device_status(conn, device_name, device_type, property_name): - """获取设备状态""" - status = await get_iot_status(conn, device_type, property_name) - if status is None: - raise Exception(f"你的设备不支持{device_name}控制") - return status - - -async def _set_device_property( - conn, - device_name, - device_type, - method_name, - property_name, - new_value=None, - action=None, - step=10, -): - """设置设备属性""" - current_value = await _get_device_status( - conn, device_name, device_type, property_name - ) - - if action == "raise": - current_value += step - elif action == "lower": - current_value -= step - elif action == "set": - if new_value is None: - raise Exception(f"缺少{property_name}参数") - current_value = new_value - - # 限制属性范围在0到100之间 - current_value = max(0, min(100, current_value)) - - await send_iot_conn(conn, device_type, method_name, {property_name: current_value}) - return current_value - - -def _handle_device_action(conn, func, success_message, error_message, *args, **kwargs): - """处理设备操作的通用函数""" - future = asyncio.run_coroutine_threadsafe(func(conn, *args, **kwargs), conn.loop) - try: - result = future.result() - logger.bind(tag=TAG).info(f"{success_message}: {result}") - response = f"{success_message}{result}" - return ActionResponse(action=Action.RESPONSE, result=result, response=response) - except Exception as e: - logger.bind(tag=TAG).error(f"{error_message}: {e}") - response = f"{error_message}: {e}" - return ActionResponse(action=Action.RESPONSE, result=None, response=response) - - -# 设备控制 -handle_device_function_desc = { - "type": "function", - "function": { - "name": "handle_speaker_volume_or_screen_brightness", - "description": ( - "用户想要获取或者设置设备的音量/亮度大小,或者用户觉得声音/亮度过高或过低,或者用户想提高或降低音量/亮度。\n" - "**严格限制**:仅当用户明确操作 **Speaker(音量)或Screen(亮度)** 时才能调用此函数!\n" - "对于其他设备(如AC、Battery、Switch等),请不要调用此函数,而是继续正常的对话。\n\n" - "示例:\n" - "- 用户说『现在亮度多少』 → 调用函数:device_type: Screen, action: get\n" - "- 用户说『设置音量为50』 → 调用函数:device_type: Speaker, action: set, value: 50\n" - "- 用户说『亮度太高了』 → 调用函数:device_type: Screen, action: lower\n" - "- 用户说『调大音量』 → 调用函数:device_type: Speaker, action: raise\n\n" - "**拒绝调用示例**(应继续对话而非调用本函数):\n" - "- 用户说『空调调低一度』 → 不调用(设备类型为AC)\n" - "- 用户说『开关灯』 → 不调用(设备类型为Switch)\n" - "- 用户说『电量多少』 → 不调用(设备类型为Battery)\n" - ), - "parameters": { - "type": "object", - "properties": { - "device_type": { - "type": "string", - "description": "设备类型,**严格限定为Speaker(音量)或Screen(亮度)**,其他设备类型禁止调用此函数", - "enum": ["Speaker", "Screen"], - }, - "action": { - "type": "string", - "description": "动作名称,可选值:get(获取),set(设置),raise(提高),lower(降低)", - }, - "value": { - "type": "integer", - "description": "值大小,可选值:0-100之间的整数", - }, - }, - "required": ["device_type", "action"], - }, - }, -} - - -@register_function( - "handle_speaker_volume_or_screen_brightness", - handle_device_function_desc, - ToolType.IOT_CTL, -) -def handle_speaker_volume_or_screen_brightness( - conn, device_type: str, action: str, value: int = None -): - # 检查value是否为中文值 - if ( - value is not None - and isinstance(value, str) - and any("\u4e00" <= char <= "\u9fff" for char in str(value)) - ): - raise Exception( - f"请直接告诉我要将{'音量' if device_type=='Speaker' else '亮度'}调整成多少" - ) - - if device_type == "Speaker": - method_name, property_name, device_name = "SetVolume", "volume", "音量" - elif device_type == "Screen": - method_name, property_name, device_name = "SetBrightness", "brightness", "亮度" - else: - raise Exception(f"未识别的设备类型: {device_type}") - - if action not in ["get", "set", "raise", "lower"]: - raise Exception(f"未识别的动作名称: {action}") - - if action == "get": - # get - return _handle_device_action( - conn, - _get_device_status, - f"当前{device_name}", - f"获取{device_name}失败", - device_name=device_name, - device_type=device_type, - property_name=property_name, - ) - else: - # set, raise, lower - return _handle_device_action( - conn, - _set_device_property, - f"{device_name}已调整到", - f"{device_name}调整失败", - device_name=device_name, - device_type=device_type, - method_name=method_name, - property_name=property_name, - new_value=value, - action=action, - )