use axum::body::Body;
use axum::http::{Request, StatusCode};
use ocserve_http::{AppState, LlmRegistry, Payloads, Wires};
use tower::ServiceExt;
fn app_with(addr: std::net::SocketAddr) -> std::sync::Arc<AppState> {
let dir = std::env::temp_dir().join(format!("ocserve-tgd-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let db = ocserve_store::writer::db_path(&dir);
let writer = std::sync::Arc::new(ocserve_store::Writer::spawn(db.clone()).unwrap());
let blobs = std::sync::Arc::new(ocserve_store::BlobStore::new(dir.join("blobs")).unwrap());
let llm = LlmRegistry {
limits: std::collections::HashMap::new(),
endpoints: [(
"fake".into(),
(format!("http://{addr}"), "test-key".to_string()),
)]
.into_iter()
.collect(),
pricing: Default::default(),
default_model: ("fake".into(), "m".into()),
systems: Default::default(),
default_agent: "build".into(),
};
let st = AppState::with_wiring(
None,
Payloads::default(),
Wires {
db,
blobs,
writer,
llm,
},
);
ocserve_store::insert_session(
&st.writer,
&serde_json::json!({
"id": "ses_tgd", "projectID": "global", "directory": "/w", "path": "ses_tgd",
"slug": "ses_tgd", "title": "tgd", "version": "1",
"time": {"created": 1, "updated": 2},
}),
)
.unwrap();
st
}
fn spawn_sse(first_data_after: std::time::Duration) -> std::net::SocketAddr {
use std::io::{Read, Write};
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
std::thread::spawn(move || {
for stream in listener.incoming().flatten() {
let mut stream = stream;
std::thread::spawn(move || {
let mut buf = vec![0u8; 65536];
let _ = stream.read(&mut buf);
let _ =
stream.write_all(b"HTTP/1.1 200 OK\r\ncontent-type: text/event-stream\r\n\r\n");
let t0 = std::time::Instant::now();
while t0.elapsed() < first_data_after {
let _ = stream.write_all(b": keep-alive\n\n");
std::thread::sleep(std::time::Duration::from_millis(200));
}
let _ = stream.write_all(
b"data: {\"id\":\"c\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"m\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"OK\"},\"finish_reason\":null}]}\n\n",
);
let _ = stream.write_all(
b"data: {\"id\":\"c\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"m\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":10,\"completion_tokens\":1,\"total_tokens\":11}}\n\n",
);
let _ = stream.write_all(b"data: [DONE]\n\n");
});
}
});
addr
}
async fn prompt(st: &std::sync::Arc<AppState>) -> StatusCode {
ocserve_http::router(st.clone())
.oneshot(
Request::builder()
.method("POST")
.uri("/session/ses_tgd/prompt_async")
.header("content-type", "application/json")
.body(Body::from(
r#"{"messageID":"msg_tgd0000000000000000000001",
"parts":[{"type":"text","text":"hi"}],
"model":{"providerID":"fake","modelID":"m"},"agent":"build"}"#,
))
.unwrap(),
)
.await
.unwrap()
.status()
}
async fn wait_assistant(st: &std::sync::Arc<AppState>, budget: std::time::Duration) -> Option<i64> {
let t0 = std::time::Instant::now();
while t0.elapsed() < budget {
if let Ok(conn) = ocserve_store::pragma::open_reader(&st.db)
&& let Ok(n) = conn.query_row(
"SELECT count(*) FROM msg WHERE session_id='ses_tgd' AND role='assistant' AND json_extract(info,'$.time.completed') IS NOT NULL",
[],
|r| r.get::<_, i64>(0),
)
&& n > 0
{
return Some(n);
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
None
}
#[tokio::test]
async fn first_data_after_stream_budget_within_ttfb_succeeds() {
unsafe { std::env::set_var("OCSERVE_PROVIDER_STALL_SECS", "1") }; unsafe { std::env::set_var("OCSERVE_PROVIDER_TTFB_SECS", "6") }; let addr = spawn_sse(std::time::Duration::from_millis(3000));
let st = app_with(addr);
assert_eq!(prompt(&st).await, StatusCode::NO_CONTENT);
let done = wait_assistant(&st, std::time::Duration::from_secs(20)).await;
assert!(
done.is_some(),
"turn must complete: first data at 3s is within the 6s TTFB budget \
(pre-fix it died at the 1s stream budget)"
);
}