@josephyan/qingflow-app-builder-mcp 0.2.0-beta.23 → 0.2.0-beta.24
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/tools/record_tools.py +33 -12
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.24
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.24 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -38,6 +38,7 @@ ATTACHMENT_QUE_TYPES = {13}
|
|
|
38
38
|
RELATION_QUE_TYPES = {25}
|
|
39
39
|
SUBTABLE_QUE_TYPES = {18}
|
|
40
40
|
VERIFY_UNSUPPORTED_WRITE_QUE_TYPES = {14, 34, 35, 36}
|
|
41
|
+
LAYOUT_ONLY_QUE_TYPES = {24}
|
|
41
42
|
DEPARTMENT_MEMBER_JUDGE_PREFIX = "deptId_"
|
|
42
43
|
JUDGE_EQUAL = 0
|
|
43
44
|
JUDGE_UNEQUAL = 1
|
|
@@ -1131,10 +1132,12 @@ class RecordTools(ToolBase):
|
|
|
1131
1132
|
self._ensure_allowed_analyze_keys(
|
|
1132
1133
|
item,
|
|
1133
1134
|
location=f"metrics[{idx}]",
|
|
1134
|
-
allowed_keys={"op", "field_id", "fieldId", "alias"},
|
|
1135
|
+
allowed_keys={"op", "type", "agg", "aggregation", "field_id", "fieldId", "alias"},
|
|
1135
1136
|
example="{'op': 'sum', 'field_id': 7, 'alias': '总金额'}",
|
|
1136
1137
|
)
|
|
1137
|
-
op = _normalize_optional_text(
|
|
1138
|
+
op = _normalize_optional_text(
|
|
1139
|
+
item.get("op") or item.get("type") or item.get("agg") or item.get("aggregation")
|
|
1140
|
+
)
|
|
1138
1141
|
if op not in supported_ops:
|
|
1139
1142
|
raise RecordInputError(
|
|
1140
1143
|
message=f"metrics[{idx}] uses unsupported op '{op}'",
|
|
@@ -1153,12 +1156,8 @@ class RecordTools(ToolBase):
|
|
|
1153
1156
|
details={"location": f"metrics[{idx}]", "field": _field_ref_payload(field), "op": op},
|
|
1154
1157
|
)
|
|
1155
1158
|
elif item.get("field_id", item.get("fieldId")) is not None:
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
error_code="INVALID_ANALYZE_METRIC",
|
|
1159
|
-
fix_hint="Remove field_id from count metrics.",
|
|
1160
|
-
details={"location": f"metrics[{idx}]", "op": op},
|
|
1161
|
-
)
|
|
1159
|
+
# LLM 经常给 count 传 field_id,静默忽略而非报错
|
|
1160
|
+
pass
|
|
1162
1161
|
alias = _normalize_optional_text(item.get("alias"))
|
|
1163
1162
|
if alias is None:
|
|
1164
1163
|
if op == "count":
|
|
@@ -3995,16 +3994,19 @@ def _build_field_index(schema: JSONObject) -> FieldIndex:
|
|
|
3995
3994
|
*[(question, False) for question in _flatten_questions(schema.get("formQues"))],
|
|
3996
3995
|
]
|
|
3997
3996
|
for question, is_base_question in all_questions:
|
|
3997
|
+
if not _should_index_question(question):
|
|
3998
|
+
continue
|
|
3998
3999
|
que_id = _coerce_count(question.get("queId"))
|
|
3999
4000
|
title = _stringify_json(question.get("queTitle")).strip()
|
|
4000
4001
|
if que_id is None or que_id < 0 or not title:
|
|
4001
4002
|
continue
|
|
4003
|
+
can_edit = question.get("canEdit")
|
|
4002
4004
|
field = FormField(
|
|
4003
4005
|
que_id=que_id,
|
|
4004
4006
|
que_title=title,
|
|
4005
4007
|
que_type=_coerce_count(question.get("queType")),
|
|
4006
4008
|
required=bool(question.get("required") or question.get("beingRequired")),
|
|
4007
|
-
readonly=bool(question.get("readonly") or question.get("beingReadonly") or is_base_question),
|
|
4009
|
+
readonly=bool(question.get("readonly") or question.get("beingReadonly") or is_base_question or can_edit is False),
|
|
4008
4010
|
system=bool(question.get("system") or question.get("beingSystem") or is_base_question),
|
|
4009
4011
|
options=_extract_question_options(question),
|
|
4010
4012
|
aliases=[],
|
|
@@ -4023,16 +4025,35 @@ def _build_field_index(schema: JSONObject) -> FieldIndex:
|
|
|
4023
4025
|
def _flatten_questions(payload: JSONValue) -> list[JSONObject]:
|
|
4024
4026
|
flattened: list[JSONObject] = []
|
|
4025
4027
|
if isinstance(payload, dict):
|
|
4026
|
-
|
|
4028
|
+
is_question = "queId" in payload or "queTitle" in payload
|
|
4029
|
+
if is_question:
|
|
4027
4030
|
flattened.append(payload)
|
|
4028
|
-
for
|
|
4029
|
-
|
|
4031
|
+
for key in ("subQuestions", "innerQuestions", "subQues"):
|
|
4032
|
+
value = payload.get(key)
|
|
4033
|
+
if isinstance(value, list):
|
|
4034
|
+
flattened.extend(_flatten_questions(value))
|
|
4035
|
+
if not is_question:
|
|
4036
|
+
for key in ("baseQues", "formQues"):
|
|
4037
|
+
value = payload.get(key)
|
|
4038
|
+
if isinstance(value, list):
|
|
4039
|
+
flattened.extend(_flatten_questions(value))
|
|
4030
4040
|
elif isinstance(payload, list):
|
|
4031
4041
|
for item in payload:
|
|
4032
4042
|
flattened.extend(_flatten_questions(item))
|
|
4033
4043
|
return flattened
|
|
4034
4044
|
|
|
4035
4045
|
|
|
4046
|
+
def _should_index_question(question: JSONObject) -> bool:
|
|
4047
|
+
if bool(question.get("beingHide") or question.get("hidden")):
|
|
4048
|
+
return False
|
|
4049
|
+
if _coerce_count(question.get("quoteId")) is not None:
|
|
4050
|
+
return False
|
|
4051
|
+
que_type = _coerce_count(question.get("queType"))
|
|
4052
|
+
if que_type in LAYOUT_ONLY_QUE_TYPES:
|
|
4053
|
+
return False
|
|
4054
|
+
return True
|
|
4055
|
+
|
|
4056
|
+
|
|
4036
4057
|
def _extract_question_options(question: JSONObject) -> list[str]:
|
|
4037
4058
|
options = question.get("options")
|
|
4038
4059
|
if not isinstance(options, list):
|