Adding a simulator
A simulator is a self-contained folder of data: an adapter, a Julia environment template, and skills. Nothing else changes, because the registry discovers the folder on its own and the agent code reads it generically.
<name>/
__init__.py # exports the adapter
adapter.py # SimulatorAdapter instance
julia_env/ # Project.toml + JutulAgent<Sim>/ warm package
skills/ # one folder per skill, each with a SKILL.md
The folder goes in one of two places, depending on who it is for:
- In-tree, under
src/jutul_agent/simulators/<name>/in a checkout of this repo, to contribute a simulator to jutul-agent itself. The rest of this page follows this route. - In your own package, to add a simulator for your own use without forking. The folder is laid out the same way; you publish its adapter under an entry point so an install discovers it. See building your application.
Either way the registry finds the adapter automatically, so there is no list to edit and no core code to change.
1. The adapter
adapter.py declares everything the harness needs to know:
from pathlib import Path
from jutul_agent.simulators.base import SimulatorAdapter
MYSIM = SimulatorAdapter(
name="mysim",
display_name="MySim",
module_dir=Path(__file__).resolve().parent,
package_imports=("Jutul", "MySim"),
primary_package="MySim",
domain_hints="What the simulator is for, in one or two sentences.",
warm_package="JutulAgentMySim",
example_prompts=("Run a small case and show me the result.", ...),
)
module_dir anchors the convention: the base class derives
julia_env_template_path and skills_dir from it. package_imports is what
the agent is told it can using, and primary_package is what doctor
checks is actually resolved in the env. example_prompts are starter tasks the
web UI offers on its welcome screen (optional; lead with a simulate-and-plot
one). Adapters can also contribute simulator subagents through
subagent_factories.
That is all the registration needed: dropping the folder in place is enough for the registry to find it.
2. The Julia environment template
julia_env/Project.toml lists the packages a workspace gets. Keep compat
loose and do not commit a Manifest.toml, so that the env resolves when
the workspace is instantiated. Declare the shared JutulAgent package as a
relative [sources] path dependency (copy the entry from an existing
simulator). Its single source lives in src/jutul_agent/julia_runtime/ and
is synced into the env at bootstrap.
The kernel itself needs no dependency in the env. Its Julia server is standard-library only.
3. The warm package
julia_env/JutulAgentMySim/ is a small Julia package whose only job is
precompilation. Start from an existing simulator's and adapt the workload:
@recompile_invalidationsaround the imports, so the simulator and the plotting stack are compiled together.- A
@compile_workloadthat runs the smallest representative solve and a plot save.
This is what makes the difference between a first solve in seconds and one in
minutes. Set the adapter's warm_package to its name, and it is loaded in
the background at session start.
4. Skills
Add at least skills/<name>-overview/SKILL.md: what the package does, the
canonical entry points, the standard workflow, where the examples live. Write
for a model that reads the package source at its real pkgdir path and has a
live REPL. Point at things to read and probe rather than duplicating the
documentation. See improving the agent for how
skills are surfaced and when to use a skill versus the system prompt.
Frontmatter is YAML and must parse (quote a description: that contains a
colon). A repo test checks every skill, and a malformed one is skipped at
runtime with only a warning.
5. CI
Add the simulator to the simulators.yml matrix. It instantiates the env
template and runs the simulator's integration smoke on PRs and weekly, which
catches upstream releases that break the template.
Trying it
mkdir try-mysim && cd try-mysim
uv run jutul-agent init --sim mysim
uv run jutul-agent web
To develop against a local checkout of the simulator package:
uv run jutul-agent init --sim mysim --source-path /path/to/MySim.jl
The checkout resolves at its real path (writable, since it is a dev checkout rather than a shared-depot install), so the agent can read and edit the package source itself.