Metadata-Version: 2.4
Name: scope-oaf
Version: 0.3.0
Summary: OpenAgentFramework — minimal, fast, transparent AI agent framework
Project-URL: Homepage, https://github.com/devincii-io/scope-oaf
Project-URL: Repository, https://github.com/devincii-io/scope-oaf
Project-URL: Issues, https://github.com/devincii-io/scope-oaf/issues
Author: devin
License-Expression: MIT
License-File: LICENSE
Keywords: agent,ai,anthropic,llm,openai,tool-calling
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: anthropic>=0.88
Requires-Dist: openai>=2.30
Requires-Dist: tiktoken>=0.7
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: python-dotenv>=1.0; extra == 'dev'
Provides-Extra: example
Requires-Dist: python-dotenv>=1.0; extra == 'example'
Requires-Dist: rich>=13.0; extra == 'example'
Provides-Extra: pdf
Requires-Dist: pymupdf>=1.24; extra == 'pdf'
Provides-Extra: web
Requires-Dist: aiohttp>=3.9; extra == 'web'
Requires-Dist: python-dotenv>=1.0; extra == 'web'
Description-Content-Type: text/markdown

<p align="center">
  <strong>OAF — OpenAgentFramework</strong><br>
  <em>Minimal, transparent AI agent framework for Python</em>
</p>

<p align="center">
  <a href="https://pypi.org/project/scope-oaf/"><img src="https://img.shields.io/pypi/v/scope-oaf?color=blue" alt="PyPI"></a>
  <a href="https://pypi.org/project/scope-oaf/"><img src="https://img.shields.io/pypi/pyversions/scope-oaf" alt="Python"></a>
  <a href="https://github.com/devincii-io/scope-oaf/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
  <a href="https://devincii-io.github.io/scope-oaf/"><img src="https://img.shields.io/badge/docs-mkdocs-blue" alt="Docs"></a>
</p>

---

Build AI agents that call tools, manage conversation context, and stream responses — with **zero magic**.
Every prompt, tool call, and LLM decision is fully inspectable. Works with **OpenAI** and **Anthropic** out of the box.

> **[Full Documentation →](https://devincii-io.github.io/scope-oaf/)**

## Why OAF?

| | OAF | Typical frameworks |
|---|---|---|
| **Abstraction** | Flat — one agent loop, one tool decorator | Deep chains, hidden prompt wrangling |
| **Debuggability** | Full prompt/response inspection via hooks | Opaque internal state |
| **Surface area** | ~25 top-level exports | Hundreds of classes |
| **Tool definition** | Decorate any `async` function | Special base classes, schemas, descriptors |
| **Context** | Single subclass point — you own the prompt | Scattered across prompt templates, chains, memory |
| **Providers** | OpenAI + Anthropic, same API | Often single-provider or heavy adapter layer |

## Install

```bash
pip install scope-oaf
```

Requires Python 3.11+. Core deps: `openai`, `anthropic`, `tiktoken`.

## Quick Start

```python
import asyncio
from oaf import Agent, ToolRegistry

registry = ToolRegistry()

@registry.register
async def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"Weather in {city}: 72°F, sunny"

agent = Agent(model="gpt-4.1-nano", tools=registry)
response = asyncio.run(agent.message("What's the weather in Tokyo?", role="user"))
print(response.text)
```

A full agent with tool calling in **10 lines**.

## What's Inside

### Tools

Decorate any `async` function — type hints become JSON Schema automatically. Supports `str`, `int`, `float`, `bool`, `list[T]`, `dict[K,V]`, `Optional[T]`, `Union`, `Enum`.

```python
@registry.register
async def search(query: str, max_results: int = 5) -> str:
    """Search the web."""
    return f"Results for {query}"
```

Group related tools with `BaseToolGroup`:

```python
from oaf import BaseToolGroup, tool_method

class MathTools(BaseToolGroup):
    name = "math"

    @tool_method
    async def add(self, a: int, b: int) -> str:
        return str(a + b)

registry.register_group(MathTools())  # → "math.add"
```

Role-based filtering and per-tool timeouts built in:

```python
@registry.register(allowed_roles={"leader"}, timeout=10.0)
async def sensitive_op(cmd: str) -> str: ...
```

### Context System

`BaseContext` is the single subclass point for prompt engineering. The default `ConversationalInMemory` handles message/token limits. Build your own for RAG, vector DB injection, or custom retention policies:

```python
from oaf import BaseContext

class MyRAGContext(BaseContext):
    def build_messages(self, **kwargs):
        # You control everything the LLM sees
        ...
```

Multiple agents can share one context safely — internal tools are passed at call time, never stored.

### Agent Loop

Generator-based execution yields typed events for real-time UIs:

```python
async for event in agent.run("What's the weather?", role="user"):
    match event.type:
        case "tool_call_start": print(f"Calling {event.data['name']}...")
        case "tool_call_end":   print(f"  → {event.data['result']}")
        case "message_complete": print(event.data["response"].text)
```

Or use the simple `agent.message()` / `agent.message_stream()` wrappers.

### Internal Tools

Built-in tools controlled via config — filesystem (sandboxed), thinking/reasoning, credential management, and channel switching:

```python
from oaf import Agent, InternalToolConfig

agent = Agent(
    model="gpt-4.1-nano",
    internal_tool_config=InternalToolConfig(
        thinking=True,
        filesystem=True,
        filesystem_settings={"base_path": "./workspace"},
        credentials=True,
    ),
)
```

Config auto-propagates to subagents with optional overrides.

### Multi-Agent

Spawn subagents with shared mailboxes. Results flow back automatically:

```python
from oaf import Agent, Project, Subagent, InMemoryMailbox, InMemoryCredentialStore

researcher = Subagent(
    name="researcher",
    description="Researches topics and summarizes findings",
    model="gpt-4.1-nano",
)

project = Project(
    name="my-project",
    mailbox=InMemoryMailbox(),
    credential_store=InMemoryCredentialStore(),
)

leader = project.agent(
    model="gpt-4.1-nano",
    role="leader",
    subagents=[researcher],
)
# Leader gets delegate.task + delegate.status tools automatically
```

### Credentials

Store secrets with auto-injection into tool parameters:

```python
from oaf import Cred, InMemoryCredentialStore

store = InMemoryCredentialStore()
await store.add("api_key", "sk-secret-123")

@registry.register
async def call_api(query: str, api_key: Cred) -> str:
    """Cred params resolve from the credential store at call time."""
    return f"Called with {api_key}"
```

### Hooks

14 lifecycle events — subclass or register ad-hoc. `before_*` events support mutation:

```python
from oaf import Hooks

class MyHooks(Hooks):
    async def before_message(self, message, role, messages):
        print(f"→ {message}")
    async def after_tool_call(self, tool_name, arguments, result):
        print(f"  {tool_name}({arguments}) = {result}")
```

### LLM Client (standalone)

Use independently of the agent framework:

```python
from oaf.llmclient import LLMClient, SyncLLMClient, Message

client = LLMClient()
response = await client.chat("gpt-4.1-nano", [Message(role="user", content="Hello")])

# Sync wrapper, streaming, embeddings all supported
```

## Architecture

```
┌──────────────────────────────────────────────────┐
│                  Your Application                │
├──────────────────────────────────────────────────┤
│             OpenAgentFramework (OAF)             │
│  ┌────────┐ ┌────────┐ ┌─────────┐ ┌─────────┐  │
│  │ Agent  │ │ Tools  │ │ Context │ │  Hooks  │  │
│  └────────┘ └────────┘ └─────────┘ └─────────┘  │
│  ┌────────┐ ┌────────┐ ┌─────────┐ ┌─────────┐  │
│  │Project │ │ Creds  │ │Mailbox  │ │Channels │  │
│  └────────┘ └────────┘ └─────────┘ └─────────┘  │
├──────────────────────────────────────────────────┤
│          llmclient (built-in, standalone)        │
│           OpenAI + Anthropic providers           │
└──────────────────────────────────────────────────┘
```

## Contributing

```bash
git clone https://github.com/devincii-io/scope-oaf.git
cd scope-oaf
pip install -e ".[dev]"
pytest tests/ -v
```

All development goes to `dev` branch. Push to `master` triggers PyPI publish via GitHub Actions.

## License

MIT
