Callbacks¶
Track and respond to execution lifecycle events.
Base Callback Handler¶
Create a custom handler by subclassing BaseCallbackHandler:
from gmas.callbacks import BaseCallbackHandler
class MyHandler(BaseCallbackHandler):
def on_run_start(self, graph, config, **kwargs):
print("Execution started")
def on_agent_end(self, agent_id, output, *, agent_name="", **kwargs):
print(f"Agent {agent_name} ({agent_id}): {output[:100]}")
def on_agent_error(self, error, *, agent_id, **kwargs):
print(f"Agent {agent_id} failed: {error}")
def on_tool_start(self, tool_name, inputs, **kwargs):
print(f"Tool {tool_name} called with {inputs}")
def on_tool_end(self, tool_name, output, **kwargs):
print(f"Tool {tool_name} returned: {output[:100]}")
Lifecycle hooks¶
BaseCallbackHandler provides no-op methods for all events. Override only what you need:
Run Lifecycle¶
| Method | When Called |
|---|---|
on_run_start(graph, config) |
Execution begins |
on_run_end(output, **kwargs) |
Execution finishes |
Agent Lifecycle¶
| Method | When Called |
|---|---|
on_agent_start(agent_id, agent_name, step_index, prompt) |
Agent begins processing |
on_agent_end(agent_id, output, agent_name, ...) |
Agent produces output |
on_agent_error(error, agent_id, ...) |
Agent fails |
on_retry(agent_id, attempt, max_attempts, ...) |
Agent is being retried |
LLM Events¶
| Method | When Called |
|---|---|
on_llm_new_token(token, agent_id, ...) |
Token streamed (streaming mode) |
Tool Events¶
| Method | When Called |
|---|---|
on_tool_start(tool_name, inputs, ...) |
Tool execution begins |
on_tool_end(tool_name, output, ...) |
Tool execution succeeds |
on_tool_error(tool_name, error, ...) |
Tool execution fails |
Topology & Planning¶
| Method | When Called |
|---|---|
on_plan_created(plan, **kwargs) |
Execution plan created |
on_topology_changed(reason, **kwargs) |
Graph structure modified |
on_prune(agent_id, reason, **kwargs) |
Agent pruned from plan |
on_fallback(failed_agent_id, fallback_agent_id, ...) |
Fallback agent activated |
Parallel Events¶
| Method | When Called |
|---|---|
on_parallel_start(agent_ids, group_index, ...) |
Parallel group starts |
on_parallel_end(agent_ids, group_index, ...) |
Parallel group ends |
Memory Events¶
| Method | When Called |
|---|---|
on_memory_read(agent_id, entries_count, ...) |
Memory entries read |
on_memory_write(agent_id, key, value_size, ...) |
Memory entry written |
Budget Events¶
| Method | When Called |
|---|---|
on_budget_warning(budget_type, current, limit, ...) |
Budget threshold crossed |
on_budget_exceeded(budget_type, current, limit, ...) |
Budget limit exceeded |
Async Callback Handler¶
For async operations in callbacks, use AsyncCallbackHandler:
from gmas.callbacks import AsyncCallbackHandler
class AsyncHandler(AsyncCallbackHandler):
async def on_agent_end(self, agent_id, output, **kwargs):
await save_to_database(agent_id, output)
async def on_run_end(self, output, **kwargs):
await notify_webhook(output)
Using Callbacks¶
Attach handlers to the runner:
from gmas.execution import MACPRunner
runner = MACPRunner(
llm_caller=llm_caller,
callbacks=[MyHandler(), AsyncHandler()],
)
result = runner.run_round(graph)
Or per-run:
CallbackManager¶
The CallbackManager dispatches events to all registered handlers:
from gmas.callbacks import CallbackManager
manager = CallbackManager(handlers=[MyHandler()])
run_id = manager.on_run_start(graph, config)
manager.on_agent_start(agent_id="researcher", agent_name="Researcher", step_index=0, prompt="...")
manager.on_run_end(output="final answer")
AsyncCallbackManager¶
from gmas.callbacks import AsyncCallbackManager
manager = AsyncCallbackManager(handlers=[AsyncHandler()])
await manager.on_agent_end(agent_id="researcher", output="result")
Built-in Handlers¶
StdoutCallbackHandler¶
Print formatted events to the console:
MetricsCallbackHandler¶
Collect execution metrics:
from gmas.callbacks import MetricsCallbackHandler
handler = MetricsCallbackHandler()
# After execution, access collected metrics
FileCallbackHandler¶
Write events to a JSON lines file:
from gmas.callbacks import FileCallbackHandler
handler = FileCallbackHandler(filepath="logs/run.jsonl")
Typed Events¶
Events are also emitted as typed Pydantic models:
from gmas.callbacks.events import (
AgentStartEvent,
AgentEndEvent,
AgentErrorEvent,
TokenEvent,
ToolStartEvent,
ToolEndEvent,
TopologyChangedEvent,
PruneEvent,
FallbackEvent,
ParallelStartEvent,
ParallelEndEvent,
MemoryReadEvent,
MemoryWriteEvent,
BudgetWarningEvent,
BudgetExceededEvent,
)
Each event has event_type, run_id, timestamp, tags, and metadata fields plus event-specific data.