use futures::StreamExt as _;
use wcode_harness::event::LlmStreamEvent;
use wcode_harness::message::AgentMessage;
use wcode_harness::streamfn::{LlmEndpoint, LlmOpts, rig_stream_fn};
fn cfg(env: &str) -> Option<String> {
std::env::var(env).ok().filter(|s| !s.is_empty())
}
fn endpoint() -> LlmEndpoint {
match cfg("WCODE_ENDPOINT").as_deref() {
Some("chat" | "completions" | "chat-completions") => LlmEndpoint::Chat,
_ => LlmEndpoint::Responses,
}
}
fn print_event(ev: &LlmStreamEvent) {
match ev {
LlmStreamEvent::ThinkingDelta(text) => println!(" thinking delta : {text:?}"),
LlmStreamEvent::ThinkingReplace(text) => println!(" thinking block : {text:?}"),
LlmStreamEvent::TextDelta(text) => println!(" text delta : {text:?}"),
LlmStreamEvent::ToolCall { name, .. } => println!(" tool call : {name}"),
LlmStreamEvent::Retrying {
attempt,
max,
reason,
} => {
println!(" retry {attempt}/{max} : {reason}")
}
LlmStreamEvent::Done { stop_reason, usage } => {
println!(" done : {stop_reason:?} usage={usage:?}")
}
LlmStreamEvent::Error { message, fatal } => {
println!(" ERROR (fatal={fatal}): {message}")
}
}
}
#[tokio::main]
async fn main() {
let Some(model) = cfg("WCODE_MODEL") else {
eprintln!(
"set WCODE_MODEL (plus WCODE_BASE_URL / WCODE_API_KEY for a real endpoint) to run the probe"
);
std::process::exit(1);
};
let opts = LlmOpts {
model,
base_url: cfg("WCODE_BASE_URL"),
api_key: cfg("WCODE_API_KEY"),
endpoint: endpoint(),
effort: cfg("WCODE_EFFORT"),
session_id: Some("responses-probe".to_string()),
..LlmOpts::default()
};
let prompt = cfg("WCODE_PROMPT")
.unwrap_or_else(|| "Think step by step, then reply with exactly: pong".to_string());
println!("endpoint : {:?}", opts.endpoint);
println!(
"base_url : {}",
opts.base_url.as_deref().unwrap_or("(default openai)")
);
println!("model : {}", opts.model);
println!("effort : {}", opts.effort.as_deref().unwrap_or("(none)"));
println!();
let mut stream = rig_stream_fn()(
&[AgentMessage::user_text(prompt)],
"You are terse.",
&[],
&opts,
);
let mut saw_thinking = false;
let mut saw_empty_replace = false;
while let Some(ev) = stream.next().await {
print_event(&ev);
match &ev {
LlmStreamEvent::ThinkingDelta(_) => saw_thinking = true,
LlmStreamEvent::ThinkingReplace(text) => {
saw_empty_replace |= text.is_empty();
saw_thinking |= !text.is_empty();
}
_ => {}
}
}
println!();
if saw_empty_replace {
println!("FAIL: a ThinkingReplace(\"\") arrived — it would erase the streamed deltas");
} else if saw_thinking {
println!("PASS: reasoning surfaced (ThinkingDelta / non-empty ThinkingReplace)");
} else {
println!("NOTE: no reasoning arrived (the model may not emit a summary on this wire)");
}
}