use futures::StreamExt;
async fn post_blocking(
client: &reqwest::Client,
gateway: &str,
body: serde_json::Value,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
Ok(client
.post(format!("{gateway}/v1/responses"))
.json(&body)
.send()
.await?
.error_for_status()?
.json()
.await?)
}
async fn drain_sse(
mut stream: impl futures::Stream<Item = Result<bytes::Bytes, reqwest::Error>> + Unpin,
) -> Result<(String, String), Box<dyn std::error::Error>> {
let mut buf = String::new();
let mut response_id = String::new();
let mut reply = String::new();
while let Some(chunk) = stream.next().await {
buf.push_str(std::str::from_utf8(&chunk?).unwrap_or_default());
while let Some(pos) = buf.find('\n') {
let line: String = buf.drain(..=pos).collect();
let line = line.trim_end_matches(['\r', '\n']);
if line == "data: [DONE]" {
return Ok((response_id, reply));
}
if let Some(data) = line.strip_prefix("data: ") {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(data) {
if json["object"].as_str() == Some("response") {
json["id"].as_str().unwrap_or_default().clone_into(&mut response_id);
json["output"][0]["content"][0]["text"]
.as_str()
.unwrap_or_default()
.clone_into(&mut reply);
}
}
}
}
}
Ok((response_id, reply))
}
async fn post_streaming(
client: &reqwest::Client,
gateway: &str,
body: serde_json::Value,
) -> Result<(String, String), Box<dyn std::error::Error>> {
let stream = client
.post(format!("{gateway}/v1/responses"))
.json(&body)
.send()
.await?
.error_for_status()?
.bytes_stream();
drain_sse(stream).await
}
async fn responses_flow(
client: &reqwest::Client,
gateway: &str,
model: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("══ Part A — responses flow (previous_response_id) ══\n");
println!("--- turn 1 (non-streaming) ---");
let t1 = post_blocking(
client,
gateway,
serde_json::json!({
"model": model,
"input": [{"type": "message", "role": "user", "content": "Please remember the keyword MANGO. Acknowledge with exactly: OK"}],
"store": true,
"stream": false
}),
)
.await?;
let t1_id = t1["id"].as_str().unwrap_or_default().to_owned();
println!("response_id : {t1_id}");
println!("reply : {}\n", t1["output"][0]["content"][0]["text"]);
println!("--- turn 2 (streaming) ---");
let (t2_id, t2_reply) = post_streaming(
client,
gateway,
serde_json::json!({
"model": model,
"input": [{"type": "message", "role": "user", "content": "What keyword did I ask you to remember?"}],
"store": true,
"stream": true,
"previous_response_id": t1_id
}),
)
.await?;
println!("reply : {t2_reply}");
println!("response_id : {t2_id}");
Ok(())
}
async fn conversation_flow(
client: &reqwest::Client,
gateway: &str,
model: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("\n══ Part B — conversation flow (conversation_id) ══\n");
println!("--- create conversation ---");
let conv: serde_json::Value = client
.post(format!("{gateway}/v1/conversations"))
.json(&serde_json::json!({ "model": model }))
.send()
.await?
.error_for_status()?
.json()
.await?;
let conv_id = conv["id"].as_str().unwrap_or_default().to_owned();
println!("conversation_id : {conv_id}\n");
println!("--- turn 1 (non-streaming) ---");
let t1 = post_blocking(
client,
gateway,
serde_json::json!({
"model": model,
"input": [{"type": "message", "role": "user", "content": "Please remember the keyword PAPAYA. Acknowledge with exactly: OK"}],
"store": true,
"stream": false,
"conversation_id": conv_id
}),
)
.await?;
let t1_id = t1["id"].as_str().unwrap_or_default().to_owned();
println!("response_id : {t1_id}");
println!("reply : {}\n", t1["output"][0]["content"][0]["text"]);
println!("--- turn 2 (streaming) ---");
let (t2_id, t2_reply) = post_streaming(
client,
gateway,
serde_json::json!({
"model": model,
"input": [{"type": "message", "role": "user", "content": "What keyword did I ask you to remember?"}],
"store": true,
"stream": true,
"conversation_id": conv_id
}),
)
.await?;
println!("reply : {t2_reply}");
println!("response_id : {t2_id}");
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let gateway = std::env::var("GATEWAY_URL").unwrap_or_else(|_| "http://localhost:9000".into());
let model = std::env::var("MODEL").unwrap_or_else(|_| "default".into());
let client = reqwest::Client::new();
responses_flow(&client, &gateway, &model).await?;
conversation_flow(&client, &gateway, &model).await?;
Ok(())
}