mirror of
https://github.com/openai/codex.git
synced 2026-11-09 00:39:32 +00:00
Preserve originating budgets for code mode notifications (#44867)
## Why A yielded code mode cell can emit a notification after the session switches models. Its output should retain the originating model's truncation budget instead of using the receiving model's budget. ## What changed Carry the cell's output token limit, including the serialization allowance, through notification dispatch and attach it as `history_truncation_token_limit` metadata. Allow `inject_if_running` to accept response item envelopes so queued notifications preserve this metadata in history and rollouts. ## Testing Add regression coverage for delayed notifications across a model switch, verifying per-cell truncation budgets, full notification text in raw events, and persisted rollout metadata. Add a task-completion test that checks notification budgets are preserved alongside queued user input. GitOrigin-RevId: 28380a039a6d41ea324401fcf1f611d81a279477
This commit is contained in:
1 parent
2fc4bda3ca
commit
122d55cba8
6 files changed
+301
-12
No files matched your search
@@ -13,10 +13,10 @@ impl Session {
|
||||
clippy::await_holding_invalid_type,
|
||||
reason = "active turn checks and turn state updates must remain atomic"
|
||||
)]
|
||||
pub async fn inject_if_running(
|
||||
pub(crate) async fn inject_if_running<T: Into<ResponseItemEnvelope>>(
|
||||
&self,
|
||||
input: Vec<ResponseItem>,
|
||||
) -> Result<(), Vec<ResponseItem>> {
|
||||
input: Vec<T>,
|
||||
) -> Result<(), Vec<T>> {
|
||||
let mut active = self.active_turn.lock().await;
|
||||
match active.as_mut() {
|
||||
Some(active_turn) => {
|
||||
@@ -25,7 +25,7 @@ impl Session {
|
||||
active_turn.turn_state.as_ref(),
|
||||
input
|
||||
.into_iter()
|
||||
.map(ResponseItemEnvelope::new)
|
||||
.map(Into::into)
|
||||
.map(PendingTurnInput::ResponseItem)
|
||||
.collect(),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//! Task completion preserves a queued notification's budget alongside real user input.
|
||||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn task_finish_preserves_notification_budget_and_queued_user_input() {
|
||||
let (session, turn, _rx) = make_session_and_context_with_rx().await;
|
||||
session
|
||||
.spawn_task(
|
||||
Arc::clone(&turn),
|
||||
Vec::new(),
|
||||
NeverEndingTask {
|
||||
kind: TaskKind::Regular,
|
||||
listen_to_cancellation_token: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let text = "notification diagnostic line\n".repeat(500);
|
||||
let notification = |text| ResponseItem::CustomToolCallOutput {
|
||||
id: None,
|
||||
call_id: "call-a".to_string(),
|
||||
name: Some("exec".to_string()),
|
||||
output: FunctionCallOutputPayload::from_text(text),
|
||||
internal_chat_message_metadata_passthrough: None,
|
||||
};
|
||||
session
|
||||
.inject_if_running(vec![ResponseItemEnvelope {
|
||||
item: notification(text.clone()),
|
||||
metadata: Some(CodexHarnessMetadata {
|
||||
history_truncation_token_limit: Some(120),
|
||||
..Default::default()
|
||||
}),
|
||||
}])
|
||||
.await
|
||||
.unwrap();
|
||||
let user_input = vec![UserInput::Text {
|
||||
text: "keep this queued user input".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
assert!(matches!(
|
||||
submit_steer_only(&session, user_input.clone(), &turn.sub_id).await,
|
||||
TurnInputSubmission::Steered { .. }
|
||||
));
|
||||
let mut current = turn.initial_settings.as_ref().clone();
|
||||
Arc::make_mut(&mut current.model_info).truncation_policy =
|
||||
codex_protocol::openai_models::TruncationPolicyConfig::tokens(/*limit*/ 400);
|
||||
turn.current_settings.store(Arc::new(current));
|
||||
|
||||
session
|
||||
.on_task_finished(Arc::clone(&turn), /*task_result*/ Ok(None))
|
||||
.await;
|
||||
|
||||
let expected = vec![
|
||||
notification(codex_utils_output_truncation::truncate_text(
|
||||
&text,
|
||||
codex_utils_output_truncation::TruncationPolicy::Tokens(120),
|
||||
)),
|
||||
session.response_item_from_user_input(user_input),
|
||||
];
|
||||
let history = session.clone_history().await;
|
||||
assert_eq!(
|
||||
strip_response_item_ids(&strip_metadata_from_items(&raw_history_items(&history))),
|
||||
strip_response_item_ids(&strip_metadata_from_items(&expected)),
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
#[path = "notification_tests.rs"]
|
||||
mod notification_tests;
|
||||
|
||||
use super::mcp_refresh::McpRefresh;
|
||||
use super::step_settings::ResolvedStepSettings;
|
||||
use super::step_settings::StepSettings;
|
||||
|
||||
Reference in new issue
Block a user