|
64 | 64 |
|
65 | 65 | MAX_WORKERS = 100 |
66 | 66 | AGENT_MAX_WORKERS = 20 |
| 67 | +_MAX_INTERACTION_CHAIN_DEPTH = 10 |
67 | 68 | CONTENT = _evals_constant.CONTENT |
68 | 69 | PARTS = _evals_constant.PARTS |
69 | 70 | USER_AUTHOR = _evals_constant.USER_AUTHOR |
@@ -1007,6 +1008,137 @@ def _build_interaction_id_dataset( |
1007 | 1008 | return types.EvaluationDataset(eval_cases=eval_cases) |
1008 | 1009 |
|
1009 | 1010 |
|
| 1011 | +def _resolve_interactions_for_display( |
| 1012 | + api_client: BaseApiClient, |
| 1013 | + dataset_list: list[types.EvaluationDataset], |
| 1014 | +) -> list[types.EvaluationDataset]: |
| 1015 | + """Resolves interactions_data_source on EvalCases for display. |
| 1016 | +
|
| 1017 | + For each EvalCase that has ``interactions_data_source`` set but no |
| 1018 | + ``agent_data``, fetches the Interaction and Agent config from the |
| 1019 | + API, converts to AgentData, and populates it on the EvalCase so |
| 1020 | + that ``show()`` can render the System Topology and Conversation |
| 1021 | + Trace sections. |
| 1022 | +
|
| 1023 | + Args: |
| 1024 | + api_client: The API client used to fetch interactions and agents. |
| 1025 | + dataset_list: The original evaluation datasets. |
| 1026 | +
|
| 1027 | + Returns: |
| 1028 | + A list of EvaluationDatasets with agent_data populated from |
| 1029 | + resolved interactions. |
| 1030 | + """ |
| 1031 | + resolved_datasets = [] |
| 1032 | + for dataset in dataset_list: |
| 1033 | + if not dataset.eval_cases: |
| 1034 | + resolved_datasets.append(dataset) |
| 1035 | + continue |
| 1036 | + |
| 1037 | + resolved_cases = [] |
| 1038 | + any_resolved = False |
| 1039 | + for case in dataset.eval_cases: |
| 1040 | + ids = case.interactions_data_source |
| 1041 | + if ids and not case.agent_data: |
| 1042 | + interaction_name = ids.interaction |
| 1043 | + gemini_cfg = ids.gemini_agent_config |
| 1044 | + agent_name = gemini_cfg.gemini_agent if gemini_cfg else None |
| 1045 | + |
| 1046 | + if interaction_name: |
| 1047 | + try: |
| 1048 | + # Extract the interaction short ID from the resource |
| 1049 | + # name; handles both full resource names and bare IDs. |
| 1050 | + interaction_id = interaction_name.split("/")[-1] |
| 1051 | + |
| 1052 | + logger.info( |
| 1053 | + "Fetching interaction chain for %s for display.", |
| 1054 | + interaction_name, |
| 1055 | + ) |
| 1056 | + current_interaction_id = interaction_id |
| 1057 | + interactions = [] |
| 1058 | + seen_ids = set() |
| 1059 | + for _ in range(_MAX_INTERACTION_CHAIN_DEPTH): |
| 1060 | + if current_interaction_id in seen_ids: |
| 1061 | + break |
| 1062 | + seen_ids.add(current_interaction_id) |
| 1063 | + path = f"interactions/{current_interaction_id}" |
| 1064 | + response = api_client.request("get", path, {}, None) |
| 1065 | + if not response.body: |
| 1066 | + if not interactions: |
| 1067 | + logger.warning( |
| 1068 | + "Empty response fetching interaction %s.", |
| 1069 | + interaction_name, |
| 1070 | + ) |
| 1071 | + break |
| 1072 | + interaction_dict = json.loads(response.body) |
| 1073 | + try: |
| 1074 | + typed_interaction = ( |
| 1075 | + interaction_types.Interaction.model_validate( |
| 1076 | + interaction_dict |
| 1077 | + ) |
| 1078 | + ) |
| 1079 | + except Exception as e: |
| 1080 | + logger.warning( |
| 1081 | + "Failed to validate interaction model: %s", e |
| 1082 | + ) |
| 1083 | + break |
| 1084 | + |
| 1085 | + interactions.append(typed_interaction) |
| 1086 | + if not typed_interaction.previous_interaction_id: |
| 1087 | + break |
| 1088 | + current_interaction_id = ( |
| 1089 | + typed_interaction.previous_interaction_id.split("/")[-1] |
| 1090 | + ) |
| 1091 | + |
| 1092 | + if not interactions: |
| 1093 | + resolved_cases.append(case) |
| 1094 | + continue |
| 1095 | + |
| 1096 | + interactions.reverse() # chronological order |
| 1097 | + all_steps = [] |
| 1098 | + for i_typed in interactions: |
| 1099 | + all_steps.extend(i_typed.steps or []) |
| 1100 | + |
| 1101 | + combined_interaction = interactions[-1].model_dump() |
| 1102 | + combined_interaction["steps"] = all_steps |
| 1103 | + agent_data = _interaction_dict_to_agent_data( |
| 1104 | + combined_interaction |
| 1105 | + ) |
| 1106 | + |
| 1107 | + # Best-effort: fetch agent config (instruction, |
| 1108 | + # tools, description) from the Agent API. |
| 1109 | + agent_config = _fetch_agent_config_dict( |
| 1110 | + api_client, agent_name or "" |
| 1111 | + ) |
| 1112 | + agent_data.agents = {agent_config.agent_id: agent_config} |
| 1113 | + |
| 1114 | + _merge_text_parts_in_agent_data(agent_data) |
| 1115 | + |
| 1116 | + # Preserve all original EvalCase fields; only |
| 1117 | + # update agent_data. |
| 1118 | + resolved_cases.append( |
| 1119 | + case.model_copy(update={"agent_data": agent_data}) |
| 1120 | + ) |
| 1121 | + any_resolved = True |
| 1122 | + continue |
| 1123 | + except Exception as e: # pylint: disable=broad-exception-caught |
| 1124 | + logger.warning( |
| 1125 | + "Failed to resolve interaction %s for display: %s", |
| 1126 | + interaction_name, |
| 1127 | + e, |
| 1128 | + ) |
| 1129 | + |
| 1130 | + resolved_cases.append(case) |
| 1131 | + |
| 1132 | + if any_resolved: |
| 1133 | + resolved_datasets.append( |
| 1134 | + dataset.model_copy(update={"eval_cases": resolved_cases}) |
| 1135 | + ) |
| 1136 | + else: |
| 1137 | + resolved_datasets.append(dataset) |
| 1138 | + |
| 1139 | + return resolved_datasets |
| 1140 | + |
| 1141 | + |
1010 | 1142 | def _add_evaluation_run_labels( |
1011 | 1143 | labels: Optional[dict[str, str]] = None, |
1012 | 1144 | agent: Optional[str] = None, |
@@ -2349,6 +2481,11 @@ def _execute_evaluation( # type: ignore[no-untyped-def] |
2349 | 2481 | t2 = time.perf_counter() |
2350 | 2482 | logger.info("Evaluation took: %f seconds", t2 - t1) |
2351 | 2483 |
|
| 2484 | + # Resolve interactions_data_source to agent_data for display. |
| 2485 | + # This fetches Interaction trace data client-side so that show() can |
| 2486 | + # render the System Topology and Conversation Trace sections. |
| 2487 | + dataset_list = _resolve_interactions_for_display(api_client, dataset_list) |
| 2488 | + |
2352 | 2489 | evaluation_result.evaluation_dataset = dataset_list |
2353 | 2490 | evaluation_result.agent_info = validated_agent_info |
2354 | 2491 |
|
|
0 commit comments