Skip to main content

A lightweight async framework for structuring and running AI agents in Python

Project description

pygents

A lightweight async framework for structuring and running AI agents in Python. Define tools, queue turns, stream results.

Install

pip install pygents

Requires Python 3.12+.

Example

import asyncio
from pygents import Agent, Turn, tool

@tool()
async def greet(name: str) -> str:
    return f"Hello, {name}!"

async def main():
    agent = Agent("greeter", "Greets people", [greet])
    # Use kwargs:
    await agent.put(Turn("greet", kwargs={"name": "World"}))
    # Or positional args:
    await agent.put(Turn("greet", args=["World"]))

    async for turn, value in agent.run():
        print(value)  # "Hello, World!"

asyncio.run(main())

Tools are async functions. Turns say which tool to run and with what args. Agents process a queue of turns and stream results. The loop exits when the queue is empty.

Features

  • Streaming — agents yield (turn, value) as results are produced
  • Inter-agent messaging — agents can send turns to each other
  • Dynamic arguments — callable positional args and kwargs evaluated at runtime
  • Timeouts — per-turn, default 60s
  • Per-tool locking — opt-in serialization for shared state (lock is acquired inside the tool wrapper, so turn-level hooks run outside the tool lock)
  • Fixed kwargs — decorator kwargs (e.g. @tool(permission="admin")) are merged into every invocation; call-time kwargs override
  • Hooks@hook(hook_type, lock=..., **fixed_kwargs) decorator; hooks stored as a list and selected by type; turn, agent, tool, and memory hooks; same fixed_kwargs and lock options as tools
  • Serializationto_dict() / from_dict() for turns and agents

Design decisions

  • Turn identity: Turn instances no longer have a built-in uuid. If you need identifiers, store them yourself in metadata or wrap Turn in a higher-level domain object.

  • Turn arguments: Turn.__init__ takes args before kwargs, and metadata is the final parameter:

    Turn(
        "tool_name",
        args=[...],
        kwargs={...},
        timeout=...,
        metadata={...},
        hooks=[...],
    )
    

    This keeps positional arguments explicit while reserving metadata purely for user-level annotations. start_time, end_time, stop_reason, and output are set by the framework during execution—they are not constructor parameters.

  • Agent serialization and current turn: Agent.to_dict() includes a current_turn key when the agent is in the middle of run() (the turn being executed). That turn is already off the queue, so without it a snapshot would lose the in-flight work. from_dict() restores it; the next run() consumes the restored current turn first, then the queue. So you can save agent state at any time and get a faithful snapshot (config, queue, and current turn if any).

  • Tool and hook metadata timing: ToolMetadata and HookMetadata include start_time and end_time (datetime | None). They are set on the metadata instance when the tool or hook runs (start at entry, end in a finally). So the same metadata object is updated each run; dict() includes ISO strings for serialization.

  • Hook protocol: Registered hooks conform to a Hook protocol (like Tool), with metadata (HookMetadata: name, description, and run timing), hook_type, fn, and lock. The decorator sets metadata (from __name__ and __doc__), fn, and lock; HookRegistry.register() sets hook_type. Raw async callables are typed as Callable[..., Awaitable[None]].

  • Hook decorator: @hook(hook_type, lock=False, **fixed_kwargs) mirrors the tool decorator: keyword arguments are merged into every invocation (call-time overrides), and lock=True uses an asyncio lock to serialize hook invocations. Pass a list of types (e.g. @hook([TurnHook.BEFORE_RUN, AgentHook.AFTER_TURN])) to reuse one hook for several events; multi-type hooks must accept *args, **kwargs since different types receive different arguments.

  • Tool lock in tool layer: The tool lock is acquired inside the @tool wrapper, not in the turn. So the lock covers only the tool’s own execution (including its BEFORE_INVOKE / ON_YIELD / AFTER_INVOKE hooks). Turn-level hooks (BEFORE_RUN, AFTER_RUN, ON_TIMEOUT, ON_ERROR) run outside the tool lock; hooks that need serialization use their own lock=True.

  • ContextQueue hooks: ContextQueue has no compact callback. It supports ContextQueueHook.BEFORE_APPEND and ContextQueueHook.AFTER_APPEND only. Hooks are stored as list[Hook] (like Agent/Turn), filtered by type when running. BEFORE_APPEND and AFTER_APPEND hooks receive (items,) — the current items as a list (read-only). Serialization uses the same by-type-by-name shape as Agent/Turn; from_dict() resolves names from HookRegistry.

Docs

Full documentation: uv run mkdocs serve. MkDocs is an optional dependency—install with pip install -e ".[docs]" (or use uv run as above) so the library itself does not depend on it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygents-0.4.2.tar.gz (17.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pygents-0.4.2-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file pygents-0.4.2.tar.gz.

File metadata

  • Download URL: pygents-0.4.2.tar.gz
  • Upload date:
  • Size: 17.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for pygents-0.4.2.tar.gz
Algorithm Hash digest
SHA256 edce2ded9fbdf10726b974fd5d1f7127293cc91c9fe9f65341a14e9733b7bcc8
MD5 661344b0783e93803636ff8b1091f0a9
BLAKE2b-256 dec1133effcf67a00204d2c306decb24dba05876341c2f52793a7ed368916504

See more details on using hashes here.

File details

Details for the file pygents-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: pygents-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 20.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for pygents-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 18e714ed2d5b0082cd360d07c070fa11148acdba88a132b5f2361c9d161eab06
MD5 a2e731cc01bafafd1f5ae8bdd1ce4b53
BLAKE2b-256 3f97ed2869cabb00a24e56a7bcc7ef46c1c185cab60181add12b31b57823842f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page