Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
4527970
feat(harness): add retry types and terminal helpers
senamakel Oct 7, 2026
046252d
feat(harness): add terminal rendering support
senamakel Oct 7, 2026
551cc70
test(harness): add terminal tests
senamakel Oct 7, 2026
c73c423
fix(tinyagents-harness): drop Hash from TerminalReason
senamakel Oct 7, 2026
0040fce
feat(harness): implement terminal outcome classification
senamakel Oct 7, 2026
383c5bc
feat(events): add turn, message and outcome events
senamakel Oct 7, 2026
adc35ce
refactor(harness): split agent loop entry from run loop
senamakel Oct 7, 2026
d4633e4
fix(agent_loop): populate new fields on queued message applied event
senamakel Oct 7, 2026
8f521b5
feat(agent-loop): add middleware hooks to the agent driver
senamakel Oct 7, 2026
6a91eaa
refactor(agent_loop): extract driver module from agent loop
senamakel Oct 7, 2026
fb5937f
test(agent_loop): register terminal outcome test module
senamakel Oct 7, 2026
ae127d0
refactor(agent_loop): split entry point from run loop
senamakel Oct 7, 2026
4f337a8
chore(agent_loop): import terminal outcome types
senamakel Oct 7, 2026
180c889
fix(harness): sanitize run failure outcome message
senamakel Oct 7, 2026
d00e9fa
fix(agent_loop): preserve terminal outcome when sanitizing hosted fai…
senamakel Oct 7, 2026
d914469
feat(agent_loop): add lifecycle tests for agent loop
senamakel Oct 7, 2026
b593892
feat(harness): add agent loop module
senamakel Oct 7, 2026
271bba4
feat(agent_loop): track turn boundaries and capture queued messages
senamakel Oct 7, 2026
114d45b
test(harness): add coverage for event module
senamakel Oct 7, 2026
eb91f1c
feat(runtime): refresh system prompt prefix on context changes
senamakel Oct 7, 2026
6c49dab
test(runtime): cover terminal outcome delivery ordering
senamakel Oct 7, 2026
57d033f
feat(runtime): add lifecycle hooks to the agent driver
senamakel Oct 7, 2026
cebaad3
refactor(session): extract session state into dedicated module
senamakel Oct 7, 2026
763d87d
refactor(session): extract session state into dedicated module
senamakel Oct 7, 2026
899b073
feat(harness): expose terminal outcome and turn lifecycle events
senamakel Oct 7, 2026
096e73c
style: apply rustfmt formatting across harness and runtime crates
senamakel Oct 7, 2026
34d7262
chore(harness): silence clippy too_many_arguments and filter lifecycl…
senamakel Oct 7, 2026
150d100
Merge origin/main into harness-uplift/terminal-outcome
senamakel Oct 7, 2026
357ec72
refactor(events): derive default for event types
senamakel Oct 7, 2026
4622042
test(harness): cover transcript mirroring across recovery and steering
senamakel Oct 7, 2026
c79025d
test(harness): cover agent loop lifecycle transitions
senamakel Oct 7, 2026
2a0a3ea
refactor(agent_loop): extract lifecycle handling into its own module
senamakel Oct 7, 2026
94c9b9a
refactor(agent_loop): split agent loop into focused modules
senamakel Oct 7, 2026
dc99ff6
test(agent_loop): cover tool surface and loop run paths
senamakel Oct 7, 2026
41f4f46
test(harness): cover halted terminal outcome for guard halts
senamakel Oct 7, 2026
c2c50c9
feat(harness): add repeat progress middleware
senamakel Oct 7, 2026
9c5e9a0
feat(runtime): add session driver for turn execution
senamakel Oct 7, 2026
5e59c96
refactor(agent_loop): split driver, runtime, and types into modules
senamakel Oct 7, 2026
39a0eaf
refactor(agent_loop): extract driver module from agent loop
senamakel Oct 7, 2026
98fbd8d
feat(tests): add integration test for loop as graph
senamakel Oct 7, 2026
9fffc16
feat(agent_loop): add terminal outcome handling for agent runs
senamakel Oct 7, 2026
f5571e4
refactor(harness): extract turn control into its own module
senamakel Oct 7, 2026
98714f2
docs(harness): document terminal outcome and transcript event semantics
senamakel Oct 7, 2026
98ac462
style: apply rustfmt formatting across agent loop and session tests
senamakel Oct 7, 2026
e1bf8cd
Resolve main merge conflict for terminal outcome
senamakel Oct 7, 2026
216c0a1
Preserve graph guard halt terminal state
senamakel Oct 7, 2026
4669757
fix(agent_loop): report accurate terminal reasons for limits and errors
senamakel Oct 7, 2026
3baec10
fix(agent_loop): clear active model call on error and mark failed runs
senamakel Oct 7, 2026
67668ec
fix(agent_loop): only attach limit kind to limit errors
senamakel Oct 7, 2026
3dcca4c
chore(agent_loop): import TerminalClass
senamakel Oct 7, 2026
469851b
refactor(terminal): simplify limit kind conversion
senamakel Oct 7, 2026
bdedb74
refactor(agent_loop): drop limit-stop bookkeeping from tool turn
senamakel Oct 7, 2026
d1e31cb
style: wrap long line in run loop
senamakel Oct 7, 2026
b18808d
feat(agent_loop): add runtime driver and context types
senamakel Oct 7, 2026
9382bba
test(harness): add terminal tests
senamakel Oct 7, 2026
d6fca09
refactor(agent_loop): extract driver module from agent loop
senamakel Oct 7, 2026
a0316a3
refactor(agent_loop): extract mixed-turn handling into its own module
senamakel Oct 7, 2026
e3892b2
test(agent_loop): cover mixed structured turn closing before queued d…
senamakel Oct 7, 2026
815703c
test(agent_loop): use tinyinference_llm response format in lifecycle …
senamakel Oct 7, 2026
f7ea35f
test(agent_loop): cover repeated final turn in mixed structured lifec…
senamakel Oct 7, 2026
e83e965
test(agent_loop): script tool-based structured output in lifecycle test
senamakel Oct 7, 2026
94572ef
test(loop_as_graph): cover failing after_agent preserving the origina…
senamakel Oct 7, 2026
7b8f73e
fix(tinyagents-harness): expose mark_model_call_failed publicly
senamakel Oct 7, 2026
25250be
test(loop_as_graph): update AgentRun import path in middleware test
senamakel Oct 7, 2026
724c685
style: apply rustfmt formatting to agent loop driver and lifecycle tests
senamakel Oct 7, 2026
5326782
Merge origin/main into harness-uplift/terminal-outcome
senamakel Oct 7, 2026
42e5d76
Merge origin/main into harness-uplift/terminal-outcome
senamakel Oct 8, 2026
3053037
fix(agent_loop): treat non-success terminal outcomes as failed
senamakel Oct 8, 2026
2dbb53f
fix(agent_loop): report the tool-call limit kind on partial stops
senamakel Oct 8, 2026
a4fa5ef
test(loop-as-graph): narrow tool cap stop test to the graph engine
senamakel Oct 8, 2026
3b1b26d
chore: I don't see a diff in your message — the "Diff:" section is em…
senamakel Oct 8, 2026
091259a
test(agent_loop): cover cache-served runs not claiming provider start
senamakel Oct 8, 2026
2825862
test(loop_as_graph): cover failing after_agent on a successful run
senamakel Oct 8, 2026
912f835
docs(harness): document typed terminal outcome on run events
senamakel Oct 8, 2026
a942821
fix(harness): provider_started after cache lookup; docs for lifecycle…
senamakel Oct 8, 2026
b49f0f7
docs(graph): document LoopState::limit_kind
senamakel Oct 8, 2026
3c2d29d
refactor(harness): move context compaction into the run loop
senamakel Oct 8, 2026
84c46c6
test(context): cover cleared limit kind not leaking into later errors
senamakel Oct 8, 2026
1cde0be
feat(agent_loop): add lifecycle tests for run loop
senamakel Oct 8, 2026
28e2198
test(agent_loop): drop token args from text_response call
senamakel Oct 8, 2026
e2bf7ab
docs(harness): document per-message payload capture and graph loop ev…
senamakel Oct 8, 2026
614c0db
docs(events): clarify per-message payload capture for applied messages
senamakel Oct 8, 2026
3589ed1
fix(agent_loop): clear active model call before after-model middleware
senamakel Oct 8, 2026
4c7f0b0
feat(agent_loop): add terminal outcome handling to agent loop
senamakel Oct 8, 2026
a86761e
test(agent_loop): collapse middleware import onto one line
senamakel Oct 8, 2026
e36254c
fix(runtime): deliver terminal hooks when the turn future is dropped
senamakel Oct 8, 2026
f08fd78
feat(agent_loop): add runtime driver and context types
senamakel Oct 8, 2026
14104d6
refactor(agent_loop): extract driver module from agent loop
senamakel Oct 8, 2026
8dc1641
fix(agent_loop): report the terminal outcome set by after_agent
senamakel Oct 8, 2026
d6a1479
feat(agent_loop): add graph driver and harness entry point
senamakel Oct 8, 2026
4df1e88
test(agent_loop): cover suspended outcomes and graph tool-cap kind
senamakel Oct 8, 2026
4071dfa
feat(agent-loop): add graph-driven agent loop driver
senamakel Oct 8, 2026
cc2bf97
test(harness): cover cache-hit provider attribution and error sanitiz…
senamakel Oct 8, 2026
c86a215
test(runtime): qualify Message import in hosted error test
senamakel Oct 8, 2026
7ac6125
feat(harness): add turn control and terminal event handling
senamakel Oct 8, 2026
5659aba
test(runtime): wait for outcomes before asserting in driver tests
senamakel Oct 8, 2026
bcd28fb
test(agent_loop): cover mixed payload capture for queued messages
senamakel Oct 8, 2026
b9efa7c
test(agent_loop): use default payload capture in lifecycle test
senamakel Oct 8, 2026
fd39592
test(agent_loop): drop redundant struct update syntax in lifecycle test
senamakel Oct 8, 2026
dbc56a0
refactor(agent_loop): extract runtime helpers into dedicated module
senamakel Oct 8, 2026
62bf67f
feat(agent_loop): add terminal output for agent loop events
senamakel Oct 8, 2026
c66788c
fix(agent_loop): match summarization usage errors by value
senamakel Oct 8, 2026
ce752f8
test(harness): cover timeout phase mapping in terminal outcomes
senamakel Oct 8, 2026
112b871
test(harness): align call timeout test with provider phase
senamakel Oct 8, 2026
ea8a4e7
style: rustfmt terminal tests
senamakel Oct 8, 2026
2b9fae6
fix(harness): classify summarizer failures from their inner error
senamakel Oct 8, 2026
6a2ff2b
test(harness): use model error in summarizer classification test
senamakel Oct 8, 2026
6f63e94
fix(agent_loop): backfill timeout phase on terminal outcomes
senamakel Oct 8, 2026
44450b4
fix(tinyagents-harness): qualify TerminalReason path in timeout check
senamakel Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 97 additions & 8 deletions crates/tinyagents-graph/src/agent_loop/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use tinyagents_harness::ids::HarnessPhase;
use tinyagents_harness::middleware::AgentRun;
use tinyagents_harness::runtime::AgentHarness;
use tinyagents_harness::steering::PauseState;
use tinyagents_harness::terminal::{TerminalOutcome, TerminalReason};
use tinyinference_llm::message::Message;

