Skip to content

datarobot_genai.drmcputils.panels.store

store

Server-side panel store.

Persists panels through a :class:~datarobot_genai.drmcputils.files.store.BlobStore: each panel is a small JSON manifest blob (the panel metadata) tagged for discovery, plus an optional separate payload blob for bulky content (a Dataset's Parquet, a Chart's spec). The manifest blob's id is the panel id, so no separate id allocation is needed and ids are globally unique.

The store depends only on the BlobStore Protocol, so it is backed by the DataRobot Files API in production and by an in-memory fake in tests.

PanelStore

CRUD + listing for panels over a :class:BlobStore.

Source code in datarobot_genai/drmcputils/panels/store.py
class PanelStore:
    """CRUD + listing for panels over a :class:`BlobStore`."""

    def __init__(self, blob_store: BlobStore) -> None:
        self._blobs = blob_store

    async def create(
        self,
        panel: BasePanel,
        *,
        source: str = DEFAULT_SOURCE,
        payload: bytes | None = None,
        payload_name: str | None = None,
        content_type: str | None = None,
    ) -> Panel:
        """Persist ``panel`` (and an optional ``payload`` blob); returns it with ``id`` set."""
        panel.updated_at = _now_iso()
        if payload is not None:
            payload_ref = await self._blobs.put(
                payload,
                name=payload_name or f"{panel.type.value}-payload",
                content_type=content_type,
                tags=[_PAYLOAD_TAG, _source_tag(source)],
            )
            panel.payload_files_id = payload_ref.files_id
            panel.payload_name = payload_ref.name

        manifest = json.dumps(panel.model_dump(mode="json", exclude={"id"})).encode("utf-8")
        try:
            manifest_ref = await self._blobs.put(
                manifest,
                name=f"panel-{panel.type.value}.json",
                content_type="application/json",
                tags=[_MANIFEST_TAG, _source_tag(source), f"dr_panel_type:{panel.type.value}"],
            )
        except Exception:
            # The panel was not created; don't leave the payload blob orphaned.
            if panel.payload_files_id:
                try:
                    await self._blobs.delete(panel.payload_files_id)
                except Exception:  # noqa: BLE001 - cleanup is best-effort; surface the original error
                    logger.warning(
                        "Failed to clean up payload blob %s after manifest write failed",
                        panel.payload_files_id,
                    )
                panel.payload_files_id = None
                panel.payload_name = None
            raise
        panel.id = manifest_ref.files_id
        return panel  # type: ignore[return-value]

    async def get(self, panel_id: str) -> Panel:
        """Load a panel by id (its manifest blob id). Payload is not hydrated here."""
        raw = await self._blobs.get(panel_id)
        manifest = json.loads(raw.decode("utf-8"))
        panel = panel_from_manifest(manifest)
        panel.id = panel_id
        return panel

    async def list(
        self,
        *,
        source: str = DEFAULT_SOURCE,
        limit: int = DEFAULT_LIST_LIMIT,
        offset: int = 0,
    ) -> list[Panel]:
        """List panels in ``source`` (metadata only); page with ``limit``/``offset``."""
        refs = await self._blobs.list(
            tags=[_MANIFEST_TAG, _source_tag(source)], limit=limit, offset=offset
        )
        panels: list[Panel] = []
        for ref in refs:
            raw = await self._blobs.get(ref.files_id)
            try:
                panel = panel_from_manifest(json.loads(raw.decode("utf-8")))
            except (json.JSONDecodeError, UnicodeDecodeError, ValueError, KeyError) as exc:
                # One corrupt manifest must not break listing the healthy ones.
                logger.warning("Skipping unreadable panel manifest %s: %s", ref.files_id, exc)
                continue
            panel.id = ref.files_id
            panels.append(panel)
        return panels

    async def get_payload(self, panel: Panel | str) -> bytes | None:
        """Fetch a panel's payload blob bytes (by id or loaded panel); None if it has none."""
        if isinstance(panel, str):
            panel = await self.get(panel)
        if not panel.payload_files_id:
            return None
        return await self._blobs.get(panel.payload_files_id)

    async def delete(self, panel_id: str) -> None:
        """Delete a panel's manifest and its payload blob (if any).

        Transient fetch errors propagate (so the caller can retry without
        orphaning the payload); only an unreadable/corrupt manifest falls back
        to deleting the manifest alone, since the payload id is unknowable.
        """
        payload_files_id: str | None = None
        try:
            payload_files_id = (await self.get(panel_id)).payload_files_id
        except (json.JSONDecodeError, UnicodeDecodeError, ValueError, KeyError) as exc:
            logger.warning(
                "Panel %s manifest is unreadable (%s); deleting the manifest only", panel_id, exc
            )
        if payload_files_id:
            await self._blobs.delete(payload_files_id)
        await self._blobs.delete(panel_id)

create async

create(panel: BasePanel, *, source: str = DEFAULT_SOURCE, payload: bytes | None = None, payload_name: str | None = None, content_type: str | None = None) -> Panel

Persist panel (and an optional payload blob); returns it with id set.

Source code in datarobot_genai/drmcputils/panels/store.py
async def create(
    self,
    panel: BasePanel,
    *,
    source: str = DEFAULT_SOURCE,
    payload: bytes | None = None,
    payload_name: str | None = None,
    content_type: str | None = None,
) -> Panel:
    """Persist ``panel`` (and an optional ``payload`` blob); returns it with ``id`` set."""
    panel.updated_at = _now_iso()
    if payload is not None:
        payload_ref = await self._blobs.put(
            payload,
            name=payload_name or f"{panel.type.value}-payload",
            content_type=content_type,
            tags=[_PAYLOAD_TAG, _source_tag(source)],
        )
        panel.payload_files_id = payload_ref.files_id
        panel.payload_name = payload_ref.name

    manifest = json.dumps(panel.model_dump(mode="json", exclude={"id"})).encode("utf-8")
    try:
        manifest_ref = await self._blobs.put(
            manifest,
            name=f"panel-{panel.type.value}.json",
            content_type="application/json",
            tags=[_MANIFEST_TAG, _source_tag(source), f"dr_panel_type:{panel.type.value}"],
        )
    except Exception:
        # The panel was not created; don't leave the payload blob orphaned.
        if panel.payload_files_id:
            try:
                await self._blobs.delete(panel.payload_files_id)
            except Exception:  # noqa: BLE001 - cleanup is best-effort; surface the original error
                logger.warning(
                    "Failed to clean up payload blob %s after manifest write failed",
                    panel.payload_files_id,
                )
            panel.payload_files_id = None
            panel.payload_name = None
        raise
    panel.id = manifest_ref.files_id
    return panel  # type: ignore[return-value]

get async

get(panel_id: str) -> Panel

Load a panel by id (its manifest blob id). Payload is not hydrated here.

Source code in datarobot_genai/drmcputils/panels/store.py
async def get(self, panel_id: str) -> Panel:
    """Load a panel by id (its manifest blob id). Payload is not hydrated here."""
    raw = await self._blobs.get(panel_id)
    manifest = json.loads(raw.decode("utf-8"))
    panel = panel_from_manifest(manifest)
    panel.id = panel_id
    return panel

list async

list(*, source: str = DEFAULT_SOURCE, limit: int = DEFAULT_LIST_LIMIT, offset: int = 0) -> list[Panel]

List panels in source (metadata only); page with limit/offset.

Source code in datarobot_genai/drmcputils/panels/store.py
async def list(
    self,
    *,
    source: str = DEFAULT_SOURCE,
    limit: int = DEFAULT_LIST_LIMIT,
    offset: int = 0,
) -> list[Panel]:
    """List panels in ``source`` (metadata only); page with ``limit``/``offset``."""
    refs = await self._blobs.list(
        tags=[_MANIFEST_TAG, _source_tag(source)], limit=limit, offset=offset
    )
    panels: list[Panel] = []
    for ref in refs:
        raw = await self._blobs.get(ref.files_id)
        try:
            panel = panel_from_manifest(json.loads(raw.decode("utf-8")))
        except (json.JSONDecodeError, UnicodeDecodeError, ValueError, KeyError) as exc:
            # One corrupt manifest must not break listing the healthy ones.
            logger.warning("Skipping unreadable panel manifest %s: %s", ref.files_id, exc)
            continue
        panel.id = ref.files_id
        panels.append(panel)
    return panels

get_payload async

get_payload(panel: Panel | str) -> bytes | None

Fetch a panel's payload blob bytes (by id or loaded panel); None if it has none.

Source code in datarobot_genai/drmcputils/panels/store.py
async def get_payload(self, panel: Panel | str) -> bytes | None:
    """Fetch a panel's payload blob bytes (by id or loaded panel); None if it has none."""
    if isinstance(panel, str):
        panel = await self.get(panel)
    if not panel.payload_files_id:
        return None
    return await self._blobs.get(panel.payload_files_id)

delete async

delete(panel_id: str) -> None

Delete a panel's manifest and its payload blob (if any).

Transient fetch errors propagate (so the caller can retry without orphaning the payload); only an unreadable/corrupt manifest falls back to deleting the manifest alone, since the payload id is unknowable.

Source code in datarobot_genai/drmcputils/panels/store.py
async def delete(self, panel_id: str) -> None:
    """Delete a panel's manifest and its payload blob (if any).

    Transient fetch errors propagate (so the caller can retry without
    orphaning the payload); only an unreadable/corrupt manifest falls back
    to deleting the manifest alone, since the payload id is unknowable.
    """
    payload_files_id: str | None = None
    try:
        payload_files_id = (await self.get(panel_id)).payload_files_id
    except (json.JSONDecodeError, UnicodeDecodeError, ValueError, KeyError) as exc:
        logger.warning(
            "Panel %s manifest is unreadable (%s); deleting the manifest only", panel_id, exc
        )
    if payload_files_id:
        await self._blobs.delete(payload_files_id)
    await self._blobs.delete(panel_id)