@qingflow-tech/qingflow-app-builder-mcp 1.0.35 → 1.0.36
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/builder_facade/service.py +119 -9
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.36
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.36 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -160,6 +160,7 @@ MATCH_TYPE_ACCURACY = 1
|
|
|
160
160
|
MATCH_TYPE_QUESTION = 2
|
|
161
161
|
JUDGE_EQUAL = 0
|
|
162
162
|
JUDGE_UNEQUAL = 1
|
|
163
|
+
JUDGE_INCLUDE = 2
|
|
163
164
|
JUDGE_GREATER_OR_EQUAL = 5
|
|
164
165
|
JUDGE_LESS_OR_EQUAL = 7
|
|
165
166
|
JUDGE_EQUAL_ANY = 9
|
|
@@ -10125,14 +10126,31 @@ class AiBuilderFacade:
|
|
|
10125
10126
|
expected_filter_summary = _normalize_view_filter_groups_for_compare(expected_filters)
|
|
10126
10127
|
expected_data_scope = "CUSTOM" if expected_filter_summary else "ALL"
|
|
10127
10128
|
actual_data_scope = str(config_result.get("dataScope") or "").strip().upper() or None
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10129
|
+
(
|
|
10130
|
+
filters_semantically_equivalent,
|
|
10131
|
+
semantic_actual_filters,
|
|
10132
|
+
lossy_filter_readbacks,
|
|
10133
|
+
) = _view_filter_groups_semantic_readback(expected_filter_summary, actual_filters)
|
|
10134
|
+
filters_verified = filters_semantically_equivalent and actual_data_scope == expected_data_scope
|
|
10135
|
+
public_expected_filters = _public_view_filter_groups_from_match_rules(
|
|
10136
|
+
expected_filter_summary,
|
|
10137
|
+
current_fields_by_name=current_fields_by_name,
|
|
10138
|
+
)
|
|
10139
|
+
public_actual_filters = _public_view_filter_groups_from_match_rules(
|
|
10140
|
+
semantic_actual_filters,
|
|
10141
|
+
current_fields_by_name=current_fields_by_name,
|
|
10131
10142
|
)
|
|
10132
10143
|
verification_entry["filters_verified"] = filters_verified
|
|
10133
10144
|
verification_entry["view_key"] = verification_key
|
|
10134
|
-
verification_entry["expected_filters"] =
|
|
10135
|
-
verification_entry["actual_filters"] =
|
|
10145
|
+
verification_entry["expected_filters"] = public_expected_filters
|
|
10146
|
+
verification_entry["actual_filters"] = public_actual_filters
|
|
10147
|
+
verification_entry["expected_filters_raw"] = expected_filter_summary
|
|
10148
|
+
if lossy_filter_readbacks:
|
|
10149
|
+
verification_entry["actual_filters_raw"] = actual_filters
|
|
10150
|
+
verification_entry["filter_value_readback_degraded"] = True
|
|
10151
|
+
verification_entry["filter_value_readback_warnings"] = lossy_filter_readbacks
|
|
10152
|
+
elif public_actual_filters != semantic_actual_filters:
|
|
10153
|
+
verification_entry["actual_filters_raw"] = semantic_actual_filters
|
|
10136
10154
|
verification_entry["expected_data_scope"] = expected_data_scope
|
|
10137
10155
|
verification_entry["actual_data_scope"] = actual_data_scope
|
|
10138
10156
|
if not filters_verified:
|
|
@@ -10140,8 +10158,10 @@ class AiBuilderFacade:
|
|
|
10140
10158
|
{
|
|
10141
10159
|
"name": name,
|
|
10142
10160
|
"type": item.get("type"),
|
|
10143
|
-
"expected_filters":
|
|
10144
|
-
"actual_filters":
|
|
10161
|
+
"expected_filters": public_expected_filters,
|
|
10162
|
+
"actual_filters": public_actual_filters,
|
|
10163
|
+
"expected_filters_raw": expected_filter_summary,
|
|
10164
|
+
"actual_filters_raw": actual_filters if lossy_filter_readbacks else semantic_actual_filters,
|
|
10145
10165
|
"expected_data_scope": expected_data_scope,
|
|
10146
10166
|
"actual_data_scope": actual_data_scope,
|
|
10147
10167
|
}
|
|
@@ -22586,7 +22606,7 @@ def _translate_flow_condition_rule(*, field: dict[str, Any], rule: dict[str, Any
|
|
|
22586
22606
|
base["judgeType"] = JUDGE_INCLUDE_ANY if field_type in INCLUDE_ANY_FLOW_FIELD_TYPES else JUDGE_EQUAL_ANY
|
|
22587
22607
|
base["judgeValues"] = [_stringify_condition_value(value) for value in values]
|
|
22588
22608
|
elif operator == "contains":
|
|
22589
|
-
base["judgeType"] =
|
|
22609
|
+
base["judgeType"] = JUDGE_INCLUDE
|
|
22590
22610
|
base["judgeValues"] = [_stringify_condition_value(values[0])]
|
|
22591
22611
|
elif operator == "gte":
|
|
22592
22612
|
base["judgeType"] = JUDGE_GREATER_OR_EQUAL
|
|
@@ -24330,7 +24350,7 @@ def _translate_view_filter_rule(*, field: dict[str, Any], rule: dict[str, Any])
|
|
|
24330
24350
|
payload["judgeType"] = JUDGE_INCLUDE_ANY if field_type in INCLUDE_ANY_FLOW_FIELD_TYPES else JUDGE_EQUAL_ANY
|
|
24331
24351
|
payload["judgeValues"] = judge_values
|
|
24332
24352
|
elif operator == "contains":
|
|
24333
|
-
payload["judgeType"] =
|
|
24353
|
+
payload["judgeType"] = JUDGE_INCLUDE
|
|
24334
24354
|
payload["judgeValues"] = judge_values[:1] if judge_values else []
|
|
24335
24355
|
elif operator == "gte":
|
|
24336
24356
|
payload["judgeType"] = JUDGE_GREATER_OR_EQUAL
|
|
@@ -24515,6 +24535,96 @@ def _view_filter_groups_equivalent(expected: Any, actual: Any) -> bool:
|
|
|
24515
24535
|
return _view_filter_groups_signature(expected) == _view_filter_groups_signature(actual)
|
|
24516
24536
|
|
|
24517
24537
|
|
|
24538
|
+
def _view_filter_groups_semantic_readback(expected: Any, actual: Any) -> tuple[bool, list[list[dict[str, Any]]], list[dict[str, Any]]]:
|
|
24539
|
+
expected_groups = _normalize_view_filter_groups_for_compare(expected)
|
|
24540
|
+
actual_groups = _normalize_view_filter_groups_for_compare(actual)
|
|
24541
|
+
if _view_filter_groups_signature(expected_groups) == _view_filter_groups_signature(actual_groups):
|
|
24542
|
+
return True, actual_groups, []
|
|
24543
|
+
if len(expected_groups) != len(actual_groups):
|
|
24544
|
+
return False, actual_groups, []
|
|
24545
|
+
semantic_groups: list[list[dict[str, Any]]] = []
|
|
24546
|
+
lossy_readbacks: list[dict[str, Any]] = []
|
|
24547
|
+
for group_index, (expected_group, actual_group) in enumerate(zip(expected_groups, actual_groups)):
|
|
24548
|
+
if len(expected_group) != len(actual_group):
|
|
24549
|
+
return False, actual_groups, []
|
|
24550
|
+
semantic_group: list[dict[str, Any]] = []
|
|
24551
|
+
for rule_index, (expected_rule, actual_rule) in enumerate(zip(expected_group, actual_group)):
|
|
24552
|
+
if expected_rule.get("queId") != actual_rule.get("queId") or expected_rule.get("judgeType") != actual_rule.get("judgeType"):
|
|
24553
|
+
return False, actual_groups, []
|
|
24554
|
+
expected_values = _view_filter_rule_values_for_signature(expected_rule)
|
|
24555
|
+
actual_values = _view_filter_rule_values_for_signature(actual_rule)
|
|
24556
|
+
if expected_values == actual_values:
|
|
24557
|
+
semantic_group.append(actual_rule)
|
|
24558
|
+
continue
|
|
24559
|
+
if (
|
|
24560
|
+
expected_values
|
|
24561
|
+
and not actual_values
|
|
24562
|
+
and actual_rule.get("judgeType") in {JUDGE_INCLUDE, JUDGE_FUZZY_MATCH}
|
|
24563
|
+
):
|
|
24564
|
+
semantic_rule = deepcopy(actual_rule)
|
|
24565
|
+
semantic_rule["judgeValues"] = expected_values
|
|
24566
|
+
semantic_group.append(semantic_rule)
|
|
24567
|
+
lossy_readbacks.append(
|
|
24568
|
+
{
|
|
24569
|
+
"group_index": group_index,
|
|
24570
|
+
"rule_index": rule_index,
|
|
24571
|
+
"queId": actual_rule.get("queId"),
|
|
24572
|
+
"judgeType": actual_rule.get("judgeType"),
|
|
24573
|
+
"message": "view filter literal value was accepted by write path but omitted by raw viewConfig readback; semantic readback was reconstructed from the write payload",
|
|
24574
|
+
}
|
|
24575
|
+
)
|
|
24576
|
+
continue
|
|
24577
|
+
return False, actual_groups, []
|
|
24578
|
+
semantic_groups.append(semantic_group)
|
|
24579
|
+
return True, semantic_groups, lossy_readbacks
|
|
24580
|
+
|
|
24581
|
+
|
|
24582
|
+
def _public_view_filter_groups_from_match_rules(
|
|
24583
|
+
groups: Any,
|
|
24584
|
+
*,
|
|
24585
|
+
current_fields_by_name: dict[str, dict[str, Any]],
|
|
24586
|
+
) -> list[list[dict[str, Any]]]:
|
|
24587
|
+
fields_by_que_id = {
|
|
24588
|
+
_coerce_positive_int(field.get("que_id")): field
|
|
24589
|
+
for field in current_fields_by_name.values()
|
|
24590
|
+
if isinstance(field, dict) and _coerce_positive_int(field.get("que_id")) is not None
|
|
24591
|
+
}
|
|
24592
|
+
public_groups: list[list[dict[str, Any]]] = []
|
|
24593
|
+
for group in _normalize_view_filter_groups_for_compare(groups):
|
|
24594
|
+
public_group: list[dict[str, Any]] = []
|
|
24595
|
+
for rule in group:
|
|
24596
|
+
que_id = _coerce_positive_int(rule.get("queId")) or 0
|
|
24597
|
+
field = fields_by_que_id.get(que_id) or {}
|
|
24598
|
+
values = _view_filter_rule_values_for_signature(rule)
|
|
24599
|
+
public_rule: dict[str, Any] = {
|
|
24600
|
+
"field_name": str(field.get("name") or rule.get("queTitle") or que_id),
|
|
24601
|
+
"operator": _public_view_filter_operator_from_judge_type(rule.get("judgeType")),
|
|
24602
|
+
}
|
|
24603
|
+
if values:
|
|
24604
|
+
public_rule["values"] = values
|
|
24605
|
+
public_group.append(public_rule)
|
|
24606
|
+
if public_group:
|
|
24607
|
+
public_groups.append(public_group)
|
|
24608
|
+
return public_groups
|
|
24609
|
+
|
|
24610
|
+
|
|
24611
|
+
def _public_view_filter_operator_from_judge_type(judge_type: Any) -> str:
|
|
24612
|
+
normalized = _coerce_positive_int(judge_type)
|
|
24613
|
+
if normalized == JUDGE_EQUAL:
|
|
24614
|
+
return "eq"
|
|
24615
|
+
if normalized == JUDGE_UNEQUAL:
|
|
24616
|
+
return "neq"
|
|
24617
|
+
if normalized in {JUDGE_INCLUDE, JUDGE_FUZZY_MATCH}:
|
|
24618
|
+
return "contains"
|
|
24619
|
+
if normalized in {JUDGE_EQUAL_ANY, JUDGE_INCLUDE_ANY}:
|
|
24620
|
+
return "in"
|
|
24621
|
+
if normalized == JUDGE_GREATER_OR_EQUAL:
|
|
24622
|
+
return "gte"
|
|
24623
|
+
if normalized == JUDGE_LESS_OR_EQUAL:
|
|
24624
|
+
return "lte"
|
|
24625
|
+
return f"judge_type:{judge_type}"
|
|
24626
|
+
|
|
24627
|
+
|
|
24518
24628
|
def _infer_status_field_id(fields: list[dict[str, Any]]) -> str | None:
|
|
24519
24629
|
preferred_names = {"status", "状态", "订单状态", "审批状态", "流程状态"}
|
|
24520
24630
|
for field in fields:
|