Skip to content

Commit d9e16b8

Browse files
authored
Merge pull request #5 from comfuture/literal-type
drop "integer" in schema type
2 parents 6a50a38 + d366d0c commit d9e16b8

File tree

8 files changed

+159
-56
lines changed

8 files changed

+159
-56
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: ["3.9", "3.10", "3.11"]
19+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2020

2121
steps:
22-
- uses: actions/checkout@v3
22+
- uses: actions/checkout@v4
2323
- name: Set up Python ${{ matrix.python-version }}
24-
uses: actions/setup-python@v3
24+
uses: actions/setup-python@v4
2525
with:
2626
python-version: ${{ matrix.python-version }}
27+
- name: Set up uv
28+
run: curl -LsSf https://astral.sh/uv/0.3.2/install.sh | sh
2729
- name: Install dependencies
28-
run: |
29-
python -m pip install pytest flit
30-
flit install -s
30+
run: uv sync --all-extras --dev
3131
- name: run test
32-
run: pytest
32+
run: uv run pytest

.github/workflows/python-publish.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,15 @@ jobs:
2121
runs-on: ubuntu-latest
2222

2323
steps:
24-
- uses: actions/checkout@v3
24+
- uses: actions/checkout@v4
2525
- name: Set up Python
26-
uses: actions/setup-python@v3
26+
uses: actions/setup-python@v4
2727
with:
2828
python-version: '3.x'
29+
- name: Set up uv
30+
run: curl -LsSf https://astral.sh/uv/0.3.2/install.sh | sh
2931
- name: Install dependencies
30-
run: |
31-
python -m pip install --upgrade pip
32-
pip install build
33-
- name: Build package
34-
run: python -m build
32+
run: uvx --from build pyproject-build --installer uv
3533
- name: Publish package
3634
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
3735
with:

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,23 @@ schema = get_function_schema(get_weather, "claude")
8989

