#![ allow( clippy::doc_markdown ) ]
use api_openai::ClientApiAccessors;
use api_openai::
{
Client,
error ::OpenAIError,
components ::realtime_shared::
{
RealtimeSessionCreateRequest,
RealtimeConversationItemContent,
RealtimeConversationItem,
RealtimeClientEventConversationItemCreate,
RealtimeResponseCreateParams,
RealtimeClientEventResponseCreate,
RealtimeClientEvent,
RealtimeServerEvent,
},
};
use std::sync::{ Arc, Mutex };
#[ tokio::main( flavor = "current_thread" ) ]
async fn main() -> Result< (), Box< dyn core::error::Error > >
{
tracing_subscriber::fmt::init();
tracing ::info!( "Initializing client..." );
let secret = api_openai::secret::Secret::load_with_fallbacks( "OPENAI_API_KEY" )
.expect( "Failed to load OPENAI_API_KEY. Please set environment variable or add to workspace secrets file." );
let env = api_openai::environment::OpenaiEnvironmentImpl::build(
secret,
None,
None,
api_openai::environment::OpenAIRecommended::base_url().to_string(),
api_openai::environment::OpenAIRecommended::realtime_base_url().to_string(),
).expect( "Failed to create environment" );
let client = Client::build( env ).expect( "Failed to create client" );
tracing ::info!( "Building realtime session request..." );
let request = RealtimeSessionCreateRequest::former()
.model( "gpt-4o-realtime-preview".to_string() )
.temperature( 0.7 )
.output_audio_format( "pcm16" ) .form();
tracing ::info!( "Sending request to OpenAI API to create session..." );
let session = client.realtime().create_session( request ).await?;
tracing ::info!( "Creating Realtime WebSocket Session Client..." );
let _ = session.client_secret.value;
let session_client = client.realtime().connect_ws( &session.id ).await?;
let content = RealtimeConversationItemContent::former()
.r#type( "input_text" )
.text( "What's the weather like in San Francisco?" )
.form();
let ci_to_create = RealtimeConversationItem::former()
.r#type( "message" )
.role( "user" )
.content( vec![ content ] )
.form();
let cic_create = RealtimeClientEventConversationItemCreate::former()
.item( ci_to_create )
.form();
tracing ::info!( "Sending preliminary conversation.item.create event..." );
session_client.send_event( RealtimeClientEvent::ConversationItemCreate( cic_create ) ).await?;
tokio ::time::sleep( tokio::time::Duration::from_millis( 100 ) ).await;
let response_params = RealtimeResponseCreateParams::former()
.temperature( 0.9 ) .modalities( vec![ "text".to_string(), "audio".to_string() ] ) .form();
let rc_create = RealtimeClientEventResponseCreate::former()
.response( response_params )
.form();
tracing ::info!( "Sending response.create event..." );
session_client.send_event( RealtimeClientEvent::ResponseCreate( rc_create ) ).await?;
tracing ::info!( "Waiting for response.created confirmation..." );
let mut confirmation_received = false;
let created_response_id = Arc::new( Mutex::new( None::< String > ) );
loop
{
let response = session_client.recv_event().await.map( Some );
#[ allow( unreachable_patterns ) ]
match response
{
Ok( Some( event ) ) =>
{
match event
{
RealtimeServerEvent::ResponseCreated( created_event ) =>
{
println!( "\n--- Response Created Confirmation Received ---" );
println!( "{created_event:?}" );
let response_id = created_event.response.id.clone();
println!( "Successfully received response.created confirmation. Response ID: {response_id}" );
*created_response_id.lock().unwrap() = Some( response_id );
confirmation_received = true;
}
RealtimeServerEvent::ResponseDone( done_event ) =>
{
println!( "\n--- Response Done Event Received ---" );
println!( "{done_event:?}" );
let expected_id = created_response_id.lock().unwrap().clone();
if let Some(expected) = expected_id
{
if done_event.response.id == expected
{
println!( "Received response.done for the created response (ID: {expected}). Status : {}", done_event.response.status );
break;
}
println!( "Received response.done for a different response ID: {}", done_event.response.id );
}
else
{
println!( "Received response.done before response.created was confirmed." );
if confirmation_received { break; }
}
}
RealtimeServerEvent::ResponseTextDelta( _ ) |
RealtimeServerEvent::ResponseAudioDelta( _ ) =>
{
println!( "\n--- Received Delta --- \n{event:?}" );
}
_ => { println!( "\n--- Received Other Event (while waiting for response confirmation) --- \n{event:?}" ); }
}
}
Ok( None ) =>
{
println!( "\nWebSocket connection closed by server." );
break; }
Err( e ) =>
{
eprintln!( "\nError reading from WebSocket : {e:?}" );
return Err( e.into() ); }
}
}
if !confirmation_received
{
eprintln!( "Loop finished without receiving response.created confirmation." );
if created_response_id.lock().unwrap().is_none()
{
return Err( OpenAIError::WsInvalidMessage( "Did not receive expected response.created confirmation".to_string() ).into() );
}
println!( "Warning : Response.created confirmation flag not set, but response ID was likely captured before loop exit." );
}
Ok( () )
}