Source code for modelly.studio.app
"""The Studio page: the layout of the panels, and ``Page``, which the server shows.
``StudioApp(session)`` is the whole app for one session, which the tests render headless.
``Page`` is what ``modelly studio`` serves: it reads the ``StudioConfig`` from the
environment, loads the model class once per server, and gives each browser tab a session.
"""
from __future__ import annotations
import functools
import solara
from modelly.studio.config import StudioConfig
from modelly.studio.loader import load_model_class, load_portrayal
from modelly.studio.panels import ExportPanel, ParameterPanel, RunControls
from modelly.studio.session import StudioSession
from modelly.studio.state import use_studio_state
from modelly.studio.tools import LintPanel, LogPanel, ProfilerPanel
from modelly.studio.views import AgentTable, Scrubber, SeriesView, SpaceView
TITLE = "Modelly Studio"
@solara.component
def StudioApp(session: StudioSession):
"""The Studio layout for one session: sidebar controls, views and tool tabs."""
state = use_studio_state(session)
solara.use_effect(state.drawn.set, [state.revision.value])
solara.Title(f"{TITLE}: {session.model_class.__name__}")
with solara.AppBar():
solara.AppBarTitle(f"{TITLE} · {session.model_class.__name__}")
with solara.Sidebar(), solara.Column():
RunControls(state)
ParameterPanel(state)
ExportPanel(state)
tab = solara.use_reactive(0)
with solara.Column():
Scrubber(state)
with solara.v.Tabs(v_model=tab.value, on_v_model=tab.set):
for name, _ in TABS:
solara.v.Tab(children=[name])
TABS[tab.value][1](state)
def ModelTab(state):
"""The space view and the reporters plot, side by side."""
with solara.Columns([1, 1]):
SpaceView(state)
SeriesView(state)
TABS = (
("Model", ModelTab),
("Agents", AgentTable),
("Profiler", ProfilerPanel),
("Lint", LintPanel),
("Log", LogPanel),
)
@functools.cache
def _loaded(config_json: str):
"""The config, model class and portrayal, loaded once per server."""
config = StudioConfig.from_json(config_json)
portrayal = load_portrayal(config.portrayal) if config.portrayal else None
return config, load_model_class(config.target), portrayal
[docs]
def new_session(config: StudioConfig) -> StudioSession:
"""A session for one browser tab."""
config, model_class, portrayal = _loaded(config.to_json())
return StudioSession(model_class, config, portrayal)
@solara.component
def Page():
"""The page ``modelly studio`` serves: one session per browser tab."""
config = StudioConfig.from_environment()
session = solara.use_memo(lambda: new_session(config), [])
StudioApp(session)