Skip to content

feat: publish typed JSON Schemas for llm-router + provider iii functions - #270

Closed
ytallo wants to merge 1 commit into
mainfrom
feat/router-provider-typed-fn-schemas
Closed

feat: publish typed JSON Schemas for llm-router + provider iii functions#270
ytallo wants to merge 1 commit into
mainfrom
feat/router-provider-typed-fn-schemas

Conversation

@ytallo

@ytallo ytallo commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Why

The API reference at workers.iii.dev/workers/<worker>?tab=api renders "unknown" for llm-router, provider-anthropic, and provider-openai.

Root cause: every iii function in these workers is registered with a Fn(Value) -> Value handler. The SDK auto-extracts request/response schemas from the handler's argument/return types, but serde_json::Value produces the permissive AnyValue schema (no structure), so the registry has nothing concrete to render. (approval-gate renders fine because it uses typed Fn(Req) -> Resp handlers — this brings the router family to parity.)

What

Attach precise request/response JSON Schemas at registration time via the SDK's request_format/response_format, without changing any handler dispatch. The handlers stay on Value, so every tolerant parse, streaming sink, bare-null answer, and error contract is byte-for-byte preserved.

  • llm-router
    • Derive JsonSchema across the wire type graph (events/messages/content/model/credential/router).
    • New wire_schema module: schema_of::<T>() (mirrors the SDK's draft-07 generator) + with_schemas::<Req, Resp>(reg).
    • Wrap all 14 router::* registrations. router::models::get publishes its real { model } | null union; the engine-trigger handlers (on_worker_available, on_config_changed) publish a null response.
    • The precise request types already existed (ChatRequest, ChatResponse, CompleteResponse, …); added small schema-only types for the previously-inline shapes (RouteRequest/Response, ModelsListRequest, etc.) and the shared provider-protocol acks.
  • provider-anthropic / provider-openai
    • Wrap the 3 provider::* registrations each, reusing llm_router's helper and the shared ProviderStreamInput / ProviderAck / RefreshModelsAck / NoParams types. No direct schemars dep needed (the lockfile change is the transitive pickup via the path-dep).

schemars is pinned to 0.8 — the same major as iii-sdk 0.19.2 — so the emitted schemas are byte-identical to what a typed SDK handler would auto-extract, and there's a single JsonSchema trait across the graph.

Behavior / risk

Schema-metadata only. request_format/response_format are descriptive registry metadata; the engine never validates payloads against them at invoke time, and the handler closures are untouched. An adversarial review confirmed dispatch is unchanged (handlers still take Value; no serde attribute changed).

Tests

Golden tests/schemas.rs in each crate asserts every published request/response type renders a structured schema (never the AnyValue true), pins key fields (ChatRequest carries writer_ref; CompleteRequest does not), and checks the {model}|null and null-response shapes. All suites green: llm-router 61, provider-anthropic 78, provider-openai 65 (incl. the existing engine-backed integration tests). cargo fmt + clippy -D warnings clean on all three.

Release note for the operator

No Cargo.toml versions are bumped here (the create-tag workflow does that). Published schemas are collected by booting the worker at release time, and release.yml publishes one worker per tag — so after merge, run Create Tag → bump patch for all three workers (llm-router, then provider-anthropic, provider-openai) or the providers will keep serving their previously-registered (stale) schemas.

Summary by CodeRabbit

  • New Features

    • JSON schema publishing is now available for all API request and response types, enabling explicit documentation of wire contracts and improved tooling integration.
  • Tests

    • Added comprehensive schema validation tests to ensure API contracts are properly documented and structured.

…JSON Schemas for the iii function surface

Every iii function in these workers was registered with a
`Fn(Value) -> Value` handler, so the SDK auto-extracted the permissive
`AnyValue` schema and the workers.iii.dev API reference rendered
request/response as "unknown".

Attach precise request/response JSON Schemas at registration via
`request_format`/`response_format`, without changing any handler:

- llm-router: derive `JsonSchema` across the wire type graph
  (events/messages/content/model/credential/router), add a `wire_schema`
  helper (`schema_of` + `with_schemas`), and wrap all 14 `router::*`
  registrations. `router::models::get` publishes its `{model}|null`
  union; the trigger handlers publish a null response.
- provider-anthropic / provider-openai: wrap the 3 `provider::*`
  registrations, reusing the shared protocol types and helper from
  llm-router (no direct schemars dep needed).

Handlers stay on `Value`, so tolerant parsing, streaming sinks, and the
`router/invalid_request` + `provider/invalid_request` error contracts are
unchanged. `schemars` is pinned to the same major as iii-sdk so the
emitted schemas match the SDK's own draft-07 settings. Golden tests in
each crate lock the published surface.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jun 16, 2026 3:25pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 22 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Draft-07 JSON schema publishing to the llm-router wire protocol. A new wire_schema module provides schema_of<T>() and with_schemas<Req, Resp>() helpers. All public router and provider types gain schemars::JsonSchema derives. Every function registration in the router and Anthropic/OpenAI provider crates is wrapped with explicit schema bindings, and schema-shape regression tests are added across all three crates.

Changes

Wire Schema Publishing for llm-router

Layer / File(s) Summary
wire_schema module and dependency
llm-router/Cargo.toml, llm-router/src/lib.rs, llm-router/src/wire_schema.rs
Adds schemars = "0.8" dependency, exposes pub mod wire_schema, and implements schema_of<T>() (Draft-07 schema → serde_json::Value) and with_schemas<Req, Resp>() (attaches request/response schemas to a RegisterFunction).
JsonSchema derives on all public types
llm-router/src/types/content.rs, llm-router/src/types/credential.rs, llm-router/src/types/events.rs, llm-router/src/types/messages.rs, llm-router/src/types/model.rs, llm-router/src/types/router.rs, llm-router/src/registry/register.rs
Adds schemars::JsonSchema to the derive list of all public router/provider types including ContentBlock, Credential, streaming event types, all message types, model descriptor types, and every request/response/event struct in router.rs. RegisterInput is also made public with JsonSchema derive.
Router function registration with schema wiring
llm-router/src/register.rs
Refactors all iii.register_function calls to use with_schemas, including a special anyOf+null response format for router::models::get and a schema-typed router::on_config_changed registration.
Provider registration with schema wiring
provider-anthropic/src/register.rs, provider-openai/src/register.rs
Wraps stream, refresh_models, and on_router_ready registrations with with_schemas using typed ProviderStreamInput/ProviderAck, NoParams/RefreshModelsAck, and NoParams/ProviderAck pairs in both provider crates.
Schema regression tests
llm-router/tests/schemas.rs, provider-anthropic/tests/schemas.rs, provider-openai/tests/schemas.rs
Asserts all published schemas are structured JSON objects (not AnyValue), verifies field presence for ChatRequest, CompleteRequest, and ProviderStreamInput, and confirms schema_of::<()>() emits a null-typed schema.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • iii-hq/workers#252: Directly related — updates provider-openai/src/register.rs to register OpenAI endpoints using with_schemas (e.g., NoParamsProviderAck), which is the same schema-binding mechanism introduced in this PR.

Suggested reviewers

  • sergiofilhowz

Poem

🐇 Hop, hop, the schemas are here!
No more AnyValue — structure is clear.
With schema_of and with_schemas in hand,
Draft-07 JSON shapes across the land.
Every request and response now defined,
A typed wire protocol, perfectly aligned! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: publish typed JSON Schemas for llm-router + provider iii functions' is clear, concise, and accurately describes the main change—adding typed JSON schemas across three workers for API reference rendering.
Docstring Coverage ✅ Passed Docstring coverage is 95.24% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/router-provider-typed-fn-schemas

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@provider-anthropic/tests/schemas.rs`:
- Around line 32-34: The `on_router_ready` handler test currently only asserts
the response schema (ProviderAck) but is missing the request schema assertion
(NoParams). Add an assertion for the request schema before the existing response
assertion in both affected files. In `provider-anthropic/tests/schemas.rs` at
lines 32-34, insert `assert_structured(&schema_of::<NoParams>(),
"on_router_ready req");` before the existing
`assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");` line.
Apply the identical change in `provider-openai/tests/schemas.rs` at lines 32-34
to ensure both provider test suites consistently validate both request and
response schemas for the `on_router_ready` registration contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b415feaa-3bf9-4803-aa18-8b826aa04862

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb91e8 and 0674de0.

⛔ Files ignored due to path filters (3)
  • llm-router/Cargo.lock is excluded by !**/*.lock
  • provider-anthropic/Cargo.lock is excluded by !**/*.lock
  • provider-openai/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • llm-router/Cargo.toml
  • llm-router/src/lib.rs
  • llm-router/src/register.rs
  • llm-router/src/registry/register.rs
  • llm-router/src/types/content.rs
  • llm-router/src/types/credential.rs
  • llm-router/src/types/events.rs
  • llm-router/src/types/messages.rs
  • llm-router/src/types/model.rs
  • llm-router/src/types/router.rs
  • llm-router/src/wire_schema.rs
  • llm-router/tests/schemas.rs
  • provider-anthropic/src/register.rs
  • provider-anthropic/tests/schemas.rs
  • provider-openai/src/register.rs
  • provider-openai/tests/schemas.rs

Comment on lines +32 to +34
// provider::anthropic::on_router_ready
assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add request-schema assertions for on_router_ready in both provider test suites.

At Line 33 in each file, only the response (ProviderAck) is asserted. The registration contract for on_router_ready is request+response (NoParams/ProviderAck), so request-side regressions would currently pass unnoticed.

  • provider-anthropic/tests/schemas.rs#L32-L34: add assert_structured(&schema_of::<NoParams>(), "on_router_ready req"); before the response assertion.
  • provider-openai/tests/schemas.rs#L32-L34: add assert_structured(&schema_of::<NoParams>(), "on_router_ready req"); before the response assertion.
Suggested patch
--- a/provider-anthropic/tests/schemas.rs
+++ b/provider-anthropic/tests/schemas.rs
@@
     // provider::anthropic::on_router_ready
+    assert_structured(&schema_of::<NoParams>(), "on_router_ready req");
     assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");
--- a/provider-openai/tests/schemas.rs
+++ b/provider-openai/tests/schemas.rs
@@
     // provider::openai::on_router_ready
+    assert_structured(&schema_of::<NoParams>(), "on_router_ready req");
     assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// provider::anthropic::on_router_ready
assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");
}
// provider::anthropic::on_router_ready
assert_structured(&schema_of::<NoParams>(), "on_router_ready req");
assert_structured(&schema_of::<ProviderAck>(), "on_router_ready resp");
}
📍 Affects 2 files
  • provider-anthropic/tests/schemas.rs#L32-L34 (this comment)
  • provider-openai/tests/schemas.rs#L32-L34
🤖 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 `@provider-anthropic/tests/schemas.rs` around lines 32 - 34, The
`on_router_ready` handler test currently only asserts the response schema
(ProviderAck) but is missing the request schema assertion (NoParams). Add an
assertion for the request schema before the existing response assertion in both
affected files. In `provider-anthropic/tests/schemas.rs` at lines 32-34, insert
`assert_structured(&schema_of::<NoParams>(), "on_router_ready req");` before the
existing `assert_structured(&schema_of::<ProviderAck>(), "on_router_ready
resp");` line. Apply the identical change in `provider-openai/tests/schemas.rs`
at lines 32-34 to ensure both provider test suites consistently validate both
request and response schemas for the `on_router_ready` registration contract.


/// Content blocks — the atomic units of message content (README § Content blocks).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we remove the schemars:: and add it to the import?

@ytallo ytallo closed this Jun 16, 2026
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