google-cloud-agentplatform 1.165.1.dev0__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.
- agentplatform/__init__.py +72 -0
- agentplatform/_genai/__init__.py +43 -0
- agentplatform/_genai/_agent_engines_utils.py +2341 -0
- agentplatform/_genai/_bigquery_utils.py +49 -0
- agentplatform/_genai/_datasets_utils.py +344 -0
- agentplatform/_genai/_evals_builtin_tools.py +209 -0
- agentplatform/_genai/_evals_common.py +4268 -0
- agentplatform/_genai/_evals_constant.py +122 -0
- agentplatform/_genai/_evals_data_converters.py +926 -0
- agentplatform/_genai/_evals_metric_handlers.py +1783 -0
- agentplatform/_genai/_evals_metric_loaders.py +401 -0
- agentplatform/_genai/_evals_utils.py +1043 -0
- agentplatform/_genai/_evals_visualization.py +2070 -0
- agentplatform/_genai/_gcs_utils.py +262 -0
- agentplatform/_genai/_logging_utils.py +47 -0
- agentplatform/_genai/_memory_bank_utils.py +206 -0
- agentplatform/_genai/_observability_data_converter.py +186 -0
- agentplatform/_genai/_operations_utils.py +94 -0
- agentplatform/_genai/_prompt_management_utils.py +147 -0
- agentplatform/_genai/_prompt_optimizer_utils.py +215 -0
- agentplatform/_genai/_skills_utils.py +69 -0
- agentplatform/_genai/_transformers.py +628 -0
- agentplatform/_genai/a2a_task_events.py +509 -0
- agentplatform/_genai/a2a_tasks.py +861 -0
- agentplatform/_genai/agent_engines.py +3931 -0
- agentplatform/_genai/client.py +519 -0
- agentplatform/_genai/datasets.py +3045 -0
- agentplatform/_genai/endpoints.py +1149 -0
- agentplatform/_genai/evals.py +6883 -0
- agentplatform/_genai/example_stores.py +1445 -0
- agentplatform/_genai/feedback_contexts.py +700 -0
- agentplatform/_genai/feedback_entries.py +1644 -0
- agentplatform/_genai/live.py +64 -0
- agentplatform/_genai/live_agent_engines.py +179 -0
- agentplatform/_genai/memories.py +2962 -0
- agentplatform/_genai/memory_banks.py +1927 -0
- agentplatform/_genai/memory_revisions.py +465 -0
- agentplatform/_genai/model_garden.py +2638 -0
- agentplatform/_genai/prompt_optimizer.py +995 -0
- agentplatform/_genai/prompts.py +4515 -0
- agentplatform/_genai/rag.py +4961 -0
- agentplatform/_genai/runtime_revisions.py +1257 -0
- agentplatform/_genai/runtimes.py +78 -0
- agentplatform/_genai/sandbox_snapshots.py +1015 -0
- agentplatform/_genai/sandbox_templates.py +1088 -0
- agentplatform/_genai/sandboxes.py +1604 -0
- agentplatform/_genai/session_events.py +543 -0
- agentplatform/_genai/sessions.py +1449 -0
- agentplatform/_genai/skill_revisions.py +377 -0
- agentplatform/_genai/skills.py +1708 -0
- agentplatform/_genai/types/__init__.py +4695 -0
- agentplatform/_genai/types/agent_engines.py +16 -0
- agentplatform/_genai/types/common.py +32784 -0
- agentplatform/_genai/types/evals.py +1031 -0
- agentplatform/_genai/types/prompt_optimizer.py +107 -0
- agentplatform/_genai/types/prompts.py +107 -0
- agentplatform/version.py +17 -0
- google_cloud_agentplatform-1.165.1.dev0.dist-info/METADATA +79 -0
- google_cloud_agentplatform-1.165.1.dev0.dist-info/RECORD +62 -0
- google_cloud_agentplatform-1.165.1.dev0.dist-info/WHEEL +5 -0
- google_cloud_agentplatform-1.165.1.dev0.dist-info/licenses/LICENSE +202 -0
- google_cloud_agentplatform-1.165.1.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2638 @@
|
|
|
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
|
+
# Code generated by the Google Gen AI SDK generator DO NOT EDIT.
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any, Optional, Union
|
|
21
|
+
from urllib.parse import urlencode
|
|
22
|
+
|
|
23
|
+
from google.genai import _api_module
|
|
24
|
+
from google.genai import _common
|
|
25
|
+
from google.genai import types as genai_types
|
|
26
|
+
from google.genai._common import get_value_by_path as getv
|
|
27
|
+
from google.genai._common import set_value_by_path as setv
|
|
28
|
+
|
|
29
|
+
from . import _operations_utils
|
|
30
|
+
from . import types
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger("agentplatform_genai.modelgarden")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _DeployRequestParameters_to_vertex(
|
|
36
|
+
from_object: Union[dict[str, Any], object],
|
|
37
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
to_object: dict[str, Any] = {}
|
|
40
|
+
if getv(from_object, ["destination"]) is not None:
|
|
41
|
+
setv(to_object, ["_url", "destination"], getv(from_object, ["destination"]))
|
|
42
|
+
|
|
43
|
+
if getv(from_object, ["publisher_model_name"]) is not None:
|
|
44
|
+
setv(
|
|
45
|
+
to_object,
|
|
46
|
+
["publisherModelName"],
|
|
47
|
+
getv(from_object, ["publisher_model_name"]),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if getv(from_object, ["hugging_face_model_id"]) is not None:
|
|
51
|
+
setv(
|
|
52
|
+
to_object,
|
|
53
|
+
["huggingFaceModelId"],
|
|
54
|
+
getv(from_object, ["hugging_face_model_id"]),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if getv(from_object, ["custom_model"]) is not None:
|
|
58
|
+
setv(to_object, ["customModel"], getv(from_object, ["custom_model"]))
|
|
59
|
+
|
|
60
|
+
if getv(from_object, ["model_config_val"]) is not None:
|
|
61
|
+
setv(to_object, ["modelConfig"], getv(from_object, ["model_config_val"]))
|
|
62
|
+
|
|
63
|
+
if getv(from_object, ["endpoint_config"]) is not None:
|
|
64
|
+
setv(to_object, ["endpointConfig"], getv(from_object, ["endpoint_config"]))
|
|
65
|
+
|
|
66
|
+
if getv(from_object, ["deploy_config"]) is not None:
|
|
67
|
+
setv(to_object, ["deployConfig"], getv(from_object, ["deploy_config"]))
|
|
68
|
+
|
|
69
|
+
return to_object
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _ExportPublisherModelConfig_to_vertex(
|
|
73
|
+
from_object: Union[dict[str, Any], object],
|
|
74
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
75
|
+
) -> dict[str, Any]:
|
|
76
|
+
to_object: dict[str, Any] = {}
|
|
77
|
+
|
|
78
|
+
if getv(from_object, ["destination"]) is not None:
|
|
79
|
+
setv(parent_object, ["destination"], getv(from_object, ["destination"]))
|
|
80
|
+
|
|
81
|
+
return to_object
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _ExportPublisherModelRequestParameters_to_vertex(
|
|
85
|
+
from_object: Union[dict[str, Any], object],
|
|
86
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
87
|
+
) -> dict[str, Any]:
|
|
88
|
+
to_object: dict[str, Any] = {}
|
|
89
|
+
if getv(from_object, ["parent"]) is not None:
|
|
90
|
+
setv(to_object, ["_url", "parent"], getv(from_object, ["parent"]))
|
|
91
|
+
|
|
92
|
+
if getv(from_object, ["name"]) is not None:
|
|
93
|
+
setv(to_object, ["_url", "name"], getv(from_object, ["name"]))
|
|
94
|
+
|
|
95
|
+
if getv(from_object, ["config"]) is not None:
|
|
96
|
+
_ExportPublisherModelConfig_to_vertex(getv(from_object, ["config"]), to_object)
|
|
97
|
+
|
|
98
|
+
return to_object
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _GetDeployOperationParameters_to_vertex(
|
|
102
|
+
from_object: Union[dict[str, Any], object],
|
|
103
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
104
|
+
) -> dict[str, Any]:
|
|
105
|
+
to_object: dict[str, Any] = {}
|
|
106
|
+
if getv(from_object, ["operation_name"]) is not None:
|
|
107
|
+
setv(
|
|
108
|
+
to_object, ["_url", "operationName"], getv(from_object, ["operation_name"])
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return to_object
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _GetExportPublisherModelOperationParameters_to_vertex(
|
|
115
|
+
from_object: Union[dict[str, Any], object],
|
|
116
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
117
|
+
) -> dict[str, Any]:
|
|
118
|
+
to_object: dict[str, Any] = {}
|
|
119
|
+
if getv(from_object, ["operation_name"]) is not None:
|
|
120
|
+
setv(
|
|
121
|
+
to_object, ["_url", "operationName"], getv(from_object, ["operation_name"])
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return to_object
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _GetPublisherModelConfig_to_vertex(
|
|
128
|
+
from_object: Union[dict[str, Any], object],
|
|
129
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
130
|
+
) -> dict[str, Any]:
|
|
131
|
+
to_object: dict[str, Any] = {}
|
|
132
|
+
|
|
133
|
+
if getv(from_object, ["hugging_face_token"]) is not None:
|
|
134
|
+
setv(
|
|
135
|
+
parent_object,
|
|
136
|
+
["_query", "huggingFaceToken"],
|
|
137
|
+
getv(from_object, ["hugging_face_token"]),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if (
|
|
141
|
+
getv(from_object, ["include_equivalent_model_garden_model_deployment_configs"])
|
|
142
|
+
is not None
|
|
143
|
+
):
|
|
144
|
+
setv(
|
|
145
|
+
parent_object,
|
|
146
|
+
["_query", "includeEquivalentModelGardenModelDeploymentConfigs"],
|
|
147
|
+
getv(
|
|
148
|
+
from_object,
|
|
149
|
+
["include_equivalent_model_garden_model_deployment_configs"],
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if getv(from_object, ["is_hugging_face_model"]) is not None:
|
|
154
|
+
setv(
|
|
155
|
+
parent_object,
|
|
156
|
+
["_query", "isHuggingFaceModel"],
|
|
157
|
+
getv(from_object, ["is_hugging_face_model"]),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return to_object
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _GetPublisherModelRequestParameters_to_vertex(
|
|
164
|
+
from_object: Union[dict[str, Any], object],
|
|
165
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
166
|
+
) -> dict[str, Any]:
|
|
167
|
+
to_object: dict[str, Any] = {}
|
|
168
|
+
if getv(from_object, ["name"]) is not None:
|
|
169
|
+
setv(to_object, ["_url", "name"], getv(from_object, ["name"]))
|
|
170
|
+
|
|
171
|
+
if getv(from_object, ["config"]) is not None:
|
|
172
|
+
_GetPublisherModelConfig_to_vertex(getv(from_object, ["config"]), to_object)
|
|
173
|
+
|
|
174
|
+
return to_object
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _ListPublisherModelsConfig_to_vertex(
|
|
178
|
+
from_object: Union[dict[str, Any], object],
|
|
179
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
180
|
+
) -> dict[str, Any]:
|
|
181
|
+
to_object: dict[str, Any] = {}
|
|
182
|
+
|
|
183
|
+
if getv(from_object, ["page_size"]) is not None:
|
|
184
|
+
setv(parent_object, ["_query", "pageSize"], getv(from_object, ["page_size"]))
|
|
185
|
+
|
|
186
|
+
if getv(from_object, ["page_token"]) is not None:
|
|
187
|
+
setv(parent_object, ["_query", "pageToken"], getv(from_object, ["page_token"]))
|
|
188
|
+
|
|
189
|
+
if getv(from_object, ["filter"]) is not None:
|
|
190
|
+
setv(parent_object, ["_query", "filter"], getv(from_object, ["filter"]))
|
|
191
|
+
|
|
192
|
+
if getv(from_object, ["list_all_versions"]) is not None:
|
|
193
|
+
setv(
|
|
194
|
+
parent_object,
|
|
195
|
+
["_query", "listAllVersions"],
|
|
196
|
+
getv(from_object, ["list_all_versions"]),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return to_object
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _ListPublisherModelsRequestParameters_to_vertex(
|
|
203
|
+
from_object: Union[dict[str, Any], object],
|
|
204
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
205
|
+
) -> dict[str, Any]:
|
|
206
|
+
to_object: dict[str, Any] = {}
|
|
207
|
+
if getv(from_object, ["parent"]) is not None:
|
|
208
|
+
setv(to_object, ["_url", "parent"], getv(from_object, ["parent"]))
|
|
209
|
+
|
|
210
|
+
if getv(from_object, ["config"]) is not None:
|
|
211
|
+
_ListPublisherModelsConfig_to_vertex(getv(from_object, ["config"]), to_object)
|
|
212
|
+
|
|
213
|
+
return to_object
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _RecommendSpecConfig_to_vertex(
|
|
217
|
+
from_object: Union[dict[str, Any], object],
|
|
218
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
219
|
+
) -> dict[str, Any]:
|
|
220
|
+
to_object: dict[str, Any] = {}
|
|
221
|
+
|
|
222
|
+
if getv(from_object, ["check_machine_availability"]) is not None:
|
|
223
|
+
setv(
|
|
224
|
+
parent_object,
|
|
225
|
+
["checkMachineAvailability"],
|
|
226
|
+
getv(from_object, ["check_machine_availability"]),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if getv(from_object, ["check_user_quota"]) is not None:
|
|
230
|
+
setv(parent_object, ["checkUserQuota"], getv(from_object, ["check_user_quota"]))
|
|
231
|
+
|
|
232
|
+
return to_object
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _RecommendSpecRequestParameters_to_vertex(
|
|
236
|
+
from_object: Union[dict[str, Any], object],
|
|
237
|
+
parent_object: Optional[dict[str, Any]] = None,
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
to_object: dict[str, Any] = {}
|
|
240
|
+
if getv(from_object, ["parent"]) is not None:
|
|
241
|
+
setv(to_object, ["_url", "parent"], getv(from_object, ["parent"]))
|
|
242
|
+
|
|
243
|
+
if getv(from_object, ["gcs_uri"]) is not None:
|
|
244
|
+
setv(to_object, ["gcsUri"], getv(from_object, ["gcs_uri"]))
|
|
245
|
+
|
|
246
|
+
if getv(from_object, ["config"]) is not None:
|
|
247
|
+
_RecommendSpecConfig_to_vertex(getv(from_object, ["config"]), to_object)
|
|
248
|
+
|
|
249
|
+
return to_object
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ModelGarden(_api_module.BaseModule):
|
|
253
|
+
"""Model Garden module."""
|
|
254
|
+
|
|
255
|
+
def _list_publisher_models(
|
|
256
|
+
self,
|
|
257
|
+
*,
|
|
258
|
+
parent: Optional[str] = None,
|
|
259
|
+
config: Optional[types.ListPublisherModelsConfigOrDict] = None,
|
|
260
|
+
) -> types.ListPublisherModelsResponse:
|
|
261
|
+
"""
|
|
262
|
+
Lists publisher models (internal).
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
parameter_model = types._ListPublisherModelsRequestParameters(
|
|
266
|
+
parent=parent,
|
|
267
|
+
config=config,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
request_url_dict: Optional[dict[str, str]]
|
|
271
|
+
if not self._api_client.vertexai:
|
|
272
|
+
raise ValueError(
|
|
273
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
274
|
+
)
|
|
275
|
+
else:
|
|
276
|
+
request_dict = _ListPublisherModelsRequestParameters_to_vertex(
|
|
277
|
+
parameter_model
|
|
278
|
+
)
|
|
279
|
+
request_url_dict = request_dict.get("_url")
|
|
280
|
+
if request_url_dict:
|
|
281
|
+
path = "{parent}/models".format_map(request_url_dict)
|
|
282
|
+
else:
|
|
283
|
+
path = "{parent}/models"
|
|
284
|
+
|
|
285
|
+
query_params = request_dict.get("_query")
|
|
286
|
+
if query_params:
|
|
287
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
288
|
+
# TODO: remove the hack that pops config.
|
|
289
|
+
request_dict.pop("config", None)
|
|
290
|
+
|
|
291
|
+
http_options: Optional[types.HttpOptions] = None
|
|
292
|
+
if (
|
|
293
|
+
parameter_model.config is not None
|
|
294
|
+
and parameter_model.config.http_options is not None
|
|
295
|
+
):
|
|
296
|
+
http_options = parameter_model.config.http_options
|
|
297
|
+
|
|
298
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
299
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
300
|
+
|
|
301
|
+
response = self._api_client.request("get", path, request_dict, http_options)
|
|
302
|
+
|
|
303
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
304
|
+
|
|
305
|
+
return_value = types.ListPublisherModelsResponse._from_response(
|
|
306
|
+
response=response_dict,
|
|
307
|
+
kwargs=(
|
|
308
|
+
{
|
|
309
|
+
"config": {
|
|
310
|
+
"response_schema": getattr(
|
|
311
|
+
parameter_model.config, "response_schema", None
|
|
312
|
+
),
|
|
313
|
+
"response_json_schema": getattr(
|
|
314
|
+
parameter_model.config, "response_json_schema", None
|
|
315
|
+
),
|
|
316
|
+
"include_all_fields": getattr(
|
|
317
|
+
parameter_model.config, "include_all_fields", None
|
|
318
|
+
),
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if getattr(parameter_model, "config", None)
|
|
322
|
+
else {}
|
|
323
|
+
),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
self._api_client._verify_response(return_value)
|
|
327
|
+
return return_value
|
|
328
|
+
|
|
329
|
+
def _get_publisher_model(
|
|
330
|
+
self, *, name: str, config: Optional[types.GetPublisherModelConfigOrDict] = None
|
|
331
|
+
) -> types.PublisherModel:
|
|
332
|
+
"""
|
|
333
|
+
Gets a publisher model (internal).
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
parameter_model = types._GetPublisherModelRequestParameters(
|
|
337
|
+
name=name,
|
|
338
|
+
config=config,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
request_url_dict: Optional[dict[str, str]]
|
|
342
|
+
if not self._api_client.vertexai:
|
|
343
|
+
raise ValueError(
|
|
344
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
request_dict = _GetPublisherModelRequestParameters_to_vertex(
|
|
348
|
+
parameter_model
|
|
349
|
+
)
|
|
350
|
+
request_url_dict = request_dict.get("_url")
|
|
351
|
+
if request_url_dict:
|
|
352
|
+
path = "{name}".format_map(request_url_dict)
|
|
353
|
+
else:
|
|
354
|
+
path = "{name}"
|
|
355
|
+
|
|
356
|
+
query_params = request_dict.get("_query")
|
|
357
|
+
if query_params:
|
|
358
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
359
|
+
# TODO: remove the hack that pops config.
|
|
360
|
+
request_dict.pop("config", None)
|
|
361
|
+
|
|
362
|
+
http_options: Optional[types.HttpOptions] = None
|
|
363
|
+
if (
|
|
364
|
+
parameter_model.config is not None
|
|
365
|
+
and parameter_model.config.http_options is not None
|
|
366
|
+
):
|
|
367
|
+
http_options = parameter_model.config.http_options
|
|
368
|
+
|
|
369
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
370
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
371
|
+
|
|
372
|
+
response = self._api_client.request("get", path, request_dict, http_options)
|
|
373
|
+
|
|
374
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
375
|
+
|
|
376
|
+
return_value = types.PublisherModel._from_response(
|
|
377
|
+
response=response_dict,
|
|
378
|
+
kwargs=(
|
|
379
|
+
{
|
|
380
|
+
"config": {
|
|
381
|
+
"response_schema": getattr(
|
|
382
|
+
parameter_model.config, "response_schema", None
|
|
383
|
+
),
|
|
384
|
+
"response_json_schema": getattr(
|
|
385
|
+
parameter_model.config, "response_json_schema", None
|
|
386
|
+
),
|
|
387
|
+
"include_all_fields": getattr(
|
|
388
|
+
parameter_model.config, "include_all_fields", None
|
|
389
|
+
),
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if getattr(parameter_model, "config", None)
|
|
393
|
+
else {}
|
|
394
|
+
),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
self._api_client._verify_response(return_value)
|
|
398
|
+
return return_value
|
|
399
|
+
|
|
400
|
+
def _recommend_spec(
|
|
401
|
+
self,
|
|
402
|
+
*,
|
|
403
|
+
parent: str,
|
|
404
|
+
gcs_uri: str,
|
|
405
|
+
config: Optional[types.RecommendSpecConfigOrDict] = None,
|
|
406
|
+
) -> types.RecommendSpecResponse:
|
|
407
|
+
"""
|
|
408
|
+
Recommends spec for a custom model (internal).
|
|
409
|
+
"""
|
|
410
|
+
|
|
411
|
+
parameter_model = types._RecommendSpecRequestParameters(
|
|
412
|
+
parent=parent,
|
|
413
|
+
gcs_uri=gcs_uri,
|
|
414
|
+
config=config,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
request_url_dict: Optional[dict[str, str]]
|
|
418
|
+
if not self._api_client.vertexai:
|
|
419
|
+
raise ValueError(
|
|
420
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
421
|
+
)
|
|
422
|
+
else:
|
|
423
|
+
request_dict = _RecommendSpecRequestParameters_to_vertex(parameter_model)
|
|
424
|
+
request_url_dict = request_dict.get("_url")
|
|
425
|
+
if request_url_dict:
|
|
426
|
+
path = "{parent}:recommendSpec".format_map(request_url_dict)
|
|
427
|
+
else:
|
|
428
|
+
path = "{parent}:recommendSpec"
|
|
429
|
+
|
|
430
|
+
query_params = request_dict.get("_query")
|
|
431
|
+
if query_params:
|
|
432
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
433
|
+
# TODO: remove the hack that pops config.
|
|
434
|
+
request_dict.pop("config", None)
|
|
435
|
+
|
|
436
|
+
http_options: Optional[types.HttpOptions] = None
|
|
437
|
+
if (
|
|
438
|
+
parameter_model.config is not None
|
|
439
|
+
and parameter_model.config.http_options is not None
|
|
440
|
+
):
|
|
441
|
+
http_options = parameter_model.config.http_options
|
|
442
|
+
|
|
443
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
444
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
445
|
+
|
|
446
|
+
response = self._api_client.request("post", path, request_dict, http_options)
|
|
447
|
+
|
|
448
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
449
|
+
|
|
450
|
+
return_value = types.RecommendSpecResponse._from_response(
|
|
451
|
+
response=response_dict,
|
|
452
|
+
kwargs=(
|
|
453
|
+
{
|
|
454
|
+
"config": {
|
|
455
|
+
"response_schema": getattr(
|
|
456
|
+
parameter_model.config, "response_schema", None
|
|
457
|
+
),
|
|
458
|
+
"response_json_schema": getattr(
|
|
459
|
+
parameter_model.config, "response_json_schema", None
|
|
460
|
+
),
|
|
461
|
+
"include_all_fields": getattr(
|
|
462
|
+
parameter_model.config, "include_all_fields", None
|
|
463
|
+
),
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if getattr(parameter_model, "config", None)
|
|
467
|
+
else {}
|
|
468
|
+
),
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
self._api_client._verify_response(return_value)
|
|
472
|
+
return return_value
|
|
473
|
+
|
|
474
|
+
def _export_publisher_model(
|
|
475
|
+
self,
|
|
476
|
+
*,
|
|
477
|
+
parent: str,
|
|
478
|
+
name: str,
|
|
479
|
+
config: Optional[types.ExportPublisherModelConfigOrDict] = None,
|
|
480
|
+
) -> types.ExportModelOperation:
|
|
481
|
+
"""
|
|
482
|
+
Exports a publisher model (internal).
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
parameter_model = types._ExportPublisherModelRequestParameters(
|
|
486
|
+
parent=parent,
|
|
487
|
+
name=name,
|
|
488
|
+
config=config,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
request_url_dict: Optional[dict[str, str]]
|
|
492
|
+
if not self._api_client.vertexai:
|
|
493
|
+
raise ValueError(
|
|
494
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
495
|
+
)
|
|
496
|
+
else:
|
|
497
|
+
request_dict = _ExportPublisherModelRequestParameters_to_vertex(
|
|
498
|
+
parameter_model
|
|
499
|
+
)
|
|
500
|
+
request_url_dict = request_dict.get("_url")
|
|
501
|
+
if request_url_dict:
|
|
502
|
+
path = "{parent}/{name}:export".format_map(request_url_dict)
|
|
503
|
+
else:
|
|
504
|
+
path = "{parent}/{name}:export"
|
|
505
|
+
|
|
506
|
+
query_params = request_dict.get("_query")
|
|
507
|
+
if query_params:
|
|
508
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
509
|
+
# TODO: remove the hack that pops config.
|
|
510
|
+
request_dict.pop("config", None)
|
|
511
|
+
|
|
512
|
+
http_options: Optional[types.HttpOptions] = None
|
|
513
|
+
if (
|
|
514
|
+
parameter_model.config is not None
|
|
515
|
+
and parameter_model.config.http_options is not None
|
|
516
|
+
):
|
|
517
|
+
http_options = parameter_model.config.http_options
|
|
518
|
+
|
|
519
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
520
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
521
|
+
|
|
522
|
+
response = self._api_client.request("post", path, request_dict, http_options)
|
|
523
|
+
|
|
524
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
525
|
+
|
|
526
|
+
return_value = types.ExportModelOperation._from_response(
|
|
527
|
+
response=response_dict,
|
|
528
|
+
kwargs=(
|
|
529
|
+
{
|
|
530
|
+
"config": {
|
|
531
|
+
"response_schema": getattr(
|
|
532
|
+
parameter_model.config, "response_schema", None
|
|
533
|
+
),
|
|
534
|
+
"response_json_schema": getattr(
|
|
535
|
+
parameter_model.config, "response_json_schema", None
|
|
536
|
+
),
|
|
537
|
+
"include_all_fields": getattr(
|
|
538
|
+
parameter_model.config, "include_all_fields", None
|
|
539
|
+
),
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if getattr(parameter_model, "config", None)
|
|
543
|
+
else {}
|
|
544
|
+
),
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
self._api_client._verify_response(return_value)
|
|
548
|
+
return return_value
|
|
549
|
+
|
|
550
|
+
def get_export_publisher_model_operation(
|
|
551
|
+
self,
|
|
552
|
+
*,
|
|
553
|
+
operation_name: str,
|
|
554
|
+
config: Optional[types.GetExportPublisherModelOperationConfigOrDict] = None,
|
|
555
|
+
) -> types.ExportModelOperation:
|
|
556
|
+
"""
|
|
557
|
+
Fetches the status of an in-flight export_open_model LRO.
|
|
558
|
+
"""
|
|
559
|
+
|
|
560
|
+
parameter_model = types._GetExportPublisherModelOperationParameters(
|
|
561
|
+
operation_name=operation_name,
|
|
562
|
+
config=config,
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
request_url_dict: Optional[dict[str, str]]
|
|
566
|
+
if not self._api_client.vertexai:
|
|
567
|
+
raise ValueError(
|
|
568
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
569
|
+
)
|
|
570
|
+
else:
|
|
571
|
+
request_dict = _GetExportPublisherModelOperationParameters_to_vertex(
|
|
572
|
+
parameter_model
|
|
573
|
+
)
|
|
574
|
+
request_url_dict = request_dict.get("_url")
|
|
575
|
+
if request_url_dict:
|
|
576
|
+
path = "{operationName}".format_map(request_url_dict)
|
|
577
|
+
else:
|
|
578
|
+
path = "{operationName}"
|
|
579
|
+
|
|
580
|
+
query_params = request_dict.get("_query")
|
|
581
|
+
if query_params:
|
|
582
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
583
|
+
# TODO: remove the hack that pops config.
|
|
584
|
+
request_dict.pop("config", None)
|
|
585
|
+
|
|
586
|
+
http_options: Optional[types.HttpOptions] = None
|
|
587
|
+
if (
|
|
588
|
+
parameter_model.config is not None
|
|
589
|
+
and parameter_model.config.http_options is not None
|
|
590
|
+
):
|
|
591
|
+
http_options = parameter_model.config.http_options
|
|
592
|
+
|
|
593
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
594
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
595
|
+
|
|
596
|
+
response = self._api_client.request("get", path, request_dict, http_options)
|
|
597
|
+
|
|
598
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
599
|
+
|
|
600
|
+
return_value = types.ExportModelOperation._from_response(
|
|
601
|
+
response=response_dict,
|
|
602
|
+
kwargs=(
|
|
603
|
+
{
|
|
604
|
+
"config": {
|
|
605
|
+
"response_schema": getattr(
|
|
606
|
+
parameter_model.config, "response_schema", None
|
|
607
|
+
),
|
|
608
|
+
"response_json_schema": getattr(
|
|
609
|
+
parameter_model.config, "response_json_schema", None
|
|
610
|
+
),
|
|
611
|
+
"include_all_fields": getattr(
|
|
612
|
+
parameter_model.config, "include_all_fields", None
|
|
613
|
+
),
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if getattr(parameter_model, "config", None)
|
|
617
|
+
else {}
|
|
618
|
+
),
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
self._api_client._verify_response(return_value)
|
|
622
|
+
return return_value
|
|
623
|
+
|
|
624
|
+
def _deploy(
|
|
625
|
+
self,
|
|
626
|
+
*,
|
|
627
|
+
destination: str,
|
|
628
|
+
publisher_model_name: Optional[str] = None,
|
|
629
|
+
hugging_face_model_id: Optional[str] = None,
|
|
630
|
+
custom_model: Optional[types.DeployRequestCustomModelOrDict] = None,
|
|
631
|
+
model_config_val: Optional[types.DeployRequestModelConfigOrDict] = None,
|
|
632
|
+
endpoint_config: Optional[types.DeployRequestEndpointConfigOrDict] = None,
|
|
633
|
+
deploy_config: Optional[types.DeployRequestDeployConfigOrDict] = None,
|
|
634
|
+
config: Optional[types.DeployConfigOrDict] = None,
|
|
635
|
+
) -> types.DeployModelOperation:
|
|
636
|
+
"""
|
|
637
|
+
Deploys a model (internal).
|
|
638
|
+
"""
|
|
639
|
+
|
|
640
|
+
parameter_model = types._DeployRequestParameters(
|
|
641
|
+
destination=destination,
|
|
642
|
+
publisher_model_name=publisher_model_name,
|
|
643
|
+
hugging_face_model_id=hugging_face_model_id,
|
|
644
|
+
custom_model=custom_model,
|
|
645
|
+
model_config_val=model_config_val,
|
|
646
|
+
endpoint_config=endpoint_config,
|
|
647
|
+
deploy_config=deploy_config,
|
|
648
|
+
config=config,
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
request_url_dict: Optional[dict[str, str]]
|
|
652
|
+
if not self._api_client.vertexai:
|
|
653
|
+
raise ValueError(
|
|
654
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
655
|
+
)
|
|
656
|
+
else:
|
|
657
|
+
request_dict = _DeployRequestParameters_to_vertex(parameter_model)
|
|
658
|
+
request_url_dict = request_dict.get("_url")
|
|
659
|
+
if request_url_dict:
|
|
660
|
+
path = "{destination}:deploy".format_map(request_url_dict)
|
|
661
|
+
else:
|
|
662
|
+
path = "{destination}:deploy"
|
|
663
|
+
|
|
664
|
+
query_params = request_dict.get("_query")
|
|
665
|
+
if query_params:
|
|
666
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
667
|
+
# TODO: remove the hack that pops config.
|
|
668
|
+
request_dict.pop("config", None)
|
|
669
|
+
|
|
670
|
+
http_options: Optional[types.HttpOptions] = None
|
|
671
|
+
if (
|
|
672
|
+
parameter_model.config is not None
|
|
673
|
+
and parameter_model.config.http_options is not None
|
|
674
|
+
):
|
|
675
|
+
http_options = parameter_model.config.http_options
|
|
676
|
+
|
|
677
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
678
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
679
|
+
|
|
680
|
+
response = self._api_client.request("post", path, request_dict, http_options)
|
|
681
|
+
|
|
682
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
683
|
+
|
|
684
|
+
return_value = types.DeployModelOperation._from_response(
|
|
685
|
+
response=response_dict,
|
|
686
|
+
kwargs=(
|
|
687
|
+
{
|
|
688
|
+
"config": {
|
|
689
|
+
"response_schema": getattr(
|
|
690
|
+
parameter_model.config, "response_schema", None
|
|
691
|
+
),
|
|
692
|
+
"response_json_schema": getattr(
|
|
693
|
+
parameter_model.config, "response_json_schema", None
|
|
694
|
+
),
|
|
695
|
+
"include_all_fields": getattr(
|
|
696
|
+
parameter_model.config, "include_all_fields", None
|
|
697
|
+
),
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if getattr(parameter_model, "config", None)
|
|
701
|
+
else {}
|
|
702
|
+
),
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
self._api_client._verify_response(return_value)
|
|
706
|
+
return return_value
|
|
707
|
+
|
|
708
|
+
def get_deploy_publisher_model_operation(
|
|
709
|
+
self,
|
|
710
|
+
*,
|
|
711
|
+
operation_name: str,
|
|
712
|
+
config: Optional[types.GetDeployOperationConfigOrDict] = None,
|
|
713
|
+
) -> types.DeployModelOperation:
|
|
714
|
+
"""
|
|
715
|
+
Fetches the status of an in-flight ``deploy_publisher_model`` LRO.
|
|
716
|
+
"""
|
|
717
|
+
|
|
718
|
+
parameter_model = types._GetDeployOperationParameters(
|
|
719
|
+
operation_name=operation_name,
|
|
720
|
+
config=config,
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
request_url_dict: Optional[dict[str, str]]
|
|
724
|
+
if not self._api_client.vertexai:
|
|
725
|
+
raise ValueError(
|
|
726
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
727
|
+
)
|
|
728
|
+
else:
|
|
729
|
+
request_dict = _GetDeployOperationParameters_to_vertex(parameter_model)
|
|
730
|
+
request_url_dict = request_dict.get("_url")
|
|
731
|
+
if request_url_dict:
|
|
732
|
+
path = "{operationName}".format_map(request_url_dict)
|
|
733
|
+
else:
|
|
734
|
+
path = "{operationName}"
|
|
735
|
+
|
|
736
|
+
query_params = request_dict.get("_query")
|
|
737
|
+
if query_params:
|
|
738
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
739
|
+
# TODO: remove the hack that pops config.
|
|
740
|
+
request_dict.pop("config", None)
|
|
741
|
+
|
|
742
|
+
http_options: Optional[types.HttpOptions] = None
|
|
743
|
+
if (
|
|
744
|
+
parameter_model.config is not None
|
|
745
|
+
and parameter_model.config.http_options is not None
|
|
746
|
+
):
|
|
747
|
+
http_options = parameter_model.config.http_options
|
|
748
|
+
|
|
749
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
750
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
751
|
+
|
|
752
|
+
response = self._api_client.request("get", path, request_dict, http_options)
|
|
753
|
+
|
|
754
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
755
|
+
|
|
756
|
+
return_value = types.DeployModelOperation._from_response(
|
|
757
|
+
response=response_dict,
|
|
758
|
+
kwargs=(
|
|
759
|
+
{
|
|
760
|
+
"config": {
|
|
761
|
+
"response_schema": getattr(
|
|
762
|
+
parameter_model.config, "response_schema", None
|
|
763
|
+
),
|
|
764
|
+
"response_json_schema": getattr(
|
|
765
|
+
parameter_model.config, "response_json_schema", None
|
|
766
|
+
),
|
|
767
|
+
"include_all_fields": getattr(
|
|
768
|
+
parameter_model.config, "include_all_fields", None
|
|
769
|
+
),
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if getattr(parameter_model, "config", None)
|
|
773
|
+
else {}
|
|
774
|
+
),
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
self._api_client._verify_response(return_value)
|
|
778
|
+
return return_value
|
|
779
|
+
|
|
780
|
+
# Fallbacks for ``ExportOpenModelConfig`` when the caller does not
|
|
781
|
+
# override them. 2h matches the legacy SDK's blocking ``.export()`` and is
|
|
782
|
+
# generous enough for large open weights (e.g. Gemma 3 27B).
|
|
783
|
+
_DEFAULT_EXPORT_TIMEOUT_SECONDS = 2 * 60 * 60
|
|
784
|
+
_DEFAULT_EXPORT_POLL_INTERVAL_SECONDS = 30
|
|
785
|
+
|
|
786
|
+
# Deploy LRO polling defaults. 2h matches legacy vertexai.model_garden and
|
|
787
|
+
# the Vertex AI Console's one-click deployment timeout.
|
|
788
|
+
_DEFAULT_DEPLOY_TIMEOUT_SECONDS = 2 * 60 * 60
|
|
789
|
+
_DEFAULT_DEPLOY_POLL_INTERVAL_SECONDS = 30
|
|
790
|
+
|
|
791
|
+
@staticmethod
|
|
792
|
+
def _build_filter_str(
|
|
793
|
+
model_filter: Optional[str],
|
|
794
|
+
include_hugging_face_models: bool,
|
|
795
|
+
deployable_only: bool,
|
|
796
|
+
) -> str:
|
|
797
|
+
"""Builds the filter string for the ListPublisherModels API.
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
model_filter: Optional substring to match against model IDs and display
|
|
801
|
+
names (case-insensitive).
|
|
802
|
+
include_hugging_face_models: Whether to include HuggingFace models. If
|
|
803
|
+
True, uses ``is_hf_wildcard(true)``; otherwise ``is_hf_wildcard(false)``.
|
|
804
|
+
deployable_only: Whether to restrict to models with verified deployment
|
|
805
|
+
configurations via the ``VERIFIED_DEPLOYMENT_SUCCEED`` label.
|
|
806
|
+
|
|
807
|
+
Returns:
|
|
808
|
+
A filter string suitable for the ``filter`` parameter of the
|
|
809
|
+
ListPublisherModels API.
|
|
810
|
+
"""
|
|
811
|
+
import re
|
|
812
|
+
|
|
813
|
+
if include_hugging_face_models:
|
|
814
|
+
filter_str = "is_hf_wildcard(true)"
|
|
815
|
+
if deployable_only:
|
|
816
|
+
filter_str += (
|
|
817
|
+
" AND labels.VERIFIED_DEPLOYMENT_CONFIG=VERIFIED_DEPLOYMENT_SUCCEED"
|
|
818
|
+
)
|
|
819
|
+
else:
|
|
820
|
+
filter_str = "is_hf_wildcard(false)"
|
|
821
|
+
|
|
822
|
+
if model_filter:
|
|
823
|
+
escaped = re.escape(model_filter)
|
|
824
|
+
filter_str = (
|
|
825
|
+
f'{filter_str} AND (model_user_id=~"(?i).*{escaped}.*"'
|
|
826
|
+
f' OR display_name=~"(?i).*{escaped}.*")'
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
return filter_str
|
|
830
|
+
|
|
831
|
+
@staticmethod
|
|
832
|
+
def _format_model_name(
|
|
833
|
+
model: types.PublisherModel,
|
|
834
|
+
include_hugging_face_models: bool,
|
|
835
|
+
) -> str:
|
|
836
|
+
"""Formats a PublisherModel into a human-readable model name string.
|
|
837
|
+
|
|
838
|
+
Args:
|
|
839
|
+
model: The PublisherModel to format.
|
|
840
|
+
include_hugging_face_models: Whether HuggingFace models are included in
|
|
841
|
+
the listing. Controls whether the ``@version`` suffix is appended.
|
|
842
|
+
|
|
843
|
+
Returns:
|
|
844
|
+
A formatted model name string in one of the following formats:
|
|
845
|
+
|
|
846
|
+
- ``'{publisher}/{model}@{version}'`` when
|
|
847
|
+
``include_hugging_face_models`` is False.
|
|
848
|
+
- ``'{publisher}/{model}'`` when ``include_hugging_face_models`` is True.
|
|
849
|
+
"""
|
|
850
|
+
import re
|
|
851
|
+
|
|
852
|
+
name = model.name or ""
|
|
853
|
+
formatted = re.sub(r"publishers/(hf-|)|models/", "", name)
|
|
854
|
+
if include_hugging_face_models:
|
|
855
|
+
return formatted
|
|
856
|
+
return formatted + "@" + (model.version_id or "")
|
|
857
|
+
|
|
858
|
+
@staticmethod
|
|
859
|
+
def _has_deploy_config(model: types.PublisherModel) -> bool:
|
|
860
|
+
"""Checks whether a model has verified deployment configurations.
|
|
861
|
+
|
|
862
|
+
Args:
|
|
863
|
+
model: The PublisherModel to check.
|
|
864
|
+
|
|
865
|
+
Returns:
|
|
866
|
+
True if the model has at least one entry in
|
|
867
|
+
``supported_actions.multi_deploy_vertex``.
|
|
868
|
+
"""
|
|
869
|
+
return bool(
|
|
870
|
+
model.supported_actions
|
|
871
|
+
and model.supported_actions.multi_deploy_vertex
|
|
872
|
+
and model.supported_actions.multi_deploy_vertex.multi_deploy_vertex
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
@staticmethod
|
|
876
|
+
def _reconcile_model_name(model_name: str) -> str:
|
|
877
|
+
"""Normalizes a model name into a publisher model resource name.
|
|
878
|
+
|
|
879
|
+
Args:
|
|
880
|
+
model_name: A Model Garden model resource name in the format
|
|
881
|
+
``'publishers/{publisher}/models/{model}@{version}'``, a simplified name
|
|
882
|
+
in the format ``'{publisher}/{model}@{version}'`` (or without
|
|
883
|
+
``@{version}``), or a Hugging Face model ID ``'{organization}/{model}'``.
|
|
884
|
+
|
|
885
|
+
Returns:
|
|
886
|
+
The resource name in the format
|
|
887
|
+
``'publishers/{publisher}/models/{model}@{version}'``.
|
|
888
|
+
|
|
889
|
+
Raises:
|
|
890
|
+
ValueError: If ``model_name`` is not a valid publisher model name.
|
|
891
|
+
"""
|
|
892
|
+
import re
|
|
893
|
+
|
|
894
|
+
model_name = model_name.lower() # Hugging Face IDs are lower-case.
|
|
895
|
+
# A full resource name must carry an @version, matching the legacy SDK; a
|
|
896
|
+
# versionless full name is not accepted.
|
|
897
|
+
full_match = re.match(
|
|
898
|
+
r"^publishers/(?P<publisher>[^/]+)/models/(?P<model>[^@]+)@(?P<version>[^@]+)$",
|
|
899
|
+
model_name,
|
|
900
|
+
)
|
|
901
|
+
if full_match:
|
|
902
|
+
return (
|
|
903
|
+
f"publishers/{full_match.group('publisher')}/models/"
|
|
904
|
+
f"{full_match.group('model')}@{full_match.group('version')}"
|
|
905
|
+
)
|
|
906
|
+
# Reject Model Registry names; they would otherwise match the simplified
|
|
907
|
+
# branch and be silently mangled.
|
|
908
|
+
if re.match(r"^projects/.+/locations/.+/models/.+$", model_name):
|
|
909
|
+
raise ValueError(f"`{model_name}` is not a valid publisher model name")
|
|
910
|
+
simplified_match = re.match(
|
|
911
|
+
r"^(?P<publisher>[^/]+)/(?P<model>[^@]+)(?:@(?P<version>.+))?$",
|
|
912
|
+
model_name,
|
|
913
|
+
)
|
|
914
|
+
if simplified_match:
|
|
915
|
+
model = simplified_match.group("model")
|
|
916
|
+
if simplified_match.group("version"):
|
|
917
|
+
model = f"{model}@{simplified_match.group('version')}"
|
|
918
|
+
return f"publishers/{simplified_match.group('publisher')}/models/{model}"
|
|
919
|
+
raise ValueError(f"`{model_name}` is not a valid publisher model name")
|
|
920
|
+
|
|
921
|
+
@staticmethod
|
|
922
|
+
def _is_hugging_face_model(model_name: str) -> bool:
|
|
923
|
+
"""Returns whether a model name looks like a Hugging Face model ID.
|
|
924
|
+
|
|
925
|
+
Matches the bare ``'{organization}/{model}'`` shape (a single slash and no
|
|
926
|
+
``@version``), e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``.
|
|
927
|
+
|
|
928
|
+
Args:
|
|
929
|
+
model_name: The model name to inspect.
|
|
930
|
+
|
|
931
|
+
Returns:
|
|
932
|
+
True if ``model_name`` matches the Hugging Face ID shape.
|
|
933
|
+
"""
|
|
934
|
+
import re
|
|
935
|
+
|
|
936
|
+
return bool(re.match(r"^(?P<publisher>[^/]+)/(?P<model>[^/@]+)$", model_name))
|
|
937
|
+
|
|
938
|
+
@staticmethod
|
|
939
|
+
def _matches_filter(
|
|
940
|
+
value: Optional[str],
|
|
941
|
+
model_filter: Optional[Union[str, list[str]]],
|
|
942
|
+
) -> bool:
|
|
943
|
+
"""Returns whether ``value`` matches the (optional) keyword filter.
|
|
944
|
+
|
|
945
|
+
Mirrors the legacy SDK: the filter may be a single keyword or a list of
|
|
946
|
+
keywords, and matching is a case-insensitive substring test where the value
|
|
947
|
+
matches if it contains *any* of the keywords.
|
|
948
|
+
|
|
949
|
+
Args:
|
|
950
|
+
value: The field value to test (e.g. a machine type), or None.
|
|
951
|
+
model_filter: A keyword, a list of keywords, or None (no filtering).
|
|
952
|
+
|
|
953
|
+
Returns:
|
|
954
|
+
True if there is no filter, or if ``value`` contains any of the keywords.
|
|
955
|
+
"""
|
|
956
|
+
if not model_filter:
|
|
957
|
+
return True
|
|
958
|
+
if value is None:
|
|
959
|
+
return False
|
|
960
|
+
keywords = [model_filter] if isinstance(model_filter, str) else model_filter
|
|
961
|
+
value_lower = value.lower()
|
|
962
|
+
return any(keyword.lower() in value_lower for keyword in keywords)
|
|
963
|
+
|
|
964
|
+
@staticmethod
|
|
965
|
+
def _extract_and_filter_deploy_options(
|
|
966
|
+
publisher_model: types.PublisherModel,
|
|
967
|
+
machine_type_filter: Optional[Union[str, list[str]]] = None,
|
|
968
|
+
accelerator_type_filter: Optional[Union[str, list[str]]] = None,
|
|
969
|
+
serving_container_image_uri_filter: Optional[Union[str, list[str]]] = None,
|
|
970
|
+
) -> list[types.DeployOption]:
|
|
971
|
+
"""Extracts and filters deploy options from a publisher model.
|
|
972
|
+
|
|
973
|
+
Args:
|
|
974
|
+
publisher_model: The publisher model to extract deploy options from.
|
|
975
|
+
machine_type_filter: Optional case-insensitive keyword (or list of
|
|
976
|
+
keywords) matched against the machine type; an option is kept if its
|
|
977
|
+
machine type contains any of them (e.g. ``'g2'`` or ``['n1', 'g2']``).
|
|
978
|
+
accelerator_type_filter: Optional case-insensitive keyword (or list of
|
|
979
|
+
keywords) matched against the accelerator type (e.g. ``'L4'`` or
|
|
980
|
+
``['T4', 'L4']``).
|
|
981
|
+
serving_container_image_uri_filter: Optional case-insensitive keyword (or
|
|
982
|
+
list of keywords) matched against the serving container image URI
|
|
983
|
+
(e.g. ``'vllm'`` or ``['vllm', 'tgi']``).
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
A list of ``DeployOption`` objects matching the provided filters.
|
|
987
|
+
|
|
988
|
+
Raises:
|
|
989
|
+
ValueError: If the model does not support deployment, or if no deploy
|
|
990
|
+
options remain after applying the filters.
|
|
991
|
+
"""
|
|
992
|
+
if not (
|
|
993
|
+
publisher_model.supported_actions
|
|
994
|
+
and publisher_model.supported_actions.multi_deploy_vertex
|
|
995
|
+
and publisher_model.supported_actions.multi_deploy_vertex.multi_deploy_vertex
|
|
996
|
+
):
|
|
997
|
+
raise ValueError(
|
|
998
|
+
"Model does not support deployment. "
|
|
999
|
+
"Use `list_deployable_models()` to find supported models."
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
options = (
|
|
1003
|
+
publisher_model.supported_actions.multi_deploy_vertex.multi_deploy_vertex
|
|
1004
|
+
)
|
|
1005
|
+
result = []
|
|
1006
|
+
for opt in options:
|
|
1007
|
+
container = opt.container_spec.image_uri if opt.container_spec else None
|
|
1008
|
+
machine = (
|
|
1009
|
+
opt.dedicated_resources.machine_spec
|
|
1010
|
+
if opt.dedicated_resources
|
|
1011
|
+
else None
|
|
1012
|
+
)
|
|
1013
|
+
machine_type = machine.machine_type if machine else None
|
|
1014
|
+
|
|
1015
|
+
# Restore the proto3 defaults the JSON transport drops, so structured
|
|
1016
|
+
# output matches the gRPC SDK on CPU/TPU machines.
|
|
1017
|
+
accelerator_enum = machine.accelerator_type if machine else None
|
|
1018
|
+
accelerator_value = accelerator_enum.value if accelerator_enum else None
|
|
1019
|
+
has_accelerator = (
|
|
1020
|
+
accelerator_value is not None
|
|
1021
|
+
and accelerator_value != "ACCELERATOR_TYPE_UNSPECIFIED"
|
|
1022
|
+
)
|
|
1023
|
+
if machine:
|
|
1024
|
+
accelerator_type = (
|
|
1025
|
+
accelerator_value
|
|
1026
|
+
if accelerator_value is not None
|
|
1027
|
+
else "ACCELERATOR_TYPE_UNSPECIFIED"
|
|
1028
|
+
)
|
|
1029
|
+
accelerator_count = (
|
|
1030
|
+
machine.accelerator_count
|
|
1031
|
+
if machine.accelerator_count is not None
|
|
1032
|
+
else 0
|
|
1033
|
+
)
|
|
1034
|
+
else:
|
|
1035
|
+
accelerator_type = None
|
|
1036
|
+
accelerator_count = None
|
|
1037
|
+
|
|
1038
|
+
if not ModelGarden._matches_filter(machine_type, machine_type_filter):
|
|
1039
|
+
continue
|
|
1040
|
+
# ACCELERATOR_TYPE_UNSPECIFIED means "no accelerator" and never matches.
|
|
1041
|
+
if accelerator_type_filter and not has_accelerator:
|
|
1042
|
+
continue
|
|
1043
|
+
if not ModelGarden._matches_filter(
|
|
1044
|
+
accelerator_type, accelerator_type_filter
|
|
1045
|
+
):
|
|
1046
|
+
continue
|
|
1047
|
+
if not ModelGarden._matches_filter(
|
|
1048
|
+
container, serving_container_image_uri_filter
|
|
1049
|
+
):
|
|
1050
|
+
continue
|
|
1051
|
+
|
|
1052
|
+
result.append(
|
|
1053
|
+
types.DeployOption(
|
|
1054
|
+
option_name=opt.deploy_task_name,
|
|
1055
|
+
serving_container_image_uri=container,
|
|
1056
|
+
machine_type=machine_type,
|
|
1057
|
+
accelerator_type=accelerator_type,
|
|
1058
|
+
accelerator_count=accelerator_count,
|
|
1059
|
+
)
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
if not result:
|
|
1063
|
+
raise ValueError("No deploy options found.")
|
|
1064
|
+
|
|
1065
|
+
return result
|
|
1066
|
+
|
|
1067
|
+
@staticmethod
|
|
1068
|
+
def _format_concise_deploy_options(
|
|
1069
|
+
options: list[types.DeployOption],
|
|
1070
|
+
) -> str:
|
|
1071
|
+
"""Formats deploy options into a human-readable string.
|
|
1072
|
+
|
|
1073
|
+
Mirrors the legacy ``vertexai.model_garden`` SDK output: each option is
|
|
1074
|
+
rendered as a ``[Option N: <option_name>]`` block followed by its non-null
|
|
1075
|
+
fields (container image, machine type, accelerator type/count).
|
|
1076
|
+
|
|
1077
|
+
Args:
|
|
1078
|
+
options: The deploy options to format.
|
|
1079
|
+
|
|
1080
|
+
Returns:
|
|
1081
|
+
A human-readable, multi-line string describing the deploy options.
|
|
1082
|
+
"""
|
|
1083
|
+
fields = [
|
|
1084
|
+
"serving_container_image_uri",
|
|
1085
|
+
"machine_type",
|
|
1086
|
+
"accelerator_type",
|
|
1087
|
+
"accelerator_count",
|
|
1088
|
+
]
|
|
1089
|
+
blocks = []
|
|
1090
|
+
for i, option in enumerate(options):
|
|
1091
|
+
if option.option_name:
|
|
1092
|
+
header = f"[Option {i + 1}: {option.option_name}]\n"
|
|
1093
|
+
else:
|
|
1094
|
+
header = f"[Option {i + 1}]\n"
|
|
1095
|
+
lines = []
|
|
1096
|
+
for field in fields:
|
|
1097
|
+
value = getattr(option, field)
|
|
1098
|
+
if value is None:
|
|
1099
|
+
continue
|
|
1100
|
+
if field == "accelerator_count":
|
|
1101
|
+
lines.append(f" {field}={value},")
|
|
1102
|
+
else:
|
|
1103
|
+
lines.append(f' {field}="{value}",')
|
|
1104
|
+
blocks.append(header + "\n".join(lines))
|
|
1105
|
+
return "\n\n".join(blocks)
|
|
1106
|
+
|
|
1107
|
+
@staticmethod
|
|
1108
|
+
def _resolve_deploy_model_name(model: str) -> tuple[Optional[str], Optional[str]]:
|
|
1109
|
+
"""Returns the ``(publisher_model_name, hugging_face_model_id)`` pair for a deploy call.
|
|
1110
|
+
|
|
1111
|
+
Exactly one element of the returned pair is set: HF model IDs (matched
|
|
1112
|
+
by ``_is_hugging_face_model``) are sent as ``huggingFaceModelId`` and
|
|
1113
|
+
lowercased for legacy parity; everything else is reconciled to the full
|
|
1114
|
+
``publishers/{pub}/models/{model}@{version}`` form.
|
|
1115
|
+
"""
|
|
1116
|
+
if ModelGarden._is_hugging_face_model(model):
|
|
1117
|
+
return None, model.lower()
|
|
1118
|
+
return ModelGarden._reconcile_model_name(model), None
|
|
1119
|
+
|
|
1120
|
+
@staticmethod
|
|
1121
|
+
def _validate_deploy_config(
|
|
1122
|
+
config: types.DeployPublisherModelConfig,
|
|
1123
|
+
) -> None:
|
|
1124
|
+
"""Rejects deploy configs whose options would be silently dropped.
|
|
1125
|
+
|
|
1126
|
+
Container overrides are only sent when ``serving_container_image_uri``
|
|
1127
|
+
is set (the backend has no way to patch a model's default container
|
|
1128
|
+
piecemeal), so a config that sets only the command/args/env would
|
|
1129
|
+
otherwise deploy successfully with none of the overrides applied.
|
|
1130
|
+
|
|
1131
|
+
Raises:
|
|
1132
|
+
ValueError: If any container override is set without
|
|
1133
|
+
``serving_container_image_uri``.
|
|
1134
|
+
"""
|
|
1135
|
+
if config.serving_container_image_uri:
|
|
1136
|
+
return
|
|
1137
|
+
dropped = [
|
|
1138
|
+
name
|
|
1139
|
+
for name in ("container_command", "container_args", "container_variables")
|
|
1140
|
+
if getattr(config, name)
|
|
1141
|
+
]
|
|
1142
|
+
if dropped:
|
|
1143
|
+
raise ValueError(
|
|
1144
|
+
f"{', '.join(dropped)} require serving_container_image_uri to "
|
|
1145
|
+
"also be set; container overrides are only applied on top of an "
|
|
1146
|
+
"explicit serving container image."
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
@staticmethod
|
|
1150
|
+
def _deploy_response_or_raise(
|
|
1151
|
+
operation: types.DeployModelOperation,
|
|
1152
|
+
) -> types.DeployResponse:
|
|
1153
|
+
"""Unwraps a completed deploy LRO into its ``DeployResponse``.
|
|
1154
|
+
|
|
1155
|
+
Shared by the sync and async surfaces so the two cannot drift as the
|
|
1156
|
+
LRO shape evolves.
|
|
1157
|
+
|
|
1158
|
+
Raises:
|
|
1159
|
+
RuntimeError: If the LRO carries an error, or completed without an
|
|
1160
|
+
endpoint resource name.
|
|
1161
|
+
"""
|
|
1162
|
+
if operation.error:
|
|
1163
|
+
raise RuntimeError(f"Deploy failed: {operation.error}")
|
|
1164
|
+
if not operation.response or not operation.response.endpoint:
|
|
1165
|
+
raise RuntimeError(
|
|
1166
|
+
f"Deploy completed but response has no endpoint: {operation!r}"
|
|
1167
|
+
)
|
|
1168
|
+
return operation.response
|
|
1169
|
+
|
|
1170
|
+
@staticmethod
|
|
1171
|
+
def _build_container_spec(
|
|
1172
|
+
config: types.DeployPublisherModelConfig,
|
|
1173
|
+
) -> Optional[types.ModelContainerSpec]:
|
|
1174
|
+
"""Returns a ``ModelContainerSpec`` when the user overrides the container, else None."""
|
|
1175
|
+
if not config.serving_container_image_uri:
|
|
1176
|
+
return None
|
|
1177
|
+
env = None
|
|
1178
|
+
variables = config.container_variables
|
|
1179
|
+
if variables:
|
|
1180
|
+
env = [types.EnvVar(name=k, value=v) for k, v in variables.items()]
|
|
1181
|
+
return types.ModelContainerSpec(
|
|
1182
|
+
image_uri=config.serving_container_image_uri,
|
|
1183
|
+
command=config.container_command,
|
|
1184
|
+
args=config.container_args,
|
|
1185
|
+
env=env,
|
|
1186
|
+
)
|
|
1187
|
+
|
|
1188
|
+
@staticmethod
|
|
1189
|
+
def _prepare_deploy_request(
|
|
1190
|
+
config: types.DeployPublisherModelConfig,
|
|
1191
|
+
) -> tuple[
|
|
1192
|
+
types.DeployRequestModelConfig,
|
|
1193
|
+
types.DeployRequestEndpointConfig,
|
|
1194
|
+
types.DeployRequestDeployConfig,
|
|
1195
|
+
]:
|
|
1196
|
+
"""Translates ``DeployPublisherModelConfig`` to the three ``DeployRequest`` sub-messages."""
|
|
1197
|
+
model_config = types.DeployRequestModelConfig(
|
|
1198
|
+
accept_eula=config.accept_eula,
|
|
1199
|
+
model_display_name=config.model_display_name,
|
|
1200
|
+
hugging_face_access_token=config.hugging_face_access_token,
|
|
1201
|
+
container_spec=ModelGarden._build_container_spec(config),
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
endpoint_config = types.DeployRequestEndpointConfig(
|
|
1205
|
+
endpoint_display_name=config.endpoint_display_name,
|
|
1206
|
+
)
|
|
1207
|
+
disabled = config.dedicated_endpoint_disabled
|
|
1208
|
+
if disabled is not None:
|
|
1209
|
+
endpoint_config.dedicated_endpoint_enabled = not disabled
|
|
1210
|
+
if config.enable_private_service_connect:
|
|
1211
|
+
endpoint_config.private_service_connect_config = (
|
|
1212
|
+
types.PrivateServiceConnectConfig(
|
|
1213
|
+
enable_private_service_connect=True,
|
|
1214
|
+
project_allowlist=config.psc_project_allow_list,
|
|
1215
|
+
)
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
deploy_config = types.DeployRequestDeployConfig(
|
|
1219
|
+
fast_tryout_enabled=config.fast_tryout_enabled,
|
|
1220
|
+
)
|
|
1221
|
+
if config.machine_type or config.accelerator_type or config.accelerator_count:
|
|
1222
|
+
deploy_config.dedicated_resources = types.DedicatedResources(
|
|
1223
|
+
machine_spec=types.MachineSpec(
|
|
1224
|
+
machine_type=config.machine_type,
|
|
1225
|
+
accelerator_type=config.accelerator_type,
|
|
1226
|
+
accelerator_count=config.accelerator_count,
|
|
1227
|
+
),
|
|
1228
|
+
min_replica_count=config.min_replica_count,
|
|
1229
|
+
max_replica_count=config.max_replica_count,
|
|
1230
|
+
spot=config.spot,
|
|
1231
|
+
)
|
|
1232
|
+
|
|
1233
|
+
return model_config, endpoint_config, deploy_config
|
|
1234
|
+
|
|
1235
|
+
def _list_all_publisher_models(
|
|
1236
|
+
self,
|
|
1237
|
+
api_config: types.ListPublisherModelsConfig,
|
|
1238
|
+
) -> list[types.PublisherModel]:
|
|
1239
|
+
"""Fetches all pages of publisher models from the API.
|
|
1240
|
+
|
|
1241
|
+
Args:
|
|
1242
|
+
api_config: The configuration for the ListPublisherModels API call,
|
|
1243
|
+
including filter and version settings.
|
|
1244
|
+
|
|
1245
|
+
Returns:
|
|
1246
|
+
A list of all ``PublisherModel`` objects across all pages.
|
|
1247
|
+
"""
|
|
1248
|
+
all_models = []
|
|
1249
|
+
page_token = None
|
|
1250
|
+
while True:
|
|
1251
|
+
if page_token:
|
|
1252
|
+
api_config = types.ListPublisherModelsConfig(
|
|
1253
|
+
filter=api_config.filter,
|
|
1254
|
+
list_all_versions=api_config.list_all_versions,
|
|
1255
|
+
page_token=page_token,
|
|
1256
|
+
)
|
|
1257
|
+
response = self._list_publisher_models(
|
|
1258
|
+
parent="publishers/*",
|
|
1259
|
+
config=api_config,
|
|
1260
|
+
)
|
|
1261
|
+
all_models.extend(response.publisher_models or [])
|
|
1262
|
+
page_token = response.next_page_token
|
|
1263
|
+
if not page_token:
|
|
1264
|
+
break
|
|
1265
|
+
return all_models
|
|
1266
|
+
|
|
1267
|
+
def _list(
|
|
1268
|
+
self,
|
|
1269
|
+
model_filter: Optional[str],
|
|
1270
|
+
include_hugging_face_models: Optional[bool],
|
|
1271
|
+
deployable_only: bool,
|
|
1272
|
+
) -> list[str]:
|
|
1273
|
+
"""Shared implementation for listing models.
|
|
1274
|
+
|
|
1275
|
+
Args:
|
|
1276
|
+
model_filter: Optional substring to filter models by.
|
|
1277
|
+
include_hugging_face_models: Whether to include HuggingFace models.
|
|
1278
|
+
deployable_only: If True, only return models with deployment configs.
|
|
1279
|
+
|
|
1280
|
+
Returns:
|
|
1281
|
+
A list of formatted model name strings.
|
|
1282
|
+
"""
|
|
1283
|
+
include_hf = include_hugging_face_models is True
|
|
1284
|
+
|
|
1285
|
+
filter_str = self._build_filter_str(
|
|
1286
|
+
model_filter, include_hf, deployable_only=deployable_only
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
api_config = types.ListPublisherModelsConfig(
|
|
1290
|
+
filter=filter_str,
|
|
1291
|
+
list_all_versions=True,
|
|
1292
|
+
)
|
|
1293
|
+
|
|
1294
|
+
models = self._list_all_publisher_models(api_config)
|
|
1295
|
+
|
|
1296
|
+
if deployable_only:
|
|
1297
|
+
# The VERIFIED_DEPLOYMENT_SUCCEED server filter only applies to HF
|
|
1298
|
+
# models; filter native models client-side via multi_deploy_vertex.
|
|
1299
|
+
models = [m for m in models if self._has_deploy_config(m)]
|
|
1300
|
+
|
|
1301
|
+
return [self._format_model_name(m, include_hf) for m in models]
|
|
1302
|
+
|
|
1303
|
+
def list_deployable_models(
|
|
1304
|
+
self,
|
|
1305
|
+
config: Optional[types.ListDeployableModelsConfigOrDict] = None,
|
|
1306
|
+
) -> list[str]:
|
|
1307
|
+
"""Lists models in Model Garden that support deployment.
|
|
1308
|
+
|
|
1309
|
+
Returns models that have at least one verified deployment configuration.
|
|
1310
|
+
When ``include_hugging_face_models`` is False (the default),
|
|
1311
|
+
HuggingFace models are excluded from the results.
|
|
1312
|
+
|
|
1313
|
+
Args:
|
|
1314
|
+
config: Optional configuration for filtering results. Accepts a
|
|
1315
|
+
``ListDeployableModelsConfig`` instance or an equivalent dict.
|
|
1316
|
+
|
|
1317
|
+
Returns:
|
|
1318
|
+
A list of model name strings in the format
|
|
1319
|
+
``'{publisher}/{model}@{version}'`` (e.g. ``'google/gemma2@gemma-2-2b-it'``)
|
|
1320
|
+
or ``'{publisher}/{model}'`` when ``include_hugging_face_models`` is True
|
|
1321
|
+
(e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
1322
|
+
"""
|
|
1323
|
+
if config is None:
|
|
1324
|
+
config = types.ListDeployableModelsConfig()
|
|
1325
|
+
if isinstance(config, dict):
|
|
1326
|
+
config = types.ListDeployableModelsConfig.model_validate(config)
|
|
1327
|
+
|
|
1328
|
+
return self._list(
|
|
1329
|
+
config.model_filter,
|
|
1330
|
+
config.include_hugging_face_models,
|
|
1331
|
+
deployable_only=True,
|
|
1332
|
+
)
|
|
1333
|
+
|
|
1334
|
+
def list_models(
|
|
1335
|
+
self,
|
|
1336
|
+
config: Optional[types.ListModelGardenModelsConfigOrDict] = None,
|
|
1337
|
+
) -> list[str]:
|
|
1338
|
+
"""Lists all models available in Model Garden.
|
|
1339
|
+
|
|
1340
|
+
Returns all models regardless of deployment support. When
|
|
1341
|
+
``include_hugging_face_models`` is False (the default), HuggingFace
|
|
1342
|
+
models are excluded from the results.
|
|
1343
|
+
|
|
1344
|
+
Args:
|
|
1345
|
+
config: Optional configuration for filtering results. Accepts a
|
|
1346
|
+
``ListModelGardenModelsConfig`` instance or an equivalent dict.
|
|
1347
|
+
|
|
1348
|
+
Returns:
|
|
1349
|
+
A list of model name strings in the format
|
|
1350
|
+
``'{publisher}/{model}@{version}'`` (e.g. ``'google/gemma2@gemma-2-2b-it'``)
|
|
1351
|
+
or ``'{publisher}/{model}'`` when ``include_hugging_face_models`` is True
|
|
1352
|
+
(e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
1353
|
+
"""
|
|
1354
|
+
if config is None:
|
|
1355
|
+
config = types.ListModelGardenModelsConfig()
|
|
1356
|
+
if isinstance(config, dict):
|
|
1357
|
+
config = types.ListModelGardenModelsConfig.model_validate(config)
|
|
1358
|
+
|
|
1359
|
+
return self._list(
|
|
1360
|
+
config.model_filter,
|
|
1361
|
+
config.include_hugging_face_models,
|
|
1362
|
+
deployable_only=False,
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
def list_publisher_model_deploy_options(
|
|
1366
|
+
self,
|
|
1367
|
+
model: str,
|
|
1368
|
+
config: Optional[types.ListPublisherModelDeployOptionsConfigOrDict] = None,
|
|
1369
|
+
) -> Union[str, list[types.DeployOption]]:
|
|
1370
|
+
"""Lists the verified deploy options for a Model Garden publisher model.
|
|
1371
|
+
|
|
1372
|
+
Supports Google open models (e.g. ``'google/gemma3@gemma-3-12b-it'``),
|
|
1373
|
+
partner publisher models (e.g.
|
|
1374
|
+
``'mistralai/mistral-7b@mistral-7b-instruct-v0.2'``), and Hugging Face
|
|
1375
|
+
model IDs (e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
1376
|
+
|
|
1377
|
+
Args:
|
|
1378
|
+
model: The publisher model to list deploy options for. Accepts the full
|
|
1379
|
+
resource name ``'publishers/{publisher}/models/{model}@{version}'``, a
|
|
1380
|
+
simplified ``'{publisher}/{model}@{version}'`` (or without the
|
|
1381
|
+
``@{version}``), or a Hugging Face model ID ``'{organization}/{model}'``.
|
|
1382
|
+
config: Optional configuration for filtering the deploy options. Accepts a
|
|
1383
|
+
``ListPublisherModelDeployOptionsConfig`` instance or an equivalent
|
|
1384
|
+
dict.
|
|
1385
|
+
|
|
1386
|
+
Returns:
|
|
1387
|
+
A list of ``DeployOption`` objects, one per verified deployment
|
|
1388
|
+
configuration (container image URI, machine type, accelerator type and
|
|
1389
|
+
count). If ``config.concise`` is True, returns a human-readable string
|
|
1390
|
+
describing those deploy options instead.
|
|
1391
|
+
|
|
1392
|
+
Raises:
|
|
1393
|
+
ValueError: If ``model`` is not a valid publisher model name, if the
|
|
1394
|
+
model does not support deployment, or if no deploy options match the
|
|
1395
|
+
provided filters.
|
|
1396
|
+
"""
|
|
1397
|
+
if config is None:
|
|
1398
|
+
config = types.ListPublisherModelDeployOptionsConfig()
|
|
1399
|
+
if isinstance(config, dict):
|
|
1400
|
+
config = types.ListPublisherModelDeployOptionsConfig.model_validate(config)
|
|
1401
|
+
|
|
1402
|
+
get_publisher_model_config = types.GetPublisherModelConfig(
|
|
1403
|
+
is_hugging_face_model=self._is_hugging_face_model(model),
|
|
1404
|
+
include_equivalent_model_garden_model_deployment_configs=True,
|
|
1405
|
+
)
|
|
1406
|
+
publisher_model = self._get_publisher_model(
|
|
1407
|
+
name=self._reconcile_model_name(model),
|
|
1408
|
+
config=get_publisher_model_config,
|
|
1409
|
+
)
|
|
1410
|
+
|
|
1411
|
+
options = self._extract_and_filter_deploy_options(
|
|
1412
|
+
publisher_model,
|
|
1413
|
+
machine_type_filter=config.machine_type_filter,
|
|
1414
|
+
accelerator_type_filter=config.accelerator_type_filter,
|
|
1415
|
+
serving_container_image_uri_filter=config.serving_container_image_uri_filter,
|
|
1416
|
+
)
|
|
1417
|
+
|
|
1418
|
+
if config.concise is True:
|
|
1419
|
+
return self._format_concise_deploy_options(options)
|
|
1420
|
+
|
|
1421
|
+
return options
|
|
1422
|
+
|
|
1423
|
+
def deploy_publisher_model(
|
|
1424
|
+
self,
|
|
1425
|
+
*,
|
|
1426
|
+
model: str,
|
|
1427
|
+
config: Optional[types.DeployPublisherModelConfigOrDict] = None,
|
|
1428
|
+
) -> Union[types.DeployResponse, types.DeployModelOperation]:
|
|
1429
|
+
"""Deploys a Model Garden publisher model to a Vertex AI endpoint.
|
|
1430
|
+
|
|
1431
|
+
Supports Google open models (e.g. ``'google/gemma3@gemma-3-12b-it'``),
|
|
1432
|
+
partner publisher models (e.g. ``'ai21/jamba-large-1.6@001'``), and
|
|
1433
|
+
Hugging Face model IDs (e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
1434
|
+
|
|
1435
|
+
Args:
|
|
1436
|
+
model: The publisher model to deploy. Accepts the full resource name
|
|
1437
|
+
``'publishers/{publisher}/models/{model}@{version}'``, a simplified
|
|
1438
|
+
``'{publisher}/{model}@{version}'`` (or without the ``@{version}``),
|
|
1439
|
+
or a Hugging Face model ID ``'{organization}/{model}'``. Hugging
|
|
1440
|
+
Face model IDs are lowercased before being sent, matching
|
|
1441
|
+
``vertexai.model_garden.OpenModel.deploy`` behavior.
|
|
1442
|
+
config: Optional deployment configuration (machine shape, container
|
|
1443
|
+
overrides, blocking behavior, poll timing). See
|
|
1444
|
+
``DeployPublisherModelConfig``.
|
|
1445
|
+
|
|
1446
|
+
Returns:
|
|
1447
|
+
When ``config.wait_for_completion`` is ``True`` (default), a
|
|
1448
|
+
``DeployResponse`` carrying the deployed ``endpoint`` and ``model``
|
|
1449
|
+
resource names (both ``str``).
|
|
1450
|
+
When ``config.wait_for_completion`` is ``False``, the
|
|
1451
|
+
``DeployModelOperation`` for the caller to poll (via
|
|
1452
|
+
``get_deploy_publisher_model_operation`` or their own strategy).
|
|
1453
|
+
|
|
1454
|
+
Raises:
|
|
1455
|
+
ValueError: If ``model`` is not a valid publisher model name, or if
|
|
1456
|
+
container overrides are set without ``serving_container_image_uri``.
|
|
1457
|
+
TimeoutError: If ``wait_for_completion=True`` and the LRO does not
|
|
1458
|
+
complete within ``config.timeout_seconds`` (default 2 hours).
|
|
1459
|
+
RuntimeError: If the LRO completes with an error, or completes
|
|
1460
|
+
successfully but without an endpoint resource name.
|
|
1461
|
+
"""
|
|
1462
|
+
if config is None:
|
|
1463
|
+
config = types.DeployPublisherModelConfig()
|
|
1464
|
+
elif isinstance(config, dict):
|
|
1465
|
+
config = types.DeployPublisherModelConfig.model_validate(config)
|
|
1466
|
+
ModelGarden._validate_deploy_config(config)
|
|
1467
|
+
|
|
1468
|
+
publisher_model_name, hugging_face_model_id = (
|
|
1469
|
+
ModelGarden._resolve_deploy_model_name(model)
|
|
1470
|
+
)
|
|
1471
|
+
model_config, endpoint_config, deploy_config = (
|
|
1472
|
+
ModelGarden._prepare_deploy_request(config)
|
|
1473
|
+
)
|
|
1474
|
+
destination = (
|
|
1475
|
+
f"projects/{self._api_client.project}/locations/"
|
|
1476
|
+
f"{self._api_client.location}"
|
|
1477
|
+
)
|
|
1478
|
+
|
|
1479
|
+
operation = self._deploy(
|
|
1480
|
+
destination=destination,
|
|
1481
|
+
publisher_model_name=publisher_model_name,
|
|
1482
|
+
hugging_face_model_id=hugging_face_model_id,
|
|
1483
|
+
model_config_val=model_config,
|
|
1484
|
+
endpoint_config=endpoint_config,
|
|
1485
|
+
deploy_config=deploy_config,
|
|
1486
|
+
)
|
|
1487
|
+
if not config.wait_for_completion:
|
|
1488
|
+
return operation
|
|
1489
|
+
|
|
1490
|
+
operation = _operations_utils.await_operation(
|
|
1491
|
+
operation_name=operation.name,
|
|
1492
|
+
get_operation_fn=self.get_deploy_publisher_model_operation,
|
|
1493
|
+
poll_interval=(
|
|
1494
|
+
config.poll_interval_seconds
|
|
1495
|
+
or ModelGarden._DEFAULT_DEPLOY_POLL_INTERVAL_SECONDS
|
|
1496
|
+
),
|
|
1497
|
+
timeout_seconds=(
|
|
1498
|
+
config.timeout_seconds or ModelGarden._DEFAULT_DEPLOY_TIMEOUT_SECONDS
|
|
1499
|
+
),
|
|
1500
|
+
)
|
|
1501
|
+
return ModelGarden._deploy_response_or_raise(operation)
|
|
1502
|
+
|
|
1503
|
+
@staticmethod
|
|
1504
|
+
def _extract_recommend_spec(spec) -> dict[str, Any]:
|
|
1505
|
+
"""Extracts machine spec fields from a single recommend-spec entry.
|
|
1506
|
+
|
|
1507
|
+
Args:
|
|
1508
|
+
spec: A ``RecommendSpecResponseMachineAndModelContainerSpec`` describing a
|
|
1509
|
+
recommended machine and container configuration.
|
|
1510
|
+
|
|
1511
|
+
Returns:
|
|
1512
|
+
A dict with the ``machine_type``, ``accelerator_type`` and
|
|
1513
|
+
``accelerator_count`` of the spec (values may be ``None``).
|
|
1514
|
+
"""
|
|
1515
|
+
machine_spec = spec.machine_spec
|
|
1516
|
+
machine_type = None
|
|
1517
|
+
accelerator_type = None
|
|
1518
|
+
accelerator_count = None
|
|
1519
|
+
|
|
1520
|
+
if machine_spec:
|
|
1521
|
+
machine_type = getattr(machine_spec, "machine_type", None)
|
|
1522
|
+
accelerator_enum = getattr(machine_spec, "accelerator_type", None)
|
|
1523
|
+
if accelerator_enum:
|
|
1524
|
+
accelerator_type = getattr(accelerator_enum, "name", None)
|
|
1525
|
+
accelerator_count = getattr(machine_spec, "accelerator_count", None)
|
|
1526
|
+
|
|
1527
|
+
return {
|
|
1528
|
+
"machine_type": machine_type,
|
|
1529
|
+
"accelerator_type": accelerator_type,
|
|
1530
|
+
"accelerator_count": accelerator_count,
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
@staticmethod
|
|
1534
|
+
def _extract_recommendation(recommendation) -> dict[str, Any]:
|
|
1535
|
+
"""Extracts the spec, region and user quota state from a recommendation.
|
|
1536
|
+
|
|
1537
|
+
Args:
|
|
1538
|
+
recommendation: A ``RecommendSpecResponseRecommendation`` returned when
|
|
1539
|
+
machine availability is requested.
|
|
1540
|
+
|
|
1541
|
+
Returns:
|
|
1542
|
+
A dict with the machine spec fields plus ``region`` and, when known, the
|
|
1543
|
+
``user_quota_state``.
|
|
1544
|
+
"""
|
|
1545
|
+
extracted_spec = ModelGarden._extract_recommend_spec(recommendation.spec)
|
|
1546
|
+
extracted_spec["region"] = getattr(recommendation, "region", None)
|
|
1547
|
+
if (
|
|
1548
|
+
recommendation.user_quota_state
|
|
1549
|
+
and recommendation.user_quota_state
|
|
1550
|
+
!= types.QuotaState.QUOTA_STATE_UNSPECIFIED
|
|
1551
|
+
):
|
|
1552
|
+
extracted_spec["user_quota_state"] = recommendation.user_quota_state.name
|
|
1553
|
+
return extracted_spec
|
|
1554
|
+
|
|
1555
|
+
@staticmethod
|
|
1556
|
+
def _format_custom_deploy_options(options: list[dict[str, Any]]) -> str:
|
|
1557
|
+
"""Formats custom model deploy options into a human-readable string.
|
|
1558
|
+
|
|
1559
|
+
Mirrors the legacy ``vertexai.model_garden`` ``CustomModel`` SDK output:
|
|
1560
|
+
each option is rendered as an ``[Option N]`` block followed by its non-null
|
|
1561
|
+
fields; ``accelerator_count`` is rendered unquoted.
|
|
1562
|
+
|
|
1563
|
+
Args:
|
|
1564
|
+
options: The extracted deploy option dicts to format.
|
|
1565
|
+
|
|
1566
|
+
Returns:
|
|
1567
|
+
A human-readable, multi-line string describing the deploy options.
|
|
1568
|
+
"""
|
|
1569
|
+
return "\n\n".join(
|
|
1570
|
+
f"[Option {i + 1}]\n"
|
|
1571
|
+
+ ",\n".join(
|
|
1572
|
+
f' {k}="{v}"' if k != "accelerator_count" else f" {k}={v}"
|
|
1573
|
+
for k, v in option.items()
|
|
1574
|
+
if v is not None
|
|
1575
|
+
)
|
|
1576
|
+
for i, option in enumerate(options)
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
def list_custom_model_deploy_options(
|
|
1580
|
+
self,
|
|
1581
|
+
src: str,
|
|
1582
|
+
config: Optional[types.ListCustomModelDeployOptionsConfigOrDict] = None,
|
|
1583
|
+
) -> str:
|
|
1584
|
+
"""Lists the recommended deploy options for a Model Garden custom model.
|
|
1585
|
+
|
|
1586
|
+
Args:
|
|
1587
|
+
src: The Google Cloud Storage URI of the custom model, storing the model
|
|
1588
|
+
weights and config files (e.g. ``'gs://my-bucket/weights/'``).
|
|
1589
|
+
config: Optional configuration. Accepts a
|
|
1590
|
+
``ListCustomModelDeployOptionsConfig`` instance or an equivalent dict.
|
|
1591
|
+
|
|
1592
|
+
Returns:
|
|
1593
|
+
A human-readable string describing the recommended deploy options
|
|
1594
|
+
(machine type, accelerator type/count, region and, when available, the
|
|
1595
|
+
user quota state).
|
|
1596
|
+
|
|
1597
|
+
Raises:
|
|
1598
|
+
ValueError: If ``src`` is not specified, or if no deploy options are
|
|
1599
|
+
returned by the API (either because the backend produced none or
|
|
1600
|
+
because the ``filter_by_user_quota`` filter dropped them all).
|
|
1601
|
+
"""
|
|
1602
|
+
if not src:
|
|
1603
|
+
raise ValueError("src must be specified.")
|
|
1604
|
+
if config is None:
|
|
1605
|
+
config = types.ListCustomModelDeployOptionsConfig()
|
|
1606
|
+
if isinstance(config, dict):
|
|
1607
|
+
config = types.ListCustomModelDeployOptionsConfig.model_validate(config)
|
|
1608
|
+
|
|
1609
|
+
parent = (
|
|
1610
|
+
f"projects/{self._api_client.project}/locations/"
|
|
1611
|
+
f"{self._api_client.location}"
|
|
1612
|
+
)
|
|
1613
|
+
|
|
1614
|
+
api_config = types.RecommendSpecConfig(
|
|
1615
|
+
check_machine_availability=config.check_machine_availability,
|
|
1616
|
+
check_user_quota=config.filter_by_user_quota,
|
|
1617
|
+
)
|
|
1618
|
+
|
|
1619
|
+
response = self._recommend_spec(
|
|
1620
|
+
parent=parent,
|
|
1621
|
+
gcs_uri=src,
|
|
1622
|
+
config=api_config,
|
|
1623
|
+
)
|
|
1624
|
+
|
|
1625
|
+
options = []
|
|
1626
|
+
if response.recommendations:
|
|
1627
|
+
options = [
|
|
1628
|
+
self._extract_recommendation(recommendation)
|
|
1629
|
+
for recommendation in response.recommendations
|
|
1630
|
+
if recommendation.spec
|
|
1631
|
+
]
|
|
1632
|
+
if config.filter_by_user_quota:
|
|
1633
|
+
options = [
|
|
1634
|
+
option
|
|
1635
|
+
for option in options
|
|
1636
|
+
if option.get("user_quota_state")
|
|
1637
|
+
== types.QuotaState.QUOTA_STATE_USER_HAS_QUOTA.name
|
|
1638
|
+
]
|
|
1639
|
+
elif response.specs:
|
|
1640
|
+
options = [
|
|
1641
|
+
self._extract_recommend_spec(spec) for spec in response.specs if spec
|
|
1642
|
+
]
|
|
1643
|
+
|
|
1644
|
+
if not options:
|
|
1645
|
+
raise ValueError("No deploy options found.")
|
|
1646
|
+
|
|
1647
|
+
return self._format_custom_deploy_options(options)
|
|
1648
|
+
|
|
1649
|
+
def export_open_model(
|
|
1650
|
+
self,
|
|
1651
|
+
*,
|
|
1652
|
+
model: str,
|
|
1653
|
+
output_gcs_uri: str,
|
|
1654
|
+
config: Optional[types.ExportOpenModelConfigOrDict] = None,
|
|
1655
|
+
) -> Union[str, types.ExportModelOperation]:
|
|
1656
|
+
"""Exports Google open model weights to a Cloud Storage bucket.
|
|
1657
|
+
|
|
1658
|
+
Args:
|
|
1659
|
+
model: The publisher model to export. Accepts a full resource name
|
|
1660
|
+
``'publishers/{publisher}/models/{model}@{version}'`` or the
|
|
1661
|
+
simplified form ``'{publisher}/{model}@{version}'``. Hugging Face
|
|
1662
|
+
model IDs are not supported.
|
|
1663
|
+
output_gcs_uri: Cloud Storage URI prefix to write the model weights
|
|
1664
|
+
to (e.g. ``'gs://my-bucket/gemma-weights/'``).
|
|
1665
|
+
config: Optional export configuration (blocking behavior, poll
|
|
1666
|
+
timing). See ``ExportOpenModelConfig``.
|
|
1667
|
+
|
|
1668
|
+
Returns:
|
|
1669
|
+
When ``config.wait_for_completion`` is ``True`` (default), the
|
|
1670
|
+
Cloud Storage URI (``str``) where the weights were written.
|
|
1671
|
+
When ``config.wait_for_completion`` is ``False``, the
|
|
1672
|
+
``ExportModelOperation`` for the caller to poll (via
|
|
1673
|
+
``get_export_publisher_model_operation`` or their own strategy).
|
|
1674
|
+
|
|
1675
|
+
Raises:
|
|
1676
|
+
ValueError: If ``output_gcs_uri`` is empty, if ``model`` is not a
|
|
1677
|
+
valid Google publisher model resource name, or if ``model`` looks
|
|
1678
|
+
like a Hugging Face model ID.
|
|
1679
|
+
TimeoutError: If ``wait_for_completion=True`` and the LRO does not
|
|
1680
|
+
complete within ``config.timeout_seconds`` (default 2 hours).
|
|
1681
|
+
RuntimeError: If the LRO completes with an error, or completes
|
|
1682
|
+
successfully but without a ``destination_uri``.
|
|
1683
|
+
"""
|
|
1684
|
+
if not output_gcs_uri:
|
|
1685
|
+
raise ValueError("output_gcs_uri must be a non-empty Cloud Storage URI.")
|
|
1686
|
+
if ModelGarden._is_hugging_face_model(model):
|
|
1687
|
+
raise ValueError(
|
|
1688
|
+
f"export_open_model does not support Hugging Face model IDs "
|
|
1689
|
+
f"(got {model!r}); only Google publisher open models are "
|
|
1690
|
+
f"exportable via this API."
|
|
1691
|
+
)
|
|
1692
|
+
if config is None:
|
|
1693
|
+
config = types.ExportOpenModelConfig()
|
|
1694
|
+
elif isinstance(config, dict):
|
|
1695
|
+
config = types.ExportOpenModelConfig.model_validate(config)
|
|
1696
|
+
|
|
1697
|
+
publisher_model_name = ModelGarden._reconcile_model_name(model)
|
|
1698
|
+
parent = (
|
|
1699
|
+
f"projects/{self._api_client.project}/locations/"
|
|
1700
|
+
f"{self._api_client.location}"
|
|
1701
|
+
)
|
|
1702
|
+
operation = self._export_publisher_model(
|
|
1703
|
+
parent=parent,
|
|
1704
|
+
name=publisher_model_name,
|
|
1705
|
+
config=types.ExportPublisherModelConfig(
|
|
1706
|
+
destination=genai_types.GcsDestination(
|
|
1707
|
+
output_uri_prefix=output_gcs_uri,
|
|
1708
|
+
),
|
|
1709
|
+
),
|
|
1710
|
+
)
|
|
1711
|
+
if not config.wait_for_completion:
|
|
1712
|
+
return operation
|
|
1713
|
+
|
|
1714
|
+
operation = _operations_utils.await_operation(
|
|
1715
|
+
operation_name=operation.name,
|
|
1716
|
+
get_operation_fn=self.get_export_publisher_model_operation,
|
|
1717
|
+
poll_interval=(
|
|
1718
|
+
config.poll_interval_seconds
|
|
1719
|
+
or ModelGarden._DEFAULT_EXPORT_POLL_INTERVAL_SECONDS
|
|
1720
|
+
),
|
|
1721
|
+
timeout_seconds=(
|
|
1722
|
+
config.timeout_seconds or ModelGarden._DEFAULT_EXPORT_TIMEOUT_SECONDS
|
|
1723
|
+
),
|
|
1724
|
+
)
|
|
1725
|
+
if operation.error:
|
|
1726
|
+
raise RuntimeError(f"Export failed: {operation.error}")
|
|
1727
|
+
if not operation.response or not operation.response.destination_uri:
|
|
1728
|
+
raise RuntimeError(
|
|
1729
|
+
f"Export completed but response has no destination_uri: "
|
|
1730
|
+
f"{operation!r}"
|
|
1731
|
+
)
|
|
1732
|
+
return operation.response.destination_uri
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
class AsyncModelGarden(_api_module.BaseModule):
|
|
1736
|
+
"""Model Garden module."""
|
|
1737
|
+
|
|
1738
|
+
async def _list_publisher_models(
|
|
1739
|
+
self,
|
|
1740
|
+
*,
|
|
1741
|
+
parent: Optional[str] = None,
|
|
1742
|
+
config: Optional[types.ListPublisherModelsConfigOrDict] = None,
|
|
1743
|
+
) -> types.ListPublisherModelsResponse:
|
|
1744
|
+
"""
|
|
1745
|
+
Lists publisher models (internal).
|
|
1746
|
+
"""
|
|
1747
|
+
|
|
1748
|
+
parameter_model = types._ListPublisherModelsRequestParameters(
|
|
1749
|
+
parent=parent,
|
|
1750
|
+
config=config,
|
|
1751
|
+
)
|
|
1752
|
+
|
|
1753
|
+
request_url_dict: Optional[dict[str, str]]
|
|
1754
|
+
if not self._api_client.vertexai:
|
|
1755
|
+
raise ValueError(
|
|
1756
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
1757
|
+
)
|
|
1758
|
+
else:
|
|
1759
|
+
request_dict = _ListPublisherModelsRequestParameters_to_vertex(
|
|
1760
|
+
parameter_model
|
|
1761
|
+
)
|
|
1762
|
+
request_url_dict = request_dict.get("_url")
|
|
1763
|
+
if request_url_dict:
|
|
1764
|
+
path = "{parent}/models".format_map(request_url_dict)
|
|
1765
|
+
else:
|
|
1766
|
+
path = "{parent}/models"
|
|
1767
|
+
|
|
1768
|
+
query_params = request_dict.get("_query")
|
|
1769
|
+
if query_params:
|
|
1770
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
1771
|
+
# TODO: remove the hack that pops config.
|
|
1772
|
+
request_dict.pop("config", None)
|
|
1773
|
+
|
|
1774
|
+
http_options: Optional[types.HttpOptions] = None
|
|
1775
|
+
if (
|
|
1776
|
+
parameter_model.config is not None
|
|
1777
|
+
and parameter_model.config.http_options is not None
|
|
1778
|
+
):
|
|
1779
|
+
http_options = parameter_model.config.http_options
|
|
1780
|
+
|
|
1781
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
1782
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
1783
|
+
|
|
1784
|
+
response = await self._api_client.async_request(
|
|
1785
|
+
"get", path, request_dict, http_options
|
|
1786
|
+
)
|
|
1787
|
+
|
|
1788
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
1789
|
+
|
|
1790
|
+
return_value = types.ListPublisherModelsResponse._from_response(
|
|
1791
|
+
response=response_dict,
|
|
1792
|
+
kwargs=(
|
|
1793
|
+
{
|
|
1794
|
+
"config": {
|
|
1795
|
+
"response_schema": getattr(
|
|
1796
|
+
parameter_model.config, "response_schema", None
|
|
1797
|
+
),
|
|
1798
|
+
"response_json_schema": getattr(
|
|
1799
|
+
parameter_model.config, "response_json_schema", None
|
|
1800
|
+
),
|
|
1801
|
+
"include_all_fields": getattr(
|
|
1802
|
+
parameter_model.config, "include_all_fields", None
|
|
1803
|
+
),
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
if getattr(parameter_model, "config", None)
|
|
1807
|
+
else {}
|
|
1808
|
+
),
|
|
1809
|
+
)
|
|
1810
|
+
|
|
1811
|
+
self._api_client._verify_response(return_value)
|
|
1812
|
+
return return_value
|
|
1813
|
+
|
|
1814
|
+
async def _get_publisher_model(
|
|
1815
|
+
self, *, name: str, config: Optional[types.GetPublisherModelConfigOrDict] = None
|
|
1816
|
+
) -> types.PublisherModel:
|
|
1817
|
+
"""
|
|
1818
|
+
Gets a publisher model (internal).
|
|
1819
|
+
"""
|
|
1820
|
+
|
|
1821
|
+
parameter_model = types._GetPublisherModelRequestParameters(
|
|
1822
|
+
name=name,
|
|
1823
|
+
config=config,
|
|
1824
|
+
)
|
|
1825
|
+
|
|
1826
|
+
request_url_dict: Optional[dict[str, str]]
|
|
1827
|
+
if not self._api_client.vertexai:
|
|
1828
|
+
raise ValueError(
|
|
1829
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
1830
|
+
)
|
|
1831
|
+
else:
|
|
1832
|
+
request_dict = _GetPublisherModelRequestParameters_to_vertex(
|
|
1833
|
+
parameter_model
|
|
1834
|
+
)
|
|
1835
|
+
request_url_dict = request_dict.get("_url")
|
|
1836
|
+
if request_url_dict:
|
|
1837
|
+
path = "{name}".format_map(request_url_dict)
|
|
1838
|
+
else:
|
|
1839
|
+
path = "{name}"
|
|
1840
|
+
|
|
1841
|
+
query_params = request_dict.get("_query")
|
|
1842
|
+
if query_params:
|
|
1843
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
1844
|
+
# TODO: remove the hack that pops config.
|
|
1845
|
+
request_dict.pop("config", None)
|
|
1846
|
+
|
|
1847
|
+
http_options: Optional[types.HttpOptions] = None
|
|
1848
|
+
if (
|
|
1849
|
+
parameter_model.config is not None
|
|
1850
|
+
and parameter_model.config.http_options is not None
|
|
1851
|
+
):
|
|
1852
|
+
http_options = parameter_model.config.http_options
|
|
1853
|
+
|
|
1854
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
1855
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
1856
|
+
|
|
1857
|
+
response = await self._api_client.async_request(
|
|
1858
|
+
"get", path, request_dict, http_options
|
|
1859
|
+
)
|
|
1860
|
+
|
|
1861
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
1862
|
+
|
|
1863
|
+
return_value = types.PublisherModel._from_response(
|
|
1864
|
+
response=response_dict,
|
|
1865
|
+
kwargs=(
|
|
1866
|
+
{
|
|
1867
|
+
"config": {
|
|
1868
|
+
"response_schema": getattr(
|
|
1869
|
+
parameter_model.config, "response_schema", None
|
|
1870
|
+
),
|
|
1871
|
+
"response_json_schema": getattr(
|
|
1872
|
+
parameter_model.config, "response_json_schema", None
|
|
1873
|
+
),
|
|
1874
|
+
"include_all_fields": getattr(
|
|
1875
|
+
parameter_model.config, "include_all_fields", None
|
|
1876
|
+
),
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
if getattr(parameter_model, "config", None)
|
|
1880
|
+
else {}
|
|
1881
|
+
),
|
|
1882
|
+
)
|
|
1883
|
+
|
|
1884
|
+
self._api_client._verify_response(return_value)
|
|
1885
|
+
return return_value
|
|
1886
|
+
|
|
1887
|
+
async def _recommend_spec(
|
|
1888
|
+
self,
|
|
1889
|
+
*,
|
|
1890
|
+
parent: str,
|
|
1891
|
+
gcs_uri: str,
|
|
1892
|
+
config: Optional[types.RecommendSpecConfigOrDict] = None,
|
|
1893
|
+
) -> types.RecommendSpecResponse:
|
|
1894
|
+
"""
|
|
1895
|
+
Recommends spec for a custom model (internal).
|
|
1896
|
+
"""
|
|
1897
|
+
|
|
1898
|
+
parameter_model = types._RecommendSpecRequestParameters(
|
|
1899
|
+
parent=parent,
|
|
1900
|
+
gcs_uri=gcs_uri,
|
|
1901
|
+
config=config,
|
|
1902
|
+
)
|
|
1903
|
+
|
|
1904
|
+
request_url_dict: Optional[dict[str, str]]
|
|
1905
|
+
if not self._api_client.vertexai:
|
|
1906
|
+
raise ValueError(
|
|
1907
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
1908
|
+
)
|
|
1909
|
+
else:
|
|
1910
|
+
request_dict = _RecommendSpecRequestParameters_to_vertex(parameter_model)
|
|
1911
|
+
request_url_dict = request_dict.get("_url")
|
|
1912
|
+
if request_url_dict:
|
|
1913
|
+
path = "{parent}:recommendSpec".format_map(request_url_dict)
|
|
1914
|
+
else:
|
|
1915
|
+
path = "{parent}:recommendSpec"
|
|
1916
|
+
|
|
1917
|
+
query_params = request_dict.get("_query")
|
|
1918
|
+
if query_params:
|
|
1919
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
1920
|
+
# TODO: remove the hack that pops config.
|
|
1921
|
+
request_dict.pop("config", None)
|
|
1922
|
+
|
|
1923
|
+
http_options: Optional[types.HttpOptions] = None
|
|
1924
|
+
if (
|
|
1925
|
+
parameter_model.config is not None
|
|
1926
|
+
and parameter_model.config.http_options is not None
|
|
1927
|
+
):
|
|
1928
|
+
http_options = parameter_model.config.http_options
|
|
1929
|
+
|
|
1930
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
1931
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
1932
|
+
|
|
1933
|
+
response = await self._api_client.async_request(
|
|
1934
|
+
"post", path, request_dict, http_options
|
|
1935
|
+
)
|
|
1936
|
+
|
|
1937
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
1938
|
+
|
|
1939
|
+
return_value = types.RecommendSpecResponse._from_response(
|
|
1940
|
+
response=response_dict,
|
|
1941
|
+
kwargs=(
|
|
1942
|
+
{
|
|
1943
|
+
"config": {
|
|
1944
|
+
"response_schema": getattr(
|
|
1945
|
+
parameter_model.config, "response_schema", None
|
|
1946
|
+
),
|
|
1947
|
+
"response_json_schema": getattr(
|
|
1948
|
+
parameter_model.config, "response_json_schema", None
|
|
1949
|
+
),
|
|
1950
|
+
"include_all_fields": getattr(
|
|
1951
|
+
parameter_model.config, "include_all_fields", None
|
|
1952
|
+
),
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
if getattr(parameter_model, "config", None)
|
|
1956
|
+
else {}
|
|
1957
|
+
),
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1960
|
+
self._api_client._verify_response(return_value)
|
|
1961
|
+
return return_value
|
|
1962
|
+
|
|
1963
|
+
async def _export_publisher_model(
|
|
1964
|
+
self,
|
|
1965
|
+
*,
|
|
1966
|
+
parent: str,
|
|
1967
|
+
name: str,
|
|
1968
|
+
config: Optional[types.ExportPublisherModelConfigOrDict] = None,
|
|
1969
|
+
) -> types.ExportModelOperation:
|
|
1970
|
+
"""
|
|
1971
|
+
Exports a publisher model (internal).
|
|
1972
|
+
"""
|
|
1973
|
+
|
|
1974
|
+
parameter_model = types._ExportPublisherModelRequestParameters(
|
|
1975
|
+
parent=parent,
|
|
1976
|
+
name=name,
|
|
1977
|
+
config=config,
|
|
1978
|
+
)
|
|
1979
|
+
|
|
1980
|
+
request_url_dict: Optional[dict[str, str]]
|
|
1981
|
+
if not self._api_client.vertexai:
|
|
1982
|
+
raise ValueError(
|
|
1983
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
1984
|
+
)
|
|
1985
|
+
else:
|
|
1986
|
+
request_dict = _ExportPublisherModelRequestParameters_to_vertex(
|
|
1987
|
+
parameter_model
|
|
1988
|
+
)
|
|
1989
|
+
request_url_dict = request_dict.get("_url")
|
|
1990
|
+
if request_url_dict:
|
|
1991
|
+
path = "{parent}/{name}:export".format_map(request_url_dict)
|
|
1992
|
+
else:
|
|
1993
|
+
path = "{parent}/{name}:export"
|
|
1994
|
+
|
|
1995
|
+
query_params = request_dict.get("_query")
|
|
1996
|
+
if query_params:
|
|
1997
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
1998
|
+
# TODO: remove the hack that pops config.
|
|
1999
|
+
request_dict.pop("config", None)
|
|
2000
|
+
|
|
2001
|
+
http_options: Optional[types.HttpOptions] = None
|
|
2002
|
+
if (
|
|
2003
|
+
parameter_model.config is not None
|
|
2004
|
+
and parameter_model.config.http_options is not None
|
|
2005
|
+
):
|
|
2006
|
+
http_options = parameter_model.config.http_options
|
|
2007
|
+
|
|
2008
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
2009
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
2010
|
+
|
|
2011
|
+
response = await self._api_client.async_request(
|
|
2012
|
+
"post", path, request_dict, http_options
|
|
2013
|
+
)
|
|
2014
|
+
|
|
2015
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
2016
|
+
|
|
2017
|
+
return_value = types.ExportModelOperation._from_response(
|
|
2018
|
+
response=response_dict,
|
|
2019
|
+
kwargs=(
|
|
2020
|
+
{
|
|
2021
|
+
"config": {
|
|
2022
|
+
"response_schema": getattr(
|
|
2023
|
+
parameter_model.config, "response_schema", None
|
|
2024
|
+
),
|
|
2025
|
+
"response_json_schema": getattr(
|
|
2026
|
+
parameter_model.config, "response_json_schema", None
|
|
2027
|
+
),
|
|
2028
|
+
"include_all_fields": getattr(
|
|
2029
|
+
parameter_model.config, "include_all_fields", None
|
|
2030
|
+
),
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if getattr(parameter_model, "config", None)
|
|
2034
|
+
else {}
|
|
2035
|
+
),
|
|
2036
|
+
)
|
|
2037
|
+
|
|
2038
|
+
self._api_client._verify_response(return_value)
|
|
2039
|
+
return return_value
|
|
2040
|
+
|
|
2041
|
+
async def get_export_publisher_model_operation(
|
|
2042
|
+
self,
|
|
2043
|
+
*,
|
|
2044
|
+
operation_name: str,
|
|
2045
|
+
config: Optional[types.GetExportPublisherModelOperationConfigOrDict] = None,
|
|
2046
|
+
) -> types.ExportModelOperation:
|
|
2047
|
+
"""
|
|
2048
|
+
Fetches the status of an in-flight export_open_model LRO.
|
|
2049
|
+
"""
|
|
2050
|
+
|
|
2051
|
+
parameter_model = types._GetExportPublisherModelOperationParameters(
|
|
2052
|
+
operation_name=operation_name,
|
|
2053
|
+
config=config,
|
|
2054
|
+
)
|
|
2055
|
+
|
|
2056
|
+
request_url_dict: Optional[dict[str, str]]
|
|
2057
|
+
if not self._api_client.vertexai:
|
|
2058
|
+
raise ValueError(
|
|
2059
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
2060
|
+
)
|
|
2061
|
+
else:
|
|
2062
|
+
request_dict = _GetExportPublisherModelOperationParameters_to_vertex(
|
|
2063
|
+
parameter_model
|
|
2064
|
+
)
|
|
2065
|
+
request_url_dict = request_dict.get("_url")
|
|
2066
|
+
if request_url_dict:
|
|
2067
|
+
path = "{operationName}".format_map(request_url_dict)
|
|
2068
|
+
else:
|
|
2069
|
+
path = "{operationName}"
|
|
2070
|
+
|
|
2071
|
+
query_params = request_dict.get("_query")
|
|
2072
|
+
if query_params:
|
|
2073
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
2074
|
+
# TODO: remove the hack that pops config.
|
|
2075
|
+
request_dict.pop("config", None)
|
|
2076
|
+
|
|
2077
|
+
http_options: Optional[types.HttpOptions] = None
|
|
2078
|
+
if (
|
|
2079
|
+
parameter_model.config is not None
|
|
2080
|
+
and parameter_model.config.http_options is not None
|
|
2081
|
+
):
|
|
2082
|
+
http_options = parameter_model.config.http_options
|
|
2083
|
+
|
|
2084
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
2085
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
2086
|
+
|
|
2087
|
+
response = await self._api_client.async_request(
|
|
2088
|
+
"get", path, request_dict, http_options
|
|
2089
|
+
)
|
|
2090
|
+
|
|
2091
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
2092
|
+
|
|
2093
|
+
return_value = types.ExportModelOperation._from_response(
|
|
2094
|
+
response=response_dict,
|
|
2095
|
+
kwargs=(
|
|
2096
|
+
{
|
|
2097
|
+
"config": {
|
|
2098
|
+
"response_schema": getattr(
|
|
2099
|
+
parameter_model.config, "response_schema", None
|
|
2100
|
+
),
|
|
2101
|
+
"response_json_schema": getattr(
|
|
2102
|
+
parameter_model.config, "response_json_schema", None
|
|
2103
|
+
),
|
|
2104
|
+
"include_all_fields": getattr(
|
|
2105
|
+
parameter_model.config, "include_all_fields", None
|
|
2106
|
+
),
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
if getattr(parameter_model, "config", None)
|
|
2110
|
+
else {}
|
|
2111
|
+
),
|
|
2112
|
+
)
|
|
2113
|
+
|
|
2114
|
+
self._api_client._verify_response(return_value)
|
|
2115
|
+
return return_value
|
|
2116
|
+
|
|
2117
|
+
async def _deploy(
|
|
2118
|
+
self,
|
|
2119
|
+
*,
|
|
2120
|
+
destination: str,
|
|
2121
|
+
publisher_model_name: Optional[str] = None,
|
|
2122
|
+
hugging_face_model_id: Optional[str] = None,
|
|
2123
|
+
custom_model: Optional[types.DeployRequestCustomModelOrDict] = None,
|
|
2124
|
+
model_config_val: Optional[types.DeployRequestModelConfigOrDict] = None,
|
|
2125
|
+
endpoint_config: Optional[types.DeployRequestEndpointConfigOrDict] = None,
|
|
2126
|
+
deploy_config: Optional[types.DeployRequestDeployConfigOrDict] = None,
|
|
2127
|
+
config: Optional[types.DeployConfigOrDict] = None,
|
|
2128
|
+
) -> types.DeployModelOperation:
|
|
2129
|
+
"""
|
|
2130
|
+
Deploys a model (internal).
|
|
2131
|
+
"""
|
|
2132
|
+
|
|
2133
|
+
parameter_model = types._DeployRequestParameters(
|
|
2134
|
+
destination=destination,
|
|
2135
|
+
publisher_model_name=publisher_model_name,
|
|
2136
|
+
hugging_face_model_id=hugging_face_model_id,
|
|
2137
|
+
custom_model=custom_model,
|
|
2138
|
+
model_config_val=model_config_val,
|
|
2139
|
+
endpoint_config=endpoint_config,
|
|
2140
|
+
deploy_config=deploy_config,
|
|
2141
|
+
config=config,
|
|
2142
|
+
)
|
|
2143
|
+
|
|
2144
|
+
request_url_dict: Optional[dict[str, str]]
|
|
2145
|
+
if not self._api_client.vertexai:
|
|
2146
|
+
raise ValueError(
|
|
2147
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
2148
|
+
)
|
|
2149
|
+
else:
|
|
2150
|
+
request_dict = _DeployRequestParameters_to_vertex(parameter_model)
|
|
2151
|
+
request_url_dict = request_dict.get("_url")
|
|
2152
|
+
if request_url_dict:
|
|
2153
|
+
path = "{destination}:deploy".format_map(request_url_dict)
|
|
2154
|
+
else:
|
|
2155
|
+
path = "{destination}:deploy"
|
|
2156
|
+
|
|
2157
|
+
query_params = request_dict.get("_query")
|
|
2158
|
+
if query_params:
|
|
2159
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
2160
|
+
# TODO: remove the hack that pops config.
|
|
2161
|
+
request_dict.pop("config", None)
|
|
2162
|
+
|
|
2163
|
+
http_options: Optional[types.HttpOptions] = None
|
|
2164
|
+
if (
|
|
2165
|
+
parameter_model.config is not None
|
|
2166
|
+
and parameter_model.config.http_options is not None
|
|
2167
|
+
):
|
|
2168
|
+
http_options = parameter_model.config.http_options
|
|
2169
|
+
|
|
2170
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
2171
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
2172
|
+
|
|
2173
|
+
response = await self._api_client.async_request(
|
|
2174
|
+
"post", path, request_dict, http_options
|
|
2175
|
+
)
|
|
2176
|
+
|
|
2177
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
2178
|
+
|
|
2179
|
+
return_value = types.DeployModelOperation._from_response(
|
|
2180
|
+
response=response_dict,
|
|
2181
|
+
kwargs=(
|
|
2182
|
+
{
|
|
2183
|
+
"config": {
|
|
2184
|
+
"response_schema": getattr(
|
|
2185
|
+
parameter_model.config, "response_schema", None
|
|
2186
|
+
),
|
|
2187
|
+
"response_json_schema": getattr(
|
|
2188
|
+
parameter_model.config, "response_json_schema", None
|
|
2189
|
+
),
|
|
2190
|
+
"include_all_fields": getattr(
|
|
2191
|
+
parameter_model.config, "include_all_fields", None
|
|
2192
|
+
),
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
if getattr(parameter_model, "config", None)
|
|
2196
|
+
else {}
|
|
2197
|
+
),
|
|
2198
|
+
)
|
|
2199
|
+
|
|
2200
|
+
self._api_client._verify_response(return_value)
|
|
2201
|
+
return return_value
|
|
2202
|
+
|
|
2203
|
+
async def get_deploy_publisher_model_operation(
|
|
2204
|
+
self,
|
|
2205
|
+
*,
|
|
2206
|
+
operation_name: str,
|
|
2207
|
+
config: Optional[types.GetDeployOperationConfigOrDict] = None,
|
|
2208
|
+
) -> types.DeployModelOperation:
|
|
2209
|
+
"""
|
|
2210
|
+
Fetches the status of an in-flight ``deploy_publisher_model`` LRO.
|
|
2211
|
+
"""
|
|
2212
|
+
|
|
2213
|
+
parameter_model = types._GetDeployOperationParameters(
|
|
2214
|
+
operation_name=operation_name,
|
|
2215
|
+
config=config,
|
|
2216
|
+
)
|
|
2217
|
+
|
|
2218
|
+
request_url_dict: Optional[dict[str, str]]
|
|
2219
|
+
if not self._api_client.vertexai:
|
|
2220
|
+
raise ValueError(
|
|
2221
|
+
"This method is only supported in Gemini Enterprise Agent Platform mode, not in Gemini Developer API mode."
|
|
2222
|
+
)
|
|
2223
|
+
else:
|
|
2224
|
+
request_dict = _GetDeployOperationParameters_to_vertex(parameter_model)
|
|
2225
|
+
request_url_dict = request_dict.get("_url")
|
|
2226
|
+
if request_url_dict:
|
|
2227
|
+
path = "{operationName}".format_map(request_url_dict)
|
|
2228
|
+
else:
|
|
2229
|
+
path = "{operationName}"
|
|
2230
|
+
|
|
2231
|
+
query_params = request_dict.get("_query")
|
|
2232
|
+
if query_params:
|
|
2233
|
+
path = f"{path}?{urlencode(query_params)}"
|
|
2234
|
+
# TODO: remove the hack that pops config.
|
|
2235
|
+
request_dict.pop("config", None)
|
|
2236
|
+
|
|
2237
|
+
http_options: Optional[types.HttpOptions] = None
|
|
2238
|
+
if (
|
|
2239
|
+
parameter_model.config is not None
|
|
2240
|
+
and parameter_model.config.http_options is not None
|
|
2241
|
+
):
|
|
2242
|
+
http_options = parameter_model.config.http_options
|
|
2243
|
+
|
|
2244
|
+
request_dict = _common.convert_to_dict(request_dict)
|
|
2245
|
+
request_dict = _common.encode_unserializable_types(request_dict)
|
|
2246
|
+
|
|
2247
|
+
response = await self._api_client.async_request(
|
|
2248
|
+
"get", path, request_dict, http_options
|
|
2249
|
+
)
|
|
2250
|
+
|
|
2251
|
+
response_dict = {} if not response.body else json.loads(response.body)
|
|
2252
|
+
|
|
2253
|
+
return_value = types.DeployModelOperation._from_response(
|
|
2254
|
+
response=response_dict,
|
|
2255
|
+
kwargs=(
|
|
2256
|
+
{
|
|
2257
|
+
"config": {
|
|
2258
|
+
"response_schema": getattr(
|
|
2259
|
+
parameter_model.config, "response_schema", None
|
|
2260
|
+
),
|
|
2261
|
+
"response_json_schema": getattr(
|
|
2262
|
+
parameter_model.config, "response_json_schema", None
|
|
2263
|
+
),
|
|
2264
|
+
"include_all_fields": getattr(
|
|
2265
|
+
parameter_model.config, "include_all_fields", None
|
|
2266
|
+
),
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
if getattr(parameter_model, "config", None)
|
|
2270
|
+
else {}
|
|
2271
|
+
),
|
|
2272
|
+
)
|
|
2273
|
+
|
|
2274
|
+
self._api_client._verify_response(return_value)
|
|
2275
|
+
return return_value
|
|
2276
|
+
|
|
2277
|
+
async def _list_all_publisher_models(
|
|
2278
|
+
self,
|
|
2279
|
+
api_config: types.ListPublisherModelsConfig,
|
|
2280
|
+
) -> list[types.PublisherModel]:
|
|
2281
|
+
"""Fetches all pages of publisher models from the API.
|
|
2282
|
+
|
|
2283
|
+
Args:
|
|
2284
|
+
api_config: The configuration for the ListPublisherModels API call,
|
|
2285
|
+
including filter and version settings.
|
|
2286
|
+
|
|
2287
|
+
Returns:
|
|
2288
|
+
A list of all ``PublisherModel`` objects across all pages.
|
|
2289
|
+
"""
|
|
2290
|
+
all_models = []
|
|
2291
|
+
page_token = None
|
|
2292
|
+
while True:
|
|
2293
|
+
if page_token:
|
|
2294
|
+
api_config = types.ListPublisherModelsConfig(
|
|
2295
|
+
filter=api_config.filter,
|
|
2296
|
+
list_all_versions=api_config.list_all_versions,
|
|
2297
|
+
page_token=page_token,
|
|
2298
|
+
)
|
|
2299
|
+
response = await self._list_publisher_models(
|
|
2300
|
+
parent="publishers/*",
|
|
2301
|
+
config=api_config,
|
|
2302
|
+
)
|
|
2303
|
+
all_models.extend(response.publisher_models or [])
|
|
2304
|
+
page_token = response.next_page_token
|
|
2305
|
+
if not page_token:
|
|
2306
|
+
break
|
|
2307
|
+
return all_models
|
|
2308
|
+
|
|
2309
|
+
async def _list(
|
|
2310
|
+
self,
|
|
2311
|
+
model_filter: Optional[str],
|
|
2312
|
+
include_hugging_face_models: Optional[bool],
|
|
2313
|
+
deployable_only: bool,
|
|
2314
|
+
) -> list[str]:
|
|
2315
|
+
"""Shared implementation for listing models.
|
|
2316
|
+
|
|
2317
|
+
Args:
|
|
2318
|
+
model_filter: Optional substring to filter models by.
|
|
2319
|
+
include_hugging_face_models: Whether to include HuggingFace models.
|
|
2320
|
+
deployable_only: If True, only return models with deployment configs.
|
|
2321
|
+
|
|
2322
|
+
Returns:
|
|
2323
|
+
A list of formatted model name strings.
|
|
2324
|
+
"""
|
|
2325
|
+
include_hf = include_hugging_face_models is True
|
|
2326
|
+
|
|
2327
|
+
filter_str = ModelGarden._build_filter_str(
|
|
2328
|
+
model_filter, include_hf, deployable_only=deployable_only
|
|
2329
|
+
)
|
|
2330
|
+
|
|
2331
|
+
api_config = types.ListPublisherModelsConfig(
|
|
2332
|
+
filter=filter_str,
|
|
2333
|
+
list_all_versions=True,
|
|
2334
|
+
)
|
|
2335
|
+
|
|
2336
|
+
models = await self._list_all_publisher_models(api_config)
|
|
2337
|
+
|
|
2338
|
+
if deployable_only:
|
|
2339
|
+
models = [m for m in models if ModelGarden._has_deploy_config(m)]
|
|
2340
|
+
|
|
2341
|
+
return [ModelGarden._format_model_name(m, include_hf) for m in models]
|
|
2342
|
+
|
|
2343
|
+
async def list_deployable_models(
|
|
2344
|
+
self,
|
|
2345
|
+
config: Optional[types.ListDeployableModelsConfigOrDict] = None,
|
|
2346
|
+
) -> list[str]:
|
|
2347
|
+
"""Lists models in Model Garden that support deployment.
|
|
2348
|
+
|
|
2349
|
+
Returns models that have at least one verified deployment configuration.
|
|
2350
|
+
When ``include_hugging_face_models`` is False (the default),
|
|
2351
|
+
HuggingFace models are excluded from the results.
|
|
2352
|
+
|
|
2353
|
+
Args:
|
|
2354
|
+
config: Optional configuration for filtering results. Accepts a
|
|
2355
|
+
``ListDeployableModelsConfig`` instance or an equivalent dict.
|
|
2356
|
+
|
|
2357
|
+
Returns:
|
|
2358
|
+
A list of model name strings in the format
|
|
2359
|
+
``'{publisher}/{model}@{version}'`` (e.g. ``'google/gemma2@gemma-2-2b-it'``)
|
|
2360
|
+
or ``'{publisher}/{model}'`` when ``include_hugging_face_models`` is True
|
|
2361
|
+
(e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
2362
|
+
"""
|
|
2363
|
+
if config is None:
|
|
2364
|
+
config = types.ListDeployableModelsConfig()
|
|
2365
|
+
if isinstance(config, dict):
|
|
2366
|
+
config = types.ListDeployableModelsConfig.model_validate(config)
|
|
2367
|
+
|
|
2368
|
+
return await self._list(
|
|
2369
|
+
config.model_filter,
|
|
2370
|
+
config.include_hugging_face_models,
|
|
2371
|
+
deployable_only=True,
|
|
2372
|
+
)
|
|
2373
|
+
|
|
2374
|
+
async def list_models(
|
|
2375
|
+
self,
|
|
2376
|
+
config: Optional[types.ListModelGardenModelsConfigOrDict] = None,
|
|
2377
|
+
) -> list[str]:
|
|
2378
|
+
"""Lists all models available in Model Garden.
|
|
2379
|
+
|
|
2380
|
+
Returns all models regardless of deployment support. When
|
|
2381
|
+
``include_hugging_face_models`` is False (the default), HuggingFace
|
|
2382
|
+
models are excluded from the results.
|
|
2383
|
+
|
|
2384
|
+
Args:
|
|
2385
|
+
config: Optional configuration for filtering results. Accepts a
|
|
2386
|
+
``ListModelGardenModelsConfig`` instance or an equivalent dict.
|
|
2387
|
+
|
|
2388
|
+
Returns:
|
|
2389
|
+
A list of model name strings in the format
|
|
2390
|
+
``'{publisher}/{model}@{version}'`` (e.g. ``'google/gemma2@gemma-2-2b-it'``)
|
|
2391
|
+
or ``'{publisher}/{model}'`` when ``include_hugging_face_models`` is True
|
|
2392
|
+
(e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
2393
|
+
"""
|
|
2394
|
+
if config is None:
|
|
2395
|
+
config = types.ListModelGardenModelsConfig()
|
|
2396
|
+
if isinstance(config, dict):
|
|
2397
|
+
config = types.ListModelGardenModelsConfig.model_validate(config)
|
|
2398
|
+
|
|
2399
|
+
return await self._list(
|
|
2400
|
+
config.model_filter,
|
|
2401
|
+
config.include_hugging_face_models,
|
|
2402
|
+
deployable_only=False,
|
|
2403
|
+
)
|
|
2404
|
+
|
|
2405
|
+
async def list_publisher_model_deploy_options(
|
|
2406
|
+
self,
|
|
2407
|
+
model: str,
|
|
2408
|
+
config: Optional[types.ListPublisherModelDeployOptionsConfigOrDict] = None,
|
|
2409
|
+
) -> Union[str, list[types.DeployOption]]:
|
|
2410
|
+
"""Lists the verified deploy options for a Model Garden publisher model.
|
|
2411
|
+
|
|
2412
|
+
Supports Google open models (e.g. ``'google/gemma3@gemma-3-12b-it'``),
|
|
2413
|
+
partner publisher models (e.g.
|
|
2414
|
+
``'mistralai/mistral-7b@mistral-7b-instruct-v0.2'``), and Hugging Face
|
|
2415
|
+
model IDs (e.g. ``'meta-llama/Llama-3.3-70B-Instruct'``).
|
|
2416
|
+
|
|
2417
|
+
Args:
|
|
2418
|
+
model: The publisher model to list deploy options for. Accepts the full
|
|
2419
|
+
resource name ``'publishers/{publisher}/models/{model}@{version}'``, a
|
|
2420
|
+
simplified ``'{publisher}/{model}@{version}'`` (or without the
|
|
2421
|
+
``@{version}``), or a Hugging Face model ID ``'{organization}/{model}'``.
|
|
2422
|
+
config: Optional configuration for filtering the deploy options. Accepts a
|
|
2423
|
+
``ListPublisherModelDeployOptionsConfig`` instance or an equivalent
|
|
2424
|
+
dict.
|
|
2425
|
+
|
|
2426
|
+
Returns:
|
|
2427
|
+
A list of ``DeployOption`` objects, one per verified deployment
|
|
2428
|
+
configuration (container image URI, machine type, accelerator type and
|
|
2429
|
+
count). If ``config.concise`` is True, returns a human-readable string
|
|
2430
|
+
describing those deploy options instead.
|
|
2431
|
+
|
|
2432
|
+
Raises:
|
|
2433
|
+
ValueError: If ``model`` is not a valid publisher model name, if the
|
|
2434
|
+
model does not support deployment, or if no deploy options match the
|
|
2435
|
+
provided filters.
|
|
2436
|
+
"""
|
|
2437
|
+
if config is None:
|
|
2438
|
+
config = types.ListPublisherModelDeployOptionsConfig()
|
|
2439
|
+
if isinstance(config, dict):
|
|
2440
|
+
config = types.ListPublisherModelDeployOptionsConfig.model_validate(config)
|
|
2441
|
+
|
|
2442
|
+
api_config = types.GetPublisherModelConfig(
|
|
2443
|
+
is_hugging_face_model=ModelGarden._is_hugging_face_model(model),
|
|
2444
|
+
include_equivalent_model_garden_model_deployment_configs=True,
|
|
2445
|
+
)
|
|
2446
|
+
publisher_model = await self._get_publisher_model(
|
|
2447
|
+
name=ModelGarden._reconcile_model_name(model), config=api_config
|
|
2448
|
+
)
|
|
2449
|
+
|
|
2450
|
+
options = ModelGarden._extract_and_filter_deploy_options(
|
|
2451
|
+
publisher_model,
|
|
2452
|
+
machine_type_filter=config.machine_type_filter,
|
|
2453
|
+
accelerator_type_filter=config.accelerator_type_filter,
|
|
2454
|
+
serving_container_image_uri_filter=config.serving_container_image_uri_filter,
|
|
2455
|
+
)
|
|
2456
|
+
|
|
2457
|
+
if config.concise is True:
|
|
2458
|
+
return ModelGarden._format_concise_deploy_options(options)
|
|
2459
|
+
|
|
2460
|
+
return options
|
|
2461
|
+
|
|
2462
|
+
async def deploy_publisher_model(
|
|
2463
|
+
self,
|
|
2464
|
+
*,
|
|
2465
|
+
model: str,
|
|
2466
|
+
config: Optional[types.DeployPublisherModelConfigOrDict] = None,
|
|
2467
|
+
) -> Union[types.DeployResponse, types.DeployModelOperation]:
|
|
2468
|
+
"""Async variant of ``ModelGarden.deploy_publisher_model``."""
|
|
2469
|
+
if config is None:
|
|
2470
|
+
config = types.DeployPublisherModelConfig()
|
|
2471
|
+
elif isinstance(config, dict):
|
|
2472
|
+
config = types.DeployPublisherModelConfig.model_validate(config)
|
|
2473
|
+
ModelGarden._validate_deploy_config(config)
|
|
2474
|
+
|
|
2475
|
+
publisher_model_name, hugging_face_model_id = (
|
|
2476
|
+
ModelGarden._resolve_deploy_model_name(model)
|
|
2477
|
+
)
|
|
2478
|
+
model_config, endpoint_config, deploy_config = (
|
|
2479
|
+
ModelGarden._prepare_deploy_request(config)
|
|
2480
|
+
)
|
|
2481
|
+
destination = (
|
|
2482
|
+
f"projects/{self._api_client.project}/locations/"
|
|
2483
|
+
f"{self._api_client.location}"
|
|
2484
|
+
)
|
|
2485
|
+
|
|
2486
|
+
operation = await self._deploy(
|
|
2487
|
+
destination=destination,
|
|
2488
|
+
publisher_model_name=publisher_model_name,
|
|
2489
|
+
hugging_face_model_id=hugging_face_model_id,
|
|
2490
|
+
model_config_val=model_config,
|
|
2491
|
+
endpoint_config=endpoint_config,
|
|
2492
|
+
deploy_config=deploy_config,
|
|
2493
|
+
)
|
|
2494
|
+
if not config.wait_for_completion:
|
|
2495
|
+
return operation
|
|
2496
|
+
|
|
2497
|
+
operation = await _operations_utils.await_operation_async(
|
|
2498
|
+
operation_name=operation.name,
|
|
2499
|
+
get_operation_fn=self.get_deploy_publisher_model_operation,
|
|
2500
|
+
poll_interval=(
|
|
2501
|
+
config.poll_interval_seconds
|
|
2502
|
+
or ModelGarden._DEFAULT_DEPLOY_POLL_INTERVAL_SECONDS
|
|
2503
|
+
),
|
|
2504
|
+
timeout_seconds=(
|
|
2505
|
+
config.timeout_seconds or ModelGarden._DEFAULT_DEPLOY_TIMEOUT_SECONDS
|
|
2506
|
+
),
|
|
2507
|
+
)
|
|
2508
|
+
return ModelGarden._deploy_response_or_raise(operation)
|
|
2509
|
+
|
|
2510
|
+
async def list_custom_model_deploy_options(
|
|
2511
|
+
self,
|
|
2512
|
+
src: str,
|
|
2513
|
+
config: Optional[types.ListCustomModelDeployOptionsConfigOrDict] = None,
|
|
2514
|
+
) -> str:
|
|
2515
|
+
"""Lists the recommended deploy options for a Model Garden custom model.
|
|
2516
|
+
|
|
2517
|
+
Args:
|
|
2518
|
+
src: The Google Cloud Storage URI of the custom model, storing the model
|
|
2519
|
+
weights and config files (e.g. ``'gs://my-bucket/weights/'``).
|
|
2520
|
+
config: Optional configuration. Accepts a
|
|
2521
|
+
``ListCustomModelDeployOptionsConfig`` instance or an equivalent dict.
|
|
2522
|
+
|
|
2523
|
+
Returns:
|
|
2524
|
+
A human-readable string describing the recommended deploy options
|
|
2525
|
+
(machine type, accelerator type/count, region and, when available, the
|
|
2526
|
+
user quota state).
|
|
2527
|
+
|
|
2528
|
+
Raises:
|
|
2529
|
+
ValueError: If ``src`` is not specified, or if no deploy options are
|
|
2530
|
+
returned by the API (either because the backend produced none or
|
|
2531
|
+
because the ``filter_by_user_quota`` filter dropped them all).
|
|
2532
|
+
"""
|
|
2533
|
+
if not src:
|
|
2534
|
+
raise ValueError("src must be specified.")
|
|
2535
|
+
if config is None:
|
|
2536
|
+
config = types.ListCustomModelDeployOptionsConfig()
|
|
2537
|
+
if isinstance(config, dict):
|
|
2538
|
+
config = types.ListCustomModelDeployOptionsConfig.model_validate(config)
|
|
2539
|
+
|
|
2540
|
+
parent = (
|
|
2541
|
+
f"projects/{self._api_client.project}/locations/"
|
|
2542
|
+
f"{self._api_client.location}"
|
|
2543
|
+
)
|
|
2544
|
+
|
|
2545
|
+
api_config = types.RecommendSpecConfig(
|
|
2546
|
+
check_machine_availability=config.check_machine_availability,
|
|
2547
|
+
check_user_quota=config.filter_by_user_quota,
|
|
2548
|
+
)
|
|
2549
|
+
|
|
2550
|
+
response = await self._recommend_spec(
|
|
2551
|
+
parent=parent,
|
|
2552
|
+
gcs_uri=src,
|
|
2553
|
+
config=api_config,
|
|
2554
|
+
)
|
|
2555
|
+
|
|
2556
|
+
options = []
|
|
2557
|
+
if response.recommendations:
|
|
2558
|
+
options = [
|
|
2559
|
+
ModelGarden._extract_recommendation(recommendation)
|
|
2560
|
+
for recommendation in response.recommendations
|
|
2561
|
+
if recommendation.spec
|
|
2562
|
+
]
|
|
2563
|
+
if config.filter_by_user_quota:
|
|
2564
|
+
options = [
|
|
2565
|
+
option
|
|
2566
|
+
for option in options
|
|
2567
|
+
if option.get("user_quota_state")
|
|
2568
|
+
== types.QuotaState.QUOTA_STATE_USER_HAS_QUOTA.name
|
|
2569
|
+
]
|
|
2570
|
+
elif response.specs:
|
|
2571
|
+
options = [
|
|
2572
|
+
ModelGarden._extract_recommend_spec(spec)
|
|
2573
|
+
for spec in response.specs
|
|
2574
|
+
if spec
|
|
2575
|
+
]
|
|
2576
|
+
|
|
2577
|
+
if not options:
|
|
2578
|
+
raise ValueError("No deploy options found.")
|
|
2579
|
+
|
|
2580
|
+
return ModelGarden._format_custom_deploy_options(options)
|
|
2581
|
+
|
|
2582
|
+
async def export_open_model(
|
|
2583
|
+
self,
|
|
2584
|
+
*,
|
|
2585
|
+
model: str,
|
|
2586
|
+
output_gcs_uri: str,
|
|
2587
|
+
config: Optional[types.ExportOpenModelConfigOrDict] = None,
|
|
2588
|
+
) -> Union[str, types.ExportModelOperation]:
|
|
2589
|
+
"""Async variant of ``ModelGarden.export_open_model``."""
|
|
2590
|
+
if not output_gcs_uri:
|
|
2591
|
+
raise ValueError("output_gcs_uri must be a non-empty Cloud Storage URI.")
|
|
2592
|
+
if ModelGarden._is_hugging_face_model(model):
|
|
2593
|
+
raise ValueError(
|
|
2594
|
+
f"export_open_model does not support Hugging Face model IDs "
|
|
2595
|
+
f"(got {model!r}); only Google publisher open models are "
|
|
2596
|
+
f"exportable via this API."
|
|
2597
|
+
)
|
|
2598
|
+
if config is None:
|
|
2599
|
+
config = types.ExportOpenModelConfig()
|
|
2600
|
+
elif isinstance(config, dict):
|
|
2601
|
+
config = types.ExportOpenModelConfig.model_validate(config)
|
|
2602
|
+
|
|
2603
|
+
publisher_model_name = ModelGarden._reconcile_model_name(model)
|
|
2604
|
+
parent = (
|
|
2605
|
+
f"projects/{self._api_client.project}/locations/"
|
|
2606
|
+
f"{self._api_client.location}"
|
|
2607
|
+
)
|
|
2608
|
+
operation = await self._export_publisher_model(
|
|
2609
|
+
parent=parent,
|
|
2610
|
+
name=publisher_model_name,
|
|
2611
|
+
config=types.ExportPublisherModelConfig(
|
|
2612
|
+
destination=genai_types.GcsDestination(
|
|
2613
|
+
output_uri_prefix=output_gcs_uri,
|
|
2614
|
+
),
|
|
2615
|
+
),
|
|
2616
|
+
)
|
|
2617
|
+
if not config.wait_for_completion:
|
|
2618
|
+
return operation
|
|
2619
|
+
|
|
2620
|
+
operation = await _operations_utils.await_operation_async(
|
|
2621
|
+
operation_name=operation.name,
|
|
2622
|
+
get_operation_fn=self.get_export_publisher_model_operation,
|
|
2623
|
+
poll_interval=(
|
|
2624
|
+
config.poll_interval_seconds
|
|
2625
|
+
or ModelGarden._DEFAULT_EXPORT_POLL_INTERVAL_SECONDS
|
|
2626
|
+
),
|
|
2627
|
+
timeout_seconds=(
|
|
2628
|
+
config.timeout_seconds or ModelGarden._DEFAULT_EXPORT_TIMEOUT_SECONDS
|
|
2629
|
+
),
|
|
2630
|
+
)
|
|
2631
|
+
if operation.error:
|
|
2632
|
+
raise RuntimeError(f"Export failed: {operation.error}")
|
|
2633
|
+
if not operation.response or not operation.response.destination_uri:
|
|
2634
|
+
raise RuntimeError(
|
|
2635
|
+
f"Export completed but response has no destination_uri: "
|
|
2636
|
+
f"{operation!r}"
|
|
2637
|
+
)
|
|
2638
|
+
return operation.response.destination_uri
|