FailureType & Step¶
Core data types used throughout the triage API.
FailureType¶
FailureType ¶
Bases: Enum
Semantic classification of why an agent step or plan failed.
Each type maps to a distinct recovery strategy. The classifier (rules-based or LLM) assigns one of these to every detected failure.
The 9 members and their stable string values:
| Member | String value | Default recovery intent |
|---|---|---|
WRONG_TOOL_CALLED |
wrong_tool_called |
Retry with correct manifest |
CONSTRAINT_IGNORED |
constraint_ignored |
Replan with constraint reminder |
LOOP_DETECTED |
loop_detected |
Replan or rollback |
PLAN_INCOMPLETE |
plan_incomplete |
Resume from subgoal |
SCHEMA_MISMATCH |
schema_mismatch |
Retry with schema hint |
CONTEXT_OVERFLOW |
context_overflow |
Replan with compressed context |
EXTERNAL_FAULT |
external_fault |
Backoff and retry |
TIMEOUT |
timeout |
Backoff and retry |
UNKNOWN |
unknown |
Escalate |
String values are the stable public identifiers used in logs and serialized state.
Step¶
Step
dataclass
¶
A single recorded step in an agent's execution trajectory.
metadata is caller-supplied and unenforced — strategies, hooks, and
RulesClassifier may inspect it, but nothing in triage populates it
automatically. Two keys have a documented convention:
metadata["http_status"](int): the real HTTP status code from the exception that produced this step (e.g. ananthropic/openaiAPIStatusError.status_code, anhttpx.HTTPStatusError'sresponse.status_code, or Ollama'sResponseError.status_code).metadata["json_rpc_code"](int): the JSON-RPC 2.0 error code from an MCP tool-call failure (e.g. anMcpError'serror.code).
RulesClassifier checks both when present, in addition to its message-text
patterns — see triage/classifier/rules.py's module docstring and
docs/concepts/classifiers.md's "Structured error codes" section. Populating
them is the caller's responsibility: extract the code from the real exception
object and pass it via record_step(Step(..., metadata={"http_status": 429})).
agent_id is caller-supplied and optional — which agent produced this step,
for multi-agent systems where a single Trajectory interleaves steps from
more than one agent. None (the default) means either a single-agent system
or an agent identity the caller didn't track; existing single-agent callers
need no changes. RulesClassifier's loop detection does not currently key on
it — see docs/concepts/multi-agent-failures.md for why that turned out to
already be correct rather than a gap to close.
triage.observability.otel_ingest.trajectory_from_spans() populates it from
an OTel span's gen_ai.agent.id/gen_ai.agent.name attribute when present.
idempotent defaults to False. Mark True only for steps that are genuinely safe
to replay — read-only tool calls and pure computations. Steps that send email, write
to a database, or charge payment methods must stay False.
When Agent(strict_idempotency=True) is set, a RETRY action is blocked if the
trajectory contains any idempotent=False step.
FailureContext¶
FailureContext
dataclass
¶
Everything a recovery strategy needs to know about a failure.
Passed to every strategy callable and raised inside TriageEscalationError and TriageAbortError.
attempt_history¶
A list of (FailureType, action_kind) tuples from all prior recovery attempts in the
current run() call. Use it to detect repeated failures and escalate intelligently:
prior_retries = sum(1 for _, kind in ctx.attempt_history if kind == "retry")
if prior_retries >= 2:
return RecoveryAction.ESCALATE("Too many retries.")
TriageContext¶
TriageContext
dataclass
¶
Structured recovery context injected into agents as _triage_context.
Replaces the scattered _triage_hint, _triage_subgoal, and
_triage_state kwargs with a single typed object. The individual kwargs
are still injected for backward compatibility.
Usage inside a wrapped agent::
async def my_agent(task: str, *, record_step, update_state, **kwargs) -> Any:
ctx: TriageContext | None = kwargs.get("_triage_context")
if ctx:
print(ctx.failure_type, ctx.hint, ctx.attempt_number)
Injected as _triage_context on every recovery attempt:
async def my_agent(task: str, *, record_step, **kwargs) -> Any:
tc: TriageContext | None = kwargs.get("_triage_context")
if tc:
print(f"Recovering from {tc.failure_type.value}, attempt {tc.attempt_number}")
if tc.hint:
... # pass hint into the LLM prompt
if tc.state:
data = tc.state.get("data") # restored from checkpoint