Metadata-Version: 2.5
Name: agentbyte
Version: 0.24.2
Summary: A toolkit for designing multiagent systems
Author-email: MrDataPsycho <mr.data.psycho@gmail.com>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28.1
Requires-Dist: pydantic-settings>=2.13.0
Requires-Dist: pydantic>=2.12.5
Requires-Dist: pyyaml>=6.0.3
Provides-Extra: all
Requires-Dist: aioboto3>=15.5.0; extra == 'all'
Requires-Dist: aiosqlite>=0.22.1; extra == 'all'
Requires-Dist: arxiv>=2.1; extra == 'all'
Requires-Dist: asyncpg>=0.31.0; extra == 'all'
Requires-Dist: azure-identity>=1.25.1; extra == 'all'
Requires-Dist: beautifulsoup4>=4.12; extra == 'all'
Requires-Dist: fastapi>=0.135.2; extra == 'all'
Requires-Dist: graphviz>=0.21; extra == 'all'
Requires-Dist: html2text>=2024.2; extra == 'all'
Requires-Dist: openai>=1.107.1; extra == 'all'
Requires-Dist: opentelemetry-api>=1.39.1; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.39.1; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.39.1; extra == 'all'
Requires-Dist: sqlalchemy>=2.0.43; extra == 'all'
Requires-Dist: sqlmodel>=0.0.38; extra == 'all'
Requires-Dist: uvicorn>=0.42.0; extra == 'all'
Requires-Dist: youtube-transcript-api>=0.6; extra == 'all'
Provides-Extra: aws
Requires-Dist: aioboto3>=15.5.0; extra == 'aws'
Provides-Extra: azureopenai
Requires-Dist: azure-identity>=1.25.1; extra == 'azureopenai'
Requires-Dist: openai>=1.107.1; extra == 'azureopenai'
Provides-Extra: coding
Provides-Extra: dataset
Requires-Dist: aiosqlite>=0.22.1; extra == 'dataset'
Provides-Extra: dev
Requires-Dist: ipykernel>=7.1.0; extra == 'dev'
Requires-Dist: nbclient>=0.10.4; extra == 'dev'
Requires-Dist: nbformat>=5.10.4; extra == 'dev'
Requires-Dist: picoagents>=0.4.0; extra == 'dev'
Requires-Dist: pydantic-settings>=2.13.0; extra == 'dev'
Requires-Dist: pymupdf-layout>=1.26.6; extra == 'dev'
Requires-Dist: pymupdf4llm>=0.2.9; extra == 'dev'
Provides-Extra: openai
Requires-Dist: openai>=1.107.1; extra == 'openai'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.39.1; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.39.1; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.39.1; extra == 'otel'
Provides-Extra: research
Requires-Dist: arxiv>=2.1; extra == 'research'
Requires-Dist: beautifulsoup4>=4.12; extra == 'research'
Requires-Dist: html2text>=2024.2; extra == 'research'
Requires-Dist: youtube-transcript-api>=0.6; extra == 'research'
Provides-Extra: sql
Requires-Dist: aiosqlite>=0.22.1; extra == 'sql'
Requires-Dist: asyncpg>=0.31.0; extra == 'sql'
Requires-Dist: sqlalchemy>=2.0.43; extra == 'sql'
Requires-Dist: sqlmodel>=0.0.38; extra == 'sql'
Provides-Extra: test
Requires-Dist: aiosqlite>=0.22.1; extra == 'test'
Requires-Dist: arxiv>=2.1; extra == 'test'
Requires-Dist: beautifulsoup4>=4.12; extra == 'test'
Requires-Dist: greenlet>=3.0.0; extra == 'test'
Requires-Dist: html2text>=2024.2; extra == 'test'
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'test'
Requires-Dist: pytest-cov>=7.0.0; extra == 'test'
Requires-Dist: pytest>=9.0.1; extra == 'test'
Requires-Dist: ruff>=0.15.0; extra == 'test'
Requires-Dist: sqlmodel>=0.0.38; extra == 'test'
Requires-Dist: youtube-transcript-api>=0.6; extra == 'test'
Provides-Extra: viz
Requires-Dist: graphviz>=0.21; extra == 'viz'
Provides-Extra: webui
Requires-Dist: fastapi>=0.135.2; extra == 'webui'
Requires-Dist: uvicorn[standard]>=0.44.0; extra == 'webui'
Description-Content-Type: text/markdown

