Skip to content

Conversation

@thoriqakbar0
Copy link
Contributor

@thoriqakbar0 thoriqakbar0 commented Jan 7, 2026

Summary

This PR introduces a SupportsPersistence Protocol that defines how environments can support multi-turn sessions. LocalREPL is provided as the reference implementation.

The Problem

Every completion() call spawns a fresh environment that gets destroyed afterward. Variables, computed results, loaded contexts — all gone.

The Solution

A SupportsPersistence Protocol that any environment can implement:

@runtime_checkable
class SupportsPersistence(Protocol):
    def add_context(self, payload, index=None) -> int
    def get_context_count(self) -> int
    def add_history(self, messages, index=None) -> int
    def get_history_count(self) -> int
    def update_handler_address(self, address) -> None

Usage

with RLM(backend="openai", persistent=True) as rlm:
    result1 = rlm.completion("Document 1...", root_prompt="Summarize")
    # context_0 = Document 1, variables computed
    
    result2 = rlm.completion("Document 2...", root_prompt="Compare with previous")
    # context_1 = Document 2, can still access context_0 and prior variables

What's Included

  • SupportsPersistence Protocol with comprehensive documentation
  • LocalREPL implementation as reference
  • Versioned contexts: context_0, context_1, ... (with context aliasing context_0)
  • Versioned histories: history_0, history_1, ... (with history aliasing history_0)
  • Runtime capability checking: isinstance(env, SupportsPersistence)
  • Clear error messages: Other envs raise NotImplementedError with guidance

For Other Environments (Docker, Modal, , etc.)

The Protocol docstrings explain exactly how to implement persistence. See:

  • rlm/environments/base_env.py — Protocol definition
  • rlm/environments/local_repl.py — Reference implementation
  • tests/test_local_repl_persistent.py — Expected behavior

Tests

  • tests/test_local_repl.py — Non-persistent behavior (env resets between calls)
  • tests/test_local_repl_persistent.py — Persistence unit tests
  • tests/test_multi_turn_integration.py — End-to-end multi-turn tests

Not Included

  • Docker/Modal persistence (they have the infrastructure, just need Protocol methods)
  • Async acompletion() support

Add persistent=True option to RLM that reuses the environment across
completion() calls instead of creating/destroying for each call. This
enables multi-turn conversations where variables and contexts persist.

- Add persistent parameter to RLM constructor
- Reuse environment when persistent=True, store as _persistent_env
- Add close() method and context manager support (__enter__/__exit__)
- Environment cleanup only on explicit close() when persistent
…sions

Add context_count parameter to build_user_prompt() so the model knows
when multiple contexts are available (context_0, context_1, etc.) during
persistent REPL sessions.
Add validation to prevent AttributeError when persistent=True is used
with environments that don't implement the required methods
(update_handler_address, add_context, get_context_count).

- Add _validate_persistent_environment_support() called at init time
- Add _env_supports_persistence() for runtime capability checking
- Add defensive runtime check before calling persistence methods
- Raise clear ValueError if unsupported environment is configured
Store conversation histories as versioned variables (history_0, history_1, etc.)
in the REPL environment, making them accessible for subsequent queries. This
enables models to reference prior conversation context in persistent sessions.
@thoriqakbar0 thoriqakbar0 changed the title proposal(local-real): persistent REPL mode for multi-turn conversations proposal(local-repl): persistent REPL mode for multi-turn conversations Jan 7, 2026
@alexzhang13 alexzhang13 self-requested a review January 7, 2026 19:45
@alexzhang13
Copy link
Owner

