Skip to content

Commit f3538a5

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
fix: GenAI Client(evals): Lazy-load ADK imports in _evals_common.py to avoid top-level ImportError
PiperOrigin-RevId: 901011383
1 parent f2d73fd commit f3538a5

2 files changed

Lines changed: 151 additions & 115 deletions

File tree

tests/unit/vertexai/genai/test_evals.py

Lines changed: 123 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,19 @@ def mock_api_client_fixture():
122122

123123
@pytest.fixture
124124
def mock_eval_dependencies(mock_api_client_fixture):
125-
with mock.patch("google.cloud.storage.Client") as mock_storage_client, mock.patch(
126-
"google.cloud.bigquery.Client"
127-
) as mock_bq_client, mock.patch(
128-
"vertexai._genai.evals.Evals.evaluate_instances"
129-
) as mock_evaluate_instances, mock.patch(
130-
"vertexai._genai._gcs_utils.GcsUtils.upload_json_to_prefix"
131-
) as mock_upload_to_gcs, mock.patch(
132-
"vertexai._genai._evals_metric_loaders.LazyLoadedPrebuiltMetric._fetch_and_parse"
133-
) as mock_fetch_prebuilt_metric:
125+
with (
126+
mock.patch("google.cloud.storage.Client") as mock_storage_client,
127+
mock.patch("google.cloud.bigquery.Client") as mock_bq_client,
128+
mock.patch(
129+
"vertexai._genai.evals.Evals.evaluate_instances"
130+
) as mock_evaluate_instances,
131+
mock.patch(
132+
"vertexai._genai._gcs_utils.GcsUtils.upload_json_to_prefix"
133+
) as mock_upload_to_gcs,
134+
mock.patch(
135+
"vertexai._genai._evals_metric_loaders.LazyLoadedPrebuiltMetric._fetch_and_parse"
136+
) as mock_fetch_prebuilt_metric,
137+
):
134138

135139
def mock_evaluate_instances_side_effect(*args, **kwargs):
136140
metric_config = kwargs.get("metric_config", {})
@@ -3386,14 +3390,8 @@ def test_run_inference_with_agent_engine_falls_back_to_managed_sessions_api(
33863390
assert inference_result.candidate_name == "agent_engine_0"
33873391

33883392
@mock.patch.object(_evals_utils, "EvalDatasetLoader")
3389-
@mock.patch("vertexai._genai._evals_common.InMemorySessionService") # fmt: skip
3390-
@mock.patch("vertexai._genai._evals_common.Runner")
3391-
@mock.patch("vertexai._genai._evals_common.LlmAgent")
33923393
def test_run_inference_with_local_agent(
33933394
self,
3394-
mock_llm_agent,
3395-
mock_runner,
3396-
mock_session_service,
33973395
mock_eval_dataset_loader,
33983396
):
33993397
mock_df = pd.DataFrame(
@@ -3421,8 +3419,15 @@ def test_run_inference_with_local_agent(
34213419
mock_agent_instance.instruction = "mock instruction"
34223420
mock_agent_instance.tools = []
34233421
mock_agent_instance.sub_agents = []
3424-
mock_llm_agent.return_value = mock_agent_instance
3422+
3423+
# Mock ADK modules for lazy imports in _execute_local_agent_run_with_retry_async
3424+
mock_session_service = mock.MagicMock()
34253425
mock_session_service.return_value.create_session = mock.AsyncMock()
3426+
mock_runner = mock.MagicMock()
3427+
mock_adk_sessions_module = mock.MagicMock()
3428+
mock_adk_sessions_module.InMemorySessionService = mock_session_service
3429+
mock_adk_runners_module = mock.MagicMock()
3430+
mock_adk_runners_module.Runner = mock_runner
34263431
mock_runner_instance = mock_runner.return_value
34273432
stream_run_return_value_1 = [
34283433
mock.Mock(
@@ -3473,10 +3478,19 @@ def run_async_side_effect(*args, **kwargs):
34733478

34743479
mock_runner_instance.run_async.side_effect = run_async_side_effect
34753480

3476-
inference_result = self.client.evals.run_inference(
3477-
agent=mock_agent_instance,
3478-
src=mock_df,
3479-
)
3481+
with mock.patch.dict(
3482+
sys.modules,
3483+
{
3484+
"google.adk": mock.MagicMock(),
3485+
"google.adk.sessions": mock_adk_sessions_module,
3486+
"google.adk.runners": mock_adk_runners_module,
3487+
"google.adk.agents": mock.MagicMock(),
3488+
},
3489+
):
3490+
inference_result = self.client.evals.run_inference(
3491+
agent=mock_agent_instance,
3492+
src=mock_df,
3493+
)
34803494

34813495
mock_eval_dataset_loader.return_value.load.assert_called_once_with(mock_df)
34823496
assert mock_session_service.call_count == 2
@@ -4178,21 +4192,23 @@ def test_run_agent_internal_multi_turn_with_agent(self, mock_run_agent):
41784192
]
41794193
assert "mock_agent" in agent_data["agents"]
41804194

4181-
@mock.patch("vertexai._genai._evals_common.ADK_SessionInput") # fmt: skip
4182-
@mock.patch("vertexai._genai._evals_common.EvaluationGenerator") # fmt: skip
4183-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulator") # fmt: skip
4184-
@mock.patch("vertexai._genai._evals_common.ConversationScenario") # fmt: skip
4185-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig") # fmt: skip
41864195
@pytest.mark.asyncio
4187-
async def test_run_adk_user_simulation_with_intermediate_events(
4188-
self,
4189-
mock_config,
4190-
mock_scenario,
4191-
mock_simulator,
4192-
mock_generator,
4193-
mock_session_input,
4194-
):
4196+
async def test_run_adk_user_simulation_with_intermediate_events(self):
41954197
"""Tests that intermediate invocation events (e.g. tool calls) are parsed successfully."""
4198+
mock_scenario = mock.MagicMock()
4199+
mock_config = mock.MagicMock()
4200+
mock_simulator = mock.MagicMock()
4201+
mock_generator = mock.MagicMock()
4202+
mock_session_input = mock.MagicMock()
4203+
mock_adk_eval_scenarios = mock.MagicMock()
4204+
mock_adk_eval_scenarios.ConversationScenario = mock_scenario
4205+
mock_adk_eval_case = mock.MagicMock()
4206+
mock_adk_eval_case.SessionInput = mock_session_input
4207+
mock_adk_eval_generator = mock.MagicMock()
4208+
mock_adk_eval_generator.EvaluationGenerator = mock_generator
4209+
mock_adk_simulator_module = mock.MagicMock()
4210+
mock_adk_simulator_module.LlmBackedUserSimulator = mock_simulator
4211+
mock_adk_simulator_module.LlmBackedUserSimulatorConfig = mock_config
41964212
row = pd.Series(
41974213
{
41984214
"starting_prompt": "I want a laptop.",
@@ -4245,7 +4261,19 @@ async def test_run_adk_user_simulation_with_intermediate_events(
42454261
mock_generator._generate_inferences_from_root_agent = mock.AsyncMock(
42464262
return_value=[mock_invocation]
42474263
)
4248-
turns = await _evals_common._run_adk_user_simulation(row, mock_agent)
4264+
with mock.patch.dict(
4265+
sys.modules,
4266+
{
4267+
"google.adk": mock.MagicMock(),
4268+
"google.adk.evaluation": mock.MagicMock(),
4269+
"google.adk.evaluation.conversation_scenarios": mock_adk_eval_scenarios,
4270+
"google.adk.evaluation.eval_case": mock_adk_eval_case,
4271+
"google.adk.evaluation.evaluation_generator": mock_adk_eval_generator,
4272+
"google.adk.evaluation.simulation": mock.MagicMock(),
4273+
"google.adk.evaluation.simulation.llm_backed_user_simulator": mock_adk_simulator_module,
4274+
},
4275+
):
4276+
turns = await _evals_common._run_adk_user_simulation(row, mock_agent)
42494277

42504278
assert len(turns) == 1
42514279
turn = turns[0]
@@ -7086,20 +7114,50 @@ def test_build_request_payload_tool_use_quality_v1_with_agent_data_tool_call(
70867114
class TestRunAdkUserSimulation:
70877115
"""Unit tests for the _run_adk_user_simulation function."""
70887116

7089-
@mock.patch("vertexai._genai._evals_common.ADK_SessionInput") # fmt: skip
7090-
@mock.patch("vertexai._genai._evals_common.EvaluationGenerator") # fmt: skip
7091-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulator") # fmt: skip
7092-
@mock.patch("vertexai._genai._evals_common.ConversationScenario") # fmt: skip
7093-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig") # fmt: skip
7117+
def _build_adk_mock_modules(self):
7118+
"""Builds mock ADK modules for lazy imports in _run_adk_user_simulation."""
7119+
mock_scenario_cls = mock.MagicMock()
7120+
mock_config_cls = mock.MagicMock()
7121+
mock_simulator_cls = mock.MagicMock()
7122+
mock_generator_cls = mock.MagicMock()
7123+
mock_session_input_cls = mock.MagicMock()
7124+
mock_modules = {
7125+
"google.adk": mock.MagicMock(),
7126+
"google.adk.evaluation": mock.MagicMock(),
7127+
"google.adk.evaluation.conversation_scenarios": mock.MagicMock(
7128+
ConversationScenario=mock_scenario_cls
7129+
),
7130+
"google.adk.evaluation.eval_case": mock.MagicMock(
7131+
SessionInput=mock_session_input_cls
7132+
),
7133+
"google.adk.evaluation.evaluation_generator": mock.MagicMock(
7134+
EvaluationGenerator=mock_generator_cls
7135+
),
7136+
"google.adk.evaluation.simulation": mock.MagicMock(),
7137+
"google.adk.evaluation.simulation.llm_backed_user_simulator": mock.MagicMock(
7138+
LlmBackedUserSimulator=mock_simulator_cls,
7139+
LlmBackedUserSimulatorConfig=mock_config_cls,
7140+
),
7141+
}
7142+
return (
7143+
mock_modules,
7144+
mock_scenario_cls,
7145+
mock_config_cls,
7146+
mock_simulator_cls,
7147+
mock_generator_cls,
7148+
mock_session_input_cls,
7149+
)
7150+
70947151
@pytest.mark.asyncio
7095-
async def test_run_adk_user_simulation_success(
7096-
self,
7097-
mock_config_cls,
7098-
mock_scenario_cls,
7099-
mock_simulator_cls,
7100-
mock_generator_cls,
7101-
mock_session_input_cls,
7102-
):
7152+
async def test_run_adk_user_simulation_success(self):
7153+
(
7154+
mock_modules,
7155+
mock_scenario_cls,
7156+
_,
7157+
_,
7158+
mock_generator_cls,
7159+
mock_session_input_cls,
7160+
) = self._build_adk_mock_modules()
71037161
row = pd.Series(
71047162
{
71057163
"starting_prompt": "start",
@@ -7119,7 +7177,8 @@ async def test_run_adk_user_simulation_success(
71197177
return_value=[mock_invocation]
71207178
)
71217179

7122-
turns = await _evals_common._run_adk_user_simulation(row, mock_agent)
7180+
with mock.patch.dict(sys.modules, mock_modules):
7181+
turns = await _evals_common._run_adk_user_simulation(row, mock_agent)
71237182

71247183
assert len(turns) == 1
71257184
turn = turns[0]
@@ -7138,40 +7197,26 @@ async def test_run_adk_user_simulation_success(
71387197
)
71397198
mock_session_input_cls.assert_called_once()
71407199

7141-
@mock.patch("vertexai._genai._evals_common.ADK_SessionInput") # fmt: skip
7142-
@mock.patch("vertexai._genai._evals_common.EvaluationGenerator") # fmt: skip
7143-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulator") # fmt: skip
7144-
@mock.patch("vertexai._genai._evals_common.ConversationScenario") # fmt: skip
7145-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig") # fmt: skip
71467200
@pytest.mark.asyncio
7147-
async def test_run_adk_user_simulation_missing_columns(
7148-
self,
7149-
mock_config_cls,
7150-
mock_scenario_cls,
7151-
mock_simulator_cls,
7152-
mock_generator_cls,
7153-
mock_session_input_cls,
7154-
):
7201+
async def test_run_adk_user_simulation_missing_columns(self):
7202+
mock_modules, _, _, _, _, _ = self._build_adk_mock_modules()
71557203
row = pd.Series({"conversation_plan": "plan"})
71567204
mock_agent = mock.Mock()
71577205

7158-
with pytest.raises(ValueError, match="User simulation requires"):
7159-
await _evals_common._run_adk_user_simulation(row, mock_agent)
7206+
with mock.patch.dict(sys.modules, mock_modules):
7207+
with pytest.raises(ValueError, match="User simulation requires"):
7208+
await _evals_common._run_adk_user_simulation(row, mock_agent)
71607209

7161-
@mock.patch("vertexai._genai._evals_common.ADK_SessionInput") # fmt: skip
7162-
@mock.patch("vertexai._genai._evals_common.EvaluationGenerator") # fmt: skip
7163-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulator") # fmt: skip
7164-
@mock.patch("vertexai._genai._evals_common.ConversationScenario") # fmt: skip
7165-
@mock.patch("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig") # fmt: skip
71667210
@pytest.mark.asyncio
7167-
async def test_run_adk_user_simulation_missing_session_inputs(
7168-
self,
7169-
mock_config_cls,
7170-
mock_scenario_cls,
7171-
mock_simulator_cls,
7172-
mock_generator_cls,
7173-
mock_session_input_cls,
7174-
):
7211+
async def test_run_adk_user_simulation_missing_session_inputs(self):
7212+
(
7213+
mock_modules,
7214+
mock_scenario_cls,
7215+
_,
7216+
_,
7217+
mock_generator_cls,
7218+
mock_session_input_cls,
7219+
) = self._build_adk_mock_modules()
71757220
row = pd.Series(
71767221
{
71777222
"starting_prompt": "start",
@@ -7190,7 +7235,8 @@ async def test_run_adk_user_simulation_missing_session_inputs(
71907235
return_value=[mock_invocation]
71917236
)
71927237

7193-
await _evals_common._run_adk_user_simulation(row, mock_agent)
7238+
with mock.patch.dict(sys.modules, mock_modules):
7239+
await _evals_common._run_adk_user_simulation(row, mock_agent)
71947240

71957241
mock_scenario_cls.assert_called_once_with(
71967242
starting_prompt="start",

0 commit comments

Comments
 (0)