Files
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

105 lines
3.8 KiB
Python

"""Dataclasses describing providers and resolved backends.
This module is the leaf of the package's dependency graph: it defines the two
value types the rest of the package builds on, and imports nothing from its
siblings.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
__all__ = ["Backend", "Provider"]
@dataclass(frozen=True)
class Provider:
"""Static description of an OpenAI-compatible backend.
Attributes:
name: Canonical provider name, e.g. ``"kimi"``.
base_url: Default API endpoint, used when no override is set.
default_model: Model id used when the caller does not pick one.
key_vars: Environment variables holding the API key, tried in order.
The first non-empty one wins; later entries exist for backwards
compatibility.
base_url_var: Environment variable overriding ``base_url``, for
self-hosted or regional deployments. ``None`` if not overridable.
requires_key: Whether a missing key is an error. Local runtimes such as
Ollama accept any placeholder, so they set this to ``False``.
namespaces_models: Whether this backend expects vendor-namespaced model
ids such as ``openai/gpt-4o`` rather than bare ones. True for
aggregators that resell many vendors' models; a bare id given to
one of these is mapped before the request goes out.
This describes *model-id formatting only*. It says nothing about
which endpoint to call or whose credentials are valid -- an
aggregator sharing OpenRouter's id format still has its own
``base_url`` and its own key, and is never routed through
OpenRouter on that basis.
"""
name: str
base_url: str
default_model: str
key_vars: tuple[str, ...] = ()
base_url_var: str | None = None
requires_key: bool = True
namespaces_models: bool = False
def api_key(self) -> str:
"""Read this provider's API key from the environment.
Returns:
The first non-empty value among ``key_vars``, stripped of
surrounding whitespace, or ``""`` when none is set.
"""
for var in self.key_vars:
value = os.getenv(var, "").strip()
if value:
return value
return ""
def resolved_base_url(self) -> str:
"""Return the endpoint to call, honouring any environment override.
Returns:
The value of ``base_url_var`` if that variable is set and non-empty,
otherwise the built-in ``base_url``.
"""
if self.base_url_var:
return os.getenv(self.base_url_var, "").strip() or self.base_url
return self.base_url
@dataclass(frozen=True)
class Backend:
"""A resolved, ready-to-use OpenAI-compatible endpoint.
Attributes:
api_key: Credential for ``base_url``. Never empty -- local runtimes get
a placeholder, because the OpenAI client rejects an empty key.
base_url: The endpoint to send requests to.
model: Model id valid at ``base_url``. Note this may differ from the
requested id when the request was rerouted through OpenRouter.
provider: The provider that was requested, after alias resolution.
using_openrouter: Whether the request is going through OpenRouter
rather than the provider's own API.
"""
api_key: str
base_url: str
model: str
provider: str
using_openrouter: bool
def __iter__(self):
"""Unpack as the 4-tuple the pre-registry chapter helpers returned.
Returns:
An iterator over ``api_key``, ``base_url``, ``model`` and
``using_openrouter``, in that order.
"""
return iter((self.api_key, self.base_url, self.model, self.using_openrouter))