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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5107773df31f0f1cfc8fdc1d4eda3d384c50f892c415434160ddc7896f4f701
|
|
| MD5 |
ac2ce0342a7898122fb2eaadac1f5a2e
|
|
| BLAKE2b-256 |
6d8d4c44ad5b7fb118069666eefca9ca1ba3b07003bf4b8cab60e93feb7460ce
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
977cb6cfcf1600f7c0b7119c8e9a16f85a5f7abcb16349822ac8887fd0d3c90a
|
|
| MD5 |
75a94f9008f3cc53e7064b80761aa9d2
|
|
| BLAKE2b-256 |
2a2f2dbb9c497c6cf86ce85347aa075dc6ff3446cdc80eeee667ef51f5b367bf
|