diff --git a/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21-py3-none-any.whl b/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21-py3-none-any.whl new file mode 100644 index 0000000..9b7b395 Binary files /dev/null and b/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21-py3-none-any.whl differ diff --git a/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21.tar.gz b/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21.tar.gz new file mode 100644 index 0000000..809f317 Binary files /dev/null and b/terminal_temi_mcp/dist/terminal_temi_mcp-0.1.21.tar.gz differ diff --git a/terminal_temi_mcp/pyproject.toml b/terminal_temi_mcp/pyproject.toml index 7095216..ac2080e 100644 --- a/terminal_temi_mcp/pyproject.toml +++ b/terminal_temi_mcp/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "terminal_temi_mcp" -version = "0.1.17" +version = "0.1.21" description = "MQTT-based navigation server for robot" requires-python = ">=3.10" dependencies = [ diff --git a/terminal_temi_mcp/terminal_temi_mcp/main.py b/terminal_temi_mcp/terminal_temi_mcp/main.py index b119b8e..1292f37 100644 --- a/terminal_temi_mcp/terminal_temi_mcp/main.py +++ b/terminal_temi_mcp/terminal_temi_mcp/main.py @@ -1,4 +1,4 @@ -from typing import Optional, Sequence +from typing import Optional, Sequence, List import logging from fastapi import FastAPI from pydantic import BaseModel @@ -27,268 +27,179 @@ class NavServer: def __init__(self, mmhandler=None): self.mmhandler = mmhandler or get_mcpmqtt_handler() - async def publish_Cmd(self, device_id: str, cmd: str, params: dict): + async def publish_Cmd(self, device_id: str, user_id: str, cmd: str, params: dict): try: payload = { "device_id": device_id, + "user_id": user_id, "cmd": cmd, "data": params["data"] } self.mmhandler.client.publish("robot/cmd", json.dumps(payload), qos=2) - return f"{cmd}工具执行成功" + return f"{cmd} 任务已经下达完成" except Exception as e: - logger.error(f"Failed to publish {cmd} command: {str(e)}", exc_info=True) - raise + logger.error(f"Failed to publish command: {str(e)}", exc_info=True) + return f"Failed to publish command: {str(e)}" - async def nav_to(self, location: str): - """ - 机器人导航去某个地方。 - Args: - - location (str): 目标地点名称 - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if location: - try: - if location == "充电桩": - location = "home base" + async def nav_to(self, user_id: str, location: str, flag: bool): + """轮足导航到指定位置""" + try: + if not location: + return "Location is not specified." + else: params = { - "data": {"location": location} + "data": {"location": location, "flag": flag} } - return await self.publish_Cmd("123456", "nav", params) - except Exception as e: - logger.error(f"Failed to call navigation mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "nav", params) + except Exception as e: + logger.error(f"Failed to call navigation mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call navigation mcp-tool: {str(e)}" - async def speak(self, speech: str): - """ - 机器人说某些话。 - Args: - - speech (str): 说话内容 - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if speech: - try: + async def speak(self, user_id: str, speech: str): + """轮足机器人语音播报""" + try: + if not speech: + return "Speech content is not specified." + else: params = { "data": {"speech": speech} } - return await self.publish_Cmd("123456", "speak", params) - except Exception as e: - logger.error(f"Failed to call speak mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "speak", params) + except Exception as e: + logger.error(f"Failed to call speak mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call speak mcp-tool: {str(e)}" - async def reception(self, location: str): - """ - 机器人接待客人。 - Args: - - location (str): 机器人接待客人到这个位置 - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if location : - try: + async def reception(self, user_id: str, location: str): + """轮足机器人移动到指定位置迎宾""" + try: + if not location: + return "Location is not specified." + else: params = { "data": { "location": location } } - return await self.publish_Cmd("123456", "reception", params) - except Exception as e: - logger.error(f"Failed to call reception mcp-tool: {str(e)} ", exc_info=True) - raise - - async def notification(self, location: str, text: str): - """ - 机器人通知事情。 - Args: - - location (str): 机器人去这个位置通知事情 - - text (str): 通知的内容 - Returns: - str: 命令执行结果消息 + return await self.publish_Cmd("123456", user_id, "reception", params) + except Exception as e: + logger.error(f"Failed to call reception mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call reception mcp-tool: {str(e)}" - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if location : - try: + async def notification(self, user_id: str, location: str, text: str): + """轮足机器人移动到指定位置通知""" + try: + if not location or not text: + return "Location or text is not specified." + else: params = { "data": { "location": location, "text": text } } - return await self.publish_Cmd("123456", "notification", params) - except Exception as e: - logger.error(f"Failed to call notification mcp-tool: {str(e)} ", exc_info=True) - raise - async def repose(self): - """ - 机器人重新定位。 - """ + return await self.publish_Cmd("123456", user_id, "notification", params) + except Exception as e: + logger.error(f"Failed to call notification mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call notification mcp-tool: {str(e)}" + + async def repose(self, user_id: str): + """轮足机器人重新定位""" try: params = { "data": { "action": "repose" } } - return await self.publish_Cmd("123456", "map", params) + return await self.publish_Cmd("123456", user_id, "map", params) except Exception as e: logger.error(f"Failed to call repose mcp-tool: {str(e)} ", exc_info=True) - raise + return f"Failed to call repose mcp-tool: {str(e)}" - async def delivery(self, first_location: str, next_location: str): - """ - 机器人运送物品。 - Args: - - first_location (str): 机器人去这个位置取物品 - - next_location (str): 机器人将物品送到这个位置 - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - - """ - if first_location and next_location: - try: + async def delivery(self, user_id: str, first_location: str, next_location: str): + """轮足机器人配送""" + try: + if not first_location or not next_location: + return "first_location or next_location is not specified." + else: params = { "data": { "first_location": first_location, "next_location": next_location } } - return await self.publish_Cmd("123456", "delivery", params) - except Exception as e: - logger.error(f"Failed to call delivery mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "delivery", params) + except Exception as e: + logger.error(f"Failed to call delivery mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call delivery mcp-tool: {str(e)}" - async def patrol(self, locations: list[str]): - """ - 机器人去巡逻。 - Args: - - locations (list[str]): 机器人巡逻经过的地点列表 - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if locations: - try: + async def patrol(self, user_id: str, locations: list): + """轮足机器人巡逻""" + try: + if not locations: + return "locations is not specified." + else: params = { "data": {"locations": locations} } - return await self.publish_Cmd("123456", "patrol", params) - except Exception as e: - logger.error(f"Failed to call patrol mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "patrol", params) + except Exception as e: + logger.error(f"Failed to call patrol mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call patrol mcp-tool: {str(e)}" - async def startFaceRecognize(self, device_id: str): - """ - 机器人进行人脸识别。 - Args: - - device_id (str): 设备id - Returns: - str: 命令执行结果消息 - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if device_id: - try: + async def startFaceRecognize(self, device_id: str, user_id: str): + """人脸识别""" + try: + if not device_id: + return "device_id is not specified." + else: params = { "data": {} } - return await self.publish_Cmd("123456", "face", params) - except Exception as e: - logger.error(f"Failed to call face recognize mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "face", params) + except Exception as e: + logger.error(f"Failed to call face recognize mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call face recognize mcp-tool: {str(e)}" - async def scanQRCode(self, device_id: str): - """ - 机器人进行二维码扫描。 - Args: - - device_id (str): 设备id - Returns: - str: 命令执行结果消息 - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ - if device_id: - try: + async def scanQRCode(self, device_id: str, user_id: str): + """扫描二维码""" + try: + if not device_id: + return "device_id is not specified." + else: params = { "data": {} } - return await self.publish_Cmd("123456", "qrcode", params) - except Exception as e: - logger.error(f"Failed to call QR code scan mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "qrcode", params) + except Exception as e: + logger.error(f"Failed to call QR code scan mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call QR code scan mcp-tool: {str(e)}" - async def save_position(self, device_id: str, location_name: str): - """ - 机器人保存具体地点。 - Args: - - device_id (str): 设备id - - location_name (str): 地点名称 - Returns: - str: 命令执行结果消息 - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - - """ - if device_id and location_name: - try: + async def save_position(self, user_id: str, location_name: str): + """保存当前位置""" + try: + if not location_name: + return "location is not specified." + else: params = { "data": {"location_name": location_name} } - return await self.publish_Cmd("123456", "saveLocation", params) - except Exception as e: - logger.error(f"Failed to call save_position mcp-tool: {str(e)} ", exc_info=True) - raise + return await self.publish_Cmd("123456", user_id, "saveLocation", params) + except Exception as e: + logger.error(f"Failed to call save_position mcp-tool: {str(e)} ", exc_info=True) + return f"Failed to call save_position mcp-tool: {str(e)}" - - async def guide(self): - """ - 机器人导览。 - Args: - Returns: - str: 命令执行结果消息 - - Raises: - ValueError: 如果参数无效或为空 - Exception: 如果MQTT发布失败 - """ + async def guide(self, user_id: str, datas: List[TourStop]): + """引导""" try: - datas = [ - TourStop(name="前台", text="欢迎来到我们的场所,这是入口区域"), - TourStop(name="灵泽万川展厅", text="这是我们的主大厅,提供接待服务"), - TourStop(name="爱易拍展厅", text="这里展示我们的主要产品和成果") - ] + if not datas: + return "datas is not specified." params = { "data": [data.model_dump() for data in datas] } - return await self.publish_Cmd("123456", "guide", params) + return await self.publish_Cmd("123456", user_id, "guide", params) except Exception as e: logger.error(f"Failed to call guide mcp-tool: {str(e)} ", exc_info=True) - raise + return f"Failed to call guide mcp-tool: {str(e)}" async def serve() -> None: server = Server("terminal_temi_mcp") @@ -301,7 +212,7 @@ async def serve() -> None: return [ Tool( name="nav_to", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人导航带路到指定地点:带我去、去XX、请引路、导航去、我想去、能带我去吗、带路、送我去、在哪里、前往XX、去XX位置、带我过去、请带我到、我想前往、去那个地方、我要去、能不能去、带我导航、这个地方怎么走、我想找XX、导航带我去、请带路到、我要去那边、走一趟去、去下XX、到XX怎么走", + description="轮足机器人导航到指定地点为用户引路。支持普通引导和通知两种模式。普通引导用于一般带路场景;通知模式下,机器人到达后会播放提示。适用于带路、引路、前往某个位置等场景。触发关键词:带我去、导航、引路、带路、怎么走、在哪里。", inputSchema={ "type": "object", "properties": { @@ -309,14 +220,22 @@ async def serve() -> None: "type": "string", "description": "目标地点名称", "minLength": 1 + }, + "flag": { + "type": "boolean", + "description": "是否为通知类型的导航。True为通知,False为普通引导(默认)。" + }, + "user_id": { + "type": "string", + "description": "用户ID" } }, - "required": ["location"] + "required": ["location", "user_id"] } ), Tool( name="speak", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人进行语音播报:告诉、提醒、告知、提示", + description="轮足机器人进行语音播报:告诉、提醒、告知、提示、通知", inputSchema={ "type": "object", "properties": { @@ -324,14 +243,18 @@ async def serve() -> None: "type": "string", "description": "要播报的语音内容", "minLength": 1 + }, + "user_id": { + "type": "string", + "description": "用户ID" } }, - "required": ["speech"] + "required": ["speech", "user_id"] } ), Tool( name="reception", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人去接待客人:去接人、请迎接客人、去接待、迎接一下、带人过来、把客人带过来、去门口接人、去接一下、请接人、接客人、帮我迎接、去把人接过来、接待来访者、请去接一下客人、送客人过来、从XX接到YY、引导访客、带访客过来、请引领客人、去门口把人接进来、从入口接人、去接待处接人、带客人进来", + description="轮足机器人去接待客人:去接人、请迎接客人、去接待、迎接一下、带人过来、把客人带过来、去门口接人、去接一下、请接人、接客人、帮我迎接、去把人接过来、接待来访者、请去接一下客人、送客人过来、从XX接到YY、引导访客、带访客过来、请引领客人、去门口把人接进来、从入口接人、去接待处接人、带客人进来", inputSchema={ "type": "object", "properties": { @@ -340,42 +263,55 @@ async def serve() -> None: "description": "引导接待客人到这个位置", "minLength": 1 }, + "user_id": { + "type": "string", + "description": "用户ID" + } }, - "required": ["location"] + "required": ["location", "user_id"] } ), + # Tool( + # name="notification", + # description="轮足机器人去通知事情:通知、告诉、提醒、告知、提示", + # inputSchema={ + # "type": "object", + # "properties": { + # "location": { + # "type": "string", + # "description": "去这个位置通知事情", + # "minLength": 1 + # }, + # "text": { + # "type": "string", + # "description": "通知事情的内容", + # "minLength": 1 + # }, + # "user_id": { + # "type": "string", + # "description": "用户ID" + # } + # }, + # "required": ["location","text", "user_id"] + # } + # ), Tool( - name="notification", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人去通知事情:通知、告诉、提醒、告知、提示", + name="repose", + description="轮足机器人、助手、机器人去重新定位", inputSchema={ "type": "object", "properties": { - "location": { + "user_id": { "type": "string", - "description": "去这个位置通知事情", - "minLength": 1 - }, - "text": { - "type": "string", - "description": "通知事情的内容", - "minLength": 1 + "description": "用户ID" } }, - "required": ["location","text"] - } - ), - Tool( - name="repose", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人去重新定位", - inputSchema={ - "type": "object", - "properties": {}, - "required": [] + "required": ["user_id"] } ), Tool( name="delivery", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人去运输物品:帮我送东西、去送一下、去送物品、帮我拿过去、送这个过去、去送快递、运送一下、帮我带东西、把东西送过去、物品配送、拿去给XX、去取个东西、取完送过去、帮我捎个东西、把这个拿去、请运过去、送过去给谁、请配送一下、帮我转交、帮我送到、从XX拿到YY、物品搬运", + description="轮足机器人去运输物品:帮我送东西、去送一下、去送物品、帮我拿过去、送这个过去、去送快递、运送一下、帮我带东西、把东西送过去、物品配送、拿去给XX、去取个东西、取完送过去、帮我捎个东西、把这个拿去、请运过去、送过去给谁、请配送一下、帮我转交、帮我送到、从XX拿到YY、物品搬运", inputSchema={ "type": "object", "properties": { @@ -388,14 +324,18 @@ async def serve() -> None: "type": "string", "description": "将物品运输、送到这个位置", "minLength": 1 + }, + "user_id": { + "type": "string", + "description": "用户ID" } }, - "required": ["first_location","next_location"] + "required": ["first_location","next_location", "user_id"] } ), Tool( name="patrol", - description="数字员工、终端、具身机器人、前台机器人、助手、机器人去巡逻:巡逻、巡查、去检查一下、去看看、去巡视", + description="轮足机器人、助手、机器人去巡逻:巡逻、巡查、去检查一下、去看看、去巡视", inputSchema={ "type": "object", "properties": { @@ -405,24 +345,32 @@ async def serve() -> None: "items": { "type": "string" } + }, + "user_id": { + "type": "string", + "description": "用户ID" } }, - "required": ["locations"] + "required": ["locations", "user_id"] } ), # Tool( # name="guide", - # description="数字员工、终端、具身机器人、前台机器人、助手、机器人进行导览服务:导览、介绍、参观、带领参观、导游、讲解、展示、演示、介绍场所、带我看看", + # description="轮足机器人进行导览服务:导览、介绍、参观、带领参观、导游、讲解、展示、演示、介绍场所、带我看看", # inputSchema={ # "type": "object", # "properties": { + # "user_id": { + # "type": "string", + # "description": "用户ID" + # } # }, - # "required": [] + # "required": ["user_id"] # } # ) # Tool( # name="face", - # description="数字员工、终端、具身机器人、前台机器人、助手、机器人进行人脸识别:识别人脸、人脸识别、脸部识别、面部识别、认脸、看看是谁、识别一下、看看这个人、辨识人脸、扫脸、人脸检测、看脸识别、脸部检测、识别面孔、看人脸、人脸扫描、脸识别、面孔识别、认人、识别身份、看看这是谁、人脸认证、脸部扫描、面部扫描", + # description="轮足机器人进行人脸识别:识别人脸、人脸识别、脸部识别、面部识别、认脸、看看是谁、识别一下、看看这个人、辨识人脸、扫脸、人脸检测、看脸识别、脸部检测、识别面孔、看人脸、人脸扫描、脸识别、面孔识别、认人、识别身份、看看这是谁、人脸认证、脸部扫描、面部扫描", # inputSchema={ # "type": "object", # "properties": { @@ -431,13 +379,17 @@ async def serve() -> None: # "description": "设备ID", # "minLength": 1 # }, + # "user_id": { + # "type": "string", + # "description": "用户ID" + # } # }, - # "required": ["device_id"] + # "required": ["device_id", "user_id"] # } # ), # Tool( # name="qrcode", - # description="数字员工、终端、具身机器人、前台机器人、助手、机器人进行二维码扫描:扫码、扫二维码、扫QR码、二维码识别、码扫描、扫一下码、读取二维码、识别二维码、二维码扫描、QR扫描、扫描条码、扫描编码、读码、解析二维码、扫描识别、码识别、条码扫描、扫描二维码、读取码、扫描条形码、二维码读取、码解析", + # description="轮足机器人进行二维码扫描:扫码、扫二维码、扫QR码、二维码识别、码扫描、扫一下码、读取二维码、识别二维码、二维码扫描、QR扫描、扫描条码、扫描编码、读码、解析二维码、扫描识别、码识别、条码扫描、扫描二维码、读取码、扫描条形码、二维码读取、码解析", # inputSchema={ # "type": "object", # "properties": { @@ -449,9 +401,13 @@ async def serve() -> None: # "task_id": { # "type": "string", # "description": "任务ID" + # }, + # "user_id": { + # "type": "string", + # "description": "用户ID" # } # }, - # "required": ["device_id"] + # "required": ["device_id", "user_id"] # } # ) ] @@ -462,56 +418,67 @@ async def serve() -> None: try: result = "" if name == "nav_to": - if "location" not in arguments: - raise ValueError("缺少必要参数: location") + if "location" not in arguments or "user_id" not in arguments: + raise ValueError("缺少必要参数: location or user_id") result = await nav_server.nav_to( - location=arguments["location"] + user_id=arguments["user_id"], + location=arguments["location"], + flag=arguments.get("flag", False) ) elif name == "speak": - if "speech" not in arguments: - raise ValueError("缺少必要参数: speech") + if "speech" not in arguments or "user_id" not in arguments: + raise ValueError("缺少必要参数: speech or user_id") result = await nav_server.speak( + user_id=arguments["user_id"], speech=arguments["speech"] ) elif name == "reception": - if "location" not in arguments: - raise ValueError("缺少必要参数: location") + if "location" not in arguments or "user_id" not in arguments: + raise ValueError("缺少必要参数: location or user_id") result = await nav_server.reception( - location=arguments["location"], - ) - elif name == "notification": - if "location" not in arguments or "text" not in arguments: - raise ValueError("缺少必要参数: location或text") - result = await nav_server.notification( - location=arguments["location"], - text=arguments["text"] + user_id=arguments["user_id"], + location=arguments["location"] ) + # elif name == "notification": + # if "location" not in arguments or "text" not in arguments or "user_id" not in arguments: + # raise ValueError("缺少必要参数: location、text or user_id") + # result = await nav_server.notification( + # user_id=arguments["user_id"], + # location=arguments["location"], + # text=arguments["text"] + # ) elif name == "repose": - result = await nav_server.repose() + if "user_id" not in arguments: + raise ValueError("缺少必要参数: user_id") + result = await nav_server.repose(user_id=arguments["user_id"]) elif name == "delivery": - if "first_location" not in arguments or "next_location" not in arguments: - raise ValueError("缺少必要参数: first_location或next_location") + if "first_location" not in arguments or "next_location" not in arguments or "user_id" not in arguments: + raise ValueError("缺少必要参数: first_location、next_location or user_id") result = await nav_server.delivery( + user_id=arguments["user_id"], first_location=arguments["first_location"], next_location=arguments["next_location"] ) elif name == "patrol": - if "locations" not in arguments: - raise ValueError("缺少必要参数: locations") + if "locations" not in arguments or "user_id" not in arguments: + raise ValueError("缺少必要参数: locations or user_id") result = await nav_server.patrol( + user_id=arguments["user_id"], locations=arguments["locations"] ) # elif name == "face": - # if "device_id" not in arguments: - # raise ValueError("缺少必要参数: device_id") + # if "device_id" not in arguments or "user_id" not in arguments: + # raise ValueError("缺少必要参数: device_id or user_id") # result = await nav_server.startFaceRecognize( # device_id=arguments["device_id"], + # user_id=arguments["user_id"] # ) # elif name == "qrcode": - # if "device_id" not in arguments: - # raise ValueError("缺少必要参数: device_id") + # if "device_id" not in arguments or "user_id" not in arguments: + # raise ValueError("缺少必要参数: device_id or user_id") # result = await nav_server.scanQRCode( # device_id=arguments["device_id"], + # user_id=arguments["user_id"] # ) else: raise ValueError(f"未知工具: {name}")