use axum::body::Body;
use axum::http::Request;
use ocserve_http::{AppState, LlmRegistry, Payloads, Wires};
use serde_json::{Value, json};
use std::io::Read as _;
use std::io::Write as _;
use std::sync::Arc;
use tower::ServiceExt;
fn reasoning_text_sse() -> &'static str {
"HTTP/1.1 200 OK\r\ncontent-type: text/event-stream\r\n\
cache-control: no-cache\r\nconnection: close\r\n\r\n\
data: {\"choices\":[{\"delta\":{\"reasoning_content\":\"think \"}}]}\n\n\
data: {\"choices\":[{\"delta\":{\"content\":\"PO\"}}]}\n\n\
data: {\"choices\":[{\"delta\":{\"content\":\"NG\"}}]}\n\n\
data: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\"}]}\n\n\
data: {\"choices\":[],\"usage\":{\"prompt_tokens\":100,\
\"completion_tokens\":5,\"total_tokens\":105}}\n\n\
data: [DONE]\n\n"
}
fn spawn_provider(body: &'static str) -> std::net::SocketAddr {
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
std::thread::spawn(move || {
for sock in listener.incoming().flatten() {
std::thread::spawn(move || {
let mut sock = sock;
let _ = sock.set_read_timeout(Some(std::time::Duration::from_secs(5)));
let mut got = Vec::new();
let mut buf = [0u8; 8192];
let head_end = loop {
match sock.read(&mut buf) {
Ok(0) => return,
Ok(k) => got.extend_from_slice(&buf[..k]),
Err(_) => return,
}
if let Some(pos) = got.windows(4).position(|w| w == b"\r\n\r\n") {
break pos + 4;
}
};
let head = String::from_utf8_lossy(&got[..head_end]).to_string();
let clen = head
.lines()
.find_map(|l| {
let (k, v) = l.split_once(':')?;
k.eq_ignore_ascii_case("content-length")
.then(|| v.trim().parse::<usize>().ok())
.flatten()
})
.unwrap_or(0);
while got.len() < head_end + clen {
match sock.read(&mut buf) {
Ok(0) => break,
Ok(k) => got.extend_from_slice(&buf[..k]),
Err(_) => break,
}
}
let _ = sock.write_all(body.as_bytes());
let _ = sock.flush();
});
}
});
addr
}
fn setup(addr: std::net::SocketAddr, sid: &str) -> Arc<AppState> {
let dir = std::env::temp_dir().join(format!("ocserve-sd-{}-{sid}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let db = ocserve_store::writer::db_path(&dir);
let writer = Arc::new(ocserve_store::Writer::spawn(db.clone()).unwrap());
let blobs = Arc::new(ocserve_store::BlobStore::new(dir.join("blobs")).unwrap());
let mut limits = std::collections::HashMap::new();
limits.insert(
("fake".to_string(), "m".to_string()),
json!({"context": 1_000_000}),
);
let llm = LlmRegistry {
limits,
endpoints: [("fake".into(), (format!("http://{addr}"), String::new()))]
.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,
&json!({
"id": sid, "projectID": "global", "directory": "/w",
"path": sid, "slug": sid, "title": "t", "version": "1",
"time": {"created": 1, "updated": 2},
}),
)
.unwrap();
st
}
fn delta_violations(frames: &[Value]) -> Vec<String> {
let mut out = Vec::new();
let mut assistant_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut announced_parts: std::collections::HashMap<String, std::collections::HashSet<String>> =
std::collections::HashMap::new();
let mut saw_live_skeleton = false; let mut saw_text_start = false;
let mut seen_delta = false;
for f in frames {
let p = match f.get("payload") {
Some(p) => p,
None => continue,
};
if p.get("type").and_then(Value::as_str) == Some("sync") {
continue;
}
let ty = p.get("type").and_then(Value::as_str).unwrap_or("");
let props = p.get("properties").cloned().unwrap_or(Value::Null);
match ty {
"message.updated" => {
let info = props.get("info").cloned().unwrap_or(Value::Null);
if info.get("role").and_then(Value::as_str) == Some("assistant") {
let id = info
.get("id")
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
if !id.starts_with("msg_") {
out.push(format!(
"assistant message.updated id must be ^msg_, got {id:?}"
));
}
if !seen_delta && info.pointer("/time/completed").is_none() {
saw_live_skeleton = true;
}
assistant_ids.insert(id);
}
}
"message.part.updated" => {
let part = props.get("part").cloned().unwrap_or(Value::Null);
let mid = part
.get("messageID")
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
let pid = part
.get("id")
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
if part.get("type").and_then(Value::as_str) == Some("text")
&& part.get("text").and_then(Value::as_str) == Some("")
{
saw_text_start = true;
}
announced_parts.entry(mid).or_default().insert(pid);
}
"message.part.delta" => {
seen_delta = true;
let mid = props.get("messageID").and_then(Value::as_str).unwrap_or("");
let pid = props.get("partID").and_then(Value::as_str).unwrap_or("");
if pid.is_empty() || !pid.starts_with("prt") {
out.push(format!(
"delta partID must match ^prt (got {pid:?}) — the web UI drops it"
));
}
if !assistant_ids.contains(mid) {
out.push(format!(
"delta messageID {mid:?} is not an announced assistant message \
(upstream targets the assistant message, not the user)"
));
}
let announced = announced_parts
.get(mid)
.map(|s| s.contains(pid))
.unwrap_or(false);
if !announced {
out.push(format!(
"delta (messageID={mid:?}, partID={pid:?}) has no preceding \
message.part.updated start part — the reducer finds no part to accumulate into"
));
}
}
_ => {}
}
}
if !saw_live_skeleton {
out.push(
"no live assistant message.updated (no time.completed) was emitted before the first delta".into(),
);
}
if !saw_text_start {
out.push("no empty-text message.part.updated start part was emitted".into());
}
out
}
async fn drain_bus(rx: &mut ocserve_core::event::EventBusReceiver) -> Vec<Value> {
let mut out = Vec::new();
while let Ok(s) = rx.try_recv() {
if let Ok(v) = serde_json::from_str::<Value>(&s) {
out.push(v);
}
}
out
}
async fn post_message(app: &axum::Router, sid: &str) -> axum::response::Response {
app.clone()
.oneshot(
Request::builder()
.method("POST")
.uri(format!("/session/{sid}/message"))
.header("content-type", "application/json")
.body(Body::from(
json!({"parts": [{"type": "text", "text": "hi"}]}).to_string(),
))
.unwrap(),
)
.await
.unwrap()
}
#[tokio::test]
async fn streaming_deltas_carry_partid_and_precede_persist() {
let addr = spawn_provider(reasoning_text_sse());
let st = setup(addr, "ses_sd_ok");
let app = ocserve_http::router(st.clone());
let mut rx = st.bus.subscribe();
let resp = post_message(&app, "ses_sd_ok").await;
assert!(resp.status().is_success(), "turn failed");
let frames = drain_bus(&mut rx).await;
let viol = delta_violations(&frames);
assert!(
viol.is_empty(),
"streaming-delta contract violations:\n {}",
viol.join("\n ")
);
let mut streamed_text_ids: Vec<String> = Vec::new();
let mut final_text_ids: Vec<String> = Vec::new();
for f in &frames {
let p = f.get("payload").unwrap();
if p.get("type").and_then(Value::as_str) == Some("message.part.updated")
&& p.pointer("/properties/part/type").and_then(Value::as_str) == Some("text")
{
let pid = p
.pointer("/properties/part/id")
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
if p.pointer("/properties/part/time/end").is_some() {
final_text_ids.push(pid);
} else {
streamed_text_ids.push(pid);
}
}
}
assert!(
!final_text_ids.is_empty(),
"no persisted (times.end) text part observed"
);
for d in &frames {
let p = d.get("payload").unwrap();
if p.get("type").and_then(Value::as_str) == Some("message.part.delta")
&& p.pointer("/properties/field").and_then(Value::as_str) == Some("text")
{
let pid = p
.pointer("/properties/partID")
.and_then(Value::as_str)
.unwrap_or("");
assert!(
final_text_ids.iter().any(|f| f == pid),
"streamed text partID {pid:?} not reused by the persisted part {final_text_ids:?}"
);
}
}
}
#[test]
fn delta_checker_flags_the_pre_fix_shape() {
let pre_fix = vec![
json!({"payload": {"type": "message.updated", "properties": {"info": {
"id": "msg_assistant01", "role": "assistant",
"time": {"created": 1}, "sessionID": "ses_x"
}}}}),
json!({"payload": {"type": "message.part.delta", "properties": {
"sessionID": "ses_x", "messageID": "msg_user01",
"partID": "", "field": "text", "delta": "P"
}}}),
];
let viol = delta_violations(&pre_fix);
assert!(
viol.iter().any(|v| v.contains("partID must match")),
"checker failed to flag empty partID: {viol:?}"
);
assert!(
viol.iter()
.any(|v| v.contains("not an announced assistant")),
"checker failed to flag user-messageID targeting: {viol:?}"
);
let healthy = vec![
json!({"payload": {"type": "message.updated", "properties": {"info": {
"id": "msg_assistant01", "role": "assistant",
"time": {"created": 1}, "sessionID": "ses_x"
}}}}),
json!({"payload": {"type": "message.part.updated", "properties": {"part": {
"type": "text", "id": "prt_text01", "messageID": "msg_assistant01",
"sessionID": "ses_x", "text": ""
}}}}),
json!({"payload": {"type": "message.part.delta", "properties": {
"sessionID": "ses_x", "messageID": "msg_assistant01",
"partID": "prt_text01", "field": "text", "delta": "P"
}}}),
];
assert!(
delta_violations(&healthy).is_empty(),
"healthy frames must be clean: {:?}",
delta_violations(&healthy)
);
}