@qingflow-tech/qingflow-app-builder-mcp 1.0.7 → 1.0.8

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.
@@ -43,6 +43,7 @@ from ..builder_facade.models import (
43
43
  SchemaPlanRequest,
44
44
  VisibilityPatch,
45
45
  ViewFilterOperator,
46
+ ViewPartialPatch,
46
47
  ViewUpsertPatch,
47
48
  ViewsPreset,
48
49
  ViewsPlanRequest,
@@ -60,6 +61,14 @@ from .solution_tools import SolutionTools
60
61
  from .view_tools import ViewTools
61
62
  from .workflow_tools import WorkflowTools
62
63
 
64
+
65
+ def _normalize_builder_view_key(value: str) -> str:
66
+ raw = str(value or "").strip()
67
+ if raw.startswith("custom:"):
68
+ return raw.split(":", 1)[1].strip()
69
+ return raw
70
+
71
+
63
72
  PUBLIC_STABLE_FLOW_NODE_TYPES = ["start", "approve", "fill", "copy", "webhook", "end"]
64
73
 
65
74
 
@@ -227,6 +236,7 @@ class AiBuilderTools(ToolBase):
227
236
  profile: str = DEFAULT_PROFILE,
228
237
  app_key: str = "",
229
238
  upsert_buttons: list[JSONObject] | None = None,
239
+ patch_buttons: list[JSONObject] | None = None,
230
240
  remove_buttons: list[JSONObject] | None = None,
231
241
  view_configs: list[JSONObject] | None = None,
232
242
  ) -> JSONObject:
@@ -234,6 +244,7 @@ class AiBuilderTools(ToolBase):
234
244
  profile=profile,
235
245
  app_key=app_key,
236
246
  upsert_buttons=upsert_buttons or [],
247
+ patch_buttons=patch_buttons or [],
237
248
  remove_buttons=remove_buttons or [],
238
249
  view_configs=view_configs or [],
239
250
  )
@@ -243,6 +254,7 @@ class AiBuilderTools(ToolBase):
243
254
  profile: str = DEFAULT_PROFILE,
244
255
  app_key: str = "",
245
256
  upsert_resources: list[JSONObject] | None = None,
257
+ patch_resources: list[JSONObject] | None = None,
246
258
  remove_associated_item_ids: list[int] | None = None,
247
259
  reorder_associated_item_ids: list[int] | None = None,
248
260
  view_configs: list[JSONObject] | None = None,
@@ -251,6 +263,7 @@ class AiBuilderTools(ToolBase):
251
263
  profile=profile,
252
264
  app_key=app_key,
253
265
  upsert_resources=upsert_resources or [],
266
+ patch_resources=patch_resources or [],
254
267
  remove_associated_item_ids=remove_associated_item_ids or [],
255
268
  reorder_associated_item_ids=reorder_associated_item_ids or [],
256
269
  view_configs=view_configs or [],
@@ -395,6 +408,7 @@ class AiBuilderTools(ToolBase):
395
408
  app_key: str = "",
396
409
  publish: bool = True,
397
410
  upsert_views: list[JSONObject] | None = None,
411
+ patch_views: list[JSONObject] | None = None,
398
412
  remove_views: list[str] | None = None,
399
413
  ) -> JSONObject:
400
414
  return self.app_views_apply(
@@ -402,6 +416,7 @@ class AiBuilderTools(ToolBase):
402
416
  app_key=app_key,
403
417
  publish=publish,
404
418
  upsert_views=upsert_views or [],
419
+ patch_views=patch_views or [],
405
420
  remove_views=remove_views or [],
406
421
  )
407
422
 
@@ -410,6 +425,7 @@ class AiBuilderTools(ToolBase):
410
425
  profile: str = DEFAULT_PROFILE,
411
426
  app_key: str = "",
412
427
  upsert_charts: list[JSONObject] | None = None,
428
+ patch_charts: list[JSONObject] | None = None,
413
429
  remove_chart_ids: list[str] | None = None,
414
430
  reorder_chart_ids: list[str] | None = None,
415
431
  ) -> JSONObject:
@@ -417,6 +433,7 @@ class AiBuilderTools(ToolBase):
417
433
  profile=profile,
