@josephyan/qingflow-cli 0.2.0-beta.985 → 0.2.0-beta.987

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +2 -2
  2. package/docs/local-agent-install.md +70 -11
  3. package/package.json +1 -1
  4. package/pyproject.toml +1 -1
  5. package/src/qingflow_mcp/__init__.py +1 -1
  6. package/src/qingflow_mcp/builder_facade/service.py +376 -19
  7. package/src/qingflow_mcp/cli/commands/auth.py +14 -43
  8. package/src/qingflow_mcp/cli/commands/workspace.py +8 -5
  9. package/src/qingflow_mcp/cli/formatters.py +19 -22
  10. package/src/qingflow_mcp/config.py +39 -0
  11. package/src/qingflow_mcp/errors.py +2 -2
  12. package/src/qingflow_mcp/public_surface.py +4 -6
  13. package/src/qingflow_mcp/response_trim.py +1 -8
  14. package/src/qingflow_mcp/server.py +1 -1
  15. package/src/qingflow_mcp/server_app_builder.py +4 -28
  16. package/src/qingflow_mcp/server_app_user.py +4 -28
  17. package/src/qingflow_mcp/session_store.py +31 -5
  18. package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
  19. package/src/qingflow_mcp/solution/executor.py +2 -2
  20. package/src/qingflow_mcp/tools/ai_builder_tools.py +117 -1
  21. package/src/qingflow_mcp/tools/app_tools.py +51 -1
  22. package/src/qingflow_mcp/tools/approval_tools.py +82 -1
  23. package/src/qingflow_mcp/tools/auth_tools.py +306 -288
  24. package/src/qingflow_mcp/tools/base.py +204 -4
  25. package/src/qingflow_mcp/tools/code_block_tools.py +21 -0
  26. package/src/qingflow_mcp/tools/custom_button_tools.py +24 -1
  27. package/src/qingflow_mcp/tools/directory_tools.py +28 -1
  28. package/src/qingflow_mcp/tools/feedback_tools.py +8 -0
  29. package/src/qingflow_mcp/tools/file_tools.py +25 -1
  30. package/src/qingflow_mcp/tools/import_tools.py +40 -1
  31. package/src/qingflow_mcp/tools/navigation_tools.py +34 -1
  32. package/src/qingflow_mcp/tools/package_tools.py +37 -1
  33. package/src/qingflow_mcp/tools/portal_tools.py +28 -1
  34. package/src/qingflow_mcp/tools/qingbi_report_tools.py +38 -1
  35. package/src/qingflow_mcp/tools/record_tools.py +255 -2
  36. package/src/qingflow_mcp/tools/repository_dev_tools.py +21 -2
  37. package/src/qingflow_mcp/tools/resource_read_tools.py +23 -1
  38. package/src/qingflow_mcp/tools/role_tools.py +19 -1
  39. package/src/qingflow_mcp/tools/solution_tools.py +56 -1
  40. package/src/qingflow_mcp/tools/task_context_tools.py +72 -1
  41. package/src/qingflow_mcp/tools/task_tools.py +49 -3
  42. package/src/qingflow_mcp/tools/view_tools.py +56 -1
  43. package/src/qingflow_mcp/tools/workflow_tools.py +65 -1
  44. package/src/qingflow_mcp/tools/workspace_tools.py +100 -217
@@ -12,7 +12,7 @@ from ..config import DEFAULT_PROFILE
12
12
  from ..errors import QingflowApiError, raise_tool_error
13
13
  from ..json_types import JSONObject
14
14
  from .approval_tools import ApprovalTools, _approval_page_amount, _approval_page_items, _approval_page_total
15
- from .base import ToolBase
15
+ from .base import ToolBase, tool_cn_name
16
16
  from .qingbi_report_tools import _qingbi_base_url
