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.9 -> 9.7.6 (#9608)
- mkdocs-redirects 1.2.2 -> 1.2.3 (#9607)
- mistune 3.0.2 -> 3.2.0 (#9625)
New Contributors
- @thecaptain789 made their first contribution in #9268
- @TheOnlyWayUp made their first contribution in #9274
- @shirvani-jr made their first contribution in #9280
- @adityasingh2400 made their first contribution in #9279
- @immerSIR made their first contribution in #9293
- @Tanmay-24 made their first contribution in #9175
- @ahmeshaf made their first contribution in #9307
- @kvr06-ai made their first contribution in #9317
- @zamal-db made their first contribution in #9327
- @bledden made their first contribution in #9319
- @gauravkumar37 made their first contribution in #8977
- @dev-josias made their first contribution in #9140
- @veeceey made their first contribution in #9272
- @stbiadmin made their first contribution in #9399
- @Jah-yee made their first contribution in #9371
- @michaelisaac-dev made their first contribution in #9313
- @juntaoyou made their first contribution in #9390
- @paul-tharun made their first contribution in #9144
- @matrn made their first contribution in #9493
- @SerjSmor made their first contribution in #9478
- @xieyj17 made their first contribution in #9495
- @Ricardo-M-L made their first contribution in #9627
- @lawrence3699 made their first contribution in #9580
- @abhicris made their first contribution in #9641
Full Changelog: 3.1.3...3.2.0