2
2
from collections import defaultdict
3
3
import inspect
4
4
from typing import Callable , Union
5
+ import re
5
6
6
7
import pydantic
7
8
from ollama ._types import Tool
@@ -14,7 +15,7 @@ def _parse_docstring(doc_string: Union[str, None]) -> dict[str, str]:
14
15
15
16
key = hash (doc_string )
16
17
for line in doc_string .splitlines ():
17
- lowered_line = line .lower ()
18
+ lowered_line = line .lower (). strip ()
18
19
if lowered_line .startswith ('args:' ):
19
20
key = 'args'
20
21
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]:
27
28
last_key = None
28
29
for line in parsed_docstring ['args' ].splitlines ():
29
30
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
+
38
43
parsed_docstring [last_key ] = arg_description
39
44
40
45
elif last_key and line :
0 commit comments