Releases: stanfordnlp/dspy
Release list
3.3.1
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 ...
3.3.0
DSPy 3.3.0
DSPy 3.3.0 is a feature release with a new experimental way to optimize programs as code, a native-tool-aware ReAct implementation, and the next stage of DSPy's move toward a typed, provider-neutral language-model system.
Most existing DSPy programs should keep working without changes. Review the API changes if you construct Image, Audio, or File values from paths or URLs; use NumPy-backed features from the base install; inspect detailed GEPA results; construct code interpreters directly; use RLM(max_iterations=...); consume raw Responses API tool-call outputs; or catch provider-specific LM exceptions.
We would especially appreciate feedback on Flex, ReActV2, and the typed LM path. These APIs expand what DSPy can optimize and how it can connect to model providers, and real-world usage will help shape their next iterations.
Highlights
Flex Optimizes Program Structure, Not Just Prompts — @michaelisaac-dev
Most DSPy modules fix the shape of a program up front: Predict makes one prediction, ReAct runs a tool loop, and RLM runs a code interpreter in a loop. Optimizers can improve the instructions around that structure, but the structure itself stays fixed. The new experimental dspy.Flex moves the implementation into the search space so GEPA can discover the decomposition instead.
Give Flex the same signature you would give Predict and it starts with the simplest working baseline: one dspy.Predict, or one dspy.RLM when tools are supplied. During compilation, GEPA can rewrite the complete module implementation—changing the predictors, control flow, DSPy primitives, and balance between Python and LM calls—against your metric.
program = dspy.Flex("question -> answer")
optimized = dspy.GEPA(metric=metric, reflection_lm=reflection_lm).compile(
program,
trainset=trainset,
valset=valset,
)
print(optimized.module_src)Optimizer-authored source always runs in a CodeInterpreter sandbox, using dspy.PythonInterpreter by default. Predictor construction and LM calls bridge back to the host, broken candidates score as failures instead of crashing the search, and max_predictor_calls guards against runaway generated programs. Metrics can also accept a program_trace to score how a result was produced—for example, penalizing programs that make too many LM calls.
The optimized module_src is part of the program's serialized state, so saving and loading preserves the implementation GEPA discovered. Flex is experimental, and ordinary GEPA behavior is unchanged when a program does not contain a Flex module.
PR: #10047
ReActV2 and Native Tool-Calling History - @isaacbmiller
dspy.ReActV2 is a new version of ReAct built around native tool calling. It is currently marked as experimental.
The signature now uses dspy.History, dspy.Tool, and dspy.ToolCalls(which can now optionally store dspy.ToolCallResults), rather than the custom next_tool_args and custom trajectory syntax. Using dspy.History also means that messages are now broken up into user/assistant/tool groups rather than one long user message with the trajectory.
This changes the execution model in a few concrete ways:
parallel_tool_callssupport: DSPy preserves each call/result pair by ID. You can do this in native mode or in non-native modeMulti-turn native tool callsupport: Prior tool calls and results can be replayed as assistant and tool messages instead of being flattened into prompt text.- Each turn lives in
dspy.Historyas structured messages rather than one ever-growingtrajectorystring, so providers with prompt caching can reuse stable prefixes more effectively. We have seen up to 50% decreases in cost for some tasks when testing this internally.
ReActV2 converts callables to dspy.Tool, adds an internal submit tool for final outputs, handles unknown tools and tool exceptions, accepts serialized history input, and can force final submission when the model does not call submit.
PRs: #9823, #9824, #9825, #9835
Typed, Provider-Neutral LM Boundary - @MaximeRivest
DSPy is moving from an untyped LM boundary based on prompt, messages, and provider-shaped kwargs toward a typed, provider-neutral contract:
def forward(self, request: dspy.LMRequest) -> dspy.LMResponse:
...The resulting API is a cleaner LM extension point:
- LiteLLM can become an optional compatibility fallback in the planned 3.5+ path, instead of a required part of the core LM contract.
- Custom LM authors can implement one typed
LMRequest -> LMResponsepath instead of guessing which OpenAI/LiteLLM-shaped inputs will arrive. - Custom LMs can translate between DSPy's typed objects and their own provider, local runtime, gateway, or inference stack.
- Adapters can start to depend on DSPy's representation of messages, multimodal content, tool calls, reasoning, citations, usage, cache controls, metadata, and stream events.
Most users do not need to change anything in 3.3. Existing lm(...), modules, and programs keep their current behavior by default.
Try out the typed return path with dspy.context(experimental=True), and the public migration plan explains the staged transition for custom LM and adapter authors.
BaseLM Runtime, Save/Load, Errors, and LiteLLM Decoupling - @MaximeRivest
BaseLM now owns shared runtime state and supports sanitized state serialization through dump_state() and load_state(). Serialized LM state excludes API keys, preserves legacy saved states, and requires explicit opt-in before importing trusted custom LM classes.
Saved programs with custom LMs are easier to reason about, LM copies isolate DSPy-owned mutable state, and callers can catch dspy.LMError or a narrower DSPy subclass instead of depending on provider-specific exception classes. LiteLLM imports are lazy, which keeps the core LM API less coupled to a specific provider bridge at import time.
PRs: #9752, #9820, #9821, #9826
LM and Responses API Updates Since 3.3.0b1 - @MaximeRivest, @isaacbmiller
Since the beta, DSPy has added an explicit BaseLM.forward() contract, exported the typed LM API, supported typed direct calls through BaseLM.__call__, made optional-provider imports thread-safe, and fixed LM state round trips for GPT-5 models.
The OpenAI Responses path now emits Responses-native tool and tool_choice request shapes. Legacy Responses outputs use the same Chat-style tool-call representation as the Chat Completions path, while typed LMToolCallPart objects preserve raw provider fields.
PRs: #9837, #9840, #9841, #9843, #9877, #9999, #10003, #10014, #10026, #10028
API Changes
Breaking Changes
Resource Construction and Validation No Longer Perform Implicit I/O
Constructing or validating dspy.Image, dspy.Audio, and dspy.File values no longer interprets locator-shaped strings as instructions to read a local file or fetch a remote URL. This prevents LM-output parsing, Pydantic validation, and deserialization from silently granting filesystem or network access merely because a value resembles a path or URL.
Resource loading now requires an explicit factory:
| Before 3.3 | DSPy 3.3 | Behavior |
|---|---|---|
Image(path) or Image(url=path) |
Image.from_path(path) |
Read and embed a local image |
Image(url, download=True) |
Image.from_url(url) |
Download and embed a remote image |
Image.from_url(url) or Image.from_url(url, download=False) |
Image(url) or Image(url=url) |
Keep a non-downloading provider-fetched URL reference |
Audio(path) |
Audio.from_path(path) |
Read and embed local audio |
Audio(url) |
Audio.from_url(url) |
Download and embed remote audio |
File(path) |
File.from_path(path) |
Read and embed a local file |
encode_image(path) |
Image.from_path(path) |
Explicitly read a local image |
encode_audio(path_or_url) |
Audio.from_path(path) or Audio.from_url(url) |
Explicitly load audio |
encode_file_to_dict(path) |
File.from_path(path) |
Explicitly read a local file |
There are several related compatibility changes:
Image.from_url()now downloads the resource and returns an embedded data URI. UseImage(url)when the model provider should fetch the reference instead.Image.from_url(..., download=...)and thedownload_images/verifyoptions onencode_image()were removed. Choose reference construction or an explicit factory instead.- Pydantic payloads containing
downloadorverifyare rejected without fetching. The deprecated direct developer callImage(url, download=True)remains available with a warning through 3.3. - The deprecated compatibility call requires a positional source:
Image(url, download=True). Validation-style calls such asImage(url=url, download=True)are rejected. Image.from_file(),Image.from_PIL(), andAudio.from_file()remain as deprecated aliases through 3.3 and are scheduled for removal in 3.4. UseImage.from_path(),Image(pil_image), andAudio.from_path()respectively.- Safe in-memory inputs—including data URIs, bytes, PIL images, audio arrays, structured dictionaries, and existing resource instances—remain supported.
The explicit Image.from_url(url, verify=...) and Audio.from_url(url, verify=...) factories still accept TLS certificate verification controls. The removed verify option applies to encode_image().
Image.from_url() and Audio.from_url() make synchronous caller-initiated requests, follow redirects, and do not provide an SSRF allowlist. Applications remain responsible for validating or allowlisting destinations derived from untrusted input.
PR: #10111 by @isaacbmiller
numpy Is Now Optional
numpy is no longer i...
3.3.0b1
DSPy 3.3.0b1 Release Notes
DSPy 3.3.0b1 is a beta release including a new ReActV2 module, a new BaseLM System, updating to GEPA 0.1.1, and fewer dependencies framework wide.
Most existing DSPy programs should keep working without changes. Review the breaking changes if you depend on NumPy from the base dspy install, inspect GEPA result internals, or catch provider-specific LM exceptions directly.
We would really appreciate feedback on ReActV2 and the new LM system! Please try them out and let us know if you run into any issues.
Highlights
ReActV2 and Native Tool-Calling History - @isaacbmiller
dspy.ReActV2 is a new version of ReAct built around native tool calling. It is currently marked as experimental.
The signature now uses dspy.History, dspy.Tool, and dspy.ToolCalls(which can now optionally store dspy.ToolCallResults), rather than the custom next_tool_args and custom trajectory syntax. Using dspy.History also means that messages are now broken up into user/assistant/tool groups rather than one long user message with the trajectory.
This changes the execution model in a few concrete ways:
parallel_tool_callssupport: DSPy preserves each call/result pair by ID. You can do this in native mode or in non-native modeMulti-turn native tool callsupport: Prior tool calls and results can be replayed as assistant and tool messages instead of being flattened into prompt text.- Each turn lives in
dspy.Historyas structured messages rather than one ever-growingtrajectorystring, so providers with prompt caching can reuse stable prefixes more effectively. We have seen up to 50% decreases in cost for some tasks when testing this internally.
ReActV2 converts callables to dspy.Tool, adds an internal submit tool for final outputs, handles unknown tools and tool exceptions, accepts serialized history input, and can force final submission when the model does not call submit.
PRs: #9823, #9824, #9825, #9835
Typed, Provider-Neutral LM Boundary - @MaximeRivest
DSPy is moving from an untyped LM boundary based on prompt, messages, and provider-shaped kwargs toward a typed, provider-neutral contract:
def forward(self, request: dspy.LMRequest) -> dspy.LMResponse:
...The resulting API is a cleaner LM extension point:
- LiteLLM can become an optional compatibility fallback in the planned 3.5+ path, instead of a required part of the core LM contract.
- Custom LM authors can implement one typed
LMRequest -> LMResponsepath instead of guessing which OpenAI/LiteLLM-shaped inputs will arrive. - Custom LMs can translate between DSPy's typed objects and their own provider, local runtime, gateway, or inference stack.
- Adapters can start to depend on DSPy's representation of messages, multimodal content, tool calls, reasoning, citations, usage, cache controls, metadata, and stream events.
Most users do not need to change anything in 3.3. Existing lm(...), modules, and programs keep their current behavior by default.
Try out the typed return path with dspy.context(experimental=True), and the public migration plan explains the staged transition for custom LM and adapter authors.
Smaller Base Install - @isaacbmiller
We have been whittling away at dependencies!
The base install is lighter: numpy is now optional via dspy[numpy], and direct dependencies on asyncer, xxhash, and typeguard were removed in favor of standard-library paths.
Users who do not need NumPy-backed retrieval, embeddings, or optimizers get a smaller default install with fewer transitive dependencies. Users who need NumPy-backed features can install dspy[numpy].
PRs: #9659, #9733, #9734, #9735
BaseLM Runtime, Save/Load, Errors, and LiteLLM Decoupling - @MaximeRivest
BaseLM now owns shared runtime state and supports sanitized state serialization through dump_state() and load_state(). Serialized LM state excludes API keys, preserves legacy saved states, and requires explicit opt-in before importing trusted custom LM classes.
Saved programs with custom LMs are easier to reason about, LM copies isolate DSPy-owned mutable state, and callers can catch dspy.LMError or a narrower DSPy subclass instead of depending on provider-specific exception classes. LiteLLM imports are lazy, which keeps the core LM API less coupled to a specific provider bridge at import time.
PRs: #9752, #9820, #9821, #9826
Custom Objects for RLM Sandboxes - @kmad
dspy.RLM can now accept custom sandbox-serializable values through SandboxSerializable.
Users can pass richer objects, such as DataFrames, into the sandbox with explicit setup, serialization, assignment, and preview behavior instead of forcing everything through prompt text.
PRs: #9411
GEPA 0.1.1 Support - @BenMcH
DSPy now supports gepa[dspy]==0.1.1, including updated DspyGEPAResult behavior, tests, and docs.
Users can move to the current GEPA DSPy integration in this beta. The breaking changes below call out the result-shape changes for code that inspects detailed GEPA outputs.
PRs: #9673
Breaking Changes
numpy Is Now Optional
numpy is no longer installed with base dspy. Features that need NumPy now require the numpy extra:
pip install "dspy[numpy]"Affected areas include embeddings, KNN/KNNFewShot, SIMBA, and other NumPy-backed optimizer or retrieval paths. (#9659 by @isaacbmiller< 10BC0 /a>)
GEPA Result Shapes Changed With gepa[dspy]==0.1.1
The upstream GEPA 0.1.1 API changed several result structures, and DspyGEPAResult now mirrors those shapes. Users who inspect optimized_program.detailed_results may need to update code:
DspyGEPAResult.candidatesis now a list of compiled DSPy modules, not instruction dictionaries.DspyGEPAResult.best_candidatenow returns a compiled DSPy module.val_subscoresis nowlist[dict[Any, float]], keyed by validation instance id.per_val_instance_best_candidatesis nowdict[Any, set[int]].best_outputs_valsetis nowdict[Any, list[tuple[int, Prediction]]]when tracked.highest_score_achieved_per_val_tasknow returns a dictionary keyed by validation instance id.
If you pass custom GEPA reflection templates directly, note that GEPA 0.1.1 renamed default placeholders from <curr_instructions> / <inputs_outputs_feedback> to <curr_param> / <side_info>. In dspy.GEPA, passing reflection_prompt_template through gepa_kwargs now raises a clear ValueError; use instruction_proposer for custom proposal behavior instead. (#9673 by @BenMcH)
LM Error Types Are Now DSPy-Normalized
LM failures are now mapped into DSPy exception classes. This should make LM error handling more consistent, but code that catches provider-specific or LiteLLM-specific errors directly may need to catch dspy.LMError or a narrower DSPy subclass. (#9826 by @MaximeRivest)
LM Runtime Changes
- Added core typed LM objects for provider-neutral requests, responses, messages, parts, tool specs, reasoning config, cache config, usage, history entries, stream events, and stream assembly. (#9786 by @MaximeRivest)
- Added OpenAI/LiteLLM compatibility conversion helpers so current provider-shaped calls can move through
LMRequestandLMResponseinternally. (#9802 by @MaximeRivest) - Routed adapter
__call__andacallthrough the normalized LM boundary while converting back to legacy parser inputs for compatibility. (#9802 by @MaximeRivest) - Current
BaseLMcalls still receive OpenAI/LiteLLM-shaped kwargs. Internally, adapters now move throughadapter messages -> LMRequest -> OpenAI/LiteLLM kwargs -> current BaseLM -> LMResponse -> existing adapter postprocess path. (#9802 by @MaximeRivest) - Added exact adapter-format regression coverage before and during the boundary work, including Chat, JSON, XML, BAML, TwoStep, tools, reasoning, citations, multimodal content, custom types, demos, and history. (#9791, #9792 by @MaximeRivest)
- Made
BaseLMthe owner of shared runtime state and changedBaseLM.copy()to an explicit shallow runtime copy. (#9821 by @MaximeRivest) - Added sanitized
BaseLM.dump_state()andBaseLM.load_state()support, including trusted custom LM class loading throughallow_unsafe_lm_state=True. (#9820 by @MaximeRivest) - Added structured DSPy LM exceptions and wired them into
dspy.LMand adapter fallback behavior. (#9826 by @MaximeRivest) - Made LiteLLM imports lazy so importing DSPy does not eagerly import the LiteLLM bridge. (#9752 by @MaximeRivest)
- Added the public typed LM API migration plan for custom LM and adapter authors. (#9828 by @MaximeRivest)
ReActV2 and Tool Calling
- Added
dspy.ReActV2, a native-tool-aware ReAct predictor withdspy.Toolconversion, an internalsubmittool, serialized history input support, unknown-tool and tool-exception handling, and forced final submission when needed. (#9825 by @isaacbmiller) - Enabled ReActV2-style agents to use provider-side parallel tool calls when the adapter is configured with
parallel_tool_calls, preserving each model-requested call and each observation by call ID. (#9823, #9824, #9825 by @isaacbmiller) - Moved ReActV2 history from a formatted
trajectorystring into structureddspy.History, so native-tool providers can see prior turns as assistant/tool messages and prompt-caching providers can reuse stable prompt prefixes more effectively. (#9824, #9825 by @isaacbmiller) - Preserved provider tool-call IDs on
ToolCalls.ToolCalland addedToolCallResultsfor call IDs, tool names, values, and error flags. (#9823 by @isaacbmiller) - Taught adapters to replay prior native assistant tool calls and matching tool results as native LM messages when native function calling is enabled, wi...
3.2.1
DSPy 3.2.1 Changelog
- Removed the upper bound on
litellm. (#9687) - Usecase page has been updated! https://dspy.ai/community/use-cases/. To add your use-case, open a PR!
Bug fixes
- Fixed async streaming LM calls so custom headers are forwarded to LiteLLM streaming completions. (#9669)
- Fixed
dspy.Embedderso per-callcaching=Falseis honored for both sync and async embedding calls. (#9708)
Documentation
- Moved Deployment into the technical documentation tabs and promoted production use cases under Community. (#9709)
- Updated the production use-cases copy for DSPy.
- Fixed MkDocs admonition rendering in Deployment and Observability docs. (#9690, #9691)
- Fixed duplicate-word typos across docs, source, and tests. (#9695)
CI and release
- Hardened TestPyPI release validation by adding strict
twine checkand reducing unused workflow permissions. (#9648) - Gave TestPyPI publishing its own GitHub environment. (#9649)
- Removed an unused
setup-nodestep from the docs push workflow. (#9702) - Replaced direct merge-to-main after publishing with a version bump PR. (#9716)
- Refreshed
uv.lockfor the 3.2.0 release state. (#9650)
Dependency updates
- Updated test and development dependencies:
pytest-asyncio,ruff,pre-commit,datamodel-code-generator,optuna, andurllib3. (#9665, #9664, #9699, #9701, #9697, #9698) - Updated documentation dependencies:
mkdocs-llmstxt,mkdocstrings,mistune, andmkdocs-jupyter. (#9661, #9668, #9666, #9700) - Updated GitHub Actions dependencies:
actions/cacheandastral-sh/setup-uv. (#9662, #9663)
Contributors
- @isaacbmiller
- @GopalGB (first contribution, #9695)
- @spjosyula (first contribution, #9708)
Excluded
- The GEPA 0.1.1 result/documentation update was intentionally excluded from this release. (#9673)
3.2.0
Highlights
BetterTogether Allows Chaining Optimizers — @dilarasoylu
BetterTogether now accepts arbitrary optimizers as keyword arguments and chains them via strategy strings. For example, BetterTogether(metric=m, p=GEPA(...), w=BootstrapFinetune(...)) with strategy="p -> w -> p" will prompt-optimize, fine-tune, then prompt-optimize again -- evaluating each step on a valset and returning the best program. (#9149)
There are many promising strategies that may come from running multiple GEPA steps in sequence, or combining prompt and weight optimization steps in sequence, and we are excited to see what the community comes up with.
Beginning of decoupling DSPy from LiteLLM — @MaximeRivest
@MaximeRivest has an ongoing effort to decouple DSPy from LiteLLM, making it much easier to use custom LMs with DSPy. In this release, adapters no longer import litellm at all -- BaseLM now exposes capability properties (supports_function_calling, supports_reasoning, supports_response_schema, supported_params) and a new dspy.ContextWindowExceededError replaces the litellm error throughout. Custom BaseLM backends can now integrate with DSPy's retry/truncation logic without any litellm dependency. (#9516, #9521, #9522)
Warning on Input field type mismatch — @michaelisaac-dev
Passing a value that doesn't match a signature's declared type now logs a warning (using typeguard). Extra fields not in the signature also warn. Disable with dspy.configure(warn_on_type_mismatch=False). (#9313)
Hardened RLM and PythonInterpreter — @isaacbmiller
Tool calls now use kwargs-only dispatch, the JS tool bridge returns structured errors instead of throwing (preventing Deno crashes), and stdout parsing skips non-JSON lines instead of crashing. Subprocess restarts now correctly replay tool/mount registration. (#9341, #9351)
Notices
Restricted pickle for disk cache
We have added an opt-in dspy.configure_cache(restrict_pickle=True) that swaps pickle.load with a restricted unpickler that only allows litellm/openai types, numpy reconstruction helpers, and user-registered safe_types. Prevents arbitrary code execution from corrupted or malicious cache files. (#9629)
In a future release, we will make this restriction the default behavior.
optuna is now optional
Moved from a required dependency to pip install dspy[optuna]. Saves ~12.7 MB. Only MIPROv2 and BootstrapFewShotWithOptuna use it. (#9397)
All Changes
Features
- Make BetterTogether compatible with all optimizers by @dilarasoylu (#9149)
- feat: add verify parameter to Image for SSL bypass by @adityasingh2400 (#9279)
- feat(signature): add type validation for input fields by @michaelisaac-dev (#9313)
- feat: add file output support to inspect_history and fix return type by @bledden (#9321)
- feat: make optuna optional by @isaacbmiller (#9397)
- feat(retrievers): add EmbeddingsWithScores for similarity score access by @SerjSmor (#9478)
- Add XMLAdapter to dspy.ai by @MaximeRivest (#9504)
Bug Fixes
- fix(adapters): skip JSON schema for DSPy custom types in prompt by @darinkishore (#9257)
- fix: ColBERTv2RetrieverLocal forward method variable scoping bug by @veeceey (#9272)
- fix(predict): remove code corruption in ProgramOfThought._parse_code by @adityasingh2400 (#9276)
- fix(RLM): show head and tail for RLM outputs by @isaacbmiller (#9282)
- fix(rlm): change repl_variable preview to 1000 chars by @isaacbmiller (#9296)
- fix(dspy): SemanticF1 and CompleteAndGrounded now return dspy.Prediction by @Copilot (#9302)
- refactor(rlm): enhance code fence parsing and REPL entry formatting by @isaacbmiller (#9309)
- Fix false rollout_id warning when LM temperature is unset by @okhat (#9316)
- fix: pass metric_threshold to BootstrapFewShot for unshuffled case by @kvr06-ai (#9317)
- fix: only suggest reducing valset in GEPA when it is large by @bledden (#9320)
- fix: make DummyLM delegate to forward() instead of overriding call by @bledden (#9322)
- fix: run evaluation on main thread when num_threads=1 by @zamal-db (#9328)
- fix(saving): block unsafe LM loading keys by @isaacbmiller (#9334)
- fix(settings): add allow_pickle flag to settings loading by @isaacbmiller (#9339)
- fix(interpreter): Harden JSONRPC communication and tool bridge by @isaacbmiller (#9341)
- fix(interpreter): reset tools/mounts when Deno subprocess restarts by @isaacbmiller (#9351)
- fix(adapters): pass use_native_function_calling in JSONAdapter.acall by @isaacbmiller (#9374)
- fix(adapters): raise AdapterParseError on empty LM response instead of silent None by @isaacbmiller (#9389)
- Deprecation warning for prefix, format, and parser kwargs in InputField/OutputField by @MaximeRivest (#9394)
- fix(signature): reject duplicate input and output field names by @MaximeRivest (#9432)
- fix(adapter): guard annotation subclass checks for ReAct tool args by @isaacbmiller (#9433)
- fix(JSONAdapter): Ensure JSON serialization handles diacritics correctly for Pydantic BaseModel by @matrn (#9493)
- Restrict litellm version to <=1.82.6 by @isaacbmiller (#9498)
- fix(deps): pin typeguard==4.4.3 to prevent supply chain attacks by @isaacbmiller (#9551)
- fix(lm): preserve per-message structure in Responses API conversion by @lawrence3699 (#9580)
- fix: cloudpickle serialization of Signature on Python 3.14 by @isaacbmiller (#9616)
- fix: correct demo index assignment in MIPROv2 raw_chosen_params by @Ricardo-M-L (#9627)
- Fix cache bugs: os.fspath(None) crash, double disk lookups, lazy logging by @isaacbmiller (#9628)
- Add restrict_pickle option for safe disk cache deserialization by @isaacbmiller (#9629)
- Revert "fix: set LITELLM_LOCAL_MODEL_COST_MAP before litellm import to avoid HTTP fetch" by @isaacbmiller (#9637)
- fix(base_lm): guard response.usage access to handle missing usage field by @isaacbmiller (#9638)
Refactors
- Refactor: Make DummyLM and callbacks depend on BaseLM instead of LM by @MaximeRivest (#9515)
- Refactor: Move model capability checks to BaseLM to remove litellm from adapters by @MaximeRivest (#9516)
- Refactor: Introduce DSPy-owned ContextWindowExceededError to decouple error handling by @MaximeRivest (#9521)
- refactor: type-hint adapters with BaseLM instead of LM by @MaximeRivest (#9522)
Security
- fix(saving): block unsafe LM loading keys by @isaacbmiller (#9334)
- Pin CI actions to SHA and replace curl-pipe-bash Deno install by @TomeHirata (#9509)
- Create SECURITY.md by @isaacbmiller (#9529)
- docs: add ai generated code policy by @isaacbmiller (#9586)
Testing
- fix(deno): Add pytest.mark.deno by @isaacbmiller (#9269)
Docs
- docs(RLMS): add initial rlm docs by @isaacbmiller (#9264)
- fix(docs): Fix tool description on RLM docs by @isaacbmiller (#9270)
- fix: correct typo 'occured' to 'occurred' by @thecaptain789 (#9268)
- Add R port to community ports documentation by @JamesHWade (#9278)
- fix: typo 'itinery_database' to 'itinerary_database' by @TheOnlyWayUp (#9274)
- docs(rlm): fix RLM docs consistency and typos by @shirvani-jr (#9280)
- Add missing docstrings in dspy/utils/hasher.py by @immerSIR (#9293)
- [docs] add Google-style docstrings to Module class by @Tanmay-24 (#9175)
- docs: complete llmstxt plugin with full nav sections by @ahmeshaf (#9307)
- docs: Add 'Copy page' button to fetch and copy raw Markdown by @zamal-db (#9327)
- docs: add Vertex AI (GCP) tab to Language Models guide by @zamal-db (#9329)
- docs: clarify max_rounds behavior in BootstrapFewShot by @bledden (#9319)
- docs: Update read_output_stream to return final value by @gauravkumar37 (#8977)
- Add docstrings to dspy/clients/provider.py by @dev-josias (#9140)
- docs: remove n=5 assumption from modules tutorial example by @MaximeRivest (#9380)
- docs(evaluate): add docstrings to auto_evaluation.py by @stbiadmin (#9399)
- docs: add docstrings to utility functions in utils.py by @Jah-yee (#9371)
- Fix/docstring examples section header by @MaximeRivest (#9418)
- Enhance docstring for Parallel class by @juntaoyou (#9390)
- docs: fix module_end_status_message description in tutorial docs by @paul-tharun (#9144)
- Update optimizer import documentation to use modern pattern by @0xRaduan (#8980)
- Add API reference pages for dspy.configure and dspy.context by @MaximeRivest (#9445)
- Improve dspy.Example docstrings and examples by @MaximeRivest (#9444)
- fix(docs): remove broken BetterTogether tutorial links by @isaacbmiller (#9480)
- [docs] Remove misleading confidence field from Classification example by @xieyj17 (#9495)
- docs: fix duplicate 'the the' typos across docs, source, and tests by @abhicris (#9641)
CI / Infrastructure
- chore: bump uv.lock by @isaacbmiller (#9263)
- fix(precommit): remove hardcoded python version by @isaacbmiller (#9346)
- chore: Add greenlet s390x wheel entries to uv.lock by @isaacbmiller (#9455)
- ci: drop unused write permissions from ruff lint job by @isaacbmiller (#9505)
- fix(ci): correct setup-node SHA typo in docs-push workflow by @isaacbmiller (#9528)
- chore: add dependabot configuration for automated dependency updates by @isaacbmiller (#9592)
- chore(dspy): bump gepa from 0.0.26 to 0.0.27 by @isaacbmiller (#9416)
- feat(ci): Create PRs with ruff fixes automatically by @isaacbmiller (#9336) -- reverted (#9345)
Dependency updates (Dependabot)
- orjson 3.11.2 -> 3.11.8 (#9597)
- requests 2.32.4 -> 2.33.1 (#9603)
- anthropic 0.54.0 -> 0.89.0 (#9599)
- tqdm 4.67.1 -> 4.67.3 (#9623)
- pre-commit 4.2.0 -> 4.5.1 (#9622)
- denoland/setup-deno 2.0.3 -> 2.0.4 (#9596)
- actions/checkout 3.6.0 -> 6.0.2 (#9595)
- actions/setup-node 3.9.1 -> 6.3.0 (#9600)
- stefanzweifel/git-auto-commit-action 5.2.0 -> 7.1.0 (#9598)
- pypa/gh-action-pypi-publish 1.13.0 -> 1.14.0 (#9618)
- mkdocs-jupyter 0.25.1 -> 0.26.1 (#9605)
- mkdocstrings 0.29.0 -> 1.0.3 (#9609)
- mkdocs-material 9.6....
3.1.3
What's Changed
RLMs
- fix(interpreter): Fix enable_read_paths with multiple files by @missing-piece in #9256
- fix: handle dict response in RLM for reasoning models by @darinkishore in #9219
- feat(CodeInterpreter): Convert messaging format to JSONRPC by @isaacbmiller in #9226
- feat(RLMs): Fix code fence parsing by @isaacbmiller in #9231
- fix(RLM): large variable injection by @isaacbmiller in #9233
- fix(RLM): no longer get stuck on imports by @isaacbmiller in #9234
- fix(RLM): Refactor tools to take list instead of dict and properly serialize None/null values by @isaacbmiller in #9247
GEPA
- Update gepa[dspy] dependency to version 0.0.26 adding support for cached evals in GEPA expected to reduce metric calls, fix MLFlow logging, and bug fixes by @LakshyAAAgrawal in #9238
- Update gepa[dspy] version to 0.0.25 adding python==3.14 support by @LakshyAAAgrawal in #9224
- Temporarily remove the GEPA tool optimization doc by @chenmoneygithub in #9220
- fix(gepa): Remove enable_tool_optimization feature by @Ju-usc in #9223
Maintenance
- Stabilize real LM tests with deterministic temperature by @okhat in #9218
- fix: handle error responses from ColBERTv2 server by @emmanuel-ferdman in #9227
- Fix format by @chenmoneygithub in #9249
- Fix the request header in streaming mode by @chenmoneygithub in #9248
- feat(docs): add community ports page by @isaacbmiller in #9222
Deferred to a later release (added then reverted before cutting this release):
- Allow DSPy to use the native reasoning from models by @chenmoneygithub in #8822
- Revert ChainOfThought to pre-dspy.Reasoning (#8822) behavior by @okhat in #9258
New Contributors
- @missing-piece made their first contribution in #9256
Full Changelog: 3.1.2...3.1.3
3.1.2
What's Changed
Maintenance
- ci: install Deno in release workflow by @okhat in #9217
- Fix download bug in RAG tutorial by @togimoto in #9156
- Update DSpy settings save / load methods by @WeichenXu123 in #9215
- Expose timeout and straggler_limit params in Parallel by @halfprice06 in #9199
- Fix JSON parsing: Removing initial regex extraction. by @blightzero in #9182
New Contributors
- @halfprice06 made their first contribution in #9199
- @blightzero made their first contribution in #9182
Full Changelog: 3.1.1...3.1.2
3.1.1
What's Changed
RLMs
- feat(rlm): Add RLM Module and improve PythonInterpreter by @isaacbmiller in #9193
- Use
languagein system instructions fordspy.Codefields by @mariusarvinte in #9106 - Update dspy.RLM to improve reliability and avoid pydantic warnings by @okhat in #9210
- fix(RLM): Change FinalAnswerResult to FinalOutput and remove RLM call by @isaacbmiller in #9212
GEPA
- Fix GEPAFeedbackMetric Protocol missing 'self' parameter by @Copilot in #9111
- feat(gepa): add tool description optimization for multi-agent systems by @Ju-usc in #8928
- Update pyproject.toml to update gepa from 0.0.22 -> 0.0.24 by @LakshyAAAgrawal in #9161
- Patch
dspy.gepato handlelist[dict]output from adspy.LMby @mariusarvinte in #9169
Adapters
- Upgrade json_repair to fix parsing issue of underscore-style float number by @chenmoneygithub in #9088
- Properly yield the last chunk in streaming by @chenmoneygithub in #9089
- Avoid unwanted exception chaining when running async tool in sync context by @stevapple in #9092
- Enhance StreamListener to support generic type annotations for output by @TomeHirata in #9112
- FIX: streamify was appending StatusStreamingCallback directly to the shared settings.callbacks list by @glesperance in #9073
- fix(BAMLAdapter): Use docstrings to describe BaseModels by @BenMcH in #9125 D63E
- Fix Responses API structured outputs by @Olocool17 in #9130
Maintenance
- Fix UsageTracker AttributeError when using ParallelExecutor with dspy.context by @Copilot in #9095
- Update uv.lock dspy version to match the latest release by @chenmoneygithub in #9067
- [docs] Add Google-style docstrings for dspy/adapters/chat_adapter.py ChatAdapter class #9063 by @azai91 in #9072
- Fix ContextWindowExceededError after 3 retries in react loop by @Copilot in #9110
- fix: skip merging when both usage entry values are None by @chizukicn in #9121
- Fix: enforce positive
memory_max_entriesfor in-memory cache by @mshr-h in #9128 - Add typing to
dspy.datasets.datasetby @max-muoto in #9143 - Increase max_iters for dspy.ReAct by @chenmoneygithub in #9162
- fix(dspy): prevent argument injection in LocalProvider subprocess calls by @Copilot in #9160
- fix(dspy): populate InputField default values in Predict by @ritsuki1227 in #9167
- fix(PythonInterpreter): Remove overly permissive read permissions and add strict allow by @isaacbmiller in #9081
- Add save / load methods for DSpy settings by @WeichenXu123 in #9165
Docs
- [doc] Correct conversation history tutorial example by @zhiyanliu in #9071
- fix: Resolve broken hover-nlp/hover dataset by @vincentkoc in #9069
- Fix missing Real-World Examples and Experimental RL Optimization sections on tutorials page by @0xRaduan in #8982
- Refactor ReflectiveExample TypedDict documentation by @LakshyAAAgrawal in #9133
- [Automatic] Update api reference by @chenmoneygithub in #9135
- Replace ujson with orjson in docs by @togimoto in #9148
New Contributors
- @zhiyanliu made their first contribution in #9071
- @azai91 made their first contribution in #9072
- @0xRaduan made their first contribution in #8982
- @Ju-usc made their first contribution in #8928
- @mariusarvinte made their first contribution in #9106
- @Olocool17 made their first contribution in #9130
- @chizukicn made their first contribution in #9121
- @mshr-h made their first contribution in #9128
- @max-muoto made their first contribution in #9143
- @togimoto made their first contribution in #9148
- @ritsuki1227 made their first contribution in #9167
- @WeichenXu123 made their first contribution in #9165
Full Changelog: 3.1.0...3.1.1
3.1.0
What's Changed
This is a 3.1.0 official release. We are making the beta release 3.1.0beta1 official.
Optimizers & Evaluation
- Add tutorial for dspy-trusted-monitor using GEPA by @ZachParent in #8938
- Update gepa[dspy] dependency version to 0.0.18 by @LakshyAAAgrawal in #8969
- fix(MIPROv2): zero shot not taking .compile parameters into account before determining if the program was zero shot by @isaacbmiller in #8909
- Update gepa[dspy] dependency version to 0.0.22 by @LakshyAAAgrawal in #9042
- [docs] Add GEPA gepa_kwargs documentation by @gabrielloiseau in #8998
- Update optimizer overview to include the description of SIMBA by @TomeHirata in #9026
- Enable callback logging only for full eval on GEPA by @TomeHirata in #9050
- Update Arbor Tutorials by @Ziems in #9007
- Update RL Tutorial by @Ziems in #9008
Features & Enhancements
- Add Disable Fallback Option in ChatAdapter by @Ziems in #8984
- Add a method to extract system message based on adapter and signature by @chenmoneygithub in #9006
- feat(File): add File type for handling file data by @TomeHirata in #9014
- Allow stream listener to work on any type by @chenmoneygithub in #8833
- fix(audio): Normalize 'x-wav' audio format to 'wav' by @akshatvishu in #9017
- Support Python 3.14 by @TomeHirata in #9041
- Introduce dspy.Reasoning to capture native reasoning from reasoning models by @chenmoneygithub in #8986
Security & Serialization
- Add guards against loading pkl files by @isaacbmiller in #9048
- Add param to load_memory_cache to stop pkl files without explicit loading by @isaacbmiller in #9055
Bug Fixes & Type Handling
- Fix TypeError when tracking usage with Anthropic models returning Pydantic objects by @Copilot in #8978
- Update old Anthropic model names by @TomeHirata in #8992
- fix(XMLAdapter): Implement user message formatting by @BenMcH in #9003
- Fix content input conversion for OpenAI Responses API by @Copilot in #8993
- Refactor: update type hints for adapter and LM methods by @TomeHirata in #9025
- fix(dspy): exclude gpt-5-chat from reasoning model classification by @mindful-time in #9033
- fix(dspy): Example.toDict() fails to serialize dspy.History objects by @Copilot in #9047
- Some continuous format fix by @chenmoneygithub in #8987
Documentation & Tutorials
- Add documentation for provider-side prompt caching with Anthropic and OpenAI by @Copilot in #8970
- [docs] Add Google-style docstrings for dspy/evaluate/metrics.py by @eramis73 in #8954
- fix: broken PyPI downloads badge from pepy.tech in README and docs home page by @dushmanta05 in #8995
- Document ToolCall.execute() availability from dspy 3.0.4b2 by @Copilot in #9004
- fix(docs): add python language id to code block by @Ahmad8864 in #9023
- docs: add note on Python version for pre-commit by @akshatvishu in #9028
- chore(docs): update dspy.settings.configure and dspy.settings.context to dspy.configure and dspy.context by @isaacbmiller in #9060
- docs: add documentation for async tool usage and error handling by @TomeHirata in #9054
Minor Fixes, Maintenance & CI
- Remove unused notebook python file by @TomeHirata in #8971
- Cache Ollama to speed up CI by @TomeHirata in #8972
- Clean out unused deps by @isaacbmiller in #8968
- docs: add note on Python version for pre-commit by @akshatvishu in #9028
- Bump DSPy version by @chenmoneygithub in #9061
New Contributors
- @ZachParent made their first contribution in #8938
- @eramis73 made their first contribution in #8954
- @dushmanta05 made their first contrib EB8A ution in #8995
- @akshatvishu made their first contribution in #9017
- @mindful-time made their first contribution in #9033
- @gabrielloiseau made their first contribution in #8998
- @Ahmad8864 made their first contribution in #9023
Full Changelog: 3.0.4...3.1.0b1
3.1.0b1
What's Changed
This is a pre-release for 3.1.0.
Optimizers & Evaluation
- Add tutorial for dspy-trusted-monitor using GEPA by @ZachParent in #8938
- Update gepa[dspy] dependency version to 0.0.18 by @LakshyAAAgrawal in #8969
- fix(MIPROv2): zero shot not taking .compile parameters into account before determining if the program was zero shot by @isaacbmiller in #8909
- Update gepa[dspy] dependency version to 0.0.22 by @LakshyAAAgrawal in #9042
- [docs] Add GEPA gepa_kwargs documentation by @gabrielloiseau in #8998
- Update optimizer overview to include the description of SIMBA by @TomeHirata in #9026
- Enable callback logging only for full eval on GEPA by @TomeHirata in #9050
- Update Arbor Tutorials by @Ziems in #9007
- Update RL Tutorial by @Ziems in #9008
Features & Enhancements
- Add Disable Fallback Option in ChatAdapter by @Ziems in #8984
- Add a method to extract system message based on adapter and signature by @chenmoneygithub in #9006
- feat(File): add File type for handling file data by @TomeHirata in #9014
- Allow stream listener to work on any type by @chenmoneygithub in #8833
- fix(audio): Normalize 'x-wav' audio format to 'wav' by @akshatvishu in #9017
- Support Python 3.14 by @TomeHirata in #9041
- Introduce dspy.Reasoning to capture native reasoning from reasoning models by @chenmoneygithub in #8986
Security & Serialization
- Add guards against loading pkl files by @isaacbmiller in #9048
- Add param to load_memory_cache to stop pkl files without explicit loading by @isaacbmiller in #9055
Bug Fixes & Type Handling
- Fix TypeError when tracking usage with Anthropic models returning Pydantic objects by @Copilot in #8978
- Update old Anthropic model names by @TomeHirata in #8992
- fix(XMLAdapter): Implement user message formatting by @BenMcH in #9003
- Fix content input conversion for OpenAI Responses API by @Copilot in #8993
- Refactor: update type hints for adapter and LM methods by @TomeHirata in #9025
- fix(dspy): exclude gpt-5-chat from reasoning model classification by @mindful-time in #9033
- fix(dspy): Example.toDict() fails to serialize dspy.History objects by @Copilot in #9047
- Some continuous format fix by @chenmoneygithub in #8987
Documentation & Tutorials
- Add documentation for provider-side prompt caching with Anthropic and OpenAI by @Copilot in #8970
- [docs] Add Google-style docstrings for dspy/evaluate/metrics.py by @eramis73 in #8954
- fix: broken PyPI downloads badge from pepy.tech in README and docs home page by @dushmanta05 in #8995
- Document ToolCall.execute() availability from dspy 3.0.4b2 by @Copilot in #9004
- fix(docs): add python language id to code block by @Ahmad8864 in #9023
- docs: add note on Python version for pre-commit by @akshatvishu in #9028
- chore(docs): update dspy.settings.configure and dspy.settings.context to dspy.configure and dspy.context by @isaacbmiller in #9060
- docs: add documentation for async tool usage and error handling by @TomeHirata in #9054
Minor Fixes, Maintenance & CI
- Remove unused notebook python file by @TomeHirata in #8971
- Cache Ollama to speed up CI by @TomeHirata in #8972
- Clean out unused deps by @isaacbmiller in #8968
- docs: add note on Python version for pre-commit by @akshatvishu in #9028
- Bump DSPy version by @chenmoneygithub in #9061
New Contributors
- @ZachParent made their first contribution in #8938
- @eramis73 made their first contribution in #8954
- @dushmanta05 made their first contribution in #8995
- @akshatvishu made their first contribution in #9017
- @mindful-time made their first contribution in #9033
- @gabrielloiseau made their first contribution in #8998
- @Ahmad8864 made their first contribution in #9023
Full Changelog: 3.0.4...3.1.0b1