9090
Please refer to the [Claude tool use](https://docs.anthropic.com/claude/docs/tool-use) documentation for more information.
9191

92+
### Literal types can be used as Enum
93+
94+
```python
95+
def get_weather(
96+
city: Annotated[str, "The city to get the weather for"],
97+
unit: Annotated[
98+
Optional[Literal["celcius", "fahrenheit"]], # <- Literal type represents Enum
99+
"The unit to return the temperature in",
100+
] = "celcius",
101+
) -> str:
102+
"""Returns the weather for the given city."""
103+
return f"Weather for {city} is 20°C"
104+
```
105+
The schema will be generated as the same as the previous example.
106+
107+
### Usage with OpenAI API
108+
92109
You can use this schema to make a function call in OpenAI API:
93110
```python
94111
import openai
@@ -118,12 +135,15 @@ result = openai.chat.completion.create(
118135
messages=[
119136
{"role": "user", "content": "What's the weather like in Seoul?"}
120137
],
121-
tools=[get_function_schema(get_weather)],
138+
tools=[{
139+
"type": "function",
140+
"function": get_function_schema(get_weather)
141+
}],
122142
tool_call="auto",
123143
)
124144
```
125145

126-
In claude api,
146+
### Usage with Anthropic Claude
127147

128148
```python
129149
import anthropic

function_schema/core.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ def guess_type(
166166
]
167167

168168
# number contains integer in JSON schema
169-
if "number" in _types and "integer" in _types:
170-
_types.remove("integer")
169+
# deduplicate
170+
_types = list(set(_types))
171171

172172
if len(_types) == 1:
173173
return _types[0]
@@ -191,10 +191,10 @@ def guess_type(
191191
return "string"
192192
if issubclass(T, bool):
193193
return "boolean"
194-
if issubclass(T, float):
194+
if issubclass(T, (float, int)):
195195
return "number"
196-
elif issubclass(T, int):
197-
return "integer"
196+
# elif issubclass(T, int):
197+
# return "integer"
198198
if T.__name__ == "list":
199199
return "array"
200200
if T.__name__ == "dict":

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "function-schema"
3-
version = "0.3.4"
3+
version = "0.3.6"
44
requires-python = ">= 3.9"
55
description = "A small utility to generate JSON schemas for python functions."
66
readme = "README.md"

test/test_guess_type.py

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
def test_primitive():
1010
"""Test primitive types"""
11-
assert guess_type(int) == "integer"
11+
assert guess_type(int) == "number"
1212
assert guess_type(str) == "string"
1313
assert guess_type(float) == "number"
1414
assert guess_type(bool) == "boolean"
@@ -32,39 +32,39 @@ def test_typings():
3232

3333
def test_optional():
3434
"""Test optional types"""
35-
assert guess_type(typing.Optional[int]) == "integer"
35+
assert guess_type(typing.Optional[int]) == "number"
3636
assert guess_type(typing.Optional[str]) == "string"
3737
assert guess_type(typing.Optional[float]) == "number"
3838
assert guess_type(typing.Optional[bool]) == "boolean"
3939

4040

4141
def test_union_null():
4242
"""Test union types with null"""
43-
assert guess_type(typing.Union[int, None]) == "integer"
43+
assert guess_type(typing.Union[int, None]) == "number"
4444
assert guess_type(typing.Union[str, None]) == "string"
4545
assert guess_type(typing.Union[float, None]) == "number"
4646
assert guess_type(typing.Union[bool, None]) == "boolean"
4747

4848

4949
def test_union():
5050
"""Test union types"""
51-
assert guess_type(typing.Union[int, str]) == ["integer", "string"]
51+
assert set(guess_type(typing.Union[int, str])) == {"number", "string"}
5252
assert guess_type(typing.Union[int, float]) == "number"
53-
assert guess_type(typing.Union[int, bool]) == ["integer", "boolean"]
54-
assert guess_type(typing.Union[bool, int]) == ["boolean", "integer"]
55-
assert guess_type(typing.Union[str, float]) == ["string", "number"]
56-
assert guess_type(typing.Union[str, bool]) == ["string", "boolean"]
57-
assert guess_type(typing.Union[float, bool]) == ["number", "boolean"]
58-
assert guess_type(typing.Union[str, float, bool]) == [
53+
assert set(guess_type(typing.Union[int, bool])) == {"number", "boolean"}
54+
assert set(guess_type(typing.Union[bool, int])) == {"boolean", "number"}
55+
assert set(guess_type(typing.Union[str, float])) == {"string", "number"}
56+
assert set(guess_type(typing.Union[str, bool])) == {"string", "boolean"}
57+
assert set(guess_type(typing.Union[float, bool])) == {"number", "boolean"}
58+
assert set(guess_type(typing.Union[str, float, bool])) == {
5959
"string",
6060
"number",
6161
"boolean",
62-
]
63-
assert guess_type(typing.Union[str, float, bool, None]) == [
62+
}
63+
assert set(guess_type(typing.Union[str, float, bool, None])) == {
6464
"string",
6565
"number",
6666
"boolean",
67-
]
67+
}
6868

6969

7070
current_version = packaging.version.parse(platform.python_version())
@@ -77,37 +77,36 @@ def test_union():
7777
def test_union_type():
7878
"""Test union types in Python 3.10+"""
7979

80-
assert guess_type(int | str) == ["integer", "string"]
80+
assert set(guess_type(int | str)) == {"number", "string"}
8181
assert guess_type(int | float) == "number"
82-
assert guess_type(int | bool) == ["integer", "boolean"]
83-
assert guess_type(bool | int) == ["boolean", "integer"]
84-
assert guess_type(str | float) == ["string", "number"]
85-
assert guess_type(str | bool) == ["string", "boolean"]
86-
assert guess_type(float | bool) == ["number", "boolean"]
87-
assert guess_type(str | float | bool) == [
82+
assert set(guess_type(int | bool)) == {"number", "boolean"}
83+
assert set(guess_type(str | float)) == {"string", "number"}
84+
assert set(guess_type(str | bool)) == {"string", "boolean"}
85+
assert set(guess_type(float | bool)) == {"number", "boolean"}
86+
assert set(guess_type(str | float | bool)) == {
8887
"string",
8988
"number",
9089
"boolean",
91-
]
92-
assert guess_type(str | float | bool | None) == [
90+
}
91+
assert set(guess_type(str | float | bool | None)) == {
9392
"string",
9493
"number",
9594
"boolean",
96-
]
95+
}
9796

9897

9998
def test_literal_type():
10099
"""Test literal type"""
101100
assert guess_type(typing.Literal["a"]) == "string", "should be string"
102-
assert guess_type(typing.Literal[1]) == "integer", "should be integer"
101+
assert guess_type(typing.Literal[1]) == "number", "should be number"
103102
assert guess_type(typing.Literal[1.0]) == "number", "should be number"
104103

105104
assert set(guess_type(typing.Literal["a", 1, None])) == {
106105
"string",
107-
"integer",
106+
"number",
108107
}, "should be string or integer, omit None"
109108

110-
assert set(guess_type(typing.Literal["a", 1])) == {"string", "integer"}
109+
assert set(guess_type(typing.Literal["a", 1])) == {"string", "number"}
111110
assert set(guess_type(typing.Literal["a", 1.0])) == {"string", "number"}
112111
assert set(guess_type(typing.Literal["a", 1.1])) == {"string", "number"}
113112
assert set(guess_type(typing.Literal["a", 1, 1.0])) == {

test/test_schema.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def func1(a: int, b: str, c: float = 1.0):
3636
schema["description"] == "My function"
3737
), "Function description should be there"
3838
assert (
39-
schema["parameters"]["properties"]["a"]["type"] == "integer"
39+
schema["parameters"]["properties"]["a"]["type"] == "number"
4040
), "parameter a should be an integer"
4141
assert (
4242
schema["parameters"]["properties"]["b"]["type"] == "string"
@@ -66,7 +66,7 @@ def func1(
6666
schema = get_function_schema(func1)
6767

6868
assert (
69-
schema["parameters"]["properties"]["a"]["type"] == "integer"
69+
schema["parameters"]["properties"]["a"]["type"] == "number"
7070
), "parameter a should be an integer"
7171
assert (
7272
schema["parameters"]["properties"]["a"]["description"] == "An integer parameter"
@@ -145,21 +145,21 @@ def func3(animal: Literal["Cat", "Dog", 1]):
145145
...
146146

147147
schema = get_function_schema(func3)
148-
assert schema["parameters"]["properties"]["animal"]["type"] == [
148+
assert set(schema["parameters"]["properties"]["animal"]["type"]) == {
149149
"string",
150-
"integer",
151-
], "parameter animal should be a string"
150+
"number",
151+
}, "parameter animal should be a string"
152152

153153
def func4(animal: Literal["Cat", "Dog", 1, None]):
154154
"""My function"""
155155
...
156156

157157
schema = get_function_schema(func4)
158158

159-
assert schema["parameters"]["properties"]["animal"]["type"] == [
159+
assert set(schema["parameters"]["properties"]["animal"]["type"]) == {
160160
"string",
161-
"integer",
162-
], "parameter animal should be a string"
161+
"number",
162+
}, "parameter animal should be a string"
163163

164164
assert schema["parameters"]["properties"]["animal"]["enum"] == [
165165
"Cat",

uv.lock

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)