fix: resolve api_key at request time to restore 0.2.x override semantics

0.2.8 stored self.api_key on the client and _headers() re-read it per
request, so reading client.api_key and reassigning it after construction
both worked. The refactor handed the key to the internal backends and never
set it on PageIndexClient: reads raised AttributeError and reassignment
silently kept requests on the old key. Same shape as the BASE_URL snapshot
fixed in dea211b — apply the same callable-indirection pattern to api_key
on both CloudBackend and LegacyCloudAPI (which also stops CloudBackend
snapshotting its headers dict at construction).
This commit is contained in:
Ray 2026-07-22 12:28:54 +08:00
parent 80493ccf62
commit a9bb019d05
3 changed files with 28 additions and 10 deletions

View file

@ -30,17 +30,24 @@ def _as_int(value):
class CloudBackend: class CloudBackend:
def __init__(self, api_key: str, base_url: str | Callable[[], str] | None = None): def __init__(self, api_key: str | Callable[[], str],
base_url: str | Callable[[], str] | None = None):
self._api_key = api_key self._api_key = api_key
self._base_url = base_url or API_BASE self._base_url = base_url or API_BASE
self._headers = {"api_key": api_key}
self._folder_id_cache: dict[str, str | None] = {} self._folder_id_cache: dict[str, str | None] = {}
self._folder_warning_shown = False self._folder_warning_shown = False
@property
def api_key(self) -> str:
return self._api_key() if callable(self._api_key) else self._api_key
@property @property
def base_url(self) -> str: def base_url(self) -> str:
return self._base_url() if callable(self._base_url) else self._base_url return self._base_url() if callable(self._base_url) else self._base_url
def _headers(self) -> dict[str, str]:
return {"api_key": self.api_key}
# ── HTTP helpers ────────────────────────────────────────────────────── # ── HTTP helpers ──────────────────────────────────────────────────────
# Folder API statuses meaning "folders are not available on this account" # Folder API statuses meaning "folders are not available on this account"
@ -77,7 +84,7 @@ class CloudBackend:
if hasattr(fobj, "seek"): if hasattr(fobj, "seek"):
fobj.seek(0) fobj.seek(0)
try: try:
resp = requests.request(method, url, headers=self._headers, **kwargs) resp = requests.request(method, url, headers=self._headers(), **kwargs)
if resp.status_code in (429, 500, 502, 503): if resp.status_code in (429, 500, 502, 503):
last_status = resp.status_code last_status = resp.status_code
if attempt == retries - 1: if attempt == retries - 1:
@ -452,7 +459,7 @@ class CloudBackend:
) )
if not doc_id: if not doc_id:
raise ValueError("collection has no documents to query") raise ValueError("collection has no documents to query")
headers = self._headers headers = self._headers()
base_url = self.base_url base_url = self.base_url
# Queue carries QueryEvent, an Exception to re-raise, or None (end). # Queue carries QueryEvent, an Exception to re-raise, or None (end).
queue: asyncio.Queue[QueryEvent | Exception | None] = asyncio.Queue() queue: asyncio.Queue[QueryEvent | Exception | None] = asyncio.Queue()

View file

@ -74,11 +74,13 @@ class PageIndexClient:
def _init_cloud(self, api_key: str): def _init_cloud(self, api_key: str):
from .backend.cloud import CloudBackend from .backend.cloud import CloudBackend
from .cloud_api import LegacyCloudAPI from .cloud_api import LegacyCloudAPI
# Callable: re-read per request so post-construction BASE_URL # Callables: re-read per request so post-construction BASE_URL /
# reassignment (a 0.2.x pattern) still applies. # api_key reassignment (0.2.x patterns) still applies.
self.api_key = api_key
base_url = lambda: self.BASE_URL base_url = lambda: self.BASE_URL
self._backend = CloudBackend(api_key=api_key, base_url=base_url) api_key_ref = lambda: self.api_key
self._legacy_cloud_api = LegacyCloudAPI(api_key=api_key, base_url=base_url) self._backend = CloudBackend(api_key=api_key_ref, base_url=base_url)
self._legacy_cloud_api = LegacyCloudAPI(api_key=api_key_ref, base_url=base_url)
def _init_local(self, model: str = None, retrieve_model: str = None, def _init_local(self, model: str = None, retrieve_model: str = None,
storage_path: str = None, storage=None, storage_path: str = None, storage=None,

View file

@ -19,10 +19,19 @@ class LegacyCloudAPI:
BASE_URL = API_BASE BASE_URL = API_BASE
def __init__(self, api_key: str, base_url: str | Callable[[], str] | None = None): def __init__(self, api_key: str | Callable[[], str],
self.api_key = api_key base_url: str | Callable[[], str] | None = None):
self._api_key = api_key
self._base_url = base_url or self.BASE_URL self._base_url = base_url or self.BASE_URL
@property
def api_key(self) -> str:
return self._api_key() if callable(self._api_key) else self._api_key
@api_key.setter
def api_key(self, value: str | Callable[[], str]) -> None:
self._api_key = value
@property @property
def base_url(self) -> str: def base_url(self) -> str:
return self._base_url() if callable(self._base_url) else self._base_url return self._base_url() if callable(self._base_url) else self._base_url