@qingflow-tech/qingflow-app-user-mcp 1.0.36 → 1.0.38
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 +115 -5
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @qingflow-tech/qingflow-app-user-mcp@1.0.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-user-mcp@1.0.38
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-user-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-user-mcp@1.0.38 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -9,7 +9,7 @@ import random
|
|
|
9
9
|
import re
|
|
10
10
|
import string
|
|
11
11
|
import tempfile
|
|
12
|
-
from typing import Any, cast
|
|
12
|
+
from typing import Any, NoReturn, cast
|
|
13
13
|
from urllib.parse import quote_plus, unquote_plus
|
|
14
14
|
from uuid import uuid4
|
|
15
15
|
|
|
@@ -11378,11 +11378,14 @@ class AiBuilderFacade:
|
|
|
11378
11378
|
|
|
11379
11379
|
if failed_items:
|
|
11380
11380
|
successful_changes = bool(created_ids or updated_ids or removed_ids or reordered)
|
|
11381
|
+
primary_failure = failed_items[0]
|
|
11382
|
+
primary_error_code = str(primary_failure.get("error_code") or "").strip()
|
|
11383
|
+
primary_message = str(primary_failure.get("message") or "").strip()
|
|
11381
11384
|
return finalize({
|
|
11382
11385
|
"status": "partial_success" if successful_changes else "failed",
|
|
11383
|
-
"error_code": "CHART_APPLY_PARTIAL" if successful_changes else "CHART_APPLY_FAILED",
|
|
11386
|
+
"error_code": "CHART_APPLY_PARTIAL" if successful_changes else primary_error_code or "CHART_APPLY_FAILED",
|
|
11384
11387
|
"recoverable": True,
|
|
11385
|
-
"message": "applied some chart operations; at least one chart operation failed" if successful_changes else "one or more chart operations failed",
|
|
11388
|
+
"message": "applied some chart operations; at least one chart operation failed" if successful_changes else primary_message or "one or more chart operations failed",
|
|
11386
11389
|
"normalized_args": normalized_args,
|
|
11387
11390
|
"missing_fields": [],
|
|
11388
11391
|
"allowed_values": {"chart.chart_type": [member.value for member in PublicChartType], "chart.filter.operator": [member.value for member in ViewFilterOperator]},
|
|
@@ -14615,7 +14618,18 @@ def _build_qingbi_chart_field_lookup(
|
|
|
14615
14618
|
field_lookup: dict[str, dict[str, Any]],
|
|
14616
14619
|
) -> dict[str, Any]:
|
|
14617
14620
|
by_selector: dict[str, list[dict[str, Any]]] = {}
|
|
14618
|
-
form_by_que_id = field_lookup.get("by_que_id") or {}
|
|
14621
|
+
form_by_que_id = dict(field_lookup.get("by_que_id") or {})
|
|
14622
|
+
if not form_by_que_id:
|
|
14623
|
+
for bucket_name in ("by_name", "by_field_id"):
|
|
14624
|
+
bucket = field_lookup.get(bucket_name) or {}
|
|
14625
|
+
if not isinstance(bucket, dict):
|
|
14626
|
+
continue
|
|
14627
|
+
for form_field in bucket.values():
|
|
14628
|
+
if not isinstance(form_field, dict):
|
|
14629
|
+
continue
|
|
14630
|
+
que_id = _coerce_any_int(form_field.get("que_id"))
|
|
14631
|
+
if que_id is not None:
|
|
14632
|
+
form_by_que_id.setdefault(que_id, form_field)
|
|
14619
14633
|
|
|
14620
14634
|
def add_selector(key: Any, field: dict[str, Any]) -> None:
|
|
14621
14635
|
normalized = str(key or "").strip()
|
|
@@ -15095,13 +15109,109 @@ def _build_public_chart_filter_matrix(
|
|
|
15095
15109
|
"fieldName": qingbi_field.get("fieldName") or form_field.get("name") or field_id,
|
|
15096
15110
|
"fieldType": qingbi_field.get("fieldType") or _qingbi_field_type_from_public_field(str(form_field.get("type") or "")),
|
|
15097
15111
|
"judgeType": judge_map.get(operator, "equal"),
|
|
15098
|
-
"
|
|
15112
|
+
"judgeValue": _build_qingbi_chart_filter_judge_value(
|
|
15113
|
+
operator=operator,
|
|
15114
|
+
values=values,
|
|
15115
|
+
form_field=form_field,
|
|
15116
|
+
chart_type=chart_type,
|
|
15117
|
+
),
|
|
15118
|
+
"judgeValueDetailList": [],
|
|
15099
15119
|
"matchType": 1,
|
|
15100
15120
|
}
|
|
15101
15121
|
)
|
|
15102
15122
|
return [group] if group else []
|
|
15103
15123
|
|
|
15104
15124
|
|
|
15125
|
+
def _build_qingbi_chart_filter_judge_value(
|
|
15126
|
+
*,
|
|
15127
|
+
operator: str,
|
|
15128
|
+
values: list[Any],
|
|
15129
|
+
form_field: dict[str, Any],
|
|
15130
|
+
chart_type: str,
|
|
15131
|
+
) -> str | None:
|
|
15132
|
+
if operator in {ViewFilterOperator.is_empty.value, ViewFilterOperator.not_empty.value}:
|
|
15133
|
+
return None
|
|
15134
|
+
if operator == ViewFilterOperator.in_.value:
|
|
15135
|
+
return "<&&>".join(
|
|
15136
|
+
_qingbi_chart_filter_value_to_text(value=value, form_field=form_field, chart_type=chart_type) for value in values
|
|
15137
|
+
)
|
|
15138
|
+
if not values:
|
|
15139
|
+
return ""
|
|
15140
|
+
return _qingbi_chart_filter_value_to_text(value=values[0], form_field=form_field, chart_type=chart_type)
|
|
15141
|
+
|
|
15142
|
+
|
|
15143
|
+
def _qingbi_chart_filter_value_to_text(*, value: Any, form_field: dict[str, Any], chart_type: str) -> str:
|
|
15144
|
+
option_details = [
|
|
15145
|
+
item
|
|
15146
|
+
for item in (form_field.get("option_details") or [])
|
|
15147
|
+
if isinstance(item, dict) and item.get("id") is not None and item.get("value") is not None
|
|
15148
|
+
]
|
|
15149
|
+
if not option_details:
|
|
15150
|
+
if isinstance(value, dict):
|
|
15151
|
+
for key in ("value", "label", "name", "title"):
|
|
15152
|
+
raw = str(value.get(key) or "").strip()
|
|
15153
|
+
if raw:
|
|
15154
|
+
return raw
|
|
15155
|
+
if value.get("id") is not None:
|
|
15156
|
+
return str(value.get("id"))
|
|
15157
|
+
return _stringify_condition_value(value)
|
|
15158
|
+
|
|
15159
|
+
option_by_value = {str(item.get("value") or "").strip(): item for item in option_details if str(item.get("value") or "").strip()}
|
|
15160
|
+
option_by_id = {str(item.get("id")): item for item in option_details if item.get("id") is not None}
|
|
15161
|
+
|
|
15162
|
+
def resolve(raw: Any) -> str | None:
|
|
15163
|
+
text = _stringify_condition_value(raw).strip()
|
|
15164
|
+
if not text:
|
|
15165
|
+
return None
|
|
15166
|
+
matched = option_by_value.get(text) or option_by_id.get(text)
|
|
15167
|
+
if not matched:
|
|
15168
|
+
return None
|
|
15169
|
+
return str(matched.get("value") or "").strip()
|
|
15170
|
+
|
|
15171
|
+
def reject(raw: Any) -> NoReturn:
|
|
15172
|
+
allowed = [
|
|
15173
|
+
{"id": str(item.get("id")), "value": str(item.get("value"))}
|
|
15174
|
+
for item in option_details
|
|
15175
|
+
]
|
|
15176
|
+
_raise_chart_rule(
|
|
15177
|
+
rule_code="CHART_FILTER_OPTION_VALUE_UNSUPPORTED",
|
|
15178
|
+
chart_type=chart_type,
|
|
15179
|
+
message="chart filter value is not in the field option list",
|
|
15180
|
+
expected="Use an existing option label or option id from the target field schema.",
|
|
15181
|
+
actual={
|
|
15182
|
+
"field_name": form_field.get("name"),
|
|
15183
|
+
"field_id": form_field.get("que_id"),
|
|
15184
|
+
"value": raw,
|
|
15185
|
+
"allowed_values": allowed,
|
|
15186
|
+
},
|
|
15187
|
+
offending_fields=[{"field_name": form_field.get("name"), "field_id": form_field.get("que_id"), "options": allowed}],
|
|
15188
|
+
next_action="Read schema first, then pass one of the allowed option labels or option ids.",
|
|
15189
|
+
)
|
|
15190
|
+
|
|
15191
|
+
if isinstance(value, dict):
|
|
15192
|
+
for key in ("value", "label", "name", "title"):
|
|
15193
|
+
if value.get(key) is not None:
|
|
15194
|
+
resolved = resolve(value.get(key))
|
|
15195
|
+
if resolved is not None:
|
|
15196
|
+
return resolved
|
|
15197
|
+
reject(value.get(key))
|
|
15198
|
+
if value.get("id") is not None:
|
|
15199
|
+
resolved = resolve(value.get("id"))
|
|
15200
|
+
if resolved is not None:
|
|
15201
|
+
return resolved
|
|
15202
|
+
reject(value.get("id"))
|
|
15203
|
+
reject(value)
|
|
15204
|
+
if isinstance(value, int):
|
|
15205
|
+
resolved = resolve(value)
|
|
15206
|
+
if resolved is not None:
|
|
15207
|
+
return resolved
|
|
15208
|
+
reject(value)
|
|
15209
|
+
resolved = resolve(value)
|
|
15210
|
+
if resolved is not None:
|
|
15211
|
+
return resolved
|
|
15212
|
+
reject(value)
|
|
15213
|
+
|
|
15214
|
+
|
|
15105
15215
|
def _build_public_chart_config_payload(
|
|
15106
15216
|
*,
|
|
15107
15217
|
patch: ChartUpsertPatch,
|