Rust SDK
The Rust SDK has the same surface as the JavaScript SDK; this page shows the Rust calls and what differs.
Install
cargo add introspection-sdk futures serde_json
cargo add tokio --features rt-multi-thread,macros| Feature | Adds |
|---|---|
| Default | REST client (runtimes, runner, tasks, files, shares, conversations, events, metrics, experiments) and auth (from_service_account, token_exchange, authorization_code_token). |
otel | IntrospectionLogs for track, feedback, identify. |
arrow | Arrow decoding for conversation and event reads. |
testing | In-memory exporter and test helpers. |
IntrospectionClient::new(ClientConfig::default()) reads INTROSPECTION_TOKEN
and returns IntrospectionError::TokenRequired when nothing resolves. Other
credentials: Authentication.
Run a task
use futures::StreamExt;
use introspection_sdk::{AgUiEvent, ClientConfig, IntrospectionClient, RunRequest, RunnerIdentity};
let client = IntrospectionClient::new(ClientConfig::default())?;
let runner = client
.runtime("support-agent")
.await?
.run(RunRequest {
identity: Some(RunnerIdentity { user_id: Some("user_123".into()), ..Default::default() }),
..Default::default()
})
.await?;
let handle = runner.tasks().start_prompt("Summarize the open tickets.").await?;
println!("{}", handle.text().await?);
let mut events = runner.tasks().start_prompt("Say hello.").await?.into_stream().await?;
while let Some(event) = events.next().await {
if let AgUiEvent::TextMessageContent(m) = event? { print!("{}", m.delta); }
}RunRequest fields are all optional: identity (user_id, anonymous_id,
conversation_id, tags, metadata), caller, agent_name,
ttl_seconds, scope. With no identity the task belongs to your credential’s own
member (an API key’s agent member); see
Run a task for an end user.
runner.tasks().start(&TaskCreate { .. }) takes the full option set (title,
agent, metadata, files, repositories, tags); follow-ups use
runner.tasks().runs.create(&task_id, &TaskRunCreate { .. }). Close with
runner.close() and client.shutdown()?.
Continue and control a task
- Reattach. Read
active_run_idorlatest_run_idfrom the task’s metadata, thenrunner.tasks().runs.stream(&task_id, &run_id).await?. There is nosincecursor; a reattach replays the run. - Stream options.
runs.stream_with(&task_id, &run_id, opts)andhandle.stream_with(opts)takeStreamOptions(max_reconnects5,backoff500 ms,timeout300 s,emit_reconnect_events) and return the stream directly. Semantics and the retry table: JavaScript. - Interrupts. Read ids from the
RUN_FINISHEDoutcome["interrupts"], then resume (Ask the user):
use introspection_sdk::{ResumeEntry, TaskCancelOptions, TaskRunResume};
runner.tasks().runs.resume(&task_id, &TaskRunResume {
resume: vec![ResumeEntry {
interrupt_id,
status: "resolved".into(),
payload: Some(serde_json::json!({ "approved": true })),
}],
}).await?;
handle.cancel().await?; // abort the turn, keep the sandbox warm
handle.cancel_with(&TaskCancelOptions::Drain { drain_within_seconds: Some(60) }).await?;Files and shares
use introspection_sdk::{FileType, FileUpload, TaskCreate, TaskFileRef};
let uploaded = runner.files().upload(FileUpload::from_path("notes.txt").with_file_type(FileType::Upload)).await?;
runner.tasks().start(&TaskCreate {
prompt: Some("Summarize the attached notes.".into()),
files: Some(vec![TaskFileRef { id: uploaded.id.to_string(), ..Default::default() }]),
..Default::default()
}).await?;FileUpload::from_bytes, files().create_text, download, and
download_stream cover the rest. runner.shares() has create
(ShareCreate::new(resource_type, id) plus granted_member_id,
granted_tag, visible_from), update (only visible_from), and delete.
Rules: Sharing.
Conversations, events, and metrics
use introspection_sdk::{ConversationListParams, MetricSpec, MetricsQuery};
let mut pages = runner.conversations().list(&ConversationListParams::default())?;
while let Some(page) = pages.next_page().await? {
for c in &page.records { println!("{} {}", c.id, c.usage.total_tokens); }
}
let result = runner.metrics().query(&MetricsQuery {
view: "conversations".into(),
metrics: vec![MetricSpec::of("sum", "total_cost_usd")],
lookback: Some("7d".into()),
..Default::default()
}).await?;conversations().items.list/getread the transcript;export_json,export_trajectory,export_stream, andexport_arrow(featurearrow) sit onconversations().events().list(&EventListParams::new(IntrospectionEventName::Feedback))?returns typedEventvariants (annotation too); passIntrospectionEventName::Unknown("introspection.track".into())for track events.MetricsQuery.lookbackis a client-side convenience lowered to absolute timestamps; rows are typedMetricRows, andtruncated()says whether the row limit was hit.Paginatoroffersnext_page(),collect_all(max_pages), andStream.
Feedback
use introspection_sdk::otel::{FeedbackOptions, IntrospectionLogs};
let logs = IntrospectionLogs::builder().service_name("support-api").build()?;
{
let _c = logs.set_conversation_id(&task_id);
logs.feedback("thumbs_up", FeedbackOptions::new().with_comments("Solved it"));
}
logs.shutdown()?;Needs the otel feature. with_previous_response_id anchors feedback to one
response. Name rules: Send feedback.
Experiments
client.experiments() has list, get, create, update, and delete
(documents as in introspection experiments create).
client.experiment(id, project) returns a handle with run, start, end,
and cancel.
let created = client.experiments().create(&serde_json::json!({
"project": "my-project", "name": "candidate", "runtime": "support-agent",
"arms": [{ "runtime_id": a, "arm_label": "baseline" }, { "runtime_id": b, "arm_label": "candidate" }],
"goal_json": { "kind": "composite", "direction": "maximize",
"components": [{ "source": "judge", "judge_id": judge_id, "weight": 1.0 }] }
})).await?;
client.experiment(created.id, "my-project").start().await?;Environment variables and errors
Environment variables match JavaScript,
except INTROSPECTION_LOG_LEVEL and INTROSPECTION_STRICT_TRACING.
Calls return ApiResult<T> with IntrospectionAPIError: Http (accessors
status(), code(), request_id(), body(), retry_after()),
RunFailed and StreamIncomplete from streams, and Transport, Decode,
Timeout, Io, InvalidConfig. Client setup returns IntrospectionError;
IntrospectionLogs returns IntrospectionLogsError.
Next
- Authentication: API keys and service accounts.
- Task operations: statuses and cancellation.
- Add to your app: the full integration.