> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qredence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gotchas

> DSPy 3.3.0 pitfalls that will cost you time: retired assertions, model-string form, optimizer API shape, module conventions, evaluation contract, tools and retrieval, streaming, and caching.

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:

```python theme={null}
dspy.Refine(module, N, reward_fn, threshold)
```

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.

## 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.
