Skip to content

Commit 0c72b97

Browse files
jsondaicopybara-github
authored andcommitted
chore: GenAI Client(evals) - normalize inference model names in create_evaluation_run
PiperOrigin-RevId: 926988958
1 parent cb12436 commit 0c72b97

7 files changed

Lines changed: 237 additions & 6 deletions

File tree

agentplatform/_genai/_evals_common.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,21 @@ def _resolve_inference_configs(
513513
else:
514514
config.agent_configs = parsed_agent_info.agents
515515

516-
# Resolve prompt template data
517516
if inference_configs:
518517
for inference_config in inference_configs.values():
518+
model_val = (
519+
inference_config.get("model")
520+
if isinstance(inference_config, dict)
521+
else inference_config.model
522+
)
523+
if model_val:
524+
normalized_model = _normalize_inference_model_name(
525+
model_val, api_client
526+
)
527+
if isinstance(inference_config, dict):
528+
inference_config["model"] = normalized_model
529+
else:
530+
inference_config.model = normalized_model
519531
prompt_template_val = (
520532
inference_config.get("prompt_template")
521533
if isinstance(inference_config, dict)
@@ -912,6 +924,62 @@ def _is_gemini_model(model: str) -> bool:
912924
)
913925

914926

927+
def _normalize_inference_model_name(model: str, api_client: BaseApiClient) -> str:
928+
"""Expands a model name to a fully-qualified resource name for inference.
929+
930+
A short or location-less model name has no serving location for the
931+
Evaluation Service to route on, so it is expanded using the client's
932+
project and location. Already fully-qualified names pass through. Raises
933+
ValueError if the client is missing a project or location, or if the model
934+
name is not a recognized Vertex form.
935+
"""
936+
if not model:
937+
return model
938+
939+
if model.startswith("projects/"):
940+
return model
941+
942+
project = getattr(api_client, "project", None)
943+
location = getattr(api_client, "location", None)
944+
prefix = f"projects/{project}/locations/{location}/"
945+
946+
def _require_project_location() -> None:
947+
if not project or not location:
948+
raise ValueError(
949+
f"Cannot expand model name '{model}' to a fully-qualified"
950+
" resource name because the client is missing a project or"
951+
" location. Set project and location on the client, or pass a"
952+
" fully-qualified"
953+
" 'projects/{project}/locations/{location}/publishers/google/models/{model}'"
954+
" resource name."
955+
)
956+
957+
if (
958+
model.startswith("publishers/")
959+
or model.startswith("endpoints/")
960+
or model.startswith("tunedModels/")
961+
):
962+
_require_project_location()
963+
return f"{prefix}{model}"
964+
965+
if model.startswith("models/"):
966+
_require_project_location()
967+
return f"{prefix}publishers/google/{model}"
968+
969+
if "/" not in model and _is_gemini_model(model):
970+
_require_project_location()
971+
return f"{prefix}publishers/google/models/{model}"
972+
973+
raise ValueError(
974+
f"Unrecognized model name '{model}'. Provide a Gemini model name (e.g."
975+
" 'gemini-2.5-flash'), or a fully-qualified publisher-model or endpoint"
976+
" resource name (e.g."
977+
" 'projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash'"
978+
" or"
979+
" 'projects/{project}/locations/{location}/endpoints/{endpoint}')."
980+
)
981+
982+
915983
def _run_inference_internal(
916984
api_client: BaseApiClient,
917985
model: Union[Callable[[Any], Any], str],

agentplatform/_genai/evals.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,11 @@ def create_evaluation_run(
26592659
The key is the candidate name, and the value is the inference config.
26602660
If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
26612661
this will be automatically constructed using `agent_info` and `user_simulator_config`.
2662+
The `model` field of an inference config accepts a short Gemini model
2663+
name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
2664+
fully-qualified resource name using the client's project and location,
2665+
or an already fully-qualified publisher-model or endpoint resource
2666+
name.
26622667
Example:
26632668
{"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
26642669
labels: The labels to apply to the evaluation run.
@@ -4448,6 +4453,11 @@ async def create_evaluation_run(
44484453
The key is the candidate name, and the value is the inference config.
44494454
If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
44504455
this will be automatically constructed using `agent_info` and `user_simulator_config`.
4456+
The `model` field of an inference config accepts a short Gemini model
4457+
name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
4458+
fully-qualified resource name using the client's project and location,
4459+
or an already fully-qualified publisher-model or endpoint resource
4460+
name.
44514461
Example:
44524462
{"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
44534463
red_teaming_config: This field is experimental and may change in future

agentplatform/_genai/types/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2586,7 +2586,7 @@ class EvaluationRunInferenceConfig(_common.BaseModel):
25862586
)
25872587
model: Optional[str] = Field(
25882588
default=None,
2589-
description="""The fully qualified name of the publisher model or endpoint to use for inference.""",
2589+
description="""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`).""",
25902590
)
25912591
prompt_template: Optional[EvaluationRunPromptTemplate] = Field(
25922592
default=None, description="""The prompt template used for inference."""
@@ -2611,7 +2611,7 @@ class EvaluationRunInferenceConfigDict(TypedDict, total=False):
26112611
"""The agent config."""
26122612

26132613
model: Optional[str]
2614-
"""The fully qualified name of the publisher model or endpoint to use for inference."""
2614+
"""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`)."""
26152615

26162616
prompt_template: Optional[EvaluationRunPromptTemplateDict]
26172617
"""The prompt template used for inference."""

tests/unit/agentplatform/genai/test_evals.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,81 @@ def test_get_api_client_with_none_location(
228228
mock_agentplatform_client.assert_not_called()
229229

230230

231+
class TestNormalizeInferenceModelName:
232+
_FQ_PREFIX = f"projects/{_TEST_PROJECT}/locations/{_TEST_LOCATION}/"
233+
234+
def test_short_gemini_name_expanded(self, mock_api_client_fixture):
235+
result = _evals_common._normalize_inference_model_name(
236+
"gemini-2.5-flash", mock_api_client_fixture
237+
)
238+
assert result == (f"{self._FQ_PREFIX}publishers/google/models/gemini-2.5-flash")
239+
240+
def test_location_less_publisher_path_prepended(self, mock_api_client_fixture):
241+
result = _evals_common._normalize_inference_model_name(
242+
"publishers/google/models/gemini-2.5-flash", mock_api_client_fixture
243+
)
244+
assert result == (f"{self._FQ_PREFIX}publishers/google/models/gemini-2.5-flash")
245+
246+
def test_third_party_publisher_path_prepended(self, mock_api_client_fixture):
247+
result = _evals_common._normalize_inference_model_name(
248+
"publishers/anthropic/models/claude-sonnet", mock_api_client_fixture
249+
)
250+
assert result == (f"{self._FQ_PREFIX}publishers/anthropic/models/claude-sonnet")
251+
252+
def test_endpoint_path_prepended(self, mock_api_client_fixture):
253+
result = _evals_common._normalize_inference_model_name(
254+
"endpoints/123", mock_api_client_fixture
255+
)
256+
assert result == f"{self._FQ_PREFIX}endpoints/123"
257+
258+
def test_models_path_canonicalized(self, mock_api_client_fixture):
259+
result = _evals_common._normalize_inference_model_name(
260+
"models/gemini-2.5-flash", mock_api_client_fixture
261+
)
262+
assert result == (f"{self._FQ_PREFIX}publishers/google/models/gemini-2.5-flash")
263+
264+
def test_fully_qualified_name_unchanged(self, mock_api_client_fixture):
265+
fq = f"{self._FQ_PREFIX}publishers/google/models/gemini-2.5-flash"
266+
assert (
267+
_evals_common._normalize_inference_model_name(fq, mock_api_client_fixture)
268+
== fq
269+
)
270+
271+
def test_empty_model_unchanged(self, mock_api_client_fixture):
272+
assert (
273+
_evals_common._normalize_inference_model_name("", mock_api_client_fixture)
274+
== ""
275+
)
276+
277+
def test_missing_project_or_location_raises(self, mock_api_client_fixture):
278+
mock_api_client_fixture.location = None
279+
with pytest.raises(ValueError, match="missing a project or location"):
280+
_evals_common._normalize_inference_model_name(
281+
"gemini-2.5-flash", mock_api_client_fixture
282+
)
283+
284+
def test_unrecognized_model_raises(self, mock_api_client_fixture):
285+
with pytest.raises(ValueError, match="Unrecognized model name"):
286+
_evals_common._normalize_inference_model_name(
287+
"not/a/valid/model", mock_api_client_fixture
288+
)
289+
290+
def test_resolve_inference_configs_normalizes_model(self, mock_api_client_fixture):
291+
inference_configs = {
292+
"candidate-1": agentplatform_genai_types.EvaluationRunInferenceConfig(
293+
model="gemini-2.5-flash"
294+
)
295+
}
296+
result = _evals_common._resolve_inference_configs(
297+
mock_api_client_fixture,
298+
common_types.EvaluationRunDataSource(),
299+
inference_configs,
300+
)
301+
assert result["candidate-1"].model == (
302+
f"{self._FQ_PREFIX}publishers/google/models/gemini-2.5-flash"
303+
)
304+
305+
231306
class TestTransformers:
232307
"""Unit tests for transformers."""
233308

vertexai/_genai/_evals_common.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,21 @@ def _resolve_inference_configs(
513513
else:
514514
config.agent_configs = parsed_agent_info.agents
515515

516-
# Resolve prompt template data
517516
if inference_configs:
518517
for inference_config in inference_configs.values():
518+
model_val = (
519+
inference_config.get("model")
520+
if isinstance(inference_config, dict)
521+
else inference_config.model
522+
)
523+
if model_val:
524+
normalized_model = _normalize_inference_model_name(
525+
model_val, api_client
526+
)
527+
if isinstance(inference_config, dict):
528+
inference_config["model"] = normalized_model
529+
else:
530+
inference_config.model = normalized_model
519531
prompt_template_val = (
520532
inference_config.get("prompt_template")
521533
if isinstance(inference_config, dict)
@@ -912,6 +924,62 @@ def _is_gemini_model(model: str) -> bool:
912924
)
913925

914926

927+
def _normalize_inference_model_name(model: str, api_client: BaseApiClient) -> str:
928+
"""Expands a model name to a fully-qualified resource name for inference.
929+
930+
A short or location-less model name has no serving location for the
931+
Evaluation Service to route on, so it is expanded using the client's
932+
project and location. Already fully-qualified names pass through. Raises
933+
ValueError if the client is missing a project or location, or if the model
934+
name is not a recognized Vertex form.
935+
"""
936+
if not model:
937+
return model
938+
939+
if model.startswith("projects/"):
940+
return model
941+
942+
project = getattr(api_client, "project", None)
943+
location = getattr(api_client, "location", None)
944+
prefix = f"projects/{project}/locations/{location}/"
945+
946+
def _require_project_location() -> None:
947+
if not project or not location:
948+
raise ValueError(
949+
f"Cannot expand model name '{model}' to a fully-qualified"
950+
" resource name because the client is missing a project or"
951+
" location. Set project and location on the client, or pass a"
952+
" fully-qualified"
953+
" 'projects/{project}/locations/{location}/publishers/google/models/{model}'"
954+
" resource name."
955+
)
956+
957+
if (
958+
model.startswith("publishers/")
959+
or model.startswith("endpoints/")
960+
or model.startswith("tunedModels/")
961+
):
962+
_require_project_location()
963+
return f"{prefix}{model}"
964+
965+
if model.startswith("models/"):
966+
_require_project_location()
967+
return f"{prefix}publishers/google/{model}"
968+
969+
if "/" not in model and _is_gemini_model(model):
970+
_require_project_location()
971+
return f"{prefix}publishers/google/models/{model}"
972+
973+
raise ValueError(
974+
f"Unrecognized model name '{model}'. Provide a Gemini model name (e.g."
975+
" 'gemini-2.5-flash'), or a fully-qualified publisher-model or endpoint"
976+
" resource name (e.g."
977+
" 'projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash'"
978+
" or"
979+
" 'projects/{project}/locations/{location}/endpoints/{endpoint}')."
980+
)
981+
982+
915983
def _run_inference_internal(
916984
api_client: BaseApiClient,
917985
model: Union[Callable[[Any], Any], str],

vertexai/_genai/evals.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,11 @@ def create_evaluation_run(
26592659
The key is the candidate name, and the value is the inference config.
26602660
If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
26612661
this will be automatically constructed using `agent_info` and `user_simulator_config`.
2662+
The `model` field of an inference config accepts a short Gemini model
2663+
name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
2664+
fully-qualified resource name using the client's project and location,
2665+
or an already fully-qualified publisher-model or endpoint resource
2666+
name.
26622667
Example:
26632668
{"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
26642669
labels: The labels to apply to the evaluation run.
@@ -4448,6 +4453,11 @@ async def create_evaluation_run(
44484453
The key is the candidate name, and the value is the inference config.
44494454
If provided, `agent_info` must be None. If omitted and `agent_info` is provided,
44504455
this will be automatically constructed using `agent_info` and `user_simulator_config`.
4456+
The `model` field of an inference config accepts a short Gemini model
4457+
name (e.g. `gemini-2.5-flash`), which is automatically expanded to a
4458+
fully-qualified resource name using the client's project and location,
4459+
or an already fully-qualified publisher-model or endpoint resource
4460+
name.
44514461
Example:
44524462
{"candidate-1": types.EvaluationRunInferenceConfig(model="gemini-2.5-flash")}
44534463
red_teaming_config: This field is experimental and may change in future

vertexai/_genai/types/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,7 +2549,7 @@ class EvaluationRunInferenceConfig(_common.BaseModel):
25492549
)
25502550
model: Optional[str] = Field(
25512551
default=None,
2552-
description="""The fully qualified name of the publisher model or endpoint to use for inference.""",
2552+
description="""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`).""",
25532553
)
25542554
prompt_template: Optional[EvaluationRunPromptTemplate] = Field(
25552555
default=None, description="""The prompt template used for inference."""
@@ -2574,7 +2574,7 @@ class EvaluationRunInferenceConfigDict(TypedDict, total=False):
25742574
"""The agent config."""
25752575

25762576
model: Optional[str]
2577-
"""The fully qualified name of the publisher model or endpoint to use for inference."""
2577+
"""The model to use for inference. Accepts a short Gemini model name (e.g. `gemini-2.5-flash`), which is automatically expanded to a fully-qualified resource name using the client's project and location, or an already fully-qualified publisher-model or endpoint resource name (e.g. `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`)."""
25782578

25792579
prompt_template: Optional[EvaluationRunPromptTemplateDict]
25802580
"""The prompt template used for inference."""

0 commit comments

Comments
 (0)