Skip to content

Commit 8715a6e

Browse files
authored
Feat: cxa-1833 update model/list (#12958)
### Summary Update `model/list` in app server to include more upgrade information.
1 parent a11da86 commit 8715a6e

File tree

10 files changed

+166
-6
lines changed

10 files changed

+166
-6
lines changed

codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10271,6 +10271,16 @@
1027110271
"string",
1027210272
"null"
1027310273
]
10274+
},
10275+
"upgradeInfo": {
10276+
"anyOf": [
10277+
{
10278+
"$ref": "#/definitions/v2/ModelUpgradeInfo"
10279+
},
10280+
{
10281+
"type": "null"
10282+
}
10283+
]
1027410284
}
1027510285
},
1027610286
"required": [
@@ -10373,6 +10383,35 @@
1037310383
"title": "ModelReroutedNotification",
1037410384
"type": "object"
1037510385
},
10386+
"ModelUpgradeInfo": {
10387+
"properties": {
10388+
"migrationMarkdown": {
10389+
"type": [
10390+
"string",
10391+
"null"
10392+
]
10393+
},
10394+
"model": {
10395+
"type": "string"
10396+
},
10397+
"modelLink": {
10398+
"type": [
10399+
"string",
10400+
"null"
10401+
]
10402+
},
10403+
"upgradeCopy": {
10404+
"type": [
10405+
"string",
10406+
"null"
10407+
]
10408+
}
10409+
},
10410+
"required": [
10411+
"model"
10412+
],
10413+
"type": "object"
10414+
},
1037610415
"NetworkAccess": {
1037710416
"enum": [
1037810417
"restricted",

codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@
6868
"string",
6969
"null"
7070
]
71+
},
72+
"upgradeInfo": {
73+
"anyOf": [
74+
{
75+
"$ref": "#/definitions/ModelUpgradeInfo"
76+
},
77+
{
78+
"type": "null"
79+
}
80+
]
7181
}
7282
},
7383
"required": [
@@ -82,6 +92,35 @@
8292
],
8393
"type": "object"
8494
},
95+
"ModelUpgradeInfo": {
96+
"properties": {
97+
"migrationMarkdown": {
98+
"type": [
99+
"string",
100+
"null"
101+
]
102+
},
103+
"model": {
104+
"type": "string"
105+
},
106+
"modelLink": {
107+
"type": [
108+
"string",
109+
"null"
110+
]
111+
},
112+
"upgradeCopy": {
113+
"type": [
114+
"string",
115+
"null"
116+
]
117+
}
118+
},
119+
"required": [
120+
"model"
121+
],
122+
"type": "object"
123+
},
85124
"ReasoningEffort": {
86125
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
87126
"enum": [

codex-rs/app-server-protocol/schema/typescript/v2/Model.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
44
import type { InputModality } from "../InputModality";
55
import type { ReasoningEffort } from "../ReasoningEffort";
6+
import type { ModelUpgradeInfo } from "./ModelUpgradeInfo";
67
import type { ReasoningEffortOption } from "./ReasoningEffortOption";
78

8-
export type Model = { id: string, model: string, upgrade: string | null, displayName: string, description: string, hidden: boolean, supportedReasoningEfforts: Array<ReasoningEffortOption>, defaultReasoningEffort: ReasoningEffort, inputModalities: Array<InputModality>, supportsPersonality: boolean, isDefault: boolean, };
9+
export type Model = { id: string, model: string, upgrade: string | null, upgradeInfo: ModelUpgradeInfo | null, displayName: string, description: string, hidden: boolean, supportedReasoningEfforts: Array<ReasoningEffortOption>, defaultReasoningEffort: ReasoningEffort, inputModalities: Array<InputModality>, supportsPersonality: boolean, isDefault: boolean, };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// GENERATED CODE! DO NOT MODIFY BY HAND!
2+
3+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
4+
5+
export type ModelUpgradeInfo = { model: string, upgradeCopy: string | null, modelLink: string | null, migrationMarkdown: string | null, };

codex-rs/app-server-protocol/schema/typescript/v2/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export type { ModelListParams } from "./ModelListParams";
111111
export type { ModelListResponse } from "./ModelListResponse";
112112
export type { ModelRerouteReason } from "./ModelRerouteReason";
113113
export type { ModelReroutedNotification } from "./ModelReroutedNotification";
114+
export type { ModelUpgradeInfo } from "./ModelUpgradeInfo";
114115
export type { NetworkAccess } from "./NetworkAccess";
115116
export type { NetworkApprovalContext } from "./NetworkApprovalContext";
116117
export type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol";

codex-rs/app-server-protocol/src/protocol/v2.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,7 @@ pub struct Model {
13961396
pub id: String,
13971397
pub model: String,
13981398
pub upgrade: Option<String>,
1399+
pub upgrade_info: Option<ModelUpgradeInfo>,
13991400
pub display_name: String,
14001401
pub description: String,
14011402
pub hidden: bool,
@@ -1409,6 +1410,16 @@ pub struct Model {
14091410
pub is_default: bool,
14101411
}
14111412

1413+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
1414+
#[serde(rename_all = "camelCase")]
1415+
#[ts(export_to = "v2/")]
1416+
pub struct ModelUpgradeInfo {
1417+
pub model: String,
1418+
pub upgrade_copy: Option<String>,
1419+
pub model_link: Option<String>,
1420+
pub migration_markdown: Option<String>,
1421+
}
1422+
14121423
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
14131424
#[serde(rename_all = "camelCase")]
14141425
#[ts(export_to = "v2/")]

codex-rs/app-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ Example with notification opt-out:
142142
- `thread/realtime/stop` — stop the active realtime session for the thread (experimental); returns `{}`.
143143
- `review/start` — kick off Codex’s automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review.
144144
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
145-
- `model/list` — list available models (set `includeHidden: true` to include entries with `hidden: true`), with reasoning effort options and optional `upgrade` model ids.
145+
- `model/list` — list available models (set `includeHidden: true` to include entries with `hidden: true`), with reasoning effort options, optional legacy `upgrade` model ids, and optional `upgradeInfo` metadata (`model`, `upgradeCopy`, `modelLink`, `migrationMarkdown`).
146146
- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. For non-beta flags, `displayName`/`description`/`announcement` are `null`.
147147
- `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination). This response omits built-in developer instructions; clients should either pass `settings.developer_instructions: null` when setting a mode to use Codex's built-in instructions, or provide their own instructions explicitly.
148148
- `skills/list` — list skills for one or more `cwd` values (optional `forceReload`).

codex-rs/app-server/src/models.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22

33
use codex_app_server_protocol::Model;
4+
use codex_app_server_protocol::ModelUpgradeInfo;
45
use codex_app_server_protocol::ReasoningEffortOption;
56
use codex_core::ThreadManager;
67
use codex_core::models_manager::manager::RefreshStrategy;
@@ -24,7 +25,13 @@ fn model_from_preset(preset: ModelPreset) -> Model {
2425
Model {
2526
id: preset.id.to_string(),
2627
model: preset.model.to_string(),
27-
upgrade: preset.upgrade.map(|upgrade| upgrade.id),
28+
upgrade: preset.upgrade.as_ref().map(|upgrade| upgrade.id.clone()),
29+
upgrade_info: preset.upgrade.as_ref().map(|upgrade| ModelUpgradeInfo {
30+
model: upgrade.id.clone(),
31+
upgrade_copy: upgrade.upgrade_copy.clone(),
32+
model_link: upgrade.model_link.clone(),
33+
migration_markdown: upgrade.migration_markdown.clone(),
34+
}),
2835
display_name: preset.display_name.to_string(),
2936
description: preset.description.to_string(),
3037
hidden: !preset.show_in_picker,

codex-rs/app-server/tests/suite/v2/model_list.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use codex_app_server_protocol::JSONRPCResponse;
99
use codex_app_server_protocol::Model;
1010
use codex_app_server_protocol::ModelListParams;
1111
use codex_app_server_protocol::ModelListResponse;
12+
use codex_app_server_protocol::ModelUpgradeInfo;
1213
use codex_app_server_protocol::ReasoningEffortOption;
1314
use codex_app_server_protocol::RequestId;
1415
use codex_protocol::openai_models::ModelPreset;
@@ -24,6 +25,12 @@ fn model_from_preset(preset: &ModelPreset) -> Model {
2425
id: preset.id.clone(),
2526
model: preset.model.clone(),
2627
upgrade: preset.upgrade.as_ref().map(|upgrade| upgrade.id.clone()),
28+
upgrade_info: preset.upgrade.as_ref().map(|upgrade| ModelUpgradeInfo {
29+
model: upgrade.id.clone(),
30+
upgrade_copy: upgrade.upgrade_copy.clone(),
31+
model_link: upgrade.model_link.clone(),
32+
migration_markdown: upgrade.migration_markdown.clone(),
33+
}),
2734
display_name: preset.display_name.clone(),
2835
description: preset.description.clone(),
2936
hidden: !preset.show_in_picker,
@@ -127,6 +134,50 @@ async fn list_models_includes_hidden_models() -> Result<()> {
127134
Ok(())
128135
}
129136

137+
#[tokio::test]
138+
async fn list_models_returns_upgrade_info_metadata() -> Result<()> {
139+
let codex_home = TempDir::new()?;
140+
write_models_cache(codex_home.path())?;
141+
let mut mcp = McpProcess::new(codex_home.path()).await?;
142+
143+
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
144+
145+
let request_id = mcp
146+
.send_list_models_request(ModelListParams {
147+
limit: Some(100),
148+
cursor: None,
149+
include_hidden: Some(true),
150+
})
151+
.await?;
152+
153+
let response: JSONRPCResponse = timeout(
154+
DEFAULT_TIMEOUT,
155+
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
156+
)
157+
.await??;
158+
159+
let ModelListResponse { data: items, .. } = to_response::<ModelListResponse>(response)?;
160+
161+
let item = items
162+
.iter()
163+
.find(|item| item.upgrade_info.is_some())
164+
.expect("expected at least one model with upgrade info");
165+
let upgrade_info = item
166+
.upgrade_info
167+
.as_ref()
168+
.expect("expected upgrade info to be populated");
169+
170+
assert_eq!(item.upgrade.as_ref(), Some(&upgrade_info.model));
171+
assert!(!upgrade_info.model.is_empty());
172+
assert!(
173+
upgrade_info.upgrade_copy.is_some()
174+
|| upgrade_info.model_link.is_some()
175+
|| upgrade_info.migration_markdown.is_some()
176+
);
177+
178+
Ok(())
179+
}
180+
130181
#[tokio::test]
131182
async fn list_models_pagination_works() -> Result<()> {
132183
let codex_home = TempDir::new()?;

codex-rs/docs/codex_mcp_interface.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,26 @@ directory (it returns the restored thread summary).
9090

9191
Fetch the catalog of models available in the current Codex build with `model/list`. The request accepts optional pagination inputs:
9292

93-
- `pageSize` – number of models to return (defaults to a server-selected value)
93+
- `limit` – number of models to return (defaults to a server-selected value)
9494
- `cursor` – opaque string from the previous response’s `nextCursor`
9595

9696
Each response yields:
9797

98-
- `items` – ordered list of models. A model includes:
98+
- `data` – ordered list of models. A model includes:
9999
- `id`, `model`, `displayName`, `description`
100100
- `supportedReasoningEfforts` – array of objects with:
101-
- `reasoningEffort` – one of `minimal|low|medium|high`
101+
- `reasoningEffort` – one of `none|minimal|low|medium|high|xhigh`
102102
- `description` – human-friendly label for the effort
103103
- `defaultReasoningEffort` – suggested effort for the UI
104+
- `inputModalities` – accepted input types for the model
104105
- `supportsPersonality` – whether the model supports personality-specific instructions
105106
- `isDefault` – whether the model is recommended for most users
106107
- `upgrade` – optional recommended upgrade model id
108+
- `upgradeInfo` – optional upgrade metadata object with:
109+
- `model` – recommended upgrade model id
110+
- `upgradeCopy` – optional display copy for the upgrade recommendation
111+
- `modelLink` – optional link for the upgrade recommendation
112+
- `migrationMarkdown` – optional markdown shown when presenting the upgrade
107113
- `nextCursor` – pass into the next request to continue paging (optional)
108114

109115
## Collaboration modes (experimental)

0 commit comments

Comments
 (0)