Skip to main content

Zett Agent

Zett Agent

Small core. Yours to shape.

Documentation · Quickstart · Examples

PyPI Python License: MIT CI Docs

Zett Agent is a small Python runtime for tool-calling agents. One provider-neutral loop calls a model, runs the tools it asks for, and streams every step back to your application. Start with a model and a few functions. Add skills, session storage, approvals, or delegation without adopting an application framework.

  • Provider-neutral. OpenAI, Anthropic, Google, DeepSeek, and Ollama behind one protocol.
  • Tools from functions. Annotate a Python function; the schema, validation, and dispatch are generated.
  • Capabilities on demand. Load skill instructions when needed; discover deferred tools on supported Responses endpoints.
  • Streaming first. Partial text, reasoning, tool calls, and results for terminals and UIs.
  • Durable when you want it. In-memory history by default, SQLite or your own store on request.
  • Your application stays yours. Embed the loop in a script, terminal, UI, or service; add lifecycle behavior with extensions.

Read the documentation or start with the complete tool-calling example below. Python 3.10+, typed, MIT licensed.

Install

pip install zett-agent
uv add zett-agent

Quickstart

Set OPENAI_MODEL to a model that supports tool calling and OPENAI_API_KEY to your credential. This example makes a real network request and may incur charges. Save it as agent.py and run python agent.py:

import asyncio
import os

from zett_agent.client import create_agent
from zett_agent.providers.openai import OpenAIProvider
from zett_agent.tools.base import tool


@tool(guidelines="Use for exact integer addition.")
def add(left: int, right: int) -> int:
    """Add two integers exactly."""
    return left + right


async def main() -> None:
    model = OpenAIProvider(
        model=os.environ["OPENAI_MODEL"],
        api_key=os.environ["OPENAI_API_KEY"],
    )
    try:
        client = await create_agent(model, tools=[add])
        reply = await client.run("Use add to calculate 20 plus 22.")
        print(reply.content)
    finally:
        await model.aclose()


asyncio.run(main())

The model can request add; the runtime validates its arguments, runs it, returns the result, and asks the model to finish. reply.content contains the final answer. Calling client.run(...) again continues the same in-memory conversation. The exact answer and tool choice depend on the model.

No API key yet? Your first agent is a complete, deterministic tutorial with no network access, and Streaming tools exercises the same tool round trip offline.

Choose a provider

Swap the adapter without changing local tools, callbacks, or the client API. Use response=True on an OpenAI-compatible adapter only when your endpoint supports Responses; Chat Completions is the default.

Provider Module
OpenAI (Chat Completions or Responses) zett_agent.providers.openai
Anthropic zett_agent.providers.anthropic
Google GenAI zett_agent.providers.google
DeepSeek zett_agent.providers.deepseek
Ollama zett_agent.providers.ollama

See Connect a model provider for complete setup with each provider, reasoning effort, retries, proxies, and cache routing. Model features are endpoint-specific; a common adapter protocol does not guarantee identical capabilities.

Stream to a terminal or UI

Dispatch callbacks receive events as they arrive; run() still returns the final answer.

from zett_agent.dispatcher import AgentEventDispatcher
from zett_agent.events import AgentEvent


class Console(AgentEventDispatcher):
    async def on_text_delta_event(self, event: AgentEvent) -> None:
        print(event.delta, end="", flush=True)


client = await create_agent(model, event_dispatcher=Console())
await client.run("Summarize this repository.")

If you need cancellation or forwarding, consume the stream directly with async with aclosing(client.stream(...)) as events. See Streaming.

Tools

Decorate a typed function; Zett Agent builds the schema and turns the docstring into model-facing guidance.

from zett_agent.tools.base import tool


@tool
def add(left: int, right: int) -> int:
    """Add two integers exactly.

    Args:
        left: First operand.
        right: Second operand.

    Guidelines:
        - Use for exact integer addition.
    """
    return left + right

Register it with create_agent(model, tools=[add]). The model can then request the call, and the runtime validates arguments, runs it, appends the result, and asks the model to continue. Filesystem and shell tools live in FileSystemExtension and CodingExtension. See Define and register tools.

Each tool needs typed arguments, a description, and at least one guideline. Use @tool(guidelines="...") for a short function or a Guidelines section in its docstring. Guidelines help the model choose tools; they do not enforce permissions.

Keep and restore conversations

AgentRunConfig.session_id identifies a conversation. Add a persistence extension to survive restarts:

from zett_agent.agent import AgentRunConfig
from zett_agent.extensions.sqlite import SQLiteSessionExtension

config = AgentRunConfig(session_id="project-notes")
storage = SQLiteSessionExtension("sessions.sqlite")
try:
    client = await create_agent(model, config=config, extensions=[storage])
    reply = await client.run("Remember my preference for Python.")