@thoriqakbar0 Can you separate out one test file for testing local repl without persistence (and add an explicit test to show that it's not persistent, so you do 2 fake RLM calls so the env gets reset, but it tries to use a variable that existed before) and one for local_repl_with_persistence to check that persistence is working?

Thanks!

@alexzhang13
Copy link
Owner

Also one more thing, let's update the abstract class to support "persistent: bool". For Docker, Modal, etc. add the flag, but if the user turns it on, throw an error that says "Persistent REPLs are currently not supported for environment: {env}".

Copy link
Owner

@alexzhang13 alexzhang13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments above:

  1. Update test_local_repl.py to be no persistence, then add a new test file with persistent test cases.
  2. Update base env class, and add persistent flag to all. But for now in Docker, etc. throw an error and say not supported currently.

@thoriqakbar0
Copy link
Contributor Author

on itt

Add persistent parameter to BaseEnv, IsolatedEnv, and NonIsolatedEnv.
DockerREPL, ModalREPL, and PrimeREPL raise NotImplementedError when
persistent=True since they don't yet support it. LocalREPL passes
the parameter through to the base class.

Also makes DockerREPL cleanup more defensive with hasattr checks.
…ulation tests

Replace TestLocalREPLMultiContext and TestLocalREPLHistory with
TestLocalREPLSimulatingRLMNoPersistence. New tests verify that
environments reset between RLM completions when persistent=False.
Add TestLocalREPLMultiContext, TestLocalREPLHistory, and
TestLocalREPLPersistentState test classes for LocalREPL's
persistent mode features including multi-context versioning
and message history storage.
Add comprehensive integration tests for persistent RLM sessions:
- Environment reuse across completion calls
- Context and history accumulation
- Variable persistence between completions
- Prompt awareness of contexts/histories
- Resource cleanup on close
- Validation of unsupported environments
Remove custom ScriptedMockLM class (58 lines) and replace with
create_mock_lm() helper (14 lines) using standard Mock patterns.
Replace scattered hasattr checks with a runtime-checkable Protocol
that defines the persistence capability contract. This provides
type checker enforcement and IDE autocomplete support.
Document expected behavior to implement persistence:
- Versioning behavior (context_0, context_1, ...)
- Aliasing behavior (context -> context_0)
- Method contracts with detailed docstrings
- References to tests and example implementation
@thoriqakbar0 thoriqakbar0 changed the title proposal(local-repl): persistent REPL mode for multi-turn conversations proposal(environments): SupportsPersistence Protocol for multi-turn sessions Jan 8, 2026
@thoriqakbar0
Copy link
Contributor Author

thoriqakbar0 commented Jan 8, 2026

Updated PR scope

Updated the title and description to better reflect what this PR actually delivers.

Before: Focused on LocalREPL persistence implementation

After: Focused on the SupportsPersistence Protocol pattern, with LocalREPL as the reference implementation

Why the change?

While implementing persistence for LocalREPL, I realized the main thing that I was trying to do is protocol pattern itself. it gives other environment authors (Docker, Modal, Cloudflare, Daytona, E2B, etc.) a clear path to add persistence:

  1. Check the Protocol in base_env.py for what methods to implement
  2. Read the docstrings for expected behavior (versioning, aliasing, deep copy)
  3. Look at local_repl.py as a working example
  4. Run tests/test_local_repl_persistent.py to see expected behavior

The implementation for persistence already exists in most environments (Docker uses dill, Modal uses sandbox state). They just need to implement the 5 Protocol methods on top.

would love feedback on this. tq!

@alexzhang13
Copy link
Owner

alexzhang13 commented Jan 8, 2026

It looks good! Will merge after linting stuff, but also @thoriqakbar0 feel free to integrate persistence into the other REPLs (e.g. Docker, Modal, etc.)

We will likely also just need to integrate the other sandboxes. I don't have any keys to test them though, which is why I haven't merged these PRs in yet.

@alexzhang13 alexzhang13 merged commit cb9cab7 into alexzhang13:main Jan 8, 2026
3 checks passed
alt-glitch added a commit to alt-glitch/rlm that referenced this pull request Jan 12, 2026
Align E2BREPL with other environments (DockerREPL, ModalREPL, PrimeREPL)
by adding the persistent parameter introduced in PR alexzhang13#40. Raises
NotImplementedError when persistent=True since E2BREPL doesn't yet
implement the SupportsPersistence protocol.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants