mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Remove legacy ModelInfo and merge it with ModelFamily (#7748)
This is a step towards removing the need to know `model` when constructing config. We firstly don't need to know `model_info` and just respect if the user has already set it. Next step, we don't need to know `model` unless the user explicitly set it in `config.toml`
This commit is contained in:
committed by
GitHub
Unverified
parent
ac5fa6baf8
commit
382f047a10
@@ -48,7 +48,6 @@ use crate::error::Result;
|
||||
use crate::flags::CODEX_RS_SSE_FIXTURE;
|
||||
use crate::model_provider_info::ModelProviderInfo;
|
||||
use crate::model_provider_info::WireApi;
|
||||
use crate::openai_model_info::get_model_info;
|
||||
use crate::openai_models::model_family::ModelFamily;
|
||||
use crate::tools::spec::create_tools_json_for_chat_completions_api;
|
||||
use crate::tools::spec::create_tools_json_for_responses_api;
|
||||
@@ -95,19 +94,11 @@ impl ModelClient {
|
||||
pub fn get_model_context_window(&self) -> Option<i64> {
|
||||
let model_family = self.get_model_family();
|
||||
let effective_context_window_percent = model_family.effective_context_window_percent;
|
||||
self.config
|
||||
.model_context_window
|
||||
.or_else(|| get_model_info(&model_family).map(|info| info.context_window))
|
||||
model_family
|
||||
.context_window
|
||||
.map(|w| w.saturating_mul(effective_context_window_percent) / 100)
|
||||
}
|
||||
|
||||
pub fn get_auto_compact_token_limit(&self) -> Option<i64> {
|
||||
let model_family = self.get_model_family();
|
||||
self.config.model_auto_compact_token_limit.or_else(|| {
|
||||
get_model_info(&model_family).and_then(|info| info.auto_compact_token_limit)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn config(&self) -> Arc<Config> {
|
||||
Arc::clone(&self.config)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ use crate::exec::StreamOutput;
|
||||
use crate::exec_policy::ExecPolicyUpdateError;
|
||||
use crate::mcp::auth::compute_auth_statuses;
|
||||
use crate::mcp_connection_manager::McpConnectionManager;
|
||||
use crate::openai_model_info::get_model_info;
|
||||
use crate::project_doc::get_user_instructions;
|
||||
use crate::protocol::AgentMessageContentDeltaEvent;
|
||||
use crate::protocol::AgentReasoningSectionBreakEvent;
|
||||
@@ -415,15 +414,11 @@ impl Session {
|
||||
otel_event_manager: &OtelEventManager,
|
||||
provider: ModelProviderInfo,
|
||||
session_configuration: &SessionConfiguration,
|
||||
mut per_turn_config: Config,
|
||||
per_turn_config: Config,
|
||||
model_family: ModelFamily,
|
||||
conversation_id: ConversationId,
|
||||
sub_id: String,
|
||||
) -> TurnContext {
|
||||
if let Some(model_info) = get_model_info(&model_family) {
|
||||
per_turn_config.model_context_window = Some(model_info.context_window);
|
||||
}
|
||||
|
||||
let otel_event_manager = otel_event_manager.clone().with_model(
|
||||
session_configuration.model.as_str(),
|
||||
model_family.slug.as_str(),
|
||||
@@ -1955,9 +1950,6 @@ async fn spawn_review_thread(
|
||||
per_turn_config.model_reasoning_effort = Some(ReasoningEffortConfig::Low);
|
||||
per_turn_config.model_reasoning_summary = ReasoningSummaryConfig::Detailed;
|
||||
per_turn_config.features = review_features.clone();
|
||||
if let Some(model_info) = get_model_info(&model_family) {
|
||||
per_turn_config.model_context_window = Some(model_info.context_window);
|
||||
}
|
||||
|
||||
let otel_event_manager = parent_turn_context
|
||||
.client
|
||||
@@ -2097,7 +2089,8 @@ pub(crate) async fn run_task(
|
||||
} = turn_output;
|
||||
let limit = turn_context
|
||||
.client
|
||||
.get_auto_compact_token_limit()
|
||||
.get_model_family()
|
||||
.auto_compact_token_limit()
|
||||
.unwrap_or(i64::MAX);
|
||||
let total_usage_tokens = sess.get_total_token_usage().await;
|
||||
let token_limit_reached = total_usage_tokens >= limit;
|
||||
|
||||
@@ -26,8 +26,6 @@ use crate::model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
use crate::model_provider_info::ModelProviderInfo;
|
||||
use crate::model_provider_info::OLLAMA_OSS_PROVIDER_ID;
|
||||
use crate::model_provider_info::built_in_model_providers;
|
||||
use crate::openai_model_info::get_model_info;
|
||||
use crate::openai_models::model_family::find_family_for_model;
|
||||
use crate::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
|
||||
use crate::project_doc::LOCAL_PROJECT_DOC_FILENAME;
|
||||
use crate::protocol::AskForApproval;
|
||||
@@ -1106,23 +1104,12 @@ impl Config {
|
||||
|
||||
let forced_login_method = cfg.forced_login_method;
|
||||
|
||||
// todo(aibrahim): make model optional
|
||||
let model = model
|
||||
.or(config_profile.model)
|
||||
.or(cfg.model)
|
||||
.unwrap_or_else(default_model);
|
||||
|
||||
let model_family = find_family_for_model(&model);
|
||||
|
||||
let openai_model_info = get_model_info(&model_family);
|
||||
let model_context_window = cfg
|
||||
.model_context_window
|
||||
.or_else(|| openai_model_info.as_ref().map(|info| info.context_window));
|
||||
let model_auto_compact_token_limit = cfg.model_auto_compact_token_limit.or_else(|| {
|
||||
openai_model_info
|
||||
.as_ref()
|
||||
.and_then(|info| info.auto_compact_token_limit)
|
||||
});
|
||||
|
||||
let compact_prompt = compact_prompt.or(cfg.compact_prompt).and_then(|value| {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
@@ -1168,8 +1155,8 @@ impl Config {
|
||||
let config = Self {
|
||||
model,
|
||||
review_model,
|
||||
model_context_window,
|
||||
model_auto_compact_token_limit,
|
||||
model_context_window: cfg.model_context_window,
|
||||
model_auto_compact_token_limit: cfg.model_auto_compact_token_limit,
|
||||
model_provider_id,
|
||||
model_provider,
|
||||
cwd: resolved_cwd,
|
||||
@@ -2950,8 +2937,8 @@ model_verbosity = "high"
|
||||
Config {
|
||||
model: "o3".to_string(),
|
||||
review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(),
|
||||
model_context_window: Some(200_000),
|
||||
model_auto_compact_token_limit: Some(180_000),
|
||||
model_context_window: None,
|
||||
model_auto_compact_token_limit: None,
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
@@ -3025,8 +3012,8 @@ model_verbosity = "high"
|
||||
let expected_gpt3_profile_config = Config {
|
||||
model: "gpt-3.5-turbo".to_string(),
|
||||
review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(),
|
||||
model_context_window: Some(16_385),
|
||||
model_auto_compact_token_limit: Some(14_746),
|
||||
model_context_window: None,
|
||||
model_auto_compact_token_limit: None,
|
||||
model_provider_id: "openai-chat-completions".to_string(),
|
||||
model_provider: fixture.openai_chat_completions_provider.clone(),
|
||||
approval_policy: AskForApproval::UnlessTrusted,
|
||||
@@ -3115,8 +3102,8 @@ model_verbosity = "high"
|
||||
let expected_zdr_profile_config = Config {
|
||||
model: "o3".to_string(),
|
||||
review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(),
|
||||
model_context_window: Some(200_000),
|
||||
model_auto_compact_token_limit: Some(180_000),
|
||||
model_context_window: None,
|
||||
model_auto_compact_token_limit: None,
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::OnFailure,
|
||||
@@ -3191,8 +3178,8 @@ model_verbosity = "high"
|
||||
let expected_gpt5_profile_config = Config {
|
||||
model: "gpt-5.1".to_string(),
|
||||
review_model: OPENAI_DEFAULT_REVIEW_MODEL.to_string(),
|
||||
model_context_window: Some(272_000),
|
||||
model_auto_compact_token_limit: Some(244_800),
|
||||
model_context_window: None,
|
||||
model_auto_compact_token_limit: None,
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::OnFailure,
|
||||
|
||||
@@ -67,7 +67,6 @@ pub use conversation_manager::NewConversation;
|
||||
pub use auth::AuthManager;
|
||||
pub use auth::CodexAuth;
|
||||
pub mod default_client;
|
||||
mod openai_model_info;
|
||||
pub mod project_doc;
|
||||
mod rollout;
|
||||
pub(crate) mod safety;
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
use crate::openai_models::model_family::ModelFamily;
|
||||
|
||||
// Shared constants for commonly used window/token sizes.
|
||||
pub(crate) const CONTEXT_WINDOW_272K: i64 = 272_000;
|
||||
|
||||
/// Metadata about a model, particularly OpenAI models.
|
||||
/// We may want to consider including details like the pricing for
|
||||
/// input tokens, output tokens, etc., though users will need to be able to
|
||||
/// override this in config.toml, as this information can get out of date.
|
||||
/// Though this would help present more accurate pricing information in the UI.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ModelInfo {
|
||||
/// Size of the context window in tokens. This is the maximum size of the input context.
|
||||
pub(crate) context_window: i64,
|
||||
|
||||
/// Token threshold where we should automatically compact conversation history. This considers
|
||||
/// input tokens + output tokens of this turn.
|
||||
pub(crate) auto_compact_token_limit: Option<i64>,
|
||||
}
|
||||
|
||||
impl ModelInfo {
|
||||
const fn new(context_window: i64) -> Self {
|
||||
Self {
|
||||
context_window,
|
||||
auto_compact_token_limit: Some(Self::default_auto_compact_limit(context_window)),
|
||||
}
|
||||
}
|
||||
|
||||
const fn default_auto_compact_limit(context_window: i64) -> i64 {
|
||||
(context_window * 9) / 10
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_model_info(model_family: &ModelFamily) -> Option<ModelInfo> {
|
||||
let slug = model_family.slug.as_str();
|
||||
match slug {
|
||||
// OSS models have a 128k shared token pool.
|
||||
// Arbitrarily splitting it: 3/4 input context, 1/4 output.
|
||||
// https://openai.com/index/gpt-oss-model-card/
|
||||
"gpt-oss-20b" => Some(ModelInfo::new(96_000)),
|
||||
"gpt-oss-120b" => Some(ModelInfo::new(96_000)),
|
||||
// https://platform.openai.com/docs/models/o3
|
||||
"o3" => Some(ModelInfo::new(200_000)),
|
||||
|
||||
// https://platform.openai.com/docs/models/o4-mini
|
||||
"o4-mini" => Some(ModelInfo::new(200_000)),
|
||||
|
||||
// https://platform.openai.com/docs/models/codex-mini-latest
|
||||
"codex-mini-latest" => Some(ModelInfo::new(200_000)),
|
||||
|
||||
// As of Jun 25, 2025, gpt-4.1 defaults to gpt-4.1-2025-04-14.
|
||||
// https://platform.openai.com/docs/models/gpt-4.1
|
||||
"gpt-4.1" | "gpt-4.1-2025-04-14" => Some(ModelInfo::new(1_047_576)),
|
||||
|
||||
// As of Jun 25, 2025, gpt-4o defaults to gpt-4o-2024-08-06.
|
||||
// https://platform.openai.com/docs/models/gpt-4o
|
||||
"gpt-4o" | "gpt-4o-2024-08-06" => Some(ModelInfo::new(128_000)),
|
||||
|
||||
// https://platform.openai.com/docs/models/gpt-4o?snapshot=gpt-4o-2024-05-13
|
||||
"gpt-4o-2024-05-13" => Some(ModelInfo::new(128_000)),
|
||||
|
||||
// https://platform.openai.com/docs/models/gpt-4o?snapshot=gpt-4o-2024-11-20
|
||||
"gpt-4o-2024-11-20" => Some(ModelInfo::new(128_000)),
|
||||
|
||||
// https://platform.openai.com/docs/models/gpt-3.5-turbo
|
||||
"gpt-3.5-turbo" => Some(ModelInfo::new(16_385)),
|
||||
|
||||
_ if slug.starts_with("gpt-5-codex")
|
||||
|| slug.starts_with("gpt-5.1-codex")
|
||||
|| slug.starts_with("gpt-5.1-codex-max") =>
|
||||
{
|
||||
Some(ModelInfo::new(CONTEXT_WINDOW_272K))
|
||||
}
|
||||
|
||||
_ if slug.starts_with("gpt-5") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
|
||||
|
||||
_ if slug.starts_with("codex-") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
|
||||
|
||||
_ if slug.starts_with("exp-") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const BASE_INSTRUCTIONS: &str = include_str!("../../prompt.md");
|
||||
const GPT_5_CODEX_INSTRUCTIONS: &str = include_str!("../../gpt_5_codex_prompt.md");
|
||||
const GPT_5_1_INSTRUCTIONS: &str = include_str!("../../gpt_5_1_prompt.md");
|
||||
const GPT_5_1_CODEX_MAX_INSTRUCTIONS: &str = include_str!("../../gpt-5.1-codex-max_prompt.md");
|
||||
pub(crate) const CONTEXT_WINDOW_272K: i64 = 272_000;
|
||||
|
||||
/// A model family is a group of models that share certain characteristics.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -23,14 +24,20 @@ pub struct ModelFamily {
|
||||
/// "gpt-4.1-2025-04-14".
|
||||
pub slug: String,
|
||||
|
||||
/// The model family name, e.g. "gpt-4.1". Note this should able to be used
|
||||
/// with [`crate::openai_model_info::get_model_info`].
|
||||
/// The model family name, e.g. "gpt-4.1". This string is used when deriving
|
||||
/// default metadata for the family, such as context windows.
|
||||
pub family: String,
|
||||
|
||||
/// True if the model needs additional instructions on how to use the
|
||||
/// "virtual" `apply_patch` CLI.
|
||||
pub needs_special_apply_patch_instructions: bool,
|
||||
|
||||
/// Maximum supported context window, if known.
|
||||
pub context_window: Option<i64>,
|
||||
|
||||
/// Token threshold for automatic compaction if config does not override it.
|
||||
auto_compact_token_limit: Option<i64>,
|
||||
|
||||
// Whether the `reasoning` field can be set when making a request to this
|
||||
// model family. Note it has `effort` and `summary` subfields (though
|
||||
// `summary` is optional).
|
||||
@@ -82,6 +89,12 @@ impl ModelFamily {
|
||||
if let Some(reasoning_summary_format) = config.model_reasoning_summary_format.as_ref() {
|
||||
self.reasoning_summary_format = reasoning_summary_format.clone();
|
||||
}
|
||||
if let Some(context_window) = config.model_context_window {
|
||||
self.context_window = Some(context_window);
|
||||
}
|
||||
if let Some(auto_compact_token_limit) = config.model_auto_compact_token_limit {
|
||||
self.auto_compact_token_limit = Some(auto_compact_token_limit);
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn with_remote_overrides(mut self, remote_models: Vec<ModelInfo>) -> Self {
|
||||
@@ -93,6 +106,15 @@ impl ModelFamily {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn auto_compact_token_limit(&self) -> Option<i64> {
|
||||
self.auto_compact_token_limit
|
||||
.or(self.context_window.map(Self::default_auto_compact_limit))
|
||||
}
|
||||
|
||||
const fn default_auto_compact_limit(context_window: i64) -> i64 {
|
||||
(context_window * 9) / 10
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! model_family {
|
||||
@@ -105,6 +127,8 @@ macro_rules! model_family {
|
||||
slug: $slug.to_string(),
|
||||
family: $family.to_string(),
|
||||
needs_special_apply_patch_instructions: false,
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
auto_compact_token_limit: None,
|
||||
supports_reasoning_summaries: false,
|
||||
reasoning_summary_format: ReasoningSummaryFormat::None,
|
||||
supports_parallel_tool_calls: false,
|
||||
@@ -136,12 +160,14 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
slug, "o3",
|
||||
supports_reasoning_summaries: true,
|
||||
needs_special_apply_patch_instructions: true,
|
||||
context_window: Some(200_000),
|
||||
)
|
||||
} else if slug.starts_with("o4-mini") {
|
||||
model_family!(
|
||||
slug, "o4-mini",
|
||||
supports_reasoning_summaries: true,
|
||||
needs_special_apply_patch_instructions: true,
|
||||
context_window: Some(200_000),
|
||||
)
|
||||
} else if slug.starts_with("codex-mini-latest") {
|
||||
model_family!(
|
||||
@@ -149,18 +175,32 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
supports_reasoning_summaries: true,
|
||||
needs_special_apply_patch_instructions: true,
|
||||
shell_type: ConfigShellToolType::Local,
|
||||
context_window: Some(200_000),
|
||||
)
|
||||
} else if slug.starts_with("gpt-4.1") {
|
||||
model_family!(
|
||||
slug, "gpt-4.1",
|
||||
needs_special_apply_patch_instructions: true,
|
||||
context_window: Some(1_047_576),
|
||||
)
|
||||
} else if slug.starts_with("gpt-oss") || slug.starts_with("openai/gpt-oss") {
|
||||
model_family!(slug, "gpt-oss", apply_patch_tool_type: Some(ApplyPatchToolType::Function))
|
||||
model_family!(
|
||||
slug, "gpt-oss",
|
||||
apply_patch_tool_type: Some(ApplyPatchToolType::Function),
|
||||
context_window: Some(96_000),
|
||||
)
|
||||
} else if slug.starts_with("gpt-4o") {
|
||||
model_family!(slug, "gpt-4o", needs_special_apply_patch_instructions: true)
|
||||
model_family!(
|
||||
slug, "gpt-4o",
|
||||
needs_special_apply_patch_instructions: true,
|
||||
context_window: Some(128_000),
|
||||
)
|
||||
} else if slug.starts_with("gpt-3.5") {
|
||||
model_family!(slug, "gpt-3.5", needs_special_apply_patch_instructions: true)
|
||||
model_family!(
|
||||
slug, "gpt-3.5",
|
||||
needs_special_apply_patch_instructions: true,
|
||||
context_window: Some(16_385),
|
||||
)
|
||||
} else if slug.starts_with("test-gpt-5") {
|
||||
model_family!(
|
||||
slug, slug,
|
||||
@@ -196,6 +236,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
supports_parallel_tool_calls: true,
|
||||
support_verbosity: true,
|
||||
truncation_policy: TruncationPolicy::Tokens(10_000),
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
} else if slug.starts_with("exp-") {
|
||||
model_family!(
|
||||
@@ -209,6 +250,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
truncation_policy: TruncationPolicy::Bytes(10_000),
|
||||
shell_type: ConfigShellToolType::UnifiedExec,
|
||||
supports_parallel_tool_calls: true,
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
|
||||
// Production models.
|
||||
@@ -223,6 +265,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
supports_parallel_tool_calls: true,
|
||||
support_verbosity: false,
|
||||
truncation_policy: TruncationPolicy::Tokens(10_000),
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
} else if slug.starts_with("gpt-5-codex")
|
||||
|| slug.starts_with("gpt-5.1-codex")
|
||||
@@ -238,6 +281,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
supports_parallel_tool_calls: true,
|
||||
support_verbosity: false,
|
||||
truncation_policy: TruncationPolicy::Tokens(10_000),
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
} else if slug.starts_with("gpt-5.1") {
|
||||
model_family!(
|
||||
@@ -251,6 +295,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
truncation_policy: TruncationPolicy::Bytes(10_000),
|
||||
shell_type: ConfigShellToolType::ShellCommand,
|
||||
supports_parallel_tool_calls: true,
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
} else if slug.starts_with("gpt-5") {
|
||||
model_family!(
|
||||
@@ -260,6 +305,7 @@ pub fn find_family_for_model(slug: &str) -> ModelFamily {
|
||||
shell_type: ConfigShellToolType::Default,
|
||||
support_verbosity: true,
|
||||
truncation_policy: TruncationPolicy::Bytes(10_000),
|
||||
context_window: Some(CONTEXT_WINDOW_272K),
|
||||
)
|
||||
} else {
|
||||
derive_default_model_family(slug)
|
||||
@@ -271,6 +317,8 @@ fn derive_default_model_family(model: &str) -> ModelFamily {
|
||||
slug: model.to_string(),
|
||||
family: model.to_string(),
|
||||
needs_special_apply_patch_instructions: false,
|
||||
context_window: None,
|
||||
auto_compact_token_limit: None,
|
||||
supports_reasoning_summaries: false,
|
||||
reasoning_summary_format: ReasoningSummaryFormat::None,
|
||||
supports_parallel_tool_calls: false,
|
||||
|
||||
@@ -550,7 +550,7 @@ impl ChatWidget {
|
||||
|
||||
fn context_remaining_percent(&self, info: &TokenUsageInfo) -> Option<i64> {
|
||||
info.model_context_window
|
||||
.or(self.config.model_context_window)
|
||||
.or(self.model_family.context_window)
|
||||
.map(|window| {
|
||||
info.last_token_usage
|
||||
.percent_of_context_window_remaining(window)
|
||||
@@ -2024,6 +2024,7 @@ impl ChatWidget {
|
||||
self.add_to_history(crate::status::new_status_output(
|
||||
&self.config,
|
||||
self.auth_manager.as_ref(),
|
||||
&self.model_family,
|
||||
total_usage,
|
||||
context_usage,
|
||||
&self.conversation_id,
|
||||
|
||||
@@ -98,7 +98,7 @@ fn snapshot(percent: f64) -> RateLimitSnapshot {
|
||||
|
||||
#[test]
|
||||
fn resumed_initial_messages_render_history() {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
let conversation_id = ConversationId::new();
|
||||
let rollout_file = NamedTempFile::new().unwrap();
|
||||
@@ -154,7 +154,7 @@ fn resumed_initial_messages_render_history() {
|
||||
/// Entering review mode uses the hint provided by the review request.
|
||||
#[test]
|
||||
fn entered_review_mode_uses_request_hint() {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "review-start".into(),
|
||||
@@ -175,7 +175,7 @@ fn entered_review_mode_uses_request_hint() {
|
||||
/// Entering review mode renders the current changes banner when requested.
|
||||
#[test]
|
||||
fn entered_review_mode_defaults_to_current_changes_banner() {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "review-start".into(),
|
||||
@@ -195,7 +195,7 @@ fn entered_review_mode_defaults_to_current_changes_banner() {
|
||||
/// the closing banner while clearing review mode state.
|
||||
#[test]
|
||||
fn exited_review_mode_emits_results_and_finishes() {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
let review = ReviewOutputEvent {
|
||||
findings: vec![ReviewFinding {
|
||||
@@ -229,7 +229,7 @@ fn exited_review_mode_emits_results_and_finishes() {
|
||||
/// Exiting review restores the pre-review context window indicator.
|
||||
#[test]
|
||||
fn review_restores_context_window_indicator() {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
let context_window = 13_000;
|
||||
let pre_review_tokens = 12_700; // ~30% remaining after subtracting baseline.
|
||||
@@ -278,7 +278,7 @@ fn review_restores_context_window_indicator() {
|
||||
/// Receiving a TokenCount event without usage clears the context indicator.
|
||||
#[test]
|
||||
fn token_count_none_resets_context_indicator() {
|
||||
let (mut chat, _rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _ops) = make_chatwidget_manual(None);
|
||||
|
||||
let context_window = 13_000;
|
||||
let pre_compact_tokens = 12_700;
|
||||
@@ -304,7 +304,7 @@ fn token_count_none_resets_context_indicator() {
|
||||
|
||||
#[test]
|
||||
fn context_indicator_shows_used_tokens_when_window_unknown() {
|
||||
let (mut chat, _rx, _ops) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _ops) = make_chatwidget_manual(Some("unknown-model"));
|
||||
|
||||
chat.config.model_context_window = None;
|
||||
let auto_compact_limit = 200_000;
|
||||
@@ -371,7 +371,9 @@ async fn helpers_are_available_and_do_not_panic() {
|
||||
}
|
||||
|
||||
// --- Helpers for tests that need direct construction and event draining ---
|
||||
fn make_chatwidget_manual() -> (
|
||||
fn make_chatwidget_manual(
|
||||
model_override: Option<&str>,
|
||||
) -> (
|
||||
ChatWidget,
|
||||
tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
|
||||
tokio::sync::mpsc::UnboundedReceiver<Op>,
|
||||
@@ -379,7 +381,10 @@ fn make_chatwidget_manual() -> (
|
||||
let (tx_raw, rx) = unbounded_channel::<AppEvent>();
|
||||
let app_event_tx = AppEventSender::new(tx_raw);
|
||||
let (op_tx, op_rx) = unbounded_channel::<Op>();
|
||||
let cfg = test_config();
|
||||
let mut cfg = test_config();
|
||||
if let Some(model) = model_override {
|
||||
cfg.model = model.to_string();
|
||||
}
|
||||
let bottom = BottomPane::new(BottomPaneParams {
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
@@ -447,7 +452,7 @@ pub(crate) fn make_chatwidget_manual_with_sender() -> (
|
||||
tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
|
||||
tokio::sync::mpsc::UnboundedReceiver<Op>,
|
||||
) {
|
||||
let (widget, rx, op_rx) = make_chatwidget_manual();
|
||||
let (widget, rx, op_rx) = make_chatwidget_manual(None);
|
||||
let app_event_tx = widget.app_event_tx.clone();
|
||||
(widget, app_event_tx, rx, op_rx)
|
||||
}
|
||||
@@ -543,7 +548,7 @@ fn test_rate_limit_warnings_monthly() {
|
||||
|
||||
#[test]
|
||||
fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
|
||||
primary: None,
|
||||
@@ -592,7 +597,7 @@ fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
|
||||
|
||||
#[test]
|
||||
fn rate_limit_snapshot_updates_and_retains_plan_type() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
|
||||
primary: Some(RateLimitWindow {
|
||||
@@ -645,7 +650,7 @@ fn rate_limit_snapshot_updates_and_retains_plan_type() {
|
||||
|
||||
#[test]
|
||||
fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
|
||||
let (mut chat, _, _) = make_chatwidget_manual();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(None);
|
||||
chat.auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
chat.config.model = NUDGE_MODEL_SLUG.to_string();
|
||||
@@ -661,7 +666,7 @@ fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
|
||||
#[test]
|
||||
fn rate_limit_switch_prompt_shows_once_per_session() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(None);
|
||||
chat.config.model = "gpt-5".to_string();
|
||||
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
|
||||
|
||||
@@ -686,7 +691,7 @@ fn rate_limit_switch_prompt_shows_once_per_session() {
|
||||
#[test]
|
||||
fn rate_limit_switch_prompt_respects_hidden_notice() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(None);
|
||||
chat.config.model = "gpt-5".to_string();
|
||||
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
|
||||
chat.config.notices.hide_rate_limit_model_nudge = Some(true);
|
||||
@@ -702,7 +707,7 @@ fn rate_limit_switch_prompt_respects_hidden_notice() {
|
||||
#[test]
|
||||
fn rate_limit_switch_prompt_defers_until_task_complete() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(None);
|
||||
chat.config.model = "gpt-5".to_string();
|
||||
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
|
||||
|
||||
@@ -723,7 +728,7 @@ fn rate_limit_switch_prompt_defers_until_task_complete() {
|
||||
|
||||
#[test]
|
||||
fn rate_limit_switch_prompt_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
chat.config.model = "gpt-5".to_string();
|
||||
@@ -739,7 +744,7 @@ fn rate_limit_switch_prompt_popup_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn exec_approval_emits_proposed_command_and_decision_history() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Trigger an exec approval request with a short, single-line command
|
||||
let ev = ExecApprovalRequestEvent {
|
||||
@@ -784,7 +789,7 @@ fn exec_approval_emits_proposed_command_and_decision_history() {
|
||||
|
||||
#[test]
|
||||
fn exec_approval_decision_truncates_multiline_and_long_commands() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Multiline command: modal should show full command, history records decision only
|
||||
let ev_multi = ExecApprovalRequestEvent {
|
||||
@@ -969,7 +974,7 @@ fn get_available_model(chat: &ChatWidget, model: &str) -> ModelPreset {
|
||||
|
||||
#[test]
|
||||
fn empty_enter_during_task_does_not_queue() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Simulate running task so submissions would normally be queued.
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
@@ -983,7 +988,7 @@ fn empty_enter_during_task_does_not_queue() {
|
||||
|
||||
#[test]
|
||||
fn alt_up_edits_most_recent_queued_message() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Simulate a running task so messages would normally be queued.
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
@@ -1016,7 +1021,7 @@ fn alt_up_edits_most_recent_queued_message() {
|
||||
/// is queued repeatedly.
|
||||
#[test]
|
||||
fn enqueueing_history_prompt_multiple_times_is_stable() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Submit an initial prompt to seed history.
|
||||
chat.bottom_pane.set_composer_text("repeat me".to_string());
|
||||
@@ -1042,7 +1047,7 @@ fn enqueueing_history_prompt_multiple_times_is_stable() {
|
||||
|
||||
#[test]
|
||||
fn streaming_final_answer_keeps_task_running_state() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.on_task_started();
|
||||
chat.on_agent_message_delta("Final answer line\n".to_string());
|
||||
@@ -1072,7 +1077,7 @@ fn streaming_final_answer_keeps_task_running_state() {
|
||||
|
||||
#[test]
|
||||
fn ctrl_c_shutdown_ignores_caps_lock() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_key_event(KeyEvent::new(KeyCode::Char('C'), KeyModifiers::CONTROL));
|
||||
|
||||
@@ -1084,7 +1089,7 @@ fn ctrl_c_shutdown_ignores_caps_lock() {
|
||||
|
||||
#[test]
|
||||
fn ctrl_c_cleared_prompt_is_recoverable_via_history() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.bottom_pane.insert_str("draft message ");
|
||||
chat.bottom_pane
|
||||
@@ -1118,7 +1123,7 @@ fn ctrl_c_cleared_prompt_is_recoverable_via_history() {
|
||||
|
||||
#[test]
|
||||
fn exec_history_cell_shows_working_then_completed() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Begin command
|
||||
let begin = begin_exec(&mut chat, "call-1", "echo done");
|
||||
@@ -1148,7 +1153,7 @@ fn exec_history_cell_shows_working_then_completed() {
|
||||
|
||||
#[test]
|
||||
fn exec_history_cell_shows_working_then_failed() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Begin command
|
||||
let begin = begin_exec(&mut chat, "call-2", "false");
|
||||
@@ -1172,7 +1177,7 @@ fn exec_history_cell_shows_working_then_failed() {
|
||||
|
||||
#[test]
|
||||
fn exec_history_shows_unified_exec_startup_commands() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let begin = begin_exec_with_source(
|
||||
&mut chat,
|
||||
@@ -1200,7 +1205,7 @@ fn exec_history_shows_unified_exec_startup_commands() {
|
||||
/// OpenReviewCustomPrompt to the app event channel.
|
||||
#[test]
|
||||
fn review_popup_custom_prompt_action_sends_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the preset selection popup
|
||||
chat.open_review_popup();
|
||||
@@ -1225,7 +1230,7 @@ fn review_popup_custom_prompt_action_sends_event() {
|
||||
|
||||
#[test]
|
||||
fn slash_init_skips_when_project_doc_exists() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
let tempdir = tempdir().unwrap();
|
||||
let existing_path = tempdir.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
std::fs::write(&existing_path, "existing instructions").unwrap();
|
||||
@@ -1257,7 +1262,7 @@ fn slash_init_skips_when_project_doc_exists() {
|
||||
|
||||
#[test]
|
||||
fn slash_quit_requests_exit() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Quit);
|
||||
|
||||
@@ -1266,7 +1271,7 @@ fn slash_quit_requests_exit() {
|
||||
|
||||
#[test]
|
||||
fn slash_exit_requests_exit() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Exit);
|
||||
|
||||
@@ -1275,7 +1280,7 @@ fn slash_exit_requests_exit() {
|
||||
|
||||
#[test]
|
||||
fn slash_resume_opens_picker() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Resume);
|
||||
|
||||
@@ -1284,7 +1289,7 @@ fn slash_resume_opens_picker() {
|
||||
|
||||
#[test]
|
||||
fn slash_undo_sends_op() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Undo);
|
||||
|
||||
@@ -1296,7 +1301,7 @@ fn slash_undo_sends_op() {
|
||||
|
||||
#[test]
|
||||
fn slash_rollout_displays_current_path() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
let rollout_path = PathBuf::from("/tmp/codex-test-rollout.jsonl");
|
||||
chat.current_rollout_path = Some(rollout_path.clone());
|
||||
|
||||
@@ -1313,7 +1318,7 @@ fn slash_rollout_displays_current_path() {
|
||||
|
||||
#[test]
|
||||
fn slash_rollout_handles_missing_path() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Rollout);
|
||||
|
||||
@@ -1332,7 +1337,7 @@ fn slash_rollout_handles_missing_path() {
|
||||
|
||||
#[test]
|
||||
fn undo_success_events_render_info_messages() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-1".to_string(),
|
||||
@@ -1369,7 +1374,7 @@ fn undo_success_events_render_info_messages() {
|
||||
|
||||
#[test]
|
||||
fn undo_failure_events_render_error_message() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-2".to_string(),
|
||||
@@ -1404,7 +1409,7 @@ fn undo_failure_events_render_error_message() {
|
||||
|
||||
#[test]
|
||||
fn undo_started_hides_interrupt_hint() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-hint".to_string(),
|
||||
@@ -1424,7 +1429,7 @@ fn undo_started_hides_interrupt_hint() {
|
||||
/// The commit picker shows only commit subjects (no timestamps).
|
||||
#[test]
|
||||
fn review_commit_picker_shows_subjects_without_timestamps() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the Review presets parent popup.
|
||||
chat.open_review_popup();
|
||||
@@ -1486,7 +1491,7 @@ fn review_commit_picker_shows_subjects_without_timestamps() {
|
||||
/// and uses the same text for the user-facing hint.
|
||||
#[test]
|
||||
fn custom_prompt_submit_sends_review_op() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.show_review_custom_prompt();
|
||||
// Paste prompt text via ChatWidget handler, then submit
|
||||
@@ -1514,7 +1519,7 @@ fn custom_prompt_submit_sends_review_op() {
|
||||
/// Hitting Enter on an empty custom prompt view does not submit.
|
||||
#[test]
|
||||
fn custom_prompt_enter_empty_does_not_send() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.show_review_custom_prompt();
|
||||
// Enter without any text
|
||||
@@ -1526,7 +1531,7 @@ fn custom_prompt_enter_empty_does_not_send() {
|
||||
|
||||
#[test]
|
||||
fn view_image_tool_call_adds_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
let image_path = chat.config.cwd.join("example.png");
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -1547,7 +1552,7 @@ fn view_image_tool_call_adds_history_cell() {
|
||||
// marker (replacing the spinner) and flushes it into history.
|
||||
#[test]
|
||||
fn interrupt_exec_marks_failed_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Begin a long-running command so we have an active exec cell with a spinner.
|
||||
begin_exec(&mut chat, "call-int", "sleep 1");
|
||||
@@ -1576,7 +1581,7 @@ fn interrupt_exec_marks_failed_snapshot() {
|
||||
// suggesting the user to tell the model what to do differently and to use /feedback.
|
||||
#[test]
|
||||
fn interrupted_turn_error_message_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Simulate an in-progress task so the widget is in a running state.
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -1607,7 +1612,7 @@ fn interrupted_turn_error_message_snapshot() {
|
||||
/// parent popup, pressing Esc again dismisses all panels (back to normal mode).
|
||||
#[test]
|
||||
fn review_custom_prompt_escape_navigates_back_then_dismisses() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the Review presets parent popup.
|
||||
chat.open_review_popup();
|
||||
@@ -1642,7 +1647,7 @@ fn review_custom_prompt_escape_navigates_back_then_dismisses() {
|
||||
/// parent popup, pressing Esc again dismisses all panels (back to normal mode).
|
||||
#[tokio::test]
|
||||
async fn review_branch_picker_escape_navigates_back_then_dismisses() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the Review presets parent popup.
|
||||
chat.open_review_popup();
|
||||
@@ -1729,7 +1734,7 @@ fn render_bottom_popup(chat: &ChatWidget, width: u16) -> String {
|
||||
|
||||
#[test]
|
||||
fn model_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.config.model = "gpt-5-codex".to_string();
|
||||
chat.open_model_popup();
|
||||
@@ -1740,7 +1745,7 @@ fn model_selection_popup_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn approvals_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.config.notices.hide_full_access_warning = None;
|
||||
chat.open_approvals_popup();
|
||||
@@ -1779,7 +1784,7 @@ fn preset_matching_ignores_extra_writable_roots() {
|
||||
|
||||
#[test]
|
||||
fn full_access_confirmation_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let preset = builtin_approval_presets()
|
||||
.into_iter()
|
||||
@@ -1794,7 +1799,7 @@ fn full_access_confirmation_popup_snapshot() {
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn windows_auto_mode_prompt_requests_enabling_sandbox_feature() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let preset = builtin_approval_presets()
|
||||
.into_iter()
|
||||
@@ -1812,7 +1817,7 @@ fn windows_auto_mode_prompt_requests_enabling_sandbox_feature() {
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn startup_prompts_for_windows_sandbox_when_agent_requested() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
set_windows_sandbox_enabled(false);
|
||||
chat.config.forced_auto_mode_downgraded_on_windows = true;
|
||||
@@ -1834,7 +1839,7 @@ fn startup_prompts_for_windows_sandbox_when_agent_requested() {
|
||||
|
||||
#[test]
|
||||
fn model_reasoning_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.config.model = "gpt-5.1-codex-max".to_string();
|
||||
@@ -1849,7 +1854,7 @@ fn model_reasoning_selection_popup_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.config.model = "gpt-5.1-codex-max".to_string();
|
||||
@@ -1864,7 +1869,7 @@ fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn reasoning_popup_shows_extra_high_with_space() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.config.model = "gpt-5.1-codex-max".to_string();
|
||||
@@ -1885,7 +1890,7 @@ fn reasoning_popup_shows_extra_high_with_space() {
|
||||
|
||||
#[test]
|
||||
fn single_reasoning_option_skips_selection() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let single_effort = vec![ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::High,
|
||||
@@ -1925,7 +1930,7 @@ fn single_reasoning_option_skips_selection() {
|
||||
|
||||
#[test]
|
||||
fn feedback_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the feedback category selection popup via slash command.
|
||||
chat.dispatch_command(SlashCommand::Feedback);
|
||||
@@ -1936,7 +1941,7 @@ fn feedback_selection_popup_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn feedback_upload_consent_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Open the consent popup directly for a chosen category.
|
||||
chat.open_feedback_consent(crate::app_event::FeedbackCategory::Bug);
|
||||
@@ -1947,7 +1952,7 @@ fn feedback_upload_consent_popup_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn reasoning_popup_escape_returns_to_model_popup() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.config.model = "gpt-5.1".to_string();
|
||||
chat.open_model_popup();
|
||||
@@ -1967,7 +1972,7 @@ fn reasoning_popup_escape_returns_to_model_popup() {
|
||||
|
||||
#[test]
|
||||
fn exec_history_extends_previous_when_consecutive() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// 1) Start "ls -la" (List)
|
||||
let begin_ls = begin_exec(&mut chat, "call-ls", "ls -la");
|
||||
@@ -1998,7 +2003,7 @@ fn exec_history_extends_previous_when_consecutive() {
|
||||
|
||||
#[test]
|
||||
fn user_shell_command_renders_output_not_exploring() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let begin_ls = begin_exec_with_source(
|
||||
&mut chat,
|
||||
@@ -2021,7 +2026,7 @@ fn user_shell_command_renders_output_not_exploring() {
|
||||
#[test]
|
||||
fn disabled_slash_command_while_task_running_snapshot() {
|
||||
// Build a chat widget and simulate an active task
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
|
||||
// Dispatch a command that is unavailable while a task runs (e.g., /model)
|
||||
@@ -2045,7 +2050,7 @@ fn disabled_slash_command_while_task_running_snapshot() {
|
||||
#[test]
|
||||
fn approval_modal_exec_snapshot() {
|
||||
// Build a chat widget with manual channels to avoid spawning the agent.
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Ensure policy allows surfacing approvals explicitly (not strictly required for direct event).
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
// Inject an exec approval request to display the approval modal.
|
||||
@@ -2100,7 +2105,7 @@ fn approval_modal_exec_snapshot() {
|
||||
// Ensures spacing looks correct when no reason text is provided.
|
||||
#[test]
|
||||
fn approval_modal_exec_without_reason_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
|
||||
let ev = ExecApprovalRequestEvent {
|
||||
@@ -2139,7 +2144,7 @@ fn approval_modal_exec_without_reason_snapshot() {
|
||||
// Snapshot test: patch approval modal
|
||||
#[test]
|
||||
fn approval_modal_patch_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
|
||||
// Build a small changeset and a reason/grant_root to exercise the prompt text.
|
||||
@@ -2178,7 +2183,7 @@ fn approval_modal_patch_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn interrupt_restores_queued_messages_into_composer() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Simulate a running task to enable queuing of user inputs.
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
@@ -2217,7 +2222,7 @@ fn interrupt_restores_queued_messages_into_composer() {
|
||||
|
||||
#[test]
|
||||
fn interrupt_prepends_queued_messages_before_existing_composer_text() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
chat.bottom_pane
|
||||
@@ -2255,7 +2260,7 @@ fn interrupt_prepends_queued_messages_before_existing_composer_text() {
|
||||
fn ui_snapshots_small_heights_idle() {
|
||||
use ratatui::Terminal;
|
||||
use ratatui::backend::TestBackend;
|
||||
let (chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
for h in [1u16, 2, 3] {
|
||||
let name = format!("chat_small_idle_h{h}");
|
||||
let mut terminal = Terminal::new(TestBackend::new(40, h)).expect("create terminal");
|
||||
@@ -2272,7 +2277,7 @@ fn ui_snapshots_small_heights_idle() {
|
||||
fn ui_snapshots_small_heights_task_running() {
|
||||
use ratatui::Terminal;
|
||||
use ratatui::backend::TestBackend;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Activate status line
|
||||
chat.handle_codex_event(Event {
|
||||
id: "task-1".into(),
|
||||
@@ -2303,7 +2308,7 @@ fn ui_snapshots_small_heights_task_running() {
|
||||
fn status_widget_and_approval_modal_snapshot() {
|
||||
use codex_core::protocol::ExecApprovalRequestEvent;
|
||||
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Begin a running task so the status indicator would be active.
|
||||
chat.handle_codex_event(Event {
|
||||
id: "task-1".into(),
|
||||
@@ -2356,7 +2361,7 @@ fn status_widget_and_approval_modal_snapshot() {
|
||||
// Ensures the VT100 rendering of the status indicator is stable when active.
|
||||
#[test]
|
||||
fn status_widget_active_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Activate the status indicator by simulating a task start.
|
||||
chat.handle_codex_event(Event {
|
||||
id: "task-1".into(),
|
||||
@@ -2383,7 +2388,7 @@ fn status_widget_active_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn background_event_updates_status_header() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "bg-1".into(),
|
||||
@@ -2399,7 +2404,7 @@ fn background_event_updates_status_header() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_events_emit_history_cells() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// 1) Approval request -> proposed patch summary cell
|
||||
let mut changes = HashMap::new();
|
||||
@@ -2497,7 +2502,7 @@ fn apply_patch_events_emit_history_cells() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_manual_approval_adjusts_header() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let mut proposed_changes = HashMap::new();
|
||||
proposed_changes.insert(
|
||||
@@ -2546,7 +2551,7 @@ fn apply_patch_manual_approval_adjusts_header() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_manual_flow_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
let mut proposed_changes = HashMap::new();
|
||||
proposed_changes.insert(
|
||||
@@ -2599,7 +2604,7 @@ fn apply_patch_manual_flow_snapshot() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_approval_sends_op_with_submission_id() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Simulate receiving an approval request with a distinct submission id and call id
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(
|
||||
@@ -2638,7 +2643,7 @@ fn apply_patch_approval_sends_op_with_submission_id() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_full_flow_integration_like() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// 1) Backend requests approval
|
||||
let mut changes = HashMap::new();
|
||||
@@ -2716,7 +2721,7 @@ fn apply_patch_full_flow_integration_like() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_untrusted_shows_approval_modal() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
// Ensure approval policy is untrusted (OnRequest)
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
|
||||
@@ -2761,7 +2766,7 @@ fn apply_patch_untrusted_shows_approval_modal() {
|
||||
|
||||
#[test]
|
||||
fn apply_patch_request_shows_diff_summary() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Ensure we are in OnRequest so an approval is surfaced
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
@@ -2827,7 +2832,7 @@ fn apply_patch_request_shows_diff_summary() {
|
||||
|
||||
#[test]
|
||||
fn plan_update_renders_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
let update = UpdatePlanArgs {
|
||||
explanation: Some("Adapting plan".to_string()),
|
||||
plan: vec![
|
||||
@@ -2863,7 +2868,7 @@ fn plan_update_renders_history_cell() {
|
||||
|
||||
#[test]
|
||||
fn stream_error_updates_status_indicator() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
let msg = "Reconnecting... 2/5";
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -2888,7 +2893,7 @@ fn stream_error_updates_status_indicator() {
|
||||
|
||||
#[test]
|
||||
fn warning_event_adds_warning_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.handle_codex_event(Event {
|
||||
id: "sub-1".into(),
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
@@ -2907,7 +2912,7 @@ fn warning_event_adds_warning_history_cell() {
|
||||
|
||||
#[test]
|
||||
fn stream_recovery_restores_previous_status_header() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.handle_codex_event(Event {
|
||||
id: "task".into(),
|
||||
msg: EventMsg::TaskStarted(TaskStartedEvent {
|
||||
@@ -2940,7 +2945,7 @@ fn stream_recovery_restores_previous_status_header() {
|
||||
|
||||
#[test]
|
||||
fn multiple_agent_messages_in_single_turn_emit_multiple_headers() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Begin turn
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -2994,7 +2999,7 @@ fn multiple_agent_messages_in_single_turn_emit_multiple_headers() {
|
||||
|
||||
#[test]
|
||||
fn final_reasoning_then_message_without_deltas_are_rendered() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// No deltas; only final reasoning followed by final message.
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -3021,7 +3026,7 @@ fn final_reasoning_then_message_without_deltas_are_rendered() {
|
||||
|
||||
#[test]
|
||||
fn deltas_then_same_final_message_are_rendered_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Stream some reasoning deltas first.
|
||||
chat.handle_codex_event(Event {
|
||||
@@ -3085,7 +3090,7 @@ fn deltas_then_same_final_message_are_rendered_snapshot() {
|
||||
// then the exec block, another blank line, the status line, a blank line, and the composer.
|
||||
#[test]
|
||||
fn chatwidget_exec_and_status_layout_vt100_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.handle_codex_event(Event {
|
||||
id: "t1".into(),
|
||||
msg: EventMsg::AgentMessage(AgentMessageEvent { message: "I’m going to search the repo for where “Change Approved” is rendered to update that view.".into() }),
|
||||
@@ -3177,7 +3182,7 @@ fn chatwidget_exec_and_status_layout_vt100_snapshot() {
|
||||
// E2E vt100 snapshot for complex markdown with indented and nested fenced code blocks
|
||||
#[test]
|
||||
fn chatwidget_markdown_code_blocks_vt100_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None);
|
||||
|
||||
// Simulate a final agent message via streaming deltas instead of a single message
|
||||
|
||||
@@ -3268,7 +3273,7 @@ printf 'fenced within fenced\n'
|
||||
|
||||
#[test]
|
||||
fn chatwidget_tall() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None);
|
||||
chat.handle_codex_event(Event {
|
||||
id: "t1".into(),
|
||||
msg: EventMsg::TaskStarted(TaskStartedEvent {
|
||||
|
||||
@@ -7,6 +7,7 @@ use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use codex_common::create_config_summary_entries;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::openai_models::model_family::ModelFamily;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
use codex_protocol::ConversationId;
|
||||
@@ -70,6 +71,7 @@ struct StatusHistoryCell {
|
||||
pub(crate) fn new_status_output(
|
||||
config: &Config,
|
||||
auth_manager: &AuthManager,
|
||||
model_family: &ModelFamily,
|
||||
total_usage: &TokenUsage,
|
||||
context_usage: Option<&TokenUsage>,
|
||||
session_id: &Option<ConversationId>,
|
||||
@@ -81,6 +83,7 @@ pub(crate) fn new_status_output(
|
||||
let card = StatusHistoryCell::new(
|
||||
config,
|
||||
auth_manager,
|
||||
model_family,
|
||||
total_usage,
|
||||
context_usage,
|
||||
session_id,
|
||||
@@ -97,6 +100,7 @@ impl StatusHistoryCell {
|
||||
fn new(
|
||||
config: &Config,
|
||||
auth_manager: &AuthManager,
|
||||
model_family: &ModelFamily,
|
||||
total_usage: &TokenUsage,
|
||||
context_usage: Option<&TokenUsage>,
|
||||
session_id: &Option<ConversationId>,
|
||||
@@ -119,7 +123,7 @@ impl StatusHistoryCell {
|
||||
let agents_summary = compose_agents_summary(config);
|
||||
let account = compose_account_display(auth_manager, plan_type);
|
||||
let session_id = session_id.as_ref().map(std::string::ToString::to_string);
|
||||
let context_window = config.model_context_window.and_then(|window| {
|
||||
let context_window = model_family.context_window.and_then(|window| {
|
||||
context_usage.map(|usage| StatusContextWindowData {
|
||||
percent_remaining: usage.percent_of_context_window_remaining(window),
|
||||
tokens_in_context: usage.tokens_in_context_window(),
|
||||
|
||||
@@ -8,6 +8,8 @@ use codex_core::AuthManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::ConfigToml;
|
||||
use codex_core::openai_models::model_family::ModelFamily;
|
||||
use codex_core::openai_models::models_manager::ModelsManager;
|
||||
use codex_core::protocol::CreditsSnapshot;
|
||||
use codex_core::protocol::RateLimitSnapshot;
|
||||
use codex_core::protocol::RateLimitWindow;
|
||||
@@ -37,6 +39,10 @@ fn test_auth_manager(config: &Config) -> AuthManager {
|
||||
)
|
||||
}
|
||||
|
||||
fn test_model_family(config: &Config) -> ModelFamily {
|
||||
ModelsManager::construct_model_family_offline(config.model.as_str(), config)
|
||||
}
|
||||
|
||||
fn render_lines(lines: &[Line<'static>]) -> Vec<String> {
|
||||
lines
|
||||
.iter()
|
||||
@@ -124,9 +130,12 @@ fn status_snapshot_includes_reasoning_details() {
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -177,9 +186,11 @@ fn status_snapshot_includes_monthly_limit() {
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -218,9 +229,11 @@ fn status_snapshot_shows_unlimited_credits() {
|
||||
plan_type: None,
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -258,9 +271,11 @@ fn status_snapshot_shows_positive_credits() {
|
||||
plan_type: None,
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -298,9 +313,11 @@ fn status_snapshot_hides_zero_credits() {
|
||||
plan_type: None,
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -336,9 +353,11 @@ fn status_snapshot_hides_when_has_no_credits_flag() {
|
||||
plan_type: None,
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -374,9 +393,11 @@ fn status_card_token_usage_excludes_cached_tokens() {
|
||||
.single()
|
||||
.expect("timestamp");
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -427,9 +448,11 @@ fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -469,9 +492,11 @@ fn status_snapshot_shows_missing_limits_message() {
|
||||
.single()
|
||||
.expect("timestamp");
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -529,9 +554,11 @@ fn status_snapshot_includes_credits_and_limits() {
|
||||
};
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -577,9 +604,11 @@ fn status_snapshot_shows_empty_limits_message() {
|
||||
.expect("timestamp");
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -634,9 +663,11 @@ fn status_snapshot_shows_stale_limits_message() {
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let now = captured_at + ChronoDuration::minutes(20);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -695,9 +726,11 @@ fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
|
||||
let now = captured_at + ChronoDuration::minutes(20);
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&usage,
|
||||
Some(&usage),
|
||||
&None,
|
||||
@@ -742,9 +775,11 @@ fn status_context_window_uses_last_usage() {
|
||||
.single()
|
||||
.expect("timestamp");
|
||||
|
||||
let model_family = test_model_family(&config);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
&model_family,
|
||||
&total_usage,
|
||||
Some(&last_usage),
|
||||
&None,
|
||||
|
||||
Reference in New Issue
Block a user