google-genai 1.56.0__py3-none-any.whl → 1.58.0__py3-none-any.whl
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.
- google/genai/_api_client.py +49 -26
- google/genai/_interactions/__init__.py +3 -0
- google/genai/_interactions/_base_client.py +1 -1
- google/genai/_interactions/_client.py +57 -3
- google/genai/_interactions/_client_adapter.py +48 -0
- google/genai/_interactions/types/__init__.py +6 -0
- google/genai/_interactions/types/audio_content.py +2 -0
- google/genai/_interactions/types/audio_content_param.py +2 -0
- google/genai/_interactions/types/content.py +65 -0
- google/genai/_interactions/types/content_delta.py +10 -2
- google/genai/_interactions/types/content_param.py +63 -0
- google/genai/_interactions/types/content_start.py +5 -46
- google/genai/_interactions/types/content_stop.py +1 -2
- google/genai/_interactions/types/document_content.py +2 -0
- google/genai/_interactions/types/document_content_param.py +2 -0
- google/genai/_interactions/types/error_event.py +1 -2
- google/genai/_interactions/types/file_search_call_content.py +32 -0
- google/genai/_interactions/types/file_search_call_content_param.py +31 -0
- google/genai/_interactions/types/generation_config.py +4 -0
- google/genai/_interactions/types/generation_config_param.py +4 -0
- google/genai/_interactions/types/image_config.py +31 -0
- google/genai/_interactions/types/image_config_param.py +30 -0
- google/genai/_interactions/types/image_content.py +2 -0
- google/genai/_interactions/types/image_content_param.py +2 -0
- google/genai/_interactions/types/interaction.py +6 -52
- google/genai/_interactions/types/interaction_create_params.py +4 -22
- google/genai/_interactions/types/interaction_event.py +1 -2
- google/genai/_interactions/types/interaction_sse_event.py +5 -3
- google/genai/_interactions/types/interaction_status_update.py +1 -2
- google/genai/_interactions/types/model.py +1 -0
- google/genai/_interactions/types/model_param.py +1 -0
- google/genai/_interactions/types/turn.py +3 -44
- google/genai/_interactions/types/turn_param.py +4 -40
- google/genai/_interactions/types/usage.py +1 -1
- google/genai/_interactions/types/usage_param.py +1 -1
- google/genai/_interactions/types/video_content.py +2 -0
- google/genai/_interactions/types/video_content_param.py +2 -0
- google/genai/_live_converters.py +118 -34
- google/genai/_local_tokenizer_loader.py +1 -0
- google/genai/_tokens_converters.py +14 -14
- google/genai/_transformers.py +15 -21
- google/genai/batches.py +27 -22
- google/genai/caches.py +42 -42
- google/genai/chats.py +0 -2
- google/genai/client.py +61 -55
- google/genai/files.py +224 -0
- google/genai/live.py +1 -1
- google/genai/models.py +56 -44
- google/genai/tests/__init__.py +21 -0
- google/genai/tests/afc/__init__.py +21 -0
- google/genai/tests/afc/test_convert_if_exist_pydantic_model.py +309 -0
- google/genai/tests/afc/test_convert_number_values_for_function_call_args.py +63 -0
- google/genai/tests/afc/test_find_afc_incompatible_tool_indexes.py +240 -0
- google/genai/tests/afc/test_generate_content_stream_afc.py +530 -0
- google/genai/tests/afc/test_generate_content_stream_afc_thoughts.py +77 -0
- google/genai/tests/afc/test_get_function_map.py +176 -0
- google/genai/tests/afc/test_get_function_response_parts.py +277 -0
- google/genai/tests/afc/test_get_max_remote_calls_for_afc.py +130 -0
- google/genai/tests/afc/test_invoke_function_from_dict_args.py +241 -0
- google/genai/tests/afc/test_raise_error_for_afc_incompatible_config.py +159 -0
- google/genai/tests/afc/test_should_append_afc_history.py +53 -0
- google/genai/tests/afc/test_should_disable_afc.py +214 -0
- google/genai/tests/batches/__init__.py +17 -0
- google/genai/tests/batches/test_cancel.py +77 -0
- google/genai/tests/batches/test_create.py +78 -0
- google/genai/tests/batches/test_create_with_bigquery.py +113 -0
- google/genai/tests/batches/test_create_with_file.py +82 -0
- google/genai/tests/batches/test_create_with_gcs.py +125 -0
- google/genai/tests/batches/test_create_with_inlined_requests.py +255 -0
- google/genai/tests/batches/test_delete.py +86 -0
- google/genai/tests/batches/test_embedding.py +157 -0
- google/genai/tests/batches/test_get.py +78 -0
- google/genai/tests/batches/test_list.py +79 -0
- google/genai/tests/caches/__init__.py +17 -0
- google/genai/tests/caches/constants.py +29 -0
- google/genai/tests/caches/test_create.py +210 -0
- google/genai/tests/caches/test_create_custom_url.py +105 -0
- google/genai/tests/caches/test_delete.py +54 -0
- google/genai/tests/caches/test_delete_custom_url.py +52 -0
- google/genai/tests/caches/test_get.py +94 -0
- google/genai/tests/caches/test_get_custom_url.py +52 -0
- google/genai/tests/caches/test_list.py +68 -0
- google/genai/tests/caches/test_update.py +70 -0
- google/genai/tests/caches/test_update_custom_url.py +58 -0
- google/genai/tests/chats/__init__.py +1 -0
- google/genai/tests/chats/test_get_history.py +598 -0
- google/genai/tests/chats/test_send_message.py +844 -0
- google/genai/tests/chats/test_validate_response.py +90 -0
- google/genai/tests/client/__init__.py +17 -0
- google/genai/tests/client/test_async_stream.py +427 -0
- google/genai/tests/client/test_client_close.py +197 -0
- google/genai/tests/client/test_client_initialization.py +1687 -0
- google/genai/tests/client/test_client_requests.py +221 -0
- google/genai/tests/client/test_custom_client.py +104 -0
- google/genai/tests/client/test_http_options.py +178 -0
- google/genai/tests/client/test_replay_client_equality.py +168 -0
- google/genai/tests/client/test_retries.py +846 -0
- google/genai/tests/client/test_upload_errors.py +136 -0
- google/genai/tests/common/__init__.py +17 -0
- google/genai/tests/common/test_common.py +954 -0
- google/genai/tests/conftest.py +162 -0
- google/genai/tests/documents/__init__.py +17 -0
- google/genai/tests/documents/test_delete.py +51 -0
- google/genai/tests/documents/test_get.py +85 -0
- google/genai/tests/documents/test_list.py +72 -0
- google/genai/tests/errors/__init__.py +1 -0
- google/genai/tests/errors/test_api_error.py +417 -0
- google/genai/tests/file_search_stores/__init__.py +17 -0
- google/genai/tests/file_search_stores/test_create.py +66 -0
- google/genai/tests/file_search_stores/test_delete.py +64 -0
- google/genai/tests/file_search_stores/test_get.py +94 -0
- google/genai/tests/file_search_stores/test_import_file.py +112 -0
- google/genai/tests/file_search_stores/test_list.py +57 -0
- google/genai/tests/file_search_stores/test_upload_to_file_search_store.py +141 -0
- google/genai/tests/files/__init__.py +17 -0
- google/genai/tests/files/test_delete.py +46 -0
- google/genai/tests/files/test_download.py +85 -0
- google/genai/tests/files/test_get.py +46 -0
- google/genai/tests/files/test_list.py +72 -0
- google/genai/tests/files/test_register.py +272 -0
- google/genai/tests/files/test_register_table.py +70 -0
- google/genai/tests/files/test_upload.py +255 -0
- google/genai/tests/imports/test_no_optional_imports.py +28 -0
- google/genai/tests/interactions/test_auth.py +476 -0
- google/genai/tests/interactions/test_integration.py +84 -0
- google/genai/tests/interactions/test_paths.py +105 -0
- google/genai/tests/live/__init__.py +16 -0
- google/genai/tests/live/test_live.py +2143 -0
- google/genai/tests/live/test_live_music.py +362 -0
- google/genai/tests/live/test_live_response.py +163 -0
- google/genai/tests/live/test_send_client_content.py +147 -0
- google/genai/tests/live/test_send_realtime_input.py +268 -0
- google/genai/tests/live/test_send_tool_response.py +222 -0
- google/genai/tests/local_tokenizer/__init__.py +17 -0
- google/genai/tests/local_tokenizer/test_local_tokenizer.py +343 -0
- google/genai/tests/local_tokenizer/test_local_tokenizer_loader.py +235 -0
- google/genai/tests/mcp/__init__.py +17 -0
- google/genai/tests/mcp/test_has_mcp_tool_usage.py +89 -0
- google/genai/tests/mcp/test_mcp_to_gemini_tools.py +191 -0
- google/genai/tests/mcp/test_parse_config_for_mcp_sessions.py +201 -0
- google/genai/tests/mcp/test_parse_config_for_mcp_usage.py +130 -0
- google/genai/tests/mcp/test_set_mcp_usage_header.py +72 -0
- google/genai/tests/models/__init__.py +17 -0
- google/genai/tests/models/constants.py +8 -0
- google/genai/tests/models/test_compute_tokens.py +120 -0
- google/genai/tests/models/test_count_tokens.py +159 -0
- google/genai/tests/models/test_delete.py +107 -0
- google/genai/tests/models/test_edit_image.py +264 -0
- google/genai/tests/models/test_embed_content.py +94 -0
- google/genai/tests/models/test_function_call_streaming.py +442 -0
- google/genai/tests/models/test_generate_content.py +2501 -0
- google/genai/tests/models/test_generate_content_cached_content.py +132 -0
- google/genai/tests/models/test_generate_content_config_zero_value.py +103 -0
- google/genai/tests/models/test_generate_content_from_apikey.py +44 -0
- google/genai/tests/models/test_generate_content_http_options.py +40 -0
- google/genai/tests/models/test_generate_content_image_generation.py +143 -0
- google/genai/tests/models/test_generate_content_mcp.py +343 -0
- google/genai/tests/models/test_generate_content_media_resolution.py +97 -0
- google/genai/tests/models/test_generate_content_model.py +139 -0
- google/genai/tests/models/test_generate_content_part.py +821 -0
- google/genai/tests/models/test_generate_content_thought.py +76 -0
- google/genai/tests/models/test_generate_content_tools.py +1761 -0
- google/genai/tests/models/test_generate_images.py +191 -0
- google/genai/tests/models/test_generate_videos.py +759 -0
- google/genai/tests/models/test_get.py +104 -0
- google/genai/tests/models/test_list.py +233 -0
- google/genai/tests/models/test_recontext_image.py +189 -0
- google/genai/tests/models/test_segment_image.py +148 -0
- google/genai/tests/models/test_update.py +95 -0
- google/genai/tests/models/test_upscale_image.py +157 -0
- google/genai/tests/operations/__init__.py +17 -0
- google/genai/tests/operations/test_get.py +38 -0
- google/genai/tests/public_samples/__init__.py +17 -0
- google/genai/tests/public_samples/test_gemini_text_only.py +34 -0
- google/genai/tests/pytest_helper.py +246 -0
- google/genai/tests/shared/__init__.py +16 -0
- google/genai/tests/shared/batches/__init__.py +14 -0
- google/genai/tests/shared/batches/test_create_delete.py +57 -0
- google/genai/tests/shared/batches/test_create_get_cancel.py +56 -0
- google/genai/tests/shared/batches/test_list.py +40 -0
- google/genai/tests/shared/caches/__init__.py +14 -0
- google/genai/tests/shared/caches/test_create_get_delete.py +67 -0
- google/genai/tests/shared/caches/test_create_update_get.py +71 -0
- google/genai/tests/shared/caches/test_list.py +40 -0
- google/genai/tests/shared/chats/__init__.py +14 -0
- google/genai/tests/shared/chats/test_send_message.py +48 -0
- google/genai/tests/shared/chats/test_send_message_stream.py +50 -0
- google/genai/tests/shared/files/__init__.py +14 -0
- google/genai/tests/shared/files/test_list.py +41 -0
- google/genai/tests/shared/files/test_upload_get_delete.py +54 -0
- google/genai/tests/shared/models/__init__.py +14 -0
- google/genai/tests/shared/models/test_compute_tokens.py +41 -0
- google/genai/tests/shared/models/test_count_tokens.py +40 -0
- google/genai/tests/shared/models/test_edit_image.py +67 -0
- google/genai/tests/shared/models/test_embed.py +40 -0
- google/genai/tests/shared/models/test_generate_content.py +39 -0
- google/genai/tests/shared/models/test_generate_content_stream.py +54 -0
- google/genai/tests/shared/models/test_generate_images.py +40 -0
- google/genai/tests/shared/models/test_generate_videos.py +38 -0
- google/genai/tests/shared/models/test_list.py +37 -0
- google/genai/tests/shared/models/test_recontext_image.py +55 -0
- google/genai/tests/shared/models/test_segment_image.py +52 -0
- google/genai/tests/shared/models/test_upscale_image.py +52 -0
- google/genai/tests/shared/tunings/__init__.py +16 -0
- google/genai/tests/shared/tunings/test_create.py +46 -0
- google/genai/tests/shared/tunings/test_create_get_cancel.py +56 -0
- google/genai/tests/shared/tunings/test_list.py +39 -0
- google/genai/tests/tokens/__init__.py +16 -0
- google/genai/tests/tokens/test_create.py +154 -0
- google/genai/tests/transformers/__init__.py +17 -0
- google/genai/tests/transformers/test_blobs.py +84 -0
- google/genai/tests/transformers/test_bytes.py +15 -0
- google/genai/tests/transformers/test_duck_type.py +96 -0
- google/genai/tests/transformers/test_function_responses.py +72 -0
- google/genai/tests/transformers/test_schema.py +653 -0
- google/genai/tests/transformers/test_t_batch.py +286 -0
- google/genai/tests/transformers/test_t_content.py +160 -0
- google/genai/tests/transformers/test_t_contents.py +398 -0
- google/genai/tests/transformers/test_t_part.py +85 -0
- google/genai/tests/transformers/test_t_parts.py +87 -0
- google/genai/tests/transformers/test_t_tool.py +157 -0
- google/genai/tests/transformers/test_t_tools.py +195 -0
- google/genai/tests/tunings/__init__.py +16 -0
- google/genai/tests/tunings/test_cancel.py +39 -0
- google/genai/tests/tunings/test_end_to_end.py +106 -0
- google/genai/tests/tunings/test_get.py +67 -0
- google/genai/tests/tunings/test_list.py +75 -0
- google/genai/tests/tunings/test_tune.py +268 -0
- google/genai/tests/types/__init__.py +16 -0
- google/genai/tests/types/test_bytes_internal.py +271 -0
- google/genai/tests/types/test_bytes_type.py +152 -0
- google/genai/tests/types/test_future.py +101 -0
- google/genai/tests/types/test_optional_types.py +36 -0
- google/genai/tests/types/test_part_type.py +616 -0
- google/genai/tests/types/test_schema_from_json_schema.py +417 -0
- google/genai/tests/types/test_schema_json_schema.py +468 -0
- google/genai/tests/types/test_types.py +2903 -0
- google/genai/types.py +631 -488
- google/genai/version.py +1 -1
- {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/METADATA +6 -11
- google_genai-1.58.0.dist-info/RECORD +358 -0
- google_genai-1.56.0.dist-info/RECORD +0 -162
- /google/genai/{_interactions/py.typed → tests/interactions/__init__.py} +0 -0
- {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/WHEEL +0 -0
- {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/licenses/LICENSE +0 -0
- {google_genai-1.56.0.dist-info → google_genai-1.58.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
"""Tests for Interactions API."""
|
|
18
|
+
|
|
19
|
+
from ... import Client
|
|
20
|
+
from ... import _base_url
|
|
21
|
+
from unittest import mock
|
|
22
|
+
import pytest
|
|
23
|
+
from httpx import Request, Response
|
|
24
|
+
from ..._api_client import AsyncHttpxClient, BaseApiClient
|
|
25
|
+
from httpx import Client as HTTPClient
|
|
26
|
+
import os
|
|
27
|
+
|
|
28
|
+
ENV_VARS = [
|
|
29
|
+
"GOOGLE_CLOUD_PROJECT",
|
|
30
|
+
"GEMINI_API_KEY",
|
|
31
|
+
"GOOGLE_API_KEY",
|
|
32
|
+
"GOOGLE_CLOUD_LOCATION",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
@pytest.fixture(autouse=True)
|
|
36
|
+
def clear_env_vars(monkeypatch):
|
|
37
|
+
for var in ENV_VARS:
|
|
38
|
+
monkeypatch.delenv(var, raising=False)
|
|
39
|
+
|
|
40
|
+
def test_interactions_gemini_url(monkeypatch):
|
|
41
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
42
|
+
client = Client()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
with mock.patch.object(HTTPClient, "send") as mock_send:
|
|
46
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
47
|
+
client.interactions.create(
|
|
48
|
+
model='gemini-1.5-flash',
|
|
49
|
+
input='Hello',
|
|
50
|
+
)
|
|
51
|
+
mock_send.assert_called_once()
|
|
52
|
+
request = mock_send.call_args[0][0]
|
|
53
|
+
assert str(request.url).endswith('/v1beta/interactions')
|
|
54
|
+
assert request.headers['x-goog-api-key'] == 'test-api-key'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_interactions_gemini_no_vertex_auth(monkeypatch):
|
|
58
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
59
|
+
client = Client()
|
|
60
|
+
|
|
61
|
+
with (
|
|
62
|
+
mock.patch.object(BaseApiClient, "_access_token") as mock_access_token,
|
|
63
|
+
mock.patch.object(HTTPClient, "send") as mock_send,
|
|
64
|
+
):
|
|
65
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
66
|
+
client.interactions.create(
|
|
67
|
+
model='gemini-1.5-flash',
|
|
68
|
+
input='Hello',
|
|
69
|
+
)
|
|
70
|
+
mock_access_token.assert_not_called()
|
|
71
|
+
|
|
72
|
+
def test_interactions_gemini_retry(monkeypatch):
|
|
73
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
74
|
+
client = Client()
|
|
75
|
+
client._api_client.max_retries = 2
|
|
76
|
+
|
|
77
|
+
with mock.patch.object(HTTPClient, "send") as mock_send:
|
|
78
|
+
mock_send.side_effect = [
|
|
79
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
80
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
81
|
+
Response(200, request=Request('POST', '')),
|
|
82
|
+
]
|
|
83
|
+
client.interactions.create(model='gemini-1.5-flash', input='Hello')
|
|
84
|
+
assert mock_send.call_count == 3
|
|
85
|
+
|
|
86
|
+
def test_interactions_gemini_extra_headers(monkeypatch):
|
|
87
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
88
|
+
client = Client()
|
|
89
|
+
|
|
90
|
+
with mock.patch.object(HTTPClient, "send") as mock_send:
|
|
91
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
92
|
+
client.interactions.create(
|
|
93
|
+
model='gemini-1.5-flash',
|
|
94
|
+
input='Hello',
|
|
95
|
+
extra_headers={'X-Custom-Header': 'TestValue'}
|
|
96
|
+
)
|
|
97
|
+
mock_send.assert_called_once()
|
|
98
|
+
request = mock_send.call_args[0][0]
|
|
99
|
+
assert request.headers['x-custom-header'] == 'TestValue'
|
|
100
|
+
assert request.headers['x-goog-api-key'] == 'test-api-key'
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_interactions_vertex_auth_header():
|
|
104
|
+
from ..._api_client import BaseApiClient
|
|
105
|
+
from ..._interactions._base_client import SyncAPIClient
|
|
106
|
+
from httpx import Client as HTTPClient
|
|
107
|
+
|
|
108
|
+
creds = mock.Mock()
|
|
109
|
+
creds.quota_project_id = "test-quota-project"
|
|
110
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
111
|
+
|
|
112
|
+
with (
|
|
113
|
+
mock.patch.object(
|
|
114
|
+
BaseApiClient, "_access_token", return_value='fake-vertex-token'
|
|
115
|
+
) as mock_access_token,
|
|
116
|
+
mock.patch.object(
|
|
117
|
+
HTTPClient, "send",
|
|
118
|
+
return_value=mock.Mock(),
|
|
119
|
+
) as mock_send,
|
|
120
|
+
):
|
|
121
|
+
|
|
122
|
+
response = client.interactions.create(
|
|
123
|
+
model='gemini-2.5-flash',
|
|
124
|
+
input='What is the largest planet in our solar system?',
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
mock_send.assert_called_once()
|
|
128
|
+
mock_access_token.assert_called_once()
|
|
129
|
+
args, kwargs = mock_send.call_args
|
|
130
|
+
headers = args[0].headers
|
|
131
|
+
assert any(
|
|
132
|
+
key == "authorization" and value == 'Bearer fake-vertex-token'
|
|
133
|
+
for key, value in headers.items())
|
|
134
|
+
assert any(
|
|
135
|
+
key == "x-goog-user-project" and value == 'test-quota-project'
|
|
136
|
+
for key, value in headers.items())
|
|
137
|
+
|
|
138
|
+
def test_interactions_vertex_key_no_auth_header():
|
|
139
|
+
from ..._api_client import BaseApiClient
|
|
140
|
+
from httpx import Client as HTTPClient
|
|
141
|
+
|
|
142
|
+
creds = mock.Mock()
|
|
143
|
+
client = Client(vertexai=True, api_key='test-api-key')
|
|
144
|
+
|
|
145
|
+
with (
|
|
146
|
+
mock.patch.object(
|
|
147
|
+
BaseApiClient, "_access_token", return_value='fake-vertex-token'
|
|
148
|
+
) as mock_access_token,
|
|
149
|
+
mock.patch.object(
|
|
150
|
+
HTTPClient, "send",
|
|
151
|
+
return_value=mock.Mock(),
|
|
152
|
+
) as mock_send,
|
|
153
|
+
):
|
|
154
|
+
|
|
155
|
+
response = client.interactions.create(
|
|
156
|
+
model='gemini-2.5-flash',
|
|
157
|
+
input='What is the largest planet in our solar system?',
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
mock_send.assert_called_once()
|
|
161
|
+
mock_access_token.assert_not_called()
|
|
162
|
+
args, kwargs = mock_send.call_args
|
|
163
|
+
headers = args[0].headers
|
|
164
|
+
assert any(
|
|
165
|
+
key == "x-goog-api-key" and value == 'test-api-key'
|
|
166
|
+
for key, value in headers.items())
|
|
167
|
+
|
|
168
|
+
def test_interactions_vertex_url():
|
|
169
|
+
creds = mock.Mock()
|
|
170
|
+
creds.quota_project_id = "test-quota-project"
|
|
171
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
172
|
+
|
|
173
|
+
with mock.patch("httpx.Client.send") as mock_send:
|
|
174
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
175
|
+
client.interactions.create(
|
|
176
|
+
model='gemini-1.5-flash',
|
|
177
|
+
input='Hello',
|
|
178
|
+
)
|
|
179
|
+
mock_send.assert_called_once()
|
|
180
|
+
request = mock_send.call_args[0][0]
|
|
181
|
+
assert str(request.url) == 'https://us-central1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/us-central1/interactions'
|
|
182
|
+
|
|
183
|
+
def test_interactions_vertex_auth_refresh_on_retry():
|
|
184
|
+
from ..._api_client import BaseApiClient
|
|
185
|
+
from httpx import Client as HTTPClient
|
|
186
|
+
|
|
187
|
+
creds = mock.Mock()
|
|
188
|
+
creds.quota_project_id = "test-quota-project"
|
|
189
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
190
|
+
client._api_client.max_retries = 2
|
|
191
|
+
|
|
192
|
+
token_values = ['token1', 'token2', 'token3']
|
|
193
|
+
token_iter = iter(token_values)
|
|
194
|
+
|
|
195
|
+
def get_token():
|
|
196
|
+
return next(token_iter)
|
|
197
|
+
|
|
198
|
+
with (
|
|
199
|
+
mock.patch.object(BaseApiClient, "_access_token", side_effect=get_token) as mock_access_token,
|
|
200
|
+
mock.patch.object(HTTPClient, "send") as mock_send,
|
|
201
|
+
):
|
|
202
|
+
mock_send.side_effect = [
|
|
203
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
204
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
205
|
+
Response(200, request=Request('POST', '')),
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
client.interactions.create(model='gemini-1.5-flash', input='Hello')
|
|
209
|
+
|
|
210
|
+
assert mock_access_token.call_count == 3
|
|
211
|
+
assert mock_send.call_count == 3
|
|
212
|
+
# Check headers of each call
|
|
213
|
+
for i in range(3):
|
|
214
|
+
headers = mock_send.call_args_list[i][0][0].headers
|
|
215
|
+
assert headers['authorization'] == f'Bearer {token_values[i]}'
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_interactions_vertex_extra_headers_override():
|
|
219
|
+
from ..._api_client import BaseApiClient
|
|
220
|
+
from httpx import Client as HTTPClient
|
|
221
|
+
|
|
222
|
+
creds = mock.Mock()
|
|
223
|
+
creds.quota_project_id = "test-quota-project"
|
|
224
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
225
|
+
|
|
226
|
+
with (
|
|
227
|
+
mock.patch.object(BaseApiClient, "_access_token", return_value='default-token') as mock_access_token,
|
|
228
|
+
mock.patch.object(HTTPClient, "send") as mock_send,
|
|
229
|
+
):
|
|
230
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
231
|
+
|
|
232
|
+
# Override Authorization
|
|
233
|
+
client.interactions.create(
|
|
234
|
+
model='gemini-1.5-flash',
|
|
235
|
+
input='Hello',
|
|
236
|
+
extra_headers={'Authorization': 'Bearer manual-token'}
|
|
237
|
+
)
|
|
238
|
+
mock_send.assert_called_once()
|
|
239
|
+
headers = mock_send.call_args[0][0].headers
|
|
240
|
+
assert headers['authorization'] == 'Bearer manual-token'
|
|
241
|
+
mock_access_token.assert_not_called() # Should not fetch default token
|
|
242
|
+
|
|
243
|
+
mock_send.reset_mock()
|
|
244
|
+
mock_access_token.reset_mock()
|
|
245
|
+
|
|
246
|
+
# Provide API Key
|
|
247
|
+
client.interactions.create(
|
|
248
|
+
model='gemini-1.5-flash',
|
|
249
|
+
input='Hello',
|
|
250
|
+
extra_headers={'x-goog-api-key': 'manual-key'}
|
|
251
|
+
)
|
|
252
|
+
mock_send.assert_called_once()
|
|
253
|
+
headers = mock_send.call_args[0][0].headers
|
|
254
|
+
assert headers['x-goog-api-key'] == 'manual-key'
|
|
255
|
+
assert 'authorization' not in headers
|
|
256
|
+
mock_access_token.assert_not_called()
|
|
257
|
+
|
|
258
|
+
@pytest.mark.asyncio
|
|
259
|
+
async def test_async_interactions_gemini_url(monkeypatch):
|
|
260
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
261
|
+
client = Client()
|
|
262
|
+
|
|
263
|
+
with mock.patch.object(AsyncHttpxClient, "send") as mock_send:
|
|
264
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
265
|
+
await client.aio.interactions.create(
|
|
266
|
+
model='gemini-1.5-flash',
|
|
267
|
+
input='Hello',
|
|
268
|
+
)
|
|
269
|
+
mock_send.assert_called_once()
|
|
270
|
+
request = mock_send.call_args[0][0]
|
|
271
|
+
assert str(request.url).endswith('/v1beta/interactions')
|
|
272
|
+
assert request.headers['x-goog-api-key'] == 'test-api-key'
|
|
273
|
+
|
|
274
|
+
@pytest.mark.asyncio
|
|
275
|
+
async def test_async_interactions_gemini_no_vertex_auth(monkeypatch):
|
|
276
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
277
|
+
client = Client()
|
|
278
|
+
|
|
279
|
+
with (
|
|
280
|
+
mock.patch.object(BaseApiClient, "_async_access_token") as mock_access_token,
|
|
281
|
+
mock.patch.object(AsyncHttpxClient, "send") as mock_send,
|
|
282
|
+
):
|
|
283
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
284
|
+
await client.aio.interactions.create(
|
|
285
|
+
model='gemini-1.5-flash',
|
|
286
|
+
input='Hello',
|
|
287
|
+
)
|
|
288
|
+
mock_access_token.assert_not_called()
|
|
289
|
+
|
|
290
|
+
@pytest.mark.asyncio
|
|
291
|
+
async def test_async_interactions_gemini_retry(monkeypatch):
|
|
292
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
293
|
+
client = Client()
|
|
294
|
+
client.aio._api_client.max_retries = 2
|
|
295
|
+
|
|
296
|
+
with mock.patch.object(AsyncHttpxClient, "send") as mock_send:
|
|
297
|
+
mock_send.side_effect = [
|
|
298
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
299
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
300
|
+
Response(200, request=Request('POST', '')),
|
|
301
|
+
]
|
|
302
|
+
await client.aio.interactions.create(model='gemini-1.5-flash', input='Hello')
|
|
303
|
+
assert mock_send.call_count == 3
|
|
304
|
+
|
|
305
|
+
@pytest.mark.asyncio
|
|
306
|
+
async def test_async_interactions_gemini_extra_headers(monkeypatch):
|
|
307
|
+
monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key')
|
|
308
|
+
client = Client()
|
|
309
|
+
|
|
310
|
+
with mock.patch.object(AsyncHttpxClient, "send") as mock_send:
|
|
311
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
312
|
+
await client.aio.interactions.create(
|
|
313
|
+
model='gemini-1.5-flash',
|
|
314
|
+
input='Hello',
|
|
315
|
+
extra_headers={'X-Custom-Header': 'TestValue'}
|
|
316
|
+
)
|
|
317
|
+
mock_send.assert_called_once()
|
|
318
|
+
request = mock_send.call_args[0][0]
|
|
319
|
+
assert request.headers['x-custom-header'] == 'TestValue'
|
|
320
|
+
assert request.headers['x-goog-api-key'] == 'test-api-key'
|
|
321
|
+
|
|
322
|
+
@pytest.mark.asyncio
|
|
323
|
+
async def test_async_interactions_vertex_auth_header():
|
|
324
|
+
from ..._api_client import BaseApiClient
|
|
325
|
+
from ..._interactions._base_client import SyncAPIClient
|
|
326
|
+
from ..._api_client import AsyncHttpxClient
|
|
327
|
+
|
|
328
|
+
creds = mock.Mock()
|
|
329
|
+
creds.quota_project_id = "test-quota-project"
|
|
330
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
331
|
+
|
|
332
|
+
with (
|
|
333
|
+
mock.patch.object(
|
|
334
|
+
BaseApiClient, "_async_access_token", return_value='fake-vertex-token'
|
|
335
|
+
) as mock_access_token,
|
|
336
|
+
mock.patch.object(
|
|
337
|
+
AsyncHttpxClient, "send",
|
|
338
|
+
return_value=mock.Mock(),
|
|
339
|
+
) as mock_send,
|
|
340
|
+
):
|
|
341
|
+
|
|
342
|
+
response = await client.aio.interactions.create(
|
|
343
|
+
model='gemini-2.5-flash',
|
|
344
|
+
input='What is the largest planet in our solar system?',
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
mock_send.assert_called_once()
|
|
348
|
+
mock_access_token.assert_called_once()
|
|
349
|
+
args, kwargs = mock_send.call_args
|
|
350
|
+
headers = args[0].headers
|
|
351
|
+
assert any(
|
|
352
|
+
key == "authorization" and value == 'Bearer fake-vertex-token'
|
|
353
|
+
for key, value in headers.items())
|
|
354
|
+
assert any(
|
|
355
|
+
key == "x-goog-user-project" and value == 'test-quota-project'
|
|
356
|
+
for key, value in headers.items())
|
|
357
|
+
|
|
358
|
+
@pytest.mark.asyncio
|
|
359
|
+
async def test_async_interactions_vertex_key_no_auth_header():
|
|
360
|
+
from ..._api_client import BaseApiClient
|
|
361
|
+
client = Client(vertexai=True, api_key='test-api-key')
|
|
362
|
+
|
|
363
|
+
with (
|
|
364
|
+
mock.patch.object(
|
|
365
|
+
BaseApiClient, "_async_access_token", return_value='fake-vertex-token'
|
|
366
|
+
) as mock_access_token,
|
|
367
|
+
mock.patch.object(
|
|
368
|
+
AsyncHttpxClient, "send",
|
|
369
|
+
return_value=mock.Mock(),
|
|
370
|
+
) as mock_send,
|
|
371
|
+
):
|
|
372
|
+
|
|
373
|
+
response = await client.aio.interactions.create(
|
|
374
|
+
model='gemini-2.5-flash',
|
|
375
|
+
input='What is the largest planet in our solar system?',
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
mock_send.assert_called_once()
|
|
379
|
+
mock_access_token.assert_not_called()
|
|
380
|
+
args, kwargs = mock_send.call_args
|
|
381
|
+
headers = args[0].headers
|
|
382
|
+
assert any(
|
|
383
|
+
key == "x-goog-api-key" and value == 'test-api-key'
|
|
384
|
+
for key, value in headers.items())
|
|
385
|
+
|
|
386
|
+
@pytest.mark.asyncio
|
|
387
|
+
async def test_async_interactions_vertex_url():
|
|
388
|
+
from ..._api_client import AsyncHttpxClient
|
|
389
|
+
creds = mock.Mock()
|
|
390
|
+
creds.quota_project_id = "test-quota-project"
|
|
391
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
392
|
+
|
|
393
|
+
with mock.patch.object(AsyncHttpxClient, "send") as mock_send:
|
|
394
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
395
|
+
await client.aio.interactions.create(
|
|
396
|
+
model='gemini-1.5-flash',
|
|
397
|
+
input='Hello',
|
|
398
|
+
)
|
|
399
|
+
mock_send.assert_called_once()
|
|
400
|
+
request = mock_send.call_args[0][0]
|
|
401
|
+
assert str(request.url) == 'https://us-central1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/us-central1/interactions'
|
|
402
|
+
|
|
403
|
+
@pytest.mark.asyncio
|
|
404
|
+
async def test_async_interactions_vertex_auth_refresh_on_retry():
|
|
405
|
+
from ..._api_client import BaseApiClient
|
|
406
|
+
from ..._api_client import AsyncHttpxClient
|
|
407
|
+
|
|
408
|
+
creds = mock.Mock()
|
|
409
|
+
creds.quota_project_id = "test-quota-project"
|
|
410
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
411
|
+
client.aio._api_client.max_retries = 2
|
|
412
|
+
|
|
413
|
+
token_values = ['token1', 'token2', 'token3']
|
|
414
|
+
token_iter = iter(token_values)
|
|
415
|
+
|
|
416
|
+
async def get_token():
|
|
417
|
+
return next(token_iter)
|
|
418
|
+
|
|
419
|
+
with (
|
|
420
|
+
mock.patch.object(BaseApiClient, "_async_access_token", side_effect=get_token) as mock_access_token,
|
|
421
|
+
mock.patch.object(AsyncHttpxClient, "send") as mock_send,
|
|
422
|
+
):
|
|
423
|
+
mock_send.side_effect = [
|
|
424
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
425
|
+
Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}),
|
|
426
|
+
Response(200, request=Request('POST', '')),
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
await client.aio.interactions.create(model='gemini-1.5-flash', input='Hello')
|
|
430
|
+
|
|
431
|
+
assert mock_access_token.call_count == 3
|
|
432
|
+
assert mock_send.call_count == 3
|
|
433
|
+
for i in range(3):
|
|
434
|
+
headers = mock_send.call_args_list[i][0][0].headers
|
|
435
|
+
assert headers['authorization'] == f'Bearer {token_values[i]}'
|
|
436
|
+
|
|
437
|
+
@pytest.mark.asyncio
|
|
438
|
+
async def test_async_interactions_vertex_extra_headers_override():
|
|
439
|
+
from ..._api_client import BaseApiClient
|
|
440
|
+
from ..._api_client import AsyncHttpxClient
|
|
441
|
+
|
|
442
|
+
creds = mock.Mock()
|
|
443
|
+
creds.quota_project_id = "test-quota-project"
|
|
444
|
+
client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds)
|
|
445
|
+
|
|
446
|
+
with (
|
|
447
|
+
mock.patch.object(BaseApiClient, "_async_access_token", return_value='default-token') as mock_access_token,
|
|
448
|
+
mock.patch.object(AsyncHttpxClient, "send") as mock_send,
|
|
449
|
+
):
|
|
450
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
451
|
+
|
|
452
|
+
# Override Authorization
|
|
453
|
+
await client.aio.interactions.create(
|
|
454
|
+
model='gemini-1.5-flash',
|
|
455
|
+
input='Hello',
|
|
456
|
+
extra_headers={'Authorization': 'Bearer manual-token'}
|
|
457
|
+
)
|
|
458
|
+
mock_send.assert_called_once()
|
|
459
|
+
headers = mock_send.call_args[0][0].headers
|
|
460
|
+
assert headers['authorization'] == 'Bearer manual-token'
|
|
461
|
+
mock_access_token.assert_not_called()
|
|
462
|
+
|
|
463
|
+
mock_send.reset_mock()
|
|
464
|
+
mock_access_token.reset_mock()
|
|
465
|
+
|
|
466
|
+
# Provide API Key
|
|
467
|
+
await client.aio.interactions.create(
|
|
468
|
+
model='gemini-1.5-flash',
|
|
469
|
+
input='Hello',
|
|
470
|
+
extra_headers={'x-goog-api-key': 'manual-key'}
|
|
471
|
+
)
|
|
472
|
+
mock_send.assert_called_once()
|
|
473
|
+
headers = mock_send.call_args[0][0].headers
|
|
474
|
+
assert headers['x-goog-api-key'] == 'manual-key'
|
|
475
|
+
assert 'authorization' not in headers
|
|
476
|
+
mock_access_token.assert_not_called()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from unittest import mock
|
|
16
|
+
import pytest
|
|
17
|
+
from ... import client as client_lib
|
|
18
|
+
|
|
19
|
+
pytest_plugins = ("pytest_asyncio",)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_client_future_warning():
|
|
23
|
+
with mock.patch.object(
|
|
24
|
+
client_lib, "_interactions_experimental_warned", new=False
|
|
25
|
+
):
|
|
26
|
+
client = client_lib.Client(
|
|
27
|
+
api_key="placeholder",
|
|
28
|
+
http_options={
|
|
29
|
+
"api_version": "v1alpha",
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
with pytest.warns(
|
|
33
|
+
UserWarning, match="Interactions.*experimental"
|
|
34
|
+
):
|
|
35
|
+
_ = client.interactions
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_client_timeout():
|
|
39
|
+
with mock.patch.object(
|
|
40
|
+
client_lib, "GeminiNextGenAPIClient", spec_set=True
|
|
41
|
+
) as mock_nextgen_client:
|
|
42
|
+
|
|
43
|
+
client = client_lib.Client(
|
|
44
|
+
api_key="placeholder",
|
|
45
|
+
http_options={"api_version": "v1alpha", "timeout": 5000},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
_ = client.interactions
|
|
49
|
+
|
|
50
|
+
mock_nextgen_client.assert_called_once_with(
|
|
51
|
+
base_url=mock.ANY,
|
|
52
|
+
api_key="placeholder",
|
|
53
|
+
api_version="v1alpha",
|
|
54
|
+
default_headers=mock.ANY,
|
|
55
|
+
http_client=mock.ANY,
|
|
56
|
+
timeout=5.0,
|
|
57
|
+
max_retries=mock.ANY,
|
|
58
|
+
client_adapter=mock.ANY,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.asyncio
|
|
63
|
+
async def test_async_client_timeout():
|
|
64
|
+
with mock.patch.object(
|
|
65
|
+
client_lib, "AsyncGeminiNextGenAPIClient", spec_set=True
|
|
66
|
+
) as mock_nextgen_client:
|
|
67
|
+
|
|
68
|
+
client = client_lib.Client(
|
|
69
|
+
api_key="placeholder",
|
|
70
|
+
http_options={"api_version": "v1alpha", "timeout": 5000},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
_ = client.aio.interactions
|
|
74
|
+
|
|
75
|
+
mock_nextgen_client.assert_called_once_with(
|
|
76
|
+
base_url=mock.ANY,
|
|
77
|
+
api_key="placeholder",
|
|
78
|
+
api_version="v1alpha",
|
|
79
|
+
default_headers=mock.ANY,
|
|
80
|
+
http_client=mock.ANY,
|
|
81
|
+
timeout=5.0,
|
|
82
|
+
max_retries=mock.ANY,
|
|
83
|
+
client_adapter=mock.ANY,
|
|
84
|
+
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
|
|
2
|
+
# Copyright 2025 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
"""Tests for Interactions API URL paths."""
|
|
19
|
+
|
|
20
|
+
from unittest import mock
|
|
21
|
+
import pytest
|
|
22
|
+
from httpx import Request, Response
|
|
23
|
+
from ..._api_client import AsyncHttpxClient
|
|
24
|
+
from httpx import Client as HTTPClient
|
|
25
|
+
from .. import pytest_helper
|
|
26
|
+
import google.auth
|
|
27
|
+
|
|
28
|
+
@mock.patch.object(google.auth, "default", autospec=True)
|
|
29
|
+
def test_interactions_paths(mock_auth_default, client):
|
|
30
|
+
interaction_id = "test-interaction-id"
|
|
31
|
+
|
|
32
|
+
mock_creds = mock.Mock()
|
|
33
|
+
mock_creds.token = "test-token"
|
|
34
|
+
mock_creds.expired = False
|
|
35
|
+
mock_creds.quota_project_id = "test-quota-project"
|
|
36
|
+
mock_auth_default.return_value = (mock_creds, "test-project")
|
|
37
|
+
|
|
38
|
+
if client._api_client.vertexai:
|
|
39
|
+
expected_base_url = f'https://{client._api_client.location}-aiplatform.googleapis.com/v1beta1/projects/{client._api_client.project}/locations/{client._api_client.location}'
|
|
40
|
+
else:
|
|
41
|
+
expected_base_url = "https://generativelanguage.googleapis.com/v1beta"
|
|
42
|
+
|
|
43
|
+
with mock.patch.object(HTTPClient, "send") as mock_send:
|
|
44
|
+
mock_send.return_value = Response(200, request=Request('GET', ''))
|
|
45
|
+
client.interactions.get(id=interaction_id)
|
|
46
|
+
mock_send.assert_called_once()
|
|
47
|
+
request = mock_send.call_args[0][0]
|
|
48
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}'
|
|
49
|
+
|
|
50
|
+
mock_send.reset_mock()
|
|
51
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
52
|
+
client.interactions.cancel(id=interaction_id)
|
|
53
|
+
mock_send.assert_called_once()
|
|
54
|
+
request = mock_send.call_args[0][0]
|
|
55
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}/cancel'
|
|
56
|
+
|
|
57
|
+
mock_send.reset_mock()
|
|
58
|
+
mock_send.return_value = Response(200, request=Request('DELETE', ''))
|
|
59
|
+
client.interactions.delete(id=interaction_id)
|
|
60
|
+
mock_send.assert_called_once()
|
|
61
|
+
request = mock_send.call_args[0][0]
|
|
62
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}'
|
|
63
|
+
|
|
64
|
+
@pytest.mark.asyncio
|
|
65
|
+
@mock.patch.object(google.auth, "default", autospec=True)
|
|
66
|
+
async def test_async_interactions_paths(mock_auth_default, client):
|
|
67
|
+
interaction_id = "test-interaction-id"
|
|
68
|
+
|
|
69
|
+
mock_creds = mock.Mock()
|
|
70
|
+
mock_creds.token = "test-token"
|
|
71
|
+
mock_creds.expired = False
|
|
72
|
+
mock_creds.quota_project_id = "test-quota-project"
|
|
73
|
+
mock_auth_default.return_value = (mock_creds, "test-project")
|
|
74
|
+
|
|
75
|
+
if client._api_client.vertexai:
|
|
76
|
+
expected_base_url = f'https://{client._api_client.location}-aiplatform.googleapis.com/v1beta1/projects/{client._api_client.project}/locations/{client._api_client.location}'
|
|
77
|
+
else:
|
|
78
|
+
expected_base_url = "https://generativelanguage.googleapis.com/v1beta"
|
|
79
|
+
|
|
80
|
+
with mock.patch.object(AsyncHttpxClient, "send") as mock_send:
|
|
81
|
+
mock_send.return_value = Response(200, request=Request('GET', ''))
|
|
82
|
+
await client.aio.interactions.get(id=interaction_id)
|
|
83
|
+
mock_send.assert_called_once()
|
|
84
|
+
request = mock_send.call_args[0][0]
|
|
85
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}'
|
|
86
|
+
|
|
87
|
+
mock_send.reset_mock()
|
|
88
|
+
mock_send.return_value = Response(200, request=Request('POST', ''))
|
|
89
|
+
await client.aio.interactions.cancel(id=interaction_id)
|
|
90
|
+
mock_send.assert_called_once()
|
|
91
|
+
request = mock_send.call_args[0][0]
|
|
92
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}/cancel'
|
|
93
|
+
|
|
94
|
+
mock_send.reset_mock()
|
|
95
|
+
mock_send.return_value = Response(200, request=Request('DELETE', ''))
|
|
96
|
+
await client.aio.interactions.delete(id=interaction_id)
|
|
97
|
+
mock_send.assert_called_once()
|
|
98
|
+
request = mock_send.call_args[0][0]
|
|
99
|
+
assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}'
|
|
100
|
+
|
|
101
|
+
pytestmark = pytest_helper.setup(
|
|
102
|
+
file=__file__,
|
|
103
|
+
globals_for_file=globals(),
|
|
104
|
+
test_table=[],
|
|
105
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
|