跳转至

API reference

Generated from the apogee-ai-providers source with mkdocstrings. Every symbol below is exported from apogee_ai_providers, so it is part of the supported public surface.

Other

AnthropicChatProvider

Python
AnthropicChatProvider(credentials: ProviderCredentials, *, config: AnthropicConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IChatCompletionProvider against Anthropic Messages API.

Uses x-api-key and anthropic-version headers (no Bearer).

Source code in apogee_ai_providers/infrastructure/providers/anthropic/anthropic_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: AnthropicConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or AnthropicConfig()
    merged_headers = {
        "x-api-key": credentials.api_key,
        "anthropic-version": self._config.api_version,
        **credentials.extra_headers,
    }
    effective = replace(
        credentials,
        base_url=credentials.base_url or self._config.base_url,
        extra_headers=merged_headers,
    )
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="anthropic",
        credentials=self._credentials,
        auth_scheme="none",
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/anthropic/anthropic_chat_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/anthropic/anthropic_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    payload = request_to_anthropic_payload(replace(request, stream=False))
    data = await self._http.post_json("/messages", payload)
    return anthropic_response_to_domain(data, model=request.model)

stream async

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/infrastructure/providers/anthropic/anthropic_chat_provider.py
Python
async def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]:
    payload = request_to_anthropic_payload(replace(request, stream=True))
    state = AnthropicStreamState(default_model=request.model)
    async for raw in self._http.stream_sse("/messages", payload):
        chunk = anthropic_event_to_chunk(raw, state=state)
        if chunk is not None:
            yield chunk

AnthropicConfig dataclass

Python
AnthropicConfig(base_url: str = 'https://api.anthropic.com/v1', default_model: str = 'claude-haiku-4-5-20251001', api_version: str = '2023-06-01')

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.anthropic.com/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'claude-haiku-4-5-20251001'

api_version class-attribute instance-attribute

Python
api_version: str = '2023-06-01'

AsyncHttpClient

Python
AsyncHttpClient(*, provider: str, credentials: ProviderCredentials, max_retries: int = 2, backoff_base: float = 0.5, auth_scheme: str = 'bearer')

Thin async HTTP client. One instance per provider adapter.