418
434
  app_key=app_key,
419
435
  upsert_charts=upsert_charts or [],
436
+ patch_charts=patch_charts or [],
420
437
  remove_chart_ids=remove_chart_ids or [],
421
438
  reorder_chart_ids=reorder_chart_ids or [],
422
439
  )
@@ -882,6 +899,7 @@ class AiBuilderTools(ToolBase):
882
899
  profile: str,
883
900
  app_key: str,
884
901
  upsert_buttons: list[JSONObject],
902
+ patch_buttons: list[JSONObject] | None = None,
885
903
  remove_buttons: list[JSONObject],
886
904
  view_configs: list[JSONObject] | None = None,
887
905
  ) -> JSONObject:
@@ -889,6 +907,7 @@ class AiBuilderTools(ToolBase):
889
907
  raw_request = {
890
908
  "app_key": app_key,
891
909
  "upsert_buttons": upsert_buttons,
910
+ "patch_buttons": patch_buttons or [],
892
911
  "remove_buttons": remove_buttons,
893
912
  "view_configs": view_configs or [],
894
913
  }
@@ -936,11 +955,13 @@ class AiBuilderTools(ToolBase):
936
955
  remove_associated_item_ids: list[int],
937
956
  reorder_associated_item_ids: list[int],
938
957
  view_configs: list[JSONObject],
958
+ patch_resources: list[JSONObject] | None = None,
939
959
  ) -> JSONObject:
940
960
  """执行应用关联资源 apply 逻辑。"""
941
961
  raw_request = {
942
962
  "app_key": app_key,
943
963
  "upsert_resources": upsert_resources,
964
+ "patch_resources": patch_resources or [],
944
965
  "remove_associated_item_ids": remove_associated_item_ids,
945
966
  "reorder_associated_item_ids": reorder_associated_item_ids,
946
967
  "view_configs": view_configs,
@@ -1272,7 +1293,7 @@ class AiBuilderTools(ToolBase):
1272
1293
  @tool_cn_name("视图详情查询")
1273
1294
  def view_get(self, *, profile: str, view_key: str = "", viewgraph_key: str = "") -> JSONObject:
1274
1295
  """执行视图相关逻辑。"""
1275
- resolved_view_key = str(view_key or viewgraph_key or "").strip()
1296
+ resolved_view_key = _normalize_builder_view_key(str(view_key or viewgraph_key or "").strip())
1276
1297
  normalized_args = {"view_key": resolved_view_key}
1277
1298
  return _safe_tool_call(
1278
1299
  lambda: self._facade.view_get(profile=profile, view_key=resolved_view_key),
@@ -1853,6 +1874,7 @@ class AiBuilderTools(ToolBase):
1853
1874
  app_key: str,
1854
1875
  publish: bool = True,
1855
1876
  upsert_views: list[JSONObject],
1877
+ patch_views: list[JSONObject] | None = None,
1856
1878
  remove_views: list[str],
1857
1879
  ) -> JSONObject:
1858
1880
  """执行应用相关逻辑。"""
@@ -1861,6 +1883,7 @@ class AiBuilderTools(ToolBase):
1861
1883
  app_key=app_key,
1862
1884
  publish=publish,
1863
1885
  upsert_views=upsert_views,
1886
+ patch_views=patch_views or [],
1864
1887
  remove_views=remove_views,
1865
1888
  )
1866
1889
  return self._retry_after_self_lock_release(
@@ -1872,6 +1895,7 @@ class AiBuilderTools(ToolBase):
1872
1895
  publish=publish,
1873
1896
  remove_views=remove_views,
1874
1897
  upsert_views=upsert_views,
1898
+ patch_views=patch_views or [],
1875
1899
  ),
1876
1900
  )
1877
1901
 
@@ -1882,9 +1906,53 @@ class AiBuilderTools(ToolBase):
1882
1906
  app_key: str,
1883
1907
  publish: bool = True,
1884
1908
  upsert_views: list[JSONObject],
1909
+ patch_views: list[JSONObject],
1885
1910
  remove_views: list[str],
1886
1911
  ) -> JSONObject:
1887
1912
  """执行内部辅助逻辑。"""
