|
25 | 25 | from vertexai import _genai |
26 | 26 | from vertexai._genai import _evals_data_converters |
27 | 27 | from vertexai._genai import _evals_metric_handlers |
| 28 | +from vertexai._genai import _observability_data_converter |
28 | 29 | from vertexai._genai import evals |
29 | 30 | from vertexai._genai import types as vertexai_genai_types |
30 | 31 | from google.genai import client |
@@ -1921,6 +1922,277 @@ def test_convert_skips_missing_request_or_response(self): |
1921 | 1922 | assert len(result_dataset.eval_cases) == 0 |
1922 | 1923 |
|
1923 | 1924 |
|
| 1925 | +class TestObservabilityDataConverter: |
| 1926 | + """Unit tests for the ObservabilityDataConverter class.""" |
| 1927 | + |
| 1928 | + def setup_method(self): |
| 1929 | + self.converter = _observability_data_converter.ObservabilityDataConverter() |
| 1930 | + |
| 1931 | + def test_convert_simple_request_response(self): |
| 1932 | + raw_data = [ |
| 1933 | + { |
| 1934 | + "format": "observability", |
| 1935 | + "request": [ |
| 1936 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]} |
| 1937 | + ], |
| 1938 | + "response": [ |
| 1939 | + { |
| 1940 | + "role": "system", |
| 1941 | + "parts": [{"content": "Hi", "type": "text"}], |
| 1942 | + } |
| 1943 | + ], |
| 1944 | + } |
| 1945 | + ] |
| 1946 | + result_dataset = self.converter.convert(raw_data) |
| 1947 | + |
| 1948 | + assert isinstance(result_dataset, vertexai_genai_types.EvaluationDataset) |
| 1949 | + assert len(result_dataset.eval_cases) == 1 |
| 1950 | + |
| 1951 | + eval_case = result_dataset.eval_cases[0] |
| 1952 | + assert eval_case.prompt == genai_types.Content( |
| 1953 | + parts=[genai_types.Part(text="Hello")], role="user" |
| 1954 | + ) |
| 1955 | + assert len(eval_case.responses) == 1 |
| 1956 | + assert eval_case.responses[0].response == genai_types.Content( |
| 1957 | + parts=[genai_types.Part(text="Hi")], role="system" |
| 1958 | + ) |
| 1959 | + assert eval_case.reference is None |
| 1960 | + assert eval_case.system_instruction is None |
| 1961 | + assert not eval_case.conversation_history |
| 1962 | + |
| 1963 | + def test_convert_with_system_instruction(self): |
| 1964 | + raw_data = [ |
| 1965 | + { |
| 1966 | + "format": "observability", |
| 1967 | + "request": [ |
| 1968 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]} |
| 1969 | + ], |
| 1970 | + "response": [ |
| 1971 | + { |
| 1972 | + "role": "system", |
| 1973 | + "parts": [{"content": "Hi", "type": "text"}], |
| 1974 | + } |
| 1975 | + ], |
| 1976 | + "system_instruction": { |
| 1977 | + "role": "user", |
| 1978 | + "parts": [{"content": "Be helpful", "type": "text"}], |
| 1979 | + }, |
| 1980 | + } |
| 1981 | + ] |
| 1982 | + result_dataset = self.converter.convert(raw_data) |
| 1983 | + eval_case = result_dataset.eval_cases[0] |
| 1984 | + assert eval_case.system_instruction == genai_types.Content( |
| 1985 | + parts=[genai_types.Part(text="Be helpful")], role="user" |
| 1986 | + ) |
| 1987 | + |
| 1988 | + def test_convert_with_conversation_history(self): |
| 1989 | + raw_data = [ |
| 1990 | + { |
| 1991 | + "format": "observability", |
| 1992 | + "request": [ |
| 1993 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]}, |
| 1994 | + {"role": "system", "parts": [{"content": "Hi", "type": "text"}]}, |
| 1995 | + { |
| 1996 | + "role": "user", |
| 1997 | + "parts": [ |
| 1998 | + {"content": "What's the meaning of life?", "type": "text"} |
| 1999 | + ], |
| 2000 | + }, |
| 2001 | + ], |
| 2002 | + "response": [ |
| 2003 | + { |
| 2004 | + "role": "system", |
| 2005 | + "parts": [{"content": "42.", "type": "text"}], |
| 2006 | + } |
| 2007 | + ], |
| 2008 | + } |
| 2009 | + ] |
| 2010 | + |
| 2011 | + result_dataset = self.converter.convert(raw_data) |
| 2012 | + eval_case = result_dataset.eval_cases[0] |
| 2013 | + |
| 2014 | + assert eval_case.prompt == genai_types.Content( |
| 2015 | + parts=[genai_types.Part(text="What's the meaning of life?")], role="user" |
| 2016 | + ) |
| 2017 | + |
| 2018 | + assert len(eval_case.conversation_history) == 2 |
| 2019 | + assert eval_case.conversation_history[0] == vertexai_genai_types.Message( |
| 2020 | + content=genai_types.Content( |
| 2021 | + parts=[genai_types.Part(text="Hello")], role="user" |
| 2022 | + ), |
| 2023 | + turn_id="0", |
| 2024 | + author="user", |
| 2025 | + ) |
| 2026 | + assert eval_case.conversation_history[1] == vertexai_genai_types.Message( |
| 2027 | + content=genai_types.Content( |
| 2028 | + parts=[genai_types.Part(text="Hi")], role="system" |
| 2029 | + ), |
| 2030 | + turn_id="1", |
| 2031 | + author="system", |
| 2032 | + ) |
| 2033 | + |
| 2034 | + def test_convert_multiple_request_response(self): |
| 2035 | + raw_data = [ |
| 2036 | + { |
| 2037 | + "format": "observability", |
| 2038 | + "request": [ |
| 2039 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]} |
| 2040 | + ], |
| 2041 | + "response": [ |
| 2042 | + { |
| 2043 | + "role": "system", |
| 2044 | + "parts": [{"content": "Hi", "type": "text"}], |
| 2045 | + } |
| 2046 | + ], |
| 2047 | + }, |
| 2048 | + { |
| 2049 | + "format": "observability", |
| 2050 | + "request": [ |
| 2051 | + {"role": "user", "parts": [{"content": "Goodbye", "type": "text"}]} |
| 2052 | + ], |
| 2053 | + "response": [ |
| 2054 | + { |
| 2055 | + "role": "system", |
| 2056 | + "parts": [{"content": "Bye", "type": "text"}], |
| 2057 | + } |
| 2058 | + ], |
| 2059 | + }, |
| 2060 | + ] |
| 2061 | + result_dataset = self.converter.convert(raw_data) |
| 2062 | + |
| 2063 | + assert isinstance(result_dataset, vertexai_genai_types.EvaluationDataset) |
| 2064 | + assert len(result_dataset.eval_cases) == 2 |
| 2065 | + |
| 2066 | + eval_case = result_dataset.eval_cases[0] |
| 2067 | + assert eval_case.prompt == genai_types.Content( |
| 2068 | + parts=[genai_types.Part(text="Hello")], role="user" |
| 2069 | + ) |
| 2070 | + assert eval_case.responses[0].response == genai_types.Content( |
| 2071 | + parts=[genai_types.Part(text="Hi")], role="system" |
| 2072 | + ) |
| 2073 | + |
| 2074 | + eval_case = result_dataset.eval_cases[1] |
| 2075 | + assert eval_case.prompt == genai_types.Content( |
| 2076 | + parts=[genai_types.Part(text="Goodbye")], role="user" |
| 2077 | + ) |
| 2078 | + assert eval_case.responses[0].response == genai_types.Content( |
| 2079 | + parts=[genai_types.Part(text="Bye")], role="system" |
| 2080 | + ) |
| 2081 | + |
| 2082 | + def test_convert_skips_unknown_part_type(self): |
| 2083 | + raw_data = [ |
| 2084 | + { |
| 2085 | + "format": "observability", |
| 2086 | + "request": [ |
| 2087 | + { |
| 2088 | + "role": "user", |
| 2089 | + "parts": [ |
| 2090 | + {"content": 123, "type": ""}, |
| 2091 | + {"content": 456}, |
| 2092 | + {"content": "Hello", "type": "text"}, |
| 2093 | + ], |
| 2094 | + } |
| 2095 | + ], |
| 2096 | + "response": [ |
| 2097 | + { |
| 2098 | + "role": "system", |
| 2099 | + "parts": [{"content": "Hi", "type": "text"}], |
| 2100 | + } |
| 2101 | + ], |
| 2102 | + } |
| 2103 | + ] |
| 2104 | + |
| 2105 | + result_dataset = self.converter.convert(raw_data) |
| 2106 | + eval_case = result_dataset.eval_cases[0] |
| 2107 | + |
| 2108 | + assert eval_case.prompt == genai_types.Content( |
| 2109 | + parts=[genai_types.Part(text="Hello")], role="user" |
| 2110 | + ) |
| 2111 | + |
| 2112 | + def test_convert_skips_missing_request(self): |
| 2113 | + raw_data = [ |
| 2114 | + { |
| 2115 | + "format": "observability", |
| 2116 | + "response": [ |
| 2117 | + { |
| 2118 | + "role": "system", |
| 2119 | + "parts": [{"content": "Hi", "type": "text"}], |
| 2120 | + } |
| 2121 | + ], |
| 2122 | + } |
| 2123 | + ] |
| 2124 | + result_dataset = self.converter.convert(raw_data) |
| 2125 | + assert not result_dataset.eval_cases |
| 2126 | + |
| 2127 | + def test_convert_skips_missing_response(self): |
| 2128 | + raw_data = [ |
| 2129 | + { |
| 2130 | + "format": "observability", |
| 2131 | + "request": [ |
| 2132 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]} |
| 2133 | + ], |
| 2134 | + } |
| 2135 | + ] |
| 2136 | + result_dataset = self.converter.convert(raw_data) |
| 2137 | + assert not result_dataset.eval_cases |
| 2138 | + |
| 2139 | + def test_convert_tool_call_parts(self): |
| 2140 | + raw_data = [ |
| 2141 | + { |
| 2142 | + "format": "observability", |
| 2143 | + "request": [ |
| 2144 | + { |
| 2145 | + "role": "user", |
| 2146 | + "parts": [ |
| 2147 | + { |
| 2148 | + "type": "tool_call", |
| 2149 | + "id": "tool_id", |
| 2150 | + "name": "tool_name", |
| 2151 | + "arguments": {"param": "1"}, |
| 2152 | + } |
| 2153 | + ], |
| 2154 | + } |
| 2155 | + ], |
| 2156 | + "response": [ |
| 2157 | + { |
| 2158 | + "role": "system", |
| 2159 | + "parts": [ |
| 2160 | + { |
| 2161 | + "type": "tool_call_response", |
| 2162 | + "id": "tool_id", |
| 2163 | + "result": {"field": "2"}, |
| 2164 | + } |
| 2165 | + ], |
| 2166 | + } |
| 2167 | + ], |
| 2168 | + } |
| 2169 | + ] |
| 2170 | + result_dataset = self.converter.convert(raw_data) |
| 2171 | + |
| 2172 | + eval_case = result_dataset.eval_cases[0] |
| 2173 | + assert eval_case.prompt == genai_types.Content( |
| 2174 | + parts=[ |
| 2175 | + genai_types.Part( |
| 2176 | + function_call=genai_types.FunctionCall( |
| 2177 | + id="tool_id", name="tool_id", args={"param": "1"} |
| 2178 | + ) |
| 2179 | + ) |
| 2180 | + ], |
| 2181 | + role="user", |
| 2182 | + ) |
| 2183 | + assert len(eval_case.responses) == 1 |
| 2184 | + assert eval_case.responses[0].response == genai_types.Content( |
| 2185 | + parts=[ |
| 2186 | + genai_types.Part( |
| 2187 | + function_response=genai_types.FunctionResponse( |
| 2188 | + id="tool_id", name="tool_id", response={"field": "2"} |
| 2189 | + ) |
| 2190 | + ) |
| 2191 | + ], |
| 2192 | + role="system", |
| 2193 | + ) |
| 2194 | + |
| 2195 | + |
1924 | 2196 | class TestMetric: |
1925 | 2197 | """Unit tests for the Metric class.""" |
1926 | 2198 |
|
@@ -2953,6 +3225,26 @@ def test_auto_detect_openai_schema(self): |
2953 | 3225 | == _evals_data_converters.EvalDatasetSchema.OPENAI |
2954 | 3226 | ) |
2955 | 3227 |
|
| 3228 | + def test_auto_detect_observability_schema(self): |
| 3229 | + raw_data = [ |
| 3230 | + { |
| 3231 | + "format": "observability", |
| 3232 | + "request": [ |
| 3233 | + {"role": "user", "parts": [{"content": "Hello", "type": "text"}]} |
| 3234 | + ], |
| 3235 | + "response": [ |
| 3236 | + { |
| 3237 | + "role": "system", |
| 3238 | + "parts": [{"content": "Hi", "type": "text"}], |
| 3239 | + } |
| 3240 | + ], |
| 3241 | + } |
| 3242 | + ] |
| 3243 | + assert ( |
| 3244 | + _evals_data_converters.auto_detect_dataset_schema(raw_data) |
| 3245 | + == _evals_data_converters.EvalDatasetSchema.OBSERVABILITY |
| 3246 | + ) |
| 3247 | + |
2956 | 3248 | def test_auto_detect_unknown_schema(self): |
2957 | 3249 | raw_data = [{"foo": "bar"}] |
2958 | 3250 | assert ( |
|
0 commit comments