use crate::command::{NodeResult, RouteTarget};
Expand Down Expand Up @@ -81,7 +82,10 @@ where
messages: input,
..LoopState::default()
};
ctx.reset_turn_tracker(loop_state.messages.len());
Comment thread
senamakel marked this conversation as resolved.
let mut current: &str = node::PLAN;
let mut limit_stop = false;
let mut limit_kind = None;

let outcome = loop {
// Keeps `run.messages` a running snapshot of the transcript as
Expand Down Expand Up @@ -165,6 +169,9 @@ where
));
}
};
limit_stop |= loop_state.limit_stop;
Comment thread
senamakel marked this conversation as resolved.
// Latch the first kind: a later command must not erase it.
limit_kind = limit_kind.or(loop_state.limit_kind);
let Some(target) = command.goto.first() else {
break Err(TinyAgentsError::Validation(
"GraphLoopDriver: loop node's command carried no route".to_string(),
Expand Down Expand Up @@ -197,11 +204,81 @@ where
}
};

let terminal = match &outcome {
Comment thread
senamakel marked this conversation as resolved.
Ok(None) => {
let reason = if limit_stop {
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
TerminalOutcome::limit_reached(
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
limit_kind,
format!(
"stopped with the partial run: {} limit reached",
limit_kind.map_or("run", |kind| kind.as_str())
),
)
Comment thread
senamakel marked this conversation as resolved.
} else {
TerminalOutcome::completed()
};
Some(reason.with_provider_started(ctx.provider_started()))
}
Ok(Some(interrupt)) => {
let reason = interrupt
.payload
.get("reason")
.or_else(|| interrupt.payload.get("message"))
.and_then(|value| value.as_str())
.map(str::to_string);
let outcome = if let Some(summary) = ctx.take_halted_by_guard() {
TerminalOutcome::halted(summary)
} else {
TerminalOutcome::new(
TerminalReason::Paused,
reason
.clone()
.unwrap_or_else(|| format!("paused at node `{}`", interrupt.node)),
)
};
Some(outcome.with_provider_started(ctx.provider_started()))
}
Err(error) => {
use tinyagents_harness::terminal::TimeoutPhase;
let in_model_call = ctx.model_call_failed() || ctx.active_model_call.is_some();
let site = if in_model_call && ctx.call_provider_started() {
TimeoutPhase::Provider
} else if in_model_call {
TimeoutPhase::BeforeProvider
} else if ctx.provider_started() {
Comment thread
senamakel marked this conversation as resolved.
TimeoutPhase::AfterTurn
} else {
TimeoutPhase::BeforeProvider
};
// A `LimitExceeded` carries only text; the cap that tripped was
// announced by a `LimitReached` event just before.
let kind = matches!(error, TinyAgentsError::LimitExceeded(_))
.then(|| ctx.peek_last_limit())
.flatten();
let mut outcome = TerminalOutcome::from_error(error, site).with_limit_kind(kind);
// A failed summarizer already received a provider response, though
// summarizer calls bypass the context's dispatch marker.
outcome.provider_started = ctx.provider_started()
|| matches!(error, TinyAgentsError::SummarizationUsage { .. });
Some(outcome)
}
};
run.terminal = terminal.clone();
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
status.mark_running(HarnessPhase::Middleware);
harness
.middleware()
.run_after_agent(ctx, state, run)
.await?;
let after_agent = harness.middleware().run_after_agent(ctx, state, run).await;
Comment thread
senamakel marked this conversation as resolved.
if let Err(hook_error) = after_agent {
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
if outcome.is_err() {
// The originating node failure stays authoritative, as in the
// direct loop; the hook still ran for its cleanup.
tracing::warn!(
target: "tinyagents::agent_loop",
error = %hook_error,
"[agent_loop] after_agent failed after a node error; keeping the node error"
);
} else {
Comment thread
senamakel marked this conversation as resolved.
return Err(hook_error);
Comment thread
senamakel marked this conversation as resolved.
}
}

// `status.mark_completed`/`mark_interrupted`/`mark_failed` and (on
// error) `AgentEvent::RunFailed` are applied centrally by
Expand All @@ -214,8 +291,15 @@ where
// interrupt.
match outcome {
Ok(None) => {
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
// `after_agent` may have replaced the outcome; report the final one.
let outcome = run
.terminal
.clone()
.or(terminal)
.expect("terminal set before after_agent");
let record = ctx.emit(AgentEvent::RunCompleted {
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
run_id: ctx.run_id().clone(),
outcome: Some(outcome),
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
});
status.set_last_event(record.id);
Ok(())
Expand All @@ -234,10 +318,15 @@ where
.unwrap_or_else(|| format!("paused at node `{}`", interrupt.node)),
});
status.set_last_event(record.id);
run.paused = Some(PauseState {
reason,
paused_at_checkpoint: 0,
});
if !matches!(
run.terminal.as_ref().map(|outcome| outcome.reason),
Some(TerminalReason::Halted)
) {
Comment thread
senamakel marked this conversation as resolved.
run.paused = Some(PauseState {
reason,
paused_at_checkpoint: 0,
});
}
Ok(())
}
Err(error) => Err(error),
Expand Down
51 changes: 46 additions & 5 deletions crates/tinyagents-graph/src/agent_loop/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,13 @@ impl<State: Send + Sync, Ctx: Send + Sync> ModelBaseCall<State, Ctx>
{
fn call<'a>(
&'a self,
_ctx: &'a mut RunContext<Ctx>,
ctx: &'a mut RunContext<Ctx>,
state: &'a State,
request: ModelRequest,
) -> BoxModelFuture<'a> {
Box::pin(async move {
// Reached only when the wrap onion elected to call the provider.
ctx.mark_provider_started();
self.model
.invoke(state, request)
.await
Expand Down Expand Up @@ -304,6 +306,8 @@ where
tinyagents_harness::limits::LimitBehavior::StopWithPartial
) {
loop_state.finished = true;
loop_state.limit_stop = true;
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
loop_state.limit_kind = Some(tinyagents_harness::events::LimitKind::ModelCalls);
Comment thread
senamakel marked this conversation as resolved.
if loop_state.final_text.is_none() {
loop_state.final_text = Some(last_assistant_text(&loop_state.messages));
}
Expand Down Expand Up @@ -425,22 +429,36 @@ where
model: model_name.clone(),
});
status.set_last_event(started_record.id);
status.active_model_call = Some(call_id.clone());
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
ctx.active_model_call = Some(call_id.clone());
ctx.begin_model_call();

let base = DirectModelBase {
model: binding.model.as_ref(),
};
let (mut response, wrap_control) = harness
let wrapped = match harness
.middleware()
.run_wrapped_model(ctx, app_state, request, &base)
.await?
.into_response_with_control();
.await
{
Ok(wrapped) => wrapped,
Err(error) => {
status.active_model_call = None;
ctx.active_model_call = None;
ctx.mark_model_call_failed();
return Err(error);
}
};
let (mut response, wrap_control) = wrapped.into_response_with_control();
if let Some(control) = wrap_control {
ctx.request_control(control);
}

run.model_calls += 1;
Comment thread
senamakel marked this conversation as resolved.
run.steps += 1;
status.model_calls = run.model_calls;
status.active_model_call = None;
ctx.active_model_call = None;
if let Some(usage) = response.usage {
run.usage.record(usage);
loop_state.usage = run.usage;
Expand Down Expand Up @@ -530,7 +548,30 @@ where
&mut loop_state.messages,
calls,
)
.await?;
.await;
let outcome = match outcome {
Ok(outcome) => outcome,
Err(error)
Comment thread
senamakel marked this conversation as resolved.
if matches!(
harness.policy().limits.behavior,
tinyagents_harness::limits::LimitBehavior::StopWithPartial
) && matches!(error, TinyAgentsError::LimitExceeded(_))
&& ctx.peek_last_limit()
== Some(tinyagents_harness::events::LimitKind::ToolCalls) =>
{
// Only the tool-cap admission announces `LimitReached(ToolCalls)`
// (and `record_tool_call` cleared any earlier kind first), so a
// `LimitExceeded` raised by middleware is not a partial stop.
loop_state.limit_stop = true;
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
loop_state.limit_kind = Some(tinyagents_harness::events::LimitKind::ToolCalls);
loop_state.finished = true;
if loop_state.final_text.is_none() {
loop_state.final_text = Some(last_assistant_text(&loop_state.messages));
}
return Ok(goto(loop_state, node::SETTLE));
}
Comment thread
senamakel marked this conversation as resolved.
Err(error) => return Err(error),
};
loop_state.tool_calls = run.tool_calls;
loop_state.executed_tools = run.executed_tools.clone();
let _ = outcome;
Expand Down
8 changes: 8 additions & 0 deletions crates/tinyagents-graph/src/agent_loop/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ pub struct LoopState {
/// How many output-validation retries have been spent so far (bounds
/// [`tinyagents_harness::runtime::RunPolicy::output_retry`]).
pub(crate) output_retry_attempts: u8,
/// Set when the run finished because a call cap tripped under
/// `LimitBehavior::StopWithPartial`, so the driver can report
/// `TerminalReason::LimitReached` instead of a plain completion.
#[serde(default)]
Comment thread
senamakel marked this conversation as resolved.
pub(crate) limit_stop: bool,
Comment thread
senamakel marked this conversation as resolved.
/// Which cap tripped when `limit_stop` is set (`None` when unknown).
#[serde(default)]
pub(crate) limit_kind: Option<tinyagents_harness::events::LimitKind>,
}

impl LoopState {
Expand Down
57 changes: 55 additions & 2 deletions crates/tinyagents-harness/src/agent_loop/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,25 +441,78 @@ impl<State: Send + Sync, Ctx: Send + Sync> AgentHarness<State, Ctx> {
// `completed` is what made "paused for a human" look identical
// to "the model produced an empty final answer".
// A deferred run (A2) is resumable for the same reason.
let paused = terminal.run.paused.is_some() || terminal.run.deferred.is_some();
// The typed outcome is authoritative (middleware may have
// replaced it after the loop set the legacy fields); fall back
// to the legacy fields only when no outcome was recorded.
let paused = match terminal.run.terminal.as_ref() {
Some(outcome) => outcome.class == TerminalClass::Suspended,
None => terminal.run.paused.is_some() || terminal.run.deferred.is_some(),
};
// Only `Success` is a completion and `Suspended` is handled by
// `paused`; Failure, Timeout and Cancellation outcomes recorded
// by middleware on an `Ok` return must not read as completed.
let failed = terminal.run.terminal.as_ref().is_some_and(|outcome| {
!matches!(
outcome.class,
TerminalClass::Success | TerminalClass::Suspended
)
Comment thread
senamakel marked this conversation as resolved.
});
if paused {
status.mark_interrupted();
} else if failed {
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
status.mark_failed("run ended with a non-success terminal outcome".to_string());
} else {
status.mark_completed();
}
PartialRunOutcome {
run: terminal.complete(
!paused,
!paused && !failed,
Comment thread
senamakel marked this conversation as resolved.
paused.then(|| "hosted turn paused before completion".to_string()),
),
status,
error: None,
}
}
Err(error) => {
// Where the run stood when it failed decides the timeout phase
// and `provider_started`: an unfinished model call leaves
// `active_model_call` set, a completed one has bumped the
// run's call counter.
// A failure inside the model-call layer is `Provider` only if
// the provider was actually dispatched; a wrap middleware that
// rejected the call first never reached it.
let in_model_call = ctx.active_model_call.is_some() || ctx.model_call_failed();
Comment thread
senamakel marked this conversation as resolved.
let site = if in_model_call && ctx.call_provider_started() {
TimeoutPhase::Provider
} else if in_model_call {
TimeoutPhase::BeforeProvider
} else if terminal.run.model_calls > 0 {
TimeoutPhase::AfterTurn
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
} else {
TimeoutPhase::BeforeProvider
};
let last_limit = matches!(error, TinyAgentsError::LimitExceeded(_))
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
Comment thread
senamakel marked this conversation as resolved.
.then(|| ctx.take_last_limit())
.flatten();
let mut outcome =
TerminalOutcome::from_error(&error, site).with_limit_kind(last_limit);
Comment thread
senamakel marked this conversation as resolved.
if outcome.reason == crate::terminal::TerminalReason::Timeout
&& outcome.timeout_phase.is_none()
{
// A wall-clock kind filled in after classification.
outcome = outcome.with_timeout_phase(site);
}
// `site` describes this failure; the run may still have reached
Comment thread
senamakel marked this conversation as resolved.
// the provider on an earlier call.
// A failed summarizer already received a provider response, though
// summarizer calls bypass the context's dispatch marker.
outcome.provider_started = ctx.provider_started()
|| matches!(&error, TinyAgentsError::SummarizationUsage { .. });
Comment thread
senamakel marked this conversation as resolved.
terminal.run.terminal = Some(outcome.clone());
let record = ctx.emit(AgentEvent::RunFailed {
run_id,
error: error.to_string(),
outcome: Some(outcome),
});
status.set_last_event(record.id);
status.mark_failed(error.to_string());
Expand Down
Loading
Loading