DeepSeek API client with streaming chat and automatic retry logic.
Purpose
-------
Provides :class:`DeepSeekClient` which wraps the OpenAI-compatible
SDK, handles streaming of reasoning content, text, tool calls, and
token usage, and retries transient errors with exponential backoff.
Moved from ``oc_chat/core/client.py`` in Phase C.2b. The old path
remains as a compat re-export shim until C.3f.
Responsibilities
----------------
- :class:`DeepSeekClient` — concrete :class:`BaseClient` for DeepSeek;
exposes ``stream_chat``, ``one_shot``, ``list_models``.
- :func:`_is_retryable` — decide whether an SDK exception is worth
retrying (auth errors no, transient HTTP yes).
- :func:`_deep_usage_dict` — defensively normalise pydantic
``CompletionUsage`` objects into plain nested dicts so consumers
can rely on ``isinstance(field, dict)``.
- Streaming chunk shaping: yield ``("reasoning", str)``,
``("content", str)``, ``("tool_call", dict)``, ``("usage", dict)``
in API arrival order, then a terminal ``("finish_reason", str)``.
Diagnostics
-----------
Domain: OC_CHAT.CLIENT
Levels:
L2 — lifecycle events
L3 — semantic details
L4 — deep tracing
Contracts
---------
- Up to 3 attempts on transient errors with delays of 1/2/4 seconds.
- Authentication failures fail fast — never retried.
- ``stream_chat`` is a generator; iteration completes when the SDK's
stream ends.
- Usage objects are always converted to plain dicts before being
yielded (no pydantic models leak past this client).
Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- openai (API client for chat completions and streaming)
- option_c_agent_contracts (lazy; SupportsCancellation type of the stream_chat cancellation token)
- httpx (lazy; HTTP client with trust_env off, handed to the OpenAI SDK)
See Also
--------
oc_chat/docs/conventions/CONTRACTS.md §5 Streaming chunk tuples — concrete impl
of the stream_chat tuple contract.