Skip to main content
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_locationproject 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 onlypredict("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.

Tools and retrieval

  • 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 August 9, 2026