|
24 | 24 | EVAL_SETS_DIRECTORY_NAME = "evaluations/eval-sets" |
25 | 25 |
|
26 | 26 |
|
| 27 | +def _apply_file_overrides_to_conversational_inputs( |
| 28 | + conversational_inputs: Any, |
| 29 | + overrides: dict[str, Any], |
| 30 | +) -> None: |
| 31 | + """Apply file overrides to conversational input attachments before mapper conversion. |
| 32 | +
|
| 33 | + Extracts file objects from override values (single dict or array), matches them |
| 34 | + to existing attachments by FullName, and replaces attachment fields in-place. |
| 35 | + No-op if there are no file overrides or no matching attachments. |
| 36 | + """ |
| 37 | + if not overrides: |
| 38 | + return |
| 39 | + |
| 40 | + file_overrides: list[dict[str, Any]] = [] |
| 41 | + for value in overrides.values(): |
| 42 | + if isinstance(value, list): |
| 43 | + file_overrides.extend(f for f in value if isinstance(f, dict) and "ID" in f) |
| 44 | + elif isinstance(value, dict) and "ID" in value: |
| 45 | + file_overrides.append(value) |
| 46 | + |
| 47 | + if not file_overrides: |
| 48 | + return |
| 49 | + |
| 50 | + override_by_name = {f["FullName"]: f for f in file_overrides if "FullName" in f} |
| 51 | + |
| 52 | + def _override_attachments(attachments: list[Any] | None) -> None: |
| 53 | + if not attachments: |
| 54 | + return |
| 55 | + for attachment in attachments: |
| 56 | + override = override_by_name.get(attachment.full_name) |
| 57 | + if override: |
| 58 | + attachment.id = override["ID"] |
| 59 | + if "FullName" in override: |
| 60 | + attachment.full_name = override["FullName"] |
| 61 | + if "MimeType" in override: |
| 62 | + attachment.mime_type = override["MimeType"] |
| 63 | + |
| 64 | + _override_attachments(conversational_inputs.current_user_prompt.attachments) |
| 65 | + |
| 66 | + for exchange in conversational_inputs.conversation_history: |
| 67 | + for message in exchange: |
| 68 | + if hasattr(message, "attachments"): |
| 69 | + _override_attachments(message.attachments) |
| 70 | + |
| 71 | + |
27 | 72 | def discriminate_eval_set(data: dict[str, Any]) -> EvaluationSet | LegacyEvaluationSet: |
28 | 73 | """Discriminate and parse evaluation set based on version field. |
29 | 74 |
|
@@ -90,13 +135,19 @@ def auto_discover_eval_set() -> str: |
90 | 135 |
|
91 | 136 | @staticmethod |
92 | 137 | def load_eval_set( |
93 | | - eval_set_path: str, eval_ids: list[str] | None = None |
| 138 | + eval_set_path: str, |
| 139 | + eval_ids: list[str] | None = None, |
| 140 | + input_overrides: dict[str, Any] | None = None, |
94 | 141 | ) -> tuple[EvaluationSet, str]: |
95 | 142 | """Load the evaluation set from file. |
96 | 143 |
|
97 | 144 | Args: |
98 | 145 | eval_set_path: Path to the evaluation set file |
99 | 146 | eval_ids: Optional list of evaluation IDs to filter |
| 147 | + input_overrides: Optional input field overrides per evaluation ID. |
| 148 | + For conversational agents, file overrides are applied to attachments |
| 149 | + before the legacy-to-messages conversion so that overridden IDs |
| 150 | + are baked into the messages before mapping. |
100 | 151 |
|
101 | 152 | Returns: |
102 | 153 | Tuple of (EvaluationSet, resolved_path) |
@@ -147,6 +198,16 @@ def migrate_evaluation_item( |
147 | 198 | ) |
148 | 199 |
|
149 | 200 | if evaluation.conversational_inputs: |
| 201 | + overrides_for_eval = ( |
| 202 | + input_overrides.get(evaluation.id, {}) |
| 203 | + if input_overrides |
| 204 | + else {} |
| 205 | + ) |
| 206 | + _apply_file_overrides_to_conversational_inputs( |
| 207 | + evaluation.conversational_inputs, |
| 208 | + overrides_for_eval, |
| 209 | + ) |
| 210 | + |
150 | 211 | conversational_messages_input = UiPathLegacyEvalChatMessagesMapper.legacy_conversational_eval_input_to_uipath_message_list( |
151 | 212 | evaluation.conversational_inputs |
152 | 213 | ) |
|
0 commit comments