17
17
  from .record_tools import (
18
18
  FieldIndex,
@@ -38,13 +38,24 @@ from .task_tools import TaskTools, _task_page_amount, _task_page_items, _task_pa
38
38
 
39
39
 
40
40
  class TaskContextTools(ToolBase):
41
+ """任务上下文工具(中文名:任务上下文与审批执行)。
42
+
43
+ 类型:任务深度上下文工具。
44
+ 主要职责:
45
+ 1. 聚合任务详情、候选人、关联报表与流程日志;
46
+ 2. 执行审批动作(通过、驳回、转交等);
47
+ 3. 为任务处理过程提供可执行上下文而非仅列表数据。
48
+ """
49
+
41
50
  def __init__(self, sessions, backend) -> None: # type: ignore[no-untyped-def]
51
+ """执行内部辅助逻辑。"""
42
52
  super().__init__(sessions, backend)
43
53
  self._task_tools = TaskTools(sessions, backend)
44
54
  self._approval_tools = ApprovalTools(sessions, backend)
45
55
  self._record_tools = RecordTools(sessions, backend)
46
56
 
47
57
  def register(self, mcp: FastMCP) -> None:
58
+ """注册当前工具到 MCP 服务。"""
48
59
  @mcp.tool(
49
60
  description=(
50
61
  "List workflow tasks. `query` first uses backend task search; if the backend returns zero rows, "
@@ -144,6 +155,7 @@ class TaskContextTools(ToolBase):
144
155
  workflow_node_id=workflow_node_id,
145
156
  )
146
157
 
158
+ @tool_cn_name("任务上下文列表")
147
159
  def task_list(
148
160
  self,
149
161
  *,
@@ -156,6 +168,7 @@ class TaskContextTools(ToolBase):
156
168
  page: int,
157
169
  page_size: int,
158
170
  ) -> dict[str, Any]:
171
+ """执行任务相关逻辑。"""
159
172
  normalized_type = self._task_tools._task_box_to_type(task_box)
160
173
  normalized_status = self._task_tools._flow_status_to_process_status(flow_status)
161
174
  raw = self._task_tools.task_list(
@@ -230,6 +243,7 @@ class TaskContextTools(ToolBase):
230
243
  },
231
244
  }
232
245
 
246
+ @tool_cn_name("任务上下文详情")
233
247
  def task_get(
234
248
  self,
235
249
  *,
@@ -240,6 +254,7 @@ class TaskContextTools(ToolBase):
240
254
  include_candidates: bool,
241
255
  include_associated_reports: bool,
242
256
  ) -> dict[str, Any]:
257
+ """执行任务相关逻辑。"""
243
258
  self._require_app_record_and_node(app_key, record_id, workflow_node_id)
244
259
 
245
260
  def runner(session_profile, context):
@@ -266,6 +281,7 @@ class TaskContextTools(ToolBase):
266
281
 
267
282
  return self._run(profile, runner)
268
283
 
284
+ @tool_cn_name("任务仅保存")
269
285
  def task_save_only(
270
286
  self,
271
287
  *,
@@ -275,6 +291,7 @@ class TaskContextTools(ToolBase):
275
291
  workflow_node_id: int,
276
292
  fields: dict[str, Any] | None = None,
277
293
  ) -> dict[str, Any]:
294
+ """执行任务相关逻辑。"""
278
295
  field_updates = dict(fields or {})
279
296
  if not field_updates:
280
297
  raise_tool_error(QingflowApiError.config_error("fields is required and must be non-empty for task_save_only"))
@@ -288,6 +305,7 @@ class TaskContextTools(ToolBase):
288
305
  fields=field_updates,
289
306
  )
290
307
 
308
+ @tool_cn_name("执行任务动作")
291
309
  def task_action_execute(
292
310
  self,
293
311
  *,
@@ -299,6 +317,7 @@ class TaskContextTools(ToolBase):
299
317
  payload: dict[str, Any],
300
318
  fields: dict[str, Any] | None = None,
301
319
  ) -> dict[str, Any]:
320
+ """执行任务相关逻辑。"""
302
321
  self._require_app_record_and_node(app_key, record_id, workflow_node_id)
303
322
  normalized_action = (action or "").strip().lower()
304
323
  if normalized_action not in {"approve", "reject", "rollback", "transfer", "urge", "save_only"}:
@@ -494,6 +513,7 @@ class TaskContextTools(ToolBase):
494
513
  payload: dict[str, Any],
495
514
  prepared_fields: dict[str, Any] | None,
496
515
  ) -> dict[str, Any]:
516
+ """执行内部辅助逻辑。"""
497
517
  merged_answers = None
498
518
  normalized_answers = None