finally:
    await storage.close()

Run this inside an async function with a configured model. Reopen the same database and session ID to continue after restart. Explicit extension lists replace default history and tool guidance; include ToolGuidelinesExtension() if you register tools. Close the provider separately when finished. See Keep and restore conversations.

Embed it safely

Your application owns credentials, session access, and tool permissions. File, shell, and MCP tools are not sandboxed. Prompts, AGENTS.md, and skills guide behavior but do not authorize actions.

Set application deadlines, close partially consumed streams, and serialize each conversation. Cancellation stops unfinished work; it does not undo already completed tool side effects. Follow Integrate an application for concurrency, errors, timeouts, and shutdown.

Built-in extensions

Pass the ones you need; extensions=[...] replaces the optional defaults.

Extension Adds
InMemoryMessageAccumulator Process-local conversation history (default).
SQLiteSessionExtension Durable raw history and compaction checkpoints.
JSONLExtension One self-contained audit record per request.
ToolGuidelinesExtension Tool snippets and usage rules in the prompt.
AgentsMdExtension Working-directory AGENTS.md instructions.
FileSystemExtension / CodingExtension Read, search, edit, and shell tools.
CompactionExtension Summarize older turns before the context limit.
AskUserExtension Let the model ask the UI a question and wait.
PlanModeExtension Model-proposed planning with user approval.
SubAgentExtension Delegate to configured child profiles.
SkillExtension Discover and load local SKILL.md instructions.
McpExtension Connect MCP servers as tools.
TodoWriteExtension Ordered, observable task progress.
GoalExtension Continue explicitly armed Goal Mode work with an evaluator.
ToolSearchExtension Load optional tools on demand instead of up front.

Use an extension when behavior belongs inside the request lifecycle; use an AgentEventDispatcher for display-only callbacks. You never subclass Agent or write a second loop. See Extend the runtime.

Documentation

Get started Install, first agent, providers, streaming, tools, sessions.
Configure your agent Defaults, per-request settings, metadata, and extension composition.
Integrate an application Timeouts, cancellation, concurrent sessions, and resource ownership.
Test your agent Deterministic models and offline provider tests.
Capabilities on demand Editable skills, deferred tools, and endpoint compatibility.
How it works Ownership, lifecycle, context, events.
Extend the runtime Extensions, hooks, middleware, model and storage adapters.
Examples Runnable programs for streaming, approvals, compaction, delegation.
API reference Every public class, function, and field.

Requirements

  • Python 3.10 or newer (CI covers 3.10 through 3.14).
  • No Node.js or external service is required by the library itself.

Development

uv sync
uv run pytest
uv run ruff check src tests examples
make docs-live         # preview the manual with live reload at http://127.0.0.1:8000
make docs-serve        # build once, then serve the static HTML
make check             # lint, tests with coverage, and documentation checks

make docs-live rebuilds on every edit under docs/ or src/, and reloads the open browser page. Pass DOCS_PORT=8080 to change the port and DOCS_OPEN= to skip opening a browser. make docs builds once without serving.

The repository layout:

  • src/zett_agent/ — the runtime published as zett-agent.
  • docs/ — the Sphinx manual; make docs-serve previews it locally.
  • examples/ — runnable programs, most of which need no API key.

ZettCode, the terminal coding agent built on this runtime, lives in its own repository: https://github.com/Chang-LeHung/zettcode.

License

MIT

Metadata

Release files for zett-agent 0.1.15

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zett-agent 0.1.15
File Size Uploaded
zett_agent-0.1.15.tar.gz 166.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zett-agent 0.1.15
File Interpreter ABI Platform
zett_agent-0.1.15-py3-none-any.whl Python 3 none any Details

Total release size: 374.0 kB

Release files / zett_agent-0.1.15.tar.gz

Download URL zett_agent-0.1.15.tar.gz
Size 166.6 kB
Tags Source
SHA-256 checksum
How to use checksums
eb3fe00b32b65a433f19506d34290185c41068bfe8acedecf6a0877d208aef5a
BLAKE2b-256 checksum
How to use checksums
4d53a77aad803cbdb2f56be9ca1266d64830f20476ccdad6a0c9ce52fb1d0842
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / zett_agent-0.1.15-py3-none-any.whl

Download URL zett_agent-0.1.15-py3-none-any.whl
Size 207.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a20f4c3bb61a67d9a431753bb7bb748d15ef16a835122bd832efa361aa0acf4c
BLAKE2b-256 checksum
How to use checksums
f4a3f7eb80d245f8d7189c98e1d3f64de8f2543c94a17d6342d90e609dbeb367
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.1.17

2 release files

0.1.16

2 release files

This release

0.1.15 This release

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page