use futures_util::StreamExt;
use super::client::ResponsesClient;
use crate::entities::sampling::{ReasoningEffort, SamplingConfig};
use crate::shared::api::contract::{
ApiMessage, ChatChunk, ChatRequest, EngineBackend, FinishReason,
};
fn client_for(var: &str) -> Option<(String, ResponsesClient)> {
let Ok(key) = std::env::var("MINDFORK_OPENAI_KEY") else {
eprintln!("skip: MINDFORK_OPENAI_KEY not set");
return None;
};
let Ok(model) = std::env::var(var) else {
eprintln!("skip: {var} not set");
return None;
};
let client = ResponsesClient::new("https://api.openai.com/v1", key, model.clone());
Some((model, client))
}
fn title_turn() -> ChatRequest {
ChatRequest {
continue_final: false,
system: Some("Give this conversation a short title. Answer with the title only.".into()),
messages: vec![ApiMessage::user("How do database indexes work?")],
sampling: SamplingConfig {
max_tokens: Some(2048),
temperature: Some(0.3),
thinking: Some(false),
reasoning_effort: Some(ReasoningEffort::None),
reasoning_budget: Some(0),
..Default::default()
},
tools: vec![],
}
}
fn turn_at(effort: ReasoningEffort) -> ChatRequest {
ChatRequest {
continue_final: false,
system: None,
messages: vec![ApiMessage::user("Reply with exactly: pong")],
sampling: SamplingConfig {
max_tokens: Some(2048),
reasoning_effort: Some(effort),
..Default::default()
},
tools: vec![],
}
}
async fn turn(client: &ResponsesClient, req: ChatRequest) -> (String, u32, Option<FinishReason>) {
let mut stream = client
.chat_stream(req, Default::default())
.await
.expect("the turn must not be refused");
let (mut text, mut reasoning, mut finish) = (String::new(), 0, None);
while let Some(chunk) = stream.next().await {
match chunk {
ChatChunk::Text(t) => text.push_str(&t),
ChatChunk::Usage(u) => reasoning = u.reasoning_tokens,
ChatChunk::Error { message, .. } => eprintln!("engine error: {message}"),
ChatChunk::Finished(r) => {
finish = Some(r);
break;
}
_ => {}
}
}
(text, reasoning, finish)
}
#[tokio::test]
#[ignore = "requires MINDFORK_OPENAI_KEY + MINDFORK_LIVE_NO_EFFORT_NONE_MODEL (an OpenAI model that refuses reasoning.effort \"none\")"]
async fn a_muted_turn_survives_a_model_that_refuses_effort_none() {
let Some((model, client)) = client_for("MINDFORK_LIVE_NO_EFFORT_NONE_MODEL") else {
return;
};
for nth in 1..=2 {
let (text, reasoning, finish) = turn(&client, title_turn()).await;
println!("{model} turn {nth}: finish={finish:?} reasoning_tokens={reasoning} title={text}");
assert!(
finish == Some(FinishReason::Stop) && !text.trim().is_empty(),
"{model}: the muted turn must complete: finish={finish:?} text={text:?}"
);
}
let learned = client.learned("none");
println!("{model}: asked instead of \"none\": {learned:?}");
assert!(
learned.is_some(),
"{model} was declared to refuse \"none\", yet nothing was refused — the recovery did not run"
);
}
#[tokio::test]
#[ignore = "requires MINDFORK_OPENAI_KEY + MINDFORK_LIVE_EFFORT_NONE_MODEL (an OpenAI model that takes reasoning.effort \"none\")"]
async fn a_muted_turn_on_a_model_that_takes_none_is_sent_as_before() {
let Some((model, client)) = client_for("MINDFORK_LIVE_EFFORT_NONE_MODEL") else {
return;
};
let (text, reasoning, finish) = turn(&client, title_turn()).await;
println!("{model}: finish={finish:?} reasoning_tokens={reasoning} title={text}");
assert!(
finish == Some(FinishReason::Stop) && !text.trim().is_empty(),
"{model}: finish={finish:?} text={text:?}"
);
assert_eq!(reasoning, 0, "{model} was told not to reason");
assert_eq!(
client.learned("none"),
None,
"{model} was declared to take \"none\": nothing should have been learned"
);
}
#[tokio::test]
#[ignore = "requires MINDFORK_OPENAI_KEY + MINDFORK_LIVE_NO_EFFORT_MINIMAL_MODEL (an OpenAI model that refuses reasoning.effort \"minimal\")"]
async fn a_chosen_depth_the_model_lacks_becomes_the_nearest_it_has() {
let Some((model, client)) = client_for("MINDFORK_LIVE_NO_EFFORT_MINIMAL_MODEL") else {
return;
};
for nth in 1..=2 {
let (text, reasoning, finish) = turn(&client, turn_at(ReasoningEffort::Minimal)).await;
println!("{model} turn {nth}: finish={finish:?} reasoning_tokens={reasoning} reply={text}");
assert!(
finish == Some(FinishReason::Stop) && !text.trim().is_empty(),
"{model}: the turn must complete: finish={finish:?} text={text:?}"
);
}
let learned = client.learned("minimal");
println!("{model}: asked instead of \"minimal\": {learned:?}");
assert!(
matches!(learned, Some(Some(_))),
"{model} was declared to refuse \"minimal\" and to list what it takes: {learned:?}"
);
}
#[tokio::test]
#[ignore = "requires MINDFORK_OPENAI_KEY + MINDFORK_LIVE_NO_EFFORT_MAX_MODEL (an OpenAI model that refuses reasoning.effort \"max\")"]
async fn the_top_tier_on_a_model_without_it_becomes_the_one_below() {
let Some((model, client)) = client_for("MINDFORK_LIVE_NO_EFFORT_MAX_MODEL") else {
return;
};
let (text, reasoning, finish) = turn(&client, turn_at(ReasoningEffort::Max)).await;
println!("{model}: finish={finish:?} reasoning_tokens={reasoning} reply={text}");
assert!(
finish == Some(FinishReason::Stop) && !text.trim().is_empty(),
"{model}: the turn must complete: finish={finish:?} text={text:?}"
);
let learned = client.learned("max");
println!("{model}: asked instead of \"max\": {learned:?}");
assert!(
matches!(learned, Some(Some(_))),
"{model} was declared to refuse \"max\" and to list what it takes: {learned:?}"
);
}