JIT Rendering
When a widget needs custom logic, the assistant writes sandboxed code that streams results into the UI.
Some interfaces need computation the A2UI vocabulary can't express — a custom calculation, a live-updating value, a transformation of fetched data. For those, MeghaOS uses Just-in-Time (JIT) code: the LLM writes Python, the agent runs it in a sandbox, and the result is bound into the rendered UI.
How it works
compose_ui returns A2UI JSON. When custom logic is required, the LLM embeds a __jit__
key holding Python code inside the same response — no separate code-generation call.
The sandbox
the sandbox runs generated code with a restricted sandbox as the primary path —
in-process, blocking filesystem and network access at the AST level. A subprocess +
sandbox-exec fallback handles code that needs numpy/pandas.
Key behaviors:
- Compile cache keyed on an MD5 of the code — repeated code skips
compile_restricted. - Exec-namespace injection —
execute(code, params=None)injects parameters as plain variables in the exec namespace. Legacy{param}string templates are auto-detected for backward compatibility. - Output is collected via a restricted sandbox's the output collector and retrieved after exec.
Allowed imports
Generated code may only import a small, vetted allowlist — enough for real computation, but no filesystem or process access:
json · math · datetime · re · httpx · requests · urllib ·
random · time · pandas · numpyAnything outside this set is rejected, which is what keeps in-process execution safe. (Code
that needs pandas/numpy may take the subprocess + sandbox-exec path.)
This is the opposite of the code_sandbox plugin's RUN_PYTHON,
which runs unrestricted on the host. JIT is the locked-down path used for UI logic;
RUN_PYTHON is the deliberate, trusted escape hatch.
A subtle bug once silently disabled Tier-1 JIT: a restricted sandbox 7.x removed safe_iter,
which the old code imported. The fix removed safe_iter and uses the output collector
class as _print_, retrieving output via glb['_print'] after exec(bc, glb).
Binding output to the UI
A UI component can declare "jit_bind": "key". After the code runs and returns a dict, the
value at that key is bound into the component deterministically — no guessing which output
maps to which widget.
{ "type": "animated_value", "jit_bind": "total", "prefix": "$" }Streaming (live widgets)
For widgets that update continuously, the streaming engine runs a loop that re-executes the code on an interval and pushes updates to the shell:
- Drift-free timer —
tick_start = time.monotonic; each cycle sleepsmax(0, interval - elapsed)so ticks don't drift. - JSON diffing —
jsonpatch.make_patchcomputes the delta each tick and sends only apatchfield, minimizing payload size. - Updates are broadcast straight to the shell via the Unix socket (no ZeroMQ).
- Max 10 concurrent streams, guarded in
start_stream.
A streaming app is auto-detected when its code references the injected elapsed_time
value (seconds since the stream started), letting the code animate or recompute against a
live clock each tick.
Reactive actions
When the user interacts with a JIT widget (e.g. changes a parameter),
handle_jit_action performs reactive binding: it re-substitutes dynamic values into
the original initial_ui template via _substitute_dynamic_values — zero additional LLM
calls. Fast, deterministic, cheap.
Semantic code cache
Generated code is cached semantically in a vector database (JITCodeCache in
). store_code / find_code(threshold=0.90) use text
embeddings so a similar query reuses prior code instead of regenerating — replacing the
old exact-match dictionary cache.
Relevant files
| File | Role |
|---|---|
| Session bookkeeping for JIT widgets | |
| Streaming loop, diffing, fallback UI builder | |
| Parameter handling | |
_execute_jit_code, compose_ui, handle_jit_action |