# Agentbyte

Agentbyte is an observability-first agentic AI framework for building and studying multiagent systems with a learning-first, implementation-oriented workflow.

Current release: **0.24.2**

## Building an Agent

Every example below builds on the same domain: a customer **support agent**. Start with a plain agent and a model client — no tools, no middleware:

```python
from agentbyte import Agent
from agentbyte.llm import OpenAIChatCompletionClient

model_client = OpenAIChatCompletionClient.from_api_key(model="gpt-4.1-mini")

support_agent = Agent(
    name="support_agent",
    description="Answers customer support questions about orders and shipping.",
    instructions="You are a helpful support agent. Be concise and accurate.",
    model_client=model_client,
)

response = await support_agent.run("Where is my order #1234?")

print(response.final_message.content)
print(response.usage)          # tokens, cost, cache hits
print(response.finish_reason)  # "stop" | "max_iterations" | ...
```

`run()` executes to completion and returns one `AgentResponse`. For live progress — token-by-token streaming, tool calls as they happen — use `run_stream()` instead, which yields events and finishes with that same `AgentResponse`:

```python
async for item in support_agent.run_stream("Where is my order #1234?", verbose=True):
    print(item)
```

## Adding Tools

A support agent is only useful once it can look things up. Turn any function into a tool with `@tool`; gate risky ones with `approval_mode`:

```python
from agentbyte import Agent
from agentbyte.tools import ApprovalMode, tool

@tool
def get_order_status(order_id: str) -> str:
    """Look up the status of an order."""
    return f"Order {order_id} is in transit."

@tool(approval_mode=ApprovalMode.ALWAYS)
def issue_refund(order_id: str, amount: float) -> str:
    """Issue a refund for an order (requires human approval)."""
    return f"Refunded ${amount} for order {order_id}"

support_agent = Agent(
    name="support_agent",
    description="Answers order/shipping questions and can issue refunds.",
    instructions="Look up orders before answering. Refunds always need approval.",
    model_client=model_client,
    tools=[get_order_status, issue_refund],
)
```

Agentbyte also ships ready-made tools you can drop in without writing any code — pass them straight into `tools=[...]`:

- **Core:** `ThinkTool`, `TaskStatusTool`, `CalculatorTool`, `DateTimeTool`, `JSONParserTool`, `RegexTool` (all at once via `create_core_tools()`)
- **Coding:** `ReadFileTool`, `WriteFileTool`, `ListDirectoryTool`, `GrepSearchTool`, `BashExecuteTool`, `PythonREPLTool`
- **Memory:** `MemoryTool` — lets the agent read/write a memory backend (`ListMemory` or `FileMemory`) as a tool call, on top of the automatic context injection every agent already gets

## Agentic Workflows

When support handling is a fixed multi-step pipeline rather than a single agent call — triage, then route — model it as a `Workflow` instead:

```python
import asyncio
from pydantic import BaseModel
from agentbyte.workflow import FunctionStep, StepMetadata, Workflow, WorkflowConfig, WorkflowRunner

class TicketInput(BaseModel):
    text: str

class TriagedTicket(BaseModel):
    text: str
    priority: str

async def triage(input_data: TicketInput, context) -> TriagedTicket:
    priority = "high" if "urgent" in input_data.text.lower() else "normal"
    return TriagedTicket(text=input_data.text, priority=priority)

async def route(input_data: TriagedTicket, context) -> TriagedTicket:
    return input_data  # e.g. assign to a queue here

workflow = Workflow(WorkflowConfig(name="support_ticket_pipeline"))
workflow.chain(
    FunctionStep("triage", StepMetadata(name="triage"), TicketInput, TriagedTicket, triage),
    FunctionStep("route", StepMetadata(name="route"), TriagedTicket, TriagedTicket, route),
)

execution = asyncio.run(
    WorkflowRunner().run(workflow, {"text": "urgent: order not received"})
)
print(execution.state["route_output"])
```

