| name | adapter-interface |
| description | Generate adapter pattern for swappable providers in Python async applications.
Use when implementing: (1) LLM provider abstraction (OpenAI, Anthropic), (2) Storage adapters
(S3, MinIO, local), (3) Search provider abstraction (Tavily, Serper), (4) Sandbox adapters
(E2B, Docker). Creates Protocol-based interfaces with async support and proper typing.
NOT for simple dependency injection or non-swappable integrations.
|
Adapter Interface Generator
Generate provider abstraction patterns for swappable external services.
Quick Reference
| Adapter Type | Command |
|---|
| LLM Provider | /adapter-interface llm |
| Storage | /adapter-interface storage |
| Search | /adapter-interface search |
| Sandbox | /adapter-interface sandbox |
| Image Generation | /adapter-interface image-gen |
| Custom | /adapter-interface <name> |
Pattern Structure
adapters/
├── base.py # Protocol definition
├── factory.py # Provider factory
├── openai_adapter.py # Concrete implementation
├── anthropic_adapter.py # Concrete implementation
└── exceptions.py # Adapter-specific errors
Implementation
1. Protocol Definition
from typing import Protocol, AsyncIterator, Any
from dataclasses import dataclass
@dataclass
class Message:
role: str
content: str
@dataclass
class LLMResponse:
content: str
model: str
usage: dict[str, int]
class LLMAdapter(Protocol):
"""Protocol for LLM providers."""
async def chat(
self,
messages: list[Message],
model: str | None = None,
temperature: float = 0.7,
max_tokens: int | None = None,
) -> LLMResponse:
"""Send chat completion request."""
...
async def stream(
self,
messages: list[Message],
model: str | None = None,
temperature: float = 0.7,
) -> AsyncIterator[str]:
"""Stream chat completion tokens."""
...
def count_tokens(self, text: str) -> int:
"""Count tokens in text."""
...
2. Factory Pattern
from typing import Literal
from adapters.llm.base import LLMAdapter
from adapters.llm.openai_adapter import OpenAIAdapter
from adapters.llm.anthropic_adapter import AnthropicAdapter
ProviderType = Literal["openai", "anthropic"]
def create_llm_adapter(
provider: ProviderType,
api_key: str | None = None,
**kwargs,
) -> LLMAdapter:
"""Factory for LLM adapters."""
adapters = {
"openai": OpenAIAdapter,
"anthropic": AnthropicAdapter,
}
if provider not in adapters:
raise ValueError(f"Unknown provider: {provider}")
return adapters[provider](api_key=api_key, **kwargs)
3. Concrete Implementation
See references/implementations.md for full adapter implementations.
Adapter Types
Usage in Services
from functools import lru_cache
from adapters.llm.factory import create_llm_adapter
@lru_cache
def get_llm_adapter():
return create_llm_adapter(
provider=settings.LLM_PROVIDER,
api_key=settings.LLM_API_KEY,
)
from fastapi import Depends
async def llm_dependency():
return get_llm_adapter()
@router.post("/chat")
async def chat(llm: LLMAdapter = Depends(llm_dependency)):
response = await llm.chat(messages)
return response
Testing
from unittest.mock import AsyncMock
mock_llm = AsyncMock(spec=LLMAdapter)
mock_llm.chat.return_value = LLMResponse(
content="Hello!",
model="gpt-4",
usage={"prompt_tokens": 10, "completion_tokens": 5},
)