codex: give ChatGPT-plan roster rows their reasoning metadata

The ChatGPT-plan roster only carries ids and display labels, so every row
was built with reasoning: None and efforts: [] and the runtime API
advertised reasoning_effort: unsupported with no levels for the seven
models the official account list returns. Models.dev knows all seven
(gpt-6-astra, gpt-6.1-sol, gpt-6-sol/luna, gpt-5.6-sol/terra/luna) and
publishes reasoning plus effort tiers for each, so join the roster
against the merged catalog for the missing fields.

model_roster_for is now the former loader plus enrich_reasoning_from_catalog;
the old body moved verbatim into cached_roster_for. Enrichment fills only
what the roster leaves blank and never touches context_window: the
superseded Sep-format snapshots report 272000 for these ids while
models.dev reports 1050000, and the OAuth route is not the wire that
number describes.

Verified: get /v1/providers/openai-codex/models on a locally built
app-server now returns reasoning_effort: supported with
["low","medium","high","xhigh","max"] at reasoning_effort_source
chatgpt_plan_api for gpt-6-astra, where the on-disk roster snapshot has
reasoning: None and efforts: []. cargo test -p codewhale-tui --lib --
codex_model_cache provider_reasoning_metadata: 6 passed.
check-blocking-calls-budget.py: 541 sites, within budget.
Not verified: the live OpenAI plan itself was not called; evidence is the
consumer route over the real cached roster and catalog.
This commit is contained in:
CodeWhale Bot committed 2026-10-23 06:36:52 -07:00
1 parent 114d185354
commit 262496dee7
1 file changed
+33
+33
View File
@@ -113,6 +113,39 @@ pub(crate) fn model_roster() -> CodexModelRoster {
#[must_use]
pub(crate) fn model_roster_for(config: &Config) -> CodexModelRoster {
let mut roster = cached_roster_for(config);
enrich_reasoning_from_catalog(&mut roster);
roster
}
fn enrich_reasoning_from_catalog(roster: &mut CodexModelRoster) {
for model in &mut roster.models {
if model.reasoning.is_some() && !model.efforts.is_empty() {
continue;
}
let Some(offering) =
crate::provider_lake::catalog_offering_for_model(ProviderKind::Openai, &model.id)
else {
continue;
};
if model.reasoning.is_none() {
model.reasoning = offering.reasoning;
}
if model.efforts.is_empty()
&& let Some(efforts) =
crate::reasoning_preference::ReasoningEffort::catalog_effort_values_for_offering(
&offering,
)
{
model.efforts = efforts
.into_iter()
.map(|effort| effort.as_setting().to_string())
.collect();
}
}
}
fn cached_roster_for(config: &Config) -> CodexModelRoster {
let Some(path) = snapshot_path(config) else {
return model_roster();
};