Steps can also wrap an agent (`AgentStep`), call HTTP endpoints (`HttpStep`), transform data (`TransformStep`), or nest another workflow (`SubWorkflowStep`) — with conditional routing, parallel branches, checkpoint/resume, and human-in-the-loop suspend/resume.

## Orchestrator Patterns

Two different ways to combine multiple agents — pick based on who's in control:

- **`AgentAsTool`** — one agent decides *if and when* to delegate. The support agent stays the single decision-maker and calls the billing agent like any other tool.
- **An orchestrator** (e.g. `RoundRobinOrchestrator`) — a separate controller drives the conversation between agents in turns, until a termination condition fires. Neither agent decides when the other speaks.

**Agent as a tool** — the support agent delegates billing questions:

```python
from agentbyte import Agent
from agentbyte.agents import AgentAsTool

billing_agent = Agent(
    name="billing_agent",
    description="Handles billing and refund questions.",
    instructions="Answer billing questions and process refund requests.",
    model_client=model_client,
)

support_agent = Agent(
    name="support_agent",
    description="Front-line support agent that can delegate billing issues.",
    instructions="Handle general support; delegate billing questions to the billing tool.",
    model_client=model_client,
    tools=[AgentAsTool(agent=billing_agent)],
)
```

**Orchestrated turns** — support and escalation agents collaborate until the ticket is resolved:

```python
from agentbyte import (
    MaxMessageTermination,
    RoundRobinOrchestrator,
    TextMentionTermination,
    UserMessage,
)

orchestrator = RoundRobinOrchestrator(
    agents=[support_agent, escalation_agent],
    termination=TextMentionTermination("RESOLVED") | MaxMessageTermination(6),
)

task = UserMessage(content="Customer says their package never arrived.", source="user")
async for item in orchestrator.run_stream(task, verbose=True):
    print(item)
```

Other orchestrators follow the same `run()`/`run_stream()` shape: `AIOrchestrator` (a model picks the next speaker), `HandoffOrchestrator` (agents explicitly hand off control), `PlanBasedOrchestrator` (a plan is drafted, then executed step by step).

## Middleware

Built in: `LoggingMiddleware`, `PIIRedactionMiddleware`, `GuardrailMiddleware`, `MetricsMiddleware`, `RateLimitMiddleware`, `ApprovalMiddleware`, `ContextCompactionMiddleware`, `RetryMiddleware`, `OTelMiddleware`. Attach any combination to the same support agent via `middlewares=[...]`:

```python
from agentbyte import Agent
from agentbyte.middleware import ApprovalMiddleware, LoggingMiddleware, RateLimitMiddleware

support_agent = Agent(
    name="support_agent",
    description="Answers order/shipping questions and can issue refunds.",
    instructions="Look up orders before answering. Refunds always need approval.",
    model_client=model_client,
    tools=[get_order_status, issue_refund],
    middlewares=[
        LoggingMiddleware(),
        RateLimitMiddleware(max_requests=10, window_seconds=60),
        ApprovalMiddleware(tool_names=["issue_refund"]),
    ],
)
```

## Observability-First Telemetry

Agentbyte exposes two complementary telemetry layers via `OTelMiddleware`:

- **Per-call spans** (`chat <model>`, `tool <name>`, `embedding <model>`) for model/tool/embedding-level diagnostics.
- **Task-level root span** (`agent <name>`) wrapping every per-call span in one run, carrying the final aggregated usage and outcome.

Enable telemetry:

```bash
export AGENTBYTE_ENABLE_OTEL=true
```

Per-call span attributes emitted by `OTelMiddleware`:

- `gen_ai.system`, `gen_ai.operation.name`, `gen_ai.agent.name`, `gen_ai.session.id`
- `gen_ai.request.model`, `gen_ai.response.model`, `gen_ai.response.finish_reason`
- `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`, `gen_ai.usage.total_tokens`, `gen_ai.usage.cost_estimate_usd`
- `gen_ai.tool.name`, `gen_ai.tool.success`
- `gen_ai.embedding.input_count`, `gen_ai.embedding.output_count`
- `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.tool.parameters`, `gen_ai.tool.result` (opt-in content capture — only when `AGENTBYTE_OTEL_CAPTURE_CONTENT=true`, since these can carry PII)

Reading a trace: `chat gpt-4.1-mini` spans show **per-call** usage/cost/finish reason; the wrapping `agent <name>` span shows the **final accumulated** usage and task outcome for the whole `run()`/`run_stream()` call.

## Installation

Python requirement: **3.11+**

```bash
uv sync --all-groups
```

Optional extras:

```bash
uv sync --extra openai
uv sync --extra azureopenai
uv sync --extra otel
uv sync --extra webui
```

### Install in another project (pip / uv add)

Use extras to enable provider + telemetry support:

```bash
pip install "agentbyte[azureopenai,otel]"
```

```bash
uv add "agentbyte[azureopenai,otel]"
```

For the browser WebUI:

```bash
pip install "agentbyte[webui]"
# or
uv add "agentbyte[webui]"
```

Install all optional features:

```bash
pip install "agentbyte[all]"
# or
uv add "agentbyte[all]"
```

Note: the Azure extra is `azureopenai`.

## Run The WebUI

### Option 1: Run the preset-backed app

This is the easiest way to see the WebUI working end to end with real preset entities:
- preset agents
- preset orchestrators
- preset workflow

Step 1. Install the WebUI extra:

```bash
uv sync --extra webui
```

Step 2. Start the preset-backed app:

```bash
uv run python examples/webui/presets_webui.py
```

Step 3. Open the browser:

```text
http://127.0.0.1:8080
```

If auto-open is enabled in your environment, the browser may open automatically.

### Option 2: Run the WebUI against your current project directory

Use this when you want Agentbyte to scan a directory for exported `agent`, `workflow`, or `orchestrator` objects.

Important: discovery is convention-based. The scanned directory must contain Python modules that expose top-level variables literally named `agent`, `workflow`, or `orchestrator`. If you point `--dir` at a folder that does not export those names, the UI will load but show `No entities found`.

Step 1. Install the WebUI extra:

```bash
uv sync --extra webui
```

Step 2. Launch the WebUI and scan the current directory:

```bash
uv run agentbyte webui --dir .
```

Step 3. Open the browser:

```text
http://127.0.0.1:8080
```

Useful variants:

```bash
uv run agentbyte webui --dir . --port 8080 --host 127.0.0.1 --no-open
uv run agentbyte webui --dir examples --port 8090
```

For this repository, the most reliable first-run path is the preset-backed launcher:

```bash
uv run python examples/webui/presets_webui.py
```

Use `agentbyte webui --dir ...` when you have a directory of exportable demo modules, for example:

```python
# my_entities.py
agent = ...
workflow = ...
orchestrator = ...
```

### Option 3: Run it programmatically

Use this when you want to serve in-memory entities directly from Python.

```python
from agentbyte.webui import serve

serve(entities=[agent], port=8080, auto_open=True)
```

### Quick Troubleshooting

If the app does not start:

```bash
uv sync --extra webui
```

If port `8080` is already in use:

```bash
uv run agentbyte webui --dir . --port 8090
```

If you do not want the browser to open automatically:

```bash
uv run agentbyte webui --dir . --no-open
```

## Development

```bash
uv run ruff check src tests
uv run pytest tests -v
```
