@josephyan/qingflow-app-user-mcp 0.2.0-beta.987 → 0.2.0-beta.989
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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/builder_facade/service.py +88 -31
- package/src/qingflow_mcp/cli/commands/record.py +5 -5
- package/src/qingflow_mcp/cli/commands/task.py +3 -3
- package/src/qingflow_mcp/id_utils.py +49 -0
- package/src/qingflow_mcp/response_trim.py +10 -0
- package/src/qingflow_mcp/server_app_builder.py +10 -0
- package/src/qingflow_mcp/server_app_user.py +10 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
- package/src/qingflow_mcp/tools/record_tools.py +96 -55
- package/src/qingflow_mcp/tools/task_context_tools.py +55 -44
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.989
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.989 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -13454,6 +13454,7 @@ def _canonicalize_reference_questions_for_save(
|
|
|
13454
13454
|
source: dict[str, Any],
|
|
13455
13455
|
field: dict[str, Any],
|
|
13456
13456
|
) -> list[dict[str, Any]]:
|
|
13457
|
+
relation_config_explicit = bool(field.get("_relation_config_explicit"))
|
|
13457
13458
|
normalized_source_questions = [
|
|
13458
13459
|
item
|
|
13459
13460
|
for item in (
|
|
@@ -13462,6 +13463,8 @@ def _canonicalize_reference_questions_for_save(
|
|
|
13462
13463
|
)
|
|
13463
13464
|
if item is not None
|
|
13464
13465
|
]
|
|
13466
|
+
if not relation_config_explicit:
|
|
13467
|
+
return normalized_source_questions
|
|
13465
13468
|
|
|
13466
13469
|
display_field = field.get("display_field") if isinstance(field.get("display_field"), dict) else None
|
|
13467
13470
|
visible_fields = [item for item in cast(list[Any], field.get("visible_fields") or []) if isinstance(item, dict)]
|
|
@@ -13498,14 +13501,19 @@ def _canonicalize_reference_questions_for_save(
|
|
|
13498
13501
|
if matched_index is not None:
|
|
13499
13502
|
used_source_indexes.add(matched_index)
|
|
13500
13503
|
|
|
13501
|
-
|
|
13502
|
-
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13504
|
+
source_target_app_key = str(source.get("referAppKey") or "").strip()
|
|
13505
|
+
target_app_key = str(field.get("target_app_key") or "").strip()
|
|
13506
|
+
preserve_remaining_source_questions = not source_target_app_key or source_target_app_key == target_app_key
|
|
13507
|
+
|
|
13508
|
+
if preserve_remaining_source_questions:
|
|
13509
|
+
next_ordinal = len(canonical_questions) + 1
|
|
13510
|
+
for index, item in enumerate(normalized_source_questions):
|
|
13511
|
+
if index in used_source_indexes:
|
|
13512
|
+
continue
|
|
13513
|
+
remaining_item = deepcopy(item)
|
|
13514
|
+
remaining_item["ordinal"] = next_ordinal
|
|
13515
|
+
next_ordinal += 1
|
|
13516
|
+
canonical_questions.append(remaining_item)
|
|
13509
13517
|
|
|
13510
13518
|
return canonical_questions
|
|
13511
13519
|
|
|
@@ -13514,6 +13522,7 @@ def _canonicalize_reference_auth_questions_for_save(
|
|
|
13514
13522
|
*,
|
|
13515
13523
|
source: dict[str, Any],
|
|
13516
13524
|
refer_questions: list[dict[str, Any]],
|
|
13525
|
+
relation_config_explicit: bool,
|
|
13517
13526
|
) -> list[dict[str, Any]]:
|
|
13518
13527
|
source_auth_questions = [
|
|
13519
13528
|
item
|
|
@@ -13530,6 +13539,40 @@ def _canonicalize_reference_auth_questions_for_save(
|
|
|
13530
13539
|
continue
|
|
13531
13540
|
source_auth_by_que_id[que_id] = item
|
|
13532
13541
|
|
|
13542
|
+
if not relation_config_explicit:
|
|
13543
|
+
auth_questions: list[dict[str, Any]] = []
|
|
13544
|
+
seen_que_ids: set[int] = set()
|
|
13545
|
+
refer_question_auth_by_que_id: dict[int, int] = {}
|
|
13546
|
+
for item in refer_questions:
|
|
13547
|
+
que_id = _coerce_any_int(item.get("queId"))
|
|
13548
|
+
que_auth = _coerce_nonnegative_int(item.get("queAuth"))
|
|
13549
|
+
if que_id is None or que_auth is None or que_id in refer_question_auth_by_que_id:
|
|
13550
|
+
continue
|
|
13551
|
+
refer_question_auth_by_que_id[que_id] = que_auth
|
|
13552
|
+
|
|
13553
|
+
for item in source_auth_questions:
|
|
13554
|
+
que_id = _coerce_any_int(item.get("queId"))
|
|
13555
|
+
if que_id is None or que_id in seen_que_ids:
|
|
13556
|
+
continue
|
|
13557
|
+
payload = deepcopy(item)
|
|
13558
|
+
if que_id in refer_question_auth_by_que_id:
|
|
13559
|
+
payload["queAuth"] = refer_question_auth_by_que_id[que_id]
|
|
13560
|
+
auth_questions.append(payload)
|
|
13561
|
+
seen_que_ids.add(que_id)
|
|
13562
|
+
|
|
13563
|
+
for item in refer_questions:
|
|
13564
|
+
que_id = _coerce_any_int(item.get("queId"))
|
|
13565
|
+
que_auth = _coerce_nonnegative_int(item.get("queAuth"))
|
|
13566
|
+
if que_id is None or que_auth is None or que_id in seen_que_ids:
|
|
13567
|
+
continue
|
|
13568
|
+
payload = deepcopy(source_auth_by_que_id.get(que_id) or {"queId": que_id})
|
|
13569
|
+
payload["queId"] = que_id
|
|
13570
|
+
payload["queAuth"] = que_auth
|
|
13571
|
+
auth_questions.append(payload)
|
|
13572
|
+
seen_que_ids.add(que_id)
|
|
13573
|
+
|
|
13574
|
+
return _dedupe_reference_auth_questions(auth_questions)
|
|
13575
|
+
|
|
13533
13576
|
auth_questions: list[dict[str, Any]] = []
|
|
13534
13577
|
for item in refer_questions:
|
|
13535
13578
|
que_id = _coerce_any_int(item.get("queId"))
|
|
@@ -13548,6 +13591,7 @@ def _enforce_reference_config_consistency_for_save(
|
|
|
13548
13591
|
*,
|
|
13549
13592
|
field: dict[str, Any],
|
|
13550
13593
|
) -> dict[str, Any]:
|
|
13594
|
+
relation_config_explicit = bool(field.get("_relation_config_explicit"))
|
|
13551
13595
|
refer_questions = [
|
|
13552
13596
|
item
|
|
13553
13597
|
for item in (
|
|
@@ -13583,28 +13627,29 @@ def _enforce_reference_config_consistency_for_save(
|
|
|
13583
13627
|
if display_field_que_id is not None:
|
|
13584
13628
|
payload["referQueId"] = display_field_que_id
|
|
13585
13629
|
|
|
13586
|
-
if
|
|
13587
|
-
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
display_question
|
|
13597
|
-
|
|
13598
|
-
|
|
13630
|
+
if relation_config_explicit:
|
|
13631
|
+
if display_field_que_id is not None and not any(
|
|
13632
|
+
_coerce_any_int(item.get("queId")) == display_field_que_id for item in refer_questions
|
|
13633
|
+
):
|
|
13634
|
+
display_selector = field.get("display_field") if isinstance(field.get("display_field"), dict) else None
|
|
13635
|
+
display_question = (
|
|
13636
|
+
_build_reference_question_from_visible_selector(display_selector, ordinal=1)
|
|
13637
|
+
if display_selector is not None
|
|
13638
|
+
else None
|
|
13639
|
+
)
|
|
13640
|
+
if display_question is not None:
|
|
13641
|
+
display_question["queId"] = display_field_que_id
|
|
13642
|
+
display_question["queAuth"] = _REFERENCE_FIELD_VISIBLE_AUTH
|
|
13643
|
+
refer_questions = [display_question, *refer_questions]
|
|
13599
13644
|
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
|
|
13645
|
+
if display_field_que_id is not None:
|
|
13646
|
+
display_questions = [
|
|
13647
|
+
item for item in refer_questions if _coerce_any_int(item.get("queId")) == display_field_que_id
|
|
13648
|
+
]
|
|
13649
|
+
trailing_questions = [
|
|
13650
|
+
item for item in refer_questions if _coerce_any_int(item.get("queId")) != display_field_que_id
|
|
13651
|
+
]
|
|
13652
|
+
refer_questions = [*display_questions, *trailing_questions]
|
|
13608
13653
|
|
|
13609
13654
|
for ordinal, item in enumerate(refer_questions, start=1):
|
|
13610
13655
|
que_id = _coerce_any_int(item.get("queId"))
|
|
@@ -13622,6 +13667,7 @@ def _enforce_reference_config_consistency_for_save(
|
|
|
13622
13667
|
payload["referAuthQues"] = _canonicalize_reference_auth_questions_for_save(
|
|
13623
13668
|
source={"referAuthQues": refer_auth_ques},
|
|
13624
13669
|
refer_questions=refer_questions,
|
|
13670
|
+
relation_config_explicit=relation_config_explicit,
|
|
13625
13671
|
)
|
|
13626
13672
|
return payload
|
|
13627
13673
|
|
|
@@ -13661,7 +13707,11 @@ def _normalize_reference_config_for_save(
|
|
|
13661
13707
|
if refer_fill_rules or "referFillRules" in source:
|
|
13662
13708
|
payload["referFillRules"] = refer_fill_rules
|
|
13663
13709
|
|
|
13664
|
-
refer_auth_ques = _canonicalize_reference_auth_questions_for_save(
|
|
13710
|
+
refer_auth_ques = _canonicalize_reference_auth_questions_for_save(
|
|
13711
|
+
source=source,
|
|
13712
|
+
refer_questions=refer_questions,
|
|
13713
|
+
relation_config_explicit=bool(field.get("_relation_config_explicit")),
|
|
13714
|
+
)
|
|
13665
13715
|
if not refer_auth_ques:
|
|
13666
13716
|
refer_auth_ques = _synthesize_reference_auth_questions_for_save(source=source, field=field)
|
|
13667
13717
|
if refer_auth_ques or "referAuthQues" in source:
|
|
@@ -13917,6 +13967,13 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
13917
13967
|
if isinstance(built_question.get("referenceConfig"), dict)
|
|
13918
13968
|
else {}
|
|
13919
13969
|
)
|
|
13970
|
+
original_target_app_key = str(existing_reference.get("referAppKey") or "").strip()
|
|
13971
|
+
next_target_app_key = str(field.get("target_app_key") or "").strip()
|
|
13972
|
+
preserve_existing_reference_questions = (
|
|
13973
|
+
relation_config_explicit
|
|
13974
|
+
and bool(original_target_app_key)
|
|
13975
|
+
and original_target_app_key == next_target_app_key
|
|
13976
|
+
)
|
|
13920
13977
|
if relation_config_explicit:
|
|
13921
13978
|
for stale_key in ("customButtonText", "customAdvancedSetting", "configShowForm", "dataShowForm"):
|
|
13922
13979
|
reference.pop(stale_key, None)
|
|
@@ -13928,7 +13985,7 @@ def _field_to_question(field: dict[str, Any], *, temp_id: int) -> dict[str, Any]
|
|
|
13928
13985
|
"fieldNameShow",
|
|
13929
13986
|
"_targetFieldId",
|
|
13930
13987
|
):
|
|
13931
|
-
if
|
|
13988
|
+
if preserve_existing_reference_questions and key in {"referQuestions", "referAuthQues"}:
|
|
13932
13989
|
continue
|
|
13933
13990
|
if key in built_reference:
|
|
13934
13991
|
reference[key] = deepcopy(built_reference[key])
|
|
@@ -32,7 +32,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
32
32
|
|
|
33
33
|
schema_update = schema_subparsers.add_parser("update", help="读取更新记录表结构")
|
|
34
34
|
schema_update.add_argument("--app-key", required=True)
|
|
35
|
-
schema_update.add_argument("--record-id", required=True
|
|
35
|
+
schema_update.add_argument("--record-id", required=True)
|
|
36
36
|
schema_update.set_defaults(handler=_handle_schema_update, format_hint="")
|
|
37
37
|
|
|
38
38
|
schema_import = schema_subparsers.add_parser("import", help="读取导入表结构")
|
|
@@ -59,7 +59,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
59
59
|
|
|
60
60
|
get = record_subparsers.add_parser("get", help="读取单条记录")
|
|
61
61
|
get.add_argument("--app-key", required=True)
|
|
62
|
-
get.add_argument("--record-id", required=True
|
|
62
|
+
get.add_argument("--record-id", required=True)
|
|
63
63
|
get.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
64
64
|
get.add_argument("--columns-file")
|
|
65
65
|
get.add_argument("--view-id")
|
|
@@ -73,7 +73,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
73
73
|
|
|
74
74
|
update = record_subparsers.add_parser("update", help="更新记录")
|
|
75
75
|
update.add_argument("--app-key", required=True)
|
|
76
|
-
update.add_argument("--record-id"
|
|
76
|
+
update.add_argument("--record-id")
|
|
77
77
|
update.add_argument("--fields-file")
|
|
78
78
|
update.add_argument("--items-file")
|
|
79
79
|
update.add_argument("--dry-run", action=argparse.BooleanOptionalAction, default=False)
|
|
@@ -82,7 +82,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
82
82
|
|
|
83
83
|
delete = record_subparsers.add_parser("delete", help="删除记录")
|
|
84
84
|
delete.add_argument("--app-key", required=True)
|
|
85
|
-
delete.add_argument("--record-id"
|
|
85
|
+
delete.add_argument("--record-id")
|
|
86
86
|
delete.add_argument("--record-ids-file")
|
|
87
87
|
delete.set_defaults(handler=_handle_delete, format_hint="")
|
|
88
88
|
|
|
@@ -102,7 +102,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
102
102
|
|
|
103
103
|
code_block = record_subparsers.add_parser("code-block-run", help="执行代码块字段")
|
|
104
104
|
code_block.add_argument("--app-key", required=True)
|
|
105
|
-
code_block.add_argument("--record-id", required=True
|
|
105
|
+
code_block.add_argument("--record-id", required=True)
|
|
106
106
|
code_block.add_argument("--code-block-field", required=True)
|
|
107
107
|
code_block.add_argument("--role", type=int, default=1)
|
|
108
108
|
code_block.add_argument("--workflow-node-id", type=int)
|
|
@@ -25,7 +25,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
25
25
|
|
|
26
26
|
get = task_subparsers.add_parser("get", help="读取待办详情")
|
|
27
27
|
get.add_argument("--app-key", required=True)
|
|
28
|
-
get.add_argument("--record-id", required=True
|
|
28
|
+
get.add_argument("--record-id", required=True)
|
|
29
29
|
get.add_argument("--workflow-node-id", required=True, type=int)
|
|
30
30
|
get.add_argument("--include-candidates", action=argparse.BooleanOptionalAction, default=True)
|
|
31
31
|
get.add_argument("--include-associated-reports", action=argparse.BooleanOptionalAction, default=True)
|
|
@@ -33,7 +33,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
33
33
|
|
|
34
34
|
action = task_subparsers.add_parser("action", help="执行待办动作")
|
|
35
35
|
action.add_argument("--app-key", required=True)
|
|
36
|
-
action.add_argument("--record-id", required=True
|
|
36
|
+
action.add_argument("--record-id", required=True)
|
|
37
37
|
action.add_argument("--workflow-node-id", required=True, type=int)
|
|
38
38
|
action.add_argument("--action", required=True)
|
|
39
39
|
action.add_argument("--payload-file")
|
|
@@ -42,7 +42,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
42
42
|
|
|
43
43
|
log = task_subparsers.add_parser("log", help="读取流程日志")
|
|
44
44
|
log.add_argument("--app-key", required=True)
|
|
45
|
-
log.add_argument("--record-id", required=True
|
|
45
|
+
log.add_argument("--record-id", required=True)
|
|
46
46
|
log.add_argument("--workflow-node-id", required=True, type=int)
|
|
47
47
|
log.set_defaults(handler=_handle_log, format_hint="")
|
|
48
48
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .errors import QingflowApiError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
JS_MAX_SAFE_INTEGER = 9_007_199_254_740_991
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def stringify_backend_id(value: Any) -> str | None:
|
|
12
|
+
"""Return an exact public id string for backend-originated identifiers."""
|
|
13
|
+
if value in (None, ""):
|
|
14
|
+
return None
|
|
15
|
+
if isinstance(value, bool):
|
|
16
|
+
return None
|
|
17
|
+
text = str(value).strip()
|
|
18
|
+
return text or None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def normalize_positive_id_text(value: Any, *, field_name: str) -> str:
|
|
22
|
+
"""Normalize a user-supplied id while rejecting JS-unsafe numeric input."""
|
|
23
|
+
if value in (None, "") or isinstance(value, bool):
|
|
24
|
+
raise QingflowApiError.config_error(f"{field_name} must be positive")
|
|
25
|
+
if isinstance(value, int):
|
|
26
|
+
if value <= 0:
|
|
27
|
+
raise QingflowApiError.config_error(f"{field_name} must be positive")
|
|
28
|
+
if value > JS_MAX_SAFE_INTEGER:
|
|
29
|
+
raise QingflowApiError.config_error(
|
|
30
|
+
f"{field_name} exceeds JavaScript's safe integer range; pass it as a string to avoid precision loss"
|
|
31
|
+
)
|
|
32
|
+
return str(value)
|
|
33
|
+
if isinstance(value, str):
|
|
34
|
+
text = value.strip()
|
|
35
|
+
if not text.isdecimal() or int(text) <= 0:
|
|
36
|
+
raise QingflowApiError.config_error(f"{field_name} must be a positive integer string")
|
|
37
|
+
return text
|
|
38
|
+
raise QingflowApiError.config_error(f"{field_name} must be a positive integer string")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_positive_id_int(value: Any, *, field_name: str) -> int:
|
|
42
|
+
"""Normalize an id to Python int after the public boundary preserves it as text."""
|
|
43
|
+
return int(normalize_positive_id_text(value, field_name=field_name))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def ids_equal(left: Any, right: Any) -> bool:
|
|
47
|
+
left_text = stringify_backend_id(left)
|
|
48
|
+
right_text = stringify_backend_id(right)
|
|
49
|
+
return left_text is not None and right_text is not None and left_text == right_text
|
|
@@ -263,6 +263,15 @@ def _trim_workspace_list(payload: JSONObject) -> None:
|
|
|
263
263
|
_trim_item_list(page, "list", allowed=("wsId", "workspaceName", "remark"))
|
|
264
264
|
|
|
265
265
|
|
|
266
|
+
def _trim_workspace_get(payload: JSONObject) -> None:
|
|
267
|
+
workspace = payload.get("workspace")
|
|
268
|
+
if isinstance(workspace, dict):
|
|
269
|
+
payload["workspace"] = _pick(
|
|
270
|
+
workspace,
|
|
271
|
+
allowed=("wsId", "workspaceName", "remark", "systemVersion", "auth"),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
266
275
|
def _trim_app_search_like(payload: JSONObject) -> None:
|
|
267
276
|
payload.pop("apps", None)
|
|
268
277
|
_trim_item_list(payload, "items", allowed=("app_key", "app_name", "package_name"))
|
|
@@ -728,6 +737,7 @@ def _register_policy(domains: tuple[str, ...], names: tuple[str, ...], transform
|
|
|
728
737
|
_register_policy((USER_DOMAIN, BUILDER_DOMAIN), ("auth_use_credential", "auth_whoami"), _trim_auth_payload)
|
|
729
738
|
_register_policy((USER_DOMAIN, BUILDER_DOMAIN), ("auth_logout",), _trim_auth_logout)
|
|
730
739
|
_register_policy((USER_DOMAIN, BUILDER_DOMAIN), ("workspace_list",), _trim_workspace_list)
|
|
740
|
+
_register_policy((USER_DOMAIN, BUILDER_DOMAIN), ("workspace_get",), _trim_workspace_get)
|
|
731
741
|
_register_policy((USER_DOMAIN,), ("app_list", "app_search"), _trim_app_search_like)
|
|
732
742
|
_register_policy((USER_DOMAIN, BUILDER_DOMAIN), ("app_get",), _trim_app_get)
|
|
733
743
|
_register_policy((BUILDER_DOMAIN,), ("app_repair_code_blocks",), _trim_builder_list_like)
|
|
@@ -93,6 +93,16 @@ def build_builder_server() -> FastMCP:
|
|
|
93
93
|
include_external=include_external,
|
|
94
94
|
)
|
|
95
95
|
|
|
96
|
+
@server.tool()
|
|
97
|
+
def workspace_get(
|
|
98
|
+
profile: str = DEFAULT_PROFILE,
|
|
99
|
+
ws_id: int | None = None,
|
|
100
|
+
) -> dict:
|
|
101
|
+
return workspace.workspace_get(
|
|
102
|
+
profile=profile,
|
|
103
|
+
ws_id=ws_id,
|
|
104
|
+
)
|
|
105
|
+
|
|
96
106
|
@server.tool()
|
|
97
107
|
def file_upload_local(
|
|
98
108
|
profile: str = DEFAULT_PROFILE,
|
|
@@ -228,6 +228,16 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
228
228
|
include_external=include_external,
|
|
229
229
|
)
|
|
230
230
|
|
|
231
|
+
@server.tool()
|
|
232
|
+
def workspace_get(
|
|
233
|
+
profile: str = DEFAULT_PROFILE,
|
|
234
|
+
ws_id: int | None = None,
|
|
235
|
+
) -> dict:
|
|
236
|
+
return workspace.workspace_get(
|
|
237
|
+
profile=profile,
|
|
238
|
+
ws_id=ws_id,
|
|
239
|
+
)
|
|
240
|
+
|
|
231
241
|
@server.tool()
|
|
232
242
|
def app_list(profile: str = DEFAULT_PROFILE) -> dict:
|
|
233
243
|
return apps.app_list(profile=profile)
|
|
@@ -91,7 +91,7 @@ class CodeBlockTools(RecordTools):
|
|
|
91
91
|
def record_code_block_run(
|
|
92
92
|
profile: str = DEFAULT_PROFILE,
|
|
93
93
|
app_key: str = "",
|
|
94
|
-
record_id:
|
|
94
|
+
record_id: str = "",
|
|
95
95
|
code_block_field: str = "",
|
|
96
96
|
role: int = 1,
|
|
97
97
|
workflow_node_id: int | None = None,
|
|
@@ -197,7 +197,7 @@ class CodeBlockTools(RecordTools):
|
|
|
197
197
|
*,
|
|
198
198
|
profile: str,
|
|
199
199
|
app_key: str,
|
|
200
|
-
record_id: int,
|
|
200
|
+
record_id: int | str,
|
|
201
201
|
code_block_field: str,
|
|
202
202
|
role: int = 1,
|
|
203
203
|
workflow_node_id: int | None = None,
|
|
@@ -13,6 +13,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
13
13
|
|
|
14
14
|
from ..config import DEFAULT_PROFILE, DEFAULT_RECORD_LIST_TYPE
|
|
15
15
|
from ..errors import QingflowApiError, raise_tool_error
|
|
16
|
+
from ..id_utils import normalize_positive_id_int, stringify_backend_id
|
|
16
17
|
from ..json_types import JSONObject, JSONScalar, JSONValue
|
|
17
18
|
from ..list_type_labels import (
|
|
18
19
|
SYSTEM_VIEW_DEFINITIONS,
|
|
@@ -284,7 +285,7 @@ class RecordTools(ToolBase):
|
|
|
284
285
|
profile: str = DEFAULT_PROFILE,
|
|
285
286
|
app_key: str = "",
|
|
286
287
|
field_id: int = 0,
|
|
287
|
-
record_id:
|
|
288
|
+
record_id: str | None = None,
|
|
288
289
|
workflow_node_id: int | None = None,
|
|
289
290
|
fields: JSONObject | None = None,
|
|
290
291
|
keyword: str = "",
|
|
@@ -315,7 +316,7 @@ class RecordTools(ToolBase):
|
|
|
315
316
|
profile: str = DEFAULT_PROFILE,
|
|
316
317
|
app_key: str = "",
|
|
317
318
|
field_id: int = 0,
|
|
318
|
-
record_id:
|
|
319
|
+
record_id: str | None = None,
|
|
319
320
|
workflow_node_id: int | None = None,
|
|
320
321
|
fields: JSONObject | None = None,
|
|
321
322
|
keyword: str = "",
|
|
@@ -407,7 +408,7 @@ class RecordTools(ToolBase):
|
|
|
407
408
|
def record_get(
|
|
408
409
|
profile: str = DEFAULT_PROFILE,
|
|
409
410
|
app_key: str = "",
|
|
410
|
-
record_id:
|
|
411
|
+
record_id: str = "",
|
|
411
412
|
columns: list[JSONObject | int] | None = None,
|
|
412
413
|
view_id: str | None = None,
|
|
413
414
|
workflow_node_id: int | None = None,
|
|
@@ -439,7 +440,7 @@ class RecordTools(ToolBase):
|
|
|
439
440
|
@mcp.tool()
|
|
440
441
|
def record_update_schema_get(
|
|
441
442
|
app_key: str = "",
|
|
442
|
-
record_id:
|
|
443
|
+
record_id: str = "",
|
|
443
444
|
output_profile: str = "normal",
|
|
444
445
|
) -> JSONObject:
|
|
445
446
|
return self.record_update_schema_get_public(
|
|
@@ -479,7 +480,7 @@ class RecordTools(ToolBase):
|
|
|
479
480
|
)
|
|
480
481
|
def record_update(
|
|
481
482
|
app_key: str = "",
|
|
482
|
-
record_id:
|
|
483
|
+
record_id: str | None = None,
|
|
483
484
|
fields: JSONObject | None = None,
|
|
484
485
|
items: list[JSONObject] | None = None,
|
|
485
486
|
dry_run: bool = False,
|
|
@@ -505,8 +506,8 @@ class RecordTools(ToolBase):
|
|
|
505
506
|
)
|
|
506
507
|
def record_delete(
|
|
507
508
|
app_key: str = "",
|
|
508
|
-
record_id:
|
|
509
|
-
record_ids: list[
|
|
509
|
+
record_id: str | None = None,
|
|
510
|
+
record_ids: list[str] | None = None,
|
|
510
511
|
output_profile: str = "normal",
|
|
511
512
|
) -> JSONObject:
|
|
512
513
|
return self.record_delete_public(
|
|
@@ -784,14 +785,13 @@ class RecordTools(ToolBase):
|
|
|
784
785
|
*,
|
|
785
786
|
profile: str = DEFAULT_PROFILE,
|
|
786
787
|
app_key: str,
|
|
787
|
-
record_id:
|
|
788
|
+
record_id: Any,
|
|
788
789
|
output_profile: str = "normal",
|
|
789
790
|
) -> JSONObject:
|
|
790
791
|
"""执行记录相关逻辑。"""
|
|
791
792
|
if not app_key:
|
|
792
793
|
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
793
|
-
|
|
794
|
-
raise_tool_error(QingflowApiError.config_error("record_id is required"))
|
|
794
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
795
795
|
normalized_output_profile = self._normalize_public_output_profile(output_profile)
|
|
796
796
|
|
|
797
797
|
def runner(session_profile, context):
|
|
@@ -815,7 +815,7 @@ class RecordTools(ToolBase):
|
|
|
815
815
|
probes = self._probe_candidate_record_contexts(
|
|
816
816
|
context,
|
|
817
817
|
app_key=app_key,
|
|
818
|
-
apply_id=
|
|
818
|
+
apply_id=record_id_int,
|
|
819
819
|
candidate_routes=candidate_routes,
|
|
820
820
|
)
|
|
821
821
|
probe_summary: list[JSONObject] = []
|
|
@@ -906,7 +906,7 @@ class RecordTools(ToolBase):
|
|
|
906
906
|
ws_id=ws_id,
|
|
907
907
|
request_route=request_route,
|
|
908
908
|
app_key=app_key,
|
|
909
|
-
record_id=
|
|
909
|
+
record_id=record_id_int,
|
|
910
910
|
blockers=blockers,
|
|
911
911
|
warnings=warnings,
|
|
912
912
|
recommended_next_actions=recommended_next_actions,
|
|
@@ -949,7 +949,7 @@ class RecordTools(ToolBase):
|
|
|
949
949
|
ws_id=ws_id,
|
|
950
950
|
request_route=request_route,
|
|
951
951
|
app_key=app_key,
|
|
952
|
-
record_id=
|
|
952
|
+
record_id=record_id_int,
|
|
953
953
|
blockers=["NO_WRITABLE_FIELDS_FOR_RECORD"],
|
|
954
954
|
warnings=[item["message"] for item in warnings if isinstance(item.get("message"), str)],
|
|
955
955
|
recommended_next_actions=[
|
|
@@ -969,7 +969,7 @@ class RecordTools(ToolBase):
|
|
|
969
969
|
"request_route": request_route,
|
|
970
970
|
"warnings": warnings,
|
|
971
971
|
"app_key": app_key,
|
|
972
|
-
"record_id":
|
|
972
|
+
"record_id": stringify_backend_id(record_id_int),
|
|
973
973
|
"schema_scope": "update_ready",
|
|
974
974
|
"writable_fields": writable_fields,
|
|
975
975
|
"payload_template": {
|
|
@@ -1009,7 +1009,7 @@ class RecordTools(ToolBase):
|
|
|
1009
1009
|
"request_route": request_route,
|
|
1010
1010
|
"warnings": [{"code": "PREFLIGHT_WARNING", "message": item} for item in warnings],
|
|
1011
1011
|
"app_key": app_key,
|
|
1012
|
-
"record_id": record_id,
|
|
1012
|
+
"record_id": stringify_backend_id(record_id),
|
|
1013
1013
|
"schema_scope": "update_ready",
|
|
1014
1014
|
"blockers": blockers,
|
|
1015
1015
|
"writable_fields": [],
|
|
@@ -1281,7 +1281,7 @@ class RecordTools(ToolBase):
|
|
|
1281
1281
|
profile: str,
|
|
1282
1282
|
app_key: str,
|
|
1283
1283
|
field_id: int,
|
|
1284
|
-
record_id:
|
|
1284
|
+
record_id: Any | None = None,
|
|
1285
1285
|
workflow_node_id: int | None = None,
|
|
1286
1286
|
fields: JSONObject | None = None,
|
|
1287
1287
|
keyword: str,
|
|
@@ -1297,6 +1297,12 @@ class RecordTools(ToolBase):
|
|
|
1297
1297
|
raise_tool_error(QingflowApiError.config_error("page_num must be positive"))
|
|
1298
1298
|
if page_size <= 0:
|
|
1299
1299
|
raise_tool_error(QingflowApiError.config_error("page_size must be positive"))
|
|
1300
|
+
record_id_int = (
|
|
1301
|
+
normalize_positive_id_int(record_id, field_name="record_id")
|
|
1302
|
+
if record_id is not None
|
|
1303
|
+
else None
|
|
1304
|
+
)
|
|
1305
|
+
record_id_text = stringify_backend_id(record_id_int) if record_id_int is not None else None
|
|
1300
1306
|
|
|
1301
1307
|
def runner(session_profile, context):
|
|
1302
1308
|
index = self._get_field_index(profile, context, app_key, force_refresh=False)
|
|
@@ -1316,7 +1322,7 @@ class RecordTools(ToolBase):
|
|
|
1316
1322
|
)
|
|
1317
1323
|
normalized_fields = fields if isinstance(fields, dict) else {}
|
|
1318
1324
|
runtime_lookup = self._candidate_lookup_uses_runtime_scope(
|
|
1319
|
-
record_id=
|
|
1325
|
+
record_id=record_id_int,
|
|
1320
1326
|
workflow_node_id=workflow_node_id,
|
|
1321
1327
|
fields=normalized_fields,
|
|
1322
1328
|
)
|
|
@@ -1327,7 +1333,7 @@ class RecordTools(ToolBase):
|
|
|
1327
1333
|
profile,
|
|
1328
1334
|
context,
|
|
1329
1335
|
app_key=app_key,
|
|
1330
|
-
record_id=
|
|
1336
|
+
record_id=record_id_int,
|
|
1331
1337
|
workflow_node_id=workflow_node_id,
|
|
1332
1338
|
fields=normalized_fields,
|
|
1333
1339
|
)
|
|
@@ -1366,7 +1372,7 @@ class RecordTools(ToolBase):
|
|
|
1366
1372
|
"app_key": app_key,
|
|
1367
1373
|
"field_id": field.que_id,
|
|
1368
1374
|
"field_title": field.que_title,
|
|
1369
|
-
"record_id":
|
|
1375
|
+
"record_id": record_id_text,
|
|
1370
1376
|
"workflow_node_id": workflow_node_id,
|
|
1371
1377
|
"fields_present": bool(normalized_fields),
|
|
1372
1378
|
"keyword": keyword,
|
|
@@ -1385,7 +1391,7 @@ class RecordTools(ToolBase):
|
|
|
1385
1391
|
profile: str,
|
|
1386
1392
|
app_key: str,
|
|
1387
1393
|
field_id: int,
|
|
1388
|
-
record_id:
|
|
1394
|
+
record_id: Any | None = None,
|
|
1389
1395
|
workflow_node_id: int | None = None,
|
|
1390
1396
|
fields: JSONObject | None = None,
|
|
1391
1397
|
keyword: str,
|
|
@@ -1401,6 +1407,12 @@ class RecordTools(ToolBase):
|
|
|
1401
1407
|
raise_tool_error(QingflowApiError.config_error("page_num must be positive"))
|
|
1402
1408
|
if page_size <= 0:
|
|
1403
1409
|
raise_tool_error(QingflowApiError.config_error("page_size must be positive"))
|
|
1410
|
+
record_id_int = (
|
|
1411
|
+
normalize_positive_id_int(record_id, field_name="record_id")
|
|
1412
|
+
if record_id is not None
|
|
1413
|
+
else None
|
|
1414
|
+
)
|
|
1415
|
+
record_id_text = stringify_backend_id(record_id_int) if record_id_int is not None else None
|
|
1404
1416
|
|
|
1405
1417
|
def runner(session_profile, context):
|
|
1406
1418
|
index = self._get_field_index(profile, context, app_key, force_refresh=False)
|
|
@@ -1420,7 +1432,7 @@ class RecordTools(ToolBase):
|
|
|
1420
1432
|
)
|
|
1421
1433
|
normalized_fields = fields if isinstance(fields, dict) else {}
|
|
1422
1434
|
runtime_lookup = self._candidate_lookup_uses_runtime_scope(
|
|
1423
|
-
record_id=
|
|
1435
|
+
record_id=record_id_int,
|
|
1424
1436
|
workflow_node_id=workflow_node_id,
|
|
1425
1437
|
fields=normalized_fields,
|
|
1426
1438
|
)
|
|
@@ -1431,7 +1443,7 @@ class RecordTools(ToolBase):
|
|
|
1431
1443
|
profile,
|
|
1432
1444
|
context,
|
|
1433
1445
|
app_key=app_key,
|
|
1434
|
-
record_id=
|
|
1446
|
+
record_id=record_id_int,
|
|
1435
1447
|
workflow_node_id=workflow_node_id,
|
|
1436
1448
|
fields=normalized_fields,
|
|
1437
1449
|
)
|
|
@@ -1487,7 +1499,7 @@ class RecordTools(ToolBase):
|
|
|
1487
1499
|
"app_key": app_key,
|
|
1488
1500
|
"field_id": field.que_id,
|
|
1489
1501
|
"field_title": field.que_title,
|
|
1490
|
-
"record_id":
|
|
1502
|
+
"record_id": record_id_text,
|
|
1491
1503
|
"workflow_node_id": workflow_node_id,
|
|
1492
1504
|
"fields_present": bool(normalized_fields),
|
|
1493
1505
|
"keyword": keyword,
|
|
@@ -1753,7 +1765,7 @@ class RecordTools(ToolBase):
|
|
|
1753
1765
|
*,
|
|
1754
1766
|
profile: str,
|
|
1755
1767
|
app_key: str,
|
|
1756
|
-
record_id:
|
|
1768
|
+
record_id: Any,
|
|
1757
1769
|
columns: list[JSONObject | int],
|
|
1758
1770
|
view_id: str | None = None,
|
|
1759
1771
|
workflow_node_id: int | None = None,
|
|
@@ -1761,8 +1773,7 @@ class RecordTools(ToolBase):
|
|
|
1761
1773
|
) -> JSONObject:
|
|
1762
1774
|
"""执行记录相关逻辑。"""
|
|
1763
1775
|
normalized_output_profile = self._normalize_public_output_profile(output_profile, allow_normalized=True)
|
|
1764
|
-
|
|
1765
|
-
raise_tool_error(QingflowApiError.config_error("record_id must be positive"))
|
|
1776
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
1766
1777
|
normalized_columns = _normalize_public_column_selectors(columns)
|
|
1767
1778
|
|
|
1768
1779
|
def runner(session_profile, context):
|
|
@@ -1791,7 +1802,7 @@ class RecordTools(ToolBase):
|
|
|
1791
1802
|
result = self.backend.request(
|
|
1792
1803
|
"GET",
|
|
1793
1804
|
context,
|
|
1794
|
-
f"/view/{resolved_view.view_selection.view_key}/apply/{
|
|
1805
|
+
f"/view/{resolved_view.view_selection.view_key}/apply/{record_id_int}",
|
|
1795
1806
|
)
|
|
1796
1807
|
used_list_type = None
|
|
1797
1808
|
else:
|
|
@@ -1808,7 +1819,7 @@ class RecordTools(ToolBase):
|
|
|
1808
1819
|
result = self.backend.request(
|
|
1809
1820
|
"GET",
|
|
1810
1821
|
context,
|
|
1811
|
-
f"/app/{app_key}/apply/{
|
|
1822
|
+
f"/app/{app_key}/apply/{record_id_int}",
|
|
1812
1823
|
params={"role": 1, "listType": lt},
|
|
1813
1824
|
)
|
|
1814
1825
|
used_list_type = lt
|
|
@@ -1823,7 +1834,7 @@ class RecordTools(ToolBase):
|
|
|
1823
1834
|
raise last_error
|
|
1824
1835
|
raise_tool_error(QingflowApiError.config_error("record_get failed: no accessible listType"))
|
|
1825
1836
|
answer_list = result.get("answers") if isinstance(result, dict) and isinstance(result.get("answers"), list) else []
|
|
1826
|
-
row = _build_flat_row(cast(list[JSONValue], answer_list), selected_fields, apply_id=
|
|
1837
|
+
row = _build_flat_row(cast(list[JSONValue], answer_list), selected_fields, apply_id=record_id_int)
|
|
1827
1838
|
normalized_record, normalized_ambiguous_fields = _build_normalized_row_from_answers(
|
|
1828
1839
|
cast(list[JSONValue], answer_list),
|
|
1829
1840
|
selected_fields,
|
|
@@ -1851,7 +1862,7 @@ class RecordTools(ToolBase):
|
|
|
1851
1862
|
if normalized_columns
|
|
1852
1863
|
else list(index.by_id.values())
|
|
1853
1864
|
)
|
|
1854
|
-
row = _build_flat_row(cast(list[JSONValue], answer_list), selected_fields, apply_id=
|
|
1865
|
+
row = _build_flat_row(cast(list[JSONValue], answer_list), selected_fields, apply_id=record_id_int)
|
|
1855
1866
|
normalized_record, normalized_ambiguous_fields = _build_normalized_row_from_answers(
|
|
1856
1867
|
cast(list[JSONValue], answer_list),
|
|
1857
1868
|
selected_fields,
|
|
@@ -1874,7 +1885,7 @@ class RecordTools(ToolBase):
|
|
|
1874
1885
|
"output_profile": normalized_output_profile,
|
|
1875
1886
|
"data": {
|
|
1876
1887
|
"app_key": app_key,
|
|
1877
|
-
"record_id": _public_record_id_text(
|
|
1888
|
+
"record_id": _public_record_id_text(record_id_int),
|
|
1878
1889
|
"record": row,
|
|
1879
1890
|
"selection": {
|
|
1880
1891
|
"columns": [_column_selector_payload(field_id) for field_id in normalized_columns] if normalized_columns else [],
|
|
@@ -1975,7 +1986,7 @@ class RecordTools(ToolBase):
|
|
|
1975
1986
|
*,
|
|
1976
1987
|
profile: str = DEFAULT_PROFILE,
|
|
1977
1988
|
app_key: str,
|
|
1978
|
-
record_id:
|
|
1989
|
+
record_id: Any | None,
|
|
1979
1990
|
fields: JSONObject | None = None,
|
|
1980
1991
|
items: list[JSONObject] | None = None,
|
|
1981
1992
|
dry_run: bool = False,
|
|
@@ -2004,14 +2015,15 @@ class RecordTools(ToolBase):
|
|
|
2004
2015
|
)
|
|
2005
2016
|
if dry_run:
|
|
2006
2017
|
raise_tool_error(QingflowApiError.config_error("dry_run currently requires items"))
|
|
2007
|
-
if record_id is None
|
|
2018
|
+
if record_id is None:
|
|
2008
2019
|
raise_tool_error(QingflowApiError.config_error("record_id is required"))
|
|
2020
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
2009
2021
|
if fields is not None and not isinstance(fields, dict):
|
|
2010
2022
|
raise_tool_error(QingflowApiError.config_error("fields must be an object map keyed by field title"))
|
|
2011
2023
|
return self._record_update_public_single(
|
|
2012
2024
|
profile=profile,
|
|
2013
2025
|
app_key=app_key,
|
|
2014
|
-
record_id=
|
|
2026
|
+
record_id=record_id_int,
|
|
2015
2027
|
fields=cast(JSONObject, fields or {}),
|
|
2016
2028
|
verify_write=verify_write,
|
|
2017
2029
|
output_profile=normalized_output_profile,
|
|
@@ -2201,7 +2213,7 @@ class RecordTools(ToolBase):
|
|
|
2201
2213
|
def _normalize_public_record_update_batch_items(
|
|
2202
2214
|
self,
|
|
2203
2215
|
*,
|
|
2204
|
-
record_id:
|
|
2216
|
+
record_id: Any | None,
|
|
2205
2217
|
fields: JSONObject | None,
|
|
2206
2218
|
items: list[JSONObject] | None,
|
|
2207
2219
|
) -> list[JSONObject]:
|
|
@@ -2217,9 +2229,10 @@ class RecordTools(ToolBase):
|
|
|
2217
2229
|
for index, item in enumerate(items):
|
|
2218
2230
|
if not isinstance(item, dict):
|
|
2219
2231
|
raise_tool_error(QingflowApiError.config_error(f"items[{index}] must be an object"))
|
|
2220
|
-
normalized_record_id =
|
|
2221
|
-
|
|
2222
|
-
|
|
2232
|
+
normalized_record_id = normalize_positive_id_int(
|
|
2233
|
+
item.get("record_id"),
|
|
2234
|
+
field_name=f"items[{index}].record_id",
|
|
2235
|
+
)
|
|
2223
2236
|
if normalized_record_id in seen_record_ids:
|
|
2224
2237
|
raise_tool_error(
|
|
2225
2238
|
QingflowApiError.config_error(f"duplicate record_id in items: {normalized_record_id}")
|
|
@@ -3069,22 +3082,26 @@ class RecordTools(ToolBase):
|
|
|
3069
3082
|
*,
|
|
3070
3083
|
profile: str = DEFAULT_PROFILE,
|
|
3071
3084
|
app_key: str,
|
|
3072
|
-
record_id:
|
|
3073
|
-
record_ids: list[
|
|
3085
|
+
record_id: Any | None = None,
|
|
3086
|
+
record_ids: list[Any] | None = None,
|
|
3074
3087
|
output_profile: str = "normal",
|
|
3075
3088
|
) -> JSONObject:
|
|
3076
3089
|
"""执行记录相关逻辑。"""
|
|
3077
3090
|
normalized_output_profile = self._normalize_public_output_profile(output_profile)
|
|
3078
3091
|
if not app_key:
|
|
3079
3092
|
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
3080
|
-
normalized_record_ids
|
|
3081
|
-
|
|
3093
|
+
normalized_record_ids: list[int] = []
|
|
3094
|
+
for index, item in enumerate(record_ids or []):
|
|
3095
|
+
normalized_record_ids.append(normalize_positive_id_int(item, field_name=f"record_ids[{index}]"))
|
|
3096
|
+
delete_ids = normalized_record_ids
|
|
3097
|
+
if not delete_ids and record_id is not None:
|
|
3098
|
+
delete_ids = [normalize_positive_id_int(record_id, field_name="record_id")]
|
|
3082
3099
|
if not delete_ids:
|
|
3083
3100
|
raise_tool_error(QingflowApiError.config_error("record_id or record_ids is required"))
|
|
3084
3101
|
normalized_payload = {
|
|
3085
3102
|
"operation": "delete",
|
|
3086
|
-
"record_id": record_id,
|
|
3087
|
-
"record_ids": delete_ids,
|
|
3103
|
+
"record_id": stringify_backend_id(record_id) if record_id is not None else None,
|
|
3104
|
+
"record_ids": [stringify_backend_id(item) for item in delete_ids],
|
|
3088
3105
|
"answers": [],
|
|
3089
3106
|
"submit_type": 1,
|
|
3090
3107
|
}
|
|
@@ -8527,8 +8544,8 @@ class RecordTools(ToolBase):
|
|
|
8527
8544
|
"""执行内部辅助逻辑。"""
|
|
8528
8545
|
payload: JSONObject = {
|
|
8529
8546
|
"operation": operation,
|
|
8530
|
-
"record_id": record_id,
|
|
8531
|
-
"record_ids": record_ids,
|
|
8547
|
+
"record_id": stringify_backend_id(record_id) if record_id is not None else None,
|
|
8548
|
+
"record_ids": [stringify_backend_id(item) for item in record_ids],
|
|
8532
8549
|
"answers": normalized_answers,
|
|
8533
8550
|
"submit_type": submit_type,
|
|
8534
8551
|
}
|
|
@@ -8727,7 +8744,7 @@ class RecordTools(ToolBase):
|
|
|
8727
8744
|
"output_profile": output_profile,
|
|
8728
8745
|
"data": {
|
|
8729
8746
|
"action": {"operation": operation, "executed": True},
|
|
8730
|
-
"resource": raw_apply.get("resource"),
|
|
8747
|
+
"resource": _public_record_resource(raw_apply.get("resource")),
|
|
8731
8748
|
"verification": raw_apply.get("verification"),
|
|
8732
8749
|
"normalized_payload": normalized_payload,
|
|
8733
8750
|
"blockers": [],
|
|
@@ -10072,8 +10089,9 @@ class RecordTools(ToolBase):
|
|
|
10072
10089
|
"""执行内部辅助逻辑。"""
|
|
10073
10090
|
if not app_key:
|
|
10074
10091
|
raise_tool_error(QingflowApiError.config_error("app_key is required"))
|
|
10075
|
-
|
|
10076
|
-
|
|
10092
|
+
try:
|
|
10093
|
+
normalized_apply_id = normalize_positive_id_int(apply_id, field_name="apply_id")
|
|
10094
|
+
except QingflowApiError:
|
|
10077
10095
|
raise_tool_error(QingflowApiError.config_error("apply_id must be positive"))
|
|
10078
10096
|
return normalized_apply_id
|
|
10079
10097
|
|
|
@@ -11084,10 +11102,14 @@ def _build_flat_row(answer_list: list[JSONValue], fields: list[FormField], *, ap
|
|
|
11084
11102
|
return row
|
|
11085
11103
|
|
|
11086
11104
|
|
|
11087
|
-
def _public_record_id_text(record_id:
|
|
11088
|
-
if record_id is None or record_id
|
|
11105
|
+
def _public_record_id_text(record_id: Any) -> str | None:
|
|
11106
|
+
if record_id is None or isinstance(record_id, bool):
|
|
11089
11107
|
return None
|
|
11090
|
-
|
|
11108
|
+
if isinstance(record_id, int) and record_id <= 0:
|
|
11109
|
+
return None
|
|
11110
|
+
if isinstance(record_id, str) and (not record_id.strip() or not record_id.strip().isdecimal()):
|
|
11111
|
+
return None
|
|
11112
|
+
return stringify_backend_id(record_id)
|
|
11091
11113
|
|
|
11092
11114
|
|
|
11093
11115
|
def _normalize_public_record_rows(rows: list[JSONValue]) -> list[JSONObject]:
|
|
@@ -11640,10 +11662,29 @@ def _field_mapping_entry(role: str, field: FormField | None, *, requested: str)
|
|
|
11640
11662
|
}
|
|
11641
11663
|
|
|
11642
11664
|
|
|
11643
|
-
def _record_resource_payload(record_id:
|
|
11644
|
-
|
|
11665
|
+
def _record_resource_payload(record_id: Any) -> JSONObject | None:
|
|
11666
|
+
public_record_id = _public_record_id_text(record_id)
|
|
11667
|
+
if public_record_id is None:
|
|
11645
11668
|
return None
|
|
11646
|
-
return {"type": "record", "record_id":
|
|
11669
|
+
return {"type": "record", "record_id": public_record_id, "apply_id": public_record_id}
|
|
11670
|
+
|
|
11671
|
+
|
|
11672
|
+
def _public_record_resource(resource: Any) -> Any:
|
|
11673
|
+
if not isinstance(resource, dict) or resource.get("type") != "record":
|
|
11674
|
+
return resource
|
|
11675
|
+
payload = dict(resource)
|
|
11676
|
+
if "record_id" in payload:
|
|
11677
|
+
payload["record_id"] = _public_record_id_text(payload.get("record_id"))
|
|
11678
|
+
if "apply_id" in payload:
|
|
11679
|
+
payload["apply_id"] = _public_record_id_text(payload.get("apply_id"))
|
|
11680
|
+
record_ids = payload.get("record_ids")
|
|
11681
|
+
if isinstance(record_ids, list):
|
|
11682
|
+
payload["record_ids"] = [
|
|
11683
|
+
stringify_backend_id(item)
|
|
11684
|
+
for item in record_ids
|
|
11685
|
+
if stringify_backend_id(item) is not None
|
|
11686
|
+
]
|
|
11687
|
+
return payload
|
|
11647
11688
|
|
|
11648
11689
|
|
|
11649
11690
|
def _query_id() -> str:
|
|
@@ -10,6 +10,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
10
10
|
from ..backend_client import BackendRequestContext
|
|
11
11
|
from ..config import DEFAULT_PROFILE
|
|
12
12
|
from ..errors import QingflowApiError, raise_tool_error
|
|
13
|
+
from ..id_utils import ids_equal, normalize_positive_id_int, stringify_backend_id
|
|
13
14
|
from ..json_types import JSONObject
|
|
14
15
|
from .approval_tools import ApprovalTools, _approval_page_amount, _approval_page_items, _approval_page_total
|
|
15
16
|
from .base import ToolBase, tool_cn_name
|
|
@@ -87,7 +88,7 @@ class TaskContextTools(ToolBase):
|
|
|
87
88
|
def task_get(
|
|
88
89
|
profile: str = DEFAULT_PROFILE,
|
|
89
90
|
app_key: str = "",
|
|
90
|
-
record_id:
|
|
91
|
+
record_id: str = "",
|
|
91
92
|
workflow_node_id: int = 0,
|
|
92
93
|
include_candidates: bool = True,
|
|
93
94
|
include_associated_reports: bool = True,
|
|
@@ -105,7 +106,7 @@ class TaskContextTools(ToolBase):
|
|
|
105
106
|
def task_action_execute(
|
|
106
107
|
profile: str = DEFAULT_PROFILE,
|
|
107
108
|
app_key: str = "",
|
|
108
|
-
record_id:
|
|
109
|
+
record_id: str = "",
|
|
109
110
|
workflow_node_id: int = 0,
|
|
110
111
|
action: str = "",
|
|
111
112
|
payload: dict[str, Any] | None = None,
|
|
@@ -125,7 +126,7 @@ class TaskContextTools(ToolBase):
|
|
|
125
126
|
def task_associated_report_detail_get(
|
|
126
127
|
profile: str = DEFAULT_PROFILE,
|
|
127
128
|
app_key: str = "",
|
|
128
|
-
record_id:
|
|
129
|
+
record_id: str = "",
|
|
129
130
|
workflow_node_id: int = 0,
|
|
130
131
|
report_id: int = 0,
|
|
131
132
|
page: int = 1,
|
|
@@ -145,7 +146,7 @@ class TaskContextTools(ToolBase):
|
|
|
145
146
|
def task_workflow_log_get(
|
|
146
147
|
profile: str = DEFAULT_PROFILE,
|
|
147
148
|
app_key: str = "",
|
|
148
|
-
record_id:
|
|
149
|
+
record_id: str = "",
|
|
149
150
|
workflow_node_id: int = 0,
|
|
150
151
|
) -> dict[str, Any]:
|
|
151
152
|
return self.task_workflow_log_get(
|
|
@@ -249,20 +250,21 @@ class TaskContextTools(ToolBase):
|
|
|
249
250
|
*,
|
|
250
251
|
profile: str,
|
|
251
252
|
app_key: str,
|
|
252
|
-
record_id:
|
|
253
|
+
record_id: Any,
|
|
253
254
|
workflow_node_id: int,
|
|
254
255
|
include_candidates: bool,
|
|
255
256
|
include_associated_reports: bool,
|
|
256
257
|
) -> dict[str, Any]:
|
|
257
258
|
"""执行任务相关逻辑。"""
|
|
258
|
-
|
|
259
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
260
|
+
self._require_app_record_and_node(app_key, record_id_int, workflow_node_id)
|
|
259
261
|
|
|
260
262
|
def runner(session_profile, context):
|
|
261
263
|
data = self._build_task_context(
|
|
262
264
|
profile=profile,
|
|
263
265
|
context=context,
|
|
264
266
|
app_key=app_key,
|
|
265
|
-
record_id=
|
|
267
|
+
record_id=record_id_int,
|
|
266
268
|
workflow_node_id=workflow_node_id,
|
|
267
269
|
include_candidates=include_candidates,
|
|
268
270
|
include_associated_reports=include_associated_reports,
|
|
@@ -287,7 +289,7 @@ class TaskContextTools(ToolBase):
|
|
|
287
289
|
*,
|
|
288
290
|
profile: str,
|
|
289
291
|
app_key: str,
|
|
290
|
-
record_id:
|
|
292
|
+
record_id: Any,
|
|
291
293
|
workflow_node_id: int,
|
|
292
294
|
fields: dict[str, Any] | None = None,
|
|
293
295
|
) -> dict[str, Any]:
|
|
@@ -311,14 +313,16 @@ class TaskContextTools(ToolBase):
|
|
|
311
313
|
*,
|
|
312
314
|
profile: str,
|
|
313
315
|
app_key: str,
|
|
314
|
-
record_id:
|
|
316
|
+
record_id: Any,
|
|
315
317
|
workflow_node_id: int,
|
|
316
318
|
action: str,
|
|
317
319
|
payload: dict[str, Any],
|
|
318
320
|
fields: dict[str, Any] | None = None,
|
|
319
321
|
) -> dict[str, Any]:
|
|
320
322
|
"""执行任务相关逻辑。"""
|
|
321
|
-
|
|
323
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
324
|
+
record_id_text = stringify_backend_id(record_id_int)
|
|
325
|
+
self._require_app_record_and_node(app_key, record_id_int, workflow_node_id)
|
|
322
326
|
normalized_action = (action or "").strip().lower()
|
|
323
327
|
if normalized_action not in {"approve", "reject", "rollback", "transfer", "urge", "save_only"}:
|
|
324
328
|
raise_tool_error(
|
|
@@ -341,7 +345,7 @@ class TaskContextTools(ToolBase):
|
|
|
341
345
|
profile=profile,
|
|
342
346
|
context=context,
|
|
343
347
|
app_key=app_key,
|
|
344
|
-
record_id=
|
|
348
|
+
record_id=record_id_int,
|
|
345
349
|
workflow_node_id=workflow_node_id,
|
|
346
350
|
include_candidates=False,
|
|
347
351
|
include_associated_reports=False,
|
|
@@ -354,7 +358,7 @@ class TaskContextTools(ToolBase):
|
|
|
354
358
|
session_profile=session_profile,
|
|
355
359
|
context=context,
|
|
356
360
|
app_key=app_key,
|
|
357
|
-
record_id=
|
|
361
|
+
record_id=record_id_int,
|
|
358
362
|
workflow_node_id=workflow_node_id,
|
|
359
363
|
action=normalized_action,
|
|
360
364
|
source_error=error,
|
|
@@ -387,7 +391,7 @@ class TaskContextTools(ToolBase):
|
|
|
387
391
|
raise_tool_error(QingflowApiError.config_error(message))
|
|
388
392
|
raise_tool_error(
|
|
389
393
|
QingflowApiError.config_error(
|
|
390
|
-
f"task action '{normalized_action}' is not currently available for app_key='{app_key}' record_id={
|
|
394
|
+
f"task action '{normalized_action}' is not currently available for app_key='{app_key}' record_id={record_id_text} workflow_node_id={workflow_node_id}"
|
|
391
395
|
)
|
|
392
396
|
)
|
|
393
397
|
feedback_required_for = capabilities.get("action_constraints", {}).get("feedback_required_for") or []
|
|
@@ -409,7 +413,7 @@ class TaskContextTools(ToolBase):
|
|
|
409
413
|
profile=profile,
|
|
410
414
|
context=context,
|
|
411
415
|
app_key=app_key,
|
|
412
|
-
record_id=
|
|
416
|
+
record_id=record_id_int,
|
|
413
417
|
workflow_node_id=workflow_node_id,
|
|
414
418
|
task_context=task_context,
|
|
415
419
|
fields=field_updates,
|
|
@@ -421,14 +425,14 @@ class TaskContextTools(ToolBase):
|
|
|
421
425
|
profile=profile,
|
|
422
426
|
context=context,
|
|
423
427
|
app_key=app_key,
|
|
424
|
-
record_id=
|
|
428
|
+
record_id=record_id_int,
|
|
425
429
|
workflow_node_id=workflow_node_id,
|
|
426
430
|
)
|
|
427
431
|
try:
|
|
428
432
|
raw = self._execute_task_action(
|
|
429
433
|
profile=profile,
|
|
430
434
|
app_key=app_key,
|
|
431
|
-
record_id=
|
|
435
|
+
record_id=record_id_int,
|
|
432
436
|
workflow_node_id=workflow_node_id,
|
|
433
437
|
normalized_action=normalized_action,
|
|
434
438
|
payload=body,
|
|
@@ -441,7 +445,7 @@ class TaskContextTools(ToolBase):
|
|
|
441
445
|
session_profile=session_profile,
|
|
442
446
|
context=context,
|
|
443
447
|
app_key=app_key,
|
|
444
|
-
record_id=
|
|
448
|
+
record_id=record_id_int,
|
|
445
449
|
workflow_node_id=workflow_node_id,
|
|
446
450
|
action=normalized_action,
|
|
447
451
|
source_error=error,
|
|
@@ -453,7 +457,7 @@ class TaskContextTools(ToolBase):
|
|
|
453
457
|
verification, warnings = self._verify_task_save_only(
|
|
454
458
|
context=context,
|
|
455
459
|
app_key=app_key,
|
|
456
|
-
record_id=
|
|
460
|
+
record_id=record_id_int,
|
|
457
461
|
workflow_node_id=workflow_node_id,
|
|
458
462
|
before_apply_status=before_apply_status,
|
|
459
463
|
expected_answers=((prepared_fields or {}).get("normalized_answers") or []),
|
|
@@ -467,7 +471,7 @@ class TaskContextTools(ToolBase):
|
|
|
467
471
|
profile=profile,
|
|
468
472
|
context=context,
|
|
469
473
|
app_key=app_key,
|
|
470
|
-
record_id=
|
|
474
|
+
record_id=record_id_int,
|
|
471
475
|
workflow_node_id=workflow_node_id,
|
|
472
476
|
action=normalized_action,
|
|
473
477
|
before_apply_status=before_apply_status,
|
|
@@ -490,7 +494,7 @@ class TaskContextTools(ToolBase):
|
|
|
490
494
|
"action": normalized_action,
|
|
491
495
|
"resource": {
|
|
492
496
|
"app_key": app_key,
|
|
493
|
-
"record_id":
|
|
497
|
+
"record_id": record_id_text,
|
|
494
498
|
"workflow_node_id": workflow_node_id,
|
|
495
499
|
},
|
|
496
500
|
"selection": {"action": normalized_action},
|
|
@@ -507,7 +511,7 @@ class TaskContextTools(ToolBase):
|
|
|
507
511
|
*,
|
|
508
512
|
profile: str,
|
|
509
513
|
app_key: str,
|
|
510
|
-
record_id:
|
|
514
|
+
record_id: Any,
|
|
511
515
|
workflow_node_id: int,
|
|
512
516
|
normalized_action: str,
|
|
513
517
|
payload: dict[str, Any],
|
|
@@ -671,12 +675,12 @@ class TaskContextTools(ToolBase):
|
|
|
671
675
|
todo_items = self._safe_task_list_items(profile=profile, task_box="todo", app_key=app_key)
|
|
672
676
|
initiated_items = self._safe_task_list_items(profile=profile, task_box="initiated", app_key=app_key)
|
|
673
677
|
downstream_todo_detected = any(
|
|
674
|
-
|
|
678
|
+
ids_equal(item.get("record_id"), record_id) and int(item.get("workflow_node_id") or 0) != workflow_node_id
|
|
675
679
|
for item in todo_items
|
|
676
680
|
if isinstance(item, dict)
|
|
677
681
|
)
|
|
678
682
|
initiated_visible = any(
|
|
679
|
-
|
|
683
|
+
ids_equal(item.get("record_id"), record_id)
|
|
680
684
|
for item in initiated_items
|
|
681
685
|
if isinstance(item, dict)
|
|
682
686
|
)
|
|
@@ -693,7 +697,7 @@ class TaskContextTools(ToolBase):
|
|
|
693
697
|
int(item.get("workflow_node_id") or 0)
|
|
694
698
|
for item in todo_items
|
|
695
699
|
if isinstance(item, dict)
|
|
696
|
-
and
|
|
700
|
+
and ids_equal(item.get("record_id"), record_id)
|
|
697
701
|
and int(item.get("workflow_node_id") or 0) != workflow_node_id
|
|
698
702
|
}
|
|
699
703
|
workflow_log_digest = self._workflow_log_digest(log_items)
|
|
@@ -898,7 +902,7 @@ class TaskContextTools(ToolBase):
|
|
|
898
902
|
int(item.get("workflow_node_id") or 0)
|
|
899
903
|
for item in todo_items
|
|
900
904
|
if isinstance(item, dict)
|
|
901
|
-
and
|
|
905
|
+
and ids_equal(item.get("record_id"), record_id)
|
|
902
906
|
and int(item.get("workflow_node_id") or 0) != workflow_node_id
|
|
903
907
|
}
|
|
904
908
|
)
|
|
@@ -918,6 +922,7 @@ class TaskContextTools(ToolBase):
|
|
|
918
922
|
before_apply_status: Any,
|
|
919
923
|
) -> dict[str, Any]:
|
|
920
924
|
"""执行内部辅助逻辑。"""
|
|
925
|
+
record_id_text = stringify_backend_id(record_id)
|
|
921
926
|
verification, warnings = self._verify_task_action_runtime(
|
|
922
927
|
profile=profile,
|
|
923
928
|
context=context,
|
|
@@ -950,7 +955,7 @@ class TaskContextTools(ToolBase):
|
|
|
950
955
|
"action": action,
|
|
951
956
|
"resource": {
|
|
952
957
|
"app_key": app_key,
|
|
953
|
-
"record_id":
|
|
958
|
+
"record_id": record_id_text,
|
|
954
959
|
"workflow_node_id": workflow_node_id,
|
|
955
960
|
},
|
|
956
961
|
"selection": {"action": action},
|
|
@@ -978,7 +983,7 @@ class TaskContextTools(ToolBase):
|
|
|
978
983
|
"action": action,
|
|
979
984
|
"resource": {
|
|
980
985
|
"app_key": app_key,
|
|
981
|
-
"record_id":
|
|
986
|
+
"record_id": record_id_text,
|
|
982
987
|
"workflow_node_id": workflow_node_id,
|
|
983
988
|
},
|
|
984
989
|
"selection": {"action": action},
|
|
@@ -1101,7 +1106,9 @@ class TaskContextTools(ToolBase):
|
|
|
1101
1106
|
page_size: int,
|
|
1102
1107
|
) -> dict[str, Any]:
|
|
1103
1108
|
"""执行任务相关逻辑。"""
|
|
1104
|
-
|
|
1109
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
1110
|
+
record_id_text = stringify_backend_id(record_id_int)
|
|
1111
|
+
self._require_app_record_and_node(app_key, record_id_int, workflow_node_id)
|
|
1105
1112
|
if report_id <= 0:
|
|
1106
1113
|
raise_tool_error(QingflowApiError.config_error("report_id must be positive"))
|
|
1107
1114
|
if page <= 0 or page_size <= 0:
|
|
@@ -1112,7 +1119,7 @@ class TaskContextTools(ToolBase):
|
|
|
1112
1119
|
profile=profile,
|
|
1113
1120
|
context=context,
|
|
1114
1121
|
app_key=app_key,
|
|
1115
|
-
record_id=
|
|
1122
|
+
record_id=record_id_int,
|
|
1116
1123
|
workflow_node_id=workflow_node_id,
|
|
1117
1124
|
include_candidates=False,
|
|
1118
1125
|
include_associated_reports=True,
|
|
@@ -1122,7 +1129,7 @@ class TaskContextTools(ToolBase):
|
|
|
1122
1129
|
if report_item is None:
|
|
1123
1130
|
raise_tool_error(
|
|
1124
1131
|
QingflowApiError.config_error(
|
|
1125
|
-
f"report_id={report_id} is not visible for app_key='{app_key}' record_id={
|
|
1132
|
+
f"report_id={report_id} is not visible for app_key='{app_key}' record_id={record_id_text} workflow_node_id={workflow_node_id}"
|
|
1126
1133
|
)
|
|
1127
1134
|
)
|
|
1128
1135
|
association_query = self._build_association_query(
|
|
@@ -1131,7 +1138,7 @@ class TaskContextTools(ToolBase):
|
|
|
1131
1138
|
)
|
|
1132
1139
|
selection = {
|
|
1133
1140
|
"app_key": app_key,
|
|
1134
|
-
"record_id":
|
|
1141
|
+
"record_id": record_id_text,
|
|
1135
1142
|
"workflow_node_id": workflow_node_id,
|
|
1136
1143
|
"report_id": report_id,
|
|
1137
1144
|
"target_app_key": report_item.get("target_app_key"),
|
|
@@ -1286,18 +1293,20 @@ class TaskContextTools(ToolBase):
|
|
|
1286
1293
|
*,
|
|
1287
1294
|
profile: str,
|
|
1288
1295
|
app_key: str,
|
|
1289
|
-
record_id:
|
|
1296
|
+
record_id: Any,
|
|
1290
1297
|
workflow_node_id: int,
|
|
1291
1298
|
) -> dict[str, Any]:
|
|
1292
1299
|
"""执行任务相关逻辑。"""
|
|
1293
|
-
|
|
1300
|
+
record_id_int = normalize_positive_id_int(record_id, field_name="record_id")
|
|
1301
|
+
record_id_text = stringify_backend_id(record_id_int)
|
|
1302
|
+
self._require_app_record_and_node(app_key, record_id_int, workflow_node_id)
|
|
1294
1303
|
|
|
1295
1304
|
def runner(session_profile, context):
|
|
1296
1305
|
task_context = self._build_task_context(
|
|
1297
1306
|
profile=profile,
|
|
1298
1307
|
context=context,
|
|
1299
1308
|
app_key=app_key,
|
|
1300
|
-
record_id=
|
|
1309
|
+
record_id=record_id_int,
|
|
1301
1310
|
workflow_node_id=workflow_node_id,
|
|
1302
1311
|
include_candidates=False,
|
|
1303
1312
|
include_associated_reports=False,
|
|
@@ -1307,7 +1316,7 @@ class TaskContextTools(ToolBase):
|
|
|
1307
1316
|
if not visibility.get("audit_record_visible"):
|
|
1308
1317
|
raise_tool_error(
|
|
1309
1318
|
QingflowApiError.config_error(
|
|
1310
|
-
f"workflow logs are not visible for app_key='{app_key}' record_id={
|
|
1319
|
+
f"workflow logs are not visible for app_key='{app_key}' record_id={record_id_text} workflow_node_id={workflow_node_id}"
|
|
1311
1320
|
)
|
|
1312
1321
|
)
|
|
1313
1322
|
page = self.backend.request(
|
|
@@ -1316,7 +1325,7 @@ class TaskContextTools(ToolBase):
|
|
|
1316
1325
|
"/application/workflow/node/record",
|
|
1317
1326
|
json_body={
|
|
1318
1327
|
"key": app_key,
|
|
1319
|
-
"rowRecordId":
|
|
1328
|
+
"rowRecordId": record_id_int,
|
|
1320
1329
|
"nodeId": workflow_node_id,
|
|
1321
1330
|
"role": 3,
|
|
1322
1331
|
"pageNum": 1,
|
|
@@ -1334,7 +1343,7 @@ class TaskContextTools(ToolBase):
|
|
|
1334
1343
|
"data": {
|
|
1335
1344
|
"selection": {
|
|
1336
1345
|
"app_key": app_key,
|
|
1337
|
-
"record_id":
|
|
1346
|
+
"record_id": record_id_text,
|
|
1338
1347
|
"workflow_node_id": workflow_node_id,
|
|
1339
1348
|
},
|
|
1340
1349
|
"visibility": {
|
|
@@ -1443,11 +1452,12 @@ class TaskContextTools(ToolBase):
|
|
|
1443
1452
|
save_only_source=save_only_source,
|
|
1444
1453
|
)
|
|
1445
1454
|
visibility = self._build_visibility(node_info, detail)
|
|
1455
|
+
record_id_text = stringify_backend_id(record_id)
|
|
1446
1456
|
return {
|
|
1447
1457
|
"task": {
|
|
1448
1458
|
"app_key": app_key,
|
|
1449
1459
|
"app_name": app_name,
|
|
1450
|
-
"record_id":
|
|
1460
|
+
"record_id": record_id_text,
|
|
1451
1461
|
"workflow_node_id": workflow_node_id,
|
|
1452
1462
|
"workflow_node_name": node_info.get("auditNodeName") or node_info.get("nodeName"),
|
|
1453
1463
|
"actionable": True,
|
|
@@ -1458,7 +1468,7 @@ class TaskContextTools(ToolBase):
|
|
|
1458
1468
|
"raw": dict(node_info),
|
|
1459
1469
|
},
|
|
1460
1470
|
"record": {
|
|
1461
|
-
"apply_id": detail.get("applyId"
|
|
1471
|
+
"apply_id": stringify_backend_id(detail.get("applyId") or record_id),
|
|
1462
1472
|
"apply_status": detail.get("applyStatus"),
|
|
1463
1473
|
"apply_num": detail.get("applyNum"),
|
|
1464
1474
|
"custom_apply_num": detail.get("customApplyNum"),
|
|
@@ -1526,7 +1536,7 @@ class TaskContextTools(ToolBase):
|
|
|
1526
1536
|
"task": {
|
|
1527
1537
|
"app_key": task.get("app_key"),
|
|
1528
1538
|
"app_name": task.get("app_name"),
|
|
1529
|
-
"record_id": task.get("record_id"),
|
|
1539
|
+
"record_id": stringify_backend_id(task.get("record_id")),
|
|
1530
1540
|
"workflow_node_id": task.get("workflow_node_id"),
|
|
1531
1541
|
"workflow_node_name": task.get("workflow_node_name"),
|
|
1532
1542
|
"initiator": self._compact_initiator(record.get("apply_user")),
|
|
@@ -1722,10 +1732,10 @@ class TaskContextTools(ToolBase):
|
|
|
1722
1732
|
record_id = raw.get("rowRecordId") or raw.get("recordId") or raw.get("applyId")
|
|
1723
1733
|
workflow_node_id = raw.get("nodeId") or raw.get("auditNodeId")
|
|
1724
1734
|
return {
|
|
1725
|
-
"task_id": raw.get("id") or raw.get("taskId") or record_id,
|
|
1735
|
+
"task_id": stringify_backend_id(raw.get("id") or raw.get("taskId") or record_id),
|
|
1726
1736
|
"app_key": app_key,
|
|
1727
1737
|
"app_name": raw.get("formTitle") or raw.get("worksheetName") or raw.get("appName"),
|
|
1728
|
-
"record_id": record_id,
|
|
1738
|
+
"record_id": stringify_backend_id(record_id),
|
|
1729
1739
|
"workflow_node_id": workflow_node_id,
|
|
1730
1740
|
"workflow_node_name": raw.get("nodeName") or raw.get("auditNodeName"),
|
|
1731
1741
|
"apply_time": raw.get("applyTime") or raw.get("receiveTime"),
|
|
@@ -1816,6 +1826,7 @@ class TaskContextTools(ToolBase):
|
|
|
1816
1826
|
current_answers: Any,
|
|
1817
1827
|
) -> dict[str, Any]:
|
|
1818
1828
|
"""执行内部辅助逻辑。"""
|
|
1829
|
+
record_id_text = stringify_backend_id(record_id)
|
|
1819
1830
|
try:
|
|
1820
1831
|
app_schema = self._record_tools._get_form_schema(profile, context, app_key, force_refresh=False)
|
|
1821
1832
|
except QingflowApiError as error:
|
|
@@ -1832,7 +1843,7 @@ class TaskContextTools(ToolBase):
|
|
|
1832
1843
|
],
|
|
1833
1844
|
"selection": {
|
|
1834
1845
|
"app_key": app_key,
|
|
1835
|
-
"record_id":
|
|
1846
|
+
"record_id": record_id_text,
|
|
1836
1847
|
"workflow_node_id": workflow_node_id,
|
|
1837
1848
|
},
|
|
1838
1849
|
"transport_error": {
|
|
@@ -1917,7 +1928,7 @@ class TaskContextTools(ToolBase):
|
|
|
1917
1928
|
"warnings": schema_warnings,
|
|
1918
1929
|
"selection": {
|
|
1919
1930
|
"app_key": app_key,
|
|
1920
|
-
"record_id":
|
|
1931
|
+
"record_id": record_id_text,
|
|
1921
1932
|
"workflow_node_id": workflow_node_id,
|
|
1922
1933
|
},
|
|
1923
1934
|
}
|