|
24 | 24 | import vertexai |
25 | 25 | from google.cloud.aiplatform import initializer as aiplatform_initializer |
26 | 26 | from vertexai import _genai |
| 27 | +from vertexai._genai import _evals_metric_handlers |
27 | 28 | from vertexai._genai import _evals_data_converters |
28 | 29 | from vertexai._genai import evals |
29 | 30 | from vertexai._genai import types as vertexai_genai_types |
@@ -1946,6 +1947,185 @@ def test_merge_with_invalid_eval_case_type(self): |
1946 | 1947 | schemas=[_evals_data_converters._EvalDatasetSchema.FLATTEN], |
1947 | 1948 | ) |
1948 | 1949 |
|
| 1950 | + |
| 1951 | +@pytest.mark.usefixtures("google_auth_mock") |
| 1952 | +class TestLLMMetricHandlerPayload: |
| 1953 | + def setup_method(self): |
| 1954 | + importlib.reload(aiplatform_initializer) |
| 1955 | + importlib.reload(aiplatform) |
| 1956 | + importlib.reload(vertexai) |
| 1957 | + importlib.reload(genai_types) |
| 1958 | + importlib.reload(vertexai_genai_types) |
| 1959 | + importlib.reload(_evals_data_converters) |
| 1960 | + importlib.reload(_evals_metric_handlers) |
| 1961 | + |
| 1962 | + vertexai.init(project=_TEST_PROJECT, location=_TEST_LOCATION) |
| 1963 | + self.mock_api_client = mock.Mock(spec=client.Client) |
| 1964 | + self.mock_evals_module = evals.Evals(api_client_=self.mock_api_client) |
| 1965 | + |
| 1966 | + def test_build_request_payload_basic_filtering_and_fields(self): |
| 1967 | + metric = vertexai_genai_types.LLMMetric( |
| 1968 | + name="test_quality", |
| 1969 | + prompt_template="Eval: {prompt} with {response}. Context: " |
| 1970 | + "{custom_context}. Ref: {reference}", |
| 1971 | + ) |
| 1972 | + handler = _evals_metric_handlers.LLMMetricHandler( |
| 1973 | + module=self.mock_evals_module, metric=metric |
| 1974 | + ) |
| 1975 | + |
| 1976 | + eval_case = vertexai_genai_types.EvalCase( |
| 1977 | + prompt=genai_types.Content( |
| 1978 | + parts=[genai_types.Part(text="User prompt text")] |
| 1979 | + ), |
| 1980 | + responses=[ |
| 1981 | + vertexai_genai_types.ResponseCandidate( |
| 1982 | + response=genai_types.Content( |
| 1983 | + parts=[genai_types.Part(text="Model response text")] |
| 1984 | + ) |
| 1985 | + ) |
| 1986 | + ], |
| 1987 | + reference=vertexai_genai_types.ResponseCandidate( |
| 1988 | + response=genai_types.Content( |
| 1989 | + parts=[genai_types.Part(text="Ground truth text")] |
| 1990 | + ) |
| 1991 | + ), |
| 1992 | + custom_context="Custom context value.", # pylint: disable=unexpected-keyword-arg |
| 1993 | + extra_field_not_in_template="This should be excluded.", # pylint: disable=unexpected-keyword-arg |
| 1994 | + eval_case_id="case-123", |
| 1995 | + ) |
| 1996 | + |
| 1997 | + payload = handler._build_request_payload(eval_case=eval_case, response_index=0) |
| 1998 | + |
| 1999 | + expected_json_instance_dict = { |
| 2000 | + "prompt": "User prompt text", |
| 2001 | + "response": "Model response text", |
| 2002 | + "custom_context": "Custom context value.", |
| 2003 | + "reference": "Ground truth text", |
| 2004 | + } |
| 2005 | + |
| 2006 | + actual_json_instance_str = payload["pointwise_metric_input"]["instance"][ |
| 2007 | + "json_instance" |
| 2008 | + ] |
| 2009 | + actual_json_instance_dict = json.loads(actual_json_instance_str) |
| 2010 | + |
| 2011 | + assert actual_json_instance_dict == expected_json_instance_dict |
| 2012 | + assert "extra_field_not_in_template" not in actual_json_instance_dict |
| 2013 | + assert "eval_case_id" not in actual_json_instance_dict |
| 2014 | + |
| 2015 | + assert ( |
| 2016 | + "custom_output_format_config" |
| 2017 | + not in payload["pointwise_metric_input"]["metric_spec"] |
| 2018 | + ) |
| 2019 | + assert ( |
| 2020 | + "system_instruction" not in payload["pointwise_metric_input"]["metric_spec"] |
| 2021 | + ) |
| 2022 | + assert "autorater_config" not in payload |
| 2023 | + |
| 2024 | + def test_build_request_payload_various_field_types(self): |
| 2025 | + metric = vertexai_genai_types.LLMMetric( |
| 2026 | + name="complex_eval", |
| 2027 | + prompt_template=( |
| 2028 | + "P: {prompt}, R: {response}, Hist: {conversation_history}, " |
| 2029 | + "SysInstruct: {system_instruction}, " |
| 2030 | + "DictField: {dict_field}, ListField: {list_field}, " |
| 2031 | + "IntField: {int_field}, BoolField: {bool_field}" |
| 2032 | + ), |
| 2033 | + ) |
| 2034 | + handler = _evals_metric_handlers.LLMMetricHandler( |
| 2035 | + module=self.mock_evals_module, metric=metric |
| 2036 | + ) |
| 2037 | + |
| 2038 | + eval_case = vertexai_genai_types.EvalCase( |
| 2039 | + prompt=genai_types.Content(parts=[genai_types.Part(text="The Prompt")]), |
| 2040 | + responses=[ |
| 2041 | + vertexai_genai_types.ResponseCandidate( |
| 2042 | + response=genai_types.Content( |
| 2043 | + parts=[genai_types.Part(text="The Response")] |
| 2044 | + ) |
| 2045 | + ) |
| 2046 | + ], |
| 2047 | + conversation_history=[ |
| 2048 | + vertexai_genai_types.Message( |
| 2049 | + content=genai_types.Content( |
| 2050 | + parts=[genai_types.Part(text="Turn 1 user")], role="user" |
| 2051 | + ) |
| 2052 | + ), |
| 2053 | + vertexai_genai_types.Message( |
| 2054 | + content=genai_types.Content( |
| 2055 | + parts=[genai_types.Part(text="Turn 1 model")], role="model" |
| 2056 | + ) |
| 2057 | + ), |
| 2058 | + ], |
| 2059 | + system_instruction=genai_types.Content( |
| 2060 | + parts=[genai_types.Part(text="System instructions here.")] |
| 2061 | + ), |
| 2062 | + dict_field={ # pylint: disable=unexpected-keyword-arg |
| 2063 | + "key1": "val1", |
| 2064 | + "key2": [1, 2], |
| 2065 | + }, |
| 2066 | + list_field=["a", "b", {"c": 3}], # pylint: disable=unexpected-keyword-arg |
| 2067 | + int_field=42, # pylint: disable=unexpected-keyword-arg |
| 2068 | + bool_field=True, # pylint: disable=unexpected-keyword-arg |
| 2069 | + ) |
| 2070 | + |
| 2071 | + payload = handler._build_request_payload(eval_case=eval_case, response_index=0) |
| 2072 | + actual_json_instance_dict = json.loads( |
| 2073 | + payload["pointwise_metric_input"]["instance"]["json_instance"] |
| 2074 | + ) |
| 2075 | + |
| 2076 | + expected_json_instance_dict = { |
| 2077 | + "prompt": "The Prompt", |
| 2078 | + "response": "The Response", |
| 2079 | + "conversation_history": "user: Turn 1 user\nmodel: Turn 1 model", |
| 2080 | + "system_instruction": "System instructions here.", |
| 2081 | + "dict_field": json.dumps({"key1": "val1", "key2": [1, 2]}), |
| 2082 | + "list_field": json.dumps(["a", "b", {"c": 3}]), |
| 2083 | + "int_field": "42", |
| 2084 | + "bool_field": "True", |
| 2085 | + } |
| 2086 | + assert actual_json_instance_dict == expected_json_instance_dict |
| 2087 | + |
| 2088 | + def test_build_request_payload_optional_metric_configs_set(self): |
| 2089 | + metric = vertexai_genai_types.LLMMetric( |
| 2090 | + name="configured_metric", |
| 2091 | + prompt_template="P: {prompt}, R: {response}", |
| 2092 | + return_raw_output=True, |
| 2093 | + judge_model_system_instruction="Be a fair judge.", |
| 2094 | + judge_model="gemini-pro", |
| 2095 | + judge_model_sampling_count=10, |
| 2096 | + ) |
| 2097 | + handler = _evals_metric_handlers.LLMMetricHandler( |
| 2098 | + module=self.mock_evals_module, metric=metric |
| 2099 | + ) |
| 2100 | + eval_case = vertexai_genai_types.EvalCase( |
| 2101 | + prompt=genai_types.Content(parts=[genai_types.Part(text="p")]), |
| 2102 | + responses=[ |
| 2103 | + vertexai_genai_types.ResponseCandidate( |
| 2104 | + response=genai_types.Content(parts=[genai_types.Part(text="r")]) |
| 2105 | + ) |
| 2106 | + ], |
| 2107 | + ) |
| 2108 | + |
| 2109 | + payload = handler._build_request_payload(eval_case=eval_case, response_index=0) |
| 2110 | + |
| 2111 | + expected_json_instance = {"prompt": "p", "response": "r"} |
| 2112 | + actual_json_instance = json.loads( |
| 2113 | + payload["pointwise_metric_input"]["instance"]["json_instance"] |
| 2114 | + ) |
| 2115 | + assert actual_json_instance == expected_json_instance |
| 2116 | + |
| 2117 | + metric_spec_payload = payload["pointwise_metric_input"]["metric_spec"] |
| 2118 | + assert ( |
| 2119 | + metric_spec_payload["metric_prompt_template"] |
| 2120 | + == "P: {prompt}, R: {response}" |
| 2121 | + ) |
| 2122 | + assert metric_spec_payload["custom_output_format_config"]["return_raw_output"] |
| 2123 | + assert metric_spec_payload["system_instruction"] == "Be a fair judge." |
| 2124 | + |
| 2125 | + autorater_config_payload = payload["autorater_config"] |
| 2126 | + assert autorater_config_payload["autorater_model"] == "gemini-pro" |
| 2127 | + assert autorater_config_payload["sampling_count"] == 10 |
| 2128 | + |
1949 | 2129 | def test_merge_with_invalid_prompt_type(self): |
1950 | 2130 | raw_dataset_1 = [ |
1951 | 2131 | { |
|
0 commit comments