vent_tools.py 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280
  1. # -*- coding: utf-8 -*-
  2. """
  3. DeepAgents 工具函数模块
  4. 所有工具函数供 DeepAgent 调用,通过 MCP 客户端获取模拟数据。
  5. 工具函数返回结构化 JSON 文本,由 LLM 解析并生成自然语言回复。
  6. 工具列表(共 56 个):
  7. 【数据查询(17个)】
  8. - query_device_data: 查询设备实时数据
  9. - query_device_data_by_id: 根据设备ID查询实时数据和报警数据
  10. - query_devices_by_tunnel: 根据巷道名称查询绑定设备
  11. - query_devices_by_tunnel_id: 根据巷道ID查询绑定设备
  12. - query_devices_by_model: 通过模型ID查询绑定设备
  13. - get_tun_list_by_modelid: 通过模型ID获取巷道列表(旧接口)
  14. - query_tunnels_by_model: 根据模型ID查询巷道列表(新接口)
  15. - query_tun_data_by_id: 根据巷道ID查询实时监测数据
  16. - query_tunnel_list: 按名称模糊搜索巷道
  17. - query_knowledge_base: 查询煤矿安全知识库
  18. - get_needq_all_data: 获取全部需风量数据
  19. - list_ventanaly_monitor_data_days: 查询监测历史时序数据
  20. - get_device_kind_dict: 查询设备大类小类全量字典
  21. - get_device_list_by_kind: 根据设备类型查询设备列表
  22. - query_device_realtime_data: 查询设备实时监测数据
  23. - get_dict_list_by_dictcode: 根据字典编码查询字典项
  24. 【需风量 / 模型(7个)】
  25. - get_model_param_pub_list: 查询默认模型参数列表
  26. - get_model_wind: 获取巷道解算风量
  27. - get_sensor_wind: 获取设备监测风量
  28. - simulate_needq_heading_face: 掘进面需风量模拟计算
  29. - simulate_needq_room: 硐室需风量模拟计算
  30. - simulate_needq_ret_work_face: 采煤工作面需风量模拟计算
  31. - simulate_needq_other: 其他用风地点需风量模拟计算
  32. 【设备信息(3个)】
  33. - query_device_info: 根据设备大类/小类查询设备信息
  34. - query_device_type_info: 根据设备类型编码查询类型信息
  35. - query_monitor_params: 查询监测点表参数
  36. 【故障诊断(5个)】
  37. - check_model_connect_status: 模型网络连通检查
  38. - check_model_one_dir_cycle: 模型循环风路检查
  39. - check_model_one_dir_node: 模型单向节点检查
  40. - check_model_diagonal_structure: 模型角联结构诊断
  41. - get_model_fault_diagnosis: 模型故障诊断(聚合)
  42. 【避灾路线(2个)】
  43. - get_escape_path: 避灾路线模拟
  44. - get_escape_path_each_exit: 避灾路线模拟(到各出口)
  45. 【关键阻力 / 压能(6个)】
  46. - get_out_shafts: 获取回风井列表
  47. - get_in_shafts: 获取进风井列表
  48. - get_max_resistance_path: 最大阻力路线
  49. - get_three_area_distribution: 三区阻力分布
  50. - get_key_path_decision: 关键路径控风决策
  51. - get_path_press_power: 节点压能图
  52. 【网络解算(2个)】
  53. - net_cal: 网络解算
  54. - net_cal_for_plan: 方案模拟解算
  55. 【报警 / 日志(3个)】
  56. - get_alarm_log_history: 设备设施报警历史
  57. - get_device_set_log_history: 设备设施控制历史
  58. - get_sys_log_history: 系统登录人员历史
  59. 【场景管理(2个)】
  60. - get_manage_system_by_strType: 通过场景类型获取场景列表
  61. - query_system_by_systemID: 根据场景ID获取场景数据
  62. 【煤矿基础(2个)】
  63. - get_gas_identify_vo: 根据矿井名称获取瓦斯等级鉴定报告
  64. - get_by_mine_name: 根据矿井名称查询工作面设计规程
  65. - query_control_testWind: 测风装置一键测风
  66. 【数据库 / 文件(3个)】
  67. - execute_sql_query: 从 vent 库执行 SQL 查询
  68. - get_file_list_by_type: 按业务类型获取文件列表
  69. - get_file_base64_by_id: 通过文件ID获取文件Base64
  70. 【报表(1个)】
  71. - get_latest_report: 获取最新测风报表
  72. 【用户偏好(3个)】
  73. - save_user_preference: 保存用户偏好
  74. - list_user_preferences: 查看用户偏好
  75. - delete_user_preference: 删除用户偏好
  76. 【计划审批(1个)】
  77. - request_plan_approval: 提交执行计划审批
  78. """
  79. import json
  80. import contextvars
  81. from fastmcp import Client
  82. from tools.mcp_logger import log_mcp_call
  83. # ============================================================
  84. # 当前用户上下文(供工具函数获取调用者身份)
  85. # ============================================================
  86. _current_user: contextvars.ContextVar[str] = contextvars.ContextVar(
  87. 'current_user', default='admin'
  88. )
  89. # 当前会话 ID(供工具函数获取本轮会话上下文)
  90. _current_session: contextvars.ContextVar[str] = contextvars.ContextVar(
  91. 'current_session', default=''
  92. )
  93. # ============================================================
  94. # MCP 客户端辅助函数
  95. # ============================================================
  96. def _get_mcp_url() -> str:
  97. """获取 MCP 服务 URL。
  98. 从 src/.env 读取 MCP_BASE_URL 配置,拼接 /mcp 路径。
  99. """
  100. from dotenv import dotenv_values
  101. from pathlib import Path as _Path
  102. _env_file = _Path(__file__).parent.parent / ".env" # src/.env
  103. _cfg = dotenv_values(str(_env_file))
  104. mcp_url = _cfg.get("MCP_BASE_URL", "http://localhost:8100").rstrip("/")
  105. return f"{mcp_url}/mcp"
  106. async def _call_mcp_tool(tool_name: str, arguments: dict) -> str:
  107. """通过 FastMCP 异步客户端调用远程 MCP 工具。
  108. 参考 xfl_demo_client.py 的实现方式,使用 fastmcp.Client 异步上下文管理器。
  109. """
  110. mcp_url = _get_mcp_url()
  111. try:
  112. client = Client(mcp_url)
  113. async with client:
  114. result = await client.call_tool(tool_name, arguments)
  115. raw = result.content[0].text
  116. log_mcp_call(tool_name, arguments, raw)
  117. return raw
  118. except Exception as e:
  119. log_mcp_call(tool_name, arguments, "", error=str(e))
  120. return json.dumps({"error": f"MCP调用失败: {e}", "tool": tool_name}, ensure_ascii=False)
  121. # ============================================================
  122. # DeepAgent 工具函数(供 Agent 调用)
  123. # ============================================================
  124. async def query_device_data(device_id: str = "") -> str:
  125. """查询设备实时监测数据。
  126. 当需要获取设备实时数据时调用此工具。
  127. Args:
  128. device_id: 设备ID。
  129. Returns:
  130. JSON格式的监测数据。
  131. """
  132. return await _call_mcp_tool("query_device_data", {"device_id": device_id})
  133. async def query_device_data_by_id(device_id: str = "", page_size: int = 20) -> str:
  134. """根据设备ID查询实时数据和报警数据。
  135. 当需要获取某设备的实时监测数据和报警信息时调用此工具。
  136. Args:
  137. device_id: 设备ID。
  138. page_size: 返回数据条数,默认20。
  139. Returns:
  140. JSON格式的监测数据和报警数据。
  141. """
  142. return await _call_mcp_tool("query_device_data_by_id", {"device_id": device_id, "page_size": page_size})
  143. async def query_devices_by_tunnel(tunnel_name: str = "", device_type: str = None) -> str:
  144. """根据巷道名称查询该巷道绑定的设备及实时数据。
  145. 当需要查询某巷道下所有关联设备及其当前监测数据时调用此工具。
  146. Args:
  147. tunnel_name: 巷道名称(必填)。
  148. device_type: 设备类型,不传查询全部设备。
  149. Returns:
  150. JSON格式的设备列表及实时数据。
  151. """
  152. params = {"tunnel_name": tunnel_name}
  153. if device_type:
  154. params["device_type"] = device_type
  155. return await _call_mcp_tool("query_devices_by_tunnel", params)
  156. async def query_devices_by_tunnel_id(tunnel_id: str = "", model_id: str = None, device_type: str = None) -> str:
  157. """根据巷道ID查询已绑定设备。
  158. 当需要通过巷道ID查询该巷道下绑定的设备列表时调用此工具。
  159. Args:
  160. tunnel_id: 巷道ID(必填)。
  161. model_id: 模型ID,可选。
  162. device_type: 设备类型,可选。
  163. Returns:
  164. JSON格式的设备列表。
  165. """
  166. params = {"tunnel_id": tunnel_id}
  167. if model_id:
  168. params["model_id"] = model_id
  169. if device_type:
  170. params["device_type"] = device_type
  171. return await _call_mcp_tool("query_devices_by_tunnel_id", params)
  172. async def get_tun_list_by_modelid(model_id: str = "") -> str:
  173. """通过模型ID获取巷道列表。
  174. 当需要获取指定模型下的所有巷道列表时调用此工具。
  175. Args:
  176. model_id: 模型ID(必填)。
  177. Returns:
  178. JSON格式的巷道列表。
  179. """
  180. return await _call_mcp_tool("get_tun_list_by_modelid", {"model_id": model_id})
  181. async def query_tunnels_by_model(model_id: str = "") -> str:
  182. """根据模型ID查询巷道列表。
  183. 通过模型ID获取该模型下所有巷道的基本信息列表,
  184. 包括巷道ID、名称、类型、需风量等。
  185. Args:
  186. model_id: 模型ID(必填)。
  187. Returns:
  188. JSON格式的巷道列表。
  189. """
  190. return await _call_mcp_tool("query_tunnels_by_model", {"model_id": model_id})
  191. async def query_tunnel_list(model_id: int, tunnel_name: str) -> str:
  192. """根据模型ID和巷道名称(模糊)查询巷道列表。
  193. 当用户提到具体巷道名称时,**优先使用此工具**进行模糊匹配,
  194. 可大幅减少返回数据量。仅返回 modelId / tunnelId / tunnelName 三个字段。
  195. 若此工具无匹配结果,再回退使用 query_tunnels_by_model 获取全量列表。
  196. Args:
  197. model_id: 模型ID(必填),默认使用 .env 中的 DEFAULT_MODEL_ID。
  198. tunnel_name: 巷道名称(模糊匹配,必填)。
  199. Returns:
  200. JSON格式的巷道列表(仅含 modelId/tunnelId/tunnelName)。
  201. 包含多跳同名的巷道,因为一条完整的巷道是多节的,所以获取挂载设备时要多次判断。
  202. """
  203. return await _call_mcp_tool("query_tunnel_list", {
  204. "model_id": model_id,
  205. "tunnel_name": tunnel_name,
  206. })
  207. async def query_tun_data_by_id(tun_id: str = "") -> str:
  208. """根据ID查询指定巷道解读数据。
  209. 当需要获取某条巷道的风速、风量、瓦斯浓度、温度、设备状态等实时数据时调用此工具。
  210. Args:
  211. tun_id: 巷道ID。
  212. Returns:
  213. JSON格式的监测数据,包含风速、风量、瓦斯、温度、设备状态等信息。
  214. tunId 巷道唯一 ID
  215. tunnelName 巷道名称
  216. needAirVolume 巷道需配风量,单位 m³/min
  217. usingType 巷道类型 0-回采工作面;1-掘进工作面;2-辅运巷;3-主运巷;4-硐室;5-联络巷;6-进风井;7-回风井;8-专用回风巷
  218. usingTypeName 巷道用途中文名称(如掘进工作面)
  219. regulationId 风速规范配置 ID
  220. permissibleMin 允许最小风速,m/s
  221. permissibleMax 允许最大风速,m/s
  222. permissibleVelocity 巷道风速规范限制对象
  223. sensorIds 绑定的所有传感器 ID 数组
  224. deviceCount 绑定设备总数量
  225. devices 巷道绑定设备列表数组
  226. permissibleVelocity
  227. model_reg_id 风速规范主键 ID,同外层 regulationId
  228. fmin 最低允许风速 m/s
  229. fmax 最高允许风速 m/s
  230. sourceType 设备数据源类型:wind = 测风设备,model_sensor = 模型计算传感器
  231. sourceId 设备数据源唯一编号
  232. sourceName 数据源名称
  233. installPos 设备井下安装位置
  234. sensorId 传感器唯一标识 ID
  235. parentId 父级传感器 ID,单设备时与 sensorId 一致
  236. airVolume 实时风量,单位 m³/min;null 表示无实时数据
  237. windSpeed 实时风速,单位 m/s;null 表示无实时数据
  238. warnFlag 报警标识:0 = 无报警,非 0 代表存在异常报警
  239. netStatus 设备网络在线状态:1 = 在线,null / 其他 = 离线 / 无数据
  240. deviceStatus 设备运行状态编码
  241. deviceStatusName 设备运行状态中文描述(在线 / 离线等)
  242. deviceName 设备展示名称
  243. readTime 最新数据采集时间,格式 yyyy-MM-dd HH:mm:ss
  244. deviceType 设备类型标识,modelsensor_speed = 风速模型传感器
  245. readData 设备实时采集原始数据对象
  246. alarmDescription 当前设备单条报警文本,无报警为空
  247. alarmDescriptions 设备多条报警信息数组,无报警为空数组
  248. m3 实时风量数值 m³/min
  249. sign 风向标识
  250. tTime 传感器原始采集时间
  251. va 实时风速数值 m/s
  252. isRun 设备运行状态标记
  253. """
  254. return await _call_mcp_tool("query_wind_by_tunid", {"tun_id": tun_id, "model_id":2012326636757958658})
  255. def query_knowledge_base(question: str = "", top_k: int = 5) -> str:
  256. """查询煤矿安全知识库,检索《煤矿安全规程》条款依据、标准规范原文。
  257. 当需要查询某项安全条款的具体依据、规程出处、标准参数限值、
  258. 技术规范原文时调用此工具。知识库地址 http://39.97.59.228:8067,
  259. 使用 /api/retrieve 接口进行语义+关键词混合检索。
  260. Args:
  261. question: 检索问句,如"煤矿安全规程 瓦斯浓度限值 采煤工作面"、"AQ1056 风量计算方法"
  262. top_k: 返回结果数量,1~20,默认5
  263. Returns:
  264. JSON格式的检索结果,包含 context(相关段落文本)和 sources(来源列表)。
  265. """
  266. import httpx
  267. try:
  268. payload = {
  269. "question": question,
  270. "top_k": max(1, min(20, top_k)),
  271. "search_mode": "hybrid",
  272. "format": "compact",
  273. }
  274. with httpx.Client(timeout=15.0) as client:
  275. resp = client.post("http://39.97.59.228:8067/api/retrieve", json=payload)
  276. resp.raise_for_status()
  277. result = resp.json()
  278. print(result.get("context", "")[:200])
  279. print(json.dumps(result, indent=2, ensure_ascii=False))
  280. return json.dumps(result, ensure_ascii=False)
  281. except Exception as e:
  282. return json.dumps({
  283. "error": f"知识库查询失败: {e}",
  284. "question": question,
  285. }, ensure_ascii=False)
  286. # ============================================================
  287. # 需风量数据查询(通防管控平台 MCP)
  288. # ============================================================
  289. async def get_needq_all_data() -> str:
  290. """从通防管控平台获取全部用风地点的需风量数据。
  291. 调用远程 MCP 工具 get_needq_all_data,返回管控平台上所有用风地点的
  292. 计划需风量、实际风量、偏差等数据。用于回答用户"查看管控平台的需风量情况"
  293. "通防管控平台上各地点需风量是多少"等问题。
  294. Returns:
  295. JSON 格式的全部需风量数据,包含各用风地点的名称、类型、计划需风量、
  296. 实际风量、偏差等字段。
  297. """
  298. return await _call_mcp_tool("get_needq_all_data", {})
  299. async def get_device_kind_dict() -> str:
  300. """查询设备大类(deviceKind)和小类(strType)的全量字典。
  301. 返回设备类型编码与中文名称的完整映射表,包含 deviceKind(设备大类)
  302. 和 strType(设备小类)两个维度的编码-名称对照。
  303. 无参数。
  304. 适用场景:
  305. - 用户询问"有哪些设备类型""设备分类有哪些""设备大类小类有哪些"
  306. - 需要将设备类型编码翻译为中文名称
  307. - 查询某种设备类型对应的 strType 编码以便后续调用历史数据工具
  308. - 用户想知道系统中支持监控哪些类型的设备
  309. Returns:
  310. JSON 格式的设备类型字典,包含 deviceKind 和 strType 映射。
  311. """
  312. return await _call_mcp_tool("get_device_kind_dict", {})
  313. async def get_device_list_by_kind(device_kind: str) -> str:
  314. """根据设备类型(deviceKind)查询该类型下的全量设备列表。
  315. 返回指定设备大类下的所有设备,包含设备ID、设备名称、安装位置、分站名称。
  316. 适用场景:
  317. - 用户询问"列出所有风速传感器""有哪些甲烷传感器"等按类型筛选设备
  318. - 需要获取某类设备的完整清单以便进一步查询实时/历史数据
  319. - 结合 get_device_kind_dict 先获取类型编码,再按类型查设备列表
  320. Args:
  321. device_kind: 设备大类编码(必填),如 "modelsensor_speed"表示风速传感器。
  322. 可先调用 get_device_kind_dict 获取所有可用的 deviceKind 编码。
  323. Returns:
  324. JSON 格式的设备列表,包含 device_id、device_name、install_pos、station_name。
  325. """
  326. return await _call_mcp_tool("get_device_list_by_kind", {"device_kind": device_kind})
  327. async def query_device_realtime_data(device_id: str) -> str:
  328. """查询指定设备的实时监测数据。
  329. 根据设备ID获取该设备当前最新的监测读数、运行状态和报警信息。
  330. 与 query_device_data_by_id 不同,本工具专注于单设备的实时快照数据,
  331. 返回结构更精简,延迟更低。
  332. 适用场景:
  333. - 用户询问"设备XXX的实时数据""传感器XXX当前读数是多少"
  334. - 已知设备ID,需要快速获取其最新监测值
  335. - 配合 get_device_list_by_kind 先查出设备ID列表,再逐个查询实时数据
  336. Args:
  337. device_id: 设备ID(必填),可从 query_devices_by_tunnel / get_device_list_by_kind 返回结果中获取。
  338. Returns:
  339. JSON 格式的设备实时监测数据,包含当前读数、单位、采集时间、在线状态、报警信息。
  340. """
  341. return await _call_mcp_tool("query_device_realtime_data", {"device_id": device_id})
  342. # ============================================================
  343. # 监测历史数据查询(通防管控平台 MCP)
  344. # ============================================================
  345. async def list_ventanaly_monitor_data_days(
  346. strtype: str = "",
  347. gdeviceids: str = "",
  348. ttime_begin: str = "",
  349. ttime_end: str = "",
  350. device_num: str = "",
  351. skip: int = 8,
  352. page_no: int = 1,
  353. page_size: int = 100,
  354. # column: str = "",
  355. ) -> str:
  356. """查询监测设备历史时序数据。
  357. 根据设备类型、设备ID、时间范围等条件,查询监测设备的历史时序数据。
  358. 适用于查询风速、风量、瓦斯、温度等传感器在一段时间内的历史趋势。
  359. Args:
  360. strtype: 设备类型(必填),对应后端 strtype,如 "fanmain_stem_wp_2"
  361. gdeviceids: 设备ID(必填),如 "11111004";多个设备按后端要求格式拼接
  362. ttime_begin: 开始时间(必填),格式 "yyyy-MM-dd HH:mm:ss"
  363. ttime_end: 结束时间(必填),格式 "yyyy-MM-dd HH:mm:ss"
  364. device_num: 设备编号(必填),对应后端 deviceNum,如 "Fan1"
  365. skip: 采样间隔/跳点参数(必填),默认8,1=5秒//2=10秒//3=30秒//4=1分钟//5=5分钟//6=10分钟//7=30分钟8=1小时,如果半天内默认按10分钟查询,如果需要跨天则默认按1小时查询,其他情况请按合适的采样间隔获取,考虑数据库的压力。
  366. page_no: 页码,默认 1
  367. page_size: 每页条数,默认 200
  368. column: 排序字段,可选
  369. Returns:
  370. JSON格式的历史监测时序数据。
  371. """
  372. return await _call_mcp_tool("list_ventanaly_monitor_data_days", {
  373. "strtype": strtype,
  374. "gdeviceids": gdeviceids,
  375. "ttime_begin": ttime_begin,
  376. "ttime_end": ttime_end,
  377. "device_num": device_num,
  378. "skip": skip,
  379. "page_no": page_no,
  380. "page_size": page_size,
  381. # "column": column,
  382. })
  383. # ============================================================
  384. # 用户偏好记忆工具
  385. # ============================================================
  386. def save_user_preference(content: str, keywords: str = "",
  387. category: str = "通用") -> str:
  388. """保存用户偏好/习惯到个人记忆库。
  389. 当用户在对话中明确要求"记住""保存为习惯/偏好""以后都用这个"时调用。
  390. 下次该用户对话时,系统会自动注入已保存的偏好作为上下文。
  391. Args:
  392. content: 偏好内容,描述具体的习惯或个性化要求。例如"习惯使用 m³/s 而非 m³/min"、"15216工作面默认采高3.5m"
  393. keywords: 触发关键词,多个用逗号分隔。例如"风速,单位,风量"。留空则自动匹配。
  394. category: 偏好分类,默认"通用"。可选值:需风量计算、数据解读、规程查询、通用
  395. Returns:
  396. 保存结果,含记录ID供后续删除用。
  397. """
  398. from db.chat_store import save_user_preference as _db_save
  399. user_name = _current_user.get()
  400. pref_id = _db_save(user_name, content, keywords, category)
  401. return json.dumps({
  402. "success": True,
  403. "id": pref_id,
  404. "message": f"已保存偏好 (id={pref_id}):{content}",
  405. }, ensure_ascii=False)
  406. def list_user_preferences() -> str:
  407. """查看当前用户已保存的所有偏好/习惯。
  408. 列出该用户所有偏好记录,包含ID、分类、内容、关键词、保存时间。
  409. Returns:
  410. JSON格式的偏好列表。
  411. """
  412. from db.chat_store import get_user_preferences as _db_list
  413. user_name = _current_user.get()
  414. prefs = _db_list(user_name)
  415. if not prefs:
  416. return json.dumps({"preferences": [], "message": "暂无保存的偏好"}, ensure_ascii=False)
  417. return json.dumps({
  418. "preferences": [
  419. {"id": p["id"], "category": p["category"], "content": p["content"],
  420. "keywords": p["keywords"], "created_at": p["created_at"]}
  421. for p in prefs
  422. ],
  423. }, ensure_ascii=False)
  424. def delete_user_preference(preference_id: int) -> str:
  425. """删除一条用户偏好记录。
  426. Args:
  427. preference_id: 要删除的偏好记录ID(可从 list_user_preferences 获取)
  428. Returns:
  429. 删除结果。
  430. """
  431. from db.chat_store import delete_user_preference as _db_delete
  432. user_name = _current_user.get()
  433. ok = _db_delete(preference_id, user_name)
  434. if ok:
  435. return json.dumps({"success": True, "message": f"已删除偏好 (id={preference_id})"}, ensure_ascii=False)
  436. return json.dumps({"success": False, "message": f"未找到偏好记录 id={preference_id} 或无权操作"}, ensure_ascii=False)
  437. # ============================================================
  438. # 计划审批工具(Human-in-the-Loop)
  439. # ============================================================
  440. def request_plan_approval(plan_summary: str) -> str:
  441. """提交执行计划等待人工审批。在制定好完整计划后调用此工具。
  442. 仅在计划模式(plan mode)下由 Agent 主动调用。
  443. 调用后会暂停执行,等待用户在前端审批(批准/拒绝)。
  444. 审批通过后自动继续执行计划。
  445. Args:
  446. plan_summary: 执行计划的简要描述,需包含:
  447. - 计划分几步,每步做什么
  448. - 每步预期调用哪些工具
  449. - 预期的输出结果
  450. Returns:
  451. "计划已批准,开始执行。" 或 "计划被拒绝。"
  452. """
  453. from langgraph.types import interrupt
  454. result = interrupt({
  455. "type": "plan_approval",
  456. "plan": plan_summary,
  457. "message": "智能体已制定执行计划,等待您的审批...",
  458. })
  459. if isinstance(result, dict) and result.get("action") == "approve":
  460. return "计划已批准,开始执行。"
  461. else:
  462. return "计划被拒绝。"
  463. # ============================================================
  464. # 需风量 / 模型数据查询
  465. # ============================================================
  466. async def get_model_param_pub_list(page_no: int = 1, page_size: int = 50) -> str:
  467. """从通防管控平台查询默认模型参数列表。
  468. 获取管控平台上配置的需风量计算模型的基本信息,包含分页数据。
  469. Args:
  470. page_no: 页码,默认 1。
  471. page_size: 每页条数,默认 50。
  472. Returns:
  473. JSON 格式的模型参数列表。
  474. """
  475. return await _call_mcp_tool("get_model_param_pub_list", {
  476. "page_no": page_no,
  477. "page_size": page_size,
  478. })
  479. async def get_model_wind(model_id: int) -> str:
  480. """获取巷道解算风量数据。
  481. 根据模型ID获取该模型下所有巷道的网络解算风量数据,
  482. 包含各巷道的计算风量、风速等模拟结果。
  483. Args:
  484. model_id: 巷道模型ID(必填)。
  485. Returns:
  486. JSON 格式的巷道解算风量数据。
  487. """
  488. return await _call_mcp_tool("get_model_wind", {"model_id": model_id})
  489. async def get_sensor_wind(model_id: int) -> str:
  490. """获取设备监测风量数据。
  491. 根据模型ID获取该模型下所有测风设备的实时监测风量数据。
  492. Args:
  493. model_id: 巷道模型ID(必填)。
  494. Returns:
  495. JSON 格式的设备监测风量数据。
  496. """
  497. return await _call_mcp_tool("get_sensor_wind", {"model_id": model_id})
  498. # ============================================================
  499. # 需风量模拟计算
  500. # ============================================================
  501. async def simulate_needq_heading_face(req_data: str) -> str:
  502. """掘进面需风量模拟计算。
  503. 对指定掘进工作面进行需风量模拟计算,输入为结构化 JSON 对象。
  504. Args:
  505. req_data: 掘进面需风量计算的请求参数(JSON 对象字符串)。
  506. Returns:
  507. JSON 格式的模拟计算结果。
  508. """
  509. import json as _json
  510. try:
  511. args = _json.loads(req_data) if isinstance(req_data, str) else req_data
  512. except _json.JSONDecodeError:
  513. args = {"req_data": req_data}
  514. return await _call_mcp_tool("simulate_needq_heading_face", args)
  515. async def simulate_needq_room(req_data: str) -> str:
  516. """硐室需风量模拟计算。
  517. 对指定硐室进行需风量模拟计算,输入为结构化 JSON 对象。
  518. Args:
  519. req_data: 硐室需风量计算的请求参数(JSON 对象字符串)。
  520. Returns:
  521. JSON 格式的模拟计算结果。
  522. """
  523. import json as _json
  524. try:
  525. args = _json.loads(req_data) if isinstance(req_data, str) else req_data
  526. except _json.JSONDecodeError:
  527. args = {"req_data": req_data}
  528. return await _call_mcp_tool("simulate_needq_room", args)
  529. async def simulate_needq_ret_work_face(req_data: str) -> str:
  530. """采煤工作面需风量模拟计算。
  531. 对指定采煤工作面进行需风量模拟计算,输入为结构化 JSON 对象。
  532. Args:
  533. req_data: 采煤面需风量计算的请求参数(JSON 对象字符串)。
  534. Returns:
  535. JSON 格式的模拟计算结果。
  536. """
  537. import json as _json
  538. try:
  539. args = _json.loads(req_data) if isinstance(req_data, str) else req_data
  540. except _json.JSONDecodeError:
  541. args = {"req_data": req_data}
  542. return await _call_mcp_tool("simulate_needq_ret_work_face", args)
  543. async def simulate_needq_other(req_data: str) -> str:
  544. """其他用风地点需风量模拟计算。
  545. 对其他用风地点(如联络巷、其他巷道等)进行需风量模拟计算。
  546. Args:
  547. req_data: 其他地点需风量计算的请求参数(JSON 对象字符串)。
  548. Returns:
  549. JSON 格式的模拟计算结果。
  550. """
  551. import json as _json
  552. try:
  553. args = _json.loads(req_data) if isinstance(req_data, str) else req_data
  554. except _json.JSONDecodeError:
  555. args = {"req_data": req_data}
  556. return await _call_mcp_tool("simulate_needq_other", args)
  557. # ============================================================
  558. # 设备信息查询
  559. # ============================================================
  560. async def query_device_info(device_kind: str = "", str_type: str = "") -> str:
  561. """根据设备大类/小类查询设备信息列表。
  562. 查询指定设备类型下的所有设备详细信息,device_kind 和 str_type 至少传一个。
  563. Args:
  564. device_kind: 设备大类编码,如 "fanmain"(可选,与 str_type 至少传一个)。
  565. str_type: 设备小类编码,如 "fanmain_stem_wp_2"(可选,与 device_kind 至少传一个)。
  566. Returns:
  567. JSON 格式的设备信息列表。
  568. """
  569. return await _call_mcp_tool("query_device_info", {
  570. "device_kind": device_kind,
  571. "str_type": str_type,
  572. })
  573. async def query_device_type_info(device_type: str = "") -> str:
  574. """根据设备类型编码查询设备大类和小类信息。
  575. 可传入大类编码(如 "fanmain")或完整小类编码(如 "fanmain_stem_wp_2")。
  576. Args:
  577. device_type: 设备类型编码(必填)。
  578. Returns:
  579. JSON 格式的设备类型信息。
  580. """
  581. return await _call_mcp_tool("query_device_type_info", {"device_type": device_type})
  582. async def query_monitor_params(device_kind: str = "", device_type: str = "",
  583. value_code: str = "") -> str:
  584. """根据设备大类/小类/测点编码查询点表监测参数。
  585. 查询指定设备的监测参数配置信息。
  586. Args:
  587. device_kind: 设备大类编码,如 "fanmain"(可选)。
  588. device_type: 设备类型编码,如 "fanmain_stem_wp_2"(可选)。
  589. value_code: 测点编码,如 "Fan1Power1IA"(可选)。
  590. Returns:
  591. JSON 格式的监测参数信息。
  592. """
  593. return await _call_mcp_tool("query_monitor_params", {
  594. "device_kind": device_kind,
  595. "device_type": device_type,
  596. "value_code": value_code,
  597. })
  598. async def query_devices_by_model(model_id: str = "", device_type: str = None) -> str:
  599. """通过模型ID查询该模型下已绑定的有效设备。
  600. 可选的 device_type 参数用于按设备类型过滤。
  601. Args:
  602. model_id: 模型ID(必填)。
  603. device_type: 设备类型过滤条件(可选),后端可模糊匹配。
  604. Returns:
  605. JSON 格式的设备列表。
  606. """
  607. params = {"model_id": model_id}
  608. if device_type:
  609. params["device_type"] = device_type
  610. return await _call_mcp_tool("query_devices_by_model", params)
  611. # ============================================================
  612. # 故障诊断
  613. # ============================================================
  614. async def check_model_connect_status(model_id: str = "") -> str:
  615. """检查模型通风网络连通性。
  616. 诊断网络联通故障,返回连通块数量及各巷道所属连通块。
  617. Args:
  618. model_id: 模型ID(必填)。
  619. Returns:
  620. JSON 格式的网络连通诊断结果。
  621. """
  622. return await _call_mcp_tool("check_model_connect_status", {"model_id": model_id})
  623. async def check_model_one_dir_cycle(model_id: str = "") -> str:
  624. """检查模型是否存在单向回路(循环风路)。
  625. 检测通风网络中的循环风路问题。
  626. Args:
  627. model_id: 模型ID(必填)。
  628. Returns:
  629. JSON 格式的循环风路检查结果。
  630. """
  631. return await _call_mcp_tool("check_model_one_dir_cycle", {"model_id": model_id})
  632. async def check_model_one_dir_node(model_id: str = "") -> str:
  633. """检查模型是否存在单风向节点。
  634. 检测通风网络中所有节点是否有风流方向异常。
  635. Args:
  636. model_id: 模型ID(必填)。
  637. Returns:
  638. JSON 格式的单向节点检查结果。
  639. """
  640. return await _call_mcp_tool("check_model_one_dir_node", {"model_id": model_id})
  641. async def check_model_diagonal_structure(model_id: str = "") -> str:
  642. """角联结构快速诊断。
  643. 检测通风网络中的角联结构,计算较重,可能耗时较长。
  644. Args:
  645. model_id: 模型ID(必填)。
  646. Returns:
  647. JSON 格式的角联结构诊断结果。
  648. """
  649. return await _call_mcp_tool("check_model_diagonal_structure", {"model_id": model_id})
  650. async def get_model_fault_diagnosis(model_id: str = "",
  651. include_diagonal: bool = False) -> str:
  652. """获取模型故障诊断聚合数据。
  653. 默认包含网络连通、循环风路、单向节点检查;
  654. 设置 include_diagonal=True 时额外追加角联结构诊断(较慢)。
  655. Args:
  656. model_id: 模型ID(必填)。
  657. include_diagonal: 是否包含角联结构诊断,默认 False。
  658. Returns:
  659. JSON 格式的故障诊断聚合结果。
  660. """
  661. return await _call_mcp_tool("get_model_fault_diagnosis", {
  662. "model_id": model_id,
  663. "include_diagonal": include_diagonal,
  664. })
  665. # ============================================================
  666. # 避灾路线
  667. # ============================================================
  668. async def get_escape_path(model_id: str = "", fire_tun_id: str = "",
  669. person_tun_id: str = "") -> str:
  670. """灾变避灾路线模拟。
  671. 给定火源巷道ID和人员/起点巷道ID,分析避灾逃生路线。
  672. Args:
  673. model_id: 模型ID(必填)。
  674. fire_tun_id: 火源巷道ID(必填)。
  675. person_tun_id: 人员所在/起点巷道ID(必填)。
  676. Returns:
  677. JSON 格式的避灾路线数据。
  678. """
  679. return await _call_mcp_tool("get_escape_path", {
  680. "model_id": model_id,
  681. "fire_tun_id": fire_tun_id,
  682. "person_tun_id": person_tun_id,
  683. })
  684. async def get_escape_path_each_exit(model_id: str = "", fire_tun_id: str = "",
  685. person_tun_id: str = "",
  686. co_per: float = 2000.0,
  687. during_time: float = 0.0) -> str:
  688. """避灾路线模拟(到各出口)。
  689. 给定火源、人员起点、CO浓度与持续时间,分析到各出口的避灾路线。
  690. Args:
  691. model_id: 模型ID(必填)。
  692. fire_tun_id: 火源巷道ID(必填)。
  693. person_tun_id: 人员/起点巷道ID(必填)。
  694. co_per: CO浓度参数,默认 2000。
  695. during_time: 持续时间,默认 0。
  696. Returns:
  697. JSON 格式的各出口避灾路线数据。
  698. """
  699. return await _call_mcp_tool("get_escape_path_each_exit", {
  700. "model_id": model_id,
  701. "fire_tun_id": fire_tun_id,
  702. "person_tun_id": person_tun_id,
  703. "co_per": co_per,
  704. "during_time": during_time,
  705. })
  706. # ============================================================
  707. # 关键阻力 / 压能
  708. # ============================================================
  709. async def get_out_shafts(model_id: str = "") -> str:
  710. """获取模型全部回风井巷道ID列表。
  711. Args:
  712. model_id: 模型ID(必填)。
  713. Returns:
  714. JSON 格式的回风井巷道ID列表。
  715. """
  716. return await _call_mcp_tool("get_out_shafts", {"model_id": model_id})
  717. async def get_in_shafts(model_id: str = "") -> str:
  718. """获取模型全部进风井巷道ID列表。
  719. Args:
  720. model_id: 模型ID(必填)。
  721. Returns:
  722. JSON 格式的进风井巷道ID列表。
  723. """
  724. return await _call_mcp_tool("get_in_shafts", {"model_id": model_id})
  725. async def get_max_resistance_path(model_id: str = "", node_id: str = "") -> str:
  726. """获取指定节点的最大阻力路线(关键阻力路线)。
  727. node_id 必须是节点ID(nNodeID),不是巷道ID。
  728. 可先调用 get_out_shafts 获取回风井,再查巷道起止节点获取 node_id。
  729. Args:
  730. model_id: 模型ID(必填)。
  731. node_id: 节点ID(必填),对应巷道 nFromID/nToID。
  732. Returns:
  733. JSON 格式的最大阻力路线数据。
  734. """
  735. return await _call_mcp_tool("get_max_resistance_path", {
  736. "model_id": model_id,
  737. "node_id": node_id,
  738. })
  739. async def get_three_area_distribution(model_id: str = "") -> str:
  740. """获取模型三区阻力分布数据。
  741. 返回进风区/用风区/回风区的分段阻力、总阻力等分布数据。
  742. Args:
  743. model_id: 模型ID(必填)。
  744. Returns:
  745. JSON 格式的三区阻力分布数据。
  746. """
  747. return await _call_mcp_tool("get_three_area_distribution", {"model_id": model_id})
  748. async def get_key_path_decision(model_id: str = "") -> str:
  749. """获取模型关键路径控风方案决策数据。
  750. Args:
  751. model_id: 模型ID(必填)。
  752. Returns:
  753. JSON 格式的关键路径决策数据。
  754. """
  755. return await _call_mcp_tool("get_key_path_decision", {"model_id": model_id})
  756. async def get_path_press_power(model_id: str = "", id_from: str = "",
  757. id_to: str = "") -> str:
  758. """获取模型节点压能图数据。
  759. 计算从起点节点到终点节点的路径压能图。id_from/id_to 为节点ID,不是巷道ID。
  760. Args:
  761. model_id: 模型ID(必填)。
  762. id_from: 起点节点ID(必填)。
  763. id_to: 终点节点ID(必填)。
  764. Returns:
  765. JSON 格式的节点压能图数据。
  766. """
  767. return await _call_mcp_tool("get_path_press_power", {
  768. "model_id": model_id,
  769. "id_from": id_from,
  770. "id_to": id_to,
  771. })
  772. # ============================================================
  773. # 网络解算
  774. # ============================================================
  775. async def net_cal(model_id: str = "") -> str:
  776. """模型网络解算。
  777. 对指定模型执行通风网络解算,获取各巷道风量分配结果。
  778. 也用于反风模拟后的风量结果查询。耗时可能较长。
  779. Args:
  780. model_id: 模型ID(必填)。
  781. Returns:
  782. JSON 格式的网络解算结果。
  783. """
  784. return await _call_mcp_tool("net_cal", {"model_id": model_id})
  785. async def net_cal_for_plan(model_id: str = "", plan: str = "") -> str:
  786. """方案模拟解算。
  787. 传入调控/反风等方案串,获取模拟解算结果。
  788. Args:
  789. model_id: 模型ID(必填)。
  790. plan: 方案串,由业务侧定义的调控/反风方案内容(必填)。
  791. Returns:
  792. JSON 格式的方案模拟解算结果。
  793. """
  794. return await _call_mcp_tool("net_cal_for_plan", {
  795. "model_id": model_id,
  796. "plan": plan,
  797. })
  798. # ============================================================
  799. # 报警 / 日志查询
  800. # ============================================================
  801. async def get_alarm_log_history(start_time: str, end_time: str,
  802. device_id: str = "", device_type: str = "") -> str:
  803. """获取设备设施报警历史数据。
  804. 按时间范围和可选设备条件查询报警历史记录。
  805. Args:
  806. start_time: 起始时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  807. end_time: 结束时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  808. device_id: 设备ID(可选)。
  809. device_type: 设备类型(可选)。
  810. Returns:
  811. JSON 格式的报警历史数据。
  812. """
  813. return await _call_mcp_tool("get_alarm_log_history", {
  814. "start_time": start_time,
  815. "end_time": end_time,
  816. "device_id": device_id,
  817. "device_type": device_type,
  818. })
  819. async def get_device_set_log_history(start_time: str, end_time: str,
  820. device_id: str = "",
  821. device_type: str = "") -> str:
  822. """获取设备设施控制历史数据。
  823. 按时间范围和可选设备条件查询设备控制操作历史。
  824. Args:
  825. start_time: 起始时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  826. end_time: 结束时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  827. device_id: 设备ID(可选)。
  828. device_type: 设备类型(可选)。
  829. Returns:
  830. JSON 格式的设备控制历史数据。
  831. """
  832. return await _call_mcp_tool("get_device_set_log_history", {
  833. "start_time": start_time,
  834. "end_time": end_time,
  835. "device_id": device_id,
  836. "device_type": device_type,
  837. })
  838. async def get_sys_log_history(start_time: str, end_time: str,
  839. user_name: str = "") -> str:
  840. """获取系统登录人员历史数据。
  841. 按时间范围和可选用户名查询系统登录历史。
  842. Args:
  843. start_time: 起始时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  844. end_time: 结束时间(必填),格式 "yyyy-MM-dd HH:mm:ss"。
  845. user_name: 用户名(可选)。
  846. Returns:
  847. JSON 格式的系统登录历史数据。
  848. """
  849. return await _call_mcp_tool("get_sys_log_history", {
  850. "start_time": start_time,
  851. "end_time": end_time,
  852. "user_name": user_name,
  853. })
  854. # ============================================================
  855. # 场景管理
  856. # ============================================================
  857. async def get_manage_system_by_strType(strType: str) -> str:
  858. """通过场景类型获取场景列表。
  859. Args:
  860. strType: 场景类型编码(必填)。
  861. Returns:
  862. JSON 格式的场景列表。
  863. """
  864. return await _call_mcp_tool("get_manage_system_by_strType", {"strType": strType})
  865. async def query_system_by_systemID(systemID: str) -> str:
  866. """根据场景ID获取场景数据。
  867. Args:
  868. systemID: 场景ID(必填)。
  869. Returns:
  870. JSON 格式的场景数据。
  871. """
  872. return await _call_mcp_tool("query_system_by_systemID", {"systemID": systemID})
  873. # ============================================================
  874. # 煤矿基础数据
  875. # ============================================================
  876. async def get_gas_identify_vo(mine_name: str = "") -> str:
  877. """根据矿井名称获取瓦斯等级鉴定报告。
  878. Args:
  879. mine_name: 矿井名称(可选)。
  880. Returns:
  881. JSON 格式的瓦斯等级鉴定报告数据。
  882. """
  883. return await _call_mcp_tool("get_gas_identify_vo", {"mine_name": mine_name})
  884. async def get_by_mine_name(mine_name: str = "") -> str:
  885. """根据矿井名称查询工作面设计规程。
  886. Args:
  887. mine_name: 矿井名称(可选)。
  888. Returns:
  889. JSON 格式的工作面设计规程数据。
  890. """
  891. return await _call_mcp_tool("get_by_mine_name", {"mine_name": mine_name})
  892. async def query_control_testWind(ids: str) -> str:
  893. """测风装置一键测风。
  894. 触发指定测风装置执行一键测风操作。
  895. Args:
  896. ids: 设备IDS(必填),多个设备按后端要求格式拼接,如 "11111004"。
  897. Returns:
  898. JSON 格式的测风结果。
  899. """
  900. return await _call_mcp_tool("query_control_testWind", {"ids": ids})
  901. # ============================================================
  902. # 数据库 / 文件
  903. # ============================================================
  904. async def execute_sql_query(sql_query: str) -> str:
  905. """从通防管控平台 vent 库执行 SQL 查询。
  906. 直接查询通防管控平台的数据库,返回 SQL 执行结果。
  907. ⚠️ 仅支持 SELECT 类只读查询。
  908. Args:
  909. sql_query: 要执行的 SQL 查询语句(必填)。
  910. Returns:
  911. JSON 格式的查询结果。
  912. """
  913. return await _call_mcp_tool("execute_sql_query", {"sql_query": sql_query})
  914. async def get_file_list_by_type(type: str) -> str:
  915. """按业务类型获取文件共享中心文件列表。
  916. Args:
  917. type: 业务类型编码(必填)。
  918. Returns:
  919. JSON 格式的文件列表。
  920. """
  921. return await _call_mcp_tool("get_file_list_by_type", {"type": type})
  922. async def get_file_base64_by_id(id: str) -> str:
  923. """通过文件ID获取文件的 Base64 编码内容。
  924. Args:
  925. id: 文件ID(必填)。
  926. Returns:
  927. JSON 格式的文件 Base64 编码数据。
  928. """
  929. return await _call_mcp_tool("get_file_base64_by_id", {"id": id})
  930. # ============================================================
  931. # 字典查询
  932. # ============================================================
  933. async def get_dict_list_by_dictcode(dictcode: str) -> str:
  934. """根据字典编码查询字典项的类型和名称。
  935. 查询系统字典表中指定 dictcode 对应的所有字典项,
  936. 包含编码和中文名称映射。
  937. Args:
  938. dictcode: 字典编码(必填)。
  939. Returns:
  940. JSON 格式的字典项列表。
  941. """
  942. return await _call_mcp_tool("get_dict_list_by_dictcode", {"dictcode": dictcode})
  943. # ============================================================
  944. # 测风报表
  945. # ============================================================
  946. async def get_latest_report(mine_name: str = "", date_month: str = "") -> str:
  947. """获取最新的测风报表。
  948. 根据矿井名称和年月获取最新的测风报表数据。
  949. Args:
  950. mine_name: 矿井名称(可选)。
  951. date_month: 年月,格式如 "2026-06"(可选)。
  952. Returns:
  953. JSON 格式的测风报表数据。
  954. """
  955. return await _call_mcp_tool("get_latest_report", {
  956. "mine_name": mine_name,
  957. "date_month": date_month,
  958. })