Models, budgets and limits
View SourceExAgent uses official stock ReqLLM as its general backend. Custom adapters
implement ExAgent.Model; the Test adapter runs locally. A model being present
in a catalogue is distinct from its tools, output and streaming profile being
qualified for ExAgent.
Resolve a text model
The application supplies credentials. This example resolves configuration;
calling ExAgent.run/3 with it makes a real provider request:
{:ok, model} = ExAgent.Model.resolve({:openai, id: "gpt-4o-mini"},
api_key: System.fetch_env!("OPENAI_API_KEY")
)
agent = ExAgent.new(model: model, instructions: "Be concise.")Use instance credentials for explicit ownership. ReqLLM's startup environment loading is described in Getting started. Never place a key in a prompt or persist the complete live model.
Configure Chat tools and streaming
The explicit profile binds the protocol, tools and reasoning capability:
model = ExAgent.Models.ReqLLM.new(
model: %{
provider: :openai,
id: "gpt-4o-mini",
capabilities: %{tools: %{enabled: true}, reasoning: %{enabled: false}},
extra: %{wire: %{protocol: "openai_chat"}}
},
api_key: System.fetch_env!("OPENAI_API_KEY"),
tool_profile: :chat_tools_v1
)These declarations configure admission; they do not prove the endpoint behaves correctly. Qualify the exact gateway/model combination you deploy. The supported profiles describe the available tool/output combinations. Unrepresented reasoning, modality and continuation combinations reject explicitly.
Native output is separate: output_profile: :chat_json_schema_v1 plus agent
output_mode: :native. See Tools and output.
Route OpenRouter Chat tools explicitly
Use tool_profile: :openrouter_chat_tools_v1 for a qualified OpenRouter Chat
endpoint. The host supplies the exact model ID and truthful capabilities:
model = ExAgent.Models.ReqLLM.new(
model: %{
provider: :openrouter,
id: qualified_model_id,
capabilities: %{tools: %{enabled: true}, reasoning: %{enabled: false}},
extra: %{wire: %{protocol: "openai_chat"}}
},
api_key: System.fetch_env!("OPENROUTER_API_KEY"),
tool_profile: :openrouter_chat_tools_v1,
provider_options: [
openrouter_provider: %{only: [qualified_provider_id], allow_fallbacks: false}
]
)Routing admits order, only, ignore, allow_fallbacks and
require_parameters. Provider lists contain 1–32 bounded identifiers. Optional
app_title and app_referer are attribution; other wire settings remain closed.
The mandatory tool argument envelope and validation still apply.
For a reasoning-capable model, reasoning_mode: :none additionally requires
declared support for effort "none" and disabling thinking. It sends one
max_tokens budget. Declaring those capabilities does not establish real
provider compatibility. This tools profile does not enable native JSON output.
The route and reasoning mode are bound before the first request and in tool history. Changing them requires a fresh conversation; an existing approval or uncertain request cannot resume through another route. Earlier unbound OpenRouter history is rejected. See Migration for continuation version 4 and the unaffected OpenAI history versions.
Bound requests and tool calls
agent = ExAgent.new(
model: "test",
usage_limits: %ExAgent.UsageLimits{request_limit: 3, tool_calls_limit: 2}
)
{:ok, result} = ExAgent.run(agent, "Hello!")
result.request_count
# => 1Request and tool counters are host observations. Limits apply before admission of the next operation. Scoped child runs inherit ancestor authority and budgets; a child cannot enlarge the parent's allowance. Parent usage is inclusive of its children, so adding every span's totals would count delegated work twice.
For token/cost dimensions, read ExAgent.UsageLimits
and ExAgent.CostGuard. Strict metric accounting
requires a finite request limit. Provider-reported or normalized tokens and
estimated cost have quality/status fields. Unknown usage or cost is not zero,
and an estimated threshold is not a guaranteed invoice ceiling.
Separate the timeout boundaries
| Boundary | Meaning |
|---|---|
ModelSettings.timeout | Receive inactivity timeout; takes precedence over the model's receive setting. |
ReqLLM total_timeout | Positive millisecond bound on one buffered model operation. |
Run deadline | Absolute monotonic milliseconds, inherited and narrowed by child scope. |
| Continuation lease and active-time budget | Bounds one owned continuation attempt; not permission to retry an uncertain effect. |
Stopping an owned task closes framework resources. It cannot roll back external IO or guarantee cancellation of arbitrary application callbacks. Host retention limits apply after stock decoding; no upstream hard RAM bound before decoding is promised. Migration documents option precedence and failure shapes.
An incomplete response stays in RunError.partial.pending_response, outside
executable history. Its duplicate in RunError.reason may carry an explicit
payload_omitted marker when that error copy would exceed 4 KiB. Read the cause
from the bounded RequestError and diagnostic partial data from RunError;
neither authorizes tools or proves that a turn completed. Arbitrary oversized
error control still fails the original retention limit.
API: ExAgent.Models.ReqLLM,
ExAgent.Model,
ExAgent.ModelSettings.