sf_voice_media — async, built on reqwest. Requires tokio.
Use this SDK inside Tokio services or jobs where ingest and polling should be part of the same async runtime.
Install
Add toCargo.toml:
[dependencies]
sf_voice_media = "0.1.1"
tokio = { version = "1", features = ["full"] }
Create a client
use sf_voice_media::SfVoiceMedia;
let client = SfVoiceMedia::new(
"https://api.sf-voice.com",
std::env::var("SF_VOICE_API_KEY").unwrap(),
)?;
Ingest
use sf_voice_media::types::{IngestRequest, MediaType};
let mut request = IngestRequest::from_url_with_asset_id(
"call_001",
"https://storage.example.com/calls/001.mp3",
).media_kind(MediaType::Audio);
request.asset_class = Some("customer_acme".into());
let resp = client.ingest(&request).await?;
let task_id = resp.task_id;
use sf_voice_media::types::{IngestRequest, MediaType};
let mut request = IngestRequest::from_s3_with_asset_id(
"call_001",
"calls/customer_acme/001.mp3",
).media_kind(MediaType::Audio);
request.asset_class = Some("customer_acme".into());
let resp = client.ingest(&request).await?;
Poll until ready
use sf_voice_media::types::TaskStatus;
use std::time::Duration;
let task = client.poll_task(
&task_id,
Duration::from_millis(1_500),
Duration::from_secs(120),
).await?;
match task.status {
TaskStatus::Ready => println!("indexed: {}", task.asset_id),
TaskStatus::Failed => eprintln!("failed: {:?}", task.error),
_ => unreachable!(),
}
Duration values.
Search
use sf_voice_media::types::SearchRequest;
let mut request = SearchRequest::new("customer asks about pricing")
.threshold(0.7)
.limit(10);
request.asset_class = Some("customer_acme".into());
let resp = client.search(&request).await?;
for result in &resp.results {
println!("{} {}–{}ms ({:.2})", result.asset_id, result.start_ms, result.end_ms, result.score);
}
Assets
// list
let resp = client.list_assets(None, None).await?;
// get one
let asset = client.get_asset("call_001").await?;
// delete
client.delete_asset("call_001").await?;
Errors
use sf_voice_media::SfVoiceMediaError;
match client.search(req).await {
Ok(resp) => { /* use resp */ }
Err(SfVoiceMediaError::Api { code, message, status }) => {
eprintln!("api error {status} [{code}]: {message}");
}
Err(SfVoiceMediaError::Http(e)) => {
eprintln!("transport error: {e}");
}
Err(SfVoiceMediaError::PollTimeout { task_id }) => {
eprintln!("timed out polling {task_id}");
}
}