499
519
  if isinstance(prepared_fields, dict):
@@ -589,6 +609,7 @@ class TaskContextTools(ToolBase):
589
609
  before_apply_status: Any,
590
610
  runtime_baseline: dict[str, Any] | None = None,
591
611
  ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
612
+ """执行内部辅助逻辑。"""
592
613
  verification: dict[str, Any] = {
593
614
  "action_executed": True,
594
615
  "runtime_continuation_verified": action == "urge",
@@ -730,6 +751,7 @@ class TaskContextTools(ToolBase):
730
751
  expected_answers: list[dict[str, Any]],
731
752
  task_context: dict[str, Any],
732
753
  ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
754
+ """执行内部辅助逻辑。"""
733
755
  verification: dict[str, Any] = {
734
756
  "action_executed": True,
735
757
  "scope": "task_field_save",
@@ -843,6 +865,7 @@ class TaskContextTools(ToolBase):
843
865
  record_id: int,
844
866
  workflow_node_id: int,
845
867
  ) -> dict[str, Any]:
868
+ """执行内部辅助逻辑。"""
846
869
  baseline: dict[str, Any] = {
847
870
  "workflow_log_visible": False,
848
871
  "workflow_log_count": None,
@@ -894,6 +917,7 @@ class TaskContextTools(ToolBase):
894
917
  source_error: QingflowApiError,
895
918
  before_apply_status: Any,
896
919
  ) -> dict[str, Any]:
920
+ """执行内部辅助逻辑。"""
897
921
  verification, warnings = self._verify_task_action_runtime(
898
922
  profile=profile,
899
923
  context=context,
@@ -969,6 +993,7 @@ class TaskContextTools(ToolBase):
969
993
  }
970
994
 
971
995
  def _safe_task_list_items(self, *, profile: str, task_box: str, app_key: str) -> list[dict[str, Any]]:
996
+ """执行内部辅助逻辑。"""
972
997
  try:
973
998
  response = self.task_list(
974
999
  profile=profile,
@@ -1063,6 +1088,7 @@ class TaskContextTools(ToolBase):
1063
1088
  return True
1064
1089
  return False
1065
1090
 
1091
+ @tool_cn_name("任务关联报表详情")
1066
1092
  def task_associated_report_detail_get(
1067
1093
  self,
1068
1094
  *,
@@ -1074,6 +1100,7 @@ class TaskContextTools(ToolBase):
1074
1100
  page: int,
1075
1101
  page_size: int,
1076
1102
  ) -> dict[str, Any]:
1103
+ """执行任务相关逻辑。"""
1077
1104
  self._require_app_record_and_node(app_key, record_id, workflow_node_id)
1078
1105
  if report_id <= 0:
1079
1106
  raise_tool_error(QingflowApiError.config_error("report_id must be positive"))
@@ -1253,6 +1280,7 @@ class TaskContextTools(ToolBase):
1253
1280
 
1254
1281
  return self._run(profile, runner)
1255
1282
 
1283
+ @tool_cn_name("任务流程日志")
1256
1284
  def task_workflow_log_get(
1257
1285
  self,
1258
1286
  *,
@@ -1261,6 +1289,7 @@ class TaskContextTools(ToolBase):
1261
1289
  record_id: int,
1262
1290
  workflow_node_id: int,
1263
1291
  ) -> dict[str, Any]:
1292
+ """执行任务相关逻辑。"""
1264
1293
  self._require_app_record_and_node(app_key, record_id, workflow_node_id)
1265
1294
 
1266
1295
  def runner(session_profile, context):
@@ -1330,6 +1359,7 @@ class TaskContextTools(ToolBase):
1330
1359
  include_associated_reports: bool,
1331
1360
  current_uid: int | None = None,
1332
1361
  ) -> dict[str, Any]:
1362
+ """执行内部辅助逻辑。"""
1333
1363
  audit_infos = self.backend.request(
1334
1364
  "GET",
1335
1365
  context,
@@ -1544,6 +1574,12 @@ class TaskContextTools(ToolBase):
1544
1574
  },
1545
1575
  },
1546
1576
  }
