Skip to content

Commit ac63910

Browse files
saxmanclaude
andcommitted
docs: document ModelConnectionError alongside its sibling error types
The feat commit that added ModelConnectionError updated the CHANGELOG but left three doc surfaces inconsistent with the new public export: - docs/reference/api/models.md: add it under ## Resilience so it gets an API page next to FallbackClient / FallbackExhaustedError. - CLAUDE.md principle 6 "Failures are apparent": add it to the error enumeration next to MCPConnectionError. - CLAUDE.md Resilience section: document the wrapping behavior (which paths catch APIConnectionError, __cause__ preservation, mirrors MCPConnectionError/A2AConnectionError, retry_on= tie-in). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 09aec39 commit ac63910

2 files changed

Lines changed: 5 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ These six principles drive every architectural decision in AIMU. When proposing
3636
3. **Composability through uniform interfaces.** `BaseModelClient` for every provider, `Runner` for every agent and workflow, `MemoryStore` for every memory backend, `StreamChunk` for every streaming source. `agent.as_model_client()` exists specifically so agents are substitutable with plain clients.
3737
4. **Progressive disclosure.** `aimu.chat()``aimu.client()``Agent` → workflows → custom `BaseModelClient` subclass. Each layer optional; the top wraps the next. New entry points should fit somewhere on this ladder, not parallel to it.
3838
5. **Direct paths for common tasks.** Common operations have one obvious, ergonomic entry point: `aimu.chat()`, `Chain.from_client(...)`, `Agent(client, "system msg", tools=[...])`, `include=["generating"]`, `builtin.web`. The library does not offer parallel, equally-recommended ways to do the same job. If a second path exists, it's a power-user escape hatch (e.g. `Agent.from_config()`), not a documented alternative.
39-
6. **Failures are apparent.** Errors raise at the layer where the cause is actionable, with messages that name the problem. `ToolSignatureError` at decoration time. `ToolArgumentError` at tool dispatch (surfaced to the model as a tool result). `SkillLoadError` on discovery. `MCPConnectionError` on construction. Silent fallbacks are bugs; chained exceptions preserve the original cause via `raise ... from exc`.
39+
6. **Failures are apparent.** Errors raise at the layer where the cause is actionable, with messages that name the problem. `ToolSignatureError` at decoration time. `ToolArgumentError` at tool dispatch (surfaced to the model as a tool result). `SkillLoadError` on discovery. `MCPConnectionError` on construction. `ModelConnectionError` when an inference server is unreachable. Silent fallbacks are bugs; chained exceptions preserve the original cause via `raise ... from exc`.
4040

4141
When reviewing a proposed change, ask which principle it serves and which (if any) it violates. The small public surface is itself a feature; keep it small.
4242

@@ -1086,6 +1086,8 @@ Networked model clients accept `timeout: Optional[float]` and `max_retries: Opti
10861086
- **In-process providers (HuggingFace, LlamaCpp)**: not networked; their constructors don't accept these kwargs, so passing `timeout`/`max_retries` raises `TypeError` (documented networked-only). The aio HF/llamacpp wrappers wrap an existing sync client, so any config lives on that sync client.
10871087
- Tests: `tests/test_resilience_api.py` (sync), `tests/test_aio_resilience_api.py` (async); both monkeypatch the SDK constructor to assert forwarding, the omit-when-unset path, the Ollama `max_retries` guard, and the in-process `TypeError`.
10881088

1089+
**Unreachable-server errors (`ModelConnectionError`).** When an inference server is down or the `base_url` is unreachable, the OpenAI-compatible clients ([aimu/models/providers/openai_compat.py](aimu/models/providers/openai_compat.py), [aimu/aio/providers/openai_compat.py](aimu/aio/providers/openai_compat.py); sync + async, streaming + non-streaming) catch the OpenAI SDK's `APIConnectionError` at the `chat.completions.create` call (and during stream consumption, where a mid-stream drop can surface it) and re-raise it as `ModelConnectionError` **from** the original, so the specific transport cause (e.g. `httpx.ConnectError: [Errno 61] Connection refused`) is preserved on `__cause__` for a front end to surface. `ModelConnectionError` is defined in [aimu/models/_base/shared.py](aimu/models/_base/shared.py) (re-exported from `aimu.models.base`, `aimu.models`, and `aimu.aio`), mirroring `MCPConnectionError` / `A2AConnectionError`. Only `APIConnectionError` is wrapped; genuine HTTP/API errors still propagate with their own detail. It's a clean class to pass to `FallbackClient(..., retry_on=...)`. Tests: `tests/test_openai_compat_connection_error.py`.
1090+
10891091
### Fallback / failover (`FallbackClient`)
10901092

10911093
`FallbackClient` ([aimu/models/fallback.py](aimu/models/fallback.py)) wraps an **ordered list** of `BaseModelClient`s and fails over to the next on error: the first that answers wins; a client raising an exception in `retry_on` (default `(Exception,)`) hands off to the next; when all fail, `FallbackExhaustedError` is raised with the last error as `__cause__` and all `(client, exception)` pairs on `.errors`. It is the P0-B roadmap item, the principle-aligned answer to provider failover (one composable class, not a gateway). The async twin is `aio.AsyncFallbackClient` ([aimu/aio/fallback.py](aimu/aio/fallback.py)). Exported from `aimu`, `aimu.models`, and `aimu.aio`.

docs/reference/api/models.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Provider-agnostic model clients.
2424

2525
::: aimu.models.FallbackExhaustedError
2626

27+
::: aimu.models.ModelConnectionError
28+
2729
## Provider clients
2830

2931
::: aimu.models.OllamaClient

0 commit comments

Comments
 (0)