Metadata-Version: 2.4
Name: tai42-agents
Version: 16.0.3
Summary: Ready-made agents for the TAI ecosystem — coding/deep agents, tool-calling, retrieval, voting, refinement, and vision agents — opt-in and manifest-loaded.
Author-email: tai42 <oss@tai42.ai>
License-Expression: Apache-2.0
Project-URL: Homepage, https://tai42.ai
Project-URL: Repository, https://github.com/tai42ai/tai42/tree/main/plugins/agents
Project-URL: Issues, https://github.com/tai42ai/tai42/issues
Project-URL: Changelog, https://github.com/tai42ai/tai42/blob/main/plugins/agents/CHANGELOG.md
Keywords: tai,agents,deepagents,langgraph,streaming,llm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: tai42-contract<30,>=29.1
Requires-Dist: tai42-kit<20,>=19.0
Requires-Dist: deepagents>=0.7.5
Requires-Dist: langgraph>=1.2.12
Requires-Dist: langchain-core>=1.2.25
Requires-Dist: langchain>=1.3.11
Requires-Dist: pydantic>=2
Requires-Dist: pydantic-settings>=2.15.0
Requires-Dist: wcmatch>=11.0.1
Requires-Dist: anyio>=4.14
Provides-Extra: park
Requires-Dist: redis>=5; extra == "park"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov>=5; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: pyright>=1.1.389; extra == "dev"
Requires-Dist: fakeredis[lua]>=2.21; extra == "dev"
Requires-Dist: pyyaml>=6; extra == "dev"
Requires-Dist: opentelemetry-api>=1.45.1; extra == "dev"
Requires-Dist: tai42-kit[monitoring]<20,>=19.0; extra == "dev"
Requires-Dist: tai42-skeleton<38,>=37.0; extra == "dev"
Dynamic: license-file

# tai42-agents