1913
+ if patch_views:
1914
+ try:
1915
+ parsed_views = [ViewUpsertPatch.model_validate(item) for item in (upsert_views or [])]
1916
+ parsed_patch_views = [ViewPartialPatch.model_validate(item) for item in patch_views]
1917
+ except ValidationError as exc:
1918
+ return _visibility_validation_failure(
1919
+ str(exc),
1920
+ tool_name="app_views_apply",
1921
+ exc=exc,
1922
+ suggested_next_call={
1923
+ "tool_name": "app_views_apply",
1924
+ "arguments": {
1925
+ "profile": profile,
1926
+ "app_key": app_key,
1927
+ "publish": publish,
1928
+ "upsert_views": upsert_views or [],
1929
+ "patch_views": [
1930
+ {"view_key": "VIEW_KEY", "set": {"query_conditions": {"enabled": True, "rows": [["字段A"]]}}},
1931
+ ],
1932
+ "remove_views": remove_views or [],
1933
+ },
1934
+ },
1935
+ )
1936
+ normalized_args = {
1937
+ "app_key": app_key,
1938
+ "publish": publish,
1939
+ "upsert_views": [view.model_dump(mode="json") for view in parsed_views],
1940
+ "patch_views": [patch.model_dump(mode="json") for patch in parsed_patch_views],
1941
+ "remove_views": list(remove_views or []),
1942
+ }
1943
+ return _safe_tool_call(
1944
+ lambda: self._facade.app_views_apply(
1945
+ profile=profile,
1946
+ app_key=app_key,
1947
+ publish=publish,
1948
+ upsert_views=parsed_views,
1949
+ patch_views=parsed_patch_views,
1950
+ remove_views=list(remove_views or []),
1951
+ ),
1952
+ error_code="VIEWS_APPLY_FAILED",
1953
+ normalized_args=normalized_args,
1954
+ suggested_next_call={"tool_name": "app_views_apply", "arguments": {"profile": profile, **normalized_args}},
1955
+ )
1888
1956
  plan_result = self._rewrite_plan_result_for_apply(
1889
1957
  result=self.app_views_plan(
1890
1958
  profile=profile,
@@ -1933,6 +2001,7 @@ class AiBuilderTools(ToolBase):
1933
2001
  app_key=str(plan_args.get("app_key") or app_key),
1934
2002
  publish=publish,
1935
2003
  upsert_views=parsed_views,
2004
+ patch_views=[],
1936
2005
  remove_views=list(plan_args.get("remove_views") or remove_views),
1937
2006
  ),
1938
2007
  error_code="VIEWS_APPLY_FAILED",
@@ -1947,6 +2016,7 @@ class AiBuilderTools(ToolBase):
1947
2016
  profile: str,
1948
2017
  app_key: str,
1949
2018
  upsert_charts: list[JSONObject],
2019
+ patch_charts: list[JSONObject] | None = None,
1950
2020
  remove_chart_ids: list[str],
1951
2021
  reorder_chart_ids: list[str],
1952
2022
  ) -> JSONObject:
@@ -1955,6 +2025,7 @@ class AiBuilderTools(ToolBase):
1955
2025
  profile=profile,
1956
2026
  app_key=app_key,
1957
2027
  upsert_charts=upsert_charts,
2028
+ patch_charts=patch_charts or [],
1958
2029
  remove_chart_ids=remove_chart_ids,
1959
2030
  reorder_chart_ids=reorder_chart_ids,
1960
2031
  )
@@ -1968,6 +2039,7 @@ class AiBuilderTools(ToolBase):
1968
2039
  upsert_charts: list[JSONObject],
1969
2040
  remove_chart_ids: list[str],
1970
2041
  reorder_chart_ids: list[str],
2042
+ patch_charts: list[JSONObject] | None = None,
1971
2043
  ) -> JSONObject:
1972
2044
  """执行应用相关逻辑。"""
1973
2045
  try:
@@ -1975,6 +2047,7 @@ class AiBuilderTools(ToolBase):
1975
2047
  {
1976
2048
  "app_key": app_key,
1977
2049
  "upsert_charts": upsert_charts or [],
2050
+ "patch_charts": patch_charts or [],
1978
2051
  "remove_chart_ids": remove_chart_ids or [],
1979
2052
  "reorder_chart_ids": reorder_chart_ids or [],
1980
2053
  }
