lmux-google 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: lmux-google
3
+ Version: 0.7.0
4
+ Summary: Google (Gemini) provider for lmux
5
+ Keywords: llm,ai,google,gemini,vertex,language-model
6
+ Author: Connor Luebbehusen
7
+ Author-email: Connor Luebbehusen <connor@luebbehusen.dev>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
12
+ Classifier: Typing :: Typed
13
+ Requires-Dist: lmux~=0.7
14
+ Requires-Dist: google-genai~=1.0
15
+ Requires-Python: >=3.13
16
+ Project-URL: Homepage, https://github.com/cluebbehusen/lmux
17
+ Project-URL: Source, https://github.com/cluebbehusen/lmux/tree/main/packages/lmux-google
18
+ Project-URL: Issues, https://github.com/cluebbehusen/lmux/issues
19
+ Description-Content-Type: text/markdown
20
+
21
+ # lmux-google
22
+
23
+ Google (Gemini) provider for [lmux](https://github.com/cluebbehusen/lmux). Wraps the [google-genai](https://pypi.org/project/google-genai/) SDK, which serves Google's models through either backend:
24
+
25
+ - **Vertex AI** (default) — authenticated with Google Cloud credentials
26
+ - **Gemini Developer API** (AI Studio) — authenticated with an API key (`vertexai=False`)
27
+
28
+ Supports chat completions, streaming, and embeddings for Google-published models: Gemini and the Gemini/text embedding models.
29
+
30
+ Part of the [lmux](https://github.com/cluebbehusen/lmux) ecosystem: standardized interface, cost tracking on every response, and registry-based routing across providers.
31
+
32
+ ## Auth
33
+
34
+ Three authentication methods:
35
+
36
+ ### Application Default Credentials (default)
37
+
38
+ Uses `google.auth.default()`, which works with `GOOGLE_APPLICATION_CREDENTIALS`, `gcloud` CLI, or instance metadata.
39
+
40
+ ```python
41
+ from lmux_google import GoogleProvider
42
+
43
+ provider = GoogleProvider(project="my-project", location="us-central1")
44
+ ```
45
+
46
+ ### Service Account
47
+
48
+ ```python
49
+ from lmux_google import GoogleServiceAccountAuthProvider
50
+
51
+ provider = GoogleProvider(
52
+ project="my-project",
53
+ location="us-central1",
54
+ auth=GoogleServiceAccountAuthProvider(service_account_file="/path/to/key.json"),
55
+ )
56
+ ```
57
+
58
+ ### API Key
59
+
60
+ Set `GOOGLE_API_KEY` in your environment:
61
+
62
+ ```python
63
+ from lmux_google import GoogleAPIKeyAuthProvider
64
+
65
+ provider = GoogleProvider(auth=GoogleAPIKeyAuthProvider(), vertexai=False)
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ### Chat
71
+
72
+ ```python
73
+ from lmux import UserMessage
74
+
75
+ response = provider.chat("gemini-2.5-pro", [UserMessage(content="Hello")])
76
+ print(response.content)
77
+ print(response.cost)
78
+ ```
79
+
80
+ ### Streaming
81
+
82
+ ```python
83
+ for chunk in provider.chat_stream("gemini-2.5-pro", [UserMessage(content="Hello")]):
84
+ if chunk.delta:
85
+ print(chunk.delta, end="")
86
+ ```
87
+
88
+ ### Embeddings
89
+
90
+ ```python
91
+ response = provider.embed("text-embedding-005", "Hello")
92
+ print(response.embeddings)
93
+ ```
94
+
95
+ ### Async
96
+
97
+ All methods have async variants: `achat`, `achat_stream`, `aembed`.
98
+
99
+ ### Registry
100
+
101
+ Use with the lmux registry to route across multiple providers:
102
+
103
+ ```python
104
+ from lmux import Registry
105
+
106
+ registry = Registry()
107
+ registry.register("google", provider)
108
+ response = registry.chat("google/gemini-2.5-pro", messages)
109
+ ```
110
+
111
+ ## Provider Params
112
+
113
+ ```python
114
+ from lmux_google import GoogleParams
115
+
116
+ response = provider.chat(
117
+ "gemini-2.5-pro",
118
+ messages,
119
+ provider_params=GoogleParams(thinking_config={"thinking_budget": 1024}),
120
+ )
121
+ ```
122
+
123
+ | Parameter | Type | Description |
124
+ | ------------------- | --------------------- | -------------------------------- |
125
+ | `safety_settings` | `list[SafetySetting]` | Content safety thresholds |
126
+ | `presence_penalty` | `float` | Presence penalty |
127
+ | `frequency_penalty` | `float` | Frequency penalty |
128
+ | `seed` | `int` | Deterministic sampling seed |
129
+ | `labels` | `dict[str, str]` | Request labels |
130
+ | `thinking_config` | `dict` | Thinking/reasoning configuration |
131
+
132
+ ## Constructor Options
133
+
134
+ ```python
135
+ GoogleProvider(
136
+ auth=..., # AuthProvider, default: GoogleADCAuthProvider()
137
+ project=..., # GCP project ID
138
+ location=..., # GCP region
139
+ vertexai=..., # Use Vertex AI (default: True) vs. AI Studio
140
+ )
141
+ ```
@@ -0,0 +1,121 @@
1
+ # lmux-google
2
+
3
+ Google (Gemini) provider for [lmux](https://github.com/cluebbehusen/lmux). Wraps the [google-genai](https://pypi.org/project/google-genai/) SDK, which serves Google's models through either backend:
4
+
5
+ - **Vertex AI** (default) — authenticated with Google Cloud credentials
6
+ - **Gemini Developer API** (AI Studio) — authenticated with an API key (`vertexai=False`)
7
+
8
+ Supports chat completions, streaming, and embeddings for Google-published models: Gemini and the Gemini/text embedding models.
9
+
10
+ Part of the [lmux](https://github.com/cluebbehusen/lmux) ecosystem: standardized interface, cost tracking on every response, and registry-based routing across providers.
11
+
12
+ ## Auth
13
+
14
+ Three authentication methods:
15
+
16
+ ### Application Default Credentials (default)
17
+
18
+ Uses `google.auth.default()`, which works with `GOOGLE_APPLICATION_CREDENTIALS`, `gcloud` CLI, or instance metadata.
19
+
20
+ ```python
21
+ from lmux_google import GoogleProvider
22
+
23
+ provider = GoogleProvider(project="my-project", location="us-central1")
24
+ ```
25
+
26
+ ### Service Account
27
+
28
+ ```python
29
+ from lmux_google import GoogleServiceAccountAuthProvider
30
+
31
+ provider = GoogleProvider(
32
+ project="my-project",
33
+ location="us-central1",
34
+ auth=GoogleServiceAccountAuthProvider(service_account_file="/path/to/key.json"),
35
+ )
36
+ ```
37
+
38
+ ### API Key
39
+
40
+ Set `GOOGLE_API_KEY` in your environment:
41
+
42
+ ```python
43
+ from lmux_google import GoogleAPIKeyAuthProvider
44
+
45
+ provider = GoogleProvider(auth=GoogleAPIKeyAuthProvider(), vertexai=False)
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ### Chat
51
+
52
+ ```python
53
+ from lmux import UserMessage
54
+
55
+ response = provider.chat("gemini-2.5-pro", [UserMessage(content="Hello")])
56
+ print(response.content)
57
+ print(response.cost)
58
+ ```
59
+
60
+ ### Streaming
61
+
62
+ ```python
63
+ for chunk in provider.chat_stream("gemini-2.5-pro", [UserMessage(content="Hello")]):
64
+ if chunk.delta:
65
+ print(chunk.delta, end="")
66
+ ```
67
+
68
+ ### Embeddings
69
+
70
+ ```python
71
+ response = provider.embed("text-embedding-005", "Hello")
72
+ print(response.embeddings)
73
+ ```
74
+
75
+ ### Async
76
+
77
+ All methods have async variants: `achat`, `achat_stream`, `aembed`.
78
+
79
+ ### Registry
80
+
81
+ Use with the lmux registry to route across multiple providers:
82
+
83
+ ```python
84
+ from lmux import Registry
85
+
86
+ registry = Registry()
87
+ registry.register("google", provider)
88
+ response = registry.chat("google/gemini-2.5-pro", messages)
89
+ ```
90
+
91
+ ## Provider Params
92
+
93
+ ```python
94
+ from lmux_google import GoogleParams
95
+
96
+ response = provider.chat(
97
+ "gemini-2.5-pro",
98
+ messages,
99
+ provider_params=GoogleParams(thinking_config={"thinking_budget": 1024}),
100
+ )
101
+ ```
102
+
103
+ | Parameter | Type | Description |
104
+ | ------------------- | --------------------- | -------------------------------- |
105
+ | `safety_settings` | `list[SafetySetting]` | Content safety thresholds |
106
+ | `presence_penalty` | `float` | Presence penalty |
107
+ | `frequency_penalty` | `float` | Frequency penalty |
108
+ | `seed` | `int` | Deterministic sampling seed |
109
+ | `labels` | `dict[str, str]` | Request labels |
110
+ | `thinking_config` | `dict` | Thinking/reasoning configuration |
111
+
112
+ ## Constructor Options
113
+
114
+ ```python
115
+ GoogleProvider(
116
+ auth=..., # AuthProvider, default: GoogleADCAuthProvider()
117
+ project=..., # GCP project ID
118
+ location=..., # GCP region
119
+ vertexai=..., # Use Vertex AI (default: True) vs. AI Studio
120
+ )
121
+ ```
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "lmux-google"
3
+ version = "0.7.0"
4
+ description = "Google (Gemini) provider for lmux"
5
+ requires-python = ">=3.13"
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ authors = [{ name = "Connor Luebbehusen", email = "connor@luebbehusen.dev" }]
9
+ keywords = ["llm", "ai", "google", "gemini", "vertex", "language-model"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Programming Language :: Python :: 3",
13
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
14
+ "Typing :: Typed",
15
+ ]
16
+ dependencies = [
17
+ "lmux~=0.7",
18
+ "google-genai~=1.0",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/cluebbehusen/lmux"
23
+ Source = "https://github.com/cluebbehusen/lmux/tree/main/packages/lmux-google"
24
+ Issues = "https://github.com/cluebbehusen/lmux/issues"
25
+
26
+ [tool.uv.sources]
27
+ lmux = { workspace = true }
28
+
29
+ [build-system]
30
+ requires = ["uv_build>=0.11.0,<0.12.0"]
31
+ build-backend = "uv_build"
@@ -0,0 +1,37 @@
1
+ """lmux-google — Google (Gemini) provider for lmux."""
2
+
3
+ from lmux_google.auth import (
4
+ GoogleADCAuthProvider,
5
+ GoogleAPIKeyAuthProvider,
6
+ GoogleServiceAccountAuthProvider,
7
+ )
8
+ from lmux_google.cost import calculate_google_cost
9
+ from lmux_google.params import (
10
+ DynamicRetrievalConfig,
11
+ GoogleParams,
12
+ GoogleSearchConfig,
13
+ GoogleSearchRetrievalConfig,
14
+ GoogleSearchTypes,
15
+ SafetySetting,
16
+ )
17
+ from lmux_google.provider import GoogleProvider
18
+
19
+ __all__ = [
20
+ "DynamicRetrievalConfig",
21
+ "GoogleADCAuthProvider",
22
+ "GoogleAPIKeyAuthProvider",
23
+ "GoogleParams",
24
+ "GoogleProvider",
25
+ "GoogleSearchConfig",
26
+ "GoogleSearchRetrievalConfig",
27
+ "GoogleSearchTypes",
28
+ "GoogleServiceAccountAuthProvider",
29
+ "SafetySetting",
30
+ "calculate_google_cost",
31
+ "preload",
32
+ ]
33
+
34
+
35
+ def preload() -> None:
36
+ """Eagerly import the google-genai SDK."""
37
+ import google.genai # noqa: PLC0415, F401 # pyright: ignore[reportUnusedImport]
@@ -0,0 +1,66 @@
1
+ """Map google-genai SDK exceptions to lmux exception hierarchy."""
2
+
3
+ from lmux.exceptions import (
4
+ AuthenticationError,
5
+ InvalidRequestError,
6
+ LmuxError,
7
+ NotFoundError,
8
+ ProviderError,
9
+ RateLimitError,
10
+ TimeoutError, # noqa: A004
11
+ )
12
+
13
+ PROVIDER = "google"
14
+
15
+ _STATUS_CODE_MAP: dict[int, type[LmuxError]] = {
16
+ 400: InvalidRequestError,
17
+ 401: AuthenticationError,
18
+ 403: AuthenticationError,
19
+ 404: NotFoundError,
20
+ 408: TimeoutError,
21
+ 429: RateLimitError,
22
+ }
23
+
24
+
25
+ def map_google_error(error: Exception) -> LmuxError:
26
+ """Convert a google-genai SDK exception to the corresponding lmux exception."""
27
+ from google.genai import errors as genai_errors # noqa: PLC0415
28
+
29
+ if isinstance(error, genai_errors.ClientError):
30
+ return _map_client_error(error)
31
+
32
+ if isinstance(error, genai_errors.ServerError):
33
+ return ProviderError(str(error), provider=PROVIDER, status_code=error.code)
34
+
35
+ if isinstance(error, genai_errors.APIError):
36
+ return ProviderError(str(error), provider=PROVIDER, status_code=error.code)
37
+
38
+ # google.auth errors
39
+ auth_error = _check_auth_error(error)
40
+ if auth_error is not None:
41
+ return auth_error
42
+
43
+ return ProviderError(str(error), provider=PROVIDER)
44
+
45
+
46
+ def _map_client_error(error: Exception) -> LmuxError:
47
+ """Map a google-genai ClientError based on its HTTP status code."""
48
+ code: int | None = getattr(error, "code", None)
49
+
50
+ exc_cls = _STATUS_CODE_MAP.get(code) if code is not None else None
51
+ if exc_cls is not None:
52
+ if exc_cls is RateLimitError:
53
+ return RateLimitError(str(error), provider=PROVIDER, status_code=code)
54
+ return exc_cls(str(error), provider=PROVIDER, status_code=code)
55
+
56
+ return ProviderError(str(error), provider=PROVIDER, status_code=code)
57
+
58
+
59
+ def _check_auth_error(error: Exception) -> AuthenticationError | None:
60
+ """Check if the error is a google.auth exception and map it."""
61
+ import google.auth.exceptions # noqa: PLC0415
62
+
63
+ if isinstance(error, google.auth.exceptions.DefaultCredentialsError | google.auth.exceptions.RefreshError):
64
+ return AuthenticationError(str(error), provider=PROVIDER)
65
+
66
+ return None
@@ -0,0 +1,34 @@
1
+ """Lazy google-genai SDK loading internals.
2
+
3
+ Client creation is isolated here so tests can easily mock it
4
+ without patching sys.modules or using TYPE_CHECKING tricks.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from google.auth.credentials import Credentials
11
+ from google.genai import Client
12
+
13
+
14
+ def create_client(
15
+ *,
16
+ vertexai: bool = True,
17
+ project: str | None = None,
18
+ location: str | None = None,
19
+ credentials: "Credentials | None" = None,
20
+ api_key: str | None = None,
21
+ ) -> "Client":
22
+ """Create a google-genai Client."""
23
+ from google import genai # noqa: PLC0415
24
+
25
+ kwargs: dict[str, Any] = {"vertexai": vertexai}
26
+ if project is not None:
27
+ kwargs["project"] = project
28
+ if location is not None:
29
+ kwargs["location"] = location
30
+ if credentials is not None:
31
+ kwargs["credentials"] = credentials
32
+ if api_key is not None:
33
+ kwargs["api_key"] = api_key
34
+ return genai.Client(**kwargs)