CompactionExtension#

Build extensions, choose hooks, and manage request-scoped state. See the guide.

class zett_agent.extensions.compaction.CompactionExtension(model=None, *, max_tokens=128000, keep_recent_tokens=32000, count_tokens=None, reasoning_effort=ReasoningEffort.LOW)[source]#

Summarize older dialogue before a model step when context exceeds a limit.

max_tokens measures provider-visible message content, including tool calls and a bounded estimate for each image. The default o200k_base tokenizer estimates text; inject count_tokens for provider-specific accounting. keep_recent_tokens is a minimum: retain the whole user turn containing the cutoff, including tool calls and results. The current turn is never split. A single oversized turn therefore cannot be compacted by this extension.

Parameters:
  • model (AgentModel | None) – Model used to summarize older dialogue. None reuses the current request model, which is useful for a shared multi-session Agent.

  • max_tokens (int) – Estimated context threshold that triggers compaction.

  • keep_recent_tokens (int) – Minimum recent token budget, extended to whole turns.

  • count_tokens (Callable[[Sequence[AnyMessage]], int] | None) – Optional provider-specific message token counter.

  • reasoning_effort (ReasoningEffort) – Reasoning level used by the summary model.

Visible state transitions:

+-------------------------+
| PRIMARY READY           |
+-------------------------+
             |
             v
+-------------------------+            +--------------------------+
| THRESHOLD CROSSED?      |-- yes ---->| COMPACTING               |
+-------------------------+            | started                  |
             |                         | reasoning/text delta     |
             |                         | completed                |
             |                         +--------------------------+
             | no                                   |
             |                                      |
             |                                      |
             v                                      |
+-------------------------+                         |
| PRIMARY MODEL           |<------------------------+
+-------------------------+

If the threshold is not crossed, no compaction event is emitted and the request moves directly from primary-ready to the primary model.

Examples

Usage:

agent = await Agent.create(model, config=config, extensions=[
    InMemoryMessageAccumulator(),
    ToolGuidelinesExtension(),
    CompactionExtension(model, max_tokens=128_000, keep_recent_tokens=32_000),
])

Only the active message list is changed. Pair this extension with SessionPersistenceExtension when durable raw history and snapshots are needed.

async compact(context, *, force=False)[source]#

Summarize older dialogue and replace it with one checkpoint.

Compaction runs when the provider-visible token estimate exceeds max_tokens, or unconditionally when force is set — the on-demand entry points ask for a pass, and a reader who asks should not have to wait for a budget to fill. keep_recent_tokens is a minimum: the whole user turn containing the cutoff is retained, and the current turn is never split, so a single oversized turn is left alone. When the summary is not smaller than the context it replaces, context is left unchanged.

Parameters:
  • context (AgentRunContext) – Request-scoped state whose dialogue is summarized in place.

  • force (bool) – Summarize whatever the context size is; automatic compaction leaves this False so only the threshold triggers it.

Returns:

The published CompactionEvent when older context was replaced, otherwise None.

Raises:

AgentProtocolError – If compaction is triggered without a usable model or the summary response is empty or contains tool calls.

Return type:

CompactionEvent | None

async before_model(context, request)[source]#

Compact older dialogue through compact() when over budget.

The incoming request describes the primary call. The summarizer uses its own request and reasoning effort; context changes are reflected in the primary request when the runtime rebuilds it after preprocessing.

Parameters:
Return type:

None

async on_compact(context)[source]#

Run one compaction pass for a caller that asked for it.

Reached through Agent.compact, where there is no primary request to prepare for: the pass is the whole job, so it runs whatever the context size is. Automatic compaction is untouched — before_model calls compact() without force, so the threshold still decides there.

Parameters:

context (AgentRunContext)

Return type:

None

async after_model(context, response)[source]#

Remember exact provider input plus output as the next-step baseline.

Parameters:
Return type:

None

accept(config, event)#

Handle one external event and report whether it was accepted.

The Agent broadcasts each event to every registered extension. Override this synchronous hook when an extension waits for input from another thread, HTTP request, TUI, or Web UI. The default ignores the event. Config is passed separately and can be None before initialization. Each extension decides whether the supplied identity and payload match its protocol. Return True to include this extension’s name in the broadcast result; returning False does not stop later receivers.