@@ -2550,6 +2623,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2550
2623
  "allowed_values": deepcopy(_VISIBILITY_ALLOWED_VALUES),
2551
2624
  "execution_notes": [
2552
2625
  "create or update package metadata, visibility, grouping, and ordering in one call",
2626
+ "metadata keys omitted on update are preserved",
2553
2627
  "package_id maps internally to backend tagId; do not use tag_id in public calls",
2554
2628
  "items is a full package layout tree; omitting existing app/portal items is blocked unless allow_detach=true",
2555
2629
  "item shapes: {type:'app', app_key}, {type:'portal', dash_key}, or {type:'group', group_id?, name, items:[...]}",
@@ -2696,6 +2770,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2696
2770
  "allowed_keys": [
2697
2771
  "app_key",
2698
2772
  "upsert_buttons",
2773
+ "patch_buttons",
2699
2774
  "remove_buttons",
2700
2775
  "view_configs",
2701
2776
  "upsert_buttons[].client_key",
@@ -2713,6 +2788,10 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2713
2788
  "upsert_buttons[].trigger_add_data_config.field_mappings[].source_field",
2714
2789
  "upsert_buttons[].trigger_add_data_config.field_mappings[].target_field",
2715
2790
  "upsert_buttons[].trigger_add_data_config.default_values",
2791
+ "patch_buttons[].button_id",
2792
+ "patch_buttons[].button_text",
2793
+ "patch_buttons[].set",
2794
+ "patch_buttons[].unset",
2716
2795
  "view_configs[].view_key",
2717
2796
  "view_configs[].mode",
2718
2797
  "view_configs[].buttons",
@@ -2728,6 +2807,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2728
2807
  ],
2729
2808
  "aliases": {
2730
2809
  "upsertButtons": "upsert_buttons",
2810
+ "patchButtons": "patch_buttons",
2731
2811
  "removeButtons": "remove_buttons",
2732
2812
  "viewConfigs": "view_configs",
2733
2813
  "clientKey": "client_key",
@@ -2767,15 +2847,19 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2767
2847
  },
2768
2848
  "execution_notes": [
2769
2849
  "this is the default custom-button write path; old list/get/create/update/delete tools are hidden from the public agent surface",
2850
+ "use patch_buttons for partial parameter replacement on existing buttons; the tool reads current button detail, merges patch_buttons[].set/unset, then submits the backend full-save payload internally",
2770
2851
  "button_id targets an existing button; without button_id, button_text is used as an exact unique upsert key, otherwise a new button is created",
2771
2852
  "for addData buttons, use trigger_add_data_config.target_app_key plus field_mappings/default_values; field_mappings compiles to backend queRelation",
2853
+ "field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id maps to current record id (-17), 编号/record_number maps to visible record number (0)",
2854
+ "to fill a target relation field with the current source record, map source_field='数据ID' to the target relation field; default_values is for static constants, not dynamic current-record values",
2772
2855
  "do not write raw que_relation unless maintaining a legacy config; field_mappings/default_values and que_relation are mutually exclusive",
2773
2856
  "view_configs binds custom buttons into views in the same apply call; button_ref may be a same-call client_key, a button_id, or an exact unique existing button_text",
2857
+ "view_configs[].view_key is the raw builder view key from app_get.views[].view_key; do not pass record-data view_id values like custom:VIEW_KEY",
2774
2858
  "view_configs[].buttons is required in merge mode; omitting buttons is blocked to avoid no-op writes and accidental publish",
2775
2859
  "view_configs[].mode defaults to merge; use mode=replace or an explicit empty buttons list to replace/clear a view's custom button bindings",
2776
2860
  "advanced view button binding supports button_limit, button_formula, button_formula_type, and print_tpls when visibility or print-template behavior is required",
2777
2861
  "default placements are header and detail; header maps to frontend top buttons",
2778
- "placement=list is accepted as an experimental row/list placement, but some backend deployments reject it; if that happens, the tool returns LIST_BUTTON_BACKEND_UNSUPPORTED and still applies header/detail placements when they were part of the same view config",
2862
+ "placement=list configures backend INSIDE row/list buttons; header maps to TOP and detail maps to DETAIL",
2779
2863
  "remove_buttons supports button_id or exact unique button_text",
2780
2864
  "all operations share one edit context and publish after at least one write succeeds; there is no draft-only mode for this tool",
2781
2865
  "background_color and text_color cannot both be white",
@@ -2792,6 +2876,12 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2792
2876
  "trigger_link_url": "https://example.com",
2793
2877
  }
2794
2878
  ],