[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

The reference agents package for the TAI ecosystem — an opt-in, manifest-loaded
collection of generic **agents** built on the deepagents/LangGraph runtime.

Every agent here registers through the `tai42_app` handle from `tai42_contract.app`
and is loaded by the host from the manifest (`agents[].module`). Its only tai-*
dependencies are `tai42-contract` (the `Agent` ABC, the `StreamEvent` taxonomy,
and the `tai42_app` handle it registers through) and `tai42-kit` (settings
machinery and the llm factories model access goes through). It **never**
imports the skeleton — tai42-agents is contract-facing.

## The TAI ecosystem

TAI is an open-source runtime for MCP tools, agents, and workflows. An agent is a
capability the runtime hosts and exposes as a tool; the six here are the
platform's ready-made, batteries-included set. This repo is their per-agent
reference doc home; the documentation site covers using them and the
platform-level story:

- Use the ready-made agents (guide): https://docs.tai42.ai/guides/use-the-ready-made-agents
- Agents concept: https://docs.tai42.ai/concepts/agents
- Plugin catalog: https://docs.tai42.ai/plugins

## Install

Requires **Python 3.13+**. Install from PyPI into the environment that runs the
server:

```bash
uv add tai42-agents
```

Or from source — clone this repo and add it as an editable dependency; the
`tai42-*` dependencies resolve in-tree from the workspace.

```bash
git clone https://github.com/tai42ai/tai42   # next to your app checkout
cd /path/to/your/app
uv add --editable ../tai42/plugins/agents
```

The agent runtime (`deepagents`, `langgraph`, `langchain-core`, `langchain`,
`pydantic`, `pydantic-settings`, `opentelemetry-api`, `wcmatch`) is a
base dependency — agents are this package's purpose, so there is no runtime extra
to opt into. Model-provider SDKs are **never** direct dependencies here: model
access goes through tai42-kit's llm factories, configured per deployment.

## Registering an agent

An agent is a class subclassing the contract `Agent` ABC, registered under a
name with the `@tai42_app.agents.agent(name)` decorator. Registration fires when
the module imports (import-to-register); the host imports the module because
the manifest names it:

```yaml
agents:
  - title: my-agents
    module: tai42_agents.<module>
    # include: [<agent name>]   # optional — omit to expose all agents in the module
```

```python
from pydantic import BaseModel
from tai42_contract.agent import Agent
from tai42_contract.app import tai42_app


class EchoInput(BaseModel):
    user_message: str = ""


@tai42_app.agents.agent("echo")
class EchoAgent(Agent):
    tool_name = "echo"
    tool_description = "Echoes the user message back."
    ToolInput = EchoInput  # a JSON-able pydantic model of the tool params

    async def run(self, *, user_message: str = "", **kwargs):
        return user_message
```

Registration gives each agent two faces, both derived from the one class:

- an in-process `astream` method (API / SSE facing) that yields the contract's
  `StreamEvent` taxonomy (`ReasoningStep`, `ToolCallStep`/`ToolResultStep`,
  `MessageDelta`, `MessageFinal`, `RunUsage`, `StructuredFinal`,
  `InterruptFinal`);
- an auto-generated JSON `run` tool (LLM / MCP / programmatic callers facing) whose
  signature is the agent's `ToolInput` model.

## Agents

The package ships six agents, each in its own module so a manifest can load
exactly the ones a deployment wants:

- **`tools_agent`** (`tai42_agents.tools_agent`) — the plain/advanced LangGraph
  tools agent. Uniform tool inputs: `tool_names` (client tools resolved through
  the app registry), live `tools`, and `presets` (a base tool bound to fixed
  kwargs, e.g. `base_tool="example_tool"` with `fixed_kwargs={"config": ...}`).
- **`langchain_deep_agent`** (`tai42_agents.langchain_deep_agent`) — a deepagents-harness agent:
  planning, a per-thread scratch filesystem, skills (served live from the
  template provider or supplied inline), one level of nested subagents, and
  human-in-the-loop interrupts with resume via a LangGraph `Command`. Both faces
  fail loudly when a requested `response_format` produces no structured output:
  the invoke face raises on drain, and the stream face raises after the stream
  drains (a pending interrupt takes precedence over the raise).
- **`retrieval_tools_agent`** (`tai42_agents.retrieval_tools_agent`) — a tools
  agent that does not bind every tool to the model up front: it embeds each
  tool's description into a vector store and exposes a `retrieve_tools`
  semantic-search tool, binding matches on demand until the model emits a
  terminal `{"status": ...}` object. Useful when the tool set is large.
- **`voting_agent`** (`tai42_agents.voting_agent`) — runs N voter LLMs in parallel
  over one prompt, then a judge LLM decides by majority vote (breaking ties with
  its own reasoning). Returns a `VotingOutput`; only the judge streams.
- **`refine_agent`** (`tai42_agents.refine_agent`) — an Evaluator↔Critic loop: the
  evaluator drafts, the critic reviews, and they alternate until the critic's whole
  reply, surrounding whitespace aside, is the approval token (a reply that only
  contains it is not an approval) or the iteration budget is exhausted (a loud
  `RuntimeError`, never an unapproved draft). Only the final approved pass streams.
- **`vqa_agent`** (`tai42_agents.vqa_agent`) — visual question answering: a single
  multimodal completion over an `image_url` and a `query`. No tools, no graph.

**Expose kwargs-carrying agents to trusted callers only.** `base_url`/`api_key`
in `llm_kwargs`/`embedding_kwargs` legitimately route to a caller-chosen
model/embedding endpoint; expose any agent or tool carrying these kwargs only to
trusted callers — an injected parent agent could redirect the model/embedding
call to a hostile endpoint and leak the key/context.

Wire the ones you want in the manifest — one `agents:` entry per module, each
with a `title`; add `include:` to expose a subset of a module's agents:

```yaml
agents:
  - title: tools-agent
    module: tai42_agents.tools_agent
  - title: deep-agent
    module: tai42_agents.langchain_deep_agent
  - title: retrieval-tools-agent
    module: tai42_agents.retrieval_tools_agent
  - title: voting-agent
    module: tai42_agents.voting_agent
  - title: refine-agent
    module: tai42_agents.refine_agent
  - title: vqa-agent
    module: tai42_agents.vqa_agent
```

## Import rule

The shipped `tai42_agents` package imports `tai42-contract`, `tai42-kit`, and the
agent runtime (`deepagents` / `langgraph` / `langchain-core` / `langchain` /
`pydantic` / `opentelemetry` / `wcmatch`) — the declared
dependencies **and their resolved dependency closure** — plus the standard
library. It never imports `tai42-skeleton`, which sits a layer above, and never
reaches for a package that is not a dependency of the shipped wheel. The rule is
enforced twice: ruff (`flake8-tidy-imports` bans) fails lint on a skeleton
import, and an import-graph test asserts every root in the module graph is on
the allowlist — once by importing every shipped module in a fresh subprocess and
inspecting `sys.modules`, and once by parsing every shipped source file, so an
import nested in a function body, a class body, or a `TYPE_CHECKING` block is
caught too.

## Development

```bash
uv venv --python 3.13
uv pip install --no-sources --editable ".[dev]"
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .
uv run --no-sync pyright
uv run --no-sync pytest --cov --cov-report=term-missing
```

**Dependency install is plain.** The deepagents/LangGraph stack (`deepagents`,
`langgraph`, `langchain-core`, `langchain`, `pydantic`, `opentelemetry-api`)
resolves as ordinary wheels from the index — no vendor directory and
no `PYTHONPATH` bridge.

## License

Apache-2.0. See `LICENSE` and `NOTICE`.
