Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.sf-voice.sh/llms.txt

Use this file to discover all available pages before exploring further.

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 to Cargo.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::{SfVoiceMedia, IngestRequest, MediaType};

let resp = client.ingest(
    IngestRequest::from_url("https://storage.example.com/calls/001.mp3")
        .asset_id("call_001")
        .asset_class("customer_acme")
        .media_type(MediaType::Audio)
        .build(),
).await?;

let task_id = resp.task_id;
S3:
use sf_voice_media::IngestRequest;

let resp = client.ingest(
    IngestRequest::from_s3("calls/customer_acme/001.mp3")
        .asset_id("call_001")
        .asset_class("customer_acme")
        .media_type(MediaType::Audio)
        .build(),
).await?;

Poll until ready

use sf_voice_media::TaskStatus;

let task = client.poll_task(&task_id, None).await?;

match task.status {
    TaskStatus::Ready => println!("indexed: {}", task.asset_id),
    TaskStatus::Failed => eprintln!("failed: {:?}", task.error),
    _ => unreachable!(),
}
poll_task takes optional PollOptions { interval_ms, timeout_ms }. Defaults: 1500ms interval, 120s timeout.
use sf_voice_media::SearchRequest;

let resp = client.search(
    SearchRequest::new("customer asks about pricing")
        .asset_class("customer_acme")
        .threshold(0.7)
        .limit(10)
        .build(),
).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).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}");
    }
}