- thinking (off, low, medium, high, xhigh, max) is an enum: serde rejects unknown levels in the config, and the schema of agent.chat lists them. reasoning_effort, a second name for the same setting, is gone. - agent.chat takes an optional thinking level for one turn. - off really switches thinking off: OpenAI gets the effort none, and Anthropic gets disabled thinking (newer Claude models think by default). A model that cannot switch thinking off gets low. - Unset means the model's default. Anthropic thinking blocks now return unless thinking is off, because newer models think by default. - The template sets thinking: medium and shows it in each example, with current model names. - Checked against the proxy with gpt-6-luna, glm-5.3-flash, and claude-haiku-5-5.
This commit is contained in:
+59
-5
@@ -241,6 +241,10 @@ async fn unconfigured_chat_names_the_configuration() {
|
||||
"api: openai-responses\nbase_url: http://localhost/v1\nmodel: test\ntoken: CHANGE_ME",
|
||||
"token",
|
||||
),
|
||||
(
|
||||
"api: openai-responses\nbase_url: http://localhost/v1\nmodel: test\ntoken: key\nthinking: hihg",
|
||||
"thinking",
|
||||
),
|
||||
] {
|
||||
std::fs::write(&path, config).unwrap();
|
||||
let error = host
|
||||
@@ -507,11 +511,7 @@ async fn private_items_are_scoped_to_the_model() {
|
||||
for api in [APIS[0], APIS[1]] {
|
||||
let mut server = Llm::new(api).await;
|
||||
let world = TestWorld::new(&server);
|
||||
let config_path = world.0.join("config/agent.yaml");
|
||||
let mut config: Value =
|
||||
serde_yaml_ng::from_slice(&std::fs::read(&config_path).unwrap()).unwrap();
|
||||
config["thinking"] = json!("high");
|
||||
std::fs::write(config_path, serde_yaml_ng::to_string(&config).unwrap()).unwrap();
|
||||
// Without a thinking level, newer models think by default, so their items return.
|
||||
std::fs::create_dir_all(world.0.join("state/agent")).unwrap();
|
||||
let mut entries = vec![json!({"kind":"user","text":"old turn"})];
|
||||
for (model, marker) in [
|
||||
@@ -546,6 +546,60 @@ async fn private_items_are_scoped_to_the_model() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Each API gets the configured thinking level. A level in the call applies to that turn only.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn thinking_levels_reach_each_api() {
|
||||
for api in APIS {
|
||||
let mut server = Llm::new(api).await;
|
||||
let world = TestWorld::new(&server);
|
||||
let config_path = world.0.join("config/agent.yaml");
|
||||
let mut config: Value =
|
||||
serde_yaml_ng::from_slice(&std::fs::read(&config_path).unwrap()).unwrap();
|
||||
config["thinking"] = json!("high");
|
||||
std::fs::write(config_path, serde_yaml_ng::to_string(&config).unwrap()).unwrap();
|
||||
let host = world.start().await;
|
||||
// Off is `none` for OpenAI, and disabled thinking without an effort for Anthropic.
|
||||
let off = if api == APIS[1] { None } else { Some("none") };
|
||||
for (thinking, effort) in [
|
||||
(None, Some("high")),
|
||||
(Some("low"), Some("low")),
|
||||
(Some("off"), off),
|
||||
(None, Some("high")),
|
||||
] {
|
||||
let mut params = json!({"text":"hello"});
|
||||
if let Some(thinking) = thinking {
|
||||
params["thinking"] = json!(thinking);
|
||||
}
|
||||
let caller = host.clone();
|
||||
let turn =
|
||||
tokio::spawn(async move { caller.call("agent.chat", params).await.unwrap() });
|
||||
let (body, reply) = server.next().await;
|
||||
let sent = if api == APIS[0] {
|
||||
&body["reasoning"]["effort"]
|
||||
} else if api == APIS[2] {
|
||||
&body["reasoning_effort"]
|
||||
} else {
|
||||
let mode = if thinking == Some("off") {
|
||||
"disabled"
|
||||
} else {
|
||||
"adaptive"
|
||||
};
|
||||
assert_eq!(body["thinking"]["type"], mode, "{body}");
|
||||
&body["output_config"]["effort"]
|
||||
};
|
||||
assert_eq!(sent.as_str(), effort, "{api} {thinking:?}");
|
||||
reply.send((200, server.answer("done", &[]))).unwrap();
|
||||
assert_eq!(turn.await.unwrap(), "done");
|
||||
}
|
||||
let error = host
|
||||
.call("agent.chat", json!({"text":"hello","thinking":"hihg"}))
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert_eq!(error["code"], -32602, "{error}");
|
||||
host.stop().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Separate assistant messages retain a separator in the final answer.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn final_texts_have_a_separator() {
|
||||
|
||||
Reference in New Issue
Block a user