Multi-provider abstraction with capability negotiation, health checks, and fallback chains
Project description
pyagent-providers
Multi-provider abstraction with capability negotiation, health checks, fallback chains, and cost-optimized routing for multi-agent LLM systems. Drop-in replacement for hardcoded model specs.
Install
pip install pyagent-providers # Core (includes MockProvider)
pip install pyagent-providers[openai] # + OpenAI adapter
pip install pyagent-providers[anthropic] # + Anthropic adapter
pip install pyagent-providers[litellm] # + LiteLLM (100+ models)
pip install pyagent-providers[all] # All adapters
Depends on: pyagent-patterns, pyagent-router.
Why Provider Abstraction?
Without pyagent-providers, switching models means rewriting LLM wrappers. With it, your agents talk to a ProviderProtocol that satisfies the existing LLMCallable interface — so every provider is a drop-in replacement for Agent.llm.
from pyagent_patterns.base import Agent
from pyagent_providers.adapters.mock import MockProvider
# Any provider works as an Agent's LLM
provider = MockProvider(name="test", responses=["Hello"])
agent = Agent("greeter", provider) # provider satisfies LLMCallable
ProviderProtocol — The Interface
Every provider implements this:
from pyagent_providers import ProviderProtocol, HealthStatus, ProviderCapabilities
class MyCustomProvider:
@property
def name(self) -> str:
return "my_provider"
@property
def capabilities(self) -> ProviderCapabilities:
return ProviderCapabilities(
models=["my-model-small", "my-model-large"],
capabilities={Capability.GENERAL, Capability.CODE},
max_context=128_000,
supports_streaming=True,
)
async def health(self) -> HealthStatus:
# check your endpoint
return HealthStatus.HEALTHY
async def complete(self, messages, model=None) -> str:
# call your API
return "response"
async def __call__(self, messages) -> str:
return await self.complete(messages)
ProviderRegistry — Register and Discover
import asyncio
from pyagent_providers import ProviderRegistry, HealthStatus
from pyagent_providers.adapters.mock import MockProvider
from pyagent_router.selector import Capability
registry = ProviderRegistry()
async def setup():
await registry.register(MockProvider(
name="openai",
models=["gpt-4o-mini", "gpt-4o"],
capabilities={Capability.GENERAL, Capability.CODE, Capability.VISION},
))
await registry.register(MockProvider(
name="anthropic",
models=["claude-haiku-3.5", "claude-sonnet-4"],
capabilities={Capability.GENERAL, Capability.CODE, Capability.CREATIVE},
))
# Discover by capability
coders = registry.discover({Capability.CODE})
print([p.name for p in coders]) # ["openai", "anthropic"]
vision = registry.discover({Capability.VISION})
print([p.name for p in vision]) # ["openai"]
# Health check all
statuses = await registry.check_health()
print(statuses) # {"openai": "healthy", "anthropic": "healthy"}
# Remove unhealthy
removed = await registry.remove_unhealthy()
print(f"Removed: {removed}") # []
asyncio.run(setup())
ProviderRouter — Strategy-Based Routing
Four strategies: CAPABILITY_FIRST, COST_FIRST, LATENCY_FIRST, ROUND_ROBIN.
from pyagent_providers import ProviderRouter, RoutingStrategy
from pyagent_patterns.base import Message
# Capability-first (default): pick the provider with broadest capabilities
router = ProviderRouter(registry, strategy=RoutingStrategy.CAPABILITY_FIRST)
provider, model = asyncio.run(router.route(
[Message.user("Write a Python REST API with FastAPI")],
required={Capability.CODE},
))
print(f"{provider.name}/{model}")
# Cost-first: cheapest provider + model for the task
router = ProviderRouter(registry, strategy=RoutingStrategy.COST_FIRST)
provider, model = asyncio.run(router.route([Message.user("What is 2+2?")]))
print(f"{provider.name}/{model}") # picks gpt-4.1-nano
# Round-robin: cycle through providers for load distribution
router = ProviderRouter(registry, strategy=RoutingStrategy.ROUND_ROBIN)
for _ in range(4):
provider, model = asyncio.run(router.route([Message.user("Balance me")]))
print(provider.name, end=" ")
# openai anthropic openai anthropic
FallbackChain — Resilient Completion
Try providers in order. If one fails, fall through to the next. Optionally integrates with CircuitBreaker from pyagent-patterns.
from pyagent_providers import FallbackChain
chain = FallbackChain(providers=[
primary_openai, # try first
fallback_anthropic, # if OpenAI fails
emergency_litellm, # last resort
])
result = asyncio.run(chain.complete([Message.user("Important task")]))
print(result.output) # response from first successful provider
print(result.provider_name) # which provider answered
print(result.attempts) # full attempt log with errors
# With circuit breaker integration
from pyagent_patterns.recovery import CircuitBreaker
chain = FallbackChain(
providers=[primary, fallback],
circuit_breakers={
"primary": CircuitBreaker(failure_threshold=3, reset_timeout_seconds=60),
},
)
CapabilityNegotiator — Match Task Requirements
Scores providers by capability overlap, context window, and feature support.
from pyagent_providers import CapabilityNegotiator
negotiator = CapabilityNegotiator(registry)
# Find best provider for code + reasoning tasks
result = negotiator.negotiate(
required_capabilities={Capability.CODE, Capability.REASONING},
min_context=100_000,
)
if result:
print(result.provider.name) # "openai" or "anthropic"
print(result.model) # best model from that provider
print(f"Match: {result.match_score:.0%}")
print(result.matched_capabilities) # {CODE, REASONING}
print(result.missing_capabilities) # set()
# Get all ranked matches
all_matches = negotiator.negotiate_all(
required_capabilities={Capability.GENERAL},
limit=5,
)
for m in all_matches:
print(f" {m.provider.name}: {m.match_score:.0%}")
CostOptimizer — Multi-Provider Cost Comparison
from pyagent_providers import CostOptimizer
optimizer = CostOptimizer(registry)
# Compare all providers for a task
estimates = optimizer.compare("Explain distributed consensus algorithms")
for est in estimates[:5]:
print(f"{est.provider_name}/{est.model}: ${est.estimate.total_cost:.7f}")
# Get cheapest option
cheapest = optimizer.cheapest("Simple greeting task")
if cheapest:
print(f"Use {cheapest.provider_name}/{cheapest.model}: ${cheapest.estimate.total_cost:.7f}")
# Get provider object + model for direct use
pair = optimizer.cheapest_provider("My task")
if pair:
provider, model = pair
agent = Agent("my_agent", provider)
Adapter Examples
OpenAI
from pyagent_providers.adapters.openai import OpenAIProvider
openai = OpenAIProvider(
api_key="sk-...", # or set OPENAI_API_KEY env var
default_model="gpt-4o-mini",
models=["gpt-4o-mini", "gpt-4o", "o3-mini"],
)
await registry.register(openai)
result = await openai.complete([Message.user("Hello")])
Anthropic
from pyagent_providers.adapters.anthropic import AnthropicProvider
anthropic = AnthropicProvider(
api_key="sk-ant-...",
default_model="claude-sonnet-4-20250514",
)
await registry.register(anthropic)
result = await anthropic.complete([Message.user("Hello")])
LiteLLM (100+ Providers)
from pyagent_providers.adapters.litellm import LiteLLMProvider
litellm = LiteLLMProvider(
models=["gpt-4o-mini", "anthropic/claude-haiku-3.5", "gemini/gemini-2.5-flash"],
default_model="gpt-4o-mini",
)
await registry.register(litellm)
result = await litellm.complete([Message.user("Hello")], model="gemini/gemini-2.5-flash")
MockProvider (Testing)
from pyagent_providers.adapters.mock import MockProvider
mock = MockProvider(
name="test",
responses=["Response 1", "Response 2"],
models=["mock-fast", "mock-smart"],
capabilities={Capability.GENERAL, Capability.CODE},
health_status=HealthStatus.HEALTHY,
)
await registry.register(mock)
result = await mock.complete([Message.user("Test")])
print(mock.call_count) # 1
Integration with pyagent-patterns
from pyagent_patterns.base import Agent
from pyagent_patterns.orchestration import Pipeline
from pyagent_providers import ProviderRegistry, CapabilityNegotiator
from pyagent_providers.adapters.mock import MockProvider
# Set up providers
registry = ProviderRegistry()
registry.register_sync(MockProvider(name="fast", responses=["Extracted facts"]))
registry.register_sync(MockProvider(name="smart", responses=["Detailed analysis"]))
# Use providers as Agent LLMs
fast = registry.get("fast")
smart = registry.get("smart")
pipeline = Pipeline(stages=[
Agent("extractor", fast, system_prompt="Extract key facts."),
Agent("analyst", smart, system_prompt="Analyse in depth."),
])
result = asyncio.run(pipeline.run("Process this document"))
print(result.output)
Full Documentation
See pyagent.dev for full API reference and integration guides.
Project details
Release history Release notifications | RSS feed
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 pyagent_providers-0.1.0.tar.gz.
File metadata
- Download URL: pyagent_providers-0.1.0.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc58db8bce93138e24e39ffdcc456eeeb630297cf03743916b34111a8bbc4733
|
|
| MD5 |
db017ca057530fc076f672589182848f
|
|
| BLAKE2b-256 |
cf653737b886f08232d47cb1c69afc9b2e8e7f5d2adc91a71c697ec9bbcb3661
|
File details
Details for the file pyagent_providers-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyagent_providers-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4e63100f33ef8f22c95e10b30b0969d5e4179df3fa74c3557a0793c00049161
|
|
| MD5 |
978cb9526fa711e9873553db61229082
|
|
| BLAKE2b-256 |
a996224d11c37a10b88c4f76880d6b05b7677bd7b1609ec1caddf9a991227450
|