Extract token usage projection to bridge
This commit is contained in:
parent
963c454c0c
commit
c744a9367f
7 changed files with 95 additions and 81 deletions
|
|
@ -12,6 +12,7 @@ pub mod retrieve;
|
|||
pub mod runtime_results;
|
||||
pub mod scene;
|
||||
pub mod state;
|
||||
pub mod token_usage;
|
||||
pub mod view;
|
||||
|
||||
pub use epiphany_openai_adapter as openai_adapter;
|
||||
|
|
|
|||
77
epiphany-codex-bridge/src/token_usage.rs
Normal file
77
epiphany-codex-bridge/src/token_usage.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use codex_app_server_protocol::Thread;
|
||||
use codex_app_server_protocol::ThreadHistoryBuilder;
|
||||
use codex_app_server_protocol::TurnStatus;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
|
||||
pub fn latest_token_usage_info_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
) -> Option<TokenUsageInfo> {
|
||||
rollout_items.iter().rev().find_map(|item| match item {
|
||||
RolloutItem::EventMsg(EventMsg::TokenCount(event)) => event.info.clone(),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Identifies the turn that was active when a `TokenCount` record appeared.
|
||||
///
|
||||
/// The id is preferred when it still appears in the rebuilt thread. The position
|
||||
/// is a fallback for histories whose implicit turn ids are regenerated during
|
||||
/// reconstruction.
|
||||
struct TokenUsageTurnOwner {
|
||||
id: String,
|
||||
position: Option<usize>,
|
||||
}
|
||||
|
||||
pub fn latest_token_usage_turn_id_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
thread: &Thread,
|
||||
) -> Option<String> {
|
||||
let owner = latest_token_usage_turn_owner_from_rollout_items(rollout_items)?;
|
||||
if thread.turns.iter().any(|turn| turn.id == owner.id) {
|
||||
return Some(owner.id);
|
||||
}
|
||||
owner
|
||||
.position
|
||||
.and_then(|position| thread.turns.get(position))
|
||||
.map(|turn| turn.id.clone())
|
||||
}
|
||||
|
||||
fn latest_token_usage_turn_owner_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
) -> Option<TokenUsageTurnOwner> {
|
||||
let mut builder = ThreadHistoryBuilder::new();
|
||||
let mut token_usage_turn_owner = None;
|
||||
|
||||
for item in rollout_items {
|
||||
if matches!(item, RolloutItem::EventMsg(EventMsg::TokenCount(_))) {
|
||||
token_usage_turn_owner =
|
||||
builder
|
||||
.active_turn_snapshot()
|
||||
.map(|turn| TokenUsageTurnOwner {
|
||||
id: turn.id,
|
||||
position: builder.active_turn_position(),
|
||||
});
|
||||
}
|
||||
builder.handle_rollout_item(item);
|
||||
}
|
||||
|
||||
token_usage_turn_owner
|
||||
}
|
||||
|
||||
/// Chooses a fallback turn id that should own a replayed token usage update.
|
||||
///
|
||||
/// Normal replay derives the owner from the rollout position of the latest
|
||||
/// `TokenCount` event. This fallback only preserves a stable wire shape for
|
||||
/// unusual histories where that rollout information cannot be read.
|
||||
pub fn latest_token_usage_turn_id(thread: &Thread) -> String {
|
||||
thread
|
||||
.turns
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|turn| matches!(turn.status, TurnStatus::Completed | TurnStatus::Failed))
|
||||
.or_else(|| thread.turns.last())
|
||||
.map(|turn| turn.id.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
|
@ -781,6 +781,12 @@ bridge now: app-server passes `EpiphanyInvalidationSnapshot` to
|
|||
`epiphany_freshness_watcher_snapshot` instead of defining that mapping beside
|
||||
`codex_message_processor.rs`.
|
||||
|
||||
Token-usage rollout projection has also left the app-server helper.
|
||||
`epiphany-codex-bridge::token_usage` owns the pure logic that finds the latest
|
||||
persisted token-usage snapshot and maps it back to the rebuilt turn owner.
|
||||
`token_usage_replay.rs` is now just the host shell: read rollout items, choose
|
||||
whether to replay, and send a connection-scoped JSON-RPC notification.
|
||||
|
||||
The MCP config path is now product-quarantined. `Config::to_mcp_config` keeps
|
||||
user-declared MCP servers but no longer accepts a `PluginsManager`, folds
|
||||
plugin-provided MCP servers into the runtime, enables Codex apps, enables skill
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
2
vendor/codex/codex-rs/Cargo.lock
generated
vendored
2
vendor/codex/codex-rs/Cargo.lock
generated
vendored
|
|
@ -5138,9 +5138,11 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"codex-client",
|
||||
"codex-keyring-store",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,15 +14,14 @@ use std::sync::Arc;
|
|||
|
||||
use codex_app_server_protocol::ServerNotification;
|
||||
use codex_app_server_protocol::Thread;
|
||||
use codex_app_server_protocol::ThreadHistoryBuilder;
|
||||
use codex_app_server_protocol::ThreadTokenUsage;
|
||||
use codex_app_server_protocol::ThreadTokenUsageUpdatedNotification;
|
||||
use codex_app_server_protocol::TurnStatus;
|
||||
use codex_core::CodexThread;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use epiphany_codex_bridge::token_usage::latest_token_usage_info_from_rollout_items;
|
||||
use epiphany_codex_bridge::token_usage::latest_token_usage_turn_id;
|
||||
pub(super) use epiphany_codex_bridge::token_usage::latest_token_usage_turn_id_from_rollout_items;
|
||||
|
||||
use crate::codex_message_processor::read_rollout_items_from_rollout;
|
||||
use crate::outgoing_message::ConnectionId;
|
||||
|
|
@ -78,73 +77,3 @@ pub(super) async fn latest_token_usage_info_from_rollout_path(
|
|||
let rollout_items = read_rollout_items_from_rollout(rollout_path).await.ok()?;
|
||||
latest_token_usage_info_from_rollout_items(&rollout_items)
|
||||
}
|
||||
|
||||
pub(super) fn latest_token_usage_info_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
) -> Option<TokenUsageInfo> {
|
||||
rollout_items.iter().rev().find_map(|item| match item {
|
||||
RolloutItem::EventMsg(EventMsg::TokenCount(event)) => event.info.clone(),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Identifies the turn that was active when a `TokenCount` record appeared.
|
||||
///
|
||||
/// The id is preferred when it still appears in the rebuilt thread. The position is a
|
||||
/// fallback for histories whose implicit turn ids are regenerated during reconstruction.
|
||||
struct TokenUsageTurnOwner {
|
||||
id: String,
|
||||
position: Option<usize>,
|
||||
}
|
||||
|
||||
pub(super) fn latest_token_usage_turn_id_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
thread: &Thread,
|
||||
) -> Option<String> {
|
||||
let owner = latest_token_usage_turn_owner_from_rollout_items(rollout_items)?;
|
||||
if thread.turns.iter().any(|turn| turn.id == owner.id) {
|
||||
return Some(owner.id);
|
||||
}
|
||||
owner
|
||||
.position
|
||||
.and_then(|position| thread.turns.get(position))
|
||||
.map(|turn| turn.id.clone())
|
||||
}
|
||||
|
||||
fn latest_token_usage_turn_owner_from_rollout_items(
|
||||
rollout_items: &[RolloutItem],
|
||||
) -> Option<TokenUsageTurnOwner> {
|
||||
let mut builder = ThreadHistoryBuilder::new();
|
||||
let mut token_usage_turn_owner = None;
|
||||
|
||||
for item in rollout_items {
|
||||
if matches!(item, RolloutItem::EventMsg(EventMsg::TokenCount(_))) {
|
||||
token_usage_turn_owner =
|
||||
builder
|
||||
.active_turn_snapshot()
|
||||
.map(|turn| TokenUsageTurnOwner {
|
||||
id: turn.id,
|
||||
position: builder.active_turn_position(),
|
||||
});
|
||||
}
|
||||
builder.handle_rollout_item(item);
|
||||
}
|
||||
|
||||
token_usage_turn_owner
|
||||
}
|
||||
|
||||
/// Chooses a fallback turn id that should own a replayed token usage update.
|
||||
///
|
||||
/// Normal replay derives the owner from the rollout position of the latest
|
||||
/// `TokenCount` event. This fallback only preserves a stable wire shape for
|
||||
/// unusual histories where that rollout information cannot be read.
|
||||
fn latest_token_usage_turn_id(thread: &Thread) -> String {
|
||||
thread
|
||||
.turns
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|turn| matches!(turn.status, TurnStatus::Completed | TurnStatus::Failed))
|
||||
.or_else(|| thread.turns.last())
|
||||
.map(|turn| turn.id.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue