Skip to main content
Fleet Reasoner ships as a FastAPI service in qlaw/serve.py. Start it locally:
Configuration is set once at startup with configure_research()dspy.configure has an owner-thread rule and must not be called inside request handlers. Use dspy.context for per-request overrides.

Endpoints

POST /seed

Returns the serialized GraphState. Errors map through the DSPy 3.3.0 exception hierarchy (see “Error mapping” below).

POST /engine

Returns:
outputs uses Prediction.toDict() — DSPy 3.3.0 has no Prediction.model_dump(). max_depth is not a request field. It is a constructor argument on ReasoningEngine and ReasoningLoop.

POST /chat/stream

The server maps session_id to a bounded, independently locked QlawChat in ChatSessionStore. Missing or expired entries start a fresh conversation. history is used only to bootstrap a fresh instance for HTTP compatibility. Later turns use the instance-owned history.

SSE frames

  • Non-Prediction yields are StatusMessage and stream events. ChatStatusProvider turns tool start and end into compact status lines.
  • 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.
  • The done frame carries the final graph. Chat tools expand the graph during the agent run, so the client must adopt done.graph.

GET /config

Returns {"model": "..."} from OPENAI_MODEL (default deepseek-v4-flash). Credentials are never exposed. The web client reads this to display the active model id.

Error mapping

DSPy 3.3.0 normalizes LM errors under dspy.LMError. Catch subclasses and map: For the streamed /chat/stream route, the response has already started when an error occurs mid-stream. HTTPException is not an option there — the generator catches the exception and emits:

Streaming configuration

Relevant DSPy settings:
  • dspy.configure(allow_tool_async_sync_conversion=True) — allow sync tool implementations in async paths.
  • async_max_workers=... — control async concurrency.
  • dspy.asyncify(fn) — wrap a sync function for the async path.
dspy.stream does not exist in 3.3.0. Streaming goes through dspy.streamify(program, status_message_provider=...), which returns an async generator ending with the final dspy.Prediction.
Last modified on August 9, 2026