answer42 0.4.75__tar.gz → 0.4.76__tar.gz
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.
- {answer42-0.4.75 → answer42-0.4.76}/PKG-INFO +1 -1
- {answer42-0.4.75 → answer42-0.4.76}/pyproject.toml +1 -1
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/service.py +8 -4
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/summary_backend.py +86 -1
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/server.py +94 -16
- {answer42-0.4.75 → answer42-0.4.76}/.gitignore +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/LICENSE +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/README.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/credentials.example.json +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/docs/agent-installation.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/docs/architecture.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/docs/assets/answer42-logo.png +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/docs/assets/platform42-logo.svg +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/docs/installation.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/build_cf.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/build_pages.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/e2e_http_smoke.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/e2e_stable.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/openclaw_mcp_autoreload.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/scripts/rag_cli.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/Configuration.xml +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Catalogs//320/237/320/241_/320/222/320/273/320/260/320/264/320/265/320/273/320/265/321/206.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Catalogs//320/237/320/241_/320/241/320/277/321/200/320/260/320/262/320/276/321/207/320/275/320/270/320/2721.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/ConfigDumpInfo.xml +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Configuration.xml +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ManagerModule.bsl" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ObjectModule.bsl" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205/Ext/Template.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262.xml" +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/__init__.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/__init__.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/skills/answer42-universal-report/SKILL.md +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/auth.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/bridge.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/credentials.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/os_support.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/platform.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/protocol.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/__init__.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/detect.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/dump.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/model.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/parsers.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/store.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/rag/summary_vector.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/recorder.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/release_helper.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/runtime.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/skill_installer.py +0 -0
- {answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/window_control.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: answer42
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.76
|
|
4
4
|
Summary: Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API
|
|
5
5
|
Author: Marvin (AI Assistant), 42Clouds, and contributors
|
|
6
6
|
Author-email: "Kosolapov Stanislav (proDOOMman)" <prodoomman@gmail.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "answer42"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.76"
|
|
4
4
|
description = "Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -417,9 +417,9 @@ class RagService:
|
|
|
417
417
|
row["data_composition_fields"] = [dict(r) for r in con.execute("SELECT schema_name,data_path,field,title,expression,dataset_name,field_type,source_path FROM data_composition_fields WHERE object_id=? ORDER BY id", (oid,))]
|
|
418
418
|
return {"query": object, "snapshot": snapshot, "matches": rows}
|
|
419
419
|
|
|
420
|
-
def lookup_form_element_types(self, snapshot: str | int | None = None) -> dict[str, Any]:
|
|
420
|
+
def lookup_form_element_types(self, snapshot: str | int | None = None, object_full_name: str | None = None) -> dict[str, Any]:
|
|
421
421
|
if self.summary_backend.is_summary_snapshot(snapshot):
|
|
422
|
-
return self.summary_backend.lookup_form_element_types(snapshot)
|
|
422
|
+
return self.summary_backend.lookup_form_element_types(snapshot, object_full_name=object_full_name)
|
|
423
423
|
"""Return a compact name/title/data-path index for UI-tree enrichment.
|
|
424
424
|
|
|
425
425
|
The 1C test-client API exposes runtime UI element names and captions, but
|
|
@@ -435,14 +435,18 @@ class RagService:
|
|
|
435
435
|
auto-generated forms using metadata alone.
|
|
436
436
|
"""
|
|
437
437
|
with self.store.connect() as con:
|
|
438
|
-
|
|
438
|
+
filters = []
|
|
439
439
|
params: list[Any] = []
|
|
440
440
|
if snapshot is not None:
|
|
441
441
|
snap = _snapshot_row(con, snapshot)
|
|
442
442
|
if snap is None:
|
|
443
443
|
raise ValueError(f"snapshot not found: {snapshot}")
|
|
444
|
-
|
|
444
|
+
filters.append("mo.source_id IN (SELECT source_id FROM snapshot_layers WHERE snapshot_id=?)")
|
|
445
445
|
params.append(snap["id"])
|
|
446
|
+
if object_full_name:
|
|
447
|
+
filters.append("mo.full_name=?")
|
|
448
|
+
params.append(str(object_full_name))
|
|
449
|
+
source_filter = " AND " + " AND ".join(filters) if filters else ""
|
|
446
450
|
rows = [
|
|
447
451
|
dict(r)
|
|
448
452
|
for r in con.execute(
|
|
@@ -397,10 +397,12 @@ class ConfigurationSummaryBackend:
|
|
|
397
397
|
seen.add(obj["full_name"])
|
|
398
398
|
return {"query": object, "snapshot": snapshot, "backend": "configuration-summary", "summary": self._summary_info(info, meta), "matches": matches}
|
|
399
399
|
|
|
400
|
-
def lookup_form_element_types(self, snapshot: str | int | None = None, limit: int = 10_000) -> dict[str, Any]:
|
|
400
|
+
def lookup_form_element_types(self, snapshot: str | int | None = None, limit: int = 10_000, object_full_name: str | None = None) -> dict[str, Any]:
|
|
401
401
|
info = self.resolve_snapshot(snapshot)
|
|
402
402
|
if not info:
|
|
403
403
|
raise ValueError(f"summary snapshot not found: {snapshot}")
|
|
404
|
+
if object_full_name:
|
|
405
|
+
return self._lookup_object_form_element_types(info, snapshot, object_full_name, limit=limit)
|
|
404
406
|
hits, meta = self.search(str(info["config_code"]), self._snapshot_layer_names(info), "форма", limit=limit, kinds="form")
|
|
405
407
|
items = []
|
|
406
408
|
for hit in hits:
|
|
@@ -411,6 +413,89 @@ class ConfigurationSummaryBackend:
|
|
|
411
413
|
# what dynamic_list_metadata_available_fields uses for a concrete object.
|
|
412
414
|
return {"snapshot": snapshot, "backend": "configuration-summary", "summary": self._summary_info(info, meta), "count": len(items), "items": items}
|
|
413
415
|
|
|
416
|
+
def _lookup_object_form_element_types(self, info: dict[str, Any], snapshot: str | int | None, object_full_name: str, *, limit: int) -> dict[str, Any]:
|
|
417
|
+
metadata_type, metadata_name = _split_object_name(object_full_name)
|
|
418
|
+
query = " ".join(part for part in (metadata_type, metadata_name, "форма") if part)
|
|
419
|
+
hits, meta = self.search(str(info["config_code"]), self._snapshot_layer_names(info), query, limit=min(max(limit, 20), 120), kinds="form")
|
|
420
|
+
items = []
|
|
421
|
+
seen = set()
|
|
422
|
+
for hit in hits:
|
|
423
|
+
row = hit.metadata
|
|
424
|
+
row_type = str(row.get("metadataType") or "")
|
|
425
|
+
row_name = str(row.get("metadataName") or "")
|
|
426
|
+
if metadata_type and row_type and normalize_query(row_type) != normalize_query(metadata_type):
|
|
427
|
+
continue
|
|
428
|
+
if metadata_name and row_name and normalize_query(row_name) != normalize_query(metadata_name):
|
|
429
|
+
continue
|
|
430
|
+
for item in self._form_element_rows(row, self._info_for_hit_layer(info, row)):
|
|
431
|
+
key = (
|
|
432
|
+
item.get("object_full_name"),
|
|
433
|
+
item.get("form_name"),
|
|
434
|
+
item.get("name"),
|
|
435
|
+
item.get("data_path"),
|
|
436
|
+
item.get("command_name"),
|
|
437
|
+
)
|
|
438
|
+
if key not in seen:
|
|
439
|
+
items.append(item)
|
|
440
|
+
seen.add(key)
|
|
441
|
+
obj_lookup = self.lookup_object(object_full_name, snapshot)
|
|
442
|
+
for obj in obj_lookup.get("matches") or []:
|
|
443
|
+
if normalize_query(str(obj.get("full_name") or "")) != normalize_query(object_full_name):
|
|
444
|
+
continue
|
|
445
|
+
source_path = str(obj.get("source_path") or "")
|
|
446
|
+
object_kind = str(obj.get("kind") or metadata_type)
|
|
447
|
+
for attr in obj.get("attributes") or []:
|
|
448
|
+
name = str(attr.get("name") or "")
|
|
449
|
+
if not name:
|
|
450
|
+
continue
|
|
451
|
+
key = (object_full_name, "<auto>", name, name, None)
|
|
452
|
+
if key in seen:
|
|
453
|
+
continue
|
|
454
|
+
items.append({
|
|
455
|
+
"object_full_name": object_full_name,
|
|
456
|
+
"object_kind": object_kind,
|
|
457
|
+
"form_name": "<auto>",
|
|
458
|
+
"name": name,
|
|
459
|
+
"element_type": "AutoGeneratedField",
|
|
460
|
+
"title": attr.get("synonym") or name,
|
|
461
|
+
"data_path": name,
|
|
462
|
+
"command_name": None,
|
|
463
|
+
"parent_name": None,
|
|
464
|
+
"source_path": attr.get("source_path") or source_path,
|
|
465
|
+
"attribute_type": attr.get("type_name"),
|
|
466
|
+
"attribute_role": attr.get("role") or "attribute",
|
|
467
|
+
"dcs_field_type": None,
|
|
468
|
+
})
|
|
469
|
+
seen.add(key)
|
|
470
|
+
for table_part in obj.get("table_parts") or []:
|
|
471
|
+
table_name = str(table_part.get("name") or "")
|
|
472
|
+
for col in table_part.get("columns") or []:
|
|
473
|
+
col_name = str(col.get("name") or "")
|
|
474
|
+
if not table_name or not col_name:
|
|
475
|
+
continue
|
|
476
|
+
data_path = f"{table_name}.{col_name}"
|
|
477
|
+
key = (object_full_name, "<auto>", data_path, data_path, None)
|
|
478
|
+
if key in seen:
|
|
479
|
+
continue
|
|
480
|
+
items.append({
|
|
481
|
+
"object_full_name": object_full_name,
|
|
482
|
+
"object_kind": object_kind,
|
|
483
|
+
"form_name": "<auto>",
|
|
484
|
+
"name": data_path,
|
|
485
|
+
"element_type": "AutoGeneratedTablePartField",
|
|
486
|
+
"title": col.get("synonym") or col_name,
|
|
487
|
+
"data_path": data_path,
|
|
488
|
+
"command_name": None,
|
|
489
|
+
"parent_name": table_name,
|
|
490
|
+
"source_path": col.get("source_path") or source_path,
|
|
491
|
+
"attribute_type": col.get("type_name"),
|
|
492
|
+
"attribute_role": "table_part_column",
|
|
493
|
+
"dcs_field_type": None,
|
|
494
|
+
})
|
|
495
|
+
seen.add(key)
|
|
496
|
+
break
|
|
497
|
+
return {"snapshot": snapshot, "backend": "configuration-summary", "summary": self._summary_info(info, meta), "object_full_name": object_full_name, "count": len(items), "items": items}
|
|
498
|
+
|
|
414
499
|
|
|
415
500
|
def _snapshot_layer_names(self, info: dict[str, Any]) -> list[str]:
|
|
416
501
|
layers = [str(item.get("layer") or "") for item in info.get("summary_layers") or [] if str(item.get("layer") or "")]
|
|
@@ -1969,6 +1969,8 @@ def _walk_ui_tree(node: Any):
|
|
|
1969
1969
|
yield node
|
|
1970
1970
|
for child in node.get("children") or []:
|
|
1971
1971
|
yield from _walk_ui_tree(child)
|
|
1972
|
+
for child in node.get("objects") or []:
|
|
1973
|
+
yield from _walk_ui_tree(child)
|
|
1972
1974
|
|
|
1973
1975
|
|
|
1974
1976
|
def _walk_ui_snapshot(node: Any):
|
|
@@ -2108,7 +2110,8 @@ _UI_TREE_FIELD_PRESETS: dict[str, set[str] | None] = {
|
|
|
2108
2110
|
"diagnostic": {
|
|
2109
2111
|
"type", "name", "title", "form_name", "navigation_url", "visible", "available", "read_only", "enabled",
|
|
2110
2112
|
"main", "home", "current", "selected", "data_path", "current_page_name", "current_page", "pages",
|
|
2111
|
-
"hidden_children_count", "
|
|
2113
|
+
"hidden_children_count", "rag_type", "rag", "_tree_ms", "_nodes_serialized", "_nodes_visited",
|
|
2114
|
+
"metrics", "flat", "source",
|
|
2112
2115
|
},
|
|
2113
2116
|
"data": {
|
|
2114
2117
|
"type", "name", "title", "form_name", "navigation_url", "text", "value", "presentation", "data",
|
|
@@ -2133,6 +2136,19 @@ def _ui_tree_field_names(fields: str, profile: str) -> set[str] | None:
|
|
|
2133
2136
|
raise ValueError(f"Unsupported ui_tree fields: {fields!r}. Expected one of: {allowed}.")
|
|
2134
2137
|
|
|
2135
2138
|
|
|
2139
|
+
def _ui_tree_rag_enabled(value: bool | str | None, profile: str) -> bool:
|
|
2140
|
+
if isinstance(value, bool):
|
|
2141
|
+
return value
|
|
2142
|
+
raw = str(value if value is not None else "auto").strip().lower()
|
|
2143
|
+
if raw in {"", "auto", "default", "profile"}:
|
|
2144
|
+
return profile in _UI_TREE_RAG_AUTO_PROFILES
|
|
2145
|
+
if raw in {"1", "true", "yes", "on", "enable", "enabled"}:
|
|
2146
|
+
return True
|
|
2147
|
+
if raw in {"0", "false", "no", "off", "disable", "disabled", "none"}:
|
|
2148
|
+
return False
|
|
2149
|
+
raise ValueError("Unsupported ui_tree include_rag_types: " f"{value!r}. Expected bool or one of: auto, true, false.")
|
|
2150
|
+
|
|
2151
|
+
|
|
2136
2152
|
def _ui_tree_state_options(field_names: set[str] | None, *, include_hidden: bool) -> dict[str, bool]:
|
|
2137
2153
|
if field_names is None:
|
|
2138
2154
|
return {
|
|
@@ -2339,6 +2355,7 @@ _UI_TREE_PROFILES = {"navigation", "diagnostic", "data", "full", "command_panels
|
|
|
2339
2355
|
_UI_TREE_COMMAND_PANEL_MODES = {"auto", "include", "full", "with", "all", "exclude", "without", "none", "off", "no", "only"}
|
|
2340
2356
|
_UI_TREE_GROUP_MODES = {"include", "flatten", "flatten_layout", "exclude", "exclude_empty", "pages_only"}
|
|
2341
2357
|
_UI_TREE_FORMATS = {"json", "outline", "yaml", "yml"}
|
|
2358
|
+
_UI_TREE_RAG_AUTO_PROFILES = {"diagnostic", "data", "full"}
|
|
2342
2359
|
|
|
2343
2360
|
|
|
2344
2361
|
def _validate_ui_tree_choice(kind: str, value: str, allowed: set[str]) -> str:
|
|
@@ -3007,9 +3024,15 @@ def _convert_1c_guid_navigation_link(
|
|
|
3007
3024
|
}
|
|
3008
3025
|
|
|
3009
3026
|
|
|
3010
|
-
def _rag_form_element_index(snapshot: str | int | None = None) -> dict[str, list[dict[str, Any]]]:
|
|
3027
|
+
def _rag_form_element_index(snapshot: str | int | None = None, *, active_object: str = "") -> dict[str, list[dict[str, Any]]]:
|
|
3028
|
+
lookup_object = _metadata_name_to_rag_name(active_object) if active_object else ""
|
|
3011
3029
|
try:
|
|
3012
|
-
|
|
3030
|
+
try:
|
|
3031
|
+
result = _RAG.lookup_form_element_types(snapshot=snapshot, object_full_name=lookup_object or None)
|
|
3032
|
+
except TypeError:
|
|
3033
|
+
# Backward compatibility for tests or external RagService-like stubs.
|
|
3034
|
+
result = _RAG.lookup_form_element_types(snapshot=snapshot)
|
|
3035
|
+
items = result.get("items", [])
|
|
3013
3036
|
except Exception as exc:
|
|
3014
3037
|
logging.debug("RAG form element lookup failed: %s", exc)
|
|
3015
3038
|
return {}
|
|
@@ -3022,21 +3045,72 @@ def _rag_form_element_index(snapshot: str | int | None = None) -> dict[str, list
|
|
|
3022
3045
|
return index
|
|
3023
3046
|
|
|
3024
3047
|
|
|
3048
|
+
def _ui_tree_form_short_name(value: Any) -> str:
|
|
3049
|
+
text = str(value or "")
|
|
3050
|
+
marker = ".Форма."
|
|
3051
|
+
if marker in text:
|
|
3052
|
+
return text.rsplit(marker, 1)[1]
|
|
3053
|
+
marker = ".Form."
|
|
3054
|
+
if marker in text:
|
|
3055
|
+
return text.rsplit(marker, 1)[1]
|
|
3056
|
+
return text
|
|
3057
|
+
|
|
3058
|
+
|
|
3059
|
+
def _metadata_name_from_ui_tree(tree: dict[str, Any]) -> str:
|
|
3060
|
+
value = _metadata_name_from_navigation_url(str(tree.get("navigation_url") or ""))
|
|
3061
|
+
if value:
|
|
3062
|
+
return value
|
|
3063
|
+
diagnostics = tree.get("client_diagnostics")
|
|
3064
|
+
if isinstance(diagnostics, dict):
|
|
3065
|
+
form_name = str(diagnostics.get("active_form_name") or diagnostics.get("modal_form_name") or "")
|
|
3066
|
+
for marker in (".Форма.", ".Form."):
|
|
3067
|
+
if marker in form_name:
|
|
3068
|
+
return form_name.split(marker, 1)[0]
|
|
3069
|
+
for node in _walk_ui_tree(tree):
|
|
3070
|
+
form_name = str(node.get("form_name") or "")
|
|
3071
|
+
for marker in (".Форма.", ".Form."):
|
|
3072
|
+
if marker in form_name:
|
|
3073
|
+
return form_name.split(marker, 1)[0]
|
|
3074
|
+
return ""
|
|
3075
|
+
|
|
3076
|
+
|
|
3077
|
+
def _ui_node_rag_lookup_keys(node: dict[str, Any]) -> list[str]:
|
|
3078
|
+
values = [node.get("name"), node.get("title"), node.get("data_path")]
|
|
3079
|
+
keys: list[str] = []
|
|
3080
|
+
for value in values:
|
|
3081
|
+
text = str(value or "").strip()
|
|
3082
|
+
if not text:
|
|
3083
|
+
continue
|
|
3084
|
+
keys.append(_norm_key(text))
|
|
3085
|
+
if "." in text:
|
|
3086
|
+
keys.append(_norm_key(text.rsplit(".", 1)[1]))
|
|
3087
|
+
return list(dict.fromkeys(key for key in keys if key))
|
|
3088
|
+
|
|
3089
|
+
|
|
3025
3090
|
def _pick_rag_element(ui_node: dict[str, Any], candidates: list[dict[str, Any]], active_object: str = "") -> dict[str, Any] | None:
|
|
3026
3091
|
if not candidates:
|
|
3027
3092
|
return None
|
|
3028
|
-
|
|
3029
|
-
|
|
3093
|
+
active_names = {_norm_key(active_object), _norm_key(_metadata_name_to_rag_name(active_object))}
|
|
3094
|
+
active_names.discard("")
|
|
3095
|
+
form_name = _norm_key(_ui_tree_form_short_name(ui_node.get("form_name")))
|
|
3030
3096
|
|
|
3031
3097
|
def score(item: dict[str, Any]) -> int:
|
|
3032
3098
|
value = 0
|
|
3033
|
-
|
|
3099
|
+
item_object = _norm_key(item.get("object_full_name"))
|
|
3100
|
+
if active_names and item_object in active_names:
|
|
3034
3101
|
value += 20
|
|
3035
|
-
|
|
3102
|
+
item_form = _norm_key(_ui_tree_form_short_name(item.get("form_name")))
|
|
3103
|
+
if form_name and form_name == item_form:
|
|
3036
3104
|
value += 5
|
|
3037
|
-
|
|
3105
|
+
elif item_form == "<auto>":
|
|
3106
|
+
value += 1
|
|
3107
|
+
node_name = _norm_key(ui_node.get("name"))
|
|
3108
|
+
item_name = _norm_key(item.get("name"))
|
|
3109
|
+
item_data_path = _norm_key(item.get("data_path"))
|
|
3110
|
+
if node_name and (node_name == item_name or node_name == item_data_path):
|
|
3038
3111
|
value += 3
|
|
3039
|
-
|
|
3112
|
+
node_title = _norm_key(ui_node.get("title"))
|
|
3113
|
+
if node_title and node_title == _norm_key(item.get("title")):
|
|
3040
3114
|
value += 2
|
|
3041
3115
|
return value
|
|
3042
3116
|
|
|
@@ -3046,12 +3120,12 @@ def _pick_rag_element(ui_node: dict[str, Any], candidates: list[dict[str, Any]],
|
|
|
3046
3120
|
def _enrich_ui_tree_with_rag_types(tree: Any, snapshot: str | int | None = None) -> Any:
|
|
3047
3121
|
if not isinstance(tree, dict):
|
|
3048
3122
|
return tree
|
|
3049
|
-
|
|
3123
|
+
active_object = _metadata_name_from_ui_tree(tree)
|
|
3124
|
+
index = _rag_form_element_index(snapshot=snapshot, active_object=active_object)
|
|
3050
3125
|
if not index:
|
|
3051
3126
|
return tree
|
|
3052
|
-
active_object = _metadata_name_from_navigation_url(str(tree.get("navigation_url") or ""))
|
|
3053
3127
|
for node in _walk_ui_tree(tree):
|
|
3054
|
-
keys =
|
|
3128
|
+
keys = _ui_node_rag_lookup_keys(node)
|
|
3055
3129
|
candidates: list[dict[str, Any]] = []
|
|
3056
3130
|
seen: set[int] = set()
|
|
3057
3131
|
for key in keys:
|
|
@@ -6118,7 +6192,7 @@ async def decoration_click(name: Annotated[str, "Decoration technical name"] = "
|
|
|
6118
6192
|
|
|
6119
6193
|
@mcp.tool()
|
|
6120
6194
|
async def ui_tree(
|
|
6121
|
-
include_rag_types: Annotated[bool, "Enrich UI elements with best-effort types from the
|
|
6195
|
+
include_rag_types: Annotated[bool | str, "Enrich UI elements with best-effort types from the RAG index. Use true/false to force; auto/default enables it for diagnostic, data and full profiles when session RAG is enabled"] = "auto",
|
|
6122
6196
|
include_hidden: Annotated[bool, "Include invisible form elements/pages; visible/available/read_only flags are returned for serialized nodes"] = False,
|
|
6123
6197
|
compact: Annotated[bool | None, "Return compact node fields; default follows ONEC_MCP_RESULT_VERBOSITY"] = None,
|
|
6124
6198
|
include_data: Annotated[bool, "Include data presentations from UI nodes; expensive on large forms, prefer field_value_text/table_rows/tabular_document_text for values"] = False,
|
|
@@ -6143,6 +6217,8 @@ async def ui_tree(
|
|
|
6143
6217
|
|
|
6144
6218
|
Default profile is `navigation`: visible elements, compact fields, no data
|
|
6145
6219
|
presentations, no RAG enrichment, and command panels excluded unless requested.
|
|
6220
|
+
`include_rag_types` defaults to `auto`: RAG enrichment is enabled for
|
|
6221
|
+
`diagnostic`, `data` and `full` profiles and disabled for navigation profiles.
|
|
6146
6222
|
The tree has no depth limit. Use `include_hidden=true` for hidden pages/elements.
|
|
6147
6223
|
Pass `parent_name` to serialize only the subtree rooted at a specific form element.
|
|
6148
6224
|
Use `fields=minimal|navigation|diagnostic|data|full` (or a comma-separated
|
|
@@ -6181,6 +6257,7 @@ async def ui_tree(
|
|
|
6181
6257
|
command_mode = _validate_ui_tree_choice("command_panels", command_panels, _UI_TREE_COMMAND_PANEL_MODES)
|
|
6182
6258
|
group_mode_key = _validate_ui_tree_choice("group_mode", group_mode, _UI_TREE_GROUP_MODES)
|
|
6183
6259
|
fmt = _validate_ui_tree_choice("format", format, _UI_TREE_FORMATS)
|
|
6260
|
+
include_rag_types_effective = _ui_tree_rag_enabled(include_rag_types, profile_key)
|
|
6184
6261
|
field_names = _ui_tree_field_names(fields, profile_key)
|
|
6185
6262
|
state_options = _ui_tree_state_options(field_names, include_hidden=include_hidden)
|
|
6186
6263
|
if profile_key in {"full", "diagnostic", "data"}:
|
|
@@ -6236,13 +6313,14 @@ async def ui_tree(
|
|
|
6236
6313
|
if isinstance(tree, dict):
|
|
6237
6314
|
tree["tree_summary"] = _summarize_ui_tree(tree, include_hidden=include_hidden)
|
|
6238
6315
|
timed_stage("python_summary_ms", summary_start)
|
|
6239
|
-
if
|
|
6316
|
+
if include_rag_types_effective:
|
|
6240
6317
|
rag_start = time.monotonic()
|
|
6241
6318
|
sess = _session(session_id)
|
|
6242
6319
|
if _RAG_ENABLED and sess.rag_enabled:
|
|
6243
|
-
|
|
6320
|
+
effective_rag_snapshot = rag_snapshot or sess.rag_snapshot or str(sess.rag_notice.get("snapshot") or "") or None
|
|
6321
|
+
tree = _enrich_ui_tree_with_rag_types(tree, snapshot=effective_rag_snapshot)
|
|
6244
6322
|
if isinstance(tree, dict):
|
|
6245
|
-
tree["rag_diagnostic"] = _rag_diagnostic(session_id,
|
|
6323
|
+
tree["rag_diagnostic"] = _rag_diagnostic(session_id, effective_rag_snapshot or "")
|
|
6246
6324
|
elif isinstance(tree, dict):
|
|
6247
6325
|
tree["rag_diagnostic"] = _rag_diagnostic(session_id, rag_snapshot)
|
|
6248
6326
|
timed_stage("python_rag_enrich_ms", rag_start)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{answer42-0.4.75 → answer42-0.4.76}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{answer42-0.4.75 → answer42-0.4.76}/src/mcp_1c/assets/skills/answer42-universal-report/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|