2879
+ "patch_buttons": [
2880
+ {
2881
+ "button_text": "同步客户",
2882
+ "set": {"trigger_link_url": "https://example.com/new"},
2883
+ }
2884
+ ],
2795
2885
  "remove_buttons": [{"button_text": "旧按钮"}],
2796
2886
  "view_configs": [],
2797
2887
  },
@@ -2820,11 +2910,36 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2820
2910
  }
2821
2911
  ],
2822
2912
  },
2913
+ "relation_current_record_example": {
2914
+ "profile": "default",
2915
+ "app_key": "EMPLOYEE_APP",
2916
+ "upsert_buttons": [
2917
+ {
2918
+ "client_key": "add_worklog",
2919
+ "button_text": "快捷添加工时",
2920
+ "style_preset": "neutral_outline",
2921
+ "button_icon": "ex-plus-circle",
2922
+ "trigger_action": "addData",
2923
+ "trigger_add_data_config": {
2924
+ "target_app_key": "WORKLOG_APP",
2925
+ "field_mappings": [{"source_field": "数据ID", "target_field": "关联员工"}],
2926
+ "default_values": {"状态": "待提交"},
2927
+ },
2928
+ }
2929
+ ],
2930
+ "view_configs": [
2931
+ {
2932
+ "view_key": "RAW_VIEW_KEY",
2933
+ "buttons": [{"button_ref": "add_worklog", "placement": "detail", "primary": True}],
2934
+ }
2935
+ ],
2936
+ },
2823
2937
  },
