1
1
"""
2
2
name: SimpleTools
3
3
author: Artur Zdolinski
4
- version: 0.0.30
4
+ version: 0.0.31
5
5
"""
6
6
# Standard library imports
7
7
import gc
17
17
from functools import wraps
18
18
from pathlib import Path
19
19
from typing import get_args , get_origin
20
- from typing import List , Dict , Any , Union , Type , Literal , ClassVar , Sequence , Tuple , TypeVar , Optional , Callable
20
+ from typing import List , Dict , Any , Union , Type , Literal , ClassVar , Sequence , Tuple , TypeVar , Optional , Callable , TypeAlias
21
21
from typing import AnyStr # noqa: F401, F403
22
22
from weakref import WeakMethod , ref , WeakSet
23
23
40
40
# Type for input arguments - can be dict or any model inheriting from SimpleInputModel
41
41
T = TypeVar ('T' , Dict [str , Any ], SimpleInputModel )
42
42
43
- # Type for return value - can be specified by the tool implementation
44
- R = TypeVar ('R' , bound = Union [Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent ])
43
+ # Define a type alias for content types instead of using a TypeVar
44
+ ContentType : TypeAlias = Union [Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent ]
45
+
46
+ # Define a type alias for response types that can be either Sequence or List of ContentType
47
+ ResponseType : TypeAlias = Union [Sequence [ContentType ], List [ContentType ]]
45
48
46
49
# Threshold for what we consider a "large" object (1MB)
47
50
LARGE_OBJECT_THRESHOLD = 1024 * 1024 # 1MB in bytes
48
51
49
52
50
53
def get_valid_content_types () -> Tuple [Type , ...]:
51
- """Directly return the types from the TypeVar definition as a tuple"""
52
- return ( Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent )
54
+ """Directly return the types from the ContentType definition as a tuple"""
55
+ return get_args ( ContentType )
53
56
54
57
55
58
def validate_tool_output (func ):
56
59
@wraps (func )
57
- def wrapper (* args : Any , ** kwargs : Any ) -> Sequence [ Union [ Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent ]] :
60
+ def wrapper (* args : Any , ** kwargs : Any ) -> ResponseType :
58
61
result = func (* args , ** kwargs )
59
62
60
63
# Handle coroutines for backward compatibility with async code
61
64
if asyncio .iscoroutine (result ):
62
65
async def _async_wrapper ():
63
66
async_result = await result
64
- if not isinstance (async_result , list ):
65
- raise ValidationError ("output" , "Tool output must be a list" )
67
+ if not isinstance (async_result , ( list , tuple ) ):
68
+ raise ValidationError ("output" , "Tool output must be a list or tuple " )
66
69
67
70
valid_types = get_valid_content_types ()
68
71
for item in async_result :
@@ -73,8 +76,8 @@ async def _async_wrapper():
73
76
return asyncio .run (_async_wrapper ())
74
77
75
78
# Handle synchronous results
76
- if not isinstance (result , list ):
77
- raise ValidationError ("output" , "Tool output must be a list" )
79
+ if not isinstance (result , ( list , tuple ) ):
80
+ raise ValidationError ("output" , "Tool output must be a list or tuple " )
78
81
79
82
valid_types = get_valid_content_types ()
80
83
for item in result :
@@ -200,7 +203,7 @@ def __init__(self):
200
203
# Initialize memory management
201
204
self ._process = psutil .Process ()
202
205
203
- def __call__ (self , arguments : Dict [str , Any ]) -> Sequence [ Union [ Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent ]] :
206
+ def __call__ (self , arguments : Dict [str , Any ]) -> ResponseType :
204
207
"""
205
208
Execute the tool with memory management and validation.
206
209
This is the main entry point that handles all the memory management.
@@ -261,7 +264,7 @@ def __call__(self, arguments: Dict[str, Any]) -> Sequence[Union[Content, TextCon
261
264
262
265
@abstractmethod
263
266
@validate_tool_output
264
- def run (self , arguments : T ) -> Sequence [ R ] :
267
+ def run (self , arguments : T ) -> ResponseType :
265
268
"""
266
269
Execute the tool with the given arguments.
267
270
This is the method that tool developers should implement.
@@ -274,7 +277,7 @@ def run(self, arguments: T) -> Sequence[R]:
274
277
"""
275
278
raise NotImplementedError ("Subclass must implement run()" )
276
279
277
- def arun (self , arguments : T ) -> Sequence [ R ] :
280
+ def arun (self , arguments : T ) -> ResponseType :
278
281
"""
279
282
For compatibility with async version. This method simply calls run().
280
283
If you need async functionality, use simpletool.asyncio.SimpleTool instead.
@@ -675,7 +678,7 @@ def create(
675
678
name : str ,
676
679
description : str ,
677
680
input_model : Type [SimpleInputModel ],
678
- run_fn : Callable [[SimpleInputModel ], Sequence [ Union [ Content , TextContent , ImageContent , FileContent , ResourceContent , BoolContent , ErrorContent ]] ]
681
+ run_fn : Callable [[SimpleInputModel ], ResponseType ]
679
682
) -> 'SimpleTool' :
680
683
"""
681
684
Create a SimpleTool instance without subclassing.
@@ -728,6 +731,8 @@ def my_run(arguments: MyInputModel) -> List[TextContent]:
728
731
'ResourceContent' ,
729
732
'BoolContent' ,
730
733
'ErrorContent' ,
734
+ 'ContentType' ,
735
+ 'ResponseType' ,
731
736
'List' ,
732
737
'Dict' ,
733
738
'Any' ,
0 commit comments