1577
+ action_metadata = self._compact_task_action_metadata(capabilities)
1578
+ if action_metadata:
1579
+ compact["action_metadata"] = action_metadata
1580
+ editable_metadata = self._compact_task_editable_metadata(update_schema)
1581
+ if editable_metadata:
1582
+ compact["editable_metadata"] = editable_metadata
1547
1583
  return compact
1548
1584
 
1549
1585
  def _compact_task_action_metadata(self, capabilities: dict[str, Any]) -> dict[str, Any]:
@@ -1681,6 +1717,7 @@ class TaskContextTools(ToolBase):
1681
1717
  }
1682
1718
 
1683
1719
  def _normalize_task_item(self, raw: dict[str, Any], *, task_box: str, flow_status: str) -> dict[str, Any]:
1720
+ """执行内部辅助逻辑。"""
1684
1721
  app_key = raw.get("appKey") or raw.get("app_key")
1685
1722
  record_id = raw.get("rowRecordId") or raw.get("recordId") or raw.get("applyId")
1686
1723
  workflow_node_id = raw.get("nodeId") or raw.get("auditNodeId")
@@ -1698,6 +1735,7 @@ class TaskContextTools(ToolBase):
1698
1735
  }
1699
1736
 
1700
1737
  def _select_task_node(self, infos: Any, workflow_node_id: int, *, app_key: str, record_id: int) -> dict[str, Any]:
1738
+ """执行内部辅助逻辑。"""
1701
1739
  if not isinstance(infos, list) or not infos:
1702
1740
  raise_tool_error(
1703
1741
  QingflowApiError.config_error(
@@ -1726,6 +1764,7 @@ class TaskContextTools(ToolBase):
1726
1764
  warnings: list[JSONObject] | None = None,
1727
1765
  save_only_source: str = "workflow_editable_que_ids",
1728
1766
  ) -> dict[str, Any]:
1767
+ """执行内部辅助逻辑。"""
1729
1768
  available_actions = ["approve"]
1730
1769
  if self._coerce_bool(node_info.get("rejectBtnStatus")):
1731
1770
  available_actions.append("reject")
@@ -1776,6 +1815,7 @@ class TaskContextTools(ToolBase):
1776
1815
  node_info: dict[str, Any],
1777
1816
  current_answers: Any,
1778
1817
  ) -> dict[str, Any]:
1818
+ """执行内部辅助逻辑。"""
1779
1819
  try:
1780
1820
  app_schema = self._record_tools._get_form_schema(profile, context, app_key, force_refresh=False)
1781
1821
  except QingflowApiError as error:
@@ -1896,6 +1936,7 @@ class TaskContextTools(ToolBase):
1896
1936
  current_answers: Any,
1897
1937
  editable_question_ids: set[int],
1898
1938
  ) -> tuple[FieldIndex, list[JSONObject]]:
1939
+ """执行内部辅助逻辑。"""
1899
1940
  if not editable_question_ids:
1900
1941
  return index, []
1901
1942
  missing_field_ids = {
@@ -1935,6 +1976,7 @@ class TaskContextTools(ToolBase):
1935
1976
  workflow_node_id: int,
1936
1977
  node_info: dict[str, Any],
1937
1978
  ) -> tuple[set[int], list[JSONObject], str]:
1979
+ """执行内部辅助逻辑。"""
1938
1980
  warnings: list[JSONObject] = []
1939
1981
  try:
1940
1982
  payload = self.backend.request(
@@ -1964,6 +2006,7 @@ class TaskContextTools(ToolBase):
1964
2006
  app_key: str,
1965
2007
  workflow_node_id: int,
1966
2008
  ) -> tuple[bool, list[JSONObject], str]:
2009
+ """执行内部辅助逻辑。"""
1967
2010
  try:
