windlass.agent.runtime¶
runtime
¶
The built-in agent runtime.
A reason/act loop: call the model, execute whatever tools it asks for, feed the
results back, repeat until it answers or runs out of budget. That is what an
agent is, and Windlass implements it directly rather than requiring a graph
library — so agents work on a bare pip install windlass.
The runtime supports everything the LangGraph path does except graph topology:
streaming, memory, guardrails, checkpointing, human-in-the-loop approval,
parallel tool execution and full tracing. Reach for
:class:~windlass.agent.graph.LangGraphRuntime (.graph() on the builder)
when you need conditional routing, subgraphs or explicit state machines.
Example
from windlass import Windlass, tool @tool ... def add(a: int, b: int) -> int: ... '''Add two numbers.''' ... return a + b agent = Windlass.agent().llm("fake", responses=["4"]).tool(add) agent.run("What is 2 + 2?").output '4'
AgentRuntime
¶
AgentRuntime(
*,
llm: LLM,
tools: ToolRegistry | Sequence[Any] | None = None,
system_prompt: str | None = None,
max_iterations: int = 10,
memory: Memory | None = None,
guardrail: Guardrail | None = None,
checkpointer: Checkpointer | None = None,
tracer: Tracer | None = None,
parallel_tools: bool = True,
require_approval: bool = False,
max_tool_call_retries: int = 2,
name: str = "agent"
)
A tool-calling agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
llm
|
LLM
|
The reasoning model. Must support tool calling if tools are bound. |
required |
tools
|
ToolRegistry | Sequence[Any] | None
|
Tools the agent may call. |
None
|
system_prompt
|
str | None
|
Instructions prepended to every run. |
None
|
max_iterations
|
int
|
Ceiling on reason/act cycles. Prevents a confused agent from looping until your budget runs out. |
10
|
memory
|
Memory | None
|
Optional conversation memory, keyed by |
None
|
guardrail
|
Guardrail | None
|
Optional input/output policy. |
None
|
checkpointer
|
Checkpointer | None
|
Optional durable state, enabling resume and time travel. |
None
|
tracer
|
Tracer | None
|
Observability backend. |
None
|
parallel_tools
|
bool
|
Execute simultaneous tool calls concurrently. |
True
|
require_approval
|
bool
|
Pause before every tool call, not just those whose
tools declare |
False
|
max_tool_call_retries
|
int
|
How many times a run may recover from the model
emitting an unparseable tool call before giving up. Each recovery
costs one iteration from |
2
|
name
|
str
|
Agent name, used in traces and multi-agent routing. |
'agent'
|
Attributes:
| Name | Type | Description |
|---|---|---|
tools |
The bound :class: |
|
name |
The agent's name. |
Raises:
| Type | Description |
|---|---|
AgentError
|
When tools are bound to a model that cannot call them. |
Source code in src\windlass\agent\runtime.py
arun
async
¶
arun(
prompt: Any,
*,
thread_id: str | None = None,
max_iterations: int | None = None,
**llm_kwargs: Any
) -> AgentResponse
Run the agent to completion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
Any
|
A string, a message, or a full transcript. |
required |
thread_id
|
str | None
|
Conversation thread. Required to use memory or checkpointing across calls; generated when omitted. |
None
|
max_iterations
|
int | None
|
Override for the step budget. |
None
|
**llm_kwargs
|
Any
|
Per-call model overrides. |
{}
|
Returns:
| Type | Description |
|---|---|
AgentResponse
|
The answer, transcript, per-step trace and aggregate usage. |
Raises:
| Type | Description |
|---|---|
MaxIterationsExceeded
|
When the budget runs out before an answer. |
InterruptedError_
|
When a tool needs human approval. Resume with
:meth: |
GuardrailViolation
|
When a guardrail blocks the input or output. |
AgentError
|
When the run cannot proceed. |
Performance
One model call per iteration, plus tool time. Independent tool calls in the same turn run concurrently, which is usually where the wall clock is won.
Example
import asyncio from windlass import Windlass agent = Windlass.agent().llm("fake", responses=["Done."]) asyncio.run(agent.arun("Say done.")).output 'Done.'
Source code in src\windlass\agent\runtime.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
run
¶
run(
prompt: Any,
*,
thread_id: str | None = None,
max_iterations: int | None = None,
**llm_kwargs: Any
) -> AgentResponse
Blocking :meth:arun.
Source code in src\windlass\agent\runtime.py
astream
async
¶
astream(
prompt: Any, *, thread_id: str | None = None, **llm_kwargs: Any
) -> AsyncIterator[StreamEvent]
Stream the agent's work as it happens.
You get text deltas as the model produces them, tool_call events
when it decides to act, and a final done. That is what a live agent
UI needs: users tolerate latency far better when they can see progress.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
Any
|
A string, a message, or a full transcript. |
required |
thread_id
|
str | None
|
Conversation thread. |
None
|
**llm_kwargs
|
Any
|
Per-call model overrides. |
{}
|
Yields:
| Type | Description |
|---|---|
AsyncIterator[StreamEvent]
|
class: |
Raises:
| Type | Description |
|---|---|
MaxIterationsExceeded
|
When the budget runs out. |
Source code in src\windlass\agent\runtime.py
stream
¶
astream_text
async
¶
Stream only the assistant's text — the common case for a chat UI.
Source code in src\windlass\agent\runtime.py
aresume
async
¶
aresume(
thread_id: str,
*,
approved: bool = True,
feedback: str = "",
edited_arguments: dict[str, dict[str, Any]] | None = None,
**llm_kwargs: Any
) -> AgentResponse
Resume a run that paused for human approval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
The thread reported on the :class: |
required |
approved
|
bool
|
True to execute the pending calls, False to reject them. |
True
|
feedback
|
str
|
Message shown to the model when rejecting, so it can try a different approach instead of simply repeating itself. |
''
|
edited_arguments
|
dict[str, dict[str, Any]] | None
|
Replacement arguments keyed by tool-call id. This is the "human edits the action" case — approve the intent, fix the parameters. |
None
|
**llm_kwargs
|
Any
|
Per-call model overrides. |
{}
|
Returns:
| Type | Description |
|---|---|
AgentResponse
|
The completed run. |
Raises:
| Type | Description |
|---|---|
AgentError
|
When there is no checkpoint, or nothing was pending. |
Example
from windlass import Windlass, tool, AgentInterrupt @tool(requires_approval=True) ... def deploy(env: str) -> str: ... '''Deploy to an environment.''' ... return f"deployed to {env}" agent = (Windlass.agent() ... .llm("fake", responses=["", "Deployed."], ... tool_calls=[[ToolCall(name="deploy", ... arguments={"env": "prod"})], []]) ... .tool(deploy).checkpoint()) try: ... agent.run("Deploy to prod", thread_id="t1") ... except AgentInterrupt as pause: ... resumed = agent.resume("t1", approved=True) resumed.output 'Deployed.'
Source code in src\windlass\agent\runtime.py
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | |
resume
¶
pending_approvals
¶
Return the tool calls awaiting approval on a thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
thread_id
|
str
|
The thread to inspect. |
required |
Returns:
| Type | Description |
|---|---|
list[ToolCall]
|
The pending calls, or |
Source code in src\windlass\agent\runtime.py
state
¶
history
¶
Return recent checkpoints for a thread, newest first.
native_llm
¶
native_graph
¶
Return the underlying execution graph.
The built-in runtime has no graph — it is a loop. Use .graph() on the
agent builder to get a LangGraph-backed runtime whose native_graph()
returns a real StateGraph you can add nodes and edges to.
Raises:
| Type | Description |
|---|---|
AgentError
|
Always, explaining how to get a real graph. |
Source code in src\windlass\agent\runtime.py
describe
¶
Return a JSON-safe description of the agent's configuration.
Source code in src\windlass\agent\runtime.py
aclose
async
¶
Release resources held by the model and any MCP connections.