fix(rlm): fail closed on interpreter failures - #68
Draft
isaacbmiller wants to merge 1 commit into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Issue / repro
RLM currently treats a broken interpreter session as if the model merely wrote bad Python.
On
main, RLM catches that failure, writes this into the trajectory, exhaustsmax_iters, and calls the extract LM:The caller can receive an apparently valid answer from extraction even though the interpreter never produced a trustworthy result. This is the unsafe failure mode reported in stanfordnlp/dspy#9643; this PR does not claim to fix that issue's underlying response-ID mismatch.
2. Why this is the root cause
CodeInterpreterErrorcurrently represents two incompatible states:RLM catches the umbrella type:
That catch erases the only distinction that should decide whether the RLM loop may continue.
3. How we know the fix addresses the root cause
The new regression gives RLM a custom interpreter that raises
CodeInterpreterError("protocol corrupt")for generated code and configures extraction to return"hallucinated".On untouched
mainat24ec85de4, the test fails with:This branch raises
CodeInterpreterErrorimmediately, so extraction is never reached.The opposite boundary is tested through the real Deno/Pyodide interpreter: generated
ZeroDivisionError,ValueError, and even a user-defined exception namedCodeInterpreterErrorbecomeCodeExecutionError. A subsequent2 + 2succeeds in the same session, proving that these failures remain recoverable.4. Why this is the concise fix
The origin of the failure is already known at one boundary:
PythonInterpreter.execute()receives either a correlated application-error response, or one of its existing process/protocol checks fails.This PR expresses that fact as a subtype:
PythonInterpreter changes one generated-runtime-error raise site, and RLM narrows one catch:
There is no message parsing, error-name heuristic, retry branch, runner rewrite, or replacement fallback.
5. Context needed to validate the change
PythonInterpreter already raises bare
CodeInterpreterErrorbefore the generated-code error branch for:Known application error codes returned for the matching
executerequest are submitted-code failures.SyntaxErrorremains its existing dedicated exception.The max-iteration extraction fallback is still strategic and remains unchanged. It runs only after a healthy interpreter session produces recoverable iterations without a valid
SUBMIT.6. What the fix does in the code
CodeExecutionError.CodeInterpreterError.CodeInterpreterError.CodeExecutionErrorandSyntaxError.forward/aforwardpropagate interpreter failures.Compatibility boundaries and downsides
CodeExecutionErrorfor recoverable submitted-code failures. A bareCodeInterpreterErrornow tells RLM that the session is unsafe. This explicit contract replaces an ambiguity; there is no reliable generic fallback for old implementations.CodeExecutionErrorsubclassesCodeInterpreterError, existingexcept CodeInterpreterErrorcode still catches both categories.CodeExecutionErrorwhere they previously observed the base class.runner.js, JSON-RPC shapes, process restart behavior, tools, and provider adapters are untouched.Validation
uv run --frozen pytest -q tests/predict/test_rlm.py --deno—110 passed, 2 skippeduv run --frozen pytest -q tests/primitives/test_python_interpreter.py --deno—54 passed9 passedmain— fails because no exception is raised; passes here