Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,37 +250,37 @@ client = genai.Client(
)
```

### HTTP client option: httpx2
### HTTP client option: httpx

By default, the SDK uses `httpx` for its HTTP transport. You can opt in to
[`httpx2`](https://github.com/pydantic/httpx2) by installing `google-genai[httpx2]`
By default, the SDK uses [`httpx2`](https://github.com/pydantic/httpx2) for its HTTP transport. You can opt in to
legacy `httpx` by installing `google-genai[httpx]`
and setting the `GOOGLE_GENAI_HTTP_CLIENT` environment variable:

```bash
export GOOGLE_GENAI_HTTP_CLIENT=httpx2
export GOOGLE_GENAI_HTTP_CLIENT=httpx
```

Alternatively, you can directly inject an `httpx2.Client` or `httpx2.AsyncClient`
Alternatively, you can directly inject an `httpx.Client` or `httpx.AsyncClient`
via `HttpOptions`:

```python
import httpx2
import httpx
from google import genai
from google.genai import types

client = genai.Client(
http_options=types.HttpOptions(
httpx_client=httpx2.Client(),
httpx_async_client=httpx2.AsyncClient(),
httpx_client=httpx.Client(),
httpx_async_client=httpx.AsyncClient(),
)
)
```

### Faster async client option: Aiohttp

By default we use httpx for both sync and async client implementations. In order
By default we use httpx2 for both sync and async client implementations. In order
to have faster performance, you may install `google-genai[aiohttp]`. In Gen AI
SDK we configure `trust_env=True` to match with the default behavior of httpx.
SDK we configure `trust_env=True` to match with the default behavior of httpx2.
Additional args of `aiohttp.ClientSession.request()` ([see `_RequestOptions` args](https://github.com/aio-libs/aiohttp/blob/v3.12.13/aiohttp/client.py#L170)) can be passed
through the following way:

Expand All @@ -295,7 +295,7 @@ client=Client(..., http_options=http_options)

### Proxy

Both httpx and aiohttp libraries use `urllib.request.getproxies` from
Both httpx2 and aiohttp libraries use `urllib.request.getproxies` from
environment variables. Before client initialization, you may set proxy (and
optional `SSL_CERT_FILE`) by setting the environment variables:

Expand All @@ -304,8 +304,8 @@ export HTTPS_PROXY='http://username:password@proxy_uri:port'
export SSL_CERT_FILE='client.pem'
```

If you need `socks5` proxy, httpx [supports](https://www.python-httpx.org/advanced/proxies/#socks) `socks5` proxy if you pass it via
args to `httpx.Client()`. You may install `httpx[socks]` to use it.
If you need `socks5` proxy, httpx2 [supports](https://www.python-httpx.org/advanced/proxies/#socks) `socks5` proxy if you pass it via
args to `httpx2.Client()`. You may install `httpx2[socks]` to use it.
Then, you can pass it through the following way:

```python
Expand Down
12 changes: 6 additions & 6 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ To set the API version to `v1alpha` for the Gemini Developer API:
Faster async client option: Aiohttp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

By default we use httpx for both sync and async client implementations. In order
By default we use httpx2 for both sync and async client implementations. In order
to have faster performance, you may install `google-genai[aiohttp]`. In Gen AI
SDK we configure `trust_env=True` to match with the default behavior of httpx.
SDK we configure `trust_env=True` to match with the default behavior of httpx2.
Additional args of `aiohttp.ClientSession.request()`
(`see _RequestOptions args <https://github.com/aio-libs/aiohttp/blob/v3.12.13/aiohttp/client.py#L170>`_)
can be passed through the following way:
Expand All @@ -237,7 +237,7 @@ can be passed through the following way:
Proxy
^^^^^^^

Both httpx and aiohttp libraries use `urllib.request.getproxies` from
Both httpx2 and aiohttp libraries use `urllib.request.getproxies` from
environment variables. Before client initialization, you may set proxy (and
optional SSL_CERT_FILE) by setting the environment variables:

Expand All @@ -248,9 +248,9 @@ optional SSL_CERT_FILE) by setting the environment variables:
export SSL_CERT_FILE='client.pem'
If you need `socks5` proxy, httpx `supports <https://www.python-httpx.org/advanced/proxies/#socks>`_
`socks5` proxy if you pass it via args to httpx.Client(). You may install
`httpx[socks]` to use it. Then you can pass it through the following way:
If you need `socks5` proxy, httpx2 `supports <https://www.python-httpx.org/advanced/proxies/#socks>`_
`socks5` proxy if you pass it via args to httpx2.Client(). You may install
`httpx2[socks]` to use it. Then you can pass it through the following way:

.. code:: python
Expand Down
96 changes: 71 additions & 25 deletions google/genai/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,13 +809,19 @@ def __del__(self) -> None:
AsyncHttpx2Client = None # type: ignore[assignment,misc]


# The HTTP client backend used when GOOGLE_GENAI_HTTP_CLIENT is unset or 'auto'.
# If it is not installed, the other backend is used instead.
_DEFAULT_HTTP_CLIENT_BACKEND = 'httpx2'


def _get_http_client_backend() -> str:
"""Determines whether to use 'httpx' or 'httpx2' as the default HTTP client.
Can be controlled via the GOOGLE_GENAI_HTTP_CLIENT environment variable:
- 'httpx2': Use httpx2 (raises ImportError if not installed).
- 'httpx': Use httpx (raises ImportError if not installed).
- 'auto' or unset: Default to httpx if installed, otherwise fall back to httpx2.
- 'auto' or unset: Use `_DEFAULT_HTTP_CLIENT_BACKEND` if installed,
otherwise fall back to the other backend.
"""
env = os.environ.get('GOOGLE_GENAI_HTTP_CLIENT', '').lower().strip()
if env == 'httpx2':
Expand All @@ -837,14 +843,14 @@ def _get_http_client_backend() -> str:
'Unrecognized GOOGLE_GENAI_HTTP_CLIENT=%r; falling back to default.', env
)

