@josephyan/qingflow-cli 0.2.0-beta.70 → 0.2.0-beta.72
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/backend_client.py +1 -0
- package/src/qingflow_mcp/builder_facade/models.py +16 -8
- package/src/qingflow_mcp/builder_facade/service.py +208 -111
- package/src/qingflow_mcp/cli/commands/__init__.py +4 -1
- package/src/qingflow_mcp/cli/commands/builder.py +24 -64
- package/src/qingflow_mcp/cli/commands/chart.py +18 -0
- package/src/qingflow_mcp/cli/commands/portal.py +25 -0
- package/src/qingflow_mcp/cli/commands/view.py +18 -0
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/response_trim.py +211 -178
- package/src/qingflow_mcp/server_app_builder.py +18 -42
- package/src/qingflow_mcp/server_app_user.py +21 -1
- package/src/qingflow_mcp/tools/ai_builder_tools.py +165 -124
- package/src/qingflow_mcp/tools/app_tools.py +0 -4
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +58 -7
- package/src/qingflow_mcp/tools/resource_read_tools.py +399 -0
|
@@ -1079,7 +1079,7 @@ class AiBuilderFacade:
|
|
|
1079
1079
|
api_error,
|
|
1080
1080
|
normalized_args=normalized_args,
|
|
1081
1081
|
details={"app_key": app_key},
|
|
1082
|
-
suggested_next_call={"tool_name": "
|
|
1082
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1083
1083
|
)
|
|
1084
1084
|
edit_version_no = _coerce_positive_int(version_result.get("editVersionNo") or version_result.get("versionNo")) or 1
|
|
1085
1085
|
try:
|
|
@@ -1114,7 +1114,7 @@ class AiBuilderFacade:
|
|
|
1114
1114
|
"edit_version_no": edit_version_no,
|
|
1115
1115
|
},
|
|
1116
1116
|
"request_id": None,
|
|
1117
|
-
"suggested_next_call": {"tool_name": "
|
|
1117
|
+
"suggested_next_call": {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1118
1118
|
"noop": False,
|
|
1119
1119
|
"verification": {"released": True},
|
|
1120
1120
|
"app_key": app_key,
|
|
@@ -1154,7 +1154,7 @@ class AiBuilderFacade:
|
|
|
1154
1154
|
"permission_check_skipped": True,
|
|
1155
1155
|
},
|
|
1156
1156
|
"request_id": api_error.request_id,
|
|
1157
|
-
"suggested_next_call": {"tool_name": "
|
|
1157
|
+
"suggested_next_call": {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1158
1158
|
"noop": False,
|
|
1159
1159
|
"warnings": [
|
|
1160
1160
|
_warning(
|
|
@@ -1174,7 +1174,7 @@ class AiBuilderFacade:
|
|
|
1174
1174
|
"APP_NOT_FOUND" if api_error.http_status == 404 else "APP_RESOLVE_FAILED",
|
|
1175
1175
|
api_error,
|
|
1176
1176
|
details={"app_key": app_key},
|
|
1177
|
-
suggested_next_call={"tool_name": "
|
|
1177
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1178
1178
|
)
|
|
1179
1179
|
result = base.get("result") if isinstance(base.get("result"), dict) else {}
|
|
1180
1180
|
return {
|
|
@@ -1852,7 +1852,7 @@ class AiBuilderFacade:
|
|
|
1852
1852
|
"category": api_error.category,
|
|
1853
1853
|
},
|
|
1854
1854
|
},
|
|
1855
|
-
suggested_next_call={"tool_name": "
|
|
1855
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1856
1856
|
request_id=api_error.request_id,
|
|
1857
1857
|
backend_code=api_error.backend_code,
|
|
1858
1858
|
http_status=None if api_error.http_status == 404 else api_error.http_status,
|
|
@@ -1890,7 +1890,7 @@ class AiBuilderFacade:
|
|
|
1890
1890
|
"required_permission": required_permission,
|
|
1891
1891
|
"permission_summary": permission_summary,
|
|
1892
1892
|
},
|
|
1893
|
-
suggested_next_call={"tool_name": "
|
|
1893
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
1894
1894
|
)
|
|
1895
1895
|
)
|
|
1896
1896
|
|
|
@@ -2003,7 +2003,7 @@ class AiBuilderFacade:
|
|
|
2003
2003
|
"current user does not have builder edit permission on this portal",
|
|
2004
2004
|
normalized_args=normalized_args,
|
|
2005
2005
|
details={"dash_key": dash_key, "permission_summary": permission_summary},
|
|
2006
|
-
suggested_next_call={"tool_name": "
|
|
2006
|
+
suggested_next_call={"tool_name": "portal_get", "arguments": {"profile": profile, "dash_key": dash_key}},
|
|
2007
2007
|
)
|
|
2008
2008
|
)
|
|
2009
2009
|
|
|
@@ -2066,6 +2066,52 @@ class AiBuilderFacade:
|
|
|
2066
2066
|
**response.model_dump(mode="json"),
|
|
2067
2067
|
}
|
|
2068
2068
|
|
|
2069
|
+
def app_get(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2070
|
+
result = self.app_read_summary(profile=profile, app_key=app_key)
|
|
2071
|
+
if result.get("status") != "success":
|
|
2072
|
+
if not result.get("suggested_next_call"):
|
|
2073
|
+
result["suggested_next_call"] = {"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}}
|
|
2074
|
+
return result
|
|
2075
|
+
permission_summary = self._read_app_permission_summary(profile=profile, app_key=app_key)
|
|
2076
|
+
result["message"] = "read app config summary"
|
|
2077
|
+
result["editability"] = {
|
|
2078
|
+
"can_edit_form": permission_summary.get("can_edit_app"),
|
|
2079
|
+
"can_edit_flow": permission_summary.get("can_edit_app"),
|
|
2080
|
+
"can_edit_views": permission_summary.get("can_manage_data"),
|
|
2081
|
+
"can_edit_charts": permission_summary.get("can_manage_data"),
|
|
2082
|
+
}
|
|
2083
|
+
return result
|
|
2084
|
+
|
|
2085
|
+
def app_get_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2086
|
+
result = self.app_read_fields(profile=profile, app_key=app_key)
|
|
2087
|
+
if result.get("status") == "success":
|
|
2088
|
+
result["message"] = "read app field config"
|
|
2089
|
+
return result
|
|
2090
|
+
|
|
2091
|
+
def app_get_layout(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2092
|
+
result = self.app_read_layout_summary(profile=profile, app_key=app_key)
|
|
2093
|
+
if result.get("status") == "success":
|
|
2094
|
+
result["message"] = "read app layout config"
|
|
2095
|
+
return result
|
|
2096
|
+
|
|
2097
|
+
def app_get_views(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2098
|
+
result = self.app_read_views_summary(profile=profile, app_key=app_key)
|
|
2099
|
+
if result.get("status") == "success":
|
|
2100
|
+
result["message"] = "read app view config"
|
|
2101
|
+
return result
|
|
2102
|
+
|
|
2103
|
+
def app_get_flow(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2104
|
+
result = self.app_read_flow_summary(profile=profile, app_key=app_key)
|
|
2105
|
+
if result.get("status") == "success":
|
|
2106
|
+
result["message"] = "read app flow config"
|
|
2107
|
+
return result
|
|
2108
|
+
|
|
2109
|
+
def app_get_charts(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2110
|
+
result = self.app_read_charts_summary(profile=profile, app_key=app_key)
|
|
2111
|
+
if result.get("status") == "success":
|
|
2112
|
+
result["message"] = "read app chart config"
|
|
2113
|
+
return result
|
|
2114
|
+
|
|
2069
2115
|
def app_read_fields(self, *, profile: str, app_key: str) -> JSONObject:
|
|
2070
2116
|
try:
|
|
2071
2117
|
state = self._load_base_schema_state(profile=profile, app_key=app_key)
|
|
@@ -2076,7 +2122,7 @@ class AiBuilderFacade:
|
|
|
2076
2122
|
api_error,
|
|
2077
2123
|
normalized_args={"app_key": app_key},
|
|
2078
2124
|
details={"app_key": app_key},
|
|
2079
|
-
suggested_next_call={"tool_name": "
|
|
2125
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
2080
2126
|
)
|
|
2081
2127
|
parsed = state["parsed"]
|
|
2082
2128
|
response = AppFieldsReadResponse(
|
|
@@ -2112,7 +2158,7 @@ class AiBuilderFacade:
|
|
|
2112
2158
|
api_error,
|
|
2113
2159
|
normalized_args={"app_key": app_key},
|
|
2114
2160
|
details={"app_key": app_key},
|
|
2115
|
-
suggested_next_call={"tool_name": "
|
|
2161
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
2116
2162
|
)
|
|
2117
2163
|
parsed = state["parsed"]
|
|
2118
2164
|
layout = parsed["layout"]
|
|
@@ -2157,7 +2203,7 @@ class AiBuilderFacade:
|
|
|
2157
2203
|
api_error,
|
|
2158
2204
|
normalized_args={"app_key": app_key},
|
|
2159
2205
|
details={"app_key": app_key},
|
|
2160
|
-
suggested_next_call={"tool_name": "
|
|
2206
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
2161
2207
|
)
|
|
2162
2208
|
summarized_views, config_read_errors = _summarize_views_with_config(self.views, profile=profile, views=views)
|
|
2163
2209
|
response = AppViewsReadResponse(
|
|
@@ -2211,7 +2257,7 @@ class AiBuilderFacade:
|
|
|
2211
2257
|
api_error,
|
|
2212
2258
|
normalized_args={"app_key": app_key},
|
|
2213
2259
|
details={"app_key": app_key},
|
|
2214
|
-
suggested_next_call={"tool_name": "
|
|
2260
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
2215
2261
|
)
|
|
2216
2262
|
response = AppFlowReadResponse(
|
|
2217
2263
|
app_key=app_key,
|
|
@@ -2251,7 +2297,7 @@ class AiBuilderFacade:
|
|
|
2251
2297
|
api_error,
|
|
2252
2298
|
normalized_args={"app_key": app_key},
|
|
2253
2299
|
details={"app_key": app_key},
|
|
2254
|
-
suggested_next_call={"tool_name": "
|
|
2300
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
2255
2301
|
)
|
|
2256
2302
|
charts = _summarize_charts(items)
|
|
2257
2303
|
response = AppChartsReadResponse(
|
|
@@ -2289,13 +2335,54 @@ class AiBuilderFacade:
|
|
|
2289
2335
|
details={},
|
|
2290
2336
|
suggested_next_call=None,
|
|
2291
2337
|
)
|
|
2292
|
-
|
|
2338
|
+
warnings: list[dict[str, Any]] = []
|
|
2339
|
+
items: list[dict[str, Any]] = []
|
|
2340
|
+
permission_verified = True
|
|
2341
|
+
for raw_item in raw_items if isinstance(raw_items, list) else []:
|
|
2342
|
+
if not isinstance(raw_item, dict):
|
|
2343
|
+
continue
|
|
2344
|
+
dash_key = str(raw_item.get("dashKey") or "").strip()
|
|
2345
|
+
if not dash_key:
|
|
2346
|
+
continue
|
|
2347
|
+
try:
|
|
2348
|
+
portal_result = self.portals.portal_get(profile=profile, dash_key=dash_key, being_draft=True).get("result") or {}
|
|
2349
|
+
except (QingflowApiError, RuntimeError):
|
|
2350
|
+
permission_verified = False
|
|
2351
|
+
warnings.append(
|
|
2352
|
+
_warning(
|
|
2353
|
+
"PORTAL_PERMISSION_READ_UNAVAILABLE",
|
|
2354
|
+
f"builder portal_list skipped `{dash_key}` because portal detail readback was unavailable during permission verification",
|
|
2355
|
+
dash_key=dash_key,
|
|
2356
|
+
)
|
|
2357
|
+
)
|
|
2358
|
+
continue
|
|
2359
|
+
permission_outcome = self._guard_portal_permission(
|
|
2360
|
+
profile=profile,
|
|
2361
|
+
dash_key=dash_key,
|
|
2362
|
+
normalized_args={"dash_key": dash_key},
|
|
2363
|
+
portal_result=portal_result if isinstance(portal_result, dict) else {},
|
|
2364
|
+
)
|
|
2365
|
+
if permission_outcome.block is not None:
|
|
2366
|
+
error_code = str(permission_outcome.block.get("error_code") or "")
|
|
2367
|
+
if error_code != "PORTAL_EDIT_UNAUTHORIZED":
|
|
2368
|
+
permission_verified = False
|
|
2369
|
+
warnings.append(
|
|
2370
|
+
_warning(
|
|
2371
|
+
"PORTAL_PERMISSION_UNVERIFIED",
|
|
2372
|
+
f"builder portal_list skipped `{dash_key}` because builder edit permission could not be verified",
|
|
2373
|
+
dash_key=dash_key,
|
|
2374
|
+
)
|
|
2375
|
+
)
|
|
2376
|
+
continue
|
|
2377
|
+
normalized = _normalize_portal_list_items([raw_item])
|
|
2378
|
+
if normalized:
|
|
2379
|
+
items.extend(normalized)
|
|
2293
2380
|
response = PortalListResponse(items=items, total=len(items))
|
|
2294
2381
|
return {
|
|
2295
2382
|
"status": "success",
|
|
2296
2383
|
"error_code": None,
|
|
2297
2384
|
"recoverable": False,
|
|
2298
|
-
"message": "list
|
|
2385
|
+
"message": "list builder-configurable portals",
|
|
2299
2386
|
"normalized_args": {},
|
|
2300
2387
|
"missing_fields": [],
|
|
2301
2388
|
"allowed_values": {},
|
|
@@ -2303,9 +2390,12 @@ class AiBuilderFacade:
|
|
|
2303
2390
|
"request_id": None,
|
|
2304
2391
|
"suggested_next_call": None,
|
|
2305
2392
|
"noop": False,
|
|
2306
|
-
"warnings":
|
|
2307
|
-
"verification": {
|
|
2308
|
-
|
|
2393
|
+
"warnings": warnings,
|
|
2394
|
+
"verification": {
|
|
2395
|
+
"portal_list_loaded": True,
|
|
2396
|
+
"portal_permissions_verified": permission_verified,
|
|
2397
|
+
},
|
|
2398
|
+
"verified": permission_verified,
|
|
2309
2399
|
**response.model_dump(mode="json"),
|
|
2310
2400
|
}
|
|
2311
2401
|
|
|
@@ -2380,7 +2470,7 @@ class AiBuilderFacade:
|
|
|
2380
2470
|
api_error,
|
|
2381
2471
|
normalized_args={"dash_key": dash_key, "being_draft": being_draft},
|
|
2382
2472
|
details={"dash_key": dash_key, "being_draft": being_draft},
|
|
2383
|
-
suggested_next_call={"tool_name": "
|
|
2473
|
+
suggested_next_call={"tool_name": "portal_get", "arguments": {"profile": profile, "dash_key": dash_key, "being_draft": being_draft}},
|
|
2384
2474
|
)
|
|
2385
2475
|
response = PortalReadSummaryResponse(
|
|
2386
2476
|
dash_key=dash_key,
|
|
@@ -2420,17 +2510,17 @@ class AiBuilderFacade:
|
|
|
2420
2510
|
**response.model_dump(mode="json"),
|
|
2421
2511
|
}
|
|
2422
2512
|
|
|
2423
|
-
def view_get(self, *, profile: str,
|
|
2513
|
+
def view_get(self, *, profile: str, view_key: str) -> JSONObject:
|
|
2424
2514
|
try:
|
|
2425
|
-
config = self.views.view_get_config(profile=profile, viewgraph_key=
|
|
2515
|
+
config = self.views.view_get_config(profile=profile, viewgraph_key=view_key).get("result") or {}
|
|
2426
2516
|
except (QingflowApiError, RuntimeError) as error:
|
|
2427
2517
|
api_error = _coerce_api_error(error)
|
|
2428
2518
|
return _failed_from_api_error(
|
|
2429
2519
|
"VIEW_GET_FAILED",
|
|
2430
2520
|
api_error,
|
|
2431
|
-
normalized_args={"
|
|
2432
|
-
details={"
|
|
2433
|
-
suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, "
|
|
2521
|
+
normalized_args={"view_key": view_key},
|
|
2522
|
+
details={"view_key": view_key},
|
|
2523
|
+
suggested_next_call={"tool_name": "view_get", "arguments": {"profile": profile, "view_key": view_key}},
|
|
2434
2524
|
)
|
|
2435
2525
|
|
|
2436
2526
|
warnings: list[dict[str, Any]] = []
|
|
@@ -2443,7 +2533,7 @@ class AiBuilderFacade:
|
|
|
2443
2533
|
|
|
2444
2534
|
base_info: dict[str, Any] = {}
|
|
2445
2535
|
try:
|
|
2446
|
-
base_info_payload = self.views.view_get_base_info(profile=profile, viewgraph_key=
|
|
2536
|
+
base_info_payload = self.views.view_get_base_info(profile=profile, viewgraph_key=view_key, passcode=None).get("result") or {}
|
|
2447
2537
|
if isinstance(base_info_payload, dict):
|
|
2448
2538
|
base_info = deepcopy(base_info_payload)
|
|
2449
2539
|
except (QingflowApiError, RuntimeError):
|
|
@@ -2452,7 +2542,7 @@ class AiBuilderFacade:
|
|
|
2452
2542
|
|
|
2453
2543
|
questions: list[dict[str, Any]] = []
|
|
2454
2544
|
try:
|
|
2455
|
-
questions_payload = self.views.view_list_questions(profile=profile, viewgraph_key=
|
|
2545
|
+
questions_payload = self.views.view_list_questions(profile=profile, viewgraph_key=view_key).get("result") or []
|
|
2456
2546
|
if isinstance(questions_payload, list):
|
|
2457
2547
|
questions = [deepcopy(item) for item in questions_payload if isinstance(item, dict)]
|
|
2458
2548
|
except (QingflowApiError, RuntimeError):
|
|
@@ -2461,7 +2551,7 @@ class AiBuilderFacade:
|
|
|
2461
2551
|
|
|
2462
2552
|
associations: list[dict[str, Any]] = []
|
|
2463
2553
|
try:
|
|
2464
|
-
associations_payload = self.views.view_list_associations(profile=profile, viewgraph_key=
|
|
2554
|
+
associations_payload = self.views.view_list_associations(profile=profile, viewgraph_key=view_key).get("result") or []
|
|
2465
2555
|
if isinstance(associations_payload, list):
|
|
2466
2556
|
associations = [deepcopy(item) for item in associations_payload if isinstance(item, dict)]
|
|
2467
2557
|
except (QingflowApiError, RuntimeError):
|
|
@@ -2469,7 +2559,7 @@ class AiBuilderFacade:
|
|
|
2469
2559
|
warnings.append(_warning("VIEW_ASSOCIATIONS_UNAVAILABLE", "view association list readback is unavailable"))
|
|
2470
2560
|
|
|
2471
2561
|
response = ViewGetResponse(
|
|
2472
|
-
|
|
2562
|
+
view_key=view_key,
|
|
2473
2563
|
base_info=base_info,
|
|
2474
2564
|
config=deepcopy(config) if isinstance(config, dict) else {},
|
|
2475
2565
|
questions=questions,
|
|
@@ -2480,7 +2570,7 @@ class AiBuilderFacade:
|
|
|
2480
2570
|
"error_code": None,
|
|
2481
2571
|
"recoverable": False,
|
|
2482
2572
|
"message": "read view detail",
|
|
2483
|
-
"normalized_args": {"
|
|
2573
|
+
"normalized_args": {"view_key": view_key},
|
|
2484
2574
|
"missing_fields": [],
|
|
2485
2575
|
"allowed_values": {},
|
|
2486
2576
|
"details": {},
|
|
@@ -2498,69 +2588,72 @@ class AiBuilderFacade:
|
|
|
2498
2588
|
*,
|
|
2499
2589
|
profile: str,
|
|
2500
2590
|
chart_id: str,
|
|
2501
|
-
data_payload: dict[str, Any] | None = None,
|
|
2502
|
-
page_num: int | None = None,
|
|
2503
|
-
page_size: int | None = None,
|
|
2504
|
-
page_num_y: int | None = None,
|
|
2505
|
-
page_size_y: int | None = None,
|
|
2506
2591
|
) -> JSONObject:
|
|
2507
|
-
|
|
2592
|
+
warnings: list[dict[str, Any]] = []
|
|
2593
|
+
verification = {
|
|
2594
|
+
"chart_exists": True,
|
|
2595
|
+
"chart_config_loaded": True,
|
|
2596
|
+
}
|
|
2508
2597
|
try:
|
|
2509
2598
|
base = self.charts.qingbi_report_get_base(profile=profile, chart_id=chart_id).get("result") or {}
|
|
2510
|
-
config = self.charts.qingbi_report_get_config(profile=profile, chart_id=chart_id).get("result") or {}
|
|
2511
|
-
data = self.charts.qingbi_report_get_data(
|
|
2512
|
-
profile=profile,
|
|
2513
|
-
chart_id=chart_id,
|
|
2514
|
-
payload=normalized_payload,
|
|
2515
|
-
page_num=page_num,
|
|
2516
|
-
page_size=page_size,
|
|
2517
|
-
page_num_y=page_num_y,
|
|
2518
|
-
page_size_y=page_size_y,
|
|
2519
|
-
).get("result") or {}
|
|
2520
2599
|
except (QingflowApiError, RuntimeError) as error:
|
|
2521
2600
|
api_error = _coerce_api_error(error)
|
|
2522
2601
|
return _failed_from_api_error(
|
|
2523
2602
|
"CHART_GET_FAILED",
|
|
2524
2603
|
api_error,
|
|
2525
|
-
normalized_args={
|
|
2526
|
-
"chart_id": chart_id,
|
|
2527
|
-
"data_payload": normalized_payload,
|
|
2528
|
-
"page_num": page_num,
|
|
2529
|
-
"page_size": page_size,
|
|
2530
|
-
"page_num_y": page_num_y,
|
|
2531
|
-
"page_size_y": page_size_y,
|
|
2532
|
-
},
|
|
2604
|
+
normalized_args={"chart_id": chart_id},
|
|
2533
2605
|
details={"chart_id": chart_id},
|
|
2534
2606
|
suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
|
|
2535
2607
|
)
|
|
2536
2608
|
|
|
2609
|
+
try:
|
|
2610
|
+
config = self.charts.qingbi_report_get_config(profile=profile, chart_id=chart_id).get("result") or {}
|
|
2611
|
+
except (QingflowApiError, RuntimeError) as error:
|
|
2612
|
+
fallback_config: dict[str, Any] | None = None
|
|
2613
|
+
try:
|
|
2614
|
+
data_fallback = self.charts.qingbi_report_get_data(profile=profile, chart_id=chart_id, payload={}).get("result") or {}
|
|
2615
|
+
config_from_data = data_fallback.get("config") if isinstance(data_fallback, dict) else None
|
|
2616
|
+
if isinstance(config_from_data, dict):
|
|
2617
|
+
fallback_config = deepcopy(config_from_data)
|
|
2618
|
+
except (QingflowApiError, RuntimeError):
|
|
2619
|
+
fallback_config = None
|
|
2620
|
+
if isinstance(fallback_config, dict):
|
|
2621
|
+
config = fallback_config
|
|
2622
|
+
warnings.append(
|
|
2623
|
+
_warning(
|
|
2624
|
+
"CHART_CONFIG_FALLBACK_FROM_DATA",
|
|
2625
|
+
"chart config endpoint is unavailable for this chart id; using config embedded in chart data instead",
|
|
2626
|
+
)
|
|
2627
|
+
)
|
|
2628
|
+
else:
|
|
2629
|
+
api_error = _coerce_api_error(error)
|
|
2630
|
+
return _failed_from_api_error(
|
|
2631
|
+
"CHART_GET_FAILED",
|
|
2632
|
+
api_error,
|
|
2633
|
+
normalized_args={"chart_id": chart_id},
|
|
2634
|
+
details={"chart_id": chart_id},
|
|
2635
|
+
suggested_next_call={"tool_name": "chart_get", "arguments": {"profile": profile, "chart_id": chart_id}},
|
|
2636
|
+
)
|
|
2637
|
+
|
|
2537
2638
|
response = ChartGetResponse(
|
|
2538
2639
|
chart_id=chart_id,
|
|
2539
2640
|
base=deepcopy(base) if isinstance(base, dict) else {},
|
|
2540
2641
|
config=deepcopy(config) if isinstance(config, dict) else {},
|
|
2541
|
-
data=deepcopy(data) if isinstance(data, dict) else {"value": data},
|
|
2542
2642
|
)
|
|
2543
2643
|
return {
|
|
2544
2644
|
"status": "success",
|
|
2545
2645
|
"error_code": None,
|
|
2546
2646
|
"recoverable": False,
|
|
2547
|
-
"message": "read chart detail",
|
|
2548
|
-
"normalized_args": {
|
|
2549
|
-
"chart_id": chart_id,
|
|
2550
|
-
"data_payload": normalized_payload,
|
|
2551
|
-
"page_num": page_num,
|
|
2552
|
-
"page_size": page_size,
|
|
2553
|
-
"page_num_y": page_num_y,
|
|
2554
|
-
"page_size_y": page_size_y,
|
|
2555
|
-
},
|
|
2647
|
+
"message": "read chart config detail",
|
|
2648
|
+
"normalized_args": {"chart_id": chart_id},
|
|
2556
2649
|
"missing_fields": [],
|
|
2557
2650
|
"allowed_values": {},
|
|
2558
2651
|
"details": {},
|
|
2559
2652
|
"request_id": None,
|
|
2560
2653
|
"suggested_next_call": None,
|
|
2561
2654
|
"noop": False,
|
|
2562
|
-
"warnings":
|
|
2563
|
-
"verification":
|
|
2655
|
+
"warnings": warnings,
|
|
2656
|
+
"verification": verification,
|
|
2564
2657
|
"verified": True,
|
|
2565
2658
|
**response.model_dump(mode="json"),
|
|
2566
2659
|
}
|
|
@@ -2664,7 +2757,7 @@ class AiBuilderFacade:
|
|
|
2664
2757
|
},
|
|
2665
2758
|
details={"unknown_selectors": missing_selectors},
|
|
2666
2759
|
missing_fields=[str(item) for item in missing_selectors],
|
|
2667
|
-
suggested_next_call={"tool_name": "
|
|
2760
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": request.app_key}},
|
|
2668
2761
|
)
|
|
2669
2762
|
merged = _merge_layout(
|
|
2670
2763
|
current_layout={
|
|
@@ -2764,7 +2857,7 @@ class AiBuilderFacade:
|
|
|
2764
2857
|
elif first_issue.get("kind", "").startswith("member"):
|
|
2765
2858
|
suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
|
|
2766
2859
|
elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
|
|
2767
|
-
suggested_call = {"tool_name": "
|
|
2860
|
+
suggested_call = {"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": request.app_key}}
|
|
2768
2861
|
return _failed(
|
|
2769
2862
|
first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
|
|
2770
2863
|
"workflow contains unresolved assignees or field permissions",
|
|
@@ -3152,7 +3245,7 @@ class AiBuilderFacade:
|
|
|
3152
3245
|
normalized_args=normalized_args,
|
|
3153
3246
|
allowed_values={"field_types": [item.value for item in PublicFieldType]},
|
|
3154
3247
|
details=_with_state_read_blocked_details({"app_key": target.app_key}, resource="schema", error=api_error),
|
|
3155
|
-
suggested_next_call={"tool_name": "
|
|
3248
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3156
3249
|
))
|
|
3157
3250
|
schema_result = _empty_schema_result(target.app_name)
|
|
3158
3251
|
_schema_source = "synthetic_new_app"
|
|
@@ -3177,7 +3270,7 @@ class AiBuilderFacade:
|
|
|
3177
3270
|
f"field '{patch.name}' already exists",
|
|
3178
3271
|
normalized_args=normalized_args,
|
|
3179
3272
|
details={"field_name": patch.name},
|
|
3180
|
-
suggested_next_call={"tool_name": "
|
|
3273
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3181
3274
|
)
|
|
3182
3275
|
current_fields.append(field_dict)
|
|
3183
3276
|
existing_index[field_dict["field_id"]] = len(current_fields) - 1
|
|
@@ -3193,7 +3286,7 @@ class AiBuilderFacade:
|
|
|
3193
3286
|
"field selector did not match any existing field",
|
|
3194
3287
|
normalized_args=normalized_args,
|
|
3195
3288
|
details={"selector": patch.selector.model_dump(mode="json")},
|
|
3196
|
-
suggested_next_call={"tool_name": "
|
|
3289
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3197
3290
|
)
|
|
3198
3291
|
field = current_fields[matched]
|
|
3199
3292
|
previous_name = field["name"]
|
|
@@ -3211,7 +3304,7 @@ class AiBuilderFacade:
|
|
|
3211
3304
|
"remove selector did not match any existing field",
|
|
3212
3305
|
normalized_args=normalized_args,
|
|
3213
3306
|
details={"selector": patch.model_dump(mode="json")},
|
|
3214
|
-
suggested_next_call={"tool_name": "
|
|
3307
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3215
3308
|
)
|
|
3216
3309
|
field = current_fields.pop(matched)
|
|
3217
3310
|
layout = _remove_field_from_layout(layout, field["name"])
|
|
@@ -3234,7 +3327,7 @@ class AiBuilderFacade:
|
|
|
3234
3327
|
api_error,
|
|
3235
3328
|
normalized_args=normalized_args,
|
|
3236
3329
|
details={"app_key": target.app_key},
|
|
3237
|
-
suggested_next_call={"tool_name": "
|
|
3330
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3238
3331
|
)
|
|
3239
3332
|
except ValueError as error:
|
|
3240
3333
|
return _failed(
|
|
@@ -3242,7 +3335,7 @@ class AiBuilderFacade:
|
|
|
3242
3335
|
str(error),
|
|
3243
3336
|
normalized_args=normalized_args,
|
|
3244
3337
|
details={"app_key": target.app_key},
|
|
3245
|
-
suggested_next_call={"tool_name": "
|
|
3338
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3246
3339
|
)
|
|
3247
3340
|
|
|
3248
3341
|
try:
|
|
@@ -3256,7 +3349,7 @@ class AiBuilderFacade:
|
|
|
3256
3349
|
str(error),
|
|
3257
3350
|
normalized_args=normalized_args,
|
|
3258
3351
|
details={"app_key": target.app_key},
|
|
3259
|
-
suggested_next_call={"tool_name": "
|
|
3352
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3260
3353
|
)
|
|
3261
3354
|
|
|
3262
3355
|
q_linker_schema_context = deepcopy(schema_result)
|
|
@@ -3272,7 +3365,7 @@ class AiBuilderFacade:
|
|
|
3272
3365
|
str(error),
|
|
3273
3366
|
normalized_args=normalized_args,
|
|
3274
3367
|
details={"app_key": target.app_key},
|
|
3275
|
-
suggested_next_call={"tool_name": "
|
|
3368
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3276
3369
|
)
|
|
3277
3370
|
|
|
3278
3371
|
relation_field_count = _count_relation_fields(current_fields)
|
|
@@ -3351,7 +3444,7 @@ class AiBuilderFacade:
|
|
|
3351
3444
|
request_id=api_error.request_id,
|
|
3352
3445
|
backend_code=api_error.backend_code,
|
|
3353
3446
|
http_status=None if api_error.http_status == 404 else api_error.http_status,
|
|
3354
|
-
suggested_next_call={"tool_name": "
|
|
3447
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3355
3448
|
)
|
|
3356
3449
|
return _failed_from_api_error(
|
|
3357
3450
|
"SCHEMA_APPLY_FAILED",
|
|
@@ -3362,7 +3455,7 @@ class AiBuilderFacade:
|
|
|
3362
3455
|
"app_key": target.app_key,
|
|
3363
3456
|
"field_diff": {"added": added, "updated": updated, "removed": removed},
|
|
3364
3457
|
},
|
|
3365
|
-
suggested_next_call={"tool_name": "
|
|
3458
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3366
3459
|
)
|
|
3367
3460
|
if _code_block_relations_need_source_rebind(compiled_question_relations) or _q_linker_relations_need_source_rebind(compiled_question_relations):
|
|
3368
3461
|
try:
|
|
@@ -3396,7 +3489,7 @@ class AiBuilderFacade:
|
|
|
3396
3489
|
str(error),
|
|
3397
3490
|
normalized_args=normalized_args,
|
|
3398
3491
|
details={"app_key": target.app_key},
|
|
3399
|
-
suggested_next_call={"tool_name": "
|
|
3492
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3400
3493
|
)
|
|
3401
3494
|
except (QingflowApiError, RuntimeError) as error:
|
|
3402
3495
|
api_error = _coerce_api_error(error)
|
|
@@ -3409,7 +3502,7 @@ class AiBuilderFacade:
|
|
|
3409
3502
|
"app_key": target.app_key,
|
|
3410
3503
|
"field_diff": {"added": added, "updated": updated, "removed": removed},
|
|
3411
3504
|
},
|
|
3412
|
-
suggested_next_call={"tool_name": "
|
|
3505
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3413
3506
|
)
|
|
3414
3507
|
rebound_payload = _build_form_payload_from_fields(
|
|
3415
3508
|
title=rebound_schema.get("formTitle") or target.app_name,
|
|
@@ -3436,7 +3529,7 @@ class AiBuilderFacade:
|
|
|
3436
3529
|
"app_key": target.app_key,
|
|
3437
3530
|
"field_diff": {"added": added, "updated": updated, "removed": removed},
|
|
3438
3531
|
},
|
|
3439
|
-
suggested_next_call={"tool_name": "
|
|
3532
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": target.app_key}},
|
|
3440
3533
|
)
|
|
3441
3534
|
current_fields = rebound_fields
|
|
3442
3535
|
response = {
|
|
@@ -3588,7 +3681,7 @@ class AiBuilderFacade:
|
|
|
3588
3681
|
api_error,
|
|
3589
3682
|
normalized_args=normalized_args,
|
|
3590
3683
|
details=_with_state_read_blocked_details({"app_key": app_key}, resource="schema", error=api_error),
|
|
3591
|
-
suggested_next_call={"tool_name": "
|
|
3684
|
+
suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
|
|
3592
3685
|
))
|
|
3593
3686
|
parsed = _parse_schema(schema_result)
|
|
3594
3687
|
current_fields = parsed["fields"]
|
|
@@ -3601,7 +3694,7 @@ class AiBuilderFacade:
|
|
|
3601
3694
|
normalized_args=normalized_args,
|
|
3602
3695
|
details={"unknown_selectors": missing_selectors},
|
|
3603
3696
|
missing_fields=[str(item) for item in missing_selectors],
|
|
3604
|
-
suggested_next_call={"tool_name": "
|
|
3697
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
|
|
3605
3698
|
)
|
|
3606
3699
|
fields_by_name = {field["name"]: field for field in current_fields}
|
|
3607
3700
|
seen: list[str] = []
|
|
@@ -3614,7 +3707,7 @@ class AiBuilderFacade:
|
|
|
3614
3707
|
f"layout references unknown field '{field_name}'",
|
|
3615
3708
|
normalized_args=normalized_args,
|
|
3616
3709
|
details={"field_name": field_name},
|
|
3617
|
-
suggested_next_call={"tool_name": "
|
|
3710
|
+
suggested_next_call={"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
|
|
3618
3711
|
)
|
|
3619
3712
|
if field_name in seen:
|
|
3620
3713
|
return _failed(
|
|
@@ -3755,7 +3848,7 @@ class AiBuilderFacade:
|
|
|
3755
3848
|
"allowed_values": {"modes": ["merge", "replace"]},
|
|
3756
3849
|
"details": {},
|
|
3757
3850
|
"request_id": None,
|
|
3758
|
-
"suggested_next_call": {"tool_name": "
|
|
3851
|
+
"suggested_next_call": {"tool_name": "app_get_layout", "arguments": {"profile": profile, "app_key": app_key}},
|
|
3759
3852
|
"noop": False,
|
|
3760
3853
|
"warnings": [],
|
|
3761
3854
|
"verification": {"layout_verified": False, "layout_summary_verified": False, "layout_read_unavailable": True},
|
|
@@ -3876,7 +3969,7 @@ class AiBuilderFacade:
|
|
|
3876
3969
|
api_error,
|
|
3877
3970
|
normalized_args=normalized_args,
|
|
3878
3971
|
details=_with_state_read_blocked_details({"app_key": app_key}, resource="workflow", error=api_error),
|
|
3879
|
-
suggested_next_call={"tool_name": "
|
|
3972
|
+
suggested_next_call={"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}},
|
|
3880
3973
|
))
|
|
3881
3974
|
entity = _entity_spec_from_app(base_info=base, schema=schema, views=None)
|
|
3882
3975
|
current_fields = _parse_schema(schema)["fields"]
|
|
@@ -3904,7 +3997,7 @@ class AiBuilderFacade:
|
|
|
3904
3997
|
elif first_issue.get("kind", "").startswith("member"):
|
|
3905
3998
|
suggested_call = {"tool_name": "member_search", "arguments": {"profile": profile, "query": first_issue.get("value") or ""}}
|
|
3906
3999
|
elif first_issue.get("kind") in {"editable_fields", "condition_fields"}:
|
|
3907
|
-
suggested_call = {"tool_name": "
|
|
4000
|
+
suggested_call = {"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}}
|
|
3908
4001
|
return _failed(
|
|
3909
4002
|
first_issue.get("error_code") or "FLOW_ASSIGNEE_UNRESOLVED",
|
|
3910
4003
|
"workflow contains unresolved assignees or field permissions",
|
|
@@ -4006,7 +4099,7 @@ class AiBuilderFacade:
|
|
|
4006
4099
|
error_code = "FLOW_READBACK_PENDING"
|
|
4007
4100
|
recoverable = True
|
|
4008
4101
|
message = "applied workflow patch; flow readback pending"
|
|
4009
|
-
suggested_next_call = {"tool_name": "
|
|
4102
|
+
suggested_next_call = {"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}}
|
|
4010
4103
|
elif workflow_verified:
|
|
4011
4104
|
status = "success"
|
|
4012
4105
|
error_code = None
|
|
@@ -4022,7 +4115,7 @@ class AiBuilderFacade:
|
|
|
4022
4115
|
if workflow_structure_verified and not branch_structure_verified
|
|
4023
4116
|
else "applied workflow patch; flow readback did not confirm the requested workflow"
|
|
4024
4117
|
)
|
|
4025
|
-
suggested_next_call = {"tool_name": "
|
|
4118
|
+
suggested_next_call = {"tool_name": "app_get_flow", "arguments": {"profile": profile, "app_key": app_key}}
|
|
4026
4119
|
if not branch_structure_verified:
|
|
4027
4120
|
warnings.append(_warning("WORKFLOW_BRANCH_STRUCTURE_UNVERIFIED", "branch or condition structure was written, but MCP could not fully verify downstream lane structure"))
|
|
4028
4121
|
response = {
|
|
@@ -4110,7 +4203,7 @@ class AiBuilderFacade:
|
|
|
4110
4203
|
api_error,
|
|
4111
4204
|
normalized_args=normalized_args,
|
|
4112
4205
|
details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
|
|
4113
|
-
suggested_next_call={"tool_name": "
|
|
4206
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4114
4207
|
))
|
|
4115
4208
|
existing_views = existing_views or []
|
|
4116
4209
|
existing_by_key: dict[str, dict[str, Any]] = {}
|
|
@@ -4187,7 +4280,7 @@ class AiBuilderFacade:
|
|
|
4187
4280
|
if len(matches) > 1:
|
|
4188
4281
|
return _failed(
|
|
4189
4282
|
"AMBIGUOUS_VIEW",
|
|
4190
|
-
"multiple views matched remove request; use
|
|
4283
|
+
"multiple views matched remove request; use app_get_views and resolve duplicates before removing by name",
|
|
4191
4284
|
normalized_args=normalized_args,
|
|
4192
4285
|
details={
|
|
4193
4286
|
"app_key": app_key,
|
|
@@ -4197,7 +4290,7 @@ class AiBuilderFacade:
|
|
|
4197
4290
|
for view in matches
|
|
4198
4291
|
],
|
|
4199
4292
|
},
|
|
4200
|
-
suggested_next_call={"tool_name": "
|
|
4293
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4201
4294
|
)
|
|
4202
4295
|
if len(matches) == 1:
|
|
4203
4296
|
key = _extract_view_key(matches[0])
|
|
@@ -4246,7 +4339,7 @@ class AiBuilderFacade:
|
|
|
4246
4339
|
"ignored_system_columns": ignored_system_columns,
|
|
4247
4340
|
},
|
|
4248
4341
|
missing_fields=missing_columns,
|
|
4249
|
-
suggested_next_call={"tool_name": "
|
|
4342
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4250
4343
|
)
|
|
4251
4344
|
if patch.group_by and patch.group_by not in field_names:
|
|
4252
4345
|
return _failed(
|
|
@@ -4259,7 +4352,7 @@ class AiBuilderFacade:
|
|
|
4259
4352
|
"missing_fields": [patch.group_by],
|
|
4260
4353
|
},
|
|
4261
4354
|
missing_fields=[patch.group_by],
|
|
4262
|
-
suggested_next_call={"tool_name": "
|
|
4355
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4263
4356
|
)
|
|
4264
4357
|
for gantt_field_name in (patch.start_field, patch.end_field, patch.title_field):
|
|
4265
4358
|
if gantt_field_name and gantt_field_name not in field_names:
|
|
@@ -4273,7 +4366,7 @@ class AiBuilderFacade:
|
|
|
4273
4366
|
"missing_fields": [gantt_field_name],
|
|
4274
4367
|
},
|
|
4275
4368
|
missing_fields=[gantt_field_name],
|
|
4276
|
-
suggested_next_call={"tool_name": "
|
|
4369
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4277
4370
|
)
|
|
4278
4371
|
translated_filters, filter_issues = _build_view_filter_groups(current_fields_by_name=current_fields_by_name, filters=patch.filters)
|
|
4279
4372
|
if filter_issues:
|
|
@@ -4289,7 +4382,7 @@ class AiBuilderFacade:
|
|
|
4289
4382
|
},
|
|
4290
4383
|
missing_fields=list(first_issue.get("missing_fields") or []),
|
|
4291
4384
|
allowed_values=first_issue.get("allowed_values") or {"view_types": [member.value for member in PublicViewType], "view.filter.operator": [member.value for member in ViewFilterOperator]},
|
|
4292
|
-
suggested_next_call={"tool_name": "
|
|
4385
|
+
suggested_next_call={"tool_name": "app_get_fields", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4293
4386
|
)
|
|
4294
4387
|
explicit_button_dtos: list[dict[str, Any]] | None = None
|
|
4295
4388
|
expected_button_summary: list[dict[str, Any]] | None = None
|
|
@@ -4328,7 +4421,7 @@ class AiBuilderFacade:
|
|
|
4328
4421
|
f"view_key '{patch.view_key}' does not exist on this app",
|
|
4329
4422
|
normalized_args=normalized_args,
|
|
4330
4423
|
details={"app_key": app_key, "view_key": patch.view_key, "view_name": patch.name},
|
|
4331
|
-
suggested_next_call={"tool_name": "
|
|
4424
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4332
4425
|
)
|
|
4333
4426
|
existing_key = patch.view_key
|
|
4334
4427
|
else:
|
|
@@ -4346,7 +4439,7 @@ class AiBuilderFacade:
|
|
|
4346
4439
|
for view in name_matches
|
|
4347
4440
|
],
|
|
4348
4441
|
},
|
|
4349
|
-
suggested_next_call={"tool_name": "
|
|
4442
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4350
4443
|
)
|
|
4351
4444
|
if len(name_matches) == 1:
|
|
4352
4445
|
matched_existing_view = name_matches[0]
|
|
@@ -4638,7 +4731,7 @@ class AiBuilderFacade:
|
|
|
4638
4731
|
api_error,
|
|
4639
4732
|
normalized_args=normalized_args,
|
|
4640
4733
|
details=_with_state_read_blocked_details({"app_key": app_key}, resource="views", error=api_error),
|
|
4641
|
-
suggested_next_call={"tool_name": "
|
|
4734
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4642
4735
|
))
|
|
4643
4736
|
verified_names = {
|
|
4644
4737
|
_extract_view_name(item)
|
|
@@ -4858,7 +4951,7 @@ class AiBuilderFacade:
|
|
|
4858
4951
|
),
|
|
4859
4952
|
},
|
|
4860
4953
|
"request_id": first_failure.get("request_id"),
|
|
4861
|
-
"suggested_next_call": {"tool_name": "
|
|
4954
|
+
"suggested_next_call": {"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4862
4955
|
"backend_code": first_failure.get("backend_code"),
|
|
4863
4956
|
"http_status": first_failure.get("http_status"),
|
|
4864
4957
|
"noop": noop,
|
|
@@ -4931,7 +5024,7 @@ class AiBuilderFacade:
|
|
|
4931
5024
|
),
|
|
4932
5025
|
},
|
|
4933
5026
|
"request_id": None,
|
|
4934
|
-
"suggested_next_call": None if verified and view_filters_verified and view_buttons_verified else {"tool_name": "
|
|
5027
|
+
"suggested_next_call": None if verified and view_filters_verified and view_buttons_verified else {"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4935
5028
|
"noop": noop,
|
|
4936
5029
|
"warnings": warnings,
|
|
4937
5030
|
"verification": {
|
|
@@ -4968,7 +5061,7 @@ class AiBuilderFacade:
|
|
|
4968
5061
|
api_error,
|
|
4969
5062
|
normalized_args=normalized_args,
|
|
4970
5063
|
details={"app_key": app_key},
|
|
4971
|
-
suggested_next_call={"tool_name": "
|
|
5064
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4972
5065
|
)
|
|
4973
5066
|
tag_ids_before = _coerce_int_list(base_before.get("tagIds"))
|
|
4974
5067
|
already_published = bool(base_before.get("appPublishStatus") in {1, 2})
|
|
@@ -4982,7 +5075,7 @@ class AiBuilderFacade:
|
|
|
4982
5075
|
api_error,
|
|
4983
5076
|
normalized_args=normalized_args,
|
|
4984
5077
|
details={"app_key": app_key},
|
|
4985
|
-
suggested_next_call={"tool_name": "
|
|
5078
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
4986
5079
|
)
|
|
4987
5080
|
views_before = views_before or []
|
|
4988
5081
|
if already_published and package_already_attached is not False and isinstance(views_before, list) and not views_before_unavailable:
|
|
@@ -5019,7 +5112,7 @@ class AiBuilderFacade:
|
|
|
5019
5112
|
api_error,
|
|
5020
5113
|
normalized_args=normalized_args,
|
|
5021
5114
|
details={"app_key": app_key, "edit_version_no": edit_version_no},
|
|
5022
|
-
suggested_next_call={"tool_name": "
|
|
5115
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
5023
5116
|
)
|
|
5024
5117
|
try:
|
|
5025
5118
|
base = self.apps.app_get_base(profile=profile, app_key=app_key, include_raw=True).get("result") or {}
|
|
@@ -5030,7 +5123,7 @@ class AiBuilderFacade:
|
|
|
5030
5123
|
api_error,
|
|
5031
5124
|
normalized_args=normalized_args,
|
|
5032
5125
|
details={"app_key": app_key},
|
|
5033
|
-
suggested_next_call={"tool_name": "
|
|
5126
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
5034
5127
|
)
|
|
5035
5128
|
tag_ids_after = _coerce_int_list(base.get("tagIds"))
|
|
5036
5129
|
package_attached = None if not expected_package_tag_id else expected_package_tag_id in tag_ids_after
|
|
@@ -5043,7 +5136,7 @@ class AiBuilderFacade:
|
|
|
5043
5136
|
api_error,
|
|
5044
5137
|
normalized_args=normalized_args,
|
|
5045
5138
|
details={"app_key": app_key},
|
|
5046
|
-
suggested_next_call={"tool_name": "
|
|
5139
|
+
suggested_next_call={"tool_name": "app_get_views", "arguments": {"profile": profile, "app_key": app_key}},
|
|
5047
5140
|
)
|
|
5048
5141
|
views = views or []
|
|
5049
5142
|
views_ok = isinstance(views, list) and not views_unavailable
|
|
@@ -5706,7 +5799,7 @@ class AiBuilderFacade:
|
|
|
5706
5799
|
api_error,
|
|
5707
5800
|
normalized_args=normalized_args,
|
|
5708
5801
|
details={"app_key": app_key, "phase": "prepare_edit_context"},
|
|
5709
|
-
suggested_next_call={"tool_name": "
|
|
5802
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
5710
5803
|
)
|
|
5711
5804
|
edit_version_no = _coerce_positive_int(version_result.get("editVersionNo") or version_result.get("versionNo")) or 1
|
|
5712
5805
|
return edit_version_no, None
|
|
@@ -5987,7 +6080,7 @@ class AiBuilderFacade:
|
|
|
5987
6080
|
"APP_CREATE_READBACK_FAILED",
|
|
5988
6081
|
api_error,
|
|
5989
6082
|
details={"app_key": new_app_key, "app_name": app_name, "package_tag_id": package_tag_id},
|
|
5990
|
-
suggested_next_call={"tool_name": "
|
|
6083
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": new_app_key}},
|
|
5991
6084
|
)
|
|
5992
6085
|
return {
|
|
5993
6086
|
"status": "success",
|
|
@@ -6060,7 +6153,7 @@ class AiBuilderFacade:
|
|
|
6060
6153
|
api_error,
|
|
6061
6154
|
normalized_args=normalized_args,
|
|
6062
6155
|
details={"app_key": app_key},
|
|
6063
|
-
suggested_next_call={"tool_name": "
|
|
6156
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
6064
6157
|
)
|
|
6065
6158
|
raw_base = base_result.get("result") if isinstance(base_result.get("result"), dict) else {}
|
|
6066
6159
|
effective_title = str(raw_base.get("formTitle") or fallback_title or "未命名应用").strip() or "未命名应用"
|
|
@@ -6086,7 +6179,7 @@ class AiBuilderFacade:
|
|
|
6086
6179
|
"app base info did not include editable auth payload required for icon update",
|
|
6087
6180
|
normalized_args=normalized_args,
|
|
6088
6181
|
details={"app_key": app_key},
|
|
6089
|
-
suggested_next_call={"tool_name": "
|
|
6182
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
6090
6183
|
)
|
|
6091
6184
|
try:
|
|
6092
6185
|
update_result = self.apps.app_update_base(profile=profile, app_key=app_key, payload=payload)
|
|
@@ -6097,7 +6190,7 @@ class AiBuilderFacade:
|
|
|
6097
6190
|
api_error,
|
|
6098
6191
|
normalized_args=normalized_args,
|
|
6099
6192
|
details={"app_key": app_key, "app_icon": desired_icon},
|
|
6100
|
-
suggested_next_call={"tool_name": "
|
|
6193
|
+
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
6101
6194
|
)
|
|
6102
6195
|
return {
|
|
6103
6196
|
"status": "success",
|
|
@@ -8075,11 +8168,15 @@ def _parse_schema(schema: dict[str, Any]) -> dict[str, Any]:
|
|
|
8075
8168
|
section_rows.append(labels)
|
|
8076
8169
|
if section_rows:
|
|
8077
8170
|
parsed_section_id = _coerce_positive_int(section_question.get("queId"))
|
|
8171
|
+
raw_section_id = None
|
|
8078
8172
|
if parsed_section_id is None:
|
|
8079
|
-
|
|
8173
|
+
raw_section_id = str(section_question.get("sectionId") or "").strip() or None
|
|
8174
|
+
if raw_section_id is None:
|
|
8175
|
+
parsed_section_id = _coerce_positive_int(section_question.get("sectionId"))
|
|
8080
8176
|
sections.append(
|
|
8081
8177
|
{
|
|
8082
|
-
"section_id":
|
|
8178
|
+
"section_id": raw_section_id
|
|
8179
|
+
or str(parsed_section_id or _slugify(section_question.get("queTitle") or "section", default="section")),
|
|
8083
8180
|
"title": section_question.get("queTitle") or "未命名分组",
|
|
8084
8181
|
"rows": section_rows,
|
|
8085
8182
|
}
|