|
5 | 5 | from gooddata_eval.core.models import ChatResult, DatasetItem |
6 | 6 |
|
7 | 7 |
|
| 8 | +def _normalize_str_list(value: object, *, lowercase: bool = False) -> list[str]: |
| 9 | + # Arguments come from raw model-emitted JSON. The search_objects schema |
| 10 | + # declares keywords/object_types as list[str], but a malformed tool call may |
| 11 | + # send a non-list or non-string entries. Drop the offending entries defensively |
| 12 | + # so bad input can't raise (.lower()/sorted() on a non-str) and abort the whole |
| 13 | + # evaluation run; a non-list collapses to [] and the surviving strings are |
| 14 | + # still compared normally. |
| 15 | + if not isinstance(value, list): |
| 16 | + return [] |
| 17 | + items = [item for item in value if isinstance(item, str)] |
| 18 | + return sorted(item.lower() if lowercase else item for item in items) |
| 19 | + |
| 20 | + |
8 | 21 | def _args_match(actual_args: dict, expected_args: dict) -> bool: |
9 | | - if sorted(actual_args.get("keywords") or []) != sorted(expected_args.get("keywords") or []): |
10 | | - return False |
11 | | - if sorted(actual_args.get("object_types") or []) != sorted(expected_args.get("object_types") or []): |
12 | | - return False |
13 | | - if actual_args.get("limit") != expected_args.get("limit"): |
| 22 | + # Only keywords and object_types determine semantic correctness. |
| 23 | + # limit is optional with a server-side default; emit_widget was renamed to |
| 24 | + # user_requested_search in the tool schema — neither affects search quality. |
| 25 | + actual_kw = _normalize_str_list(actual_args.get("keywords"), lowercase=True) |
| 26 | + expected_kw = _normalize_str_list(expected_args.get("keywords"), lowercase=True) |
| 27 | + if actual_kw != expected_kw: |
14 | 28 | return False |
15 | | - return actual_args.get("emit_widget") == expected_args.get("emit_widget") |
| 29 | + # object_types is compared case-sensitively (no lowercase=True): they are |
| 30 | + # controlled ObjectType StrEnum values the model emits verbatim ("metric", |
| 31 | + # "dashboard"), so a case mismatch is a genuine error, not a formatting quirk. |
| 32 | + return _normalize_str_list(actual_args.get("object_types")) == _normalize_str_list( |
| 33 | + expected_args.get("object_types") |
| 34 | + ) |
16 | 35 |
|
17 | 36 |
|
18 | 37 | class SearchToolEvaluator: |
|
0 commit comments