if httpx is not None:
return 'httpx'
# httpx is a required dependency so it _should_ always be present in a
# standard install, but if a user has uninstalled httpx in an environment
# that only has httpx2 installed, fall back to httpx2 before raising
installed = {'httpx': httpx is not None, 'httpx2': httpx2 is not None}
fallback = 'httpx' if _DEFAULT_HTTP_CLIENT_BACKEND == 'httpx2' else 'httpx2'
# Either backend may be missing (httpx2 is an optional extra, and a user may
# have uninstalled httpx), so fall back to the other one before raising
# ImportError.
if httpx2 is not None:
return 'httpx2'
for backend in (_DEFAULT_HTTP_CLIENT_BACKEND, fallback):
if installed[backend]:
return backend
raise ImportError(
'Neither httpx nor httpx2 is installed. Please install httpx2 or httpx.'
)
Expand Down Expand Up @@ -1377,19 +1383,70 @@ def _ensure_httpx_ssl_ctx(
else None
)

default_backend = (
_get_http_client_backend()
if (client_cls is None or async_client_cls is None)
else None
)
if client_cls is not None:
sync_cls = client_cls
elif default_backend == 'httpx2' and SyncHttpx2Client is not None:
sync_cls = SyncHttpx2Client
else:
sync_cls = (
SyncHttpxClient
if SyncHttpxClient is not None
else (httpx.Client if httpx is not None else None)
)

if async_client_cls is not None:
async_cls = async_client_cls
elif default_backend == 'httpx2' and AsyncHttpx2Client is not None:
async_cls = AsyncHttpx2Client
else:
async_cls = (
AsyncHttpxClient
if AsyncHttpxClient is not None
else (httpx.AsyncClient if httpx is not None else None)
)

use_httpx2 = httpx2 is not None and (
(
sync_cls is not None
and isinstance(getattr(httpx2, 'Client', None), type)
and issubclass(sync_cls, httpx2.Client)
)
or (
async_cls is not None
and isinstance(getattr(httpx2, 'AsyncClient', None), type)
and issubclass(async_cls, httpx2.AsyncClient)
)
)

if ctx is None:
# Initialize the SSL context for the httpx client.
# Initialize the SSL context for the httpx/httpx2 client.
# Unlike requests, the httpx package does not automatically pull in the
# environment variables SSL_CERT_FILE or SSL_CERT_DIR. They need to be
# enabled explicitly.
# enabled explicitly. When using httpx2 without SSL_CERT_FILE or
# SSL_CERT_DIR, delegate to httpx2.create_ssl_context(), which uses
# truststore (OS native trust store).
if vertexai:
ctx = _get_client_cert_ssl_ctx()

