# agentrspy

`agentrspy` is the Python port of the Rust project `agentrs`: an async-native SDK for building AI agents with a unified provider interface, composable tools, MCP integration, pluggable memory, multi-agent orchestration, and YAML-driven runtime loading.

The goal of this port is behavioral fidelity first and Python ergonomics second: keep the same core capabilities, preserve the tested runtime semantics, and expose them through idiomatic Python APIs with type hints.

## Highlights

- OpenAI, Azure OpenAI, Anthropic, Gemini, and Ollama providers
- Built-in tools for calculation, web fetch/search, filesystem access, shell execution, and inline Python execution
- MCP support for local stdio servers and remote Streamable HTTP endpoints
- In-memory, sliding-window, token-aware, vector, and optional Redis memory backends
- Single-agent loops for ReAct, single-pass reasoning, plan-and-execute, and custom instruction modes
- Multi-agent orchestration with sequential, parallel, supervisor, and graph routing
- YAML loading for single agents, orchestrators, and generic runtimes
- Examples, tests, and rewritten Python-first documentation

## Installation

```bash
pip install -e .
```

Optional extras:

```bash
pip install -e ".[redis]"
pip install -e ".[dev]"
pip install -e ".[full]"
```

## Quick Start

```python
from __future__ import annotations

import asyncio

from dotenv import load_dotenv

from agentrspy.prelude import Agent, CalculatorTool, LoopStrategy, OpenAiProvider


async def main() -> None:
    load_dotenv()

    llm = OpenAiProvider.from_env().build()

    agent = (
        Agent.builder()
        .llm(llm)
        .system("You are a concise assistant.")
        .tool(CalculatorTool())
        .loop_strategy(LoopStrategy.react(max_steps=4))
        .build()
    )

    output = await agent.run("What is 7 * (8 + 1)?")
    print(output.text)


if __name__ == "__main__":
    asyncio.run(main())
```

## Project Layout

```text
agentrspy/
├── agentrspy/                 # import-friendly wrapper package
├── src/agentrspy/core         # shared contracts, types, errors, streaming helpers
├── src/agentrspy/llm          # provider implementations
├── src/agentrspy/tools        # registry, built-ins, decorator-based tools
├── src/agentrspy/mcp          # MCP transport, protocol, adapters
├── src/agentrspy/memory       # memory backends
├── src/agentrspy/agents       # builders and execution loops
├── src/agentrspy/multi        # orchestrators, graph routing, event bus
├── src/agentrspy/config.py    # YAML runtime loading
├── examples/                  # runnable Python examples
├── tests/                     # pytest coverage for parity-critical behavior
└── docs/                      # rewritten Python documentation
```

## Core Semantics Preserved From Rust

- ReAct stores the assistant message before executing tool calls
- Tool failures are returned to the model as tool error outputs instead of raising immediately
- Sequential multi-agent routing feeds each agent the previous agent's `output.text`
- Parallel multi-agent routing merges outputs as `[agent_name]\n<text>` blocks separated by blank lines
- Agent streaming intentionally replays final whitespace-split tokens after completion to match the current Rust behavior
- YAML runtime loading accepts the same practical config shapes used by the Rust examples and integration tests

## Examples

- `examples/simple_agent.py`
- `examples/custom_tool.py`
- `examples/streaming.py`
- `examples/multi_agent.py`
- `examples/mcp_integration.py`
- `examples/yaml_single_agent.py`
- `examples/yaml_multi_agent.py`
- `examples/yaml_runtime.py`

Run one with:

```bash
python examples/simple_agent.py
python examples/multi_agent.py
python examples/yaml_runtime.py
```

Or use the module entrypoint:

```bash
python -m agentrspy --config examples/configs/single-agent.yaml --input "What is 21 * 2?"
```

## Environment Variables

See `.env.example` for a ready-to-copy template.

Common values:

- `OPENAI_API_KEY`
- `OPENAI_BASE_URL`
- `OPENAI_MODEL`
- `AZURE_OPENAI_ENDPOINT`
- `AZURE_OPENAI_KEY`
- `AZURE_OPENAI_MODEL`
- `ANTHROPIC_API_KEY`
- `GEMINI_API_KEY`
- `CONTEXT7_API_KEY`

## Documentation

- `docs/architecture.md`
- `docs/providers.md`
- `docs/tools.md`
- `docs/memory.md`
- `docs/multi-agent.md`
- `docs/yaml.md`
- `docs/migration-from-rust.md`
- `docs/testing.md`

## Testing

```bash
python -m pytest
python -m compileall src agentrspy tests examples
```

## Notes

- Python integers do not overflow like Rust integer types; where Rust relied on integer bounds, Python callers should validate explicitly.
- The Rust macro-based `#[tool]` workflow becomes a Python `@tool(...)` decorator.
- Rust feature flags become Python extras and import-time availability rather than compile-time gating.
- Anthropic and Gemini currently implement `complete()` but keep streaming intentionally unsupported, matching the Rust scope.

## License

MIT.
