Hooks & Side Effects¶
HookResult ¶
Bases: BaseModel
Result returned by a sync hook.
event
class-attribute
instance-attribute
¶
The replacement payload when action is "modify".
Typed loosely because a sync hook receives whatever its trigger passes, and only one of them passes a RoomEvent: BEFORE_TTS receives a string, the bridge triggers receive frames, the tool and generation triggers receive their own event types. Restricting this to RoomEvent made "modify" impossible for all of them — the hook raised, the engine logged and carried on, and the unmodified payload continued on its way.
Consumers must check the type they expect before using it.
allow_unverified
classmethod
¶
On ON_VERIFICATION_REQUIRED: let an unverified sender's message through.
It goes on labelled with the sender's verification; without this, the
message is held from the room's agents (RFC §11.7.3). A plain
:meth:allow does not pass it: a hook that only watches never lets a
message through by accident.
block
classmethod
¶
Block the event from proceeding.
modify
classmethod
¶
Replace the payload before it proceeds.
Takes whatever the trigger passed the hook — a string for BEFORE_TTS, a RoomEvent for BEFORE_BROADCAST — because restricting it to RoomEvent left every other sync trigger unable to use its own constructor.
InjectedEvent ¶
Bases: BaseModel
An event injected by a hook as a side effect.
AI Generation Hooks¶
What ON_AI_RESPONSE carries once a turn of intelligence completes, and the
record its declared_tools is made of. See the hook table in
Features.
AIResponseEvent
dataclass
¶
AIResponseEvent(channel_id, response_content, room_id=None, tool_calls_count=0, usage=dict(), thinking='', round_count=0, loop_end_reason=None, latency_ms=0, streaming=False, timestamp=_utcnow(), segments=list(), usage_metadata=dict(), declared_tools=list())
Emitted through ON_AI_RESPONSE hooks after AI generation completes.
Provides response content, usage metrics, and timing for evaluation and scoring integrations.
response_content
instance-attribute
¶
Everything the model said in the turn, as one readable transcript.
The turn's :attr:segments joined with :data:RESPONSE_SEGMENT_SEPARATOR
— a blank line at every tool-call boundary, nothing inside a segment. A
turn without a tool call is its single segment, verbatim.
tool_calls_count
class-attribute
instance-attribute
¶
Number of tool calls executed during generation.
usage
class-attribute
instance-attribute
¶
Token usage from the provider (input_tokens, output_tokens).
thinking
class-attribute
instance-attribute
¶
Extended thinking/reasoning text (if supported by provider).
loop_end_reason
class-attribute
instance-attribute
¶
Which of the tool loop's rules ended the turn, or None if unreported.
The loop names it on its :class:LoopEndMarker, and this event carries
it: without it a hook could see that a turn ended and how much work it did, never
whether it finished or was cut off. Counting tool calls does not answer it
— :attr:tool_calls_count reports the calls the turn ran, so a healthy
multi-round answer and one guillotined by the round cap both report a
positive count. Read this instead: "completed" is a turn that ended on
its own terms, and "max_rounds", "timeout", "budget_exceeded", "cancelled",
"force_stopped", "truncated", "empty_response", "unfinished"
and "error" each name the rule that stopped it ("unfinished": the
channel's continuation policy still asked to go on once its tries had run
out; "error": the provider interrupted the turn after a tool round and
the turn was delivered once its loop ended, RFC §6.4). An ACP agent's
turn carries its stop reason: "completed" for end_turn, else the
agent's own ("max_tokens", "max_turn_requests", "refusal",
"cancelled").
A turn whose loop did not reach its end fires no event at all: one that
raised (a streamed turn the provider interrupted included) or whose
stream was closed first (a barge-in, a transport that stopped reading, a
task cancelled from outside). Its llm.generate span carries what its
rounds used.
None means the path that fired the hook reported no reason, not that the turn completed.
latency_ms
class-attribute
instance-attribute
¶
Total generation time in milliseconds.
timestamp
class-attribute
instance-attribute
¶
When the response was generated.
segments
class-attribute
instance-attribute
¶
The turn's text, one entry per stretch between tool calls, in order.
Empty stretches are dropped, so :attr:response_content is exactly these
joined with :data:RESPONSE_SEGMENT_SEPARATOR, and segments[-1] is
the text that followed the last tool call — the answer, for a consumer
that wants it without the narration before it. Empty when the turn
produced no text.
usage_metadata
class-attribute
instance-attribute
¶
Optional provenance and scope of the provider's usage observations.
ACP includes the native session, prompt source and session usage report. These are observations, not a price or a claim that generation succeeded. Existing providers and consumers can leave this empty.
declared_tools
class-attribute
instance-attribute
¶
The tools the provider received this turn, over every generation round.
BEFORE_AI_GENERATION sees the turn's toolset once, as the turn starts:
under Tool Search the whole catalogue, not what any round declares. A tool
find_tools reveals only enters the declaration of the next round, so
a host recording "what the model was offered" reads it here. This is the union of
the toolsets handed to the provider on each round of the turn, in first
declaration order, one entry per name: a tool declared on several rounds
keeps its first entry, and since the reveal window slides from one
find_tools to the next, the union is the only reading that keeps a
tool revealed earlier in the turn. A turn without Tool Search reports its
one declaration here too, every entry "always": one reading for the
host, whatever the turn's mode.
Empty when the turn declared no tool, and for a channel whose toolset is not RoomKit's to declare (an external ACP agent).
DeclaredTool
dataclass
¶
One tool as the provider received it, and why it was there.
The record :attr:AIResponseEvent.declared_tools is made of. name,
description and parameters are the AITool fields RoomKit handed
the provider: its declaration, not the provider's wire form of it. The
schema is that object, not a copy, so a consumer reads it and never
writes it.
from_tool
classmethod
¶
The record for tool, declared for the reason origin says.
Task ¶
Bases: BaseModel
A task assigned within a room.
Observation ¶
Bases: BaseModel
An observation produced by an intelligence channel.