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
Metadata
Release files for yoaiagent 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| yoaiagent-0.2.3.tar.gz | 170.8 kB | Without provenance | Details for yoaiagent-0.2.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| yoaiagent-0.2.3-py3-none-any.whl | Python 3 | none | any | Details for yoaiagent-0.2.3-py3-none-any.whl |
Total release size: 230.7 kB
Release files / yoaiagent-0.2.3.tar.gz
| Download URL | yoaiagent-0.2.3.tar.gz |
|---|---|
| Size | 170.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e5107773df31f0f1cfc8fdc1d4eda3d384c50f892c415434160ddc7896f4f701
|
|
BLAKE2b-256 checksum How to use checksums |
6d8d4c44ad5b7fb118069666eefca9ca1ba3b07003bf4b8cab60e93feb7460ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / yoaiagent-0.2.3-py3-none-any.whl
| Download URL | yoaiagent-0.2.3-py3-none-any.whl |
|---|---|
| Size | 59.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
977cb6cfcf1600f7c0b7119c8e9a16f85a5f7abcb16349822ac8887fd0d3c90a
|
|
BLAKE2b-256 checksum How to use checksums |
2a2f2dbb9c497c6cf86ce85347aa075dc6ff3446cdc80eeee667ef51f5b367bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for yoaiagent 0.2.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.