refactor: extract is_aborted helpers, DRY 16 call sites
This commit is contained in:
@@ -306,9 +306,7 @@ pub(super) fn run_agent_turn(
|
|||||||
// Check abort after pipeline completes, before entering main loop.
|
// Check abort after pipeline completes, before entering main loop.
|
||||||
// This catches the case where the user pressed Esc during the pipeline
|
// This catches the case where the user pressed Esc during the pipeline
|
||||||
// phase, which previously ran unchecked for minutes at a time.
|
// phase, which previously ran unchecked for minutes at a time.
|
||||||
if tc
|
if crate::app::util::abort::is_aborted_direct(&tc.abort_flag)
|
||||||
.abort_flag
|
|
||||||
.load(std::sync::atomic::Ordering::SeqCst)
|
|
||||||
{
|
{
|
||||||
push_event(&events_q, TurnEvent::Error("Generation aborted by user".to_string()));
|
push_event(&events_q, TurnEvent::Error("Generation aborted by user".to_string()));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -326,9 +324,7 @@ pub(super) fn run_agent_turn(
|
|||||||
|
|
||||||
// Skip message compaction if abort was requested — the non-streaming
|
// Skip message compaction if abort was requested — the non-streaming
|
||||||
// LLM call for summarization would block without checking abort_flag.
|
// LLM call for summarization would block without checking abort_flag.
|
||||||
let wire_msgs = if !tc
|
let wire_msgs = if !crate::app::util::abort::is_aborted_direct(&tc.abort_flag)
|
||||||
.abort_flag
|
|
||||||
.load(std::sync::atomic::Ordering::SeqCst)
|
|
||||||
&& crate::app::runtime::context::shaping::should_shape(
|
&& crate::app::runtime::context::shaping::should_shape(
|
||||||
token_estimate,
|
token_estimate,
|
||||||
max_wire_tokens,
|
max_wire_tokens,
|
||||||
@@ -371,7 +367,7 @@ pub(super) fn run_agent_turn(
|
|||||||
Some(tc.temperature),
|
Some(tc.temperature),
|
||||||
tc.max_tokens,
|
tc.max_tokens,
|
||||||
|event| -> bool {
|
|event| -> bool {
|
||||||
if tc.abort_flag.load(std::sync::atomic::Ordering::SeqCst) {
|
if crate::app::util::abort::is_aborted_direct(&tc.abort_flag) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if let Ok(mut q) = events_q.lock() {
|
if let Ok(mut q) = events_q.lock() {
|
||||||
@@ -425,7 +421,7 @@ pub(super) fn run_agent_turn(
|
|||||||
Ok((msg, u)) => (msg, u.or(usage)),
|
Ok((msg, u)) => (msg, u.or(usage)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// If abort was requested, return immediately.
|
// If abort was requested, return immediately.
|
||||||
if tc.abort_flag.load(std::sync::atomic::Ordering::SeqCst)
|
if crate::app::util::abort::is_aborted_direct(&tc.abort_flag)
|
||||||
|| e.to_string().contains("aborted")
|
|| e.to_string().contains("aborted")
|
||||||
{
|
{
|
||||||
push_event(&events_q, TurnEvent::Error(
|
push_event(&events_q, TurnEvent::Error(
|
||||||
@@ -552,9 +548,7 @@ pub(super) fn run_agent_turn(
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (tool_call, tool_name, args, output, is_error, is_edit) in results_vec {
|
for (tool_call, tool_name, args, output, is_error, is_edit) in results_vec {
|
||||||
if tc
|
if crate::app::util::abort::is_aborted_direct(&tc.abort_flag)
|
||||||
.abort_flag
|
|
||||||
.load(std::sync::atomic::Ordering::SeqCst)
|
|
||||||
{
|
{
|
||||||
push_event(&events_q, TurnEvent::Error(
|
push_event(&events_q, TurnEvent::Error(
|
||||||
"Turn aborted by user".to_string(),
|
"Turn aborted by user".to_string(),
|
||||||
|
|||||||
@@ -117,11 +117,7 @@ pub fn run_subagent(
|
|||||||
for step in 0..ctx.max_steps {
|
for step in 0..ctx.max_steps {
|
||||||
// Check abort flag before each LLM call so a stuck subagent can
|
// Check abort flag before each LLM call so a stuck subagent can
|
||||||
// be cancelled from the parent (mirrors main agent behaviour).
|
// be cancelled from the parent (mirrors main agent behaviour).
|
||||||
if ctx
|
if crate::app::util::abort::is_aborted(&ctx.abort_flag) {
|
||||||
.abort_flag
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst))
|
|
||||||
{
|
|
||||||
let _ = tx.blocking_send(SubagentEvent::StepFailed {
|
let _ = tx.blocking_send(SubagentEvent::StepFailed {
|
||||||
step,
|
step,
|
||||||
error: "subagent aborted by parent".to_string(),
|
error: "subagent aborted by parent".to_string(),
|
||||||
@@ -154,11 +150,7 @@ pub fn run_subagent(
|
|||||||
Some(4096),
|
Some(4096),
|
||||||
|event| -> bool {
|
|event| -> bool {
|
||||||
// Check abort on every SSE event for responsive cancellation.
|
// Check abort on every SSE event for responsive cancellation.
|
||||||
if ctx
|
if crate::app::util::abort::is_aborted(&ctx.abort_flag) {
|
||||||
.abort_flag
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst))
|
|
||||||
{
|
|
||||||
return false; // signals provider to abort
|
return false; // signals provider to abort
|
||||||
}
|
}
|
||||||
match event {
|
match event {
|
||||||
@@ -194,10 +186,7 @@ pub fn run_subagent(
|
|||||||
Ok(result) => break result,
|
Ok(result) => break result,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_str = e.to_string();
|
let err_str = e.to_string();
|
||||||
let is_abort = ctx
|
let is_abort = crate::app::util::abort::is_aborted(&ctx.abort_flag)
|
||||||
.abort_flag
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst))
|
|
||||||
|| err_str.contains("aborted");
|
|| err_str.contains("aborted");
|
||||||
|
|
||||||
if is_abort || !should_retry_subagent_step(&err_str) || step_attempt >= max_step_retries {
|
if is_abort || !should_retry_subagent_step(&err_str) || step_attempt >= max_step_retries {
|
||||||
@@ -279,7 +268,7 @@ pub fn run_subagent(
|
|||||||
for tool_call in &tool_calls {
|
for tool_call in &tool_calls {
|
||||||
let handle = s.spawn(move || {
|
let handle = s.spawn(move || {
|
||||||
// Check abort flag before each tool execution
|
// Check abort flag before each tool execution
|
||||||
if ctx.abort_flag.as_ref().is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst)) {
|
if crate::app::util::abort::is_aborted(&ctx.abort_flag) {
|
||||||
return (tool_call, Err(anyhow::anyhow!("subagent aborted by parent during tool execution")));
|
return (tool_call, Err(anyhow::anyhow!("subagent aborted by parent during tool execution")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//! Shared abort-flag checks.
|
||||||
|
//!
|
||||||
|
//! The two variants (Option<Arc<AtomicBool>> and bare AtomicBool) are
|
||||||
|
//! used across the agent runtime, subagent, workflow engine, and provider.
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Check whether an optional abort flag has been signalled.
|
||||||
|
pub fn is_aborted(flag: &Option<Arc<AtomicBool>>) -> bool {
|
||||||
|
flag.as_ref().is_some_and(|f| f.load(Ordering::SeqCst))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether a bare abort flag has been signalled.
|
||||||
|
pub fn is_aborted_direct(flag: &AtomicBool) -> bool {
|
||||||
|
flag.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether an optional borrowed abort flag has been signalled.
|
||||||
|
///
|
||||||
|
/// This variant handles the `Option<&AtomicBool>` pattern used in
|
||||||
|
/// service/provider.rs where the flag is passed as a by-value optional
|
||||||
|
/// reference rather than an `Arc`.
|
||||||
|
pub fn is_aborted_ref(flag: Option<&AtomicBool>) -> bool {
|
||||||
|
flag.is_some_and(|f| f.load(Ordering::SeqCst))
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
//! Utility modules for shared helpers.
|
//! Utility modules for shared helpers.
|
||||||
|
|
||||||
|
pub mod abort;
|
||||||
pub mod backoff;
|
pub mod backoff;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub(crate) use primitives::PrimitiveCtx;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::AtomicBool,
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -376,10 +376,7 @@ fn spawn_single_agent(sp: SpawnCtx<'_>) -> anyhow::Result<String> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check abort before even starting the subagent.
|
// Check abort before even starting the subagent.
|
||||||
if sp
|
if crate::app::util::abort::is_aborted(sp.abort_flag)
|
||||||
.abort_flag
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|f| f.load(Ordering::SeqCst))
|
|
||||||
{
|
{
|
||||||
anyhow::bail!("subagent '{}' aborted before start", sp.agent_name);
|
anyhow::bail!("subagent '{}' aborted before start", sp.agent_name);
|
||||||
}
|
}
|
||||||
@@ -405,9 +402,7 @@ fn spawn_single_agent(sp: SpawnCtx<'_>) -> anyhow::Result<String> {
|
|||||||
|
|
||||||
for attempt in 1..=2 {
|
for attempt in 1..=2 {
|
||||||
// Don't retry if aborted.
|
// Don't retry if aborted.
|
||||||
if bg_abort_thread
|
if crate::app::util::abort::is_aborted(&bg_abort_thread)
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|f| f.load(Ordering::SeqCst))
|
|
||||||
{
|
{
|
||||||
let _ = done_tx.send(Err(anyhow::anyhow!(
|
let _ = done_tx.send(Err(anyhow::anyhow!(
|
||||||
"subagent '{bg_name_thread}' aborted by user"
|
"subagent '{bg_name_thread}' aborted by user"
|
||||||
@@ -453,7 +448,7 @@ fn spawn_single_agent(sp: SpawnCtx<'_>) -> anyhow::Result<String> {
|
|||||||
"subagent '{bg_name}' timed out after {timeout}ms",
|
"subagent '{bg_name}' timed out after {timeout}ms",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if bg_abort.as_ref().is_some_and(|f| f.load(Ordering::SeqCst)) {
|
if crate::app::util::abort::is_aborted(&bg_abort) {
|
||||||
break Err(anyhow::anyhow!("subagent '{bg_name}' aborted by user"));
|
break Err(anyhow::anyhow!("subagent '{bg_name}' aborted by user"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,7 +457,7 @@ fn spawn_single_agent(sp: SpawnCtx<'_>) -> anyhow::Result<String> {
|
|||||||
if let Ok(r) = done_rx.recv_timeout(poll_interval) {
|
if let Ok(r) = done_rx.recv_timeout(poll_interval) {
|
||||||
break r;
|
break r;
|
||||||
}
|
}
|
||||||
if bg_abort.as_ref().is_some_and(|f| f.load(Ordering::SeqCst)) {
|
if crate::app::util::abort::is_aborted(&bg_abort) {
|
||||||
break Err(anyhow::anyhow!("subagent '{bg_name}' aborted by user"));
|
break Err(anyhow::anyhow!("subagent '{bg_name}' aborted by user"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ impl LlmClient {
|
|||||||
|
|
||||||
// Check abort before each retry so user cancellation is
|
// Check abort before each retry so user cancellation is
|
||||||
// responsive even during a long non-streaming backoff chain.
|
// responsive even during a long non-streaming backoff chain.
|
||||||
if abort_flag.is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst)) {
|
if crate::app::util::abort::is_aborted_ref(abort_flag) {
|
||||||
anyhow::bail!("aborted");
|
anyhow::bail!("aborted");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ impl LlmClient {
|
|||||||
if meaningful_content {
|
if meaningful_content {
|
||||||
// Check abort before entering the blocking non-streaming
|
// Check abort before entering the blocking non-streaming
|
||||||
// call — otherwise the fallback ignores user cancellation.
|
// call — otherwise the fallback ignores user cancellation.
|
||||||
if abort_flag.is_some_and(|f| f.load(std::sync::atomic::Ordering::SeqCst)) {
|
if crate::app::util::abort::is_aborted_ref(abort_flag) {
|
||||||
return Err(anyhow::anyhow!("aborted"));
|
return Err(anyhow::anyhow!("aborted"));
|
||||||
}
|
}
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
|
|||||||
Reference in New Issue
Block a user