Skip to content

feat(protocol): Add an error type#134

Merged
grahamking merged 5 commits into
mainfrom
gk-protocol-err-type
Jul 24, 2026
Merged

feat(protocol): Add an error type#134
grahamking merged 5 commits into
mainfrom
gk-protocol-err-type

Conversation

@grahamking

@grahamking grahamking commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The RoutedLlmClient implementations now have a concrete error enum to
return. This allows the caller to identify what happened. No more
downcast. Particularly, it allows the caller to identify timeouts
separately from other errors.

  • Before RoutedLlmClient::call returned Result<Response, Box<dyn Error + Send + Sync>>
  • Now it returns Result<Response, LlmClientError>

Which has these variants (see code for messages, this is edited) (note some more added in later commit):

pub enum LlmClientError {
    InvalidRequest { message: String },
    Configuration { message: String },
    Transport { #[source] source: BoxError},
    Timeout { #[source] source: BoxError },
    ContextWindowExceeded { .. },
    UpstreamHttp { status: u16, body: String, },
    InvalidResponse { #[source] source: BoxError },
    Other(#[from] BoxError),
}

libsy-llm-client uses this error type directly.

Signed-off-by: Graham King grahamk@nvidia.com
Assisted-by: Codex:GPT 5.6 Sol medium

The RoutedLlmClient implementations now have a concrete error `enum` to
return. This allows the caller to identify what happened. No more
`downcast`. Particularly, it allows the caller to identify timeouts
separately from other errors.

- Before RoutedLlmClient::call returned `Result<Response, Box<dyn Error + Send + Sync>>`
- Now it returns `Result<Response, RoutedLlmClientError>`

Which has these variants (see code for messages, this is edited):

```
pub enum RoutedLlmClientError {
    InvalidRequest { message: String },
    Configuration { message: String },
    Transport { #[source] source: BoxError},
    Timeout { #[source] source: BoxError },
    ContextWindowExceeded { .. },
    UpstreamHttp { status: u16, body: String, },
    InvalidResponse { #[source] source: BoxError },
    Other(#[from] BoxError),
}
```

Signed-off-by: Graham King <grahamk@nvidia.com>
Assisted-by: Codex:GPT 5.6 Sol medium
@grahamking

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Thanks Claude!

Signed-off-by: Graham King <grahamk@nvidia.com>
@grahamking
grahamking marked this pull request as ready for review July 24, 2026 15:43
@grahamking
grahamking requested a review from a team as a code owner July 24, 2026 15:43
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR replaces boxed routed-client errors with a typed RoutedLlmClientError, adds timeout and request/response translation classification, propagates the type through libsy and client implementations, and updates Python and server error handling.

Changes

Typed Routed Client Error Flow

Layer / File(s) Summary
Protocol error contract
crates/protocol/Cargo.toml, crates/protocol/src/client.rs
Adds RoutedLlmClientError and changes RoutedLlmClient::call to return the structured error type.
LLM error classification and conversion
crates/libsy-llm-client/src/error.rs, crates/libsy-llm-client/src/client.rs, crates/libsy-llm-client/README.md
Separates request and response translation failures, adds timeout handling, changes transport errors to wrap reqwest::Error, and maps client errors into routed errors.
libsy error propagation and documentation
crates/libsy/src/error.rs, crates/libsy/src/core/algorithm.rs, crates/libsy/README.md
Stores RoutedLlmClientError in LibsyError::ClientCall, updates the constructor and examples, and re-exports Usage.
Client implementations and test mocks
crates/switchyard-py/src/libsy_bindings.rs, crates/libsy/examples/*, crates/libsy/src/algorithms/*, crates/libsy/src/core/algorithm.rs, crates/libsy/tests/observability.rs
Updates Python-backed, example, algorithm, and observability clients to use the typed error return.
Server HTTP error mapping
crates/switchyard-server/src/lib.rs
Matches RoutedLlmClientError directly when producing HTTP statuses and error codes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

I’m a rabbit with errors neatly tied,
Typed little carrots hop side to side.
Timeout thumps softly, transport stays clear,
Requests and responses now know where to steer.
The server translates each wiggle and cheer.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing a new protocol error type.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/switchyard-server/src/lib.rs (1)

366-409: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Fallback branch leaks the internal target name; use source.to_string() for consistency.

Every explicit arm here renders just the routed error's own message. The catch-all (RoutedLlmClientError::Other and any future unmatched variant) instead renders error.to_string() on the outer LibsyError, whose #[error("client call to target {target:?} failed: {source}")] format embeds the internal target/model alias into the HTTP response body — inconsistent with the rest of the mapping and an avoidable bit of internal detail exposed to API callers.

🔒 Suggested fix
-        _ => server_error(error.to_string()),
+        _ => server_error(source.to_string()),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/switchyard-server/src/lib.rs` around lines 366 - 409, Update the
catch-all branch in algorithm_error to return server_error(source.to_string())
instead of error.to_string(), matching the other RoutedLlmClientError arms and
avoiding exposure of the outer LibsyError target name.
🧹 Nitpick comments (1)
crates/libsy-llm-client/src/client.rs (1)

217-224: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Distinguish decode failures from body-read errors

Response::json() can fail on malformed JSON (is_decode()) or a body I/O problem (is_body()), but both currently fall through to ResponseTranslation("invalid upstream JSON: ..."). Split those cases so transport failures stay in Transport and only actual JSON parse errors land in ResponseTranslation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/libsy-llm-client/src/client.rs` around lines 217 - 224, Update the
`http_response.json::<Value>()` error mapping in the response-handling branch to
distinguish body I/O failures from JSON decode failures: preserve timeout
mapping, map `error.is_body()` failures to `LlmClientError::Transport`, and map
only `error.is_decode()` failures to `LlmClientError::ResponseTranslation`. Keep
the existing invalid-JSON context for decode errors and handle any remaining
error variant consistently with the client’s existing error model.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/switchyard-server/src/lib.rs`:
- Around line 366-409: Update the catch-all branch in algorithm_error to return
server_error(source.to_string()) instead of error.to_string(), matching the
other RoutedLlmClientError arms and avoiding exposure of the outer LibsyError
target name.

---

Nitpick comments:
In `@crates/libsy-llm-client/src/client.rs`:
- Around line 217-224: Update the `http_response.json::<Value>()` error mapping
in the response-handling branch to distinguish body I/O failures from JSON
decode failures: preserve timeout mapping, map `error.is_body()` failures to
`LlmClientError::Transport`, and map only `error.is_decode()` failures to
`LlmClientError::ResponseTranslation`. Keep the existing invalid-JSON context
for decode errors and handle any remaining error variant consistently with the
client’s existing error model.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 20cc504e-468d-44cc-a241-56001748c9af

📥 Commits

Reviewing files that changed from the base of the PR and between 5a142c8 and b1a6f42.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock
📒 Files selected for processing (17)
  • crates/libsy-llm-client/README.md
  • crates/libsy-llm-client/src/client.rs
  • crates/libsy-llm-client/src/error.rs
  • crates/libsy/README.md
  • crates/libsy/examples/ensemble.rs
  • crates/libsy/examples/research_agent.rs
  • crates/libsy/src/algorithms/fall_through.rs
  • crates/libsy/src/algorithms/llm_class.rs
  • crates/libsy/src/algorithms/rand.rs
  • crates/libsy/src/algorithms/subagent_override.rs
  • crates/libsy/src/core/algorithm.rs
  • crates/libsy/src/error.rs
  • crates/libsy/tests/observability.rs
  • crates/protocol/Cargo.toml
  • crates/protocol/src/client.rs
  • crates/switchyard-py/src/libsy_bindings.rs
  • crates/switchyard-server/src/lib.rs

Signed-off-by: Graham King <grahamk@nvidia.com>
@grahamking grahamking changed the title feat(libsy-llm-client): Add an error type feat(protocol): Add an error type Jul 24, 2026
Comment thread crates/libsy-llm-client/src/client.rs Outdated
Signed-off-by: Graham King <grahamk@nvidia.com>
Signed-off-by: Graham King <grahamk@nvidia.com>
@grahamking
grahamking merged commit 980619a into main Jul 24, 2026
16 checks passed
@grahamking
grahamking deleted the gk-protocol-err-type branch July 24, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants