mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat(auto-review) short-circuit (#18890)
## Summary Short circuit the convo if auto-review hits too many denials ## Testing - [x] Added unit tests --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
b77791c228
commit
ed4def8286
@@ -695,6 +695,8 @@ impl Session {
|
||||
models_manager: Arc::clone(&models_manager),
|
||||
tool_approvals: Mutex::new(ApprovalStore::default()),
|
||||
guardian_rejections: Mutex::new(HashMap::new()),
|
||||
guardian_rejection_circuit_breaker: Mutex::new(Default::default()),
|
||||
runtime_handle: tokio::runtime::Handle::current(),
|
||||
skills_manager,
|
||||
plugins_manager: Arc::clone(&plugins_manager),
|
||||
mcp_manager: Arc::clone(&mcp_manager),
|
||||
|
||||
@@ -3093,6 +3093,8 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
models_manager: Arc::clone(&models_manager),
|
||||
tool_approvals: Mutex::new(ApprovalStore::default()),
|
||||
guardian_rejections: Mutex::new(std::collections::HashMap::new()),
|
||||
guardian_rejection_circuit_breaker: Mutex::new(Default::default()),
|
||||
runtime_handle: tokio::runtime::Handle::current(),
|
||||
skills_manager,
|
||||
plugins_manager,
|
||||
mcp_manager,
|
||||
@@ -4313,6 +4315,8 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
models_manager: Arc::clone(&models_manager),
|
||||
tool_approvals: Mutex::new(ApprovalStore::default()),
|
||||
guardian_rejections: Mutex::new(std::collections::HashMap::new()),
|
||||
guardian_rejection_circuit_breaker: Mutex::new(Default::default()),
|
||||
runtime_handle: tokio::runtime::Handle::current(),
|
||||
skills_manager,
|
||||
plugins_manager,
|
||||
mcp_manager,
|
||||
@@ -5545,6 +5549,125 @@ impl SessionTask for NeverEndingTask {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct GuardianDeniedApprovalTask;
|
||||
|
||||
impl SessionTask for GuardianDeniedApprovalTask {
|
||||
fn kind(&self) -> TaskKind {
|
||||
TaskKind::Regular
|
||||
}
|
||||
|
||||
fn span_name(&self) -> &'static str {
|
||||
"session_task.guardian_denied_approval"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
self: Arc<Self>,
|
||||
session: Arc<SessionTaskContext>,
|
||||
ctx: Arc<TurnContext>,
|
||||
_input: Vec<UserInput>,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<String> {
|
||||
let session = session.clone_session();
|
||||
for _ in 0..3 {
|
||||
crate::guardian::record_guardian_denial_for_test(&session, &ctx, &ctx.sub_id).await;
|
||||
}
|
||||
|
||||
cancellation_token.cancelled().await;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn guardian_auto_review_interrupts_after_three_consecutive_denials() {
|
||||
let (sess, tc, rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "trigger guardian denials".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
sess.spawn_task(Arc::clone(&tc), input, GuardianDeniedApprovalTask)
|
||||
.await;
|
||||
|
||||
let mut observed = Vec::new();
|
||||
let aborted = tokio::time::timeout(std::time::Duration::from_secs(5), async {
|
||||
loop {
|
||||
let event = rx.recv().await.expect("event");
|
||||
if let EventMsg::TurnAborted(event) = &event.msg {
|
||||
let event = event.clone();
|
||||
observed.push(EventMsg::TurnAborted(event.clone()));
|
||||
break event;
|
||||
}
|
||||
observed.push(event.msg);
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"guardian denial circuit breaker should interrupt the turn; observed events: {observed:?}"
|
||||
)
|
||||
});
|
||||
assert_eq!(aborted.reason, TurnAbortReason::Interrupted);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn guardian_helper_review_interrupts_after_three_consecutive_denials() {
|
||||
let (sess, tc, rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "keep turn active for helper reviews".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
sess.spawn_task(
|
||||
Arc::clone(&tc),
|
||||
input,
|
||||
NeverEndingTask {
|
||||
kind: TaskKind::Regular,
|
||||
listen_to_cancellation_token: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let session_for_review = Arc::clone(&sess);
|
||||
let turn_for_review = Arc::clone(&tc);
|
||||
let turn_id = tc.sub_id.clone();
|
||||
let review_thread = std::thread::spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("helper review runtime");
|
||||
runtime.block_on(async move {
|
||||
for _ in 0..3 {
|
||||
crate::guardian::record_guardian_denial_for_test(
|
||||
&session_for_review,
|
||||
&turn_for_review,
|
||||
&turn_id,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
});
|
||||
review_thread.join().expect("helper review thread");
|
||||
|
||||
let mut observed = Vec::new();
|
||||
let aborted = timeout(StdDuration::from_secs(5), async {
|
||||
loop {
|
||||
let event = rx.recv().await.expect("event");
|
||||
if let EventMsg::TurnAborted(event) = &event.msg {
|
||||
let event = event.clone();
|
||||
observed.push(EventMsg::TurnAborted(event.clone()));
|
||||
break event;
|
||||
}
|
||||
observed.push(event.msg);
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"helper review circuit breaker should interrupt the turn; observed events: {observed:?}"
|
||||
)
|
||||
});
|
||||
assert_eq!(aborted.reason, TurnAbortReason::Interrupted);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[test_log::test]
|
||||
async fn abort_regular_task_emits_turn_aborted_only() {
|
||||
|
||||
@@ -1484,6 +1484,7 @@ pub(super) fn realtime_text_for_event(msg: &EventMsg) -> Option<String> {
|
||||
},
|
||||
EventMsg::Error(_)
|
||||
| EventMsg::Warning(_)
|
||||
| EventMsg::GuardianWarning(_)
|
||||
| EventMsg::RealtimeConversationStarted(_)
|
||||
| EventMsg::RealtimeConversationSdp(_)
|
||||
| EventMsg::RealtimeConversationRealtime(_)
|
||||
|
||||
Reference in New Issue
Block a user