Parameters:
Return type:

bool

async after_run(context, result)#

Run after a successful final answer is appended.

Parameters:
Return type:

None

async after_tool(context, call, result, error)#

Inspect or modify a tool result before it is appended.

error is the original execution exception for a failed result and None after successful execution. Changes made to result are included in context, persistence, and the next model request. Raising prevents the result from being appended.

Parameters:
Return type:

None

async after_turn(context, result)#

Run once the turn’s assistant message and tool results are settled.

result is the assistant message that opened the turn: either the final answer, or the tool-calling message whose ToolMessages were appended. Every requested call is settled first, including calls that failed, and calls that steering skipped, which arrive as unsuccessful or skipped ToolMessages.

Steering that interrupts a turn is accepted while the runtime settles that batch, so its UserMessage is already appended when this hook runs and the steering turn follows as the next turn.

A turn that never completed, because the request failed or was cancelled, has no after_turn call; use on_error and RunCancelledEvent for those paths.

Parameters:
Return type:

None

async before_run(context)#

Run after the transformed user input is appended and published.

Parameters:

context (AgentRunContext)

Return type:

None

async before_tool(context, call)#

Run before one requested tool is invoked.

Parameters:
Return type:

None

async before_turn(context)#

Run before this turn’s model call.

Runs after before_run() on the first turn and after the previous turn’s after_turn() on later ones. Model request assembly, compaction, and before_model() follow this hook, so context edited here reaches the provider.

Parameters:

context (AgentRunContext)

Return type:

None

property name: str#

Unique registration identity, defaulting to the concrete class name.

Override with a class attribute or assign an instance name to register independently configured instances of the same extension type.

async on_error(context, error)#

Run before an agent error is propagated to the caller.

Parameters:
Return type:

None

async on_event(context, event)#

Process a published notification; inspect its concrete type with match.

Parameters:
Return type:

None

async on_message(context)#

Transform context.input_message after state restoration, before append.

Replace the pending UserMessage to implement externally selected modes. Do not append or persist it here: the runtime appends the final message exactly once after all input hooks finish. before_run sees that message through AgentRunContext.add_message() or AgentRunContext.replace_messages(), and persistence subscribers have already received it.

Parameters:

context (AgentRunContext)

Return type:

None

async on_model_request(context, request, call_next)#

Wrap one provider request and its complete streamed response.

Pass request (or a dataclass replacement) to call_next. The default closes the inner stream so cancellation reaches the provider.

Parameters:
Return type:

AsyncIterator[ModelEvent]

async on_state(context)#

Restore history and initialize state before any input conversion.

Examples

Usage:

async def on_state(self, context):
    context.add_message(SystemMessage(content="Use concise answers."), index=0)
Parameters:

context (AgentRunContext)

Return type:

None

async on_success(context, result)#

Run once after all after_run hooks succeed, before RUN_COMPLETED.

This callback observes a completed request, not an intermediate model step. Failed or cancelled requests do not trigger it. Callback errors propagate through on_error and prevent RUN_COMPLETED from being emitted.

Examples

Usage:

async def on_success(self, context, result):
    await save_answer(context.session_id, result.content)
Parameters:
Return type:

None

async on_tool(context)#

Register local or provider-hosted tools before context restoration.

All extensions finish this hook before the first on_state() call, so prompt extensions can reliably inspect context.tools and context.server_tools as complete request-scoped registries.

Examples

Usage:

async def on_tool(self, context):
    context.register_tool(read_file)
    context.register_server_tool(ServerToolDefinition(type="web_search"))
Parameters:

context (AgentRunContext)

Return type:

None

async on_tool_call(context, call, call_next)#

Wrap one registered local tool handler and return its ToolResult.

An extension may adjust call.arguments, return without calling the next layer, transform its result, or raise into normal tool-failure handling.

Parameters:
Return type:

ToolResult

property pending: bool#

Return whether this extension is participating in an active request.

The Agent updates this state for every registered extension, including extensions whose hooks are currently idle. One extension instance may be shared by multiple Agents and sessions; pending remains true until all requests using that instance have finished, failed, or been cancelled. Persistent data retained between requests does not count.