auth_scheme: "bearer" (Authorization: Bearer ) or "none" (extra_headers only).

Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
def __init__(
    self,
    *,
    provider: str,
    credentials: ProviderCredentials,
    max_retries: int = 2,
    backoff_base: float = 0.5,
    auth_scheme: str = "bearer",
) -> None:
    """auth_scheme: "bearer" (Authorization: Bearer <key>) or "none" (extra_headers only)."""
    self._provider = provider
    self._credentials = credentials
    self._max_retries = max_retries
    self._backoff_base = backoff_base
    self._auth_scheme = auth_scheme
    self._client = httpx.AsyncClient(
        base_url=credentials.base_url or "",
        timeout=credentials.timeout,
        headers=self._default_headers(),
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
async def aclose(self) -> None:
    await self._client.aclose()

post_json async

Python
post_json(url: str, payload: Mapping[str, Any], *, extra_headers: Mapping[str, str] | None = None) -> dict[str, Any]
Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
async def post_json(
    self,
    url: str,
    payload: Mapping[str, Any],
    *,
    extra_headers: Mapping[str, str] | None = None,
) -> dict[str, Any]:
    response = await self._request_with_retry("POST", url, json=payload, headers=extra_headers)
    return response.json()

post_bytes async

Python
post_bytes(url: str, payload: Mapping[str, Any], *, extra_headers: Mapping[str, str] | None = None) -> bytes

POST a JSON payload, return the raw response body (e.g. audio bytes).

Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
async def post_bytes(
    self,
    url: str,
    payload: Mapping[str, Any],
    *,
    extra_headers: Mapping[str, str] | None = None,
) -> bytes:
    """POST a JSON payload, return the raw response body (e.g. audio bytes)."""

    response = await self._request_with_retry(
        "POST", url, json=payload, headers=extra_headers
    )
    return response.content

post_multipart async

Python
post_multipart(url: str, *, data: Mapping[str, Any] | None = None, files: Mapping[str, tuple[str, bytes, str]] | None = None, extra_headers: Mapping[str, str] | None = None) -> dict[str, Any]

POST a multipart/form-data request — used by STT endpoints.

We open a one-shot AsyncClient without the JSON default Content-Type so httpx can negotiate the multipart boundary itself.

Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
async def post_multipart(
    self,
    url: str,
    *,
    data: Mapping[str, Any] | None = None,
    files: Mapping[str, tuple[str, bytes, str]] | None = None,
    extra_headers: Mapping[str, str] | None = None,
) -> dict[str, Any]:
    """POST a multipart/form-data request — used by STT endpoints.

    We open a one-shot AsyncClient without the JSON default Content-Type
    so httpx can negotiate the multipart boundary itself.
    """

    attempt = 0
    # Build auth headers without Content-Type so httpx can set multipart.
    headers: dict[str, str] = {}
    if self._auth_scheme == "bearer":
        headers["Authorization"] = f"Bearer {self._credentials.api_key}"
    headers.update(self._credentials.extra_headers)
    if extra_headers:
        headers.update(extra_headers)

    base_url = str(self._client.base_url) or self._credentials.base_url or ""
    timeout = self._credentials.timeout
    async with httpx.AsyncClient(base_url=base_url, timeout=timeout) as fresh:
        while True:
            try:
                response = await fresh.post(
                    url,
                    data=dict(data or {}),
                    files=dict(files or {}),
                    headers=headers,
                )
                if response.status_code == 429 and attempt < self._max_retries:
                    retry_after = self._parse_retry_after(response)
                    attempt += 1
                    await asyncio.sleep(
                        retry_after or self._backoff_base * (2 ** (attempt - 1))
                    )
                    continue
                if 500 <= response.status_code < 600 and attempt < self._max_retries:
                    attempt += 1
                    await asyncio.sleep(self._backoff_base * (2 ** (attempt - 1)))
                    continue
                self._raise_for_status(response)
                return response.json()
            except (httpx.TimeoutException, httpx.TransportError) as exc:
                attempt += 1
                if attempt > self._max_retries:
                    raise self._map_transport_error(exc) from exc
                await asyncio.sleep(self._backoff_base * (2 ** (attempt - 1)))

stream_sse async

Python
stream_sse(url: str, payload: Mapping[str, Any], *, extra_headers: Mapping[str, str] | None = None) -> AsyncIterator[str]

Yields raw SSE data: lines (without the data: prefix).

Source code in apogee_ai_providers/infrastructure/http/async_http_client.py
Python
async def stream_sse(
    self,
    url: str,
    payload: Mapping[str, Any],
    *,
    extra_headers: Mapping[str, str] | None = None,
) -> AsyncIterator[str]:
    """Yields raw SSE `data:` lines (without the `data: ` prefix)."""
    headers = dict(extra_headers or {})
    headers.setdefault("Accept", "text/event-stream")
    attempt = 0
    while True:
        try:
            async with self._client.stream(
                "POST", url, json=payload, headers=headers
            ) as response:
                self._raise_for_status(response)
                async for line in response.aiter_lines():
                    if not line:
                        continue
                    if line.startswith("data: "):
                        yield line[6:]
                    elif line.startswith("data:"):
                        yield line[5:]
            return
        except (httpx.TimeoutException, httpx.TransportError) as exc:
            attempt += 1
            if attempt > self._max_retries:
                raise self._map_transport_error(exc) from exc
            await asyncio.sleep(self._backoff_base * (2 ** (attempt - 1)))

AzureOpenAIChatProvider

Python
AzureOpenAIChatProvider(credentials: ProviderCredentials, *, config: AzureOpenAIConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IChatCompletionProvider against Azure OpenAI.

Source code in apogee_ai_providers/infrastructure/providers/azure/azure_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: AzureOpenAIConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or AzureOpenAIConfig()
    if not credentials.base_url:
        raise ValueError(
            "Azure OpenAI requires `base_url` to be set on ProviderCredentials "
            "(e.g. https://<resource>.openai.azure.com)."
        )
    self._credentials = credentials
    # Azure uses a custom header; we always start with auth_scheme="none"
    # on the underlying client and inject our own header below.
    self._http = http_client or AsyncHttpClient(
        provider=self.provider_name,
        credentials=self._auth_credentials(credentials),
        auth_scheme="bearer" if self._config.auth_scheme == "bearer" else "none",
    )

provider_name class-attribute instance-attribute

Python
provider_name = 'azure'

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/azure/azure_chat_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/azure/azure_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    deployment = self._resolve_deployment(request)
    payload = request_to_openai_payload(replace(request, stream=False))
    data = await self._http.post_json(self._path_for(deployment), payload)
    response = openai_response_to_domain(data)
    return replace(response, provider=self.provider_name)

stream async

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/infrastructure/providers/azure/azure_chat_provider.py
Python
async def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]:
    deployment = self._resolve_deployment(request)
    payload = request_to_openai_payload(replace(request, stream=True))
    async for raw in self._http.stream_sse(self._path_for(deployment), payload):
        chunk = openai_chunk_to_domain(raw)
        if chunk is not None:
            yield chunk

AzureOpenAIConfig dataclass

Python
AzureOpenAIConfig(api_version: str = '2024-10-21', default_deployment: str | None = None, auth_scheme: str = 'api-key')

api_version class-attribute instance-attribute

Python
api_version: str = '2024-10-21'

default_deployment class-attribute instance-attribute

Python
default_deployment: str | None = None

auth_scheme class-attribute instance-attribute

Python
auth_scheme: str = 'api-key'

Either "api-key" (header api-key: ) or "bearer" (AAD token).

BedrockChatProvider

Python
BedrockChatProvider(credentials: ProviderCredentials, *, config: BedrockConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IChatCompletionProvider against AWS Bedrock Converse API.

Source code in apogee_ai_providers/infrastructure/providers/bedrock/bedrock_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: BedrockConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or BedrockConfig()
    effective = replace(
        credentials,
        base_url=credentials.base_url or self._config.base_url,
    )
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="bedrock",
        credentials=self._credentials,
        auth_scheme="bearer",
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/bedrock/bedrock_chat_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/bedrock/bedrock_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    payload = request_to_bedrock_payload(replace(request, stream=False))
    model_id = quote(request.model, safe="")
    path = f"/model/{model_id}/converse"
    data = await self._http.post_json(path, payload)
    return bedrock_response_to_domain(data, model=request.model)

stream async

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/infrastructure/providers/bedrock/bedrock_chat_provider.py
Python
async def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]:
    raise NotImplementedError(
        "Bedrock streaming uses AWS Event Stream (binary framing); not yet supported. "
        "Use `complete()` instead."
    )
    # hint for type-checker: make this an async generator
    yield  # type: ignore[unreachable]  # pragma: no cover

BedrockConfig dataclass

Python
BedrockConfig(region: str = 'us-east-1', default_model: str = 'amazon.nova-micro-v1:0')

Holds Bedrock runtime endpoint + default model.

The Converse API is region-scoped. Default region is us-east-1. Override via env var AI_PROVIDER_URL_BEDROCK (full base URL) or pass credentials.base_url.

region class-attribute instance-attribute

Python
region: str = 'us-east-1'

default_model class-attribute instance-attribute

Python
default_model: str = 'amazon.nova-micro-v1:0'

base_url property

Python
base_url: str

ChatChoice dataclass

Python
ChatChoice(index: int, message: ChatMessage, finish_reason: FinishReason | None = None)

index instance-attribute

Python
index: int

message instance-attribute

Python
message: ChatMessage

finish_reason class-attribute instance-attribute

Python
finish_reason: FinishReason | None = None

ChatChunk dataclass

Python
ChatChunk(id: str, model: str, delta: str = '', tool_calls: list[ToolCall] = list(), finish_reason: FinishReason | None = None)

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

delta class-attribute instance-attribute

Python
delta: str = ''

tool_calls class-attribute instance-attribute

Python
tool_calls: list[ToolCall] = field(default_factory=list)

finish_reason class-attribute instance-attribute

Python
finish_reason: FinishReason | None = None

ChatMessage dataclass

Python
ChatMessage(role: MessageRole, content: str | None = None, name: str | None = None, tool_calls: list[ToolCall] = list(), tool_call_id: str | None = None)

role instance-attribute

Python
role: MessageRole

content class-attribute instance-attribute

Python
content: str | None = None

name class-attribute instance-attribute

Python
name: str | None = None

tool_calls class-attribute instance-attribute

Python
tool_calls: list[ToolCall] = field(default_factory=list)

tool_call_id class-attribute instance-attribute

Python
tool_call_id: str | None = None

ChatRequest dataclass

Python
ChatRequest(model: str, messages: list[ChatMessage], tools: list[ToolDefinition] = list(), tool_choice: str | dict[str, Any] | None = None, thinking: ThinkingConfig | None = None, stream: bool = False, temperature: float | None = None, top_p: float | None = None, max_tokens: int | None = None, stop: list[str] | None = None, response_format: dict[str, Any] | None = None, seed: int | None = None, user: str | None = None, metadata: dict[str, Any] = dict())

model instance-attribute

Python
model: str

messages instance-attribute

Python
messages: list[ChatMessage]

tools class-attribute instance-attribute

Python
tools: list[ToolDefinition] = field(default_factory=list)

tool_choice class-attribute instance-attribute

Python
tool_choice: str | dict[str, Any] | None = None

thinking class-attribute instance-attribute

Python
thinking: ThinkingConfig | None = None

stream class-attribute instance-attribute

Python
stream: bool = False

temperature class-attribute instance-attribute

Python
temperature: float | None = None

top_p class-attribute instance-attribute

Python
top_p: float | None = None

max_tokens class-attribute instance-attribute

Python
max_tokens: int | None = None

stop class-attribute instance-attribute

Python
stop: list[str] | None = None

response_format class-attribute instance-attribute

Python
response_format: dict[str, Any] | None = None

seed class-attribute instance-attribute

Python
seed: int | None = None

user class-attribute instance-attribute

Python
user: str | None = None

metadata class-attribute instance-attribute

Python
metadata: dict[str, Any] = field(default_factory=dict)

ChatResponse dataclass

Python
ChatResponse(id: str, model: str, choices: list[ChatChoice], usage: TokenUsage = TokenUsage(), provider: str = '')

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

choices instance-attribute

Python
choices: list[ChatChoice]

usage class-attribute instance-attribute

Python
usage: TokenUsage = field(default_factory=TokenUsage)

provider class-attribute instance-attribute

Python
provider: str = ''

DeepSeekChatProvider

Python
DeepSeekChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'deepseek'

DeepSeekConfig dataclass

Python
DeepSeekConfig(base_url: str = 'https://api.deepseek.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'deepseek-chat')

Bases: OpenAICompatibleConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.deepseek.com/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'deepseek-chat'

EmbeddingRequest dataclass

Python
EmbeddingRequest(model: str, inputs: list[str], dimensions: int | None = None, encoding_format: str = 'float', user: str | None = None)

One or more inputs to embed against a single model.

model instance-attribute

Python
model: str

inputs instance-attribute

Python
inputs: list[str]

dimensions class-attribute instance-attribute

Python
dimensions: int | None = None

encoding_format class-attribute instance-attribute

Python
encoding_format: str = 'float'

user class-attribute instance-attribute

Python
user: str | None = None

EmbeddingResponse dataclass

Python
EmbeddingResponse(model: str, provider: str, embeddings: list[list[float]], usage: TokenUsage)

model instance-attribute

Python
model: str

provider instance-attribute

Python
provider: str

embeddings instance-attribute

Python
embeddings: list[list[float]]

usage instance-attribute

Python
usage: TokenUsage

FinishReason

Bases: str, Enum

STOP class-attribute instance-attribute

Python
STOP = 'stop'

LENGTH class-attribute instance-attribute

Python
LENGTH = 'length'

TOOL_CALLS class-attribute instance-attribute

Python
TOOL_CALLS = 'tool_calls'

CONTENT_FILTER class-attribute instance-attribute

Python
CONTENT_FILTER = 'content_filter'

ERROR class-attribute instance-attribute

Python
ERROR = 'error'

GeminiChatProvider

Python
GeminiChatProvider(credentials: ProviderCredentials, *, config: GeminiConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IChatCompletionProvider against Google Generative Language API.

Authentication uses the x-goog-api-key header (no Bearer).

Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: GeminiConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or GeminiConfig()
    merged_headers = {
        "x-goog-api-key": credentials.api_key,
        **credentials.extra_headers,
    }
    effective = replace(
        credentials,
        base_url=credentials.base_url or self._config.base_url,
        extra_headers=merged_headers,
    )
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="gemini",
        credentials=self._credentials,
        auth_scheme="none",
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_chat_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    payload = request_to_gemini_payload(replace(request, stream=False))
    path = f"/models/{request.model}:generateContent"
    data = await self._http.post_json(path, payload)
    return gemini_response_to_domain(data, model=request.model)

stream async

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_chat_provider.py
Python
async def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]:
    payload = request_to_gemini_payload(replace(request, stream=True))
    path = f"/models/{request.model}:streamGenerateContent?alt=sse"
    async for raw in self._http.stream_sse(path, payload):
        chunk = gemini_chunk_to_domain(raw, model=request.model)
        if chunk is not None:
            yield chunk

GeminiConfig dataclass

Python
GeminiConfig(base_url: str = 'https://generativelanguage.googleapis.com/v1beta', default_model: str = 'gemini-2.5-flash', api_version: str = 'v1beta')

base_url class-attribute instance-attribute

Python
base_url: str = 'https://generativelanguage.googleapis.com/v1beta'

default_model class-attribute instance-attribute

Python
default_model: str = 'gemini-2.5-flash'

api_version class-attribute instance-attribute

Python
api_version: str = 'v1beta'

GeminiEmbeddingProvider

Python
GeminiEmbeddingProvider(credentials: ProviderCredentials, *, config: GeminiConfig | None = None, http_client: AsyncHttpClient | None = None)
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_embedding_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: GeminiConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or GeminiConfig()
    merged_headers = {
        "x-goog-api-key": credentials.api_key,
        **credentials.extra_headers,
    }
    effective = replace(
        credentials,
        base_url=credentials.base_url or self._config.base_url,
        extra_headers=merged_headers,
    )
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider=self.provider_name,
        credentials=self._credentials,
        auth_scheme="none",
    )

provider_name class-attribute instance-attribute

Python
provider_name = 'gemini'

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_embedding_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

embed async

Python
embed(request: EmbeddingRequest) -> EmbeddingResponse
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_embedding_provider.py
Python
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
    if not request.inputs:
        raise ValueError("EmbeddingRequest.inputs cannot be empty")
    model_id = quote(request.model, safe="")
    url = f"/models/{model_id}:batchEmbedContents"
    payload: dict[str, Any] = {
        "requests": [
            {
                "model": f"models/{request.model}",
                "content": {"parts": [{"text": text}]},
                **(
                    {"outputDimensionality": request.dimensions}
                    if request.dimensions is not None
                    else {}
                ),
            }
            for text in request.inputs
        ]
    }
    data = await self._http.post_json(url, payload)
    embeddings = [
        [float(v) for v in (entry.get("values") or [])]
        for entry in (data.get("embeddings") or [])
    ]
    usage_meta = data.get("usageMetadata") or {}
    return EmbeddingResponse(
        model=request.model,
        provider=self.provider_name,
        embeddings=embeddings,
        usage=TokenUsage(
            prompt_tokens=int(usage_meta.get("promptTokenCount", 0)),
            completion_tokens=0,
            total_tokens=int(usage_meta.get("totalTokenCount", 0)),
        ),
    )

GeminiMultimodalProvider

Python
GeminiMultimodalProvider(credentials: ProviderCredentials, *, config: GeminiConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IMultimodalProvider using Gemini's generateContent.

Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_multimodal_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: GeminiConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or GeminiConfig()
    merged_headers = {
        "x-goog-api-key": credentials.api_key,
        **credentials.extra_headers,
    }
    effective = replace(
        credentials,
        base_url=credentials.base_url or self._config.base_url,
        extra_headers=merged_headers,
    )
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="gemini",
        credentials=self._credentials,
        auth_scheme="none",
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_multimodal_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

generate async

Python
generate(request: MultimodalRequest) -> MultimodalResponse
Source code in apogee_ai_providers/infrastructure/providers/gemini/gemini_multimodal_provider.py
Python
async def generate(self, request: MultimodalRequest) -> MultimodalResponse:
    parts = [self._to_part(inp) for inp in request.inputs]
    payload: dict[str, Any] = {"contents": [{"role": "user", "parts": parts}]}
    if request.instructions:
        payload["systemInstruction"] = {
            "role": "system",
            "parts": [{"text": request.instructions}],
        }
    gen_config: dict[str, Any] = {}
    if request.max_tokens is not None:
        gen_config["maxOutputTokens"] = request.max_tokens
    if request.temperature is not None:
        gen_config["temperature"] = request.temperature
    if gen_config:
        payload["generationConfig"] = gen_config

    path = f"/models/{request.model}:generateContent"
    data = await self._http.post_json(path, payload)

    text = ""
    for cand in data.get("candidates") or []:
        for part in (cand.get("content") or {}).get("parts") or []:
            if "text" in part:
                text += str(part["text"])
    usage_meta = data.get("usageMetadata") or {}
    return MultimodalResponse(
        id=str(data.get("responseId") or ""),
        model=str(data.get("modelVersion") or request.model),
        output_text=text,
        usage=TokenUsage(
            prompt_tokens=int(usage_meta.get("promptTokenCount", 0)),
            completion_tokens=int(usage_meta.get("candidatesTokenCount", 0)),
            total_tokens=int(usage_meta.get("totalTokenCount", 0)),
            reasoning_tokens=int(usage_meta.get("thoughtsTokenCount", 0)),
        ),
        provider="gemini",
    )

HuggingFaceChatProvider

Python
HuggingFaceChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'huggingface'

HuggingFaceConfig dataclass

Python
HuggingFaceConfig(base_url: str = 'https://router.huggingface.co/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'meta-llama/Llama-3.3-70B-Instruct')

Bases: OpenAIConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://router.huggingface.co/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'meta-llama/Llama-3.3-70B-Instruct'

KimiChatProvider

Python
KimiChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'kimi'

KimiConfig dataclass

Python
KimiConfig(base_url: str = 'https://api.moonshot.cn/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'moonshot-v1-8k')

Bases: OpenAICompatibleConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.moonshot.cn/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'moonshot-v1-8k'

MessageRole

Bases: str, Enum

SYSTEM class-attribute instance-attribute

Python
SYSTEM = 'system'

USER class-attribute instance-attribute

Python
USER = 'user'

ASSISTANT class-attribute instance-attribute

Python
ASSISTANT = 'assistant'

TOOL class-attribute instance-attribute

Python
TOOL = 'tool'

DEVELOPER class-attribute instance-attribute

Python
DEVELOPER = 'developer'

MetaChatProvider

Python
MetaChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'meta'

MetaConfig dataclass

Python
MetaConfig(base_url: str = 'https://api.llama.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'Llama-3.3-70B-Instruct')

Bases: OpenAIConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.llama.com/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'Llama-3.3-70B-Instruct'

Modality

Bases: str, Enum

TEXT class-attribute instance-attribute

Python
TEXT = 'text'

IMAGE class-attribute instance-attribute

Python
IMAGE = 'image'

AUDIO class-attribute instance-attribute

Python
AUDIO = 'audio'

VIDEO class-attribute instance-attribute

Python
VIDEO = 'video'

FILE class-attribute instance-attribute

Python
FILE = 'file'

MultimodalInput dataclass

Python
MultimodalInput(modality: Modality, text: str | None = None, url: str | None = None, data_b64: str | None = None, mime_type: str | None = None)

modality instance-attribute

Python
modality: Modality

text class-attribute instance-attribute

Python
text: str | None = None

url class-attribute instance-attribute

Python
url: str | None = None

data_b64 class-attribute instance-attribute

Python
data_b64: str | None = None

mime_type class-attribute instance-attribute

Python
mime_type: str | None = None

MultimodalRequest dataclass

Python
MultimodalRequest(model: str, inputs: list[MultimodalInput], instructions: str | None = None, max_tokens: int | None = None, temperature: float | None = None, metadata: dict[str, Any] = dict())

model instance-attribute

Python
model: str

inputs instance-attribute

Python
inputs: list[MultimodalInput]

instructions class-attribute instance-attribute

Python
instructions: str | None = None

max_tokens class-attribute instance-attribute

Python
max_tokens: int | None = None

temperature class-attribute instance-attribute

Python
temperature: float | None = None

metadata class-attribute instance-attribute

Python
metadata: dict[str, Any] = field(default_factory=dict)

MultimodalResponse dataclass

Python
MultimodalResponse(id: str, model: str, output_text: str, usage: TokenUsage = TokenUsage(), provider: str = '')

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

output_text instance-attribute

Python
output_text: str

usage class-attribute instance-attribute

Python
usage: TokenUsage = field(default_factory=TokenUsage)

provider class-attribute instance-attribute

Python
provider: str = ''

NvidiaChatProvider

Python
NvidiaChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'nvidia'

NvidiaConfig dataclass

Python
NvidiaConfig(base_url: str = 'https://integrate.api.nvidia.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'meta/llama-3.3-70b-instruct')

Bases: OpenAIConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://integrate.api.nvidia.com/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'meta/llama-3.3-70b-instruct'

OpenAIAudioConfig dataclass

Python
OpenAIAudioConfig(base_url: str = 'https://api.openai.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'gpt-4o-mini', speech_path: str = '/audio/speech', transcription_path: str = '/audio/transcriptions')

Bases: OpenAIConfig

speech_path class-attribute instance-attribute

Python
speech_path: str = '/audio/speech'

transcription_path class-attribute instance-attribute

Python
transcription_path: str = '/audio/transcriptions'

OpenAIAudioProvider

Python
OpenAIAudioProvider(credentials: ProviderCredentials, *, config: OpenAIAudioConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements ITextToSpeechProvider + ISpeechToTextProvider for OpenAI.

Source code in apogee_ai_providers/infrastructure/providers/openai/openai_audio_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAIAudioConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or OpenAIAudioConfig()
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=self._config.base_url)
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider=self.provider_name, credentials=self._credentials
    )

provider_name class-attribute instance-attribute

Python
provider_name = 'openai'

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_audio_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

synthesize async

Python
synthesize(request: TextToSpeechRequest) -> TextToSpeechResponse
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_audio_provider.py
Python
async def synthesize(self, request: TextToSpeechRequest) -> TextToSpeechResponse:
    payload = {
        "model": request.model,
        "input": request.text,
        "voice": request.voice,
        "response_format": request.audio_format,
    }
    if request.speed is not None:
        payload["speed"] = request.speed
    audio_bytes = await self._http.post_bytes(self._config.speech_path, payload)
    return TextToSpeechResponse(
        audio=audio_bytes,
        audio_format=request.audio_format,
        model=request.model,
        provider=self.provider_name,
    )

transcribe async

Python
transcribe(request: SpeechToTextRequest) -> SpeechToTextResponse
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_audio_provider.py
Python
async def transcribe(self, request: SpeechToTextRequest) -> SpeechToTextResponse:
    filename = f"audio.{request.audio_format}"
    mime = _audio_mime(request.audio_format)
    data: dict[str, str] = {"model": request.model}
    if request.language:
        data["language"] = request.language
    if request.prompt:
        data["prompt"] = request.prompt
    result = await self._http.post_multipart(
        self._config.transcription_path,
        data=data,
        files={"file": (filename, request.audio, mime)},
    )
    return SpeechToTextResponse(
        text=str(result.get("text", "")),
        model=request.model,
        provider=self.provider_name,
        language=str(result.get("language")) if result.get("language") else None,
        duration_seconds=(
            float(result["duration"]) if "duration" in result else None
        ),
    )

OpenAIChatProvider

Python
OpenAIChatProvider(credentials: ProviderCredentials, *, config: OpenAIConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IChatCompletionProvider against OpenAI's /chat/completions.

Source code in apogee_ai_providers/infrastructure/providers/openai/openai_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAIConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or OpenAIConfig()
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=self._config.base_url)
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="openai", credentials=self._credentials
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_chat_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    payload = request_to_openai_payload(replace(request, stream=False))
    data = await self._http.post_json(self._config.chat_completions_path, payload)
    return openai_response_to_domain(data)

stream async

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_chat_provider.py
Python
async def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]:
    payload = request_to_openai_payload(replace(request, stream=True))
    async for raw in self._http.stream_sse(self._config.chat_completions_path, payload):
        chunk = openai_chunk_to_domain(raw)
        if chunk is not None:
            yield chunk

OpenAICompatibleChatProvider

Python
OpenAICompatibleChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAIChatProvider

Subclass that re-stamps responses/chunks with the upstream provider name.

Subclasses set :attr:provider_name and override :attr:_default_config.

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name: str = 'openai-compat'

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    response = await super().complete(request)
    return replace(response, provider=self.provider_name)

stream async

Python
stream(request: ChatRequest)
Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
async def stream(self, request: ChatRequest):  # type: ignore[override]
    async for chunk in super().stream(request):
        yield ChatChunk(
            id=chunk.id,
            model=chunk.model,
            delta=chunk.delta,
            tool_calls=chunk.tool_calls,
            finish_reason=chunk.finish_reason,
        )

OpenAICompatibleConfig dataclass

Python
OpenAICompatibleConfig(base_url: str = 'https://api.openai.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'gpt-4o-mini')

Bases: OpenAIConfig

Config a generic OpenAI-compatible adapter understands.

OpenAIConfig dataclass

Python
OpenAIConfig(base_url: str = 'https://api.openai.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'gpt-4o-mini')

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.openai.com/v1'

chat_completions_path class-attribute instance-attribute

Python
chat_completions_path: str = '/chat/completions'

default_model class-attribute instance-attribute

Python
default_model: str = 'gpt-4o-mini'

OpenAIEmbeddingConfig dataclass

Python
OpenAIEmbeddingConfig(base_url: str = 'https://api.openai.com/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'gpt-4o-mini', embeddings_path: str = '/embeddings')

Bases: OpenAIConfig

embeddings_path class-attribute instance-attribute

Python
embeddings_path: str = '/embeddings'

OpenAIEmbeddingProvider

Python
OpenAIEmbeddingProvider(credentials: ProviderCredentials, *, config: OpenAIEmbeddingConfig | None = None, http_client: AsyncHttpClient | None = None)
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_embedding_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAIEmbeddingConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or OpenAIEmbeddingConfig()
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=self._config.base_url)
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider=self.provider_name, credentials=self._credentials
    )

provider_name class-attribute instance-attribute

Python
provider_name = 'openai'

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_embedding_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

embed async

Python
embed(request: EmbeddingRequest) -> EmbeddingResponse
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_embedding_provider.py
Python
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
    if not request.inputs:
        raise ValueError("EmbeddingRequest.inputs cannot be empty")
    payload: dict[str, Any] = {
        "model": request.model,
        "input": request.inputs,
        "encoding_format": request.encoding_format,
    }
    if request.dimensions is not None:
        payload["dimensions"] = request.dimensions
    if request.user:
        payload["user"] = request.user
    data = await self._http.post_json(self._config.embeddings_path, payload)
    return _openai_embeddings_to_domain(data, provider=self.provider_name)

OpenAIMultimodalProvider

Python
OpenAIMultimodalProvider(credentials: ProviderCredentials, *, config: OpenAIConfig | None = None, http_client: AsyncHttpClient | None = None)

Implements IMultimodalProvider. Supports text + image inputs natively.

Source code in apogee_ai_providers/infrastructure/providers/openai/openai_multimodal_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAIConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    self._config = config or OpenAIConfig()
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=self._config.base_url)
    self._credentials = effective
    self._http = http_client or AsyncHttpClient(
        provider="openai", credentials=self._credentials
    )

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_multimodal_provider.py
Python
async def aclose(self) -> None:
    await self._http.aclose()

generate async

Python
generate(request: MultimodalRequest) -> MultimodalResponse
Source code in apogee_ai_providers/infrastructure/providers/openai/openai_multimodal_provider.py
Python
async def generate(self, request: MultimodalRequest) -> MultimodalResponse:
    content_parts = [self._to_content_part(inp) for inp in request.inputs]
    messages: list[dict[str, Any]] = []
    if request.instructions:
        messages.append({"role": "system", "content": request.instructions})
    messages.append({"role": "user", "content": content_parts})
    payload: dict[str, Any] = {
        "model": request.model,
        "messages": messages,
    }
    if request.max_tokens is not None:
        payload["max_tokens"] = request.max_tokens
    if request.temperature is not None:
        payload["temperature"] = request.temperature
    data = await self._http.post_json(self._config.chat_completions_path, payload)
    choices = data.get("choices") or []
    text = ""
    if choices:
        text = str((choices[0].get("message") or {}).get("content") or "")
    usage = data.get("usage") or {}
    return MultimodalResponse(
        id=str(data.get("id", "")),
        model=str(data.get("model", "")),
        output_text=text,
        usage=TokenUsage(
            prompt_tokens=int(usage.get("prompt_tokens", 0)),
            completion_tokens=int(usage.get("completion_tokens", 0)),
            total_tokens=int(usage.get("total_tokens", 0)),
        ),
        provider="openai",
    )

OpenRouterChatProvider

Python
OpenRouterChatProvider(credentials: ProviderCredentials, *, config: OpenRouterConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAIChatProvider

Thin wrapper: OpenAI-compatible endpoint at openrouter.ai.

Source code in apogee_ai_providers/infrastructure/providers/openrouter/openrouter_chat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenRouterConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or OpenRouterConfig()
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    # Override provider string stored by the HTTP client for error attribution.
    self._http._provider = "openrouter"  # noqa: SLF001

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/infrastructure/providers/openrouter/openrouter_chat_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse:
    response = await super().complete(request)
    return replace(response, provider="openrouter")

OpenRouterConfig dataclass

Python
OpenRouterConfig(base_url: str = 'https://openrouter.ai/api/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'openai/gpt-4o-mini')

Bases: OpenAIConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://openrouter.ai/api/v1'

chat_completions_path class-attribute instance-attribute

Python
chat_completions_path: str = '/chat/completions'

default_model class-attribute instance-attribute

Python
default_model: str = 'openai/gpt-4o-mini'

Provider

Bases: str, Enum

OPENAI class-attribute instance-attribute

Python
OPENAI = 'openai'

ANTHROPIC class-attribute instance-attribute

Python
ANTHROPIC = 'anthropic'

GEMINI class-attribute instance-attribute

Python
GEMINI = 'gemini'

OPENROUTER class-attribute instance-attribute

Python
OPENROUTER = 'openrouter'

BEDROCK class-attribute instance-attribute

Python
BEDROCK = 'bedrock'

AZURE class-attribute instance-attribute

Python
AZURE = 'azure'

DEEPSEEK class-attribute instance-attribute

Python
DEEPSEEK = 'deepseek'

XAI class-attribute instance-attribute

Python
XAI = 'xai'

META class-attribute instance-attribute

Python
META = 'meta'

HUGGINGFACE class-attribute instance-attribute

Python
HUGGINGFACE = 'huggingface'

KIMI class-attribute instance-attribute

Python
KIMI = 'kimi'

QWEN class-attribute instance-attribute

Python
QWEN = 'qwen'

ZAI class-attribute instance-attribute

Python
ZAI = 'zai'

NVIDIA class-attribute instance-attribute

Python
NVIDIA = 'nvidia'

ProviderCredentials dataclass

Python
ProviderCredentials(api_key: str, base_url: str | None = None, organization: str | None = None, project: str | None = None, timeout: float = 60.0, extra_headers: dict[str, str] = dict())

api_key instance-attribute

Python
api_key: str

base_url class-attribute instance-attribute

Python
base_url: str | None = None

organization class-attribute instance-attribute

Python
organization: str | None = None

project class-attribute instance-attribute

Python
project: str | None = None

timeout class-attribute instance-attribute

Python
timeout: float = 60.0

extra_headers class-attribute instance-attribute

Python
extra_headers: dict[str, str] = field(default_factory=dict)

ProviderFactory

Builds a chat provider from a Provider value or canonical string.

build staticmethod

Python
build(provider: Provider | str, credentials: ProviderCredentials) -> IChatCompletionProvider
Source code in apogee_ai_providers/infrastructure/factory/provider_factory.py
Python
@staticmethod
def build(
    provider: Provider | str,
    credentials: ProviderCredentials,
) -> IChatCompletionProvider:
    key = provider.value if isinstance(provider, Provider) else str(provider).lower()
    if key == Provider.OPENAI.value:
        return OpenAIChatProvider(credentials)
    if key == Provider.GEMINI.value:
        return GeminiChatProvider(credentials)
    if key == Provider.BEDROCK.value:
        return BedrockChatProvider(credentials)
    if key == Provider.ANTHROPIC.value:
        return AnthropicChatProvider(credentials)
    if key == Provider.OPENROUTER.value:
        return OpenRouterChatProvider(credentials)
    if key == Provider.AZURE.value:
        return AzureOpenAIChatProvider(credentials)
    if key in _OPENAI_COMPAT_PROVIDERS:
        return _OPENAI_COMPAT_PROVIDERS[key](credentials)
    if key in STUB_PROVIDERS:
        return STUB_PROVIDERS[key](credentials)
    raise ValueError(f"Unknown provider: {key}")

QwenChatProvider

Python
QwenChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'qwen'

QwenConfig dataclass

Python
QwenConfig(base_url: str = 'https://dashscope.aliyuncs.com/compatible-mode/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'qwen-plus')

Bases: OpenAICompatibleConfig

Alibaba DashScope OpenAI-compatible endpoint.

base_url class-attribute instance-attribute

Python
base_url: str = 'https://dashscope.aliyuncs.com/compatible-mode/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'qwen-plus'

SpeechToTextRequest dataclass

Python
SpeechToTextRequest(model: str, audio: bytes, audio_format: str = 'wav', language: str | None = None, prompt: str | None = None)

Transcribe spoken audio to text.

model instance-attribute

Python
model: str

audio instance-attribute

Python
audio: bytes

audio_format class-attribute instance-attribute

Python
audio_format: str = 'wav'

language class-attribute instance-attribute

Python
language: str | None = None

prompt class-attribute instance-attribute

Python
prompt: str | None = None

SpeechToTextResponse dataclass

Python
SpeechToTextResponse(text: str, model: str, provider: str, language: str | None = None, duration_seconds: float | None = None)

Transcript text plus optional metadata returned by the provider.

text instance-attribute

Python
text: str

model instance-attribute

Python
model: str

provider instance-attribute

Python
provider: str

language class-attribute instance-attribute

Python
language: str | None = None

duration_seconds class-attribute instance-attribute

Python
duration_seconds: float | None = None

TextToSpeechRequest dataclass

Python
TextToSpeechRequest(model: str, text: str, voice: str = 'alloy', audio_format: str = 'mp3', speed: float | None = None)

Synthesise spoken audio from text.

model instance-attribute

Python
model: str

text instance-attribute

Python
text: str

voice class-attribute instance-attribute

Python
voice: str = 'alloy'

audio_format class-attribute instance-attribute

Python
audio_format: str = 'mp3'

speed class-attribute instance-attribute

Python
speed: float | None = None

TextToSpeechResponse dataclass

Python
TextToSpeechResponse(audio: bytes, audio_format: str, model: str, provider: str)

Synthesised audio bytes plus the format requested.

audio instance-attribute

Python
audio: bytes

audio_format instance-attribute

Python
audio_format: str

model instance-attribute

Python
model: str

provider instance-attribute

Python
provider: str

ThinkingConfig dataclass

Python
ThinkingConfig(enabled: bool = False, budget_tokens: int | None = None, effort: str | None = None)

enabled class-attribute instance-attribute

Python
enabled: bool = False

budget_tokens class-attribute instance-attribute

Python
budget_tokens: int | None = None

effort class-attribute instance-attribute

Python
effort: str | None = None

TokenUsage dataclass

Python
TokenUsage(prompt_tokens: int = 0, completion_tokens: int = 0, total_tokens: int = 0, reasoning_tokens: int = 0)

prompt_tokens class-attribute instance-attribute

Python
prompt_tokens: int = 0

completion_tokens class-attribute instance-attribute

Python
completion_tokens: int = 0

total_tokens class-attribute instance-attribute

Python
total_tokens: int = 0

reasoning_tokens class-attribute instance-attribute

Python
reasoning_tokens: int = 0

ToolCall dataclass

Python
ToolCall(id: str, name: str, arguments: str)

id instance-attribute

Python
id: str

name instance-attribute

Python
name: str

arguments instance-attribute

Python
arguments: str

ToolDefinition dataclass

Python
ToolDefinition(name: str, description: str, parameters: dict[str, Any])

Canonical (OpenAI-compatible) tool/function schema.

name instance-attribute

Python
name: str

description instance-attribute

Python
description: str

parameters instance-attribute

Python
parameters: dict[str, Any]

XAIChatProvider

Python
XAIChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'xai'

XAIConfig dataclass

Python
XAIConfig(base_url: str = 'https://api.x.ai/v1', chat_completions_path: str = '/chat/completions', default_model: str = 'grok-2-latest')

Bases: OpenAICompatibleConfig

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.x.ai/v1'

default_model class-attribute instance-attribute

Python
default_model: str = 'grok-2-latest'

ZAIChatProvider

Python
ZAIChatProvider(credentials: ProviderCredentials, *, config: OpenAICompatibleConfig | None = None, http_client: AsyncHttpClient | None = None)

Bases: OpenAICompatibleChatProvider

Source code in apogee_ai_providers/infrastructure/providers/openai_compat/openai_compat_provider.py
Python
def __init__(
    self,
    credentials: ProviderCredentials,
    *,
    config: OpenAICompatibleConfig | None = None,
    http_client: AsyncHttpClient | None = None,
) -> None:
    cfg = config or self._default_config
    effective = credentials
    if credentials.base_url is None:
        effective = replace(credentials, base_url=cfg.base_url)
    super().__init__(effective, config=cfg, http_client=http_client)
    self._http._provider = self.provider_name  # noqa: SLF001

provider_name class-attribute instance-attribute

Python
provider_name = 'zai'

ZAIConfig dataclass

Python
ZAIConfig(base_url: str = 'https://api.z.ai/api/paas/v4', chat_completions_path: str = '/chat/completions', default_model: str = 'glm-4-plus')

Bases: OpenAICompatibleConfig

Z.ai (BigModel / GLM) OpenAI-compatible endpoint.

base_url class-attribute instance-attribute

Python
base_url: str = 'https://api.z.ai/api/paas/v4'

default_model class-attribute instance-attribute

Python
default_model: str = 'glm-4-plus'

Other · DTOs

ChatChoiceDTO

Bases: BaseModel

index instance-attribute

Python
index: int

message instance-attribute

Python
message: ChatMessageDTO

finish_reason class-attribute instance-attribute

Python
finish_reason: FinishReason | None = None

ChatChunkDTO

Bases: BaseModel

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

delta class-attribute instance-attribute

Python
delta: str = ''

tool_calls class-attribute instance-attribute

Python
tool_calls: list[ToolCallDTO] = Field(default_factory=list)

finish_reason class-attribute instance-attribute

Python
finish_reason: FinishReason | None = None

ChatMessageDTO

Bases: BaseModel

role instance-attribute

Python
role: MessageRole

content class-attribute instance-attribute

Python
content: str | None = None

name class-attribute instance-attribute

Python
name: str | None = None

tool_calls class-attribute instance-attribute

Python
tool_calls: list[ToolCallDTO] = Field(default_factory=list)

tool_call_id class-attribute instance-attribute

Python
tool_call_id: str | None = None

ChatRequestDTO

Bases: BaseModel

model instance-attribute

Python
model: str

messages instance-attribute

Python
messages: list[ChatMessageDTO]

tools class-attribute instance-attribute

Python
tools: list[ToolDefinitionDTO] = Field(default_factory=list)

tool_choice class-attribute instance-attribute

Python
tool_choice: str | dict[str, Any] | None = None

thinking class-attribute instance-attribute

Python
thinking: ThinkingConfigDTO | None = None

stream class-attribute instance-attribute

Python
stream: bool = False

temperature class-attribute instance-attribute

Python
temperature: float | None = None

top_p class-attribute instance-attribute

Python
top_p: float | None = None

max_tokens class-attribute instance-attribute

Python
max_tokens: int | None = None

stop class-attribute instance-attribute

Python
stop: list[str] | None = None

response_format class-attribute instance-attribute

Python
response_format: dict[str, Any] | None = None

seed class-attribute instance-attribute

Python
seed: int | None = None

user class-attribute instance-attribute

Python
user: str | None = None

metadata class-attribute instance-attribute

Python
metadata: dict[str, Any] = Field(default_factory=dict)

ChatResponseDTO

Bases: BaseModel

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

provider class-attribute instance-attribute

Python
provider: str = ''

choices instance-attribute

Python
choices: list[ChatChoiceDTO]

usage class-attribute instance-attribute

Python
usage: TokenUsageDTO = Field(default_factory=TokenUsageDTO)

MultimodalInputDTO

Bases: BaseModel

modality instance-attribute

Python
modality: Modality

text class-attribute instance-attribute

Python
text: str | None = None

url class-attribute instance-attribute

Python
url: str | None = None

data_b64 class-attribute instance-attribute

Python
data_b64: str | None = None

mime_type class-attribute instance-attribute

Python
mime_type: str | None = None

MultimodalRequestDTO

Bases: BaseModel

model instance-attribute

Python
model: str

inputs instance-attribute

Python
inputs: list[MultimodalInputDTO]

instructions class-attribute instance-attribute

Python
instructions: str | None = None

max_tokens class-attribute instance-attribute

Python
max_tokens: int | None = None

temperature class-attribute instance-attribute

Python
temperature: float | None = None

metadata class-attribute instance-attribute

Python
metadata: dict[str, Any] = Field(default_factory=dict)

MultimodalResponseDTO

Bases: BaseModel

id instance-attribute

Python
id: str

model instance-attribute

Python
model: str

provider class-attribute instance-attribute

Python
provider: str = ''

output_text instance-attribute

Python
output_text: str

usage class-attribute instance-attribute

Python
usage: TokenUsageDTO = Field(default_factory=TokenUsageDTO)

ThinkingConfigDTO

Bases: BaseModel

enabled class-attribute instance-attribute

Python
enabled: bool = False

budget_tokens class-attribute instance-attribute

Python
budget_tokens: int | None = None

effort class-attribute instance-attribute

Python
effort: str | None = None

TokenUsageDTO

Bases: BaseModel

prompt_tokens class-attribute instance-attribute

Python
prompt_tokens: int = 0

completion_tokens class-attribute instance-attribute

Python
completion_tokens: int = 0

total_tokens class-attribute instance-attribute

Python
total_tokens: int = 0

reasoning_tokens class-attribute instance-attribute

Python
reasoning_tokens: int = 0

ToolCallDTO

Bases: BaseModel

id instance-attribute

Python
id: str

name instance-attribute

Python
name: str

arguments instance-attribute

Python
arguments: str

ToolDefinitionDTO

Bases: BaseModel

name instance-attribute

Python
name: str

description instance-attribute

Python
description: str

parameters class-attribute instance-attribute

Python
parameters: dict[str, Any] = Field(default_factory=dict)

Other · Exceptions

ProviderAuthError

Python
ProviderAuthError(message: str, *, provider: str, status: int | None = None, code: str | None = None, raw: Any = None)

Bases: ProviderError

Source code in apogee_ai_providers/domain/exceptions/provider_error.py
Python
def __init__(
    self,
    message: str,
    *,
    provider: str,
    status: int | None = None,
    code: str | None = None,
    raw: Any = None,
) -> None:
    super().__init__(message)
    self.provider = provider
    self.status = status
    self.code = code
    self.raw = raw

ProviderError

Python
ProviderError(message: str, *, provider: str, status: int | None = None, code: str | None = None, raw: Any = None)

Bases: Exception

Raised when a provider call fails.

Attributes:

Name Type Description
provider

Provider identifier (e.g. "openai").

status

HTTP status code, if available.

code

Provider-specific error code, if available.

raw

Raw response payload, if available.

Source code in apogee_ai_providers/domain/exceptions/provider_error.py
Python
def __init__(
    self,
    message: str,
    *,
    provider: str,
    status: int | None = None,
    code: str | None = None,
    raw: Any = None,
) -> None:
    super().__init__(message)
    self.provider = provider
    self.status = status
    self.code = code
    self.raw = raw

provider instance-attribute

Python
provider = provider

status instance-attribute

Python
status = status

code instance-attribute

Python
code = code

raw instance-attribute

Python
raw = raw

ProviderRateLimitError

Python
ProviderRateLimitError(*args, retry_after: float | None = None, **kwargs)

Bases: ProviderError

Source code in apogee_ai_providers/domain/exceptions/provider_rate_limit_error.py
Python
def __init__(self, *args, retry_after: float | None = None, **kwargs) -> None:
    super().__init__(*args, **kwargs)
    self.retry_after = retry_after

retry_after instance-attribute

Python
retry_after = retry_after

ProviderTimeoutError

Python
ProviderTimeoutError(message: str, *, provider: str, status: int | None = None, code: str | None = None, raw: Any = None)

Bases: ProviderError

Source code in apogee_ai_providers/domain/exceptions/provider_error.py
Python
def __init__(
    self,
    message: str,
    *,
    provider: str,
    status: int | None = None,
    code: str | None = None,
    raw: Any = None,
) -> None:
    super().__init__(message)
    self.provider = provider
    self.status = status
    self.code = code
    self.raw = raw

ProviderValidationError

Python
ProviderValidationError(message: str, *, provider: str, status: int | None = None, code: str | None = None, raw: Any = None)

Bases: ProviderError

Source code in apogee_ai_providers/domain/exceptions/provider_error.py
Python
def __init__(
    self,
    message: str,
    *,
    provider: str,
    status: int | None = None,
    code: str | None = None,
    raw: Any = None,
) -> None:
    super().__init__(message)
    self.provider = provider
    self.status = status
    self.code = code
    self.raw = raw

Other · Protocols (ports)

IChatCompletionProvider

Bases: Protocol

Async chat-completion contract. Any provider implements both methods.

complete async

Python
complete(request: ChatRequest) -> ChatResponse
Source code in apogee_ai_providers/domain/services/i_chat_completion_provider.py
Python
async def complete(self, request: ChatRequest) -> ChatResponse: ...

stream

Python
stream(request: ChatRequest) -> AsyncIterator[ChatChunk]
Source code in apogee_ai_providers/domain/services/i_chat_completion_provider.py
Python
def stream(self, request: ChatRequest) -> AsyncIterator[ChatChunk]: ...

IEmbeddingProvider

Bases: Protocol

embed async

Python
embed(request: EmbeddingRequest) -> EmbeddingResponse
Source code in apogee_ai_providers/domain/services/i_embedding_provider.py
Python
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse: ...

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/domain/services/i_embedding_provider.py
Python
async def aclose(self) -> None: ...

IMultimodalProvider

Bases: Protocol

generate async

Python
generate(request: MultimodalRequest) -> MultimodalResponse
Source code in apogee_ai_providers/domain/services/i_multimodal_provider.py
Python
async def generate(self, request: MultimodalRequest) -> MultimodalResponse: ...

ISpeechToTextProvider

Bases: Protocol

transcribe async

Python
transcribe(request: SpeechToTextRequest) -> SpeechToTextResponse
Source code in apogee_ai_providers/domain/services/i_audio_provider.py
Python
async def transcribe(self, request: SpeechToTextRequest) -> SpeechToTextResponse: ...

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/domain/services/i_audio_provider.py
Python
async def aclose(self) -> None: ...

ITextToSpeechProvider

Bases: Protocol

synthesize async

Python
synthesize(request: TextToSpeechRequest) -> TextToSpeechResponse
Source code in apogee_ai_providers/domain/services/i_audio_provider.py
Python
async def synthesize(self, request: TextToSpeechRequest) -> TextToSpeechResponse: ...

aclose async

Python
aclose() -> None
Source code in apogee_ai_providers/domain/services/i_audio_provider.py
Python
async def aclose(self) -> None: ...

Other · Use cases

ExecuteChatCompletionUseCase

Python
ExecuteChatCompletionUseCase(provider: IChatCompletionProvider)
Source code in apogee_ai_providers/application/use_cases/execute_chat_completion_use_case.py
Python
def __init__(self, provider: IChatCompletionProvider) -> None:
    self._provider = provider

execute async

Python
execute(request: ChatRequestDTO) -> ChatResponseDTO
Source code in apogee_ai_providers/application/use_cases/execute_chat_completion_use_case.py
Python
async def execute(self, request: ChatRequestDTO) -> ChatResponseDTO:
    domain_request = dto_to_chat_request(request)
    response = await self._provider.complete(domain_request)
    return chat_response_to_dto(response)

ExecuteMultimodalUseCase

Python
ExecuteMultimodalUseCase(provider: IMultimodalProvider)
Source code in apogee_ai_providers/application/use_cases/execute_multimodal_use_case.py
Python
def __init__(self, provider: IMultimodalProvider) -> None:
    self._provider = provider

execute async

Python
execute(request: MultimodalRequestDTO) -> MultimodalResponseDTO
Source code in apogee_ai_providers/application/use_cases/execute_multimodal_use_case.py
Python
async def execute(self, request: MultimodalRequestDTO) -> MultimodalResponseDTO:
    domain_request = dto_to_multimodal_request(request)
    response = await self._provider.generate(domain_request)
    return multimodal_response_to_dto(response)

StreamChatCompletionUseCase

Python
StreamChatCompletionUseCase(provider: IChatCompletionProvider)
Source code in apogee_ai_providers/application/use_cases/stream_chat_completion_use_case.py
Python
def __init__(self, provider: IChatCompletionProvider) -> None:
    self._provider = provider

execute async

Python
execute(request: ChatRequestDTO) -> AsyncIterator[ChatChunkDTO]
Source code in apogee_ai_providers/application/use_cases/stream_chat_completion_use_case.py
Python
async def execute(self, request: ChatRequestDTO) -> AsyncIterator[ChatChunkDTO]:
    domain_request = replace(dto_to_chat_request(request), stream=True)
    async for chunk in self._provider.stream(domain_request):
        yield chat_chunk_to_dto(chunk)