diff --git a/google/genai/_gaos/_hooks/adapters.py b/google/genai/_gaos/_hooks/adapters.py index 5a9c41c70..85520e9c0 100644 --- a/google/genai/_gaos/_hooks/adapters.py +++ b/google/genai/_gaos/_hooks/adapters.py @@ -34,7 +34,7 @@ BeforeRequestHook, ) import asyncio -import httpx +import httpx2 as httpx from typing import Optional, Tuple, Union # ============================================================================ diff --git a/google/genai/_gaos/_hooks/asyncregistration.py b/google/genai/_gaos/_hooks/asyncregistration.py index 5612ebaa6..1009594b1 100644 --- a/google/genai/_gaos/_hooks/asyncregistration.py +++ b/google/genai/_gaos/_hooks/asyncregistration.py @@ -42,7 +42,7 @@ def init_async_hooks(hooks: AsyncHooks): Example: from .asynctypes import AsyncBeforeRequestHook, BeforeRequestContext - import httpx + import httpx2 as httpx class MyAsyncHook(AsyncBeforeRequestHook): async def before_request(self, hook_ctx: BeforeRequestContext, request: httpx.Request): diff --git a/google/genai/_gaos/_hooks/asyncsdkhooks.py b/google/genai/_gaos/_hooks/asyncsdkhooks.py index ad81b1d06..6ed1a56f0 100644 --- a/google/genai/_gaos/_hooks/asyncsdkhooks.py +++ b/google/genai/_gaos/_hooks/asyncsdkhooks.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx from .asynctypes import ( AsyncBeforeRequestHook, AsyncAfterSuccessHook, diff --git a/google/genai/_gaos/_hooks/asynctypes.py b/google/genai/_gaos/_hooks/asynctypes.py index 0869abee1..a8b53b060 100644 --- a/google/genai/_gaos/_hooks/asynctypes.py +++ b/google/genai/_gaos/_hooks/asynctypes.py @@ -24,7 +24,7 @@ BeforeRequestContext, ) from abc import ABC, abstractmethod -import httpx +import httpx2 as httpx from typing import Optional, Tuple, Union diff --git a/google/genai/_gaos/_hooks/google_genai_auth.py b/google/genai/_gaos/_hooks/google_genai_auth.py index 7ec30b9d8..4faeab80b 100644 --- a/google/genai/_gaos/_hooks/google_genai_auth.py +++ b/google/genai/_gaos/_hooks/google_genai_auth.py @@ -21,7 +21,7 @@ from typing import Any, Callable, Mapping, Optional, Union, cast -import httpx +import httpx2 as httpx from .. import types, utils from .types import BeforeRequestContext, BeforeRequestHook diff --git a/google/genai/_gaos/_hooks/sdkhooks.py b/google/genai/_gaos/_hooks/sdkhooks.py index 5dc2b7bc3..f872d2361 100644 --- a/google/genai/_gaos/_hooks/sdkhooks.py +++ b/google/genai/_gaos/_hooks/sdkhooks.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx from .types import ( SDKInitHook, BeforeRequestContext, diff --git a/google/genai/_gaos/_hooks/types.py b/google/genai/_gaos/_hooks/types.py index fdee86829..80a609166 100644 --- a/google/genai/_gaos/_hooks/types.py +++ b/google/genai/_gaos/_hooks/types.py @@ -19,7 +19,7 @@ from ..sdkconfiguration import SDKConfiguration from abc import ABC, abstractmethod -import httpx +import httpx2 as httpx from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union diff --git a/google/genai/_gaos/_internal.py b/google/genai/_gaos/_internal.py index abd8077f0..bea4a1ba8 100644 --- a/google/genai/_gaos/_internal.py +++ b/google/genai/_gaos/_internal.py @@ -22,7 +22,7 @@ from .basesdk import AsyncBaseSDK, BaseSDK from .types import OptionalNullable, UNSET from .utils import get_security_from_env, response_helpers -import httpx +import httpx2 as httpx from typing import Any, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/agents.py b/google/genai/_gaos/agents.py index 1118eeed9..f38663462 100644 --- a/google/genai/_gaos/agents.py +++ b/google/genai/_gaos/agents.py @@ -24,7 +24,7 @@ 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 +import httpx2 as httpx from typing import Any, Iterable, List, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/basesdk.py b/google/genai/_gaos/basesdk.py index 0306654c1..36562971a 100644 --- a/google/genai/_gaos/basesdk.py +++ b/google/genai/_gaos/basesdk.py @@ -26,18 +26,24 @@ ) from .sdkconfiguration import SDKConfiguration from .utils import RetryConfig, SerializedRequestBody, get_body_content -import httpx +import httpx2 as httpx try: - import httpx2 + import httpx as httpx1 except ImportError: - httpx2 = None + httpx1 = None _HTTPX_TIMEOUT_TYPES = ( (httpx.Timeout,) - if httpx2 is None - else (httpx.Timeout, httpx2.Timeout) + if httpx1 is None + else (httpx.Timeout, httpx1.Timeout) ) + + +def _default_timeout(client): + if httpx1 is not None and isinstance(client, (httpx1.Client, httpx1.AsyncClient)): + return httpx1.USE_CLIENT_DEFAULT + return httpx.USE_CLIENT_DEFAULT from typing import Any, Callable, List, Mapping, Optional, Tuple from urllib.parse import parse_qs, urlparse @@ -234,7 +240,7 @@ 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 _default_timeout(client), ) def do_request( @@ -501,7 +507,7 @@ 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 _default_timeout(client), ) async def do_request_async( diff --git a/google/genai/_gaos/credentials.py b/google/genai/_gaos/credentials.py index f132a9d5d..fd712fd00 100644 --- a/google/genai/_gaos/credentials.py +++ b/google/genai/_gaos/credentials.py @@ -23,7 +23,7 @@ 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 +import httpx2 as httpx from typing import Any, List, Literal, Mapping, Optional, Union, cast, overload diff --git a/google/genai/_gaos/environments.py b/google/genai/_gaos/environments.py index 92846eb14..273ac3d82 100644 --- a/google/genai/_gaos/environments.py +++ b/google/genai/_gaos/environments.py @@ -30,7 +30,7 @@ from .types.interactions import source as interactions_source from .utils import get_security_from_env, response_helpers from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Iterable, List, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/errors/cancelinteractionbyid.py b/google/genai/_gaos/errors/cancelinteractionbyid.py index 8fe786cbf..3b86900f7 100644 --- a/google/genai/_gaos/errors/cancelinteractionbyid.py +++ b/google/genai/_gaos/errors/cancelinteractionbyid.py @@ -22,7 +22,7 @@ from ..types import BaseModel from ..types.interactions import error as interactions_error from dataclasses import dataclass, field -import httpx +import httpx2 as httpx from typing import Optional diff --git a/google/genai/_gaos/errors/createinteraction.py b/google/genai/_gaos/errors/createinteraction.py index 776c4ed29..e7b16b849 100644 --- a/google/genai/_gaos/errors/createinteraction.py +++ b/google/genai/_gaos/errors/createinteraction.py @@ -22,7 +22,7 @@ from ..types import BaseModel from ..types.interactions import error as interactions_error from dataclasses import dataclass, field -import httpx +import httpx2 as httpx from typing import Optional diff --git a/google/genai/_gaos/errors/deleteinteraction.py b/google/genai/_gaos/errors/deleteinteraction.py index 05f2536ef..1351ed4d4 100644 --- a/google/genai/_gaos/errors/deleteinteraction.py +++ b/google/genai/_gaos/errors/deleteinteraction.py @@ -22,7 +22,7 @@ from ..types import BaseModel from ..types.interactions import error as interactions_error from dataclasses import dataclass, field -import httpx +import httpx2 as httpx from typing import Optional diff --git a/google/genai/_gaos/errors/genaidefaulterror.py b/google/genai/_gaos/errors/genaidefaulterror.py index 318be1050..6fccd8f49 100644 --- a/google/genai/_gaos/errors/genaidefaulterror.py +++ b/google/genai/_gaos/errors/genaidefaulterror.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx from typing import Optional from dataclasses import dataclass diff --git a/google/genai/_gaos/errors/genaierror.py b/google/genai/_gaos/errors/genaierror.py index 516f13a5d..01d2788d3 100644 --- a/google/genai/_gaos/errors/genaierror.py +++ b/google/genai/_gaos/errors/genaierror.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx from typing import Optional from dataclasses import dataclass, field diff --git a/google/genai/_gaos/errors/getinteractionbyid.py b/google/genai/_gaos/errors/getinteractionbyid.py index a29d02716..1a0bb7993 100644 --- a/google/genai/_gaos/errors/getinteractionbyid.py +++ b/google/genai/_gaos/errors/getinteractionbyid.py @@ -22,7 +22,7 @@ from ..types import BaseModel from ..types.interactions import error as interactions_error from dataclasses import dataclass, field -import httpx +import httpx2 as httpx from typing import Optional diff --git a/google/genai/_gaos/errors/responsevalidationerror.py b/google/genai/_gaos/errors/responsevalidationerror.py index 24fe08c10..64049787e 100644 --- a/google/genai/_gaos/errors/responsevalidationerror.py +++ b/google/genai/_gaos/errors/responsevalidationerror.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx from typing import Optional from dataclasses import dataclass diff --git a/google/genai/_gaos/files.py b/google/genai/_gaos/files.py index 6155254a3..58918f4f9 100644 --- a/google/genai/_gaos/files.py +++ b/google/genai/_gaos/files.py @@ -23,7 +23,7 @@ from .types import OptionalNullable, UNSET, environments from .utils import get_security_from_env, response_helpers from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/google_genai.py b/google/genai/_gaos/google_genai.py index 95c9fdf7c..95aeb152d 100644 --- a/google/genai/_gaos/google_genai.py +++ b/google/genai/_gaos/google_genai.py @@ -31,7 +31,7 @@ import mimetypes import os -import httpx +import httpx2 as httpx from ._hooks.google_genai_auth import ( GOOGLE_GENAI_API_REVISION as _GOOGLE_GENAI_API_REVISION, diff --git a/google/genai/_gaos/httpclient.py b/google/genai/_gaos/httpclient.py index 0e1a8dcd7..a2aefcfa0 100644 --- a/google/genai/_gaos/httpclient.py +++ b/google/genai/_gaos/httpclient.py @@ -20,7 +20,7 @@ # pyright: reportReturnType = false import asyncio from typing_extensions import Protocol, runtime_checkable -import httpx +import httpx2 as httpx from typing import Any, Optional, Union diff --git a/google/genai/_gaos/interactions.py b/google/genai/_gaos/interactions.py index d8951ad44..bf2d64e41 100644 --- a/google/genai/_gaos/interactions.py +++ b/google/genai/_gaos/interactions.py @@ -24,7 +24,7 @@ from .utils import get_security_from_env, response_helpers from .utils.eventstreaming import AsyncStream, Stream from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Dict, List, Literal, Mapping, Optional, Union, cast, overload diff --git a/google/genai/_gaos/lib/compat_errors.py b/google/genai/_gaos/lib/compat_errors.py index 7bd524927..c2f378117 100644 --- a/google/genai/_gaos/lib/compat_errors.py +++ b/google/genai/_gaos/lib/compat_errors.py @@ -29,22 +29,22 @@ import json from typing import Any, Awaitable, Callable, Optional, TypeVar, cast +import httpx2 as httpx + try: - import httpx2 + import httpx as httpx1 except ImportError: - httpx2 = None - -import httpx + httpx1 = None _HTTPX_TIMEOUT_ERRORS = ( (httpx.TimeoutException,) - if httpx2 is None - else (httpx.TimeoutException, httpx2.TimeoutException) + if httpx1 is None + else (httpx.TimeoutException, httpx1.TimeoutException) ) _HTTPX_HTTP_ERRORS = ( (httpx.HTTPError,) - if httpx2 is None - else (httpx.HTTPError, httpx2.HTTPError) + if httpx1 is None + else (httpx.HTTPError, httpx1.HTTPError) ) from ..errors.genaierror import GenAiError @@ -336,8 +336,8 @@ def wrap_sdk_error(error: BaseException) -> BaseException: _WRAP_EXCEPTIONS = ( (GenAiError, NoResponseError, httpx.HTTPError) - if httpx2 is None - else (GenAiError, NoResponseError, httpx.HTTPError, httpx2.HTTPError) + if httpx1 is None + else (GenAiError, NoResponseError, httpx.HTTPError, httpx1.HTTPError) ) diff --git a/google/genai/_gaos/sdk.py b/google/genai/_gaos/sdk.py index 2f493dcf7..2b926a9fd 100644 --- a/google/genai/_gaos/sdk.py +++ b/google/genai/_gaos/sdk.py @@ -31,7 +31,7 @@ from .types import OptionalNullable, UNSET from .utils.logger import Logger, get_default_logger from .utils.retries import RetryConfig -import httpx +import httpx2 as httpx import importlib import importlib.util import sys diff --git a/google/genai/_gaos/triggers.py b/google/genai/_gaos/triggers.py index df24c6920..900c81abb 100644 --- a/google/genai/_gaos/triggers.py +++ b/google/genai/_gaos/triggers.py @@ -27,7 +27,7 @@ from .types.triggers import triggerupdate as triggers_triggerupdate from .utils import get_security_from_env, response_helpers from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/utils/eventstreaming.py b/google/genai/_gaos/utils/eventstreaming.py index 423689b29..17830f4df 100644 --- a/google/genai/_gaos/utils/eventstreaming.py +++ b/google/genai/_gaos/utils/eventstreaming.py @@ -31,7 +31,7 @@ AsyncGenerator, Tuple, ) -import httpx +import httpx2 as httpx T = TypeVar("T") diff --git a/google/genai/_gaos/utils/headers.py b/google/genai/_gaos/utils/headers.py index b95606938..ba52a76ac 100644 --- a/google/genai/_gaos/utils/headers.py +++ b/google/genai/_gaos/utils/headers.py @@ -23,7 +23,7 @@ List, Optional, ) -from httpx import Headers +from httpx2 import Headers from pydantic import BaseModel from pydantic.fields import FieldInfo diff --git a/google/genai/_gaos/utils/logger.py b/google/genai/_gaos/utils/logger.py index 1085e0b04..05e77d411 100644 --- a/google/genai/_gaos/utils/logger.py +++ b/google/genai/_gaos/utils/logger.py @@ -17,7 +17,7 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -import httpx +import httpx2 as httpx import logging import os from typing import Any, Protocol diff --git a/google/genai/_gaos/utils/response_helpers.py b/google/genai/_gaos/utils/response_helpers.py index bbb844d39..88d78ee96 100644 --- a/google/genai/_gaos/utils/response_helpers.py +++ b/google/genai/_gaos/utils/response_helpers.py @@ -46,7 +46,7 @@ overload, ) -import httpx +import httpx2 as httpx from pydantic import BaseModel from typing_extensions import TypeAliasType diff --git a/google/genai/_gaos/utils/retries.py b/google/genai/_gaos/utils/retries.py index 1dc009996..5d957a236 100644 --- a/google/genai/_gaos/utils/retries.py +++ b/google/genai/_gaos/utils/retries.py @@ -24,7 +24,7 @@ from email.utils import parsedate_to_datetime from typing import List, Optional -import httpx +import httpx2 as httpx class BackoffStrategy: diff --git a/google/genai/_gaos/utils/serializers.py b/google/genai/_gaos/utils/serializers.py index 99747b4f6..cd469f6c3 100644 --- a/google/genai/_gaos/utils/serializers.py +++ b/google/genai/_gaos/utils/serializers.py @@ -25,7 +25,7 @@ import typing_extensions from typing_extensions import get_origin -import httpx +import httpx2 as httpx from pydantic import ConfigDict, create_model from pydantic_core import from_json diff --git a/google/genai/_gaos/utils/unmarshal_json_response.py b/google/genai/_gaos/utils/unmarshal_json_response.py index 4ba1a154b..216d28f12 100644 --- a/google/genai/_gaos/utils/unmarshal_json_response.py +++ b/google/genai/_gaos/utils/unmarshal_json_response.py @@ -19,7 +19,7 @@ from typing import Any, Optional, Type, TypeVar, overload -import httpx +import httpx2 as httpx from .serializers import unmarshal_json, construct_unvalidated from .. import errors diff --git a/google/genai/_gaos/utils/values.py b/google/genai/_gaos/utils/values.py index cc953332d..2c8fe8a2e 100644 --- a/google/genai/_gaos/utils/values.py +++ b/google/genai/_gaos/utils/values.py @@ -24,7 +24,7 @@ import os from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, cast -from httpx import Response +from httpx2 import Response from pydantic import BaseModel from pydantic.fields import FieldInfo diff --git a/google/genai/_gaos/voices.py b/google/genai/_gaos/voices.py index 454f466f5..cf1db9978 100644 --- a/google/genai/_gaos/voices.py +++ b/google/genai/_gaos/voices.py @@ -24,7 +24,7 @@ from .types.voices import voice_input as voices_voice_input from .utils import get_security_from_env, response_helpers from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Iterable, List, Mapping, Optional, Union, cast diff --git a/google/genai/_gaos/webhooks.py b/google/genai/_gaos/webhooks.py index 145a39259..d07f82e27 100644 --- a/google/genai/_gaos/webhooks.py +++ b/google/genai/_gaos/webhooks.py @@ -29,7 +29,7 @@ ) from .utils import get_security_from_env, response_helpers from .utils.unmarshal_json_response import unmarshal_json_response -import httpx +import httpx2 as httpx from typing import Any, Iterable, List, Mapping, Optional, Union, cast diff --git a/google/genai/tests/interactions/test_httpx_compat.py b/google/genai/tests/interactions/test_httpx_compat.py index ea867f6a0..1b656ca14 100644 --- a/google/genai/tests/interactions/test_httpx_compat.py +++ b/google/genai/tests/interactions/test_httpx_compat.py @@ -409,3 +409,248 @@ def handler(request: httpx2.Request) -> httpx2.Response: assert len(recorded_requests) == 1 assert isinstance(recorded_requests[0], httpx2.Request) assert result.status == "completed" + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +def test_interactions_api_error_handling_with_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + return httpx2.Response( + 404, + headers={"content-type": "application/json"}, + content=b'{"error": {"code": 404, "message": "Interaction not found"}}', + ) + + http_client = httpx2.Client(transport=httpx2.MockTransport(handler)) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_client": http_client}, + ) + with pytest.raises(compat_errors.NotFoundError) as exc_info: + client.interactions.create( + model="gemini-1.5-flash", + input="Hello", + ) + assert exc_info.value.status_code == 404 + assert isinstance(exc_info.value.response, httpx2.Response) + assert isinstance(exc_info.value.request, httpx2.Request) + assert exc_info.value.response.status_code == 404 + finally: + http_client.close() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +@pytest.mark.asyncio +async def test_async_interactions_api_error_handling_with_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + return httpx2.Response( + 400, + headers={"content-type": "application/json"}, + content=b'{"error": {"code": 400, "message": "Invalid request"}}', + ) + + http_async_client = httpx2.AsyncClient( + transport=httpx2.MockTransport(handler) + ) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_async_client": http_async_client}, + ) + with pytest.raises(compat_errors.BadRequestError) as exc_info: + await client.aio.interactions.create( + model="gemini-1.5-flash", + input="Hello", + ) + assert exc_info.value.status_code == 400 + assert isinstance(exc_info.value.response, httpx2.Response) + assert isinstance(exc_info.value.request, httpx2.Request) + finally: + await http_async_client.aclose() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +def test_interactions_with_raw_response_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + return httpx2.Response( + 200, + headers={"content-type": "application/json"}, + content=b'{"id": "raw-interaction-id", "status": "completed"}', + ) + + http_client = httpx2.Client(transport=httpx2.MockTransport(handler)) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_client": http_client}, + ) + raw = client.interactions.with_raw_response.create( + model="gemini-1.5-flash", + input="Hello", + ) + assert isinstance(raw.http_response, httpx2.Response) + assert raw.http_response.status_code == 200 + parsed = raw.parse() + assert parsed.id == "raw-interaction-id" + assert parsed.status == "completed" + finally: + http_client.close() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +@pytest.mark.asyncio +async def test_async_interactions_with_raw_response_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + return httpx2.Response( + 200, + headers={"content-type": "application/json"}, + content=b'{"id": "async-raw-id", "status": "completed"}', + ) + + http_async_client = httpx2.AsyncClient( + transport=httpx2.MockTransport(handler) + ) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_async_client": http_async_client}, + ) + raw = await client.aio.interactions.with_raw_response.create( + model="gemini-1.5-flash", + input="Hello", + ) + assert isinstance(raw.http_response, httpx2.Response) + assert raw.http_response.status_code == 200 + parsed = await raw.parse() + assert parsed.id == "async-raw-id" + assert parsed.status == "completed" + finally: + await http_async_client.aclose() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +def test_interactions_streaming_events_with_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + sse_content = ( + b'data: {"event_id": "e1", "interaction": {"id": "int-1", "status": "in_progress"}}\n\n' + b'data: [DONE]\n\n' + ) + return httpx2.Response( + 200, + headers={"content-type": "text/event-stream"}, + content=sse_content, + ) + + http_client = httpx2.Client(transport=httpx2.MockTransport(handler)) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_client": http_client}, + ) + stream = client.interactions.create( + model="gemini-1.5-flash", + input="Hello", + stream=True, + ) + events = list(stream) + assert len(events) == 1 + assert events[0].event_id == "e1" + assert events[0].interaction["id"] == "int-1" + finally: + http_client.close() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +@pytest.mark.asyncio +async def test_async_interactions_streaming_events_with_httpx2(): + def handler(request: httpx2.Request) -> httpx2.Response: + sse_content = ( + b'data: {"event_id": "e1", "interaction": {"id": "int-async-1", "status": "in_progress"}}\n\n' + b'data: [DONE]\n\n' + ) + return httpx2.Response( + 200, + headers={"content-type": "text/event-stream"}, + content=sse_content, + ) + + http_async_client = httpx2.AsyncClient( + transport=httpx2.MockTransport(handler) + ) + try: + client = client_lib.Client( + api_key="fake-key", + http_options={"httpx_async_client": http_async_client}, + ) + stream = await client.aio.interactions.create( + model="gemini-1.5-flash", + input="Hello", + stream=True, + ) + events = [event async for event in stream] + assert len(events) == 1 + assert events[0].event_id == "e1" + assert events[0].interaction["id"] == "int-async-1" + finally: + await http_async_client.aclose() + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +def test_genaierror_raw_response_typing_httpx2(): + from ..._gaos.errors.genaierror import GenAiError + + resp = httpx2.Response( + 400, + headers={"x-test-header": "test-val"}, + content=b'{"error": "bad request"}', + ) + err = GenAiError("bad request error", raw_response=resp) + assert isinstance(err.raw_response, httpx2.Response) + assert isinstance(err.headers, httpx2.Headers) + assert err.status_code == 400 + assert err.body == '{"error": "bad request"}' + assert err.headers.get("x-test-header") == "test-val" + + +@pytest.mark.skipif( + httpx2 is None, reason="httpx2 not installed in this environment" +) +def test_gaos_works_when_httpx1_not_installed(monkeypatch): + from ..._gaos import basesdk + + monkeypatch.delenv("GOOGLE_GENAI_HTTP_CLIENT", raising=False) + monkeypatch.setattr(basesdk, "httpx1", None) + monkeypatch.setattr(compat_errors, "httpx1", None) + + def handler(request: httpx2.Request) -> httpx2.Response: + return httpx2.Response( + 200, + headers={"content-type": "application/json"}, + content=b'{"id": "no-httpx1-id", "status": "completed"}', + ) + + client = client_lib.Client( + api_key="fake-key", + http_options={"client_args": {"transport": httpx2.MockTransport(handler)}}, + ) + result = client.interactions.create( + model="gemini-1.5-flash", + input="Hello without httpx1", + ) + assert result.status == "completed" + +