Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/humanloop/decorators/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def _build_function_parameters_property(func) -> _JSONSchemaFunctionParameters:
)


if sys.version_info >= (3, 10):
if sys.version_info >= (3, 11):
_PRIMITIVE_TYPES = Union[
str,
int,
Expand All @@ -223,7 +223,7 @@ def _build_function_parameters_property(func) -> _JSONSchemaFunctionParameters:
Ellipsis, # type: ignore
]
else:
# Ellipsis not supported in typing module before Python 3.10
# Ellipsis not supported as type before Python 3.11
_PRIMITIVE_TYPES = Union[
str,
int,
Expand Down
179 changes: 87 additions & 92 deletions tests/decorators/test_tool_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,106 +463,101 @@ def calculator(operation: str, num1: float, num2: float) -> float:
assert hl_file_higher_order_fn["tool"]["source_code"] == hl_file_decorated_fn["tool"]["source_code"] # type: ignore


def test_python310_syntax(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
if sys.version_info < (3, 10):
pytest.skip("Requires Python 3.10")
# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration
if sys.version_info >= (3, 10):
# Testing that function parsing for Tool decorator
# works with Python 3.10 and above syntax

# GIVEN a function annotated with @tool where a parameter uses `|` for Optional
@tool(opentelemetry_tracer=tracer)
def calculator(a: float, b: float | None = None) -> float:
# NOTE: dummy function, only testing its signature not correctness
if a is None:
a = 0
return a + b # type: ignore
def test_python310_syntax(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
"a": {"type": "number"},
"b": {"type": ["number", "null"]},
# GIVEN a function annotated with @tool where a parameter uses `|` for Optional
@tool(opentelemetry_tracer=tracer)
def calculator(a: float, b: float | None = None) -> float:
# NOTE: dummy function, only testing its signature not correctness
if a is None:
a = 0
return a + b # type: ignore

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
"a": {"type": "number"},
"b": {"type": ["number", "null"]},
},
"required": ("a",),
"type": "object",
"additionalProperties": False,
},
"required": ("a",),
"type": "object",
"additionalProperties": False,
},
"strict": True,
}

Validator.check_schema(calculator.json_schema)


def test_python310_union_syntax(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
if sys.version_info < (3, 10):
pytest.skip("Requires Python 3.10")
"strict": True,
}

# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration
Validator.check_schema(calculator.json_schema)

# GIVEN a function annotated with @tool where a parameter uses `|` for Union
@tool(opentelemetry_tracer=tracer)
def calculator(a: float, b: float | int | str) -> float:
# NOTE: dummy function, only testing its signature not correctness
return a + b # type: ignore
def test_python310_union_syntax(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
"a": {"type": "number"},
"b": {"anyOf": [{"type": "number"}, {"type": "integer"}, {"type": "string"}]},
# GIVEN a function annotated with @tool where a parameter uses `|` for Union
@tool(opentelemetry_tracer=tracer)
def calculator(a: float, b: float | int | str) -> float:
# NOTE: dummy function, only testing its signature not correctness
return a + b # type: ignore

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
"a": {"type": "number"},
"b": {"anyOf": [{"type": "number"}, {"type": "integer"}, {"type": "string"}]},
},
"required": ("a", "b"),
"type": "object",
"additionalProperties": False,
},
"required": ("a", "b"),
"type": "object",
"additionalProperties": False,
},
"strict": True,
}

Validator.check_schema(calculator.json_schema)

"strict": True,
}

def test_python_list_ellipsis(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
if sys.version_info < (3, 10):
pytest.skip("Requires Python 3.10")
# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration
Validator.check_schema(calculator.json_schema)

# GIVEN a function annotated with @tool where a parameter uses `...`
@tool(opentelemetry_tracer=tracer)
def calculator(b: ...) -> float | None: # type: ignore
# NOTE: dummy function, only testing its signature not correctness
if isinstance(b, list):
return sum(b)
return None
def test_python_list_ellipsis(
opentelemetry_test_configuration: tuple[Tracer, InMemorySpanExporter],
):
# GIVEN an OTel configuration
tracer, _ = opentelemetry_test_configuration

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
# THEN b is of any type
"b": {"type": ["string", "integer", "number", "boolean", "object", "array", "null"]},
# GIVEN a function annotated with @tool where a parameter uses `...`
@tool(opentelemetry_tracer=tracer)
def calculator(b: ...) -> float | None: # type: ignore
# NOTE: dummy function, only testing its signature not correctness
if isinstance(b, list):
return sum(b)
return None

# WHEN building the Tool kernel
# THEN the JSON schema is correct
assert calculator.json_schema == {
"description": "",
"name": "calculator",
"parameters": {
"properties": {
# THEN b is of any type
"b": {"type": ["string", "integer", "number", "boolean", "object", "array", "null"]},
},
"required": ("b",),
"type": "object",
"additionalProperties": False,
},
"required": ("b",),
"type": "object",
"additionalProperties": False,
},
"strict": True,
}
"strict": True,
}
Loading