2824
2938
  "app_associated_resources_apply": {
2825
2939
  "allowed_keys": [
2826
2940
  "app_key",
2827
2941
  "upsert_resources",
2942
+ "patch_resources",
2828
2943
  "remove_associated_item_ids",
2829
2944
  "reorder_associated_item_ids",
2830
2945
  "view_configs",
@@ -2835,7 +2950,15 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2835
2950
  "upsert_resources[].view_key",
2836
2951
  "upsert_resources[].chart_key",
2837
2952
  "upsert_resources[].report_source",
2953
+ "upsert_resources[].match_mappings",
2954
+ "upsert_resources[].match_mappings[].target_field",
2955
+ "upsert_resources[].match_mappings[].source_field",
2956
+ "upsert_resources[].match_mappings[].value",
2957
+ "upsert_resources[].match_mappings[].operator",
2838
2958
  "upsert_resources[].match_rules",
2959
+ "patch_resources[].associated_item_id",
2960
+ "patch_resources[].set",
2961
+ "patch_resources[].unset",
2839
2962
  "view_configs[].view_key",
2840
2963
  "view_configs[].visible",
2841
2964
  "view_configs[].limit_type",
@@ -2844,6 +2967,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2844
2967
  ],
2845
2968
  "aliases": {
2846
2969
  "upsertResources": "upsert_resources",
2970
+ "patchResources": "patch_resources",
2847
2971
  "resources": "upsert_resources",
2848
2972
  "removeAssociatedItemIds": "remove_associated_item_ids",
2849
2973
  "reorderAssociatedItemIds": "reorder_associated_item_ids",
@@ -2855,6 +2979,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2855
2979
  "viewKey": "view_key",
2856
2980
  "viewgraphKey": "view_key",
2857
2981
  "reportSource": "report_source",
2982
+ "matchMappings": "match_mappings",
2858
2983
  "matchRules": "match_rules",
2859
2984
  "associatedItemRefs": "associated_item_refs",
2860
2985
  "associatedItemIds": "associated_item_ids",
@@ -2866,10 +2991,15 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2866
2991
  },
2867
2992
  "execution_notes": [
2868
2993
  "this is the default associated report/view path; it manages both the app-level associated resource pool and per-view display config",
2994
+ "use patch_resources for partial parameter replacement on existing associated resources; the tool reads the current resource including backend-required raw fields, merges patch_resources[].set/unset, then submits the backend full-save payload internally",
2869
2995
  "associated_item_id is form_asos_chart.id from app_get.associated_resources[].associated_item_id; it is not chart_id, chart_key, or view_key",
2996
+ "before creating an associated resource, read app_get.associated_resources and reuse an existing item with patch_resources when target_app_key + view_key/chart_key already matches; repeated upsert_resources without associated_item_id can create duplicate associated items",
2870
2997
  "graph_type=view uses view_key and internally compiles to the Qingflow view source; graph_type=chart uses chart_key and defaults to report_source=app",
2871
2998
  "report_source=app maps to BI_QINGFLOW; report_source=dataset maps to BI_DATASET; do not pass raw backend sourceType",
2872
- "client_key lets a view_config reference a resource created earlier in the same apply call through associated_item_refs",
2999
+ "use match_mappings for associated view/report filtering; dynamic conditions use source_field and static conditions use value",
3000
+ "match_mappings.source_field accepts source schema fields plus system fields 数据ID(-17) and 编号(0); match_mappings compiles to backend matchRules",
3001
+ "do not write raw match_rules unless preserving a legacy backend config; match_mappings and match_rules are mutually exclusive",
3002
+ "client_key only lets a view_config reference a resource created earlier in the same apply call through associated_item_refs; it is not persisted and cannot deduplicate later apply calls",
2873
3003
  "this tool publishes after at least one write succeeds; there is no draft-only mode",
2874
3004
  "visible=false hides the associated-resource area without clearing previous selected ids; visible=true with limit_type=all shows the whole app-level pool",
2875
3005
  ],
@@ -2882,7 +3012,13 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
2882
3012
  "graph_type": "view",
2883
3013
  "target_app_key": "TARGET_APP",
2884
3014
  "view_key": "VIEW_KEY",
2885
- "match_rules": [],
3015
+ "match_mappings": [{"target_field": "关联员工", "source_field": "数据ID"}],
3016
+ }
3017
+ ],
3018
+ "patch_resources": [
3019
+ {
3020
+ "associated_item_id": 123,
3021
+ "set": {"match_mappings": [{"target_field": "状态", "value": "待提交"}]},
2886
3022
  }
2887
3023
  ],
2888
3024
  "remove_associated_item_ids": [],
@@ -3009,6 +3145,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3009
3145
  "create mode: package_id + app_name + create_if_missing=true",
3010
3146
  "create mode defaults new app visibility to workspace/not when visibility is omitted; edit mode preserves current visibility when omitted",
3011
3147
  *_VISIBILITY_EXECUTION_NOTES,
3148
+ "update_fields is the field-level partial update path; it reads current form schema and preserves untouched field config",
3012
3149
  "multiple relation fields are backend-risky; read verification.relation_field_limit_verified and warnings before declaring the schema stable",
3013
3150
  "backend 49614 is normalized to MULTIPLE_RELATION_FIELDS_UNSUPPORTED with a workaround message",
3014
3151
  "relation_mode=multiple maps to referenceConfig.optionalDataNum=0",
@@ -3208,6 +3345,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3208
3345
  },
3209
3346
  "allowed_values": {"mode": [member.value for member in LayoutApplyMode]},
3210
3347
  "execution_notes": [
3348
+ "mode=merge is the layout partial update path: mentioned sections are merged and unmentioned fields are preserved",
3349
+ "mode=replace is full layout replacement and should be used only when intentionally rewriting all sections",
3211
3350
  "layout verification is split into layout_verified and layout_summary_verified",
3212
3351
  "LAYOUT_SUMMARY_UNVERIFIED means raw form readback is stronger than the compact summary",
3213
3352
  ],
@@ -3285,6 +3424,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3285
3424
  ],