if ctx is None:
ctx = ssl.create_default_context(
cafile=os.environ.get('SSL_CERT_FILE', certifi.where()),
capath=os.environ.get('SSL_CERT_DIR'),
)
if use_httpx2:
if os.environ.get('SSL_CERT_FILE') or os.environ.get('SSL_CERT_DIR'):
ctx = ssl.create_default_context(
cafile=os.environ.get('SSL_CERT_FILE'),
capath=os.environ.get('SSL_CERT_DIR'),
)
else:
ctx = httpx2.create_ssl_context()
else:
ctx = ssl.create_default_context(
cafile=os.environ.get('SSL_CERT_FILE', certifi.where()),
capath=os.environ.get('SSL_CERT_DIR'),
)

def _maybe_set(
raw_args: Optional[_common.StringDict],
Expand Down Expand Up @@ -1432,17 +1489,6 @@ def _maybe_set(
break
return args

sync_cls = client_cls or (
SyncHttpxClient
if SyncHttpxClient is not None
else (httpx.Client if httpx is not None else None)
)
async_cls = async_client_cls or (
AsyncHttpxClient
if AsyncHttpxClient is not None
else (httpx.AsyncClient if httpx is not None else None)
)

return (
_maybe_set(args, ctx, sync_cls),
_maybe_set(async_args, ctx, async_cls),
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
BeforeRequestHook,
)
import asyncio
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Optional, Tuple, Union

# ============================================================================
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/asyncsdkhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from .asynctypes import (
AsyncBeforeRequestHook,
AsyncAfterSuccessHook,
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/asynctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
BeforeRequestContext,
)
from abc import ABC, abstractmethod
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Optional, Tuple, Union


Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/google_genai_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@

from typing import Any, Callable, Mapping, Optional, Union, cast

import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx

from .. import types, utils
from .types import BeforeRequestContext, BeforeRequestHook
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/sdkhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from .types import (
SDKInitHook,
BeforeRequestContext,
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_hooks/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

from ..sdkconfiguration import SDKConfiguration
from abc import ABC, abstractmethod
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union


Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
from .basesdk import AsyncBaseSDK, BaseSDK
from .types import OptionalNullable, UNSET
from .utils import get_security_from_env, response_helpers
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Any, Mapping, Optional, Union, cast


Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
from .types.agents import agent as agents_agent, agenttool as agents_agenttool
from .utils import get_security_from_env, response_helpers
from .utils.unmarshal_json_response import unmarshal_json_response
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Any, Iterable, List, Mapping, Optional, Union, cast


Expand Down
21 changes: 18 additions & 3 deletions google/genai/_gaos/basesdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
)
from .sdkconfiguration import SDKConfiguration
from .utils import RetryConfig, SerializedRequestBody, get_body_content
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx

try:
import httpx2
Expand All @@ -38,6 +41,14 @@
if httpx2 is None
else (httpx.Timeout, httpx2.Timeout)
)


def _use_client_default(client: Any) -> Any:
if httpx2 is not None and isinstance(
client, (httpx2.Client, httpx2.AsyncClient)
):
return httpx2.USE_CLIENT_DEFAULT
return httpx.USE_CLIENT_DEFAULT
from typing import Any, Callable, List, Mapping, Optional, Tuple
from urllib.parse import parse_qs, urlparse

Expand Down Expand Up @@ -234,7 +245,9 @@ def _build_request_with_client(
data=serialized_request_body.data,
files=serialized_request_body.files,
headers=headers,
timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT,
timeout=(
timeout if timeout is not None else _use_client_default(client)
),
)

def do_request(
Expand Down Expand Up @@ -501,7 +514,9 @@ def _build_request_with_client(
data=serialized_request_body.data,
files=serialized_request_body.files,
headers=headers,
timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT,
timeout=(
timeout if timeout is not None else _use_client_default(client)
),
)

async def do_request_async(
Expand Down
5 changes: 4 additions & 1 deletion google/genai/_gaos/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
from .types import BaseModel, OptionalNullable, UNSET, credentials, interactions
from .utils import get_security_from_env, response_helpers
from .utils.unmarshal_json_response import unmarshal_json_response
import httpx
try:
import httpx
except ImportError:
import httpx2 as httpx
from typing import Any, List, Literal, Mapping, Optional, Union, cast, overload


Expand Down
Loading
Loading