Skip to main content

YoAI Agent

Provider-agnostic Python AI agent framework with Bring Your Own Provider architecture.

Write your agent code once, run it against OpenAI, Anthropic, Gemini, Ollama, OpenRouter, vLLM, or any OpenAI-compatible endpoint — without changing a line of agent code.

Installation

pip install yoaiagent

With provider SDKs:

pip install yoaiagent[openai]       # Native OpenAI
pip install yoaiagent[anthropic]    # Native Anthropic
pip install yoaiagent[gemini]       # Native Google Gemini
pip install yoaiagent[all]          # All providers
pip install yoaiagent[config]       # YAML/TOML config + .env loading
pip install yoaiagent[otel]         # OpenTelemetry tracing
pip install yoaiagent[cli]          # CLI with rich output

Quickstart

from yoaiagent import Agent, LLM

llm = LLM(
    provider="openai-compatible",
    base_url="http://localhost:11434/v1",
    api_key="ollama",
    model="llama3.2",
)

agent = Agent(
    model=llm,
    instructions="You are a helpful AI assistant.",
)

result = agent.run("Explain quantum computing simply.")
print(result.output)

Built-in Tools

10 ready-to-use tools included — no extra install needed:

from yoaiagent import Agent, LLM, ALL_TOOLS

llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")

agent = Agent(
    model=llm,
    instructions="You are a coding assistant.",
    tools=ALL_TOOLS,
)

result = agent.run("Read the file config.json and summarize it")
print(result.output)
Tool Description
read_file Read a file's contents with line numbers
write_file Create or overwrite a file
edit_file Replace text in a file
list_files List files in a directory
search_files Search for text inside files (grep)
shell Execute a shell command ⚠️ dangerous
get_repo_context Get git repo overview
web_fetch Fetch and extract text from a URL
context_summary Summarize long text
plan Create numbered task plans

Select specific tools:

from yoaiagent.builtin_tools.coder import read_file, write_file, shell
from yoaiagent.builtin_tools.web import web_fetch

agent = Agent(model=llm, tools=[read_file, write_file, shell, web_fetch])

Providers

OpenAI

llm = LLM(provider="openai", api_key="sk-...", model="gpt-5")

Anthropic

llm = LLM(provider="anthropic", api_key="sk-ant-...", model="claude-sonnet-4-20250514")

Google Gemini

llm = LLM(provider="gemini", api_key="AIza...", model="gemini-2.0-flash")

OpenAI-Compatible (OpenRouter, Ollama, vLLM, etc.)

# OpenRouter
llm = LLM(
    provider="openai-compatible",
    base_url="https://openrouter.ai/api/v1",
    api_key="your-key",
    model="meta-llama/llama-3.1-8b-instruct",
)

# Ollama (local)
llm = LLM(
    provider="openai-compatible",
    base_url="http://localhost:11434/v1",
    api_key="ollama",
    model="llama3.2",
)

# vLLM
llm = LLM(
    provider="openai-compatible",
    base_url="http://localhost:8080/v1",
    api_key="token",
    model="meta-llama/Llama-3.1-8B-Instruct",
)

# Custom gateway with headers
llm = LLM(
    provider="openai-compatible",
    base_url="https://ai.company.internal/v1",
    api_key="key",
    model="internal-model",
    headers={"X-Tenant-ID": "acme-corp"},
)

Environment Variables

export YOAI_PROVIDER=openai
export YOAI_API_KEY=sk-...
export YOAI_MODEL=gpt-5
export YOAI_BASE_URL=https://api.openai.com/v1
llm = LLM.from_env()

Custom Tools

from yoaiagent import Agent, LLM, tool