1968
2011
  payload = self.backend.request(
1969
2012
  "GET",
@@ -1983,6 +2026,7 @@ class TaskContextTools(ToolBase):
1983
2026
  return bool(self._extract_question_ids(payload)), [], "workflow_editable_que_ids"
1984
2027
 
1985
2028
  def _extract_question_ids(self, payload: Any) -> set[int]:
2029
+ """执行内部辅助逻辑。"""
1986
2030
  candidates: list[Any] = []
1987
2031
  if isinstance(payload, list):
1988
2032
  candidates = payload
@@ -2006,6 +2050,7 @@ class TaskContextTools(ToolBase):
2006
2050
  return question_ids
2007
2051
 
2008
2052
  def _editable_ids_from_que_auth_setting(self, payload: Any) -> set[int]:
2053
+ """执行内部辅助逻辑。"""
2009
2054
  if not isinstance(payload, list):
2010
2055
  return set()
2011
2056
  editable_ids: set[int] = set()
@@ -2040,6 +2085,7 @@ class TaskContextTools(ToolBase):
2040
2085
  task_context: dict[str, Any],
2041
2086
  fields: dict[str, Any],
2042
2087
  ) -> dict[str, Any]:
2088
+ """执行内部辅助逻辑。"""
2043
2089
  record = task_context.get("record") if isinstance(task_context.get("record"), dict) else {}
2044
2090
  current_answers = record.get("answers") if isinstance(record.get("answers"), list) else []
2045
2091
  node = task_context.get("node") if isinstance(task_context.get("node"), dict) else {}
@@ -2124,6 +2170,7 @@ class TaskContextTools(ToolBase):
2124
2170
  index: Any,
2125
2171
  effective_editable_ids: set[int],
2126
2172
  ) -> list[dict[str, Any]]:
2173
+ """执行内部辅助逻辑。"""
2127
2174
  if index is None:
2128
2175
  return []
2129
2176
  field_errors: list[dict[str, Any]] = []
@@ -2173,6 +2220,7 @@ class TaskContextTools(ToolBase):
2173
2220
  workflow_node_id: int,
2174
2221
  apply_answers: list[dict[str, Any]],
2175
2222
  ) -> dict[str, Any]:
2223
+ """执行内部辅助逻辑。"""
2176
2224
  def runner(session_profile, context):
2177
2225
  result = self.backend.request(
2178
2226
  "POST",
@@ -2192,6 +2240,7 @@ class TaskContextTools(ToolBase):
2192
2240
  return self._run(profile, runner)
2193
2241
 
2194
2242
  def _build_visibility(self, node_info: dict[str, Any], detail: dict[str, Any]) -> dict[str, bool]:
2243
+ """执行内部辅助逻辑。"""
2195
2244
  return {
2196
2245
  "comment_visible": self._coerce_bool(node_info.get("commentStatus")),
2197
2246
  "audit_record_visible": self._coerce_bool(node_info.get("auditRecordVisible")),
@@ -2201,12 +2250,14 @@ class TaskContextTools(ToolBase):
2201
2250
  }
2202
2251
 
2203
2252
  def _resolve_associated_report_visible(self, node_info: dict[str, Any], detail: dict[str, Any]) -> bool:
2253
+ """执行内部辅助逻辑。"""
2204
2254
  node_visible = node_info.get("asosChartVisible")
2205
2255
  if node_visible is not None:
2206
2256
  return self._coerce_bool(node_visible)
2207
2257
  return self._coerce_bool(detail.get("viewAsosChartVisible"))
2208
2258
 
2209
2259
  def _normalize_associated_report(self, raw: dict[str, Any]) -> dict[str, Any]:
2260
+ """执行内部辅助逻辑。"""
2210
2261
  graph_type = str(raw.get("graphType") or "").strip().lower()
2211
2262
  source_type = str(raw.get("sourceType") or "").strip().lower()
2212
2263
  return {
@@ -2222,6 +2273,7 @@ class TaskContextTools(ToolBase):
2222
2273
  }
2223
2274
 
2224
2275
  def _rollback_candidate_items(self, payload: Any) -> list[dict[str, Any]]:
2276
+ """执行内部辅助逻辑。"""
2225
2277
  if isinstance(payload, dict):
2226
2278
  revert_nodes = payload.get("revertNodes")
2227
2279
  if isinstance(revert_nodes, list):
@@ -2229,6 +2281,7 @@ class TaskContextTools(ToolBase):
2229
2281
  return [item for item in _approval_page_items(payload) if isinstance(item, dict)]
2230
2282
 
2231
2283
  def _filter_transfer_members(self, items: Any, *, current_uid: int | None) -> list[dict[str, Any]]:
2284
+ """执行内部辅助逻辑。"""
2232
2285
  if not isinstance(items, list):
2233
2286
  return []
2234
2287
  filtered: list[dict[str, Any]] = []
@@ -2315,6 +2368,7 @@ class TaskContextTools(ToolBase):
2315
2368
  return json.dumps(item, ensure_ascii=False, sort_keys=True, default=str)
2316
2369
 
2317
2370
  def _find_associated_report(self, task_context: dict[str, Any], report_id: int) -> dict[str, Any] | None:
2371
+ """执行内部辅助逻辑。"""
2318
2372
  associated_reports = ((task_context.get("associated_reports") or {}).get("items") or [])
2319
2373
  for item in associated_reports:
2320
2374
  if isinstance(item, dict) and item.get("report_id") == report_id:
@@ -2322,6 +2376,7 @@ class TaskContextTools(ToolBase):
2322
2376
  return None
2323
2377
 
2324
2378
  def _build_association_query(self, asos_chart: dict[str, Any], answers: list[dict[str, Any]]) -> dict[str, Any]:
2379
+ """执行内部辅助逻辑。"""
2325
2380
  key_que_ids = self._collect_match_rule_question_ids(asos_chart.get("matchRules") or [])
2326
2381
  key_values: list[dict[str, Any]] = []
2327
2382
  for answer in answers:
@@ -2356,6 +2411,7 @@ class TaskContextTools(ToolBase):
2356
2411
  }
