Skip to content

Commit 401f94c

Browse files
authored
[app-server] add thread/tokenUsage/updated v2 event (#7268)
the TokenEvent event message becomes `thread/tokenUsage/updated` in v2. before & after: ``` < { < "method": "codex/event/token_count", < "params": { < "conversationId": "019ab891-4c55-7790-9670-6c3b48c33281", < "id": "1", < "msg": { < "info": { < "last_token_usage": { < "cached_input_tokens": 3072, < "input_tokens": 5152, < "output_tokens": 16, < "reasoning_output_tokens": 0, < "total_tokens": 5168 < }, < "model_context_window": 258400, < "total_token_usage": { < "cached_input_tokens": 3072, < "input_tokens": 5152, < "output_tokens": 16, < "reasoning_output_tokens": 0, < "total_tokens": 5168 < } < }, < "rate_limits": { < "credits": null, < "primary": null, < "secondary": null < }, < "type": "token_count" < } < } < } < { < "method": "thread/tokenUsage/updated", < "params": { < "threadId": "019ab891-4c55-7790-9670-6c3b48c33281", < "tokenUsage": { < "last": { < "cachedInputTokens": 3072, < "inputTokens": 5152, < "outputTokens": 16, < "reasoningOutputTokens": 0, < "totalTokens": 5168 < }, < "modelContextWindow": 258400, < "total": { < "cachedInputTokens": 3072, < "inputTokens": 5152, < "outputTokens": 16, < "reasoningOutputTokens": 0, < "totalTokens": 5168 < } < }, < "turnId": "1" < } < } ```
1 parent 865e225 commit 401f94c

File tree

4 files changed

+287
-64
lines changed

4 files changed

+287
-64
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ server_notification_definitions! {
502502
/// NEW NOTIFICATIONS
503503
Error => "error" (v2::ErrorNotification),
504504
ThreadStarted => "thread/started" (v2::ThreadStartedNotification),
505+
ThreadTokenUsageUpdated => "thread/tokenUsage/updated" (v2::ThreadTokenUsageUpdatedNotification),
505506
TurnStarted => "turn/started" (v2::TurnStartedNotification),
506507
TurnCompleted => "turn/completed" (v2::TurnCompletedNotification),
507508
TurnDiffUpdated => "turn/diff/updated" (v2::TurnDiffUpdatedNotification),

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
1616
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
1717
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
1818
use codex_protocol::protocol::SessionSource as CoreSessionSource;
19+
use codex_protocol::protocol::TokenUsage as CoreTokenUsage;
20+
use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo;
1921
use codex_protocol::user_input::UserInput as CoreUserInput;
2022
use mcp_types::ContentBlock as McpContentBlock;
2123
use schemars::JsonSchema;
@@ -780,6 +782,63 @@ pub struct AccountUpdatedNotification {
780782
pub auth_mode: Option<AuthMode>,
781783
}
782784

785+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
786+
#[serde(rename_all = "camelCase")]
787+
#[ts(export_to = "v2/")]
788+
pub struct ThreadTokenUsageUpdatedNotification {
789+
pub thread_id: String,
790+
pub turn_id: String,
791+
pub token_usage: ThreadTokenUsage,
792+
}
793+
794+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
795+
#[serde(rename_all = "camelCase")]
796+
#[ts(export_to = "v2/")]
797+
pub struct ThreadTokenUsage {
798+
pub total: TokenUsageBreakdown,
799+
pub last: TokenUsageBreakdown,
800+
#[ts(type = "number | null")]
801+
pub model_context_window: Option<i64>,
802+
}
803+
804+
impl From<CoreTokenUsageInfo> for ThreadTokenUsage {
805+
fn from(value: CoreTokenUsageInfo) -> Self {
806+
Self {
807+
total: value.total_token_usage.into(),
808+
last: value.last_token_usage.into(),
809+
model_context_window: value.model_context_window,
810+
}
811+
}
812+
}
813+
814+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
815+
#[serde(rename_all = "camelCase")]
816+
#[ts(export_to = "v2/")]
817+
pub struct TokenUsageBreakdown {
818+
#[ts(type = "number")]
819+
pub total_tokens: i64,
820+
#[ts(type = "number")]
821+
pub input_tokens: i64,
822+
#[ts(type = "number")]
823+
pub cached_input_tokens: i64,
824+
#[ts(type = "number")]
825+
pub output_tokens: i64,
826+
#[ts(type = "number")]
827+
pub reasoning_output_tokens: i64,
828+
}
829+
830+
impl From<CoreTokenUsage> for TokenUsageBreakdown {
831+
fn from(value: CoreTokenUsage) -> Self {
832+
Self {
833+
total_tokens: value.total_tokens,
834+
input_tokens: value.input_tokens,
835+
cached_input_tokens: value.cached_input_tokens,
836+
output_tokens: value.output_tokens,
837+
reasoning_output_tokens: value.reasoning_output_tokens,
838+
}
839+
}
840+
}
841+
783842
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
784843
#[serde(rename_all = "camelCase")]
785844
#[ts(export_to = "v2/")]

codex-rs/app-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ Event notifications are the server-initiated event stream for thread lifecycles,
240240

241241
### Turn events
242242

243-
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` plus token `usage`), and clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
243+
The app-server streams JSON-RPC notifications while a turn is running. Each turn starts with `turn/started` (initial `turn`) and ends with `turn/completed` (final `turn` status). Token usage events stream separately via `thread/tokenUsage/updated`. Clients subscribe to the events they care about, rendering each item incrementally as updates arrive. The per-item lifecycle is always: `item/started` → zero or more item-specific deltas → `item/completed`.
244244

245245
- `turn/started``{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
246246
- `turn/completed``{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo? } }`.

0 commit comments

Comments
 (0)