You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 81a41fd
Browse filesBrowse the repository at this point in the historyBrowse files
authored
fix(facade): host the dspy facade in in-process interpreters without replacing host dspy (#10627)
* fix(facade): host the dspy facade in in-process interpreters without replacing host dspy
The shim refused any interpreter whose code runs in the host's memory, so
dspy.Flex failed every forward on exec()-based interpreters such as
dspy_interpreters.InProcessInterpreter (RLM degraded to no sub-agents).
The refusal removed no capability: in-process code already has the host's
memory. What it prevented was the shim replacing the host's
sys.modules["dspy"].
In the host's memory, the shim now binds dspy through the interpreter
namespace's own __import__ and leaves sys.modules alone. Isolated sandboxes
keep registering the shim in sys.modules. Docs say an in-process
interpreter isolates nothing.
Amp-Thread-ID: https://ampcode.com/threads/T-01a12205-a696-77cb-8f91-6b213cec210f
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
* test(flex): cover the in-process shim's rejection of dotted dspy imports
Addresses review: the in-process regression test only exercised a bare
'import dspy'. The generated module now also checks that 'import dspy.predict'
raises ModuleNotFoundError instead of reaching the host's dspy package.
Amp-Thread-ID: https://ampcode.com/threads/T-01a12205-a696-77cb-8f91-6b213cec210f
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
Copy file name to clipboardExpand all lines: docs/docs/api/modules/Flex.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -84,7 +84,7 @@ The `program_trace` parameter is opt-in *by declaration*: only metrics that name
84
84
85
85
## Interpreter Execution
86
86
87
-
`Flex` always runs its generated code through a `CodeInterpreter`. `interpreter_factory` defaults to `dspy.PythonInterpreter` (Deno/Pyodide) and must be a **zero-argument factory** returning a fresh interpreter; a bare instance is not accepted, so parallel evaluations receive isolated sessions. `dspy.configure(interpreter_factory=...)` replaces that default. A factory passed to `Flex` wins, unless it is `PythonInterpreter`. The factory is called once per interpreter session, including separate sessions requested by nested code-executing modules. With the default interpreter, optimizer-authored control flow, string work, arithmetic, and supported imports run inside the sandbox, and only provided-tool calls, predictor construction, and predictor calls bridge back to the host, which makes the real LM calls. Custom factories define their own trust boundary: `dspy.LocalInterpreter`, for example, separates process memory and stdout but retains the host user's filesystem, environment, credentials, network, and process authority.
87
+
`Flex` always runs its generated code through a `CodeInterpreter`. `interpreter_factory` defaults to `dspy.PythonInterpreter` (Deno/Pyodide) and must be a **zero-argument factory** returning a fresh interpreter; a bare instance is not accepted, so parallel evaluations receive isolated sessions. `dspy.configure(interpreter_factory=...)` replaces that default. A factory passed to `Flex` wins, unless it is `PythonInterpreter`. The factory is called once per interpreter session, including separate sessions requested by nested code-executing modules. With the default interpreter, optimizer-authored control flow, string work, arithmetic, and supported imports run inside the sandbox, and only provided-tool calls, predictor construction, and predictor calls bridge back to the host, which makes the real LM calls. Custom factories define their own trust boundary: `dspy.LocalInterpreter`, for example, separates process memory and stdout but retains the host user's filesystem, environment, credentials, network, and process authority. An in-process interpreter that runs code with `exec()` in the host works, but isolates nothing; use one only for trusted code.
88
88
89
89
Because the default builds a `PythonInterpreter`, *running* a `Flex` needs [Deno](https://deno.land/) installed; without it, the call raises.
Copy file name to clipboardExpand all lines: docs/docs/diving-deeper/flex.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ The reflection model authors code, and code can be wrong. A candidate that doesn
34
34
35
35
### 7. Generated code always runs through an interpreter
36
36
37
-
`Flex` takes its interpreter from `interpreter_factory`, else from `dspy.settings.interpreter_factory`, else from `dspy.PythonInterpreter` (Deno/Pyodide). Each of those must be a *zero-argument factory* — a bare instance or `None` is rejected. With the default, everything stays in the sandbox except provided-tool calls, predictor construction, and predictor calls, which bridge back to the host. Custom factories define their own trust boundary: `LocalInterpreter` separates process memory and stdout but retains the host user's filesystem, environment, credentials, network, and process authority. The factory creates a fresh interpreter for each session; a forward owns an outer session and nested code-executing modules may request separate sessions. Source portability between custom interpreters is not guaranteed. `max_predictor_calls` caps how many predictor calls the generated code can make in one `forward`.
37
+
`Flex` takes its interpreter from `interpreter_factory`, else from `dspy.settings.interpreter_factory`, else from `dspy.PythonInterpreter` (Deno/Pyodide). Each of those must be a *zero-argument factory* — a bare instance or `None` is rejected. With the default, everything stays in the sandbox except provided-tool calls, predictor construction, and predictor calls, which bridge back to the host. Custom factories define their own trust boundary: `LocalInterpreter` separates process memory and stdout but retains the host user's filesystem, environment, credentials, network, and process authority. An in-process interpreter (one that runs code with `exec()` in the host, such as `dspy_interpreters.InProcessInterpreter`) works but isolates nothing: generated code shares the host's memory, so `max_predictor_calls` and the bridged LM-option allowlist bind only well-behaved code. Use one only for code you trust. The facade binds `dspy` in that interpreter's namespace and leaves the host's `dspy` module in place. The factory creates a fresh interpreter for each session; a forward owns an outer session and nested code-executing modules may request separate sessions. Source portability between custom interpreters is not guaranteed. `max_predictor_calls` caps how many predictor calls the generated code can make in one `forward`.
38
38
39
39
### 8. The declared output types are enforced at the sandbox boundary
0 commit comments