@tool
def calculator(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

@tool
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"Sunny, 25°C in {city}"

# Mark dangerous tools (requires user confirmation)
@tool(dangerous=True)
def shell(command: str) -> str:
    """Run a shell command."""
    import subprocess
    return subprocess.run(command, shell=True, capture_output=True, text=True).stdout

agent = Agent(
    model=llm,
    instructions="You are a helpful assistant.",
    tools=[calculator, get_weather],
)

result = agent.run("What is 123 + 456?")
print(result.output)

Middleware

Intercept agent lifecycle for logging, budgeting, and safety:

from yoaiagent import (
    Agent, LLM,
    TokenBudgetMiddleware,
    RateLimitMiddleware,
    CircuitBreakerMiddleware,
    ToolConfirmationMiddleware,
    StructuredLoggingHook,
    ConsoleLogger,
)

agent = Agent(
    model=llm,
    instructions="You are helpful.",
    tools=ALL_TOOLS,
    middleware=[
        ConsoleLogger(),                              # Print tool calls
        TokenBudgetMiddleware(max_total_tokens=50_000),  # Cap token usage
        RateLimitMiddleware(max_rpm=60),              # Throttle requests
        CircuitBreakerMiddleware(failure_threshold=3), # Stop on failures
        ToolConfirmationMiddleware(auto_approve=["read_file", "list_files"]),  # Confirm dangerous tools
    ],
)

Available Middleware

Middleware Purpose
ConsoleLogger Print tool calls to console
TokenBudgetMiddleware Stop when token/cost limit exceeded
RateLimitMiddleware Throttle to prevent rate limit hits
CircuitBreakerMiddleware Stop calling LLM after repeated failures
ToolConfirmationMiddleware Prompt before running dangerous tools
StructuredLoggingHook JSON logs with correlation IDs
OpenTelemetryMiddleware Export traces to Jaeger/Zipkin/Datadog

Streaming

import asyncio
from yoaiagent import Agent, LLM

async def main():
    llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
    agent = Agent(model=llm, instructions="You are a storyteller.")

    async for event in agent.astream("Tell me a short story"):
        if event.type == "text_delta":
            print(event.delta, end="", flush=True)
        elif event.type == "tool_call_started":
            print(f"\n[Using {event.tool_name}]")

asyncio.run(main())

Structured Output

from pydantic import BaseModel
from yoaiagent import Agent, LLM

class UserInfo(BaseModel):
    name: str
    age: int

llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
agent = Agent(model=llm, instructions="Extract info.")

result = agent.run("John is 30 years old.", response_model=UserInfo)
print(result.output.name)  # "John"
print(result.output.age)   # 30

Memory

In-Memory (Process Only)

from yoaiagent import Agent, LLM, InMemory

llm = LLM(provider="openai-compatible", base_url="http://localhost:11434/v1", api_key="ollama", model="llama3.2")
memory = InMemory()

agent = Agent(model=llm, instructions="You are helpful.", memory=memory)

agent.run("My name is Alice.")
result = agent.run("What is my name?")
print(result.output)  # "Your name is Alice."

SQLite (Persistent)

from yoaiagent import Agent, LLM, SQLiteMemory

memory = SQLiteMemory(db_path="~/.yoaiagent/memory.db")
agent = Agent(model=llm, memory=memory)

# Survives restarts
agent.run("My name is Alice.")
# ... restart your app ...
result = agent.run("What is my name?")
print(result.output)  # "Your name is Alice."

Multi-Agent

from yoaiagent import Agent, LLM

researcher = Agent(name="researcher", model=llm, instructions="Research assistant.")
writer = Agent(name="writer", model=llm, instructions="Write articles.")

# Make researcher available as a tool
writer.add_tool(researcher.as_tool())

result = writer.run("Write about AI.")

Workflows

from yoaiagent import Agent, LLM, Workflow

researcher = Agent(name="researcher", model=llm, instructions="Gather facts.")
writer = Agent(name="writer", model=llm, instructions="Write content.")
reviewer = Agent(name="reviewer", model=llm, instructions="Review for quality.")

workflow = Workflow()
workflow.add_node("research", researcher)
workflow.add_node("write", writer)
workflow.add_node("review", reviewer)

workflow.connect("research", "write")
workflow.connect("write", "review")

results = workflow.run("History of the internet")

Configuration

Config File

Create yoaiagent.yaml in your project root:

llm:
  provider: openai-compatible
  model: llama3
  base_url: http://localhost:11434/v1
  api_key: ollama
  timeout: 60.0
  max_retries: 3

Then load it:

llm = LLM.from_env()  # Reads config file + env vars

Config Precedence

Direct code kwargs → Environment variables → Config file → .env → Defaults

Custom Providers

from yoaiagent import register_provider, BaseModel, ProviderCapabilities, LLMConfig, Message, RunResult

class MyProvider(BaseModel):
    provider = "my-provider"
    capabilities = ProviderCapabilities(supports_streaming=True)

    def __init__(self, config: LLMConfig):
        self.model = config.model
        # Initialize your HTTP client or SDK here

    async def generate(self, messages, **kwargs):
        # Call your API
        pass

    async def stream(self, messages, **kwargs):
        # Stream from your API
        pass

register_provider("my-provider", MyProvider)
llm = LLM(provider="my-provider", model="my-model", api_key="key")

CLI

yoai providers     # List registered providers
yoai doctor        # Diagnose configuration issues
yoai version       # Show version

Architecture

Agent
  ↓
LLM (config)
  ↓
Model Interface (BaseModel)
  ↓
Provider Adapter (OpenAICompatibleModel, OpenAIModel, AnthropicModel, GeminiModel)
  ↓
HTTP / SDK

The Agent communicates only with the common BaseModel interface. Provider adapters translate between internal messages and provider-specific formats. The agent code never changes when switching providers.

License

MIT

Download files

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

Source Distribution

yoaiagent-0.2.3.tar.gz (170.8 kB view details)

Uploaded Source

Built Distribution

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

yoaiagent-0.2.3-py3-none-any.whl (59.9 kB view details)

Uploaded Python 3

File details

Details for the file yoaiagent-0.2.3.tar.gz.

File metadata

  • Download URL: yoaiagent-0.2.3.tar.gz
  • Upload date:
  • Size: 170.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for yoaiagent-0.2.3.tar.gz
Algorithm Hash digest
SHA256 e5107773df31f0f1cfc8fdc1d4eda3d384c50f892c415434160ddc7896f4f701
MD5 ac2ce0342a7898122fb2eaadac1f5a2e
BLAKE2b-256 6d8d4c44ad5b7fb118069666eefca9ca1ba3b07003bf4b8cab60e93feb7460ce

See more details on using hashes here.

File details

Details for the file yoaiagent-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: yoaiagent-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 59.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for yoaiagent-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 977cb6cfcf1600f7c0b7119c8e9a16f85a5f7abcb16349822ac8887fd0d3c90a
MD5 75a94f9008f3cc53e7064b80761aa9d2
BLAKE2b-256 2a2f2dbb9c497c6cf86ce85347aa075dc6ff3446cdc80eeee667ef51f5b367bf

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page