Skip to content

refactor(rust-sdk)!: simplify register_function — 3 constructors + IIIError handlers#1578

Open
guibeira wants to merge 17 commits into
mainfrom
align-rust-sdk
Open

refactor(rust-sdk)!: simplify register_function — 3 constructors + IIIError handlers#1578
guibeira wants to merge 17 commits into
mainfrom
align-rust-sdk

Conversation

@guibeira
Copy link
Copy Markdown
Contributor

@guibeira guibeira commented Apr 29, 2026

Summary

Collapses the Rust SDK's function-registration surface to a single entry point that mirrors Node and Python:

iii.register_function(
    "http::fetch",
    RegisterFunction::new_async(fetch).description("Fetches a URL"),
);
SDK Signature
Node registerFunction(functionId, handler, options?)
Python register_function(function_id, handler, *, description, metadata, request_format, response_format)
Rust register_function(id, RegisterFunction)

RegisterFunction carries the handler plus all optional metadata. There are three constructors:

Constructor When to use
RegisterFunction::new(f) Sync handler — typed (schemars-extracted schemas) OR Fn(Value) -> Result<Value, IIIError>
RegisterFunction::new_async(f) Async equivalent of new — same dual support
RegisterFunction::http(config) HTTP-invoked function (Lambda, Cloudflare Workers, etc.) — no local handler

Value implements JsonSchema (via schemars), so untyped handlers go through new / new_async and emit a permissive AnyValue schema. No separate untyped constructor needed.

Builder methods (chainable, consume self): .description(s), .metadata(v), .request_format(schema), .response_format(schema). Setters override any auto-extracted schema.

Handler error type fixed to IIIError

To enable clean type inference for Fn(Value) -> ... closures (where Ok(...) alone left the error type unbound), the handler bound is fixed:

// Before
F: Fn(T) -> Result<R, E>
E: Display + Send + 'static

// After
F: Fn(T) -> Result<R, IIIError>

To smooth migration, IIIError now implements From<String> and From<&str> (alongside the existing From<serde_json::Error> and From<tungstenite::Error>). Existing handlers can:

  • Return Result<R, IIIError> and use IIIError::Handler(s) directly, OR
  • Use ?-propagation with Err(\"oops\")? / Err(\"oops\".to_string())?, OR
  • Migrate Result<R, String> to Result<R, IIIError> with no body changes (string literals work as errors via From<&str>).

What was removed

Removed Replacement
III::register_function_with(id, handler, options) register_function(id, RegisterFunction::new_async(handler).description(...))
register_function((RegisterFunctionMessage, handler)) tuple form register_function(id, RegisterFunction::new_async(handler))
RegisterFunction::untyped(f) RegisterFunction::new_async(f) (now accepts Value closures directly)
RegisterFunctionOptions Builder methods on RegisterFunction
IntoFunctionRegistration trait Single concrete RegisterFunction argument
IntoFunctionHandler trait + impls Each registration kind has its own RegisterFunction::* constructor
iii_fn, iii_async_fn, IIIFn, IIIAsyncFn RegisterFunction::new / RegisterFunction::new_async
Handler bound E: Display Bound is Result<_, IIIError>; use From<String> / From<&str> to lift

In-tree migration

All call sites migrated:

  • sdk/packages/rust/iii/tests/*.rs — integration tests (~57 sites).
  • sdk/packages/rust/iii-example/src/*.rs — example crate; Result<R, String> -> Result<R, IIIError>.
  • console/packages/console-rust/src/bridge/functions.rs — 33 sites; reg_fn_msg helper retired.
  • engine/src/workers/{bridge_client,queue/adapters/bridge,stream/adapters/bridge}.rs — 3 sites.
  • crates/iii-worker/src/sandbox_daemon/{mod,fs/*}.rs — 14 sites.
  • skills/iii-rust-sdk/SKILL.md, skills/references/http-invoked-functions.rs — teaching references.
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx — rewritten with the final shape.
  • docs/api-reference/sdk-rust.mdx — regenerated.

BREAKING CHANGE

register_function_with, RegisterFunctionOptions, IntoFunctionRegistration, IntoFunctionHandler, iii_fn, iii_async_fn, IIIFn, IIIAsyncFn, the (RegisterFunctionMessage, handler) tuple form, and the untyped constructor are all removed. Handler error type must be IIIError. See the changelog entry for the migration table.

Test plan

  • CI: 31 checks SUCCESS, 2 NEUTRAL (Mintlify link-rot, pre-existing)
  • `cargo test -p iii-sdk --lib` — 64/64 passing
  • `cargo test -p iii-sdk --doc` — 16/16 passing
  • `cargo build --workspace --all-targets` — clean
  • `cargo fmt --all -- --check` — clean
  • `cargo clippy -p iii-sdk -p iii-console -p iii-example --all-targets -- -D warnings` — clean
  • `rg "register_function_with|RegisterFunctionMessage::with_id|RegisterFunctionOptions|IntoFunctionRegistration|IntoFunctionHandler|iii_fn|iii_async_fn|::untyped"` returns no source matches outside legacy 0-10-0 docs

Notes

  • Pre-existing flaky engine tests (builtins::kv::test_builtin_kv_lock_ttl_expiry, test_file_based_preserves_insertion_order) fail under workspace-wide test load due to tight 10-20 ms timing windows; pass when run in isolation. Unrelated to this branch and pass in CI.
  • Pre-existing clippy warnings in crates/iii-supervisor/src/shell_protocol.rs (`set_len` after reserve) and engine code are not enforced by CI for those crates and are unrelated to this branch.
  • Out of scope follow-ups: rename `id` -> `function_id` for full parameter-name parity with Python; fix `scripts/generate-api-docs.sh` step 3 which silently fails because rustdoc-JSON now needs `-Z unstable-options`.

Summary by CodeRabbit

  • New Features

    • Simplified function registration: pass an id plus a registration builder with metadata, request/response formats, and HTTP registration helpers.
  • Bug Fixes

    • Unified handler error type across the SDK and added conversions to simplify error propagation.
  • Documentation

    • Expanded API reference (streaming channels, triggers, OTLP exporters), updated examples and migration guide/changelog.
  • Tests

    • Many tests migrated to the new registration API; some legacy registration tests removed.
  • Chores

    • Doc-generation logic adjusted to retain types referenced in narratives/examples.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 29, 2026

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

Project Deployment Actions Updated (UTC)
iii-website Ready Ready Preview, Comment May 13, 2026 0:50am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Migrate Rust function registration to an id-first API: III::register_function(id, RegisterFunction). Handlers must return Result<_, IIIError>. Update call sites, examples, docs, doc-generation, and tests; remove legacy registration helpers and some legacy tests.

Changes

Register API core

Layer / File(s) Summary
Core registration, handler types, and error conversions
sdk/packages/rust/iii/src/iii.rs, sdk/packages/rust/iii/src/lib.rs, sdk/packages/rust/iii/src/error.rs
Introduce RegisterFunction::{new,new_async,http} and change III::register_function to accept (id, RegisterFunction); require handlers return Result<_, IIIError>; remove legacy handler/registration adapters/macros; add From<String> and From<&str> for IIIError.

Callsites, bridges, and worker modules

Layer / File(s) Summary
Console, engine, queue, stream, and bridge registrations
console/packages/console-rust/src/bridge/functions.rs, console/packages/console-rust/src/main.rs, engine/src/workers/bridge_client/mod.rs, engine/src/workers/queue/adapters/bridge.rs, engine/src/workers/stream/adapters/bridge.rs
Replace tuple/RegisterFunctionMessage registration with bridge.register_function("<id>", RegisterFunction::new_async(...)); adjust closures to accept serde_json::Value where applicable; remove RegisterFunctionMessage imports.
Worker sandbox and daemon registrations
crates/iii-worker/src/sandbox_daemon/..., crates/iii-worker/src/sandbox_daemon/mod.rs, crates/iii-worker/src/cli/worker_manager_daemon.rs
Migrate all sandbox function registrations to id-first register_function with RegisterFunction::new_async(handler) and update worker trigger handlers to use IIIError-typed error paths; simplify imports.
Examples and reference implementations
sdk/packages/rust/iii-example/src/*, skills/references/http-invoked-functions.rs
Update examples to call iii.register_function(id, RegisterFunction::new/_async(...)); HTTP functions use RegisterFunction::http(...); example handlers return Result<_, IIIError>.

Docs, changelog, and doc-generation

Layer / File(s) Summary
Rust/Node/Python API docs and changelog
docs/api-reference/sdk-rust.mdx, docs/api-reference/sdk-python.mdx, docs/api-reference/sdk-node.mdx, docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx, skills/iii-rust-sdk/SKILL.md
Update API reference and examples to document id-first register_function(id, RegisterFunction) signature and RegisterFunction builder; add changelog; update telemetry/docs wording and types (add/remove entries as noted).
Doc tooling: generator & parser
docs/scripts/generate-api-docs.mts, docs/scripts/parsers/parse-rustdoc.mts
Expand doc-generation scanning to include descriptions and examples when resolving referenced types; whitelist RegisterFunctionOptions for extraction.

Tests

Layer / File(s) Summary
Migration of integration/unit tests
sdk/packages/rust/iii/tests/* (many files: api_triggers.rs, bridge.rs, data_channels.rs, healthcheck.rs, http_external_functions.rs, middleware.rs, pubsub.rs, queue_integration.rs, rbac_workers.rs, state.rs, etc.)
Update tests to use iii.register_function("<id>", RegisterFunction::new_async(...)); adjust imports to remove RegisterFunctionMessage and add RegisterFunction; update handler error types to IIIError where applicable.
Removed legacy tests
sdk/packages/rust/iii/tests/iii_fn_test.rs, sdk/packages/rust/iii/tests/register_function_test.rs
Delete legacy test modules that covered iii_fn/iii_async_fn adapters and prior registration/schema-generation expectations.

Sequence Diagram(s)

sequenceDiagram
    participant Worker
    participant III_SDK as "III (SDK)"
    participant Engine
    participant Remote as "Remote/Invoker"

    rect rgba(200,230,201,0.5)
    Worker->>III_SDK: iii.register_function("id", RegisterFunction::new_async(handler))
    III_SDK->>Engine: send RegisterFunctionMessage(id, registration)
    end

    rect rgba(187,222,251,0.5)
    Remote->>Engine: invoke function(id, payload)
    Engine->>III_SDK: route invocation to registered handler
    III_SDK->>Worker: call handler(payload)
    Worker-->>III_SDK: Result<Value, IIIError>
    III_SDK-->>Engine: map to invocation response
    Engine-->>Remote: return response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sergiofilhowz

"🐰
I hopped through code both old and new,
Swapped messages for IDs, tidy and true.
Handlers now return IIIError with grace,
RegisterFunction leads in its rightful place.
A tiny dance, a cleaner API view."

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: simplifying the Rust SDK's register_function API with 3 RegisterFunction constructors and IIIError as the fixed handler error type.
Description check ✅ Passed The PR description thoroughly covers the What, Why, and implementation details with migration examples, removed APIs, test results, and breaking change notices, though it lacks the explicit Apache 2 license checkbox from the template.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 align-rust-sdk

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.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented Apr 29, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
iii 🟢 Ready View Preview Apr 29, 2026, 9:29 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx`:
- Line 13: Update the table entry for the Python API so the signature for
register_function matches the real API: rename the second parameter from handler
to handler_or_invocation and remove the keyword-only marker "*" so it reads
register_function(function_id, handler_or_invocation, description=None,
metadata=None, request_format=None, response_format=None) (locate the Python
signature line for register_function in the changelog file to make this edit).
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f3a1a312-be07-4968-b461-2865796d5ece

📥 Commits

Reviewing files that changed from the base of the PR and between 969d003 and 651e1a0.

📒 Files selected for processing (12)
  • console/packages/console-rust/src/bridge/functions.rs
  • docs/api-reference/sdk-python.mdx
  • docs/api-reference/sdk-rust.mdx
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
  • docs/scripts/generate-api-docs.mts
  • docs/scripts/parsers/parse-rustdoc.mts
  • sdk/packages/rust/iii/src/iii.rs
  • sdk/packages/rust/iii/src/lib.rs
  • sdk/packages/rust/iii/src/protocol.rs
  • sdk/packages/rust/iii/tests/init_api.rs
  • skills/iii-rust-sdk/SKILL.md
  • skills/references/http-invoked-functions.rs

| SDK | Full-options signature |
| --- | --- |
| Node | `registerFunction(functionId, handler, options?)` |
| Python | `register_function(function_id, handler, *, description, metadata, request_format, response_format)` |
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify signature naming consistency between changelog and Python API reference
rg -n "register_function\\(" docs/api-reference/sdk-python.mdx docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx -C 2

Repository: iii-hq/iii

Length of output: 4347


Use Python's canonical handler_or_invocation parameter name and remove the keyword-only marker.

The Python signature in the comparison table should match the actual API documentation: the parameter is named handler_or_invocation (not handler), and since these optional parameters accept positional arguments with defaults, the * keyword-only marker is incorrect.

✏️ Suggested edit
-| Python | `register_function(function_id, handler, *, description, metadata, request_format, response_format)` |
+| Python | `register_function(function_id, handler_or_invocation, description, metadata, request_format, response_format)` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx` at
line 13, Update the table entry for the Python API so the signature for
register_function matches the real API: rename the second parameter from handler
to handler_or_invocation and remove the keyword-only marker "*" so it reads
register_function(function_id, handler_or_invocation, description=None,
metadata=None, request_format=None, response_format=None) (locate the Python
signature line for register_function in the changelog file to make this edit).

@guibeira guibeira marked this pull request as draft April 29, 2026 21:39
@guibeira guibeira changed the title feat(rust-sdk)!: align register_function_with with Node/Python order refactor(rust-sdk)!: single register_function entry point Apr 30, 2026
@guibeira guibeira changed the title refactor(rust-sdk)!: single register_function entry point refactor(rust-sdk)!: unified register_function API with RegisterFunction builder Apr 30, 2026
@guibeira guibeira marked this pull request as ready for review April 30, 2026 21:30
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
engine/src/workers/queue/adapters/bridge.rs (1)

184-255: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Strip bridge trace metadata before invoking the condition and target handler.

This closure reads __traceparent / __baggage and then forwards data unchanged into check_condition(...) and engine.call(...). That leaks the internal bridge envelope into user payloads and contradicts the contract documented in build_enqueue_payload().

Suggested fix
         RegisterFunction::new_async(move |data: Value| {
             let engine = Arc::clone(&engine);
             let function_id = function_id_owned.clone();
             let condition_function_id = condition_function_id_owned.clone();
             let topic_name = topic_owned.clone();
             async move {
-                // Extract trace context embedded by the sender
-                let traceparent = data["__traceparent"].as_str().map(|s| s.to_string());
-                let baggage = data["__baggage"].as_str().map(|s| s.to_string());
+                // Extract trace context embedded by the sender
+                let mut payload = data;
+                let traceparent = payload
+                    .get("__traceparent")
+                    .and_then(Value::as_str)
+                    .map(str::to_owned);
+                let baggage = payload
+                    .get("__baggage")
+                    .and_then(Value::as_str)
+                    .map(str::to_owned);
+                if let Some(obj) = payload.as_object_mut() {
+                    obj.remove("__traceparent");
+                    obj.remove("__baggage");
+                }
 
                 let span = tracing::info_span!(
                     "queue_job",
@@
-                            match check_condition(engine.as_ref(), &condition_path, data.clone())
+                            match check_condition(engine.as_ref(), &condition_path, payload.clone())
                                 .await
                             {
@@
-                        match engine.call(&function_id, data).await {
+                        match engine.call(&function_id, payload).await {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@engine/src/workers/queue/adapters/bridge.rs` around lines 184 - 255, The
closure registered with iii_sdk::RegisterFunction::new_async reads __traceparent
and __baggage but passes the original data through to check_condition(...) and
engine.call(...), leaking bridge envelope fields; fix by removing those keys
from the payload before invoking check_condition and engine.call (e.g., create a
cleaned Value without "__traceparent" and "__baggage" and use that cleaned value
for check_condition(engine.as_ref(), &condition_path, cleaned.clone()) and
engine.call(&function_id, cleaned).await), keeping the extracted
traceparent/baggage for span creation only and referencing the RegisterFunction
closure, check_condition, engine.call, and build_enqueue_payload in your
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx`:
- Around line 78-80: The migration table incorrectly points sync handler usages
to RegisterFunction::new_async(...) — update the rows for synchronous
replacements so they reference RegisterFunction::new(...) instead of new_async;
specifically change the replacements for III::register_function_with(id,
handler, options), the tuple form register_function((RegisterFunctionMessage,
handler)), and RegisterFunction::untyped(f) so that sync handler variants map to
RegisterFunction::new(...) while keeping async variants mapped to
RegisterFunction::new_async(...); apply the same correction for the other
occurrences noted around lines 105-110 to keep argument shapes consistent across
SDKs.

In `@sdk/packages/rust/iii/src/iii.rs`:
- Around line 765-773: The docs for register_function erroneously list
RegisterFunction::new_async twice; update the doc comment for the
register_function item so the constructor list reads the three actual
constructors: RegisterFunction::new, RegisterFunction::new_async, and
RegisterFunction::http (remove the duplicated RegisterFunction::new_async
entry), and verify the RegisterFunction doc string and any cross-SDK references
use those exact names for consistency.

In `@sdk/packages/rust/iii/tests/queue_integration.rs`:
- Around line 356-357: The test registers functions using dot-separated IDs
derived from function_id and RegisterFunction::new_async; update all such base
ID strings (e.g., "test.queue...") to use double-colon separators
("test::queue::...") so durable test function IDs follow the repo standard;
locate occurrences around RegisterFunction::new_async and any places where
function_id.clone() or explicit string IDs are built and replace dots with "::"
consistently (also update the other noted instances where
RegisterFunction::new_async is used).

---

Outside diff comments:
In `@engine/src/workers/queue/adapters/bridge.rs`:
- Around line 184-255: The closure registered with
iii_sdk::RegisterFunction::new_async reads __traceparent and __baggage but
passes the original data through to check_condition(...) and engine.call(...),
leaking bridge envelope fields; fix by removing those keys from the payload
before invoking check_condition and engine.call (e.g., create a cleaned Value
without "__traceparent" and "__baggage" and use that cleaned value for
check_condition(engine.as_ref(), &condition_path, cleaned.clone()) and
engine.call(&function_id, cleaned).await), keeping the extracted
traceparent/baggage for span creation only and referencing the RegisterFunction
closure, check_condition, engine.call, and build_enqueue_payload in your
changes.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3a3ccc2f-3e22-40da-98c6-5be9bb31c821

📥 Commits

Reviewing files that changed from the base of the PR and between 651e1a0 and 1135fe6.

📒 Files selected for processing (42)
  • console/packages/console-rust/src/bridge/functions.rs
  • console/packages/console-rust/src/main.rs
  • crates/iii-worker/src/sandbox_daemon/fs/chmod.rs
  • crates/iii-worker/src/sandbox_daemon/fs/grep.rs
  • crates/iii-worker/src/sandbox_daemon/fs/ls.rs
  • crates/iii-worker/src/sandbox_daemon/fs/mkdir.rs
  • crates/iii-worker/src/sandbox_daemon/fs/mv.rs
  • crates/iii-worker/src/sandbox_daemon/fs/read.rs
  • crates/iii-worker/src/sandbox_daemon/fs/rm.rs
  • crates/iii-worker/src/sandbox_daemon/fs/sed.rs
  • crates/iii-worker/src/sandbox_daemon/fs/stat.rs
  • crates/iii-worker/src/sandbox_daemon/fs/write.rs
  • crates/iii-worker/src/sandbox_daemon/mod.rs
  • docs/api-reference/sdk-rust.mdx
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
  • engine/src/workers/bridge_client/mod.rs
  • engine/src/workers/queue/adapters/bridge.rs
  • engine/src/workers/stream/adapters/bridge.rs
  • sdk/packages/rust/iii-example/src/cron_trigger_example.rs
  • sdk/packages/rust/iii-example/src/custom_trigger_example.rs
  • sdk/packages/rust/iii-example/src/http_example.rs
  • sdk/packages/rust/iii-example/src/logger_example.rs
  • sdk/packages/rust/iii-example/src/main.rs
  • sdk/packages/rust/iii-example/src/trigger_type_example.rs
  • sdk/packages/rust/iii/src/error.rs
  • sdk/packages/rust/iii/src/iii.rs
  • sdk/packages/rust/iii/src/lib.rs
  • sdk/packages/rust/iii/tests/api_triggers.rs
  • sdk/packages/rust/iii/tests/bridge.rs
  • sdk/packages/rust/iii/tests/data_channels.rs
  • sdk/packages/rust/iii/tests/healthcheck.rs
  • sdk/packages/rust/iii/tests/http_external_functions.rs
  • sdk/packages/rust/iii/tests/iii_fn_test.rs
  • sdk/packages/rust/iii/tests/init_api.rs
  • sdk/packages/rust/iii/tests/middleware.rs
  • sdk/packages/rust/iii/tests/pubsub.rs
  • sdk/packages/rust/iii/tests/queue_integration.rs
  • sdk/packages/rust/iii/tests/rbac_workers.rs
  • sdk/packages/rust/iii/tests/register_function_test.rs
  • sdk/packages/rust/iii/tests/state.rs
  • skills/iii-rust-sdk/SKILL.md
  • skills/references/http-invoked-functions.rs
💤 Files with no reviewable changes (3)
  • console/packages/console-rust/src/main.rs
  • sdk/packages/rust/iii/tests/iii_fn_test.rs
  • sdk/packages/rust/iii/tests/register_function_test.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • sdk/packages/rust/iii/tests/init_api.rs
  • skills/iii-rust-sdk/SKILL.md
  • console/packages/console-rust/src/bridge/functions.rs

Comment thread docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx Outdated
Comment thread sdk/packages/rust/iii/src/iii.rs Outdated
Comment thread sdk/packages/rust/iii/tests/queue_integration.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sdk/packages/rust/iii/src/iii.rs`:
- Around line 372-376: The code currently converts serde_json errors from
serde_json::from_value and serde_json::to_value into IIIError::Handler, losing
the distinction between user handler failures and serialization errors; update
the map_err closures around serde_json::from_value::<T>(input) and
serde_json::to_value(&val) to return IIIError::Serde(e.to_string()) instead of
IIIError::Handler, and make the same change in the other adapter branch that
handles serde_json failures (the block around the second from_value/to_value
usage mentioned in the comment).
- Around line 793-808: The examples use bare names for function IDs; update the
rustdoc snippets that call iii.register_function to use the repository's
::-separated ID convention (e.g., replace "greet" and "echo" with something like
"greet::default" or domain-style IDs such as "orders::validate" /
"reports::daily-summary") so the examples follow the engine-standard function ID
format; locate the calls to iii.register_function in the examples around
RegisterFunction::new_async and change the string IDs accordingly.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4f2b220f-83aa-4364-a70d-d4a07da8fcbb

📥 Commits

Reviewing files that changed from the base of the PR and between 1135fe6 and 70e3dd5.

📒 Files selected for processing (3)
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
  • sdk/packages/rust/iii/src/iii.rs
  • sdk/packages/rust/iii/tests/queue_integration.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
  • sdk/packages/rust/iii/tests/queue_integration.rs

Comment thread sdk/packages/rust/iii/src/iii.rs Outdated
Comment thread sdk/packages/rust/iii/src/iii.rs
@guibeira guibeira changed the title refactor(rust-sdk)!: unified register_function API with RegisterFunction builder refactor(rust-sdk)!: simplify register_function — 3 constructors + IIIError handlers Apr 30, 2026
andersonleal
andersonleal previously approved these changes May 1, 2026
guibeira added 3 commits May 13, 2026 09:36
Mirror of Node's RegisterFunctionOptions: description, request_format,
response_format, metadata. Will back the reshaped register_function_with
signature in the next commit.
Reshape register_function_with from (message, handler) to
(id, handler, options) so the cross-SDK call shape matches:

    Node:    registerFunction(id, handler, options?)
    Python:  register_function(function_id, handler, *, description, ...)
    Rust:    register_function_with(id, handler, options)

register_function(id, f) and the tuple form
register_function((message, handler)) are unchanged.

BREAKING CHANGE: register_function_with argument order changed.
See docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
for migration.
guibeira added 14 commits May 13, 2026 09:36
Reflect new (id, handler, options) signature.
The Before cell used Default::default() on RegisterFunctionMessage,
which does not derive Default. Replace with the existing
.with_id(...).with_description(...) builder.
The reshaped register_function_with(id, handler, options) signature
references RegisterFunctionOptions. Add it to EXPORTED_TYPES so its
section renders in the regenerated API reference.
Drop reg_fn_msg helper and update all 32 call sites to the new
(id, handler, options) shape after iii-sdk's BREAKING change.

BREAKING CHANGE: registration call sites changed shape; no behavioral
change.
Reflect new (id, handler, options) signature in the SKILL.md table
and the http-invoked-functions.rs reference snippet.
The auto-generated SDK reference dropped HttpInvocationConfig,
HttpAuthConfig, HttpMethod, FunctionInfo, TriggerInfo, WorkerInfo,
and RegisterFunctionMessage sections after register_function_with's
signature stopped naming HttpInvocationConfig directly. Those types
are still referenced in method/type doc-comments via intra-doc links
(e.g., 'Async closure or [HttpInvocationConfig]'). Extend
filterReferencedTypes to also scan description fields and examples,
so types named in prose stay in the rendered output.

Symmetric fix lands sdk-python.mdx changes too: ChannelReader,
ChannelWriter, TriggerAction, EnqueueResult, StreamChannelRef,
EngineLogExporter, EngineMetricsExporter, Trigger, TriggerTypeRef,
and Channel are also referenced only in prose (e.g. ':meth:' /
'\`\`trigger()\`\`') and were dropped for the same reason.
Replace the prior register_function / register_function_with /
RegisterFunction tuple split with a single API:

    iii.register_function(id, RegisterFunction::new_async(fetch).description(...));

Constructors carry the registration kind:

    RegisterFunction::new(f)        // sync typed (auto-extracted schemas)
    RegisterFunction::new_async(f)  // async typed (auto-extracted schemas)
    RegisterFunction::raw(f)        // async untyped Value handler
    RegisterFunction::http(config)  // HTTP-invoked

Builder methods (chainable, all consume self):

    .description(s) .metadata(v)
    .request_format(schema) .response_format(schema)

When request_format / response_format are set on the builder they
override any auto-extracted schemas.

Removed:

  * register_function_with
  * register_function((RegisterFunctionMessage, handler)) tuple form
  * RegisterFunctionOptions
  * IntoFunctionRegistration trait
  * IntoFunctionHandler trait + impls
  * iii_fn / iii_async_fn / IIIFn / IIIAsyncFn

Migrated all in-tree consumers: SDK integration tests, iii-example
crate, console-rust (33 callers), and three engine bridges. Skill
references and the changelog entry rewritten. docs/api-reference/
sdk-rust.mdx regenerated.

BREAKING CHANGE: register_function_with, RegisterFunctionOptions,
the (RegisterFunctionMessage, handler) tuple form, IntoFunctionHandler,
IntoFunctionRegistration, iii_fn/iii_async_fn/IIIFn/IIIAsyncFn are all
removed. See docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
for the migration table.
CI surfaced two issues with the prior commit:

1. crates/iii-worker/src/sandbox_daemon/{mod,fs/*}.rs (14 sites)
   were still using the old register_function_with(message, handler)
   signature — never picked up by the previous migration sweep, which
   only searched sdk/, console/, engine/, and skills/. Migrated to the
   new register_function(id, RegisterFunction::raw(handler).description(...))
   shape.

2. The migration script's output had inconsistent spacing that
   tripped cargo fmt --check across the workspace. Ran cargo fmt --all
   to fix.

Verified locally:
- cargo build --workspace --all-targets — clean
- cargo fmt --all -- --check — clean
- cargo test -p iii-sdk --lib — 64/64 pass
- cargo test -p iii-sdk --doc — 16/16 pass
The 'untyped' name is more self-explanatory: it pairs cleanly with the
typed 'new' / 'new_async' constructors and signals 'no schema
introspection' rather than implying low-level access.

Pure mechanical rename across all consumers, the changelog, the API
reference, and skill docs. No behavioral change.
… IIIError

Drop RegisterFunction::untyped — `serde_json::Value: JsonSchema` (via
schemars), so `RegisterFunction::new` and `::new_async` already accept
both typed handlers AND `Fn(Value) -> ...` closures. The `untyped`
constructor was redundant.

To make this work without forcing every caller to write
`Ok::<_, IIIError>(...)` annotations, fix the handler error type to
`IIIError` (was generic `E: Display`):

    // Before
    F: Fn(T) -> Result<R, E>
    E: Display + Send + 'static

    // After
    F: Fn(T) -> Result<R, IIIError>

Add `From<String>` and `From<&str>` for `IIIError` to smooth the
migration of existing handlers returning `Result<R, String>`.

Constructors are now 3:
- `RegisterFunction::new(f)`        — sync, typed or Value
- `RegisterFunction::new_async(f)`  — async, typed or Value
- `RegisterFunction::http(config)`  — HTTP-invoked

Migration:
- `RegisterFunction::untyped(f)` -> `RegisterFunction::new_async(f)` (~118 sites)
- `Result<R, String>` -> `Result<R, IIIError>` (~40 sites in iii-example, tests)
- Closures with implicit `_input` -> `_input: Value` for inference (~3 sites)

BREAKING CHANGE: handler signatures now require `Result<_, IIIError>`.
Use `IIIError::Handler(s)`, `Err(s.into())`, or `?`-propagation instead
of `Err(string)`.
- queue_integration.rs: switch durable test function IDs from dot-
  separated (test.queue.foo.rs.X) to ::-separated (test::queue::foo::rs::X)
  to match the repo standard.
- iii.rs: drop the duplicated RegisterFunction::new_async entry from the
  register_function rustdoc (the API has 3 constructors, not 4).
- changelog: distinguish sync vs async replacements in the migration
  table — register_function_with and the tuple form map to either
  RegisterFunction::new or RegisterFunction::new_async depending on the
  handler kind.
- changelog: fix Python signature parameter name from `handler` to
  `handler_or_invocation` (keeping the `*` keyword-only marker since the
  real Python API does require it).
- IntoSyncHandler / IntoAsyncHandler: map serde_json::from_value and
  serde_json::to_value failures to IIIError::Serde instead of
  IIIError::Handler. Preserves the distinction between user handler
  errors and SDK-level (de)serialization errors.
- iii.rs rustdoc: replace bare ids "greet" / "echo" in the
  register_function examples with the repo's ::-separated convention
  ("greetings::greet" / "debug::echo") to match other examples.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/iii-worker/src/sandbox_daemon/fs/mkdir.rs (1)

82-86: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Map serde_json errors to the correct IIIError variant.

Both deserialization (lines 82-83) and serialization (lines 85-86) errors are currently wrapped in IIIError::Handler, but according to the PR commit message, "serde_json (de)serialization failures [should map] to IIIError::Serde (preserving distinction between handler and SDK errors)." Since IIIError implements From<serde_json::Error>, you can use the ? operator directly to leverage the automatic conversion.

This distinction is important for observability: SDK/serialization errors should be categorized separately from business-logic handler errors.

🔧 Proposed fix
         let registry = registry.clone();
         let runner = runner.clone();
         Box::pin(async move {
-            let req: MkdirRequest = serde_json::from_value(payload)
-                .map_err(|e| IIIError::Handler(format!("bad request: {e}")))?;
+            let req: MkdirRequest = serde_json::from_value(payload)?;
             match handle_mkdir(req, &registry, &*runner).await {
-                Ok(resp) => serde_json::to_value(resp)
-                    .map_err(|e| IIIError::Handler(format!("serialize: {e}"))),
+                Ok(resp) => Ok(serde_json::to_value(resp)?),
                 Err(e) => Err(IIIError::Handler(
                     serde_json::to_string(&e.to_payload()).unwrap_or_else(|_| e.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/iii-worker/src/sandbox_daemon/fs/mkdir.rs` around lines 82 - 86, The
serde_json (de)serialization errors in the Mkdir RPC are incorrectly wrapped as
IIIError::Handler; change the deserialization and serialization calls so they
use the ? operator and let serde_json::Error convert into IIIError via its From
impl (i.e., replace the explicit map_err(...) for
serde_json::from_value(payload) and serde_json::to_value(resp) with direct ?
usage), keeping handler errors from handle_mkdir(...) unchanged; look for
MkdirRequest, serde_json::from_value, handle_mkdir, serde_json::to_value and
remove the IIIError::Handler mapping so serde errors become IIIError::Serde.
🤖 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/iii-worker/src/sandbox_daemon/fs/mkdir.rs`:
- Around line 82-86: The serde_json (de)serialization errors in the Mkdir RPC
are incorrectly wrapped as IIIError::Handler; change the deserialization and
serialization calls so they use the ? operator and let serde_json::Error convert
into IIIError via its From impl (i.e., replace the explicit map_err(...) for
serde_json::from_value(payload) and serde_json::to_value(resp) with direct ?
usage), keeping handler errors from handle_mkdir(...) unchanged; look for
MkdirRequest, serde_json::from_value, handle_mkdir, serde_json::to_value and
remove the IIIError::Handler mapping so serde errors become IIIError::Serde.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f06ffdee-2a0a-45bd-8b00-9ebf32bf3300

📥 Commits

Reviewing files that changed from the base of the PR and between dba240c and ee38c81.

📒 Files selected for processing (22)
  • console/packages/console-rust/src/bridge/functions.rs
  • console/packages/console-rust/src/main.rs
  • crates/iii-worker/src/cli/worker_manager_daemon.rs
  • crates/iii-worker/src/sandbox_daemon/fs/chmod.rs
  • crates/iii-worker/src/sandbox_daemon/fs/grep.rs
  • crates/iii-worker/src/sandbox_daemon/fs/ls.rs
  • crates/iii-worker/src/sandbox_daemon/fs/mkdir.rs
  • crates/iii-worker/src/sandbox_daemon/fs/mv.rs
  • crates/iii-worker/src/sandbox_daemon/fs/read.rs
  • crates/iii-worker/src/sandbox_daemon/fs/rm.rs
  • crates/iii-worker/src/sandbox_daemon/fs/sed.rs
  • crates/iii-worker/src/sandbox_daemon/fs/stat.rs
  • crates/iii-worker/src/sandbox_daemon/fs/write.rs
  • crates/iii-worker/src/sandbox_daemon/mod.rs
  • docs/api-reference/sdk-node.mdx
  • docs/api-reference/sdk-python.mdx
  • docs/api-reference/sdk-rust.mdx
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
  • docs/scripts/generate-api-docs.mts
  • docs/scripts/parsers/parse-rustdoc.mts
  • engine/src/workers/bridge_client/mod.rs
  • engine/src/workers/queue/adapters/bridge.rs
💤 Files with no reviewable changes (1)
  • console/packages/console-rust/src/main.rs
✅ Files skipped from review due to trivial changes (2)
  • docs/api-reference/sdk-node.mdx
  • docs/changelog/0-11-0/align-rust-register-function-with-signature.mdx
🚧 Files skipped from review as they are similar to previous changes (17)
  • crates/iii-worker/src/sandbox_daemon/fs/write.rs
  • crates/iii-worker/src/sandbox_daemon/fs/rm.rs
  • crates/iii-worker/src/sandbox_daemon/fs/chmod.rs
  • crates/iii-worker/src/sandbox_daemon/fs/sed.rs
  • crates/iii-worker/src/sandbox_daemon/fs/read.rs
  • docs/scripts/parsers/parse-rustdoc.mts
  • console/packages/console-rust/src/bridge/functions.rs
  • crates/iii-worker/src/sandbox_daemon/fs/stat.rs
  • crates/iii-worker/src/sandbox_daemon/fs/ls.rs
  • crates/iii-worker/src/sandbox_daemon/fs/grep.rs
  • engine/src/workers/bridge_client/mod.rs
  • engine/src/workers/queue/adapters/bridge.rs
  • crates/iii-worker/src/sandbox_daemon/mod.rs
  • docs/scripts/generate-api-docs.mts
  • docs/api-reference/sdk-python.mdx
  • crates/iii-worker/src/sandbox_daemon/fs/mv.rs
  • docs/api-reference/sdk-rust.mdx

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