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,95 @@
|
|
|
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
|
+
import pytest
|
|
18
|
+
from ... import errors
|
|
19
|
+
from ... import types
|
|
20
|
+
from .. import pytest_helper
|
|
21
|
+
|
|
22
|
+
test_http_options = {'headers': {'test': 'headers'}}
|
|
23
|
+
|
|
24
|
+
test_table: list[pytest_helper.TestTableItem] = [
|
|
25
|
+
pytest_helper.TestTableItem(
|
|
26
|
+
name='test_vertex_tuned_models_update',
|
|
27
|
+
parameters=types._UpdateModelParameters(
|
|
28
|
+
model='models/2171259487439028224',
|
|
29
|
+
config={
|
|
30
|
+
'description': (
|
|
31
|
+
'My SupervisedTuningJob'
|
|
32
|
+
),
|
|
33
|
+
'default_checkpoint_id': '8',
|
|
34
|
+
},
|
|
35
|
+
),
|
|
36
|
+
exception_if_mldev='404',
|
|
37
|
+
),
|
|
38
|
+
pytest_helper.TestTableItem(
|
|
39
|
+
name='test_vertex_tuned_models_update_with_http_options_in_method',
|
|
40
|
+
parameters=types._UpdateModelParameters(
|
|
41
|
+
model='models/2171259487439028224',
|
|
42
|
+
config={
|
|
43
|
+
'description': (
|
|
44
|
+
'My SupervisedTuningJob'
|
|
45
|
+
),
|
|
46
|
+
'default_checkpoint_id': '8',
|
|
47
|
+
'http_options': test_http_options,
|
|
48
|
+
},
|
|
49
|
+
),
|
|
50
|
+
exception_if_mldev='404',
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
pytestmark = pytest_helper.setup(
|
|
55
|
+
file=__file__,
|
|
56
|
+
globals_for_file=globals(),
|
|
57
|
+
test_method='models.update',
|
|
58
|
+
test_table=test_table,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.asyncio
|
|
63
|
+
async def test_async_update_tuned_model(client):
|
|
64
|
+
if client._api_client.vertexai:
|
|
65
|
+
with pytest.raises(errors.ClientError) as e:
|
|
66
|
+
await client.aio.models.update(
|
|
67
|
+
model='tunedModels/generatenum5443-ekrw7ie9wis23zbeogbw6jq8',
|
|
68
|
+
config={
|
|
69
|
+
'description': 'My tuned gemini model',
|
|
70
|
+
'http_options': test_http_options,
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
assert '404' in str(e)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.mark.asyncio
|
|
77
|
+
async def test_async_update_model(client):
|
|
78
|
+
if client._api_client.vertexai:
|
|
79
|
+
response = await client.aio.models.update(
|
|
80
|
+
model='models/2171259487439028224',
|
|
81
|
+
config={
|
|
82
|
+
'display_name': 'My tuned gemini model',
|
|
83
|
+
'http_options': test_http_options,
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
with pytest.raises(errors.ClientError) as e:
|
|
88
|
+
await client.aio.models.update(
|
|
89
|
+
model='models/2171259487439028224',
|
|
90
|
+
config={
|
|
91
|
+
'display_name': 'My tuned gemini model',
|
|
92
|
+
'http_options': test_http_options,
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
assert '404' in str(e)
|
|
@@ -0,0 +1,157 @@
|
|
|
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 upscale_image."""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
from pydantic import ValidationError
|
|
22
|
+
import pytest
|
|
23
|
+
|
|
24
|
+
from ... import types
|
|
25
|
+
from .. import pytest_helper
|
|
26
|
+
|
|
27
|
+
IMAGEN_MODEL_LATEST = 'imagen-4.0-upscale-preview'
|
|
28
|
+
|
|
29
|
+
IMAGE_FILE_PATH = os.path.abspath(
|
|
30
|
+
os.path.join(os.path.dirname(__file__), '../data/bridge1.png')
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
test_table: list[pytest_helper.TestTableItem] = [
|
|
34
|
+
pytest_helper.TestTableItem(
|
|
35
|
+
name='test_upscale_no_config',
|
|
36
|
+
exception_if_mldev='only supported in the Vertex AI client',
|
|
37
|
+
parameters=types.UpscaleImageParameters(
|
|
38
|
+
model=IMAGEN_MODEL_LATEST,
|
|
39
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
40
|
+
upscale_factor='x2',
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
pytest_helper.TestTableItem(
|
|
44
|
+
name='test_upscale',
|
|
45
|
+
exception_if_mldev='only supported in the Vertex AI client',
|
|
46
|
+
parameters=types.UpscaleImageParameters(
|
|
47
|
+
model=IMAGEN_MODEL_LATEST,
|
|
48
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
49
|
+
upscale_factor='x2',
|
|
50
|
+
config=types.UpscaleImageConfig(
|
|
51
|
+
include_rai_reason=True,
|
|
52
|
+
person_generation=types.PersonGeneration.ALLOW_ADULT,
|
|
53
|
+
safety_filter_level=types.SafetyFilterLevel.BLOCK_LOW_AND_ABOVE,
|
|
54
|
+
output_mime_type='image/jpeg',
|
|
55
|
+
output_compression_quality=80,
|
|
56
|
+
enhance_input_image=True,
|
|
57
|
+
image_preservation_factor=0.6,
|
|
58
|
+
labels={'imagen_label_key': 'upscale_image'}
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
pytest_helper.TestTableItem(
|
|
63
|
+
name='test_upscale_gcs',
|
|
64
|
+
exception_if_mldev='only supported in the Vertex AI client',
|
|
65
|
+
parameters=types.UpscaleImageParameters(
|
|
66
|
+
model=IMAGEN_MODEL_LATEST,
|
|
67
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
68
|
+
upscale_factor='x2',
|
|
69
|
+
config=types.UpscaleImageConfig(
|
|
70
|
+
output_gcs_uri='gs://genai-sdk-tests/temp/images/',
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
pytestmark = pytest_helper.setup(
|
|
76
|
+
file=__file__,
|
|
77
|
+
globals_for_file=globals(),
|
|
78
|
+
test_method='models.upscale_image',
|
|
79
|
+
test_table=test_table,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_upscale_extra_config_parameters(client):
|
|
84
|
+
# MLDev currently does not support upscale_image, but the ValidationError
|
|
85
|
+
# occurs before the ValueError.
|
|
86
|
+
try:
|
|
87
|
+
# User is not allowed to set mode or number_of_images
|
|
88
|
+
client.models.upscale_image(
|
|
89
|
+
model=IMAGEN_MODEL_LATEST,
|
|
90
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
91
|
+
upscale_factor='x2',
|
|
92
|
+
config={
|
|
93
|
+
'mode': 'upscale',
|
|
94
|
+
'number_of_images': 1,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
# Should never reach this.
|
|
98
|
+
assert False
|
|
99
|
+
except Exception as e:
|
|
100
|
+
assert isinstance(e, ValidationError)
|
|
101
|
+
assert 'Extra inputs are not permitted' in str(e)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.mark.asyncio
|
|
105
|
+
async def test_upscale_async(client):
|
|
106
|
+
with pytest_helper.exception_if_mldev(client, ValueError):
|
|
107
|
+
response = await client.aio.models.upscale_image(
|
|
108
|
+
model=IMAGEN_MODEL_LATEST,
|
|
109
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
110
|
+
upscale_factor='x2',
|
|
111
|
+
config=types.UpscaleImageConfig(
|
|
112
|
+
person_generation=types.PersonGeneration.ALLOW_ADULT,
|
|
113
|
+
safety_filter_level=types.SafetyFilterLevel.BLOCK_LOW_AND_ABOVE,
|
|
114
|
+
include_rai_reason=True,
|
|
115
|
+
output_mime_type='image/jpeg',
|
|
116
|
+
output_compression_quality=80,
|
|
117
|
+
enhance_input_image=True,
|
|
118
|
+
image_preservation_factor=0.6,
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
assert response.generated_images[0].image.image_bytes
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@pytest.mark.asyncio
|
|
125
|
+
async def test_upscale_gcs_async(client):
|
|
126
|
+
with pytest_helper.exception_if_mldev(client, ValueError):
|
|
127
|
+
response = await client.aio.models.upscale_image(
|
|
128
|
+
model=IMAGEN_MODEL_LATEST,
|
|
129
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
130
|
+
upscale_factor='x2',
|
|
131
|
+
config=types.UpscaleImageConfig(
|
|
132
|
+
output_gcs_uri='gs://genai-sdk-tests/temp/images/',
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
assert response.generated_images[0].image.gcs_uri
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@pytest.mark.asyncio
|
|
139
|
+
async def test_upscale_extra_config_parameters_async(client):
|
|
140
|
+
# MLDev currently does not support upscale_image, but the ValidationError
|
|
141
|
+
# occurs before the ValueError.
|
|
142
|
+
try:
|
|
143
|
+
# User is not allowed to set mode or number_of_images
|
|
144
|
+
await client.aio.models.upscale_image(
|
|
145
|
+
model=IMAGEN_MODEL_LATEST,
|
|
146
|
+
image=types.Image.from_file(location=IMAGE_FILE_PATH),
|
|
147
|
+
upscale_factor='x2',
|
|
148
|
+
config={
|
|
149
|
+
'mode': 'upscale',
|
|
150
|
+
'number_of_images': 1,
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
# Should never reach this.
|
|
154
|
+
assert False
|
|
155
|
+
except Exception as e:
|
|
156
|
+
assert isinstance(e, ValidationError)
|
|
157
|
+
assert 'Extra inputs are not permitted' in str(e)
|
|
@@ -0,0 +1,17 @@
|
|
|
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 operations.get."""
|
|
@@ -0,0 +1,38 @@
|
|
|
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 operations._get."""
|
|
18
|
+
|
|
19
|
+
from .. import pytest_helper
|
|
20
|
+
from ... import types
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_project_operation_get(client):
|
|
24
|
+
test_operation_id = '3787416390563004416'
|
|
25
|
+
if client._api_client.vertexai:
|
|
26
|
+
operation = client.operations._get(
|
|
27
|
+
operation_id=test_operation_id
|
|
28
|
+
)
|
|
29
|
+
assert operation.name.endswith(test_operation_id)
|
|
30
|
+
assert operation.done
|
|
31
|
+
assert isinstance(operation, types.ProjectOperation)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
pytestmark = pytest_helper.setup(
|
|
35
|
+
file=__file__,
|
|
36
|
+
globals_for_file=globals(),
|
|
37
|
+
test_method='operations._get',
|
|
38
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
"""Public Samples and Tests for the Google GenAI SDK."""
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
"""Test for the code sample for Gemini text-only request."""
|
|
18
|
+
|
|
19
|
+
from .. import pytest_helper
|
|
20
|
+
|
|
21
|
+
pytestmark = pytest_helper.setup(file=__file__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_sample(client):
|
|
25
|
+
# [START generativeaionvertexai_gemini_text_only]
|
|
26
|
+
response = client.models.generate_content(
|
|
27
|
+
model="gemini-2.5-flash",
|
|
28
|
+
contents=(
|
|
29
|
+
"What's a good name for a flower shop that specializes in selling"
|
|
30
|
+
" bouquets of dried flowers?"
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
print(response.text)
|
|
34
|
+
# [END generativeaionvertexai_gemini_text_only]
|
|
@@ -0,0 +1,246 @@
|
|
|
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
|
+
import contextlib
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import pathlib
|
|
21
|
+
from typing import Any, Optional
|
|
22
|
+
from pydantic import BaseModel, Field, SerializeAsAny
|
|
23
|
+
import pytest
|
|
24
|
+
import re
|
|
25
|
+
from .. import _common
|
|
26
|
+
from .. import _replay_api_client
|
|
27
|
+
from .. import types
|
|
28
|
+
from .._api_client import HttpOptions
|
|
29
|
+
|
|
30
|
+
is_api_mode = "config.getoption('--mode') == 'api'"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestTableItem(types.TestTableItem):
|
|
34
|
+
# This is not a test suite class.
|
|
35
|
+
__test__ = False
|
|
36
|
+
|
|
37
|
+
# Overrides to support Pydantic models.
|
|
38
|
+
parameters: SerializeAsAny[BaseModel] = Field(
|
|
39
|
+
description="""The parameters to the test. Use pydantic models.""",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def base_test_function(
|
|
44
|
+
client,
|
|
45
|
+
use_vertex: bool,
|
|
46
|
+
replays_prefix: str,
|
|
47
|
+
test_method: str,
|
|
48
|
+
test_table_item: TestTableItem,
|
|
49
|
+
globals_for_file: dict[str, Any],
|
|
50
|
+
):
|
|
51
|
+
replay_id = (
|
|
52
|
+
test_table_item.override_replay_id
|
|
53
|
+
if test_table_item.override_replay_id
|
|
54
|
+
else test_table_item.name
|
|
55
|
+
)
|
|
56
|
+
api_type = 'vertex' if use_vertex else 'mldev'
|
|
57
|
+
replay_id = f'{replays_prefix}/{replay_id}.{api_type}'
|
|
58
|
+
client._api_client.initialize_replay_session(replay_id)
|
|
59
|
+
# vars().copy() provides a shallow copy of the parameters.
|
|
60
|
+
parameters_dict = vars(test_table_item.parameters).copy()
|
|
61
|
+
try:
|
|
62
|
+
if '.' in test_method:
|
|
63
|
+
method_name_parts = test_method.split('.')
|
|
64
|
+
module_path_parts = method_name_parts[:-1]
|
|
65
|
+
method_name = method_name_parts[-1]
|
|
66
|
+
# Iterates the nested module starting from client
|
|
67
|
+
current_object = client
|
|
68
|
+
for part in module_path_parts:
|
|
69
|
+
current_object = getattr(current_object, part)
|
|
70
|
+
method = getattr(current_object, method_name)
|
|
71
|
+
method(**parameters_dict)
|
|
72
|
+
else:
|
|
73
|
+
custom_method = globals_for_file[test_method]
|
|
74
|
+
custom_method(client, test_table_item.parameters)
|
|
75
|
+
# Should not reach here if expecting an exception.
|
|
76
|
+
if test_table_item.exception_if_mldev and not client._api_client.vertexai:
|
|
77
|
+
assert False, 'Should have raised exception in MLDev.'
|
|
78
|
+
elif test_table_item.exception_if_vertex and client._api_client.vertexai:
|
|
79
|
+
assert False, 'Should have raised exception in Vertex.'
|
|
80
|
+
client._api_client.close()
|
|
81
|
+
except Exception as e:
|
|
82
|
+
if test_table_item.exception_if_mldev and not client._api_client.vertexai:
|
|
83
|
+
if test_table_item.exception_if_mldev not in str(e):
|
|
84
|
+
raise AssertionError(
|
|
85
|
+
f"'{test_table_item.exception_if_mldev}' not in '{str(e)}'"
|
|
86
|
+
) from e
|
|
87
|
+
elif test_table_item.exception_if_vertex and client._api_client.vertexai:
|
|
88
|
+
if test_table_item.exception_if_vertex not in str(e):
|
|
89
|
+
raise AssertionError(
|
|
90
|
+
f"'{test_table_item.exception_if_vertex}' not in '{str(e)}'"
|
|
91
|
+
) from e
|
|
92
|
+
else:
|
|
93
|
+
raise e
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def create_test_for_table_item(
|
|
97
|
+
globals_for_file: dict[str, Any],
|
|
98
|
+
test_method: str,
|
|
99
|
+
test_table_item: TestTableItem,
|
|
100
|
+
):
|
|
101
|
+
return lambda client, use_vertex, replays_prefix: base_test_function(
|
|
102
|
+
client,
|
|
103
|
+
use_vertex,
|
|
104
|
+
replays_prefix,
|
|
105
|
+
test_method,
|
|
106
|
+
test_table_item,
|
|
107
|
+
globals_for_file,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def create_test_for_table(
|
|
112
|
+
globals_for_file: dict[str, Any],
|
|
113
|
+
test_method: str,
|
|
114
|
+
test_table: list[TestTableItem],
|
|
115
|
+
):
|
|
116
|
+
for test_table_item in test_table:
|
|
117
|
+
if test_table_item.has_union:
|
|
118
|
+
assert test_table_item.name.startswith('test_union_'), f"""
|
|
119
|
+
test: {test_table_item.name}
|
|
120
|
+
When has_union is true, it must be a test that explicitly test the
|
|
121
|
+
non-canonical type in the union.
|
|
122
|
+
For all other tests, use transformers to convert data to the canonical
|
|
123
|
+
type of the field so it can be tested in other languages.
|
|
124
|
+
If this test is truly about testing the non-canonical type of the union,
|
|
125
|
+
rename the test to start with 'test_union_'. E.g.
|
|
126
|
+
test_union_contents_is_string()"""
|
|
127
|
+
globals_for_file[test_table_item.name] = create_test_for_table_item(
|
|
128
|
+
globals_for_file, test_method, test_table_item
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# Sets up the test framework.
|
|
133
|
+
# file: Always use __file__
|
|
134
|
+
# globals_for_file: Always use globals()
|
|
135
|
+
# test_table: The test table for the file.
|
|
136
|
+
# Use test table over individual tests whenever possible.
|
|
137
|
+
# Tests built with test_table will run in all other languages automatically.
|
|
138
|
+
# Otherwise, you will need to write tests in all other languages manually.
|
|
139
|
+
def setup(
|
|
140
|
+
*,
|
|
141
|
+
file: str,
|
|
142
|
+
globals_for_file: Optional[dict[str, Any]] = None,
|
|
143
|
+
test_method: Optional[str] = None,
|
|
144
|
+
test_table: Optional[list[TestTableItem]] = None,
|
|
145
|
+
http_options: Optional[HttpOptions] = None,
|
|
146
|
+
):
|
|
147
|
+
"""Generates parameterization for tests, run for both Vertex and MLDev."""
|
|
148
|
+
replays_directory = (
|
|
149
|
+
file.replace(os.path.dirname(__file__), 'tests')
|
|
150
|
+
.replace('.py', '')
|
|
151
|
+
.replace('/test_', '/')
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
is_tap_mode = False
|
|
155
|
+
if os.environ.get('UNITTEST_ON_FORGE', None) is not None:
|
|
156
|
+
is_tap_mode = True
|
|
157
|
+
|
|
158
|
+
if test_table:
|
|
159
|
+
create_test_for_table(globals_for_file, test_method, test_table)
|
|
160
|
+
|
|
161
|
+
if test_table and not is_tap_mode:
|
|
162
|
+
replays_root_directory = os.environ.get(
|
|
163
|
+
'GOOGLE_GENAI_REPLAYS_DIRECTORY', None
|
|
164
|
+
)
|
|
165
|
+
if replays_root_directory is None:
|
|
166
|
+
raise ValueError(
|
|
167
|
+
'GOOGLE_GENAI_REPLAYS_DIRECTORY environment variable is not set'
|
|
168
|
+
)
|
|
169
|
+
abs_replay_directory = os.path.join(
|
|
170
|
+
replays_root_directory, replays_directory
|
|
171
|
+
)
|
|
172
|
+
test_table_file_path = os.path.join(
|
|
173
|
+
abs_replay_directory, '_test_table.json'
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
pathlib.Path(abs_replay_directory).mkdir(parents=True, exist_ok=True)
|
|
177
|
+
assert isinstance(
|
|
178
|
+
test_table[0].parameters, BaseModel
|
|
179
|
+
), f'{test_table_file_path} parameters must be a BaseModel.'
|
|
180
|
+
test_table_file = types.TestTableFile(
|
|
181
|
+
comment='Auto-generated. Do not edit.',
|
|
182
|
+
test_method=test_method,
|
|
183
|
+
parameter_names=list(test_table[0].parameters.model_fields.keys()),
|
|
184
|
+
test_table=test_table,
|
|
185
|
+
)
|
|
186
|
+
os.makedirs(os.path.dirname(test_table_file_path), exist_ok=True)
|
|
187
|
+
|
|
188
|
+
with open(test_table_file_path, 'w') as f:
|
|
189
|
+
f.write(
|
|
190
|
+
test_table_file.model_dump_json(exclude_none=True, by_alias=True, indent=2),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Add fixture for requested client option.
|
|
194
|
+
return pytest.mark.parametrize(
|
|
195
|
+
'use_vertex, replays_prefix, http_options',
|
|
196
|
+
[
|
|
197
|
+
(True, replays_directory, http_options),
|
|
198
|
+
(False, replays_directory, http_options),
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def exception_if_mldev(client, exception_type: type[Exception]):
|
|
204
|
+
if client._api_client.vertexai:
|
|
205
|
+
return contextlib.nullcontext()
|
|
206
|
+
else:
|
|
207
|
+
return pytest.raises(exception_type)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def exception_if_vertex(client, exception_type: type[Exception]):
|
|
211
|
+
if client._api_client.vertexai:
|
|
212
|
+
return pytest.raises(exception_type)
|
|
213
|
+
else:
|
|
214
|
+
return contextlib.nullcontext()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def snake_to_camel(snake_str: str) -> str:
|
|
218
|
+
"""Converts a snake_case string to CamelCase."""
|
|
219
|
+
return re.sub(r'_([a-zA-Z])', lambda match: match.group(1).upper(), snake_str)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def camel_to_snake(camel_str: str) -> str:
|
|
223
|
+
"""Converts a CamelCase string to snake_case."""
|
|
224
|
+
return re.sub(r'([A-Z])', r'_\1', camel_str).lower().lstrip('_')
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def get_value_ignore_key_case(obj, key):
|
|
228
|
+
"""Returns the value of the key in the object, converting to camelCase or snake_case if necessary."""
|
|
229
|
+
return obj.get(snake_to_camel(key), obj.get(camel_to_snake(key), None))
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def camel_to_snake_all_keys(data):
|
|
233
|
+
"""Converts all keys in a dictionary or list to snake_case."""
|
|
234
|
+
if isinstance(data, dict):
|
|
235
|
+
new_dict = {}
|
|
236
|
+
for key, value in data.items():
|
|
237
|
+
if isinstance(key, str):
|
|
238
|
+
new_key = camel_to_snake(key)
|
|
239
|
+
else:
|
|
240
|
+
new_key = key
|
|
241
|
+
new_dict[new_key] = camel_to_snake_all_keys(value)
|
|
242
|
+
return new_dict
|
|
243
|
+
elif isinstance(data, list):
|
|
244
|
+
return [camel_to_snake_all_keys(item) for item in data]
|
|
245
|
+
else:
|
|
246
|
+
return data
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
#
|