DSPy 3.3.1
DSPy 3.3.1 contains many interpreter fixes and improvements. It makes PythonInterpreter
easier to install, substantially strengthens sandbox isolation and request
handling, and adds end-to-end visibility into interpreter execution. The release
also improves optimizer throughput, adapter correctness, and MCP compatibility.
Highlights
PythonInterpreter: Managed Runtime, Hardening, and Lifecycle Visibility
Installation, isolation, and execution integrity
PythonInterpreter now has an optional managed runtime installation:
pip install "dspy[deno]"DSPy prefers that managed binary when present, while continuing to support
system Deno 2.x and an explicit custom deno_command. The default path pins
Pyodide, validates Deno >=2.0.0,<3.0.0, and ignores ambient Node and Deno
project configuration so nearby application files cannot change sandbox startup.
The interpreter also closes several execution-integrity and isolation gaps:
- unsolicited sandbox diagnostics can no longer desynchronize JSON-RPC replies;
- request IDs are unpredictable, and recursive execution through one of an
interpreter's own host tools is rejected; - bundled runtime files are protected and Deno-cache access is revoked after
startup; - mounted files with distinct host paths cannot silently collide at the same
sandbox basename; and - guest code cannot change host-tool identity by mutating JavaScript globals or
prototypes.
Observability and agent integration
DSPy's callback API now exposes the complete interpreter lifecycle:
- interpreter execution start and end;
- sandbox-to-host tool-call start and end;
- interpreter process startup and shutdown.
Events retain callback ancestry across modules, interpreters, tools, and LM
calls. End callbacks receive terminating BaseException values such as
cancellation and interruption instead of incorrectly reporting those operations
as successful. Optimizer compile() runs receive the same start/end coverage.
PythonInterpreter.execution_instructions now gives RLM an accurate description
of the Pyodide environment, including state persistence and unavailable native
process capabilities. This helps generated code use the sandbox correctly.
Typing, serialization, and compatibility details
- Tool defaults and
NoneTypeannotations serialize correctly across the
sandbox boundary. CodeInterpreterErroris now aDSPyErrorwhile retaining its existing
RuntimeErrorcompatibility.- LM-facing execution errors use one consistent formatter across interpreter and
agent modules. - Existing system Deno and custom-command integrations remain supported; the
managed runtime is opt-in.
PRs: #10119,
#10120,
#10134,
#10135,
#10136,
#10186,
#10190,
#10194,
#10205,
#10206,
#10208, and
#10255
Faster Multi-Proposal GEPA Optimization
DSPy now uses GEPA 0.1.4 and supports its multi-proposal sampling, selection,
acceptance, tracking, and checkpoint-state contracts through gepa_kwargs.
DSPy's adapter saves and restores its random-number-generator state, making
resumed proposal sampling consistent with uninterrupted optimization.
When a sampling strategy produces multiple candidates, DSPy can evaluate those
candidates concurrently. Candidate-level and example-level concurrency share the
existing num_threads budget rather than multiplying it. For example, four
candidates evaluated with num_threads=8 receive two example workers each; total
DSPy-controlled concurrency remains eight.
import dspy
from gepa.strategies.proposal_sampling import IndependentSampling
from gepa.strategies.proposal_selection import BestImprovement
optimizer = dspy.GEPA(
metric=metric,
max_metric_calls=2_000,
reflection_lm=dspy.LM("openai/gpt-5", temperature=1.0, max_tokens=32_000),
num_threads=8,
gepa_kwargs={
"sampling_strategy": IndependentSampling(4),
"selection_strategy": BestImprovement(),
"acceptance_criterion": "strict_improvement",
},
)The default single-proposal strategy retains its previous execution shape.
max_reflection_cost is not yet supported by DSPy's GEPA adapter and now raises
clearly when set instead of silently providing an ineffective budget.
Metrics can also report named objective_scores and select an objective-aware
frontier through gepa_kwargs. Objective, hybrid, and cartesian frontiers let
GEPA use dimensions such as quality, privacy, or cost when selecting parents and
merges, while the scalar metric continues to gate acceptance and select the final
program. Tracked results expose aggregate objective scores, best candidates per
objective, and each objective's independently achieved maximum.
GEPA 0.1.4 has a known upstream limitation: it requests traces while evaluating
accepted candidates on the full validation set. For programs whose traced and
ordinary evaluation paths differ after a runtime failure, this can cause missing
or misaligned validation results. The upstream correction is targeted for GEPA
0.1.5.
More Reliable Structured Adapter Outputs
When an LM omits an output field with a declared default, default factory, or
None-allowing annotation, ChatAdapter, JSONAdapter, XMLAdapter, and adapters
built on them now apply the declared fallback. Missing required outputs continue
to raise AdapterParseError.
This also prevents adapter fallback merely because a provider omitted a native
optional output.
XMLAdapter now formats and parses nested Pydantic models, typed dictionaries,
lists, mappings, nullable fields, and unions as nested XML. It continues to
accept the previous JSON-inside-an-outer-XML-field representation for backward
compatibility.
MCP SDK v2 Compatibility and Structured Results
DSPy's MCP bridge supports both MCP SDK v1 and v2 field names, v1
ClientSession, and the v2 high-level Client. The default tool-result semantics
are unchanged: historical text and non-text content remains authoritative rather
than being replaced by v2 structured content.
Applications can now opt into machine-readable MCP results:
tool = dspy.Tool.from_mcp_tool(client, mcp_tool, result_mode="structured")Structured mode returns structuredContent exactly when the server supplies it,
including arrays, scalar values, empty values, and explicit JSON null. It falls
back to the existing content conversion when structured content is absent. DSPy
does not infer, parse, or unwrap the returned value.
API and Compatibility Changes
CodeAct and ProgramOfThought Deprecation
dspy.CodeAct and dspy.ProgramOfThought now emit DeprecationWarning when
constructed. They are scheduled for removal in DSPy 3.5; use dspy.RLM for new
code.
PR: #10198
Resource Download Timeouts
Image.from_url() and Audio.from_url() now default to a 30-second request
timeout instead of potentially waiting forever:
image = dspy.Image.from_url(url, timeout=60)
audio = dspy.Audio.from_url(url, timeout=60)Pass timeout=None to retain the previous unbounded behavior.
PR: #10149
Typed Interpreter and ReAct Errors
CodeInterpreterError is now also a DSPyError while retaining
RuntimeError compatibility. ReAct preserves ContextWindowExceededError after
trajectory truncation is exhausted, and LM-facing execution errors now use one
consistent formatter.
PRs: #10134,
#10132,
#10135,
#10139
Additional Fixes
ParallelExecutorcorrectly treats a completed task returningNoneas
complete. #10142COPRO.compile(..., eval_kwargs=None)now matches its documented optional
contract. #10087Dataset.reset_seeds()now honors valid zero-valued sizes and seeds.
#9906
Full PR List
GEPA and Optimizers
- Upgrade DSPy's GEPA engine to 0.1.4 by @isaacbmiller
(#10209). - Parallelize GEPA candidate evaluation within the existing thread budget by
@isaacbmiller (#10210). - Add objective-aware GEPA frontier tracking and result metadata by @isaacbmiller
(#10259). - Make
COPRO.compile'seval_kwargsoptional by @katherineahn
(#10087).
Callbacks and Parallel Execution
- Expose interpreter lifecycle callback events by @isaacbmiller
(#10119). - Expose optimizer compile callback events by @isaacbmiller
(#10120). - Report terminating
BaseExceptionvalues to callback end handlers by
@isaacbmiller (#10194). - Track completed
Noneresults inParallelExecutorby @isaacbmiller
(#10142).
Adapters, Tools, and MCP
- Apply defaults and nullable fallbacks for omitted optional output fields by
@michaelisaac-dev (#10148). - Add default timeouts to explicit image and audio URL downloads by @immu4989
(#10149). - Support MCP SDK v2 without changing tool-result semantics by @isaacbmiller
(#10188). - Add opt-in structured MCP tool results by @isaacbmiller
(#10235). - Format and parse nested structured data with XMLAdapter by @isaacbmiller
(#10239).
Agents, Errors, and Code Execution
- Preserve typed ReAct context-window errors by @isaacbmiller
(#10132). - Root
CodeInterpreterErrorunderDSPyErrorwhile retaining
RuntimeErrorcompatibility by @isaacbmiller
(#10134). - Share LM-facing exception formatting across modules by @isaacbmiller
(#10135). - Add interpreter execution instructions to RLM prompts by @isaacbmiller
(#10136). - Repair ReAct's formatter after parallel merges by @isaacbmiller
(#10139). - Add managed Deno support and isolate interpreter dependencies by @isaacbmiller
(#10186). - Prevent unhandled sandbox rejections from desynchronizing interpreter requests
by @michaelisaac-dev
(#10190). - Deprecate
CodeActandProgramOfThoughtin favor of RLM by @isaacbmiller
(#10198). - Reject colliding interpreter file mounts by @isaacbmiller
(#10205). - Isolate the interpreter's Deno cache and runtime files by @isaacbmiller
(#10206). - Harden interpreter request handling and tool wrappers by @isaacbmiller
(#10208). - Scope Python standard-library guidance to
PythonInterpreter-backed RLMs by
@isaacbmiller (#10255).
Datasets
- Honor zero-valued sizes and seeds in
Dataset.reset_seedsby @alvinttang
(#9906).
Documentation
- Fix four dead external documentation links by @yzxcj797
(#10213). - Add the Nubank DSPy case study by @isaacbmiller
(#10228). - Document the ReActV2 transition, structured history, and tool interfaces by
@isaacbmiller (#10229). - Update the MCP tutorial to use the SDK v2 high-level
Clientby @isaacbmiller
(#10249). - Repair the BetterTogether optimizer-overview link by @saime428
(#10253).
Testing, CI, Dependencies, and Release Engineering
- Update main's version metadata after the 3.3.0 release
(#10127). - Repair dead settings ownership assertions by @isaacbmiller
(#10131). - Cap FastAPI for LiteLLM proxy compatibility in the development environment by
@daleselaji-dev (#10151). - Reuse one LiteLLM test server per pytest worker by @isaacbmiller
(#10154). - Use a memory-only cache by default in tests by @isaacbmiller
(#10156). - Remove wall-clock retry waits from tests by @isaacbmiller
(#10157). - Run extra test suites in parallel by @isaacbmiller
(#10164). - Prefetch the Ollama image during test setup by @isaacbmiller
(#10169). - Regenerate the project lockfile by @isaacbmiller
(#10199). - Update the documentation build's urllib3 dependency
(#9931). - Update GitHub Actions checkout
(#9945). - Update GitHub Actions Python setup
(#9965). - Update GitHub Actions caching
(#9966). - Update the zizmor security-analysis action
(#9967). - Update the documentation build's mkdocstrings-python dependency
(#9968). - Update the actionlint workflow action
(#9988). - Update the documentation build's Mistune dependency
(#10109). - Update the documentation build's MkDocs Material dependency
(#10110).
Contributors
Thank you to @alvinttang, @daleselaji-dev, @immu4989, @isaacbmiller,
@katherineahn, @michaelisaac-dev, @saime428, and @yzxcj797 for contributing to
this release.
Full Changelog: 3.3.0...3.3.1