You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
Copy file name to clipboardExpand all lines: CLAUDE.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,7 +36,7 @@ These six principles drive every architectural decision in AIMU. When proposing
36
36
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.
37
37
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.
38
38
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`.
40
40
41
41
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.
42
42
@@ -1086,6 +1086,8 @@ Networked model clients accept `timeout: Optional[float]` and `max_retries: Opti
1086
1086
-**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.
1087
1087
- 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`.
1088
1088
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
+
1089
1091
### Fallback / failover (`FallbackClient`)
1090
1092
1091
1093
`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`.
0 commit comments