3286
3425
  "execution_notes": [
3287
3426
  "public flow building is intentionally limited to linear workflows",
3427
+ "app_flow_apply is replace-only; do not treat node snippets as partial patches",
3288
3428
  "branch and condition nodes are disabled because the backend workflow route is not front-end stable for these node types",
3289
3429
  "workflow verification only covers linear node structure in the public tool surface",
3290
3430
  ],
@@ -3308,7 +3448,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3308
3448
  },
3309
3449
  },
3310
3450
  "app_views_plan": {
3311
- "allowed_keys": ["app_key", "upsert_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions"],
3451
+ "allowed_keys": ["app_key", "upsert_views", "patch_views", "remove_views", "preset", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions", "patch_views[].view_key", "patch_views[].name", "patch_views[].set", "patch_views[].unset"],
3312
3452
  "aliases": {
3313
3453
  "fields": "columns",
3314
3454
  "column_names": "columns",
@@ -3322,6 +3462,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3322
3462
  "queryConditions": "query_conditions",
3323
3463
  "query_condition": "query_conditions",
3324
3464
  "queryCondition": "query_conditions",
3465
+ "patchViews": "patch_views",
3325
3466
  "startField": "start_field",
3326
3467
  "endField": "end_field",
3327
3468
  "titleField": "title_field",
@@ -3346,6 +3487,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3346
3487
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
3347
3488
  "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
3348
3489
  "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
3490
+ "use patch_views for partial parameter replacement on existing views; the tool reads current config, merges patch_views[].set/unset, then submits the backend full-save payload internally",
3349
3491
  "view associated report/view display is now configured through app_associated_resources_apply, not app_views_apply",
3350
3492
  "for multi-value operators such as in, pass values as a list; value may also be used as an alias when it already contains a list",
3351
3493
  *_VISIBILITY_EXECUTION_NOTES,
@@ -3357,6 +3499,22 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3357
3499
  "remove_views": [],
3358
3500
  },
3359
3501
  "query_conditions_example": {
3502
+ "profile": "default",
3503
+ "app_key": "APP_KEY",
3504
+ "patch_views": [
3505
+ {
3506
+ "view_key": "VIEW_KEY",
3507
+ "set": {
3508
+ "query_conditions": {
3509
+ "enabled": True,
3510
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3511
+ }
3512
+ },
3513
+ }
3514
+ ],
3515
+ "remove_views": [],
3516
+ },
3517
+ "full_upsert_query_conditions_example": {
3360
3518
  "profile": "default",
3361
3519
  "app_key": "APP_KEY",
3362
3520
  "upsert_views": [
@@ -3393,7 +3551,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3393
3551
  },
3394
3552
  },
3395
3553
  "app_views_apply": {
3396
- "allowed_keys": ["app_key", "publish", "upsert_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions"],
3554
+ "allowed_keys": ["app_key", "publish", "upsert_views", "patch_views", "remove_views", "upsert_views[].view_key", "upsert_views[].buttons", "upsert_views[].visibility", "upsert_views[].query_conditions", "patch_views[].view_key", "patch_views[].name", "patch_views[].set", "patch_views[].unset"],
3397
3555
  "aliases": {
3398
3556
  "fields": "columns",
3399
3557
  "column_names": "columns",
@@ -3407,6 +3565,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3407
3565
  "queryConditions": "query_conditions",
3408
3566
  "query_condition": "query_conditions",
3409
3567
  "queryCondition": "query_conditions",
3568
+ "patchViews": "patch_views",
3410
3569
  "startField": "start_field",
3411
3570
  "endField": "end_field",
3412
3571
  "titleField": "title_field",
@@ -3435,6 +3594,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3435
3594
  "upsert_views[].visibility may set per-view visibility; omit it to preserve an existing view's auth or default a new view to workspace/not",
3436
3595
  "filters are saved fixed filters that apply when the view opens; query_conditions configure the frontend query panel and only apply after a user enters query values",
3437
3596
  "upsert_views[].query_conditions.rows is a layout matrix of field names; it is compiled to backend queryCondition queIds",
3597
+ "use patch_views for partial parameter replacement on existing views; the public update mode is patch even though the backend save is still a full view payload",
3438
3598
  "view associated report/view display is now configured through app_associated_resources_apply; app_views_apply keeps legacy associated_resources input compatible but it is no longer the recommended public contract",
3439
3599
  "for multi-value operators such as in, pass values as a list; value may also be used as an alias when it already contains a list",
3440
3600
  *_VISIBILITY_EXECUTION_NOTES,
@@ -3447,6 +3607,23 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3447
3607
  "remove_views": [],
3448
3608
  },
3449
3609
  "query_conditions_example": {
3610
+ "profile": "default",
3611
+ "app_key": "APP_KEY",
3612
+ "publish": True,
3613
+ "patch_views": [
3614
+ {
3615
+ "view_key": "VIEW_KEY",
3616
+ "set": {
3617
+ "query_conditions": {
3618
+ "enabled": True,
3619
+ "rows": [["客户名称", "负责人"], ["创建时间"]],
3620
+ }
3621
+ },
3622
+ }
3623
+ ],
3624
+ "remove_views": [],
3625
+ },
3626
+ "full_upsert_query_conditions_example": {
3450
3627
  "profile": "default",
3451
3628
  "app_key": "APP_KEY",
3452
3629
  "publish": True,
@@ -3604,8 +3781,9 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3604
3781
  },
3605
3782
  },
3606
3783
  "app_charts_apply": {
3607
- "allowed_keys": ["app_key", "upsert_charts", "remove_chart_ids", "reorder_chart_ids", "upsert_charts[].visibility"],
3784
+ "allowed_keys": ["app_key", "upsert_charts", "patch_charts", "remove_chart_ids", "reorder_chart_ids", "upsert_charts[].visibility", "patch_charts[].chart_id", "patch_charts[].name", "patch_charts[].set", "patch_charts[].unset"],
3608
3785
  "aliases": {
3786
+ "patchCharts": "patch_charts",
3609
3787
  "chart.id": "chart.chart_id",
3610
3788
  "chart.type": "chart.chart_type",
3611
3789
  "chart.dimension_fields": "chart.dimension_field_ids",
@@ -3620,6 +3798,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3620
3798
  },
3621
3799
  "execution_notes": [
3622
3800
  "app_charts_apply is immediate-live and does not publish",
3801
+ "use patch_charts for partial parameter replacement on existing charts; the tool reads current chart base/config, merges patch_charts[].set/unset, then submits full QingBI base/config payloads internally",
3623
3802
  "chart matching precedence is chart_id first, then exact unique chart name",
3624
3803
  "when chart names are not unique, supply chart_id instead of guessing by name",
3625
3804
  "successful create results must return a real backend chart_id",
@@ -3631,6 +3810,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3631
3810
  "profile": "default",
3632
3811
  "app_key": "APP_KEY",
3633
3812
  "upsert_charts": [{"name": "数据总量", "chart_type": "target", "indicator_field_ids": [], "visibility": deepcopy(_VISIBILITY_WORKSPACE_EXAMPLE)}],
3813
+ "patch_charts": [{"chart_id": "CHART_ID", "set": {"name": "数据总量-新版"}}],
3634
3814
  "remove_chart_ids": [],
3635
3815
  "reorder_chart_ids": [],
3636
3816
  },
@@ -3642,6 +3822,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3642
3822
  "execution_notes": [
3643
3823
  "returns one builder-side view definition detail",
3644
3824
  "does not return record data; use user-side view_get or record_list for runtime rows",
3825
+ "view_key is a raw builder view key; if a record-data custom:VIEW_KEY value is passed, the tool normalizes it to VIEW_KEY",
3645
3826
  "use this after builder portal_get when a component references a view_ref.view_key",
3646
3827
  "returns normalized view visibility when backend auth is readable",
3647
3828
  ],
@@ -3688,6 +3869,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
3688
3869
  "create mode: package_id + dash_name",
3689
3870
  "portal_apply uses replace semantics for sections",
3690
3871
  "when editing an existing portal, sections may be omitted to update only base info such as visibility, icon, or package",
3872
+ "portal section-level patch is not exposed; supplying sections means full sections replacement",
3691
3873
  "remove a section by omitting it from the new sections list",
3692
3874
  "package_id is required when creating a new portal",
3693
3875
  "publish=false only guarantees draft and base-info updates; it does not claim live has changed",