@@ -923,6 +923,125 @@ def _fetch_agent_config_dict(
923923 )
924924
925925
926+ def _resolve_interactions_for_display (
927+ api_client : BaseApiClient ,
928+ dataset_list : list [types .EvaluationDataset ],
929+ ) -> list [types .EvaluationDataset ]:
930+ """Resolves interactions_data_source on EvalCases for display.
931+
932+ For each EvalCase that has ``interactions_data_source`` set but no
933+ ``agent_data``, fetches the Interaction and Agent config from the
934+ API, converts to AgentData, and populates it on the EvalCase so
935+ that ``show()`` can render the System Topology and Conversation
936+ Trace sections.
937+
938+ Args:
939+ api_client: The API client used to fetch interactions and agents.
940+ dataset_list: The original evaluation datasets.
941+
942+ Returns:
943+ A list of EvaluationDatasets with agent_data populated from
944+ resolved interactions.
945+ """
946+ resolved_datasets = []
947+ for dataset in dataset_list :
948+ if not dataset .eval_cases :
949+ resolved_datasets .append (dataset )
950+ continue
951+
952+ resolved_cases = []
953+ any_resolved = False
954+ for case in dataset .eval_cases :
955+ ids = case .interactions_data_source
956+ if ids and not case .agent_data :
957+ interaction_name = ids .interaction
958+ gemini_cfg = ids .gemini_agent_config
959+ agent_name = gemini_cfg .gemini_agent if gemini_cfg else None
960+
961+ if interaction_name :
962+ try :
963+ # Extract the interaction short ID from the resource
964+ # name; handles both full resource names and bare IDs.
965+ interaction_id = interaction_name .split ("/" )[- 1 ]
966+
967+ logger .info (
968+ "Fetching interaction chain for %s for display." ,
969+ interaction_name ,
970+ )
971+ current_interaction_id = interaction_id
972+ interactions = []
973+ seen_ids = set ()
974+ for _ in range (10 ): # max depth
975+ if current_interaction_id in seen_ids :
976+ break
977+ seen_ids .add (current_interaction_id )
978+ path = f"interactions/{ current_interaction_id } "
979+ response = api_client .request ("get" , path , {}, None )
980+ if not response .body :
981+ if not interactions :
982+ logger .warning (
983+ "Empty response fetching interaction %s." ,
984+ interaction_name ,
985+ )
986+ break
987+ interaction_dict = json .loads (response .body )
988+ interactions .append (interaction_dict )
989+ prev_id_str = interaction_dict .get ("previousInteractionId" ) or interaction_dict .get ("previous_interaction_id" )
990+ if not prev_id_str :
991+ break
992+ current_interaction_id = prev_id_str .split ("/" )[- 1 ]
993+
994+ if not interactions :
995+ resolved_cases .append (case )
996+ continue
997+
998+ interactions .reverse () # chronological order
999+ all_steps = []
1000+ for i_dict in interactions :
1001+ all_steps .extend (i_dict .get ("steps" , []))
1002+
1003+ combined_interaction = interactions [- 1 ].copy ()
1004+ combined_interaction ["steps" ] = all_steps
1005+ logger .warning (f"DEBUG: I fetched { len (interactions )} interactions. all_steps length: { len (all_steps )} " )
1006+ for i , idct in enumerate (interactions ):
1007+ logger .warning (f"DEBUG int { i } : { idct .get ('id' , 'no_id' )} has { len (idct .get ('steps' , []))} steps" )
1008+ agent_data = _interaction_dict_to_agent_data (combined_interaction )
1009+
1010+ # Best-effort: fetch agent config (instruction,
1011+ # tools, description) from the Agent API.
1012+ agent_config = _fetch_agent_config_dict (
1013+ api_client , agent_name or ""
1014+ )
1015+ agent_data .agents = {agent_config .agent_id : agent_config }
1016+
1017+ _merge_text_parts_in_agent_data (agent_data )
1018+
1019+ # Preserve all original EvalCase fields; only
1020+ # update agent_data.
1021+ resolved_cases .append (
1022+ case .model_copy (update = {"agent_data" : agent_data })
1023+ )
1024+ any_resolved = True
1025+ continue
1026+ except Exception as e : # pylint: disable=broad-exception-caught
1027+ logger .warning (
1028+ "Failed to resolve interaction %s for display: %s" ,
1029+ interaction_name ,
1030+ e ,
1031+ )
1032+
1033+ resolved_cases .append (case )
1034+
1035+ if any_resolved :
1036+ resolved_datasets .append (
1037+ dataset .model_copy (update = {"eval_cases" : resolved_cases })
1038+ )
1039+ else :
1040+ resolved_datasets .append (dataset )
1041+
1042+ return resolved_datasets
1043+
1044+
9261045def _add_evaluation_run_labels (
9271046 labels : Optional [dict [str , str ]] = None ,
9281047 agent : Optional [str ] = None ,
@@ -2245,6 +2364,11 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
22452364 t2 = time .perf_counter ()
22462365 logger .info ("Evaluation took: %f seconds" , t2 - t1 )
22472366
2367+ # Resolve interactions_data_source to agent_data for display.
2368+ # This fetches Interaction trace data client-side so that show() can
2369+ # render the System Topology and Conversation Trace sections.
2370+ dataset_list = _resolve_interactions_for_display (api_client , dataset_list )
2371+
22482372 evaluation_result .evaluation_dataset = dataset_list
22492373 evaluation_result .agent_info = validated_agent_info
22502374
0 commit comments