use audacity_sdk::types::{ContentBlock, ConversationRole, InferenceConfiguration, Message};
use audacity_sdk::{Client, ConverseStreamOutput, Error};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
println!("--- non-streaming ---");
let response = client
.converse()
.model_id("gpt-5.4-mini")
.messages(
Message::builder()
.role(ConversationRole::User)
.content(ContentBlock::Text("Reply with exactly: OK".into()))
.build()?,
)
.inference_config(
InferenceConfiguration::builder()
.max_tokens(20)
.temperature(0.0)
.build(),
)
.send()
.await?;
let text = response
.output()
.ok_or("missing output")?
.as_message()
.map_err(|_| "not a message")?
.content()
.first()
.ok_or("missing content")?
.as_text()
.map_err(|_| "not text")?;
println!("text: {text:?}");
println!(
"stopReason: {:?} | usage: {:?} | latencyMs: {}",
response.stop_reason(),
response.usage(),
response.metrics().latency_ms
);
println!("--- streaming ---");
let mut output = client
.converse_stream()
.model_id("gpt-5.4-mini")
.messages(
Message::builder()
.role(ConversationRole::User)
.content(ContentBlock::Text(
"Count from 1 to 5, digits separated by spaces.".into(),
))
.build()?,
)
.inference_config(InferenceConfiguration::builder().max_tokens(30).build())
.send()
.await?;
let mut kinds: Vec<&'static str> = Vec::new();
let mut text = String::new();
while let Some(event) = output.stream.recv().await? {
let kind = match &event {
ConverseStreamOutput::MessageStart(_) => "messageStart",
ConverseStreamOutput::ContentBlockStart(_) => "contentBlockStart",
ConverseStreamOutput::ContentBlockDelta(e) => {
if let audacity_sdk::ContentBlockDeltaPayload::Text(t) = &e.delta {
text.push_str(t);
}
"contentBlockDelta"
}
ConverseStreamOutput::ContentBlockStop(_) => "contentBlockStop",
ConverseStreamOutput::MessageStop(_) => "messageStop",
ConverseStreamOutput::Metadata(e) => {
println!("metadata usage: {:?}", e.usage);
"metadata"
}
_ => "other",
};
if kinds.last() != Some(&kind) {
kinds.push(kind);
}
}
println!("event kinds in order: {kinds:?}");
println!("streamed text: {text:?}");
println!("--- error: bad api key ---");
let bad = Client::new(
&audacity_sdk::Config::builder()
.api_key("audacity_api_bogus")
.build()?,
)?;
match bad
.converse()
.model_id("gpt-5.4-mini")
.messages(
Message::builder()
.role(ConversationRole::User)
.content(ContentBlock::Text("hi".into()))
.build()?,
)
.send()
.await
{
Err(Error::AccessDenied(d)) => {
println!(
"AccessDenied: {:?} {:?} {}",
d.status_code, d.error_code, d.message
)
}
other => println!("FAIL: expected AccessDenied, got {other:?}"),
}
println!("--- error: bad model ---");
match client
.converse()
.model_id("not-a-real-model-xyz")
.messages(
Message::builder()
.role(ConversationRole::User)
.content(ContentBlock::Text("hi".into()))
.build()?,
)
.send()
.await
{
Err(e) => println!("{e:?}"),
Ok(_) => println!("FAIL: no error"),
}
Ok(())
}