mirror of
https://github.com/openai/codex.git
synced 2026-05-02 12:21:26 +03:00
Fast Mode status was still tied to one model name in the TUI and model-list plumbing. This changes the model metadata shape so a model can advertise additional speed tiers, carries that field through the app-server model list, and uses it to decide when to show Fast Mode status. For people using Codex, the behavior is intended to stay the same for existing models. Fast Mode still requires the existing signed-in / feature-gated path; the difference is that the UI can now recognize any model the model list marks as Fast-capable, instead of requiring a new client-side slug check.
228 lines
6.8 KiB
Rust
228 lines
6.8 KiB
Rust
use super::*;
|
|
use codex_protocol::openai_models::ModelPreset;
|
|
use codex_protocol::openai_models::ReasoningEffort;
|
|
use codex_protocol::openai_models::ReasoningEffortPreset;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
|
|
fn model_preset(id: &str, show_in_picker: bool) -> ModelPreset {
|
|
ModelPreset {
|
|
id: id.to_string(),
|
|
model: format!("{id}-model"),
|
|
display_name: format!("{id} display"),
|
|
description: format!("{id} description"),
|
|
default_reasoning_effort: ReasoningEffort::Medium,
|
|
supported_reasoning_efforts: vec![ReasoningEffortPreset {
|
|
effort: ReasoningEffort::Medium,
|
|
description: "Balanced".to_string(),
|
|
}],
|
|
supports_personality: false,
|
|
additional_speed_tiers: Vec::new(),
|
|
is_default: false,
|
|
upgrade: None,
|
|
show_in_picker,
|
|
availability_nux: None,
|
|
supported_in_api: true,
|
|
input_modalities: Vec::new(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn spawn_agent_tool_v2_requires_task_name_and_lists_visible_models() {
|
|
let tool = create_spawn_agent_tool_v2(SpawnAgentToolOptions {
|
|
available_models: &[
|
|
model_preset("visible", /*show_in_picker*/ true),
|
|
model_preset("hidden", /*show_in_picker*/ false),
|
|
],
|
|
agent_type_description: "role help".to_string(),
|
|
hide_agent_type_model_reasoning: false,
|
|
});
|
|
|
|
let ToolSpec::Function(ResponsesApiTool {
|
|
description,
|
|
parameters,
|
|
output_schema,
|
|
..
|
|
}) = tool
|
|
else {
|
|
panic!("spawn_agent should be a function tool");
|
|
};
|
|
let JsonSchema::Object {
|
|
properties,
|
|
required,
|
|
..
|
|
} = parameters
|
|
else {
|
|
panic!("spawn_agent should use object params");
|
|
};
|
|
assert!(description.contains("visible display (`visible-model`)"));
|
|
assert!(!description.contains("hidden display (`hidden-model`)"));
|
|
assert!(properties.contains_key("task_name"));
|
|
assert!(properties.contains_key("message"));
|
|
assert!(properties.contains_key("fork_turns"));
|
|
assert!(!properties.contains_key("items"));
|
|
assert!(!properties.contains_key("fork_context"));
|
|
assert_eq!(
|
|
properties.get("agent_type"),
|
|
Some(&JsonSchema::String {
|
|
description: Some("role help".to_string()),
|
|
})
|
|
);
|
|
assert_eq!(
|
|
required,
|
|
Some(vec!["task_name".to_string(), "message".to_string()])
|
|
);
|
|
assert_eq!(
|
|
output_schema.expect("spawn_agent output schema")["required"],
|
|
json!(["task_name", "nickname"])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn spawn_agent_tool_v1_keeps_legacy_fork_context_field() {
|
|
let tool = create_spawn_agent_tool_v1(SpawnAgentToolOptions {
|
|
available_models: &[],
|
|
agent_type_description: "role help".to_string(),
|
|
hide_agent_type_model_reasoning: false,
|
|
});
|
|
|
|
let ToolSpec::Function(ResponsesApiTool { parameters, .. }) = tool else {
|
|
panic!("spawn_agent should be a function tool");
|
|
};
|
|
let JsonSchema::Object { properties, .. } = parameters else {
|
|
panic!("spawn_agent should use object params");
|
|
};
|
|
|
|
assert!(properties.contains_key("fork_context"));
|
|
assert!(!properties.contains_key("fork_turns"));
|
|
}
|
|
|
|
#[test]
|
|
fn send_message_tool_requires_message_and_has_no_output_schema() {
|
|
let ToolSpec::Function(ResponsesApiTool {
|
|
parameters,
|
|
output_schema,
|
|
..
|
|
}) = create_send_message_tool()
|
|
else {
|
|
panic!("send_message should be a function tool");
|
|
};
|
|
let JsonSchema::Object {
|
|
properties,
|
|
required,
|
|
..
|
|
} = parameters
|
|
else {
|
|
panic!("send_message should use object params");
|
|
};
|
|
assert!(properties.contains_key("target"));
|
|
assert!(properties.contains_key("message"));
|
|
assert!(!properties.contains_key("interrupt"));
|
|
assert!(!properties.contains_key("items"));
|
|
assert_eq!(
|
|
required,
|
|
Some(vec!["target".to_string(), "message".to_string()])
|
|
);
|
|
assert_eq!(output_schema, None);
|
|
}
|
|
|
|
#[test]
|
|
fn followup_task_tool_requires_message_and_has_no_output_schema() {
|
|
let ToolSpec::Function(ResponsesApiTool {
|
|
parameters,
|
|
output_schema,
|
|
..
|
|
}) = create_followup_task_tool()
|
|
else {
|
|
panic!("followup_task should be a function tool");
|
|
};
|
|
let JsonSchema::Object {
|
|
properties,
|
|
required,
|
|
..
|
|
} = parameters
|
|
else {
|
|
panic!("followup_task should use object params");
|
|
};
|
|
assert!(properties.contains_key("target"));
|
|
assert!(properties.contains_key("message"));
|
|
assert!(properties.contains_key("interrupt"));
|
|
assert!(!properties.contains_key("items"));
|
|
assert_eq!(
|
|
required,
|
|
Some(vec!["target".to_string(), "message".to_string()])
|
|
);
|
|
assert_eq!(output_schema, None);
|
|
}
|
|
|
|
#[test]
|
|
fn wait_agent_tool_v2_uses_timeout_only_summary_output() {
|
|
let ToolSpec::Function(ResponsesApiTool {
|
|
parameters,
|
|
output_schema,
|
|
..
|
|
}) = create_wait_agent_tool_v2(WaitAgentTimeoutOptions {
|
|
default_timeout_ms: 30_000,
|
|
min_timeout_ms: 10_000,
|
|
max_timeout_ms: 3_600_000,
|
|
})
|
|
else {
|
|
panic!("wait_agent should be a function tool");
|
|
};
|
|
let JsonSchema::Object {
|
|
properties,
|
|
required,
|
|
..
|
|
} = parameters
|
|
else {
|
|
panic!("wait_agent should use object params");
|
|
};
|
|
assert!(!properties.contains_key("targets"));
|
|
assert!(properties.contains_key("timeout_ms"));
|
|
assert_eq!(required, None);
|
|
assert_eq!(
|
|
output_schema.expect("wait output schema")["properties"]["message"]["description"],
|
|
json!("Brief wait summary without the agent's final content.")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn list_agents_tool_includes_path_prefix_and_agent_fields() {
|
|
let ToolSpec::Function(ResponsesApiTool {
|
|
parameters,
|
|
output_schema,
|
|
..
|
|
}) = create_list_agents_tool()
|
|
else {
|
|
panic!("list_agents should be a function tool");
|
|
};
|
|
let JsonSchema::Object { properties, .. } = parameters else {
|
|
panic!("list_agents should use object params");
|
|
};
|
|
assert!(properties.contains_key("path_prefix"));
|
|
assert_eq!(
|
|
output_schema.expect("list_agents output schema")["properties"]["agents"]["items"]["required"],
|
|
json!(["agent_name", "agent_status", "last_task_message"])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn list_agents_tool_status_schema_includes_interrupted() {
|
|
let ToolSpec::Function(ResponsesApiTool { output_schema, .. }) = create_list_agents_tool()
|
|
else {
|
|
panic!("list_agents should be a function tool");
|
|
};
|
|
|
|
assert_eq!(
|
|
output_schema.expect("list_agents output schema")["properties"]["agents"]["items"]["properties"]
|
|
["agent_status"]["allOf"][0]["oneOf"][0]["enum"],
|
|
json!([
|
|
"pending_init",
|
|
"running",
|
|
"interrupted",
|
|
"shutdown",
|
|
"not_found"
|
|
])
|
|
);
|
|
}
|