Distilled from the DSPy 3.3.0 research and verified against the installed package. Each item will cost real time if ignored.
Assertions are gone — use dspy.Refine
dspy.Assert, dspy.Suggest, dspy.constrain, and dspy.SoftAssert do not exist in 3.x. There is also no dspy.BestofN in 3.3.0. The replacement is:
Do not port assertion code.
Model string
deepseek-v4-flash is addressed as openai/deepseek-v4-flash via the OpenAI-compatible endpoint — the same pattern DSPy uses for SGLang and local servers. api_base is required. Set model_type="chat" if the endpoint needs it.
Fleet Reasoner uses the repository’s existing OPENAI_* environment variables (OPENAI_MODEL, OPENAI_BASE_URL, OPENAI_API_KEY). The old INKLING_* and DEEPSEEK_API_KEY conventions are gone. One model serves every tier.
If you ever switch to Gemini: the prefix is gemini/, not google/. A bare string silently defaults to Vertex AI and fails without GCP credentials. vertex_ai/ is the GCP variant; use vertex_project and vertex_location — project and location are silently ignored.
Optimizer API shape
- The metric goes on the constructor:
dspy.MIPROv2(metric=...), dspy.BootstrapFewShot(metric=...).
trainset= is keyword-only at compile() — train_set= fails with TypeError.
MIPROv2(auto="light"|"medium"|"heavy") cannot be combined with explicit num_candidates or num_trials — that raises ValueError.
compile() returns a new copy; the student is not mutated.
Module conventions
- No
dspy.Program class. dspy.Module is the base. Call module(...) or module.acall(...), never module.forward(...) — that bypasses tracing and emits a deprecation warning.
super().__init__() is mandatory (metaclass-enforced).
- Never read
dspy.settings in __init__. Read it inside forward() so dspy.context overrides apply.
- Sub-module registration is attribute assignment (
self.predict = ...). Only dspy.Parameter attributes are optimizer-visible.
Predict and ChainOfThought accept keyword args only — predict("q") raises ValueError.
Evaluation contract
dspy.Evaluate calls the metric as metric(example, prediction) — exactly two positional args. trace is populated by optimizers, never by Evaluate.
EvaluationResult.score is a 0–100 percentage, not 0–1.
- Every devset example must call
.with_inputs(...) or program(**example.inputs()) crashes.
- Metric return:
bool, float, or dspy.Prediction(score, feedback). Feedback is read only by GEPA.
dspy.Tool needs valid type hints. The docstring is the description; the type hints are the arg schema.
ReAct and ReActV2 dedupe tools by name — collisions silently overwrite. Keep names unique.
dspy.Retrieve reads dspy.settings.rm at call time. dspy.configure(rm=...) first, or it raises AssertionError("No RM is loaded.").
- Async tools need
acall() or dspy.configure(allow_tool_async_sync_conversion=True).
3.3.0-specific
dspy.ReActV2 is experimental and its prompt format differs from ReAct — reserved submit tool, dspy.ToolCalls, termination_reason. Fleet Reasoner uses it as the default with ReAct as a documented fallback.
dspy.Flex is experimental and starts from a signature, not a composed dspy.Module. It runs optimizer-authored code in a CodeInterpreter sandbox. Because ReasoningEngine is a hand-structured module, Flex only applies to a future signature-first rewrite of the loop.
- NumPy is optional — install
dspy[numpy] if any metric or visual code imports numpy.
- Image / Audio / File constructors no longer do I/O. Use
Image.from_path(path) or Image.from_url(url). Image(path) and Image(url, download=True) are gone. Image.from_file() and from_PIL() are deprecated aliases and are removed in 3.4.
- GEPA result shapes changed with
gepa[dspy]==0.1.1: candidates are compiled modules, best_candidate returns a module, and val_subscores is keyed by validation instance id.
- LM errors are normalized. Catch
dspy.LMError and its subclasses — LMRateLimitError, ContextWindowExceededError, LMUnsupportedModelError, LMTimeoutError — not provider-specific exceptions.
- Typed LM boundary (
dspy.LMRequest / dspy.LMResponse, opt-in via dspy.context(experimental=True)) targets custom LM authors — irrelevant until you replace the built-in OpenAI-compatible provider.
Streaming
dspy.stream does not exist in 3.3.0. The streaming surface is dspy.streamify(program, ...) in dspy.streaming. It wraps any program and returns a callable whose result is an async generator of events, ending with the final dspy.Prediction.
- ReActV2 emits its final answer as
submit tool-call arguments, so token-level streaming of answer does not apply. Surface tool activity as status events and the complete answer in the done frame.
- LM errors raised inside the stream are caught in the generator and emitted as
{"event": "error", "status": 429|413|502} frames. The response has already started, so HTTPException is not an option mid-stream.
Caching and concurrency
- LM caching is ON by default. Pass a unique
rollout_id plus a non-zero temperature to force fresh calls. This is critical for Refine sampling.
dspy.configure has an owner-thread rule. Configure once at startup and use dspy.context in request handlers and worker threads.
- Save and load.
program.save(path) (state only) or program.save(dir, save_program=True) + dspy.load(dir). allow_pickle defaults to False. API keys are never serialized.
Last modified on September 7, 2026