OpenPay (3 crates, ~13k LOC, 175 tests) op-connect sub-merchant onboarding + KYB + split-payments + payouts + 1099-K op-3ds2 3DS2.3 SCA processor (AReq/ARes/CReq/CRes/RReq/RRes + exemptions) op-tax US/EU/UK/AU/CA/IN tax with Avalara/Stripe/CCH/Vertex/TaxJar adapters Smart Byte (3 crates, ~7k LOC, 57 tests) smart-byte-libp2p libp2p transport (kad/gossipsub/mdns/autonat/relay/dcutr/identify/ping/req_resp) smart-byte-crdt LWW/GCounter/PnCounter/OrSet/LwwMap/RgaList/2P-Set + HLC + vector clock + Automerge/Yjs interop smart-byte-keri-witness KERI key-rotation cycle + witness layer + watchers + recovery + delegation EOC (3 crates, ~8k LOC, 108 tests) eoc-tools function-calling across Anthropic/OpenAI/Google/Mistral/Cohere + parallel exec + sandbox eoc-multimodal vision (CLIP/SigLIP/ColPali) + audio (Whisper/MMS) + video sampling + modality router eoc-spec-decode vanilla + SpS-with-temp + lookahead speculative decoding + EAGLE/Medusa stubs All three workspaces compile cleanly. Forbidden-unsafe, no unwrap outside tests. Co-Authored-By: Claude Opus 4.7 <[email protected]>
108 lines
3.5 KiB
Rust
108 lines
3.5 KiB
Rust
//! Canonical tool schema — name, description, JSON Schema for arguments
|
|
//! and (optionally) returns. Generated from Rust types via `schemars`.
|
|
//!
|
|
//! Operators define a tool's argument shape as an ordinary Rust struct
|
|
//! with `serde` annotations + `#[derive(schemars::JsonSchema)]` and call
|
|
//! [`schema_for`] to produce the canonical [`ToolSchema`]. From that
|
|
//! canonical form, vendor-specific shapes are derived by the
|
|
//! `*_schema` modules.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value as JsonSchemaValue;
|
|
|
|
use schemars::JsonSchema;
|
|
|
|
/// The canonical, vendor-agnostic description of a single tool.
|
|
///
|
|
/// `parameters` and `returns` are JSON Schema documents (Draft 2020-12
|
|
/// compatible). Vendor adapters re-shape this into the wire layout each
|
|
/// provider expects.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ToolSchema {
|
|
/// Tool name — matched against `ToolCallRequest::name`.
|
|
pub name: String,
|
|
/// Free-form description shown to the model.
|
|
pub description: String,
|
|
/// JSON Schema for the tool's argument object.
|
|
pub parameters: JsonSchemaValue,
|
|
/// Optional JSON Schema for the tool's return value (advisory; many
|
|
/// vendor APIs do not yet consume this).
|
|
pub returns: Option<JsonSchemaValue>,
|
|
}
|
|
|
|
impl ToolSchema {
|
|
/// Construct a schema directly (for tests / non-Rust-derived tools).
|
|
pub fn new(
|
|
name: impl Into<String>,
|
|
description: impl Into<String>,
|
|
parameters: JsonSchemaValue,
|
|
) -> Self {
|
|
Self {
|
|
name: name.into(),
|
|
description: description.into(),
|
|
parameters,
|
|
returns: None,
|
|
}
|
|
}
|
|
|
|
/// Attach a return schema.
|
|
pub fn with_returns(mut self, returns: JsonSchemaValue) -> Self {
|
|
self.returns = Some(returns);
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Derive a canonical [`ToolSchema`] from a Rust argument type `T`.
|
|
///
|
|
/// `T` must implement `schemars::JsonSchema`. The resulting `parameters`
|
|
/// is the JSON Schema produced by `schemars` for `T`.
|
|
///
|
|
/// ```ignore
|
|
/// use schemars::JsonSchema;
|
|
/// use serde::{Deserialize, Serialize};
|
|
///
|
|
/// #[derive(Serialize, Deserialize, JsonSchema)]
|
|
/// struct AddArgs { a: i64, b: i64 }
|
|
///
|
|
/// let s = eoc_tools::schema_for::<AddArgs>("add", "Sum two integers.");
|
|
/// assert_eq!(s.name, "add");
|
|
/// ```
|
|
pub fn schema_for<T: JsonSchema>(
|
|
name: impl Into<String>,
|
|
description: impl Into<String>,
|
|
) -> ToolSchema {
|
|
let schema = schemars::schema_for!(T);
|
|
// schemars::Schema -> serde_json::Value cannot fail for a well-formed
|
|
// schema; if it ever does, fall back to an empty object schema so the
|
|
// caller still receives a usable ToolSchema.
|
|
let parameters = serde_json::to_value(&schema)
|
|
.unwrap_or_else(|_| serde_json::json!({"type": "object"}));
|
|
ToolSchema::new(name, description, parameters)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
|
struct AddArgs {
|
|
a: i64,
|
|
b: i64,
|
|
}
|
|
|
|
#[test]
|
|
fn derives_schema_from_rust_type() {
|
|
let s = schema_for::<AddArgs>("add", "Sum two integers.");
|
|
assert_eq!(s.name, "add");
|
|
assert!(s.parameters.is_object());
|
|
// schemars Draft 2020-12 output contains a `properties` object.
|
|
let params = s.parameters.as_object().unwrap();
|
|
let props = params
|
|
.get("properties")
|
|
.and_then(|v| v.as_object())
|
|
.expect("properties present");
|
|
assert!(props.contains_key("a"));
|
|
assert!(props.contains_key("b"));
|
|
}
|
|
}
|