Beta. The Rust SDK is at
0.0.5. The wire contract against the Raindrop ingestion API is
stable and verified end-to-end against the live backend on every push, but the crate API may still
change in minor ways before 0.1.0. We recommend pinning the git tag in your Cargo.toml.Installation
The Rust SDK is hosted on GitHub (not on crates.io). Add it to yourCargo.toml:
src/main.rs and runs:
The Rust SDK requires Rust 1.88+ (MSRV). It is
async-first and uses tokio. Most fallible methods return Result<_, Error> — track_ai, track_event, identify, track_signal, the Interaction mutators (set_input, set_property, set_properties, add_attachments, patch, finish), and Client::flush / Client::close. Propagate errors with ? as you would for any fallible call. The two constructors — Client::begin(...).await and Client::resume_interaction(...) — are infallible: they always return an Interaction (a no-op handle when the client is disabled), so don’t put a ? on those.Quick Start: Interaction API
The Interaction API uses a simple three-step pattern:begin()– Create an interaction and log the initial user input- Update – Optionally call
set_property,set_properties,set_input, oradd_attachments finish()– Record the AI’s final output and close the interaction
Example: Chat Completion
Updating an Interaction
Update an interaction at any point usingset_property, set_properties, set_input, or add_attachments:
Resuming an Interaction
If you no longer have the interaction object returned frombegin(), resume it with resume_interaction():
Single-Shot Tracking (track_ai)
For simple request-response interactions, you can use track_ai() directly:
We recommend usingUsebegin()→finish()for new code to take advantage of partial-event buffering and tracing.
track_event() for non-AI events:
Tracking Signals (Feedback)
Signals capture quality ratings on AI events. Usetrack_signal() with the same event ID from begin() or track_ai():
| Field | Type | Description |
|---|---|---|
event_id | String | The ID of the AI event you’re evaluating |
name | String | Signal name (e.g. "thumbs_up", "thumbs_down") |
kind | String | One of SignalKind::{DEFAULT, STANDARD, FEEDBACK, EDIT, AGENT, AGENT_INTERNAL}. Defaults to "default". |
sentiment | String | "POSITIVE" or "NEGATIVE" |
comment | String | Merged into properties.comment for feedback signals |
after | String | Merged into properties.after for edit signals |
attachment_id | String | Optional attachment ID to associate the signal with |
properties | BTreeMap<String, _> | Additional metadata |
Identifying Users
Attachments
Attachments let you include additional context — documents, images, code, or embedded content — with your events. They work with bothbegin() interactions and track_ai() calls.
| Field | Type | Description |
|---|---|---|
kind | String | "code", "text", "image", or "iframe" (serialized as type on the wire) |
role | String | "input" or "output" |
name | String | Optional display name |
value | String | Content or URL |
language | String | Programming language (only meaningful for "code" attachments) |
attachment_id | String | Optional UUID. Backend auto-assigns one if empty. Set explicitly to round-trip with Signal::attachment_id. |
The dashboard’s attachment viewer renders
text, image, and iframe attachments. code
attachments survive ingestion and are searchable, but are not currently displayed in the visual
attachments tab.Configuration
| Builder method | Description | Default |
|---|---|---|
.write_key(&str) | Your Raindrop API key. Empty/missing key → SDK becomes a no-op. | — |
.endpoint(&str) | Override the API endpoint | https://api.raindrop.ai/v1/ |
.debug(bool) | Verbose debug logging via tracing | false |
.partial_flush_interval(Duration) | Periodic event flush. Duration::ZERO disables periodic flush | 1s |
.trace_flush_interval(Duration) | Periodic span flush. Duration::ZERO disables periodic flush | 1s |
.trace_max_batch_size(usize) | Max spans per trace export request | 50 |
.trace_max_queue_size(usize) | Max spans buffered before back-pressuring | 5000 |
.max_attempts(u32) | HTTP retries. 1 disables retries. | 3 |
.base_delay(Duration) | Backoff base (exponential, ±20% jitter) | 1s |
.jitter_fraction(f64) | Backoff jitter fraction (0.0–1.0) | 0.2 |
.service_name(&str) | OTLP resource.service.name | "raindrop.rust-sdk" |
.library_name(&str) | $context.library.name reported with each event | "raindrop-rust" |
.library_version(&str) | $context.library.version | crate version |
.http_client(reqwest::Client) | Inject a custom reqwest::Client | new client w/ 10s timeout |
.local_workshop_url(&str) | Mirror cloud-bound posts to a local Workshop daemon | auto-detected localhost |
.disable_local_workshop() | Disable env/probe-based local Workshop mirroring | — |
client.close().await? before your process exits to flush buffered events and spans. If write_key is empty and no local Workshop is configured, the client becomes a no-op (zero HTTP calls) instead of failing.
Local Workshop mirroring is enabled when RAINDROP_LOCAL_DEBUGGER is set to a URL, when RAINDROP_WORKSHOP is a truthy value or URL, or when the SDK can connect to the default Workshop daemon at http://localhost:5899/v1/.
Tracing
Tracing captures detailed execution information from your AI pipelines — multi-model interactions, chained prompts, and tool calls. This helps you:- Visualize the full execution flow of your AI application
- Debug and optimize prompt chains
- Understand the intermediate steps that led to a response
Manual Spans
Use manual spans for workflow, task, retrieval, or any other work that is not specifically an LLM generation or tool call. Build span trees by passing a parent intoSpanOptions::parent:
Interaction automatically inherit its user_id, convo_id, and event as traceloop.association.properties.* attributes, so the dashboard groups them under the same user, conversation, and event:
Plain
client.start_span(...) calls only need name + event_id. The SDK automatically emits
traceloop.association.properties.event_id so the span survives the backend’s ingestion filter.
For non-event-bound spans, set operation_id (e.g. "ai.workflow") or pass properties so the
span has at least one of ai.operationId, traceloop.span.kind, traceloop.workflow.name,
traceloop.association.properties.*, or gen_ai.* — otherwise it will be dropped server-side.LLM Spans
Usestart_llm_span for model calls. LlmSpan emits the attributes that Raindrop’s backend and frontend understand for prompt messages, response text, model/provider, and token usage.
messages is the chat-shaped prompt representation. Use it when your provider call takes a role/content array instead of a single text prompt:
LlmMessage::system, LlmMessage::user, and LlmMessage::assistant are convenience constructors; use LlmMessage::new(role, content) for provider-specific roles. If both input and messages are set in LlmOptions, messages wins. The backend uses the last user message as the span’s input_payload, while the frontend renderer can show the full message array.
If you only know the messages after creating the span, use set_messages. It replaces any previous input or prompt-message attributes:
LlmOptions:
LlmSpan exposes set_model, set_provider, set_input, set_messages, set_output, set_io, set_token_usage, set_error, end, and end_at.
Closure-style helpers
If you prefer scoped instrumentation,with_span runs a closure inside a span and automatically marks the span as failed on Err:
Tool Spans
Tool spans use the dedicated wire format (traceloop.span.kind=tool) so they surface in the dashboard’s event.toolCalls[] array.
| Field | Type | Description |
|---|---|---|
name | String | Tool name |
input | Option<Value> | JSON input |
output | Option<Value> | JSON output |
duration | Option<Duration> | Total duration |
start_time | Option<OffsetDateTime> | When the tool started (defaults to now - duration) |
end_time | Option<OffsetDateTime> | When the tool ended |
error | Option<String> | Error message; sets status=ERROR |
parent | Option<Span> | Parent span for nesting |
properties | BTreeMap<String, _> | Additional metadata |
with_tool and with_tool_async free helpers that run a closure inside a tool span and JSON-serialize the result onto traceloop.entity.output:
Standalone Tracer
UseClient::tracer() for batch jobs or non-conversation work where you still want spans, LLM spans, and tool traces:
Span Attributes
The SDK provides typed helpers for OTLP-compatible attributes:Known Limitations
- No automatic LLM-client instrumentation. Unlike the Python and TypeScript SDKs, the Rust SDK does not auto-hook into LLM frameworks. Create spans manually via
start_span,start_llm_span,start_tool_span,with_span,with_tool, ortrack_tool. - No PII redaction. The Python SDK exposes
set_redact_piiand the TypeScript SDK hasredactPii. The Rust SDK does not yet implement client-side redaction. Redact at the call site or upstream oftrack_ai/track_eventif needed. - Oversized payload guard. Payloads larger than 1 MiB after JSON serialization are dropped client-side (matching the JS / Python SDKs) to avoid 413s on the gateway. The drop is logged via
tracing::warn!so production callers can detect it.
That’s it! You’re ready to explore your events in the Raindrop dashboard. Ping us on Slack or email us if you get stuck!