Skip to content

Invocation Limits

Per-invocation limits cap how much work a single agent call can do — model turns, output tokens, or total tokens — before the event loop stops cleanly. Limits apply to one agent(...), invoke_async(...), or stream_async(...) call only; counters reset on the next invocation even when reusing the same Agent instance.

When unset, behavior matches the pre-limits agent loop (unbounded tool turns).

Quick start

python
from elsai import Agent
from elsai.agent import AgentConfig

# Default cap for every call on this agent
agent = Agent(
    config=AgentConfig(limits={"turns": 50}),
)

result = agent("Research and summarize the latest AI news")

# Per-call override — tighter budget for this invocation only
result = agent("Quick sanity check", limits={"turns": 3})

Check how the loop stopped:

python
print(result.stop_reason)
# "limit_turns" | "limit_output_tokens" | "limit_total_tokens"

Limit dimensions

KeyWhat it countsstop_reason when tripped
turnsAgent loop iterations — one model call plus any tool execution that follows"limit_turns"
output_tokensCumulative model-generated tokens across all model calls in the loop"limit_output_tokens"
total_tokensCumulative input + output tokens across all model calls"limit_total_tokens"

Set any subset. Omit a key (or pass limits=None) for no cap on that dimension.

Turn semantics

A turn is one trip through the loop: the model is called, and if it requests tools, those tools run before the next turn begins. Limits are checked at the top of each iteration, so tools requested by the previous turn always finish before a cap fires. agent.messages stays in a reinvokable state when a limit stops the loop.

Token caps are soft

output_tokens and total_tokens are evaluated at turn boundaries, not mid-stream. A single oversized model response can overshoot the budget slightly before the next check.

Priority when multiple caps trip together

Highest first: turnstotal_tokensoutput_tokens.

Configuration

Agent default via AgentConfig

python
from elsai import Agent
from elsai.agent import AgentConfig

agent = Agent(
    config=AgentConfig(
        limits={
            "turns": 50,
            "output_tokens": 8000,
            "total_tokens": 16000,
        },
    ),
)

Every call uses these caps unless overridden at the call site.

Per-call override

python
result = await agent.invoke_async(
    "Deep research task",
    limits={"turns": 10, "total_tokens": 4000},
)

async for event in agent.stream_async("Stream with a cap", limits={"turns": 5}):
    ...

Call-site limits= replaces the agent default for that invocation only (not merged field-by-field).

Validation

Each set cap must be a positive int. Booleans are rejected (True/False would otherwise pass as 1/0 in Python).

python
# TypeError at construction or call time
Agent(config=AgentConfig(limits={"turns": -1}))
agent("hi", limits={"turns": True})

Production guidance

Long-running agents that call tools in a loop should set an explicit turn cap to avoid runaway cost:

python
agent = Agent(
    tools=[web_search, file_read],
    config=AgentConfig(limits={"turns": 50}),
)

For sub-agents wrapped with as_tool(), pass tighter per-call limits from the orchestrator:

python
# Orchestrator controls budget per delegation
result = specialist.invoke_async(task, limits={"turns": 5, "total_tokens": 3000})

Recursion depth

Very large turns values (roughly 100–150+) may risk RecursionError in the current recursive loop implementation, depending on stack depth from hooks and nested agent-as-tool calls. Prefer reasonable caps for production; an iterative loop rewrite would remove this coupling in a future release.

Relationship to other stop reasons

MechanismScopeTypical stop_reason
Invocation limitsSingle calllimit_turns, limit_output_tokens, limit_total_tokens
Model max tokensSingle model responsemax_tokens
User cancelSingle callcancelled
Normal completionSingle callend_turn

Invocation limits do not persist across calls. Conversation managers and context-window retry logic are separate — see Agent Loop.

  • Agent Loop — where limits are evaluated each iteration
  • Hooks — observe AfterInvocationEvent when a limit stops the run
  • AgentConfiglimits field
  • Limits — type reference

Copyright © 2026 elsai foundry.