The ToolAPI exchanges data between clients and tools using a canonical set of typed values. Upon calling a tool, the client sends a Value as input; when finished, the tool returns a Value as output.
The type system is intentionally limited to a small, well-defined set. This promotes interoperability: tools with similar purposes can be exchanged freely, because they all speak the same types. Structured types exist to give values that could be expressed with Dicts and Lists a known structure and semantic meaning that tools and clients can rely on.
Design principle
The number of structured types is kept low to maximize reusability. For niche applications, it is preferred that tool and client agree on a structure using dynamic types (
Dict,List) rather than extending the ToolAPI with new structured types.
Overview
| Category | Type | Description |
|---|---|---|
| Atomic | None | Absence of a value |
Bool | true or false | |
Int | 64-bit signed integer | |
Float | 64-bit IEEE 754 double | |
Str | UTF-8 string | |
Complex | Complex number (real + imaginary Float) | |
Vec3 | 3-element float vector [f64; 3] | |
Bytes | Raw binary data Vec<u8> | |
Vec4 | 4-element float vector [f64; 4] | |
| Structured | InstantSeqEvent | Instantaneous MRI sequence event (Pulse, Fid, or Adc) |
Volume | 3D voxel grid with affine transform | |
SegmentedPhantom | Multi-tissue MRI phantom | |
PhantomTissue | Single tissue with relaxation parameters | |
| Dynamic | Dict | String-keyed map of heterogeneous values |
List | Ordered sequence of heterogeneous values | |
| Typed | TypedList | Homogeneous list (one variant per value type) |
TypedDict | Homogeneous dict (one variant per value type) |
Atomic Types
None
Absence of a value. Maps to None in Python, null in JavaScript.
Bool
Boolean value: true or false.
Int
64-bit signed integer. Ranges from \(-2^{63}\) to \(2^{63} - 1\).
Float
Double-precision (64-bit) IEEE 754 floating point value.
Str
UTF-8 encoded string.
Complex
Complex number, encoded as two Floats (real and imaginary part). Uses num_complex::Complex64 in Rust, complex in Python, and { re, im } in JavaScript.
Bytes
Raw binary data, stored as Vec<u8>. Uses the serde_bytes crate for efficient binary serialization (compact encoding instead of a JSON array of numbers). Maps to bytes in Python and Uint8Array in JavaScript.
Vec3
A 3-element float vector: [f64; 3]. Used for spatial positions, FOV sizes, and similar 3D quantities.
Vec4
A 4-element float vector: [f64; 4]. Used for k-space encoding [kx, ky, kz, t] and similar quantities.
Structured Types
These types carry MRI-specific semantic meaning. They have a fixed structure that tools and clients can rely on.
InstantSeqEvent
An instantaneous MRI sequence event. This is a tagged union with three variants:
| Variant | Fields | Description |
|---|---|---|
Pulse | angle: Float, phase: Float | An instantaneous RF pulse |
Fid | kt: Vec4 | Free induction decay with encoding [kx, ky, kz, t] |
Adc | phase: Float | An ADC sample capturing the current signal |
This type is used for discrete-event sequence representations, optimized for simulations that operate on gradient moments and timing rather than continuous pulse shapes.
Volume
A 3D voxel grid with an affine transform describing its position and orientation in physical space.
| Field | Type | Description |
|---|---|---|
shape | [u64; 3] | Number of voxels in each dimension |
affine | [[f64; 4]; 3] | 3x4 affine matrix mapping voxel indices to physical coordinates |
data | TypedList | Flattened voxel data (type depends on content, e.g. Float, Complex) |
The data field uses a TypedList for efficient, homogeneous storage. The element type depends on the context — a density map might use Float, while a coil sensitivity map might use Complex.
SegmentedPhantom
A multi-tissue segmented MRI phantom. Unlike classical voxel phantoms where each voxel has independent tissue parameters, a segmented phantom groups voxels by tissue type.
| Field | Type | Description |
|---|---|---|
tissues | HashMap<String, PhantomTissue> | Named tissues (e.g. "white_matter", "gray_matter") |
b1_tx | Vec<Volume> | B1 transmit sensitivity maps (one per transmit channel) |
b1_rx | Vec<Volume> | B1 receive sensitivity maps (one per receive coil) |
PhantomTissue
A single tissue within a SegmentedPhantom, containing spatially-varying maps and scalar relaxation parameters.
| Field | Type | Description |
|---|---|---|
density | Volume | Proton density map \([a.u.]\) |
db0 | Volume | \(\Delta B_0\) off-resonance map \([Hz]\) |
t1 | Float | \(T_1\) relaxation time \([s]\) |
t2 | Float | \(T_2\) relaxation time \([s]\) |
t2dash | Float | \(T_2’\) dephasing time \([s]\) |
adc | Float | Apparent diffusion coefficient \([10^{-3}\ mm^2/s]\) |
Dynamic Collections
Dict
A string-keyed map where each value can be a different Value type. This is the standard way to pass named parameters to tools and return named results.
Dict { "flip_angle": Float(0.26), "resolution": List([Int(128), Int(128), Int(1)]) }
List
An ordered sequence where each element can be a different Value type.
List [Float(1.0), Int(42), Str("hello")]
Typed Collections
For performance, homogeneous collections are stored as TypedList and TypedDict instead of List and Dict. They avoid the overhead of wrapping every element in a Value enum.
TypedList
A homogeneous list with one variant per value type. For example, TypedList::Float(Vec<f64>) stores a list of floats without per-element tagging.
In Python and JavaScript, typed lists are represented as regular list / Array — the type is inferred automatically from the element types during serialization.
TypedDict
A homogeneous string-keyed map, analogous to TypedList. For example, TypedDict::Int(HashMap<String, i64>).
Extracting Values
Values can be indexed using a path-based syntax. The Value::get(pointer) method (Rust) accepts a /-separated path to navigate nested structures:
// Extract from nested Dict / List
let val = input.get("tissues/white_matter/t1")?;
let val = input.get("events/0/angle")?;
// Convert to a concrete Rust type
let t1: f64 = input.get("tissues/white_matter/t1")?.try_into()?;Path segments that parse as integers index into List / TypedList; string segments index into Dict / TypedDict.
Collection Extraction
Typed collections can be extracted directly to standard Rust types using TryFrom / try_into():
// TypedList → Vec<T>
let signal: Vec<f64> = output.try_into()?;
// TypedDict → HashMap<String, T>
let params: HashMap<String, f64> = input.try_into()?;The reverse also works — Vec<T> and HashMap<String, T> convert into Value via From:
let result = Value::from(vec![1.0, 2.5, 3.14]); // → TypedList::Float
let result = Value::from(HashMap::from([ // → TypedDict::Float
("mean".into(), 2.5),
("std".into(), 0.3),
]));These conversions are available for all value types (atomic, structured, and Bytes).
See also: Implementations for language-specific value handling.