nav_copilot.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. from __future__ import annotations
  2. from plugins_func.register import register_function, ToolType, ActionResponse, Action
  3. from plugins_func.nav_copilot.service import get_service
  4. nav_start_record_desc = {
  5. "type": "function",
  6. "function": {
  7. "name": "nav_start_record",
  8. "description": (
  9. "开始录制越野路线(教练/带跑模式)。"
  10. "录制期间服务器会持续读取车载端GPS(GNSS)位置并保存为轨迹。"
  11. "你可以随时再调用 nav_mark_hazard 来标记障碍点/风险点。"
  12. "当用户说“开始录制路线/开始录路线/带我跑一遍”等,优先调用本工具。"
  13. ),
  14. "parameters": {
  15. "type": "object",
  16. "properties": {
  17. "route_name": {"type": "string", "description": "路线名称(可选)"},
  18. "sample_hz": {
  19. "type": "integer",
  20. "description": "采样频率Hz(默认1,建议1~5)",
  21. },
  22. },
  23. "required": [],
  24. },
  25. },
  26. }
  27. @register_function("nav_start_record", nav_start_record_desc, ToolType.SYSTEM_CTL)
  28. async def nav_start_record(conn, route_name: str | None = None, sample_hz: int = 1):
  29. svc = get_service(conn)
  30. rid = await svc.start_recording(route_name=route_name, sample_hz=sample_hz)
  31. text = f"已开始录制路线,路线ID是{rid}。你可以说“标记急转弯/标记大坑/标记障碍”来添加风险点。"
  32. return ActionResponse(action=Action.RESPONSE, result=str(rid), response=text)
  33. nav_stop_record_desc = {
  34. "type": "function",
  35. "function": {
  36. "name": "nav_stop_record",
  37. "description": "结束录制越野路线,并保存路线轨迹。",
  38. "parameters": {"type": "object", "properties": {}, "required": []},
  39. },
  40. }
  41. @register_function("nav_stop_record", nav_stop_record_desc, ToolType.SYSTEM_CTL)
  42. async def nav_stop_record(conn):
  43. svc = get_service(conn)
  44. rid = await svc.stop_recording()
  45. if rid is None:
  46. return ActionResponse(action=Action.RESPONSE, response="当前没有在录制路线。")
  47. text = f"路线录制已结束,路线ID是{rid}。你可以说“开始领航”进行预警播报。"
  48. return ActionResponse(action=Action.RESPONSE, result=str(rid), response=text)
  49. nav_mark_hazard_desc = {
  50. "type": "function",
  51. "function": {
  52. "name": "nav_mark_hazard",
  53. "description": (
  54. "在当前位置标记一个障碍点/风险点(例如急转弯、大坑、跳台、陡坡、涉水、障碍)。"
  55. "用于“带着AI跑一遍”时把赛道风险点记录下来,之后领航时自动提前播报。"
  56. "当用户说“标记急转弯/标记大坑/标记跳台/标记障碍点”等,优先调用本工具。"
  57. ),
  58. "parameters": {
  59. "type": "object",
  60. "properties": {
  61. "hazard_type": {
  62. "type": "string",
  63. "description": "风险点类型",
  64. "enum": [
  65. "sharp_turn",
  66. "bump",
  67. "jump",
  68. "slope",
  69. "water",
  70. "obstacle",
  71. "other",
  72. ],
  73. },
  74. "note": {"type": "string", "description": "补充说明(可选)"},
  75. },
  76. "required": ["hazard_type"],
  77. },
  78. },
  79. }
  80. @register_function("nav_mark_hazard", nav_mark_hazard_desc, ToolType.SYSTEM_CTL)
  81. async def nav_mark_hazard(conn, hazard_type: str, note: str | None = None):
  82. svc = get_service(conn)
  83. hid = await svc.mark_hazard(hazard_type=hazard_type, note=note or "")
  84. text = "已标记风险点。"
  85. return ActionResponse(action=Action.RESPONSE, result=str(hid), response=text)
  86. nav_start_run_desc = {
  87. "type": "function",
  88. "function": {
  89. "name": "nav_start_run",
  90. "description": (
  91. "开始领航播报(预警模式)。服务器会持续读取GPS定位,匹配到已录制路线,"
  92. "在接近前方障碍点时自动语音播报提示。"
  93. "当用户说“开始领航/开始预警/开始播报”等,优先调用本工具。"
  94. ),
  95. "parameters": {
  96. "type": "object",
  97. "properties": {
  98. "route_id": {"type": "integer", "description": "指定路线ID(可选)"},
  99. "tick_hz": {
  100. "type": "integer",
  101. "description": "领航刷新频率Hz(默认1,建议1~10;频率越高越实时,但更耗资源)",
  102. },
  103. },
  104. "required": [],
  105. },
  106. },
  107. }
  108. @register_function("nav_start_run", nav_start_run_desc, ToolType.SYSTEM_CTL)
  109. async def nav_start_run(conn, route_id: int | None = None, tick_hz: int = 5):
  110. svc = get_service(conn)
  111. rid = await svc.start_run(route_id=route_id, tick_hz=tick_hz)
  112. text = f"已开始领航,使用路线ID={rid}。接近风险点时我会自动提醒。"
  113. return ActionResponse(action=Action.RESPONSE, result=str(rid), response=text)
  114. nav_stop_run_desc = {
  115. "type": "function",
  116. "function": {
  117. "name": "nav_stop_run",
  118. "description": "停止领航播报(预警模式)。",
  119. "parameters": {"type": "object", "properties": {}, "required": []},
  120. },
  121. }
  122. @register_function("nav_stop_run", nav_stop_run_desc, ToolType.SYSTEM_CTL)
  123. async def nav_stop_run(conn):
  124. svc = get_service(conn)
  125. rid = await svc.stop_run()
  126. if rid is None:
  127. return ActionResponse(action=Action.RESPONSE, response="当前没有在领航。")
  128. return ActionResponse(action=Action.RESPONSE, result=str(rid), response="领航已停止。")
  129. nav_status_desc = {
  130. "type": "function",
  131. "function": {
  132. "name": "nav_status",
  133. "description": "查询路线录制/领航播报的当前状态。",
  134. "parameters": {"type": "object", "properties": {}, "required": []},
  135. },
  136. }
  137. @register_function("nav_status", nav_status_desc, ToolType.SYSTEM_CTL)
  138. async def nav_status(conn):
  139. svc = get_service(conn)
  140. return ActionResponse(action=Action.RESPONSE, response=svc.status_text())
  141. nav_list_routes_desc = {
  142. "type": "function",
  143. "function": {
  144. "name": "nav_list_routes",
  145. "description": "列出最近保存的路线(含点数和障碍点数量)。",
  146. "parameters": {
  147. "type": "object",
  148. "properties": {"limit": {"type": "integer", "description": "数量(默认10)"}},
  149. "required": [],
  150. },
  151. },
  152. }
  153. @register_function("nav_list_routes", nav_list_routes_desc, ToolType.SYSTEM_CTL)
  154. async def nav_list_routes(conn, limit: int = 10):
  155. svc = get_service(conn)
  156. routes = svc.db.list_routes(limit=int(limit or 10))
  157. if not routes:
  158. return ActionResponse(action=Action.RESPONSE, response="暂无路线。先说“开始录制路线”。")
  159. lines = []
  160. for r in routes:
  161. active = "(激活)" if r.active else ""
  162. lines.append(f"路线{r.id}{active}:{r.name},点{r.points},风险点{r.hazards}")
  163. return ActionResponse(action=Action.RESPONSE, response=";".join(lines))
  164. nav_set_active_route_desc = {
  165. "type": "function",
  166. "function": {
  167. "name": "nav_set_active_route",
  168. "description": "设置某条路线为当前激活路线(领航默认使用激活路线)。",
  169. "parameters": {
  170. "type": "object",
  171. "properties": {"route_id": {"type": "integer", "description": "路线ID"}},
  172. "required": ["route_id"],
  173. },
  174. },
  175. }
  176. @register_function("nav_set_active_route", nav_set_active_route_desc, ToolType.SYSTEM_CTL)
  177. async def nav_set_active_route(conn, route_id: int):
  178. svc = get_service(conn)
  179. svc.db.set_active_route(int(route_id))
  180. return ActionResponse(action=Action.RESPONSE, response=f"已激活路线ID={int(route_id)}。")