From 66fb5b5607ec93f2b410ae61aa614260e0ff4250 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Sun, 5 Jul 2026 01:12:43 -0500 Subject: [PATCH 1/2] feat(ledger): support holon-jsonld report download format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit download_report_bundle now accepts format="holon-jsonld" — the dataset-form scene/boundary/projection holon the backend materializes on demand at reportDownloadUrl. Maps to the HOLON_JSONLD GraphQL enum name and defaults the on-disk extension to .holon.jsonld. --- robosystems_client/clients/ledger_client.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/robosystems_client/clients/ledger_client.py b/robosystems_client/clients/ledger_client.py index e065476..ffed5e7 100644 --- a/robosystems_client/clients/ledger_client.py +++ b/robosystems_client/clients/ledger_client.py @@ -327,7 +327,11 @@ # Map the wire flavor strings the facade accepts to the GraphQL # ``ReportDownloadFormat`` enum names used as query variables. -_DOWNLOAD_FORMAT_ALIASES = {"jsonld": "JSONLD", "xbrl-2.1": "XBRL_2_1"} +_DOWNLOAD_FORMAT_ALIASES = { + "jsonld": "JSONLD", + "holon-jsonld": "HOLON_JSONLD", + "xbrl-2.1": "XBRL_2_1", +} def _parse_filename(content_disposition: str) -> str | None: @@ -1824,9 +1828,11 @@ def download_report_bundle( Args: graph_id: Graph identifier owning the Report. report_id: Report identifier (``rpt_``-prefixed ULID). - format: Serialization flavor — ``"jsonld"`` (default) or - ``"xbrl-2.1"``. The enum names ``"JSONLD"`` / ``"XBRL_2_1"`` - are also accepted. + format: Serialization flavor — ``"jsonld"`` (default, the flat + canonical bundle), ``"holon-jsonld"`` (the dataset-form + scene/boundary/projection holon), or ``"xbrl-2.1"``. The enum + names ``"JSONLD"`` / ``"HOLON_JSONLD"`` / ``"XBRL_2_1"`` are + also accepted. to: Optional file path to write the bytes to. When set, the returned ``ReportBundleDownload.path`` points at the written file. @@ -1870,7 +1876,9 @@ def download_report_bundle( ) generation_count = info.get("generation_count") - default_ext = "zip" if gql_format == "XBRL_2_1" else "jsonld" + default_ext = {"XBRL_2_1": "zip", "HOLON_JSONLD": "holon.jsonld"}.get( + gql_format, "jsonld" + ) filename = ( _parse_filename(artifact.headers.get("content-disposition", "")) or f"{report_id}-g{generation_count or 1}.{default_ext}" From 3d546d8d9c5adb78df9bc1df056f4a63d9ab4317 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Sun, 5 Jul 2026 01:12:52 -0500 Subject: [PATCH 2/2] chore(sdk): regenerate from latest OpenAPI spec Pulls through MCPToolsResponse.instructions (per-graph MCP routing guidance) from the current backend schema. --- .../models/mcp_tools_response.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/robosystems_client/models/mcp_tools_response.py b/robosystems_client/models/mcp_tools_response.py index bd00e41..7d255c9 100644 --- a/robosystems_client/models/mcp_tools_response.py +++ b/robosystems_client/models/mcp_tools_response.py @@ -1,11 +1,13 @@ from __future__ import annotations from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..types import UNSET, Unset + if TYPE_CHECKING: from ..models.mcp_tools_response_tools_item import MCPToolsResponseToolsItem @@ -19,9 +21,13 @@ class MCPToolsResponse: Attributes: tools (list[MCPToolsResponseToolsItem]): List of available MCP tools with their schemas + instructions (None | str | Unset): Per-graph routing guidance for MCP clients, tailored to the graph's category + and live tool set. Clients should pass this to the MCP server's `instructions` handshake field so it is always + in the agent's context. """ tools: list[MCPToolsResponseToolsItem] + instructions: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -30,6 +36,12 @@ def to_dict(self) -> dict[str, Any]: tools_item = tools_item_data.to_dict() tools.append(tools_item) + instructions: None | str | Unset + if isinstance(self.instructions, Unset): + instructions = UNSET + else: + instructions = self.instructions + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -37,6 +49,8 @@ def to_dict(self) -> dict[str, Any]: "tools": tools, } ) + if instructions is not UNSET: + field_dict["instructions"] = instructions return field_dict @@ -52,8 +66,18 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: tools.append(tools_item) + def _parse_instructions(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + instructions = _parse_instructions(d.pop("instructions", UNSET)) + mcp_tools_response = cls( tools=tools, + instructions=instructions, ) mcp_tools_response.additional_properties = d