2357
2412
 
2358
2413
  def _collect_match_rule_question_ids(self, match_rules: Any) -> set[int]:
2414
+ """执行内部辅助逻辑。"""
2359
2415
  question_ids: set[int] = set()
2360
2416
 
2361
2417
  def visit(node: Any) -> None:
@@ -2377,6 +2433,7 @@ class TaskContextTools(ToolBase):
2377
2433
  return question_ids
2378
2434
 
2379
2435
  def _extract_answer_values(self, answer: dict[str, Any]) -> list[str]:
2436
+ """执行内部辅助逻辑。"""
2380
2437
  values = answer.get("values")
2381
2438
  if not isinstance(values, list):
2382
2439
  return []
@@ -2404,6 +2461,7 @@ class TaskContextTools(ToolBase):
2404
2461
  return deduped
2405
2462
 
2406
2463
  def _sanitize_associated_chart(self, asos_chart: dict[str, Any]) -> dict[str, Any]:
2464
+ """执行内部辅助逻辑。"""
2407
2465
  return {
2408
2466
  "id": asos_chart.get("id"),
2409
2467
  "appKey": asos_chart.get("appKey"),
@@ -2418,6 +2476,7 @@ class TaskContextTools(ToolBase):
2418
2476
  }
2419
2477
 
2420
2478
  def _qingflow_chart_uses_apply_filter(self, context: BackendRequestContext, chart_key: str) -> bool:
2479
+ """执行内部辅助逻辑。"""
2421
2480
  if not chart_key:
2422
2481
  return False
2423
2482
  try:
@@ -2433,6 +2492,7 @@ class TaskContextTools(ToolBase):
2433
2492
  return self._coerce_bool(auth.get("detailedViewStatus")) and auth.get("lastViewType") == 1
2434
2493
 
2435
2494
  def _normalize_chart_result(self, payload: Any) -> dict[str, Any]:
2495
+ """执行内部辅助逻辑。"""
2436
2496
  if isinstance(payload, dict):
2437
2497
  rows = payload.get("rows")
2438
2498
  if not isinstance(rows, list):
@@ -2455,6 +2515,7 @@ class TaskContextTools(ToolBase):
2455
2515
  return {"summary": {}, "rows": [], "series": [], "metrics": []}
2456
2516
 
2457
2517
  def _normalize_workflow_logs(self, payload: Any) -> list[dict[str, Any]]:
2518
+ """执行内部辅助逻辑。"""
2458
2519
  if isinstance(payload, dict):
2459
2520
  page = payload.get("list") if isinstance(payload.get("list"), list) else payload.get("rows")
2460
2521
  if not isinstance(page, list):
@@ -2503,6 +2564,7 @@ class TaskContextTools(ToolBase):
2503
2564
  return items
2504
2565
 
2505
2566
  def _workflow_log_digest(self, items: list[dict[str, Any]]) -> str | None:
2567
+ """执行内部辅助逻辑。"""
2506
2568
  if not items:
2507
2569
  return None
2508
2570
  try:
@@ -2511,6 +2573,7 @@ class TaskContextTools(ToolBase):
2511
2573
  return str(items)
2512
2574
 
2513
2575
  def _first_nested_operation_detail(self, operation: dict[str, Any]) -> Any:
2576
+ """执行内部辅助逻辑。"""
2514
2577
  for key in ("approval", "filling", "cc", "applicant", "qRobotAdd", "qRobotUpdate", "webhook", "qRobotSMS", "qRobotMail"):
2515
2578
  value = operation.get(key)
2516
2579
  if value is not None:
@@ -2518,6 +2581,7 @@ class TaskContextTools(ToolBase):
2518
2581
  return None
2519
2582
 
2520
2583
  def _extract_remark(self, detail: Any) -> Any:
2584
+ """执行内部辅助逻辑。"""
2521
2585
  if not isinstance(detail, dict):
2522
2586
  return None
2523
2587
  for key in ("remark", "feedback", "comment", "content"):
@@ -2527,6 +2591,7 @@ class TaskContextTools(ToolBase):
2527
2591
  return None
2528
2592
 
2529
2593
  def _extract_signature_url(self, detail: Any) -> Any:
2594
+ """执行内部辅助逻辑。"""
2530
2595
  if not isinstance(detail, dict):
2531
2596
  return None
2532
2597
  for key in ("signatureUrl", "handSignImageUrl"):
@@ -2536,6 +2601,7 @@ class TaskContextTools(ToolBase):
2536
2601
  return None
2537
2602
 
2538
2603
  def _extract_attachments(self, detail: Any) -> Any:
2604
+ """执行内部辅助逻辑。"""
2539
2605
  if not isinstance(detail, dict):
2540
2606
  return []
2541
2607
  for key in ("attachments", "files", "uploadFiles"):
@@ -2545,6 +2611,7 @@ class TaskContextTools(ToolBase):
2545
2611
  return []
2546
2612
 
2547
2613
  def _extract_audit_feedback(self, payload: dict[str, Any]) -> str | None:
2614
+ """执行内部辅助逻辑。"""
2548
2615
  for key in ("audit_feedback", "auditFeedback"):
2549
2616
  value = payload.get(key)
2550
2617
  if isinstance(value, str) and value.strip():
@@ -2552,6 +2619,7 @@ class TaskContextTools(ToolBase):
2552
2619
  return None
2553
2620
 
2554
2621
  def _extract_positive_int(self, payload: dict[str, Any], key: str, *, aliases: tuple[str, ...] = ()) -> int:
2622
+ """执行内部辅助逻辑。"""
2555
2623
  candidates = (key, *aliases)
2556
2624
  value: Any = None
2557
2625
  for candidate in candidates:
@@ -2564,6 +2632,7 @@ class TaskContextTools(ToolBase):
2564
2632
  return value
2565
2633
 
2566
2634
  def _require_app_record_and_node(self, app_key: str, record_id: int, workflow_node_id: int) -> None:
2635
+ """执行内部辅助逻辑。"""
2567
2636
  if not app_key:
2568
2637
  raise_tool_error(QingflowApiError.config_error("app_key is required"))
2569
2638
  if record_id <= 0:
@@ -2572,6 +2641,7 @@ class TaskContextTools(ToolBase):
2572
2641
  raise_tool_error(QingflowApiError.config_error("workflow_node_id must be positive"))
2573
2642
 
2574
2643
  def _coerce_bool(self, value: Any) -> bool:
2644
+ """执行内部辅助逻辑。"""
2575
2645
  if isinstance(value, bool):
2576
2646
  return value
2577
2647
  if isinstance(value, int):
@@ -2581,6 +2651,7 @@ class TaskContextTools(ToolBase):
2581
2651
  return bool(value)
2582
2652
 
2583
2653
  def _request_route_payload(self, context: BackendRequestContext) -> dict[str, Any]:
2654
+ """执行内部辅助逻辑。"""
2584
2655
  describe_route = getattr(self.backend, "describe_route", None)
2585
2656
  if callable(describe_route):
2586
2657
  payload = describe_route(context)