Skip to content

Commit 3a994b2

Browse files
committed
Better docstring parsing and some fixes
1 parent 6d9c156 commit 3a994b2

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

ollama/_utils.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections import defaultdict
33
import inspect
44
from typing import Callable, Union
5+
import re
56

67
import pydantic
78
from ollama._types import Tool
@@ -14,7 +15,7 @@ def _parse_docstring(doc_string: Union[str, None]) -> dict[str, str]:
1415

1516
key = hash(doc_string)
1617
for line in doc_string.splitlines():
17-
lowered_line = line.lower()
18+
lowered_line = line.lower().strip()
1819
if lowered_line.startswith('args:'):
1920
key = 'args'
2021
elif lowered_line.startswith('returns:') or lowered_line.startswith('yields:') or lowered_line.startswith('raises:'):
@@ -27,14 +28,18 @@ def _parse_docstring(doc_string: Union[str, None]) -> dict[str, str]:
2728
last_key = None
2829
for line in parsed_docstring['args'].splitlines():
2930
line = line.strip()
30-
if ':' in line and not line.lower().startswith('args:'):
31-
# Split on first occurrence of '(' or ':' to separate arg name from description
32-
split_char = '(' if '(' in line else ':'
33-
arg_name, rest = line.split(split_char, 1)
34-
35-
last_key = arg_name.strip()
36-
# Get description after the colon
37-
arg_description = rest.split(':', 1)[1].strip() if split_char == '(' else rest.strip()
31+
if ':' in line:
32+
parts = re.split(r'(?:\(([^)]*)\)|:)\s*', line, maxsplit=1)
33+
34+
# First part is always the argument name
35+
arg_name = parts[0].strip()
36+
last_key = arg_name
37+
38+
# Get the description - will be in parts[1] if parenthetical or parts[-1] if after colon
39+
arg_description = parts[-1].strip()
40+
if len(parts) > 2 and parts[1]: # Has parenthetical content
41+
arg_description = parts[-1].split(':', 1)[-1].strip()
42+
3843
parsed_docstring[last_key] = arg_description
3944

4045
elif last_key and line:

tests/test_utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,33 @@ def no_types(a, b):
238238
tool = convert_function_to_tool(no_types).model_dump()
239239
assert tool['function']['parameters']['properties']['a']['type'] == 'string'
240240
assert tool['function']['parameters']['properties']['b']['type'] == 'string'
241+
242+
243+
def test_function_with_parentheses():
244+
def func_with_parentheses(a: int, b: int) -> int:
245+
"""
246+
A function with parentheses.
247+
Args:
248+
a: First (:thing) number to add
249+
b: Second number to add
250+
Returns:
251+
int: The sum of a and b
252+
"""
253+
pass
254+
255+
def func_with_parentheses_and_args(a: int, b: int):
256+
"""
257+
A function with parentheses and args.
258+
Args:
259+
a(integer): First (:thing) number to add
260+
b(integer): Second number to add
261+
"""
262+
pass
263+
264+
tool = convert_function_to_tool(func_with_parentheses).model_dump()
265+
assert tool['function']['parameters']['properties']['a']['description'] == 'First (:thing) number to add'
266+
assert tool['function']['parameters']['properties']['b']['description'] == 'Second number to add'
267+
268+
tool = convert_function_to_tool(func_with_parentheses_and_args).model_dump()
269+
assert tool['function']['parameters']['properties']['a']['description'] == 'First (:thing) number to add'
270+
assert tool['function']['parameters']['properties']['b']['description'] == 'Second number to add'

0 commit comments

Comments
 (0)