Source code for langgoap.viz.jupyter

"""Display-aware visualization helper for Jupyter environments.

The helper detects IPython at call time and returns an
``IPython.display.Markdown`` or ``IPython.display.Image`` instance when
available so notebooks can render rich output; otherwise it returns the
raw string.  Keeping the detection at call time (rather than at import
time) lets the core package stay a zero-weight dependency on IPython.
"""

from __future__ import annotations

import importlib.util
import logging
from typing import TYPE_CHECKING, Any, Literal

from langgoap.viz.ascii import render_ascii, render_ascii_gantt
from langgoap.viz.dot import render_dot
from langgoap.viz.mermaid import render_mermaid, render_mermaid_gantt

if TYPE_CHECKING:
    from langgoap.planner.types import Plan

logger = logging.getLogger(__name__)

VizFormat = Literal["auto", "mermaid", "dot", "ascii", "gantt", "ascii_gantt"]


def _ipython_available() -> bool:
    """Return True when IPython is importable in the current environment."""
    return importlib.util.find_spec("IPython") is not None


[docs] def visualize( plan: Plan, *, format: VizFormat = "auto", show_resources: bool = True, show_schedule: bool = True, ) -> str | Any: """Render a Plan for display. Return types: * ``format="ascii"`` or ``format="ascii_gantt"`` → always a ``str``. * ``format="mermaid"`` → ``IPython.display.Markdown`` if IPython is importable, otherwise a plain ``str``. * ``format="gantt"`` → same detection rules as ``"mermaid"``. * ``format="dot"`` → ``IPython.display.Image`` when IPython *and* the ``graphviz`` Python package with the ``dot`` binary are available. Otherwise returns the DOT source as ``str``. * ``format="auto"`` → ``"mermaid"`` when IPython is available, ``"ascii"`` otherwise. Args: plan: Plan to render. format: Output format selector. show_resources: Forwarded to the underlying renderer. show_schedule: Forwarded to the underlying renderer. Returns: Rendered output; exact type depends on ``format`` and the presence of IPython in the current environment. """ if format == "auto": format = "mermaid" if _ipython_available() else "ascii" if format == "ascii": return render_ascii( plan, show_resources=show_resources, show_schedule=show_schedule ) if format == "ascii_gantt": return render_ascii_gantt(plan) if format == "mermaid": return _wrap_mermaid( render_mermaid( plan, show_resources=show_resources, show_schedule=show_schedule ) ) if format == "gantt": return _wrap_mermaid(render_mermaid_gantt(plan)) if format == "dot": return _render_dot_display( render_dot(plan, show_resources=show_resources, show_schedule=show_schedule) ) raise ValueError(f"Unknown visualization format: {format!r}")
def _wrap_mermaid(source: str) -> str | Any: """Wrap Mermaid source in an IPython Markdown cell when available.""" if _ipython_available(): from IPython.display import Markdown return Markdown(f"```mermaid\n{source}\n```") return source def _render_dot_display(source: str) -> str | Any: """Render DOT source via graphviz+IPython, falling back to raw source.""" if not _ipython_available(): return source try: import graphviz except ImportError: logger.warning("graphviz Python package not available; returning DOT source.") return source try: png_bytes = graphviz.Source(source).pipe(format="png") except Exception as exc: # pragma: no cover \u2014 environment-dependent logger.warning("graphviz render failed (%s); returning DOT source.", exc) return source from IPython.display import Image return Image(png_bytes) def repr_mimebundle(plan: Plan, **_kwargs: Any) -> dict[str, Any]: """Build the Jupyter rich-display MIME bundle for ``plan``. Used by :meth:`langgoap.planner.types.Plan._repr_mimebundle_`. Renders the plan as an inline PNG via :func:`draw_mermaid_png`. """ from langgoap.viz.mermaid import draw_mermaid_png return { "text/plain": repr(plan), "image/png": draw_mermaid_png(plan), }