| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import unittest |
| | from typing import Optional, Union |
| |
|
| | from transformers.utils import DocstringParsingException, TypeHintParsingException, get_json_schema |
| |
|
| |
|
| | class JsonSchemaGeneratorTest(unittest.TestCase): |
| | def test_simple_function(self): |
| | def fn(x: int): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": {"x": {"type": "integer", "description": "The input"}}, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_no_arguments(self): |
| | def fn(): |
| | """ |
| | Test function |
| | """ |
| | return True |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": {"type": "object", "properties": {}}, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_union(self): |
| | def fn(x: Union[int, float]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": {"x": {"type": ["integer", "number"], "description": "The input"}}, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_optional(self): |
| | def fn(x: Optional[int]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": {"x": {"type": "integer", "description": "The input", "nullable": True}}, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_default_arg(self): |
| | def fn(x: int = 42): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": {"type": "object", "properties": {"x": {"type": "integer", "description": "The input"}}}, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_nested_list(self): |
| | def fn(x: list[list[Union[str, int]]]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": { |
| | "type": "array", |
| | "items": {"type": "array", "items": {"type": ["integer", "string"]}}, |
| | "description": "The input", |
| | } |
| | }, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_multiple_arguments(self): |
| | def fn(x: int, y: str): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | y: Also the input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": {"type": "integer", "description": "The input"}, |
| | "y": {"type": "string", "description": "Also the input"}, |
| | }, |
| | "required": ["x", "y"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_multiple_complex_arguments(self): |
| | def fn(x: list[Union[int, float]], y: Optional[Union[int, str]] = None): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | y: Also the input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": {"type": "array", "items": {"type": ["integer", "number"]}, "description": "The input"}, |
| | "y": { |
| | "type": ["integer", "string"], |
| | "nullable": True, |
| | "description": "Also the input", |
| | }, |
| | }, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_missing_docstring(self): |
| | def fn(x: int): |
| | return x |
| |
|
| | with self.assertRaises(DocstringParsingException): |
| | get_json_schema(fn) |
| |
|
| | def test_missing_param_docstring(self): |
| | def fn(x: int): |
| | """ |
| | Test function |
| | """ |
| | return x |
| |
|
| | with self.assertRaises(DocstringParsingException): |
| | get_json_schema(fn) |
| |
|
| | def test_missing_type_hint(self): |
| | def fn(x): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | with self.assertRaises(TypeHintParsingException): |
| | get_json_schema(fn) |
| |
|
| | def test_return_value(self): |
| | def fn(x: int) -> int: |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": {"x": {"type": "integer", "description": "The input"}}, |
| | "required": ["x"], |
| | }, |
| | "return": {"type": "integer"}, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_return_value_docstring(self): |
| | def fn(x: int) -> int: |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | |
| | |
| | Returns: |
| | The output |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": {"x": {"type": "integer", "description": "The input"}}, |
| | "required": ["x"], |
| | }, |
| | "return": {"type": "integer", "description": "The output"}, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_tuple(self): |
| | def fn(x: tuple[int, str]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | |
| | |
| | Returns: |
| | The output |
| | """ |
| | return x |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": { |
| | "type": "array", |
| | "prefixItems": [{"type": "integer"}, {"type": "string"}], |
| | "description": "The input", |
| | } |
| | }, |
| | "required": ["x"], |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_single_element_tuple_fails(self): |
| | def fn(x: tuple[int]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | |
| | |
| | Returns: |
| | The output |
| | """ |
| | return x |
| |
|
| | |
| | with self.assertRaises(TypeHintParsingException): |
| | get_json_schema(fn) |
| |
|
| | def test_ellipsis_type_fails(self): |
| | def fn(x: tuple[int, ...]): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The input |
| | |
| | |
| | Returns: |
| | The output |
| | """ |
| | return x |
| |
|
| | |
| | with self.assertRaises(TypeHintParsingException): |
| | get_json_schema(fn) |
| |
|
| | def test_enum_extraction(self): |
| | def fn(temperature_format: str): |
| | """ |
| | Test function |
| | |
| | Args: |
| | temperature_format: The temperature format to use (Choices: ["celsius", "fahrenheit"]) |
| | |
| | |
| | Returns: |
| | The temperature |
| | """ |
| | return -40.0 |
| |
|
| | |
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "temperature_format": { |
| | "type": "string", |
| | "enum": ["celsius", "fahrenheit"], |
| | "description": "The temperature format to use", |
| | } |
| | }, |
| | "required": ["temperature_format"], |
| | }, |
| | } |
| |
|
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_multiline_docstring_with_types(self): |
| | def fn(x: int, y: int): |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The first input |
| | |
| | y: The second input. This is a longer description |
| | that spans multiple lines with indentation and stuff. |
| | |
| | Returns: |
| | God knows what |
| | """ |
| | pass |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": {"type": "integer", "description": "The first input"}, |
| | "y": { |
| | "type": "integer", |
| | "description": "The second input. This is a longer description that spans multiple lines with indentation and stuff.", |
| | }, |
| | }, |
| | "required": ["x", "y"], |
| | }, |
| | } |
| |
|
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_return_none(self): |
| | def fn(x: int) -> None: |
| | """ |
| | Test function |
| | |
| | Args: |
| | x: The first input |
| | """ |
| | pass |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": {"type": "integer", "description": "The first input"}, |
| | }, |
| | "required": ["x"], |
| | }, |
| | "return": {"type": "null"}, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|
| | def test_everything_all_at_once(self): |
| | def fn( |
| | x: str, y: Optional[list[Union[str, int]]], z: tuple[Union[str, int], str] = (42, "hello") |
| | ) -> tuple[int, str]: |
| | """ |
| | Test function with multiple args, and docstring args that we have to strip out. |
| | |
| | Args: |
| | x: The first input. It's got a big multiline |
| | description and also contains |
| | (choices: ["a", "b", "c"]) |
| | |
| | y: The second input. It's a big list with a single-line description. |
| | |
| | z: The third input. It's some kind of tuple with a default arg. |
| | |
| | Returns: |
| | The output. The return description is also a big multiline |
| | description that spans multiple lines. |
| | """ |
| | pass |
| |
|
| | schema = get_json_schema(fn) |
| | expected_schema = { |
| | "name": "fn", |
| | "description": "Test function with multiple args, and docstring args that we have to strip out.", |
| | "parameters": { |
| | "type": "object", |
| | "properties": { |
| | "x": { |
| | "type": "string", |
| | "enum": ["a", "b", "c"], |
| | "description": "The first input. It's got a big multiline description and also contains", |
| | }, |
| | "y": { |
| | "type": "array", |
| | "items": {"type": ["integer", "string"]}, |
| | "nullable": True, |
| | "description": "The second input. It's a big list with a single-line description.", |
| | }, |
| | "z": { |
| | "type": "array", |
| | "prefixItems": [{"type": ["integer", "string"]}, {"type": "string"}], |
| | "description": "The third input. It's some kind of tuple with a default arg.", |
| | }, |
| | }, |
| | "required": ["x", "y"], |
| | }, |
| | "return": { |
| | "type": "array", |
| | "prefixItems": [{"type": "integer"}, {"type": "string"}], |
| | "description": "The output. The return description is also a big multiline\n description that spans multiple lines.", |
| | }, |
| | } |
| | self.assertEqual(schema["function"], expected_schema) |
| |
|