Skip to content

viz

InferenceVisualizer

Web-based visualization server for SMC inference results.

This class is intended to be used in conjunction with the InferenceEngine class.

Example
from genlm.control import InferenceVisualizer
# create the visualizer
viz = InferenceVisualizer()
# run inference and save the record to a JSON file
sequences = await token_sampler.smc(
    n_particles=10,
    max_tokens=20,
    ess_threshold=0.5,
    json_path="smc_record.json" # save the record to a JSON file
)
# visualize the inference run
viz.visualize("smc_record.json")
# clean up visualization server
viz.shutdown_server()
Source code in genlm/control/viz.py
class InferenceVisualizer:
    """Web-based visualization server for SMC inference results.

    This class is intended to be used in conjunction with the `InferenceEngine` class.

    Example:
        ```python
        from genlm.control import InferenceVisualizer
        # create the visualizer
        viz = InferenceVisualizer()
        # run inference and save the record to a JSON file
        sequences = await token_sampler.smc(
            n_particles=10,
            max_tokens=20,
            ess_threshold=0.5,
            json_path="smc_record.json" # save the record to a JSON file
        )
        # visualize the inference run
        viz.visualize("smc_record.json")
        # clean up visualization server
        viz.shutdown_server()
        ```
    """

    def __init__(self, port=8000, serve_dir=None):
        """Initialize the visualization server.

        Args:
            port (int): Port to run the server on.
            serve_dir (str | Path, optional): Directory to serve files from.
                If None, creates a temporary directory.

        Raises:
            OSError: If the port is already in use
        """
        self._server = None
        self._server_thread = None
        self._port = port
        self._html_dir = Path(__file__).parent / "html"

        # Set up serve directory
        if serve_dir is None:
            self._serve_dir = Path(tempfile.mkdtemp(prefix="smc_viz_"))
            self._using_temp_dir = True
        else:
            self._serve_dir = Path(serve_dir).resolve()
            self._using_temp_dir = False
            self._serve_dir.mkdir(exist_ok=True)

        # Create handler that serves from both directories
        class Handler(http.server.SimpleHTTPRequestHandler):
            def translate_path(self_, path):
                # Remove query parameters for file lookup
                clean_path = path.split("?")[0]
                # HTML files come from package
                if clean_path.endswith(".html"):
                    return str(self._html_dir / clean_path.lstrip("/"))
                # JSON files come from serve directory
                return str(self._serve_dir / clean_path.lstrip("/"))

        self._start_server(Handler)

    def visualize(self, json_path, auto_open=False):
        """Visualize the inference run in a browser.

        Args:
            json_path (str | Path): Path to the JSON file to visualize. If the file is not
                in the serve directory, it will be copied there. For efficiency, you can
                write JSON files directly to the serve directory
            auto_open (bool): Whether to automatically open in browser

        Returns:
            (str): URL where visualization can be accessed
        """
        if self._server is None:
            raise RuntimeError("Server is not running")

        json_path = Path(json_path)
        if not json_path.exists():
            raise FileNotFoundError(f"JSON file not found: {json_path}")

        # If file isn't in serve directory, copy it there
        dest_path = self._serve_dir / json_path.name
        if json_path.resolve() != dest_path.resolve():
            shutil.copy2(json_path, dest_path)

        url = f"http://localhost:{self._port}/smc.html?path={json_path.name}"

        if auto_open:
            webbrowser.open(url)

        return url

    def _start_server(self, handler_class):
        """Start the HTTP server."""
        try:
            self._server = socketserver.TCPServer(
                ("", self._port), handler_class, bind_and_activate=False
            )
            self._server.allow_reuse_address = True
            self._server.server_bind()
            self._server.server_activate()
        except OSError as e:
            if e.errno == 48 or e.errno == 98:  # Address already in use
                raise OSError(f"Port {self._port} is already in use") from None
            raise

        self._server_thread = threading.Thread(target=self._server.serve_forever)
        self._server_thread.daemon = True
        self._server_thread.start()

    def shutdown_server(self):
        """Shut down the visualization server."""
        if self._server is not None:
            if self._server_thread is not None and self._server_thread.is_alive():
                self._server.shutdown()
                self._server_thread.join()
            self._server.server_close()
            self._server = None
            self._server_thread = None

        # Clean up any temporary files
        if self._using_temp_dir and self._serve_dir.exists():
            shutil.rmtree(self._serve_dir)

    def __del__(self):
        """Ensure server is shut down when object is deleted."""
        self.shutdown_server()

__init__(port=8000, serve_dir=None)

Initialize the visualization server.

Parameters:

Name Type Description Default
port int

Port to run the server on.

8000
serve_dir str | Path

Directory to serve files from. If None, creates a temporary directory.

None

Raises:

Type Description
OSError

If the port is already in use

Source code in genlm/control/viz.py
def __init__(self, port=8000, serve_dir=None):
    """Initialize the visualization server.

    Args:
        port (int): Port to run the server on.
        serve_dir (str | Path, optional): Directory to serve files from.
            If None, creates a temporary directory.

    Raises:
        OSError: If the port is already in use
    """
    self._server = None
    self._server_thread = None
    self._port = port
    self._html_dir = Path(__file__).parent / "html"

    # Set up serve directory
    if serve_dir is None:
        self._serve_dir = Path(tempfile.mkdtemp(prefix="smc_viz_"))
        self._using_temp_dir = True
    else:
        self._serve_dir = Path(serve_dir).resolve()
        self._using_temp_dir = False
        self._serve_dir.mkdir(exist_ok=True)

    # Create handler that serves from both directories
    class Handler(http.server.SimpleHTTPRequestHandler):
        def translate_path(self_, path):
            # Remove query parameters for file lookup
            clean_path = path.split("?")[0]
            # HTML files come from package
            if clean_path.endswith(".html"):
                return str(self._html_dir / clean_path.lstrip("/"))
            # JSON files come from serve directory
            return str(self._serve_dir / clean_path.lstrip("/"))

    self._start_server(Handler)

visualize(json_path, auto_open=False)

Visualize the inference run in a browser.

Parameters:

Name Type Description Default
json_path str | Path

Path to the JSON file to visualize. If the file is not in the serve directory, it will be copied there. For efficiency, you can write JSON files directly to the serve directory

required
auto_open bool

Whether to automatically open in browser

False

Returns:

Type Description
str

URL where visualization can be accessed

Source code in genlm/control/viz.py
def visualize(self, json_path, auto_open=False):
    """Visualize the inference run in a browser.

    Args:
        json_path (str | Path): Path to the JSON file to visualize. If the file is not
            in the serve directory, it will be copied there. For efficiency, you can
            write JSON files directly to the serve directory
        auto_open (bool): Whether to automatically open in browser

    Returns:
        (str): URL where visualization can be accessed
    """
    if self._server is None:
        raise RuntimeError("Server is not running")

    json_path = Path(json_path)
    if not json_path.exists():
        raise FileNotFoundError(f"JSON file not found: {json_path}")

    # If file isn't in serve directory, copy it there
    dest_path = self._serve_dir / json_path.name
    if json_path.resolve() != dest_path.resolve():
        shutil.copy2(json_path, dest_path)

    url = f"http://localhost:{self._port}/smc.html?path={json_path.name}"

    if auto_open:
        webbrowser.open(url)

    return url

shutdown_server()

Shut down the visualization server.

Source code in genlm/control/viz.py
def shutdown_server(self):
    """Shut down the visualization server."""
    if self._server is not None:
        if self._server_thread is not None and self._server_thread.is_alive():
            self._server.shutdown()
            self._server_thread.join()
        self._server.server_close()
        self._server = None
        self._server_thread = None

    # Clean up any temporary files
    if self._using_temp_dir and self._serve_dir.exists():
        shutil.rmtree(self._serve_dir)

__del__()

Ensure server is shut down when object is deleted.

Source code in genlm/control/viz.py
def __del__(self):
    """Ensure server is shut down when object is deleted."""
    self.shutdown_server()