From f405764937f4df3b03ae7596d3716089d00cd88a Mon Sep 17 00:00:00 2001 From: kamilbenkirane Date: Fri, 6 Feb 2026 12:43:34 +0100 Subject: [PATCH 1/2] chore(deps): make google-auth an optional dependency under `celeste-ai[gcp]` Move `google-auth[requests]` from core dependencies to an optional extra, reducing the default install footprint by ~24.5 MB (google-auth pulls in cryptography, pyasn1, rsa, etc.). Users who need Vertex AI or Cloud TTS now install: pip install "celeste-ai[gcp]" A clear `MissingDependencyError` is raised at runtime if `google-auth` is not installed when `GoogleADC` credentials are used. Addresses feedback from @Seluj78 in #130. --- pyproject.toml | 4 +++- src/celeste/exceptions.py | 11 +++++++++++ src/celeste/providers/google/auth.py | 26 ++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 729d42b2..83d27ee4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,9 +27,11 @@ dependencies = [ "websockets>=15.0", "asgiref>=3.11.0", "filetype>=1.2.0", - "google-auth[requests]>=2.0.0", ] +[project.optional-dependencies] +gcp = ["google-auth[requests]>=2.0.0"] + [project.urls] Homepage = "https://withceleste.ai" Documentation = "https://withceleste.ai/docs" diff --git a/src/celeste/exceptions.py b/src/celeste/exceptions.py index 08f6cb5f..f2812745 100644 --- a/src/celeste/exceptions.py +++ b/src/celeste/exceptions.py @@ -170,6 +170,16 @@ def __init__(self) -> None: super().__init__("Stream completed but no chunks were produced") +class MissingDependencyError(Error): + """Raised when a required optional dependency is not installed.""" + + def __init__(self, *, library: str, extra: str) -> None: + super().__init__( + f"Missing required dependency `{library}`. " + f'Install it with: pip install "celeste-ai[{extra}]"' + ) + + class CredentialsError(Error): """Errors related to API credentials.""" @@ -217,6 +227,7 @@ def __init__(self, parameter: str, model_id: str) -> None: "ConstraintViolationError", "Error", "MissingCredentialsError", + "MissingDependencyError", "ModalityNotFoundError", "ModelNotFoundError", "StreamEmptyError", diff --git a/src/celeste/providers/google/auth.py b/src/celeste/providers/google/auth.py index 75d7253e..de276567 100644 --- a/src/celeste/providers/google/auth.py +++ b/src/celeste/providers/google/auth.py @@ -6,6 +6,9 @@ from celeste.auth import Authentication +VERTEX_BASE_URL = "https://{location}-aiplatform.googleapis.com" +VERTEX_GLOBAL_BASE_URL = "https://aiplatform.googleapis.com" + class GoogleADC(Authentication): """Google Application Default Credentials authentication. @@ -18,6 +21,7 @@ class GoogleADC(Authentication): scopes: ClassVar[list[str]] = ["https://www.googleapis.com/auth/cloud-platform"] project_id: str | None = None + location: str = "global" model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) @@ -35,8 +39,13 @@ def get_headers(self) -> dict[str, str]: def _get_access_token(self) -> tuple[str, str | None]: """Get OAuth access token using Application Default Credentials.""" - import google.auth - import google.auth.transport.requests + try: + import google.auth + import google.auth.transport.requests + except ImportError: + from celeste.exceptions import MissingDependencyError + + raise MissingDependencyError(library="google-auth", extra="gcp") from None if self._credentials is None: self._credentials, self._project = google.auth.default(scopes=self.scopes) @@ -47,5 +56,18 @@ def _get_access_token(self) -> tuple[str, str | None]: return self._credentials.token, self.project_id or self._project + @property + def resolved_project_id(self) -> str | None: + """Return effective project_id (explicit or from ADC credentials).""" + if self._credentials is None: + self._get_access_token() + return self.project_id or self._project + + def get_vertex_base_url(self) -> str: + """Return the Vertex AI base URL for the configured location.""" + if self.location == "global": + return VERTEX_GLOBAL_BASE_URL + return VERTEX_BASE_URL.format(location=self.location) + __all__ = ["GoogleADC"] From e330d7fbbfca5dc78ae98868ae6821f5fd0e0210 Mon Sep 17 00:00:00 2001 From: kamilbenkirane Date: Fri, 6 Feb 2026 13:02:40 +0100 Subject: [PATCH 2/2] fix(review): address PR feedback on auth.py - Move MissingDependencyError import to top-level (not deferred) - Use `from e` instead of `from None` to preserve exception chain --- src/celeste/providers/google/auth.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/celeste/providers/google/auth.py b/src/celeste/providers/google/auth.py index de276567..24eba8d8 100644 --- a/src/celeste/providers/google/auth.py +++ b/src/celeste/providers/google/auth.py @@ -5,6 +5,7 @@ from pydantic import ConfigDict from celeste.auth import Authentication +from celeste.exceptions import MissingDependencyError VERTEX_BASE_URL = "https://{location}-aiplatform.googleapis.com" VERTEX_GLOBAL_BASE_URL = "https://aiplatform.googleapis.com" @@ -42,10 +43,8 @@ def _get_access_token(self) -> tuple[str, str | None]: try: import google.auth import google.auth.transport.requests - except ImportError: - from celeste.exceptions import MissingDependencyError - - raise MissingDependencyError(library="google-auth", extra="gcp") from None + except ImportError as e: + raise MissingDependencyError(library="google-auth", extra="gcp") from e if self._credentials is None: self._credentials, self._project = google.auth.default(scopes=self.scopes)