Quickstart¶
Every provider implements the same IChatCompletionProvider port, so the only
thing that changes between OpenAI, Anthropic, Gemini, Bedrock, OpenRouter and
Azure is the value you pass to the factory.
Pick a provider, ask a question¶
quickstart.py
import asyncio
import os
from apogee_ai_providers import (
ChatMessage,
ChatRequest,
MessageRole,
Provider,
ProviderCredentials,
ProviderFactory,
)
async def main() -> None:
credentials = ProviderCredentials(
api_key=os.environ["AI_PROVIDER_KEY_ANTHROPIC"],
timeout=30.0,
)
provider = ProviderFactory.build(Provider.ANTHROPIC, credentials)
request = ChatRequest(
model="claude-haiku-4-5-20251001",
messages=[
ChatMessage(role=MessageRole.SYSTEM, content="Answer in one sentence."),
ChatMessage(role=MessageRole.USER, content="What is RAG?"),
],
max_tokens=128,
temperature=0.2,
)
response = await provider.complete(request)
print(response.choices[0].message.content)
print("usage:", response.usage)
await provider.aclose()
asyncio.run(main())
Swap providers without touching application code¶
Only the Provider value and the model id change — ChatRequest, the
response shape and the error types stay identical.
ProviderFactory.build() also accepts a plain string ("anthropic"), which is
what the CLI and environment-driven configuration use.
Stream responses¶
Set stream=True on the request and iterate. Each ChatChunk carries a
delta; the final one carries finish_reason.
streaming.py
request = ChatRequest(
model="claude-haiku-4-5-20251001",
messages=[ChatMessage(role=MessageRole.USER, content="Count from 1 to 5.")],
max_tokens=64,
temperature=0.0,
stream=True,
)
async for chunk in provider.stream(request):
if chunk.delta:
print(chunk.delta, end="", flush=True)
Close the client
Providers hold an HTTP connection pool. Call await provider.aclose() when
you are done, or build them inside a DI container that owns the lifecycle.
Read next¶
- Reference — all 88 exported symbols.
- Apogee Framework — how the packages fit together.