@@ -122,15 +122,19 @@ def mock_api_client_fixture():
122122
123123@pytest .fixture
124124def 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" , {})
@@ -3306,14 +3310,8 @@ def test_run_inference_with_agent_engine_falls_back_to_managed_sessions_api(
33063310 assert inference_result .candidate_name == "agent_engine_0"
33073311
33083312 @mock .patch .object (_evals_utils , "EvalDatasetLoader" )
3309- @mock .patch ("vertexai._genai._evals_common.InMemorySessionService" ) # fmt: skip
3310- @mock .patch ("vertexai._genai._evals_common.Runner" )
3311- @mock .patch ("vertexai._genai._evals_common.LlmAgent" )
33123313 def test_run_inference_with_local_agent (
33133314 self ,
3314- mock_llm_agent ,
3315- mock_runner ,
3316- mock_session_service ,
33173315 mock_eval_dataset_loader ,
33183316 ):
33193317 mock_df = pd .DataFrame (
@@ -3341,8 +3339,15 @@ def test_run_inference_with_local_agent(
33413339 mock_agent_instance .instruction = "mock instruction"
33423340 mock_agent_instance .tools = []
33433341 mock_agent_instance .sub_agents = []
3344- mock_llm_agent .return_value = mock_agent_instance
3342+
3343+ # Mock ADK modules for lazy imports in _execute_local_agent_run_with_retry_async
3344+ mock_session_service = mock .MagicMock ()
33453345 mock_session_service .return_value .create_session = mock .AsyncMock ()
3346+ mock_runner = mock .MagicMock ()
3347+ mock_adk_sessions_module = mock .MagicMock ()
3348+ mock_adk_sessions_module .InMemorySessionService = mock_session_service
3349+ mock_adk_runners_module = mock .MagicMock ()
3350+ mock_adk_runners_module .Runner = mock_runner
33463351 mock_runner_instance = mock_runner .return_value
33473352 stream_run_return_value_1 = [
33483353 mock .Mock (
@@ -3393,10 +3398,19 @@ def run_async_side_effect(*args, **kwargs):
33933398
33943399 mock_runner_instance .run_async .side_effect = run_async_side_effect
33953400
3396- inference_result = self .client .evals .run_inference (
3397- agent = mock_agent_instance ,
3398- src = mock_df ,
3399- )
3401+ with mock .patch .dict (
3402+ sys .modules ,
3403+ {
3404+ "google.adk" : mock .MagicMock (),
3405+ "google.adk.sessions" : mock_adk_sessions_module ,
3406+ "google.adk.runners" : mock_adk_runners_module ,
3407+ "google.adk.agents" : mock .MagicMock (),
3408+ },
3409+ ):
3410+ inference_result = self .client .evals .run_inference (
3411+ agent = mock_agent_instance ,
3412+ src = mock_df ,
3413+ )
34003414
34013415 mock_eval_dataset_loader .return_value .load .assert_called_once_with (mock_df )
34023416 assert mock_session_service .call_count == 2
@@ -3522,11 +3536,14 @@ def test_run_inference_with_litellm_string_prompt_format(
35223536 mock_api_client_fixture ,
35233537 ):
35243538 """Tests inference with LiteLLM using a simple prompt string."""
3525- with mock .patch (
3526- "vertexai._genai._evals_common.litellm"
3527- ) as mock_litellm , mock .patch (
3528- "vertexai._genai._evals_common._call_litellm_completion"
3529- ) as mock_call_litellm_completion :
3539+ with (
3540+ mock .patch (
3541+ "vertexai._genai._evals_common.litellm"
3542+ ) as mock_litellm ,
3543+ mock .patch (
3544+ "vertexai._genai._evals_common._call_litellm_completion"
3545+ ) as mock_call_litellm_completion ,
3546+ ):
35303547 mock_litellm .utils .get_valid_models .return_value = ["gpt-4o" ]
35313548 prompt_df = pd .DataFrame ([{"prompt" : "What is LiteLLM?" }])
35323549 expected_messages = [{"role" : "user" , "content" : "What is LiteLLM?" }]
@@ -3578,11 +3595,14 @@ def test_run_inference_with_litellm_openai_request_format(
35783595 mock_api_client_fixture ,
35793596 ):
35803597 """Tests inference with LiteLLM where the row contains a chat completion request body."""
3581- with mock .patch (
3582- "vertexai._genai._evals_common.litellm"
3583- ) as mock_litellm , mock .patch (
3584- "vertexai._genai._evals_common._call_litellm_completion"
3585- ) as mock_call_litellm_completion :
3598+ with (
3599+ mock .patch (
3600+ "vertexai._genai._evals_common.litellm"
3601+ ) as mock_litellm ,
3602+ mock .patch (
3603+ "vertexai._genai._evals_common._call_litellm_completion"
3604+ ) as mock_call_litellm_completion ,
3605+ ):
35863606 mock_litellm .utils .get_valid_models .return_value = ["gpt-4o" ]
35873607 prompt_df = pd .DataFrame (
35883608 [
@@ -4098,21 +4118,23 @@ def test_run_agent_internal_multi_turn_with_agent(self, mock_run_agent):
40984118 ]
40994119 assert "mock_agent" in agent_data ["agents" ]
41004120
4101- @mock .patch ("vertexai._genai._evals_common.ADK_SessionInput" ) # fmt: skip
4102- @mock .patch ("vertexai._genai._evals_common.EvaluationGenerator" ) # fmt: skip
4103- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulator" ) # fmt: skip
4104- @mock .patch ("vertexai._genai._evals_common.ConversationScenario" ) # fmt: skip
4105- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig" ) # fmt: skip
41064121 @pytest .mark .asyncio
4107- async def test_run_adk_user_simulation_with_intermediate_events (
4108- self ,
4109- mock_config ,
4110- mock_scenario ,
4111- mock_simulator ,
4112- mock_generator ,
4113- mock_session_input ,
4114- ):
4122+ async def test_run_adk_user_simulation_with_intermediate_events (self ):
41154123 """Tests that intermediate invocation events (e.g. tool calls) are parsed successfully."""
4124+ mock_scenario = mock .MagicMock ()
4125+ mock_config = mock .MagicMock ()
4126+ mock_simulator = mock .MagicMock ()
4127+ mock_generator = mock .MagicMock ()
4128+ mock_session_input = mock .MagicMock ()
4129+ mock_adk_eval_scenarios = mock .MagicMock ()
4130+ mock_adk_eval_scenarios .ConversationScenario = mock_scenario
4131+ mock_adk_eval_case = mock .MagicMock ()
4132+ mock_adk_eval_case .SessionInput = mock_session_input
4133+ mock_adk_eval_generator = mock .MagicMock ()
4134+ mock_adk_eval_generator .EvaluationGenerator = mock_generator
4135+ mock_adk_simulator_module = mock .MagicMock ()
4136+ mock_adk_simulator_module .LlmBackedUserSimulator = mock_simulator
4137+ mock_adk_simulator_module .LlmBackedUserSimulatorConfig = mock_config
41164138 row = pd .Series (
41174139 {
41184140 "starting_prompt" : "I want a laptop." ,
@@ -4165,7 +4187,19 @@ async def test_run_adk_user_simulation_with_intermediate_events(
41654187 mock_generator ._generate_inferences_from_root_agent = mock .AsyncMock (
41664188 return_value = [mock_invocation ]
41674189 )
4168- turns = await _evals_common ._run_adk_user_simulation (row , mock_agent )
4190+ with mock .patch .dict (
4191+ sys .modules ,
4192+ {
4193+ "google.adk" : mock .MagicMock (),
4194+ "google.adk.evaluation" : mock .MagicMock (),
4195+ "google.adk.evaluation.conversation_scenarios" : mock_adk_eval_scenarios ,
4196+ "google.adk.evaluation.eval_case" : mock_adk_eval_case ,
4197+ "google.adk.evaluation.evaluation_generator" : mock_adk_eval_generator ,
4198+ "google.adk.evaluation.simulation" : mock .MagicMock (),
4199+ "google.adk.evaluation.simulation.llm_backed_user_simulator" : mock_adk_simulator_module ,
4200+ },
4201+ ):
4202+ turns = await _evals_common ._run_adk_user_simulation (row , mock_agent )
41694203
41704204 assert len (turns ) == 1
41714205 turn = turns [0 ]
@@ -7006,20 +7040,50 @@ def test_build_request_payload_tool_use_quality_v1_with_agent_data_tool_call(
70067040class TestRunAdkUserSimulation :
70077041 """Unit tests for the _run_adk_user_simulation function."""
70087042
7009- @mock .patch ("vertexai._genai._evals_common.ADK_SessionInput" ) # fmt: skip
7010- @mock .patch ("vertexai._genai._evals_common.EvaluationGenerator" ) # fmt: skip
7011- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulator" ) # fmt: skip
7012- @mock .patch ("vertexai._genai._evals_common.ConversationScenario" ) # fmt: skip
7013- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig" ) # fmt: skip
7043+ def _build_adk_mock_modules (self ):
7044+ """Builds mock ADK modules for lazy imports in _run_adk_user_simulation."""
7045+ mock_scenario_cls = mock .MagicMock ()
7046+ mock_config_cls = mock .MagicMock ()
7047+ mock_simulator_cls = mock .MagicMock ()
7048+ mock_generator_cls = mock .MagicMock ()
7049+ mock_session_input_cls = mock .MagicMock ()
7050+ mock_modules = {
7051+ "google.adk" : mock .MagicMock (),
7052+ "google.adk.evaluation" : mock .MagicMock (),
7053+ "google.adk.evaluation.conversation_scenarios" : mock .MagicMock (
7054+ ConversationScenario = mock_scenario_cls
7055+ ),
7056+ "google.adk.evaluation.eval_case" : mock .MagicMock (
7057+ SessionInput = mock_session_input_cls
7058+ ),
7059+ "google.adk.evaluation.evaluation_generator" : mock .MagicMock (
7060+ EvaluationGenerator = mock_generator_cls
7061+ ),
7062+ "google.adk.evaluation.simulation" : mock .MagicMock (),
7063+ "google.adk.evaluation.simulation.llm_backed_user_simulator" : mock .MagicMock (
7064+ LlmBackedUserSimulator = mock_simulator_cls ,
7065+ LlmBackedUserSimulatorConfig = mock_config_cls ,
7066+ ),
7067+ }
7068+ return (
7069+ mock_modules ,
7070+ mock_scenario_cls ,
7071+ mock_config_cls ,
7072+ mock_simulator_cls ,
7073+ mock_generator_cls ,
7074+ mock_session_input_cls ,
7075+ )
7076+
70147077 @pytest .mark .asyncio
7015- async def test_run_adk_user_simulation_success (
7016- self ,
7017- mock_config_cls ,
7018- mock_scenario_cls ,
7019- mock_simulator_cls ,
7020- mock_generator_cls ,
7021- mock_session_input_cls ,
7022- ):
7078+ async def test_run_adk_user_simulation_success (self ):
7079+ (
7080+ mock_modules ,
7081+ mock_scenario_cls ,
7082+ _ ,
7083+ _ ,
7084+ mock_generator_cls ,
7085+ mock_session_input_cls ,
7086+ ) = self ._build_adk_mock_modules ()
70237087 row = pd .Series (
70247088 {
70257089 "starting_prompt" : "start" ,
@@ -7039,7 +7103,8 @@ async def test_run_adk_user_simulation_success(
70397103 return_value = [mock_invocation ]
70407104 )
70417105
7042- turns = await _evals_common ._run_adk_user_simulation (row , mock_agent )
7106+ with mock .patch .dict (sys .modules , mock_modules ):
7107+ turns = await _evals_common ._run_adk_user_simulation (row , mock_agent )
70437108
70447109 assert len (turns ) == 1
70457110 turn = turns [0 ]
@@ -7058,40 +7123,26 @@ async def test_run_adk_user_simulation_success(
70587123 )
70597124 mock_session_input_cls .assert_called_once ()
70607125
7061- @mock .patch ("vertexai._genai._evals_common.ADK_SessionInput" ) # fmt: skip
7062- @mock .patch ("vertexai._genai._evals_common.EvaluationGenerator" ) # fmt: skip
7063- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulator" ) # fmt: skip
7064- @mock .patch ("vertexai._genai._evals_common.ConversationScenario" ) # fmt: skip
7065- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig" ) # fmt: skip
70667126 @pytest .mark .asyncio
7067- async def test_run_adk_user_simulation_missing_columns (
7068- self ,
7069- mock_config_cls ,
7070- mock_scenario_cls ,
7071- mock_simulator_cls ,
7072- mock_generator_cls ,
7073- mock_session_input_cls ,
7074- ):
7127+ async def test_run_adk_user_simulation_missing_columns (self ):
7128+ mock_modules , _ , _ , _ , _ , _ = self ._build_adk_mock_modules ()
70757129 row = pd .Series ({"conversation_plan" : "plan" })
70767130 mock_agent = mock .Mock ()
70777131
7078- with pytest .raises (ValueError , match = "User simulation requires" ):
7079- await _evals_common ._run_adk_user_simulation (row , mock_agent )
7132+ with mock .patch .dict (sys .modules , mock_modules ):
7133+ with pytest .raises (ValueError , match = "User simulation requires" ):
7134+ await _evals_common ._run_adk_user_simulation (row , mock_agent )
70807135
7081- @mock .patch ("vertexai._genai._evals_common.ADK_SessionInput" ) # fmt: skip
7082- @mock .patch ("vertexai._genai._evals_common.EvaluationGenerator" ) # fmt: skip
7083- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulator" ) # fmt: skip
7084- @mock .patch ("vertexai._genai._evals_common.ConversationScenario" ) # fmt: skip
7085- @mock .patch ("vertexai._genai._evals_common.LlmBackedUserSimulatorConfig" ) # fmt: skip
70867136 @pytest .mark .asyncio
7087- async def test_run_adk_user_simulation_missing_session_inputs (
7088- self ,
7089- mock_config_cls ,
7090- mock_scenario_cls ,
7091- mock_simulator_cls ,
7092- mock_generator_cls ,
7093- mock_session_input_cls ,
7094- ):
7137+ async def test_run_adk_user_simulation_missing_session_inputs (self ):
7138+ (
7139+ mock_modules ,
7140+ mock_scenario_cls ,
7141+ _ ,
7142+ _ ,
7143+ mock_generator_cls ,
7144+ mock_session_input_cls ,
7145+ ) = self ._build_adk_mock_modules ()
70957146 row = pd .Series (
70967147 {
70977148 "starting_prompt" : "start" ,
@@ -7110,7 +7161,8 @@ async def test_run_adk_user_simulation_missing_session_inputs(
71107161 return_value = [mock_invocation ]
71117162 )
71127163
7113- await _evals_common ._run_adk_user_simulation (row , mock_agent )
7164+ with mock .patch .dict (sys .modules , mock_modules ):
7165+ await _evals_common ._run_adk_user_simulation (row , mock_agent )
71147166
71157167 mock_scenario_cls .assert_called_once_with (
71167168 starting_prompt = "start" ,
0 commit comments