2
2
3
3
import functools
4
4
import inspect
5
- from collections .abc import Callable
5
+ from collections .abc import Awaitable , Callable
6
6
from functools import cached_property
7
7
from typing import TYPE_CHECKING , Any , Literal
8
8
@@ -39,6 +39,9 @@ class Tool(BaseModel):
39
39
default = ["sync" ], description = "Supported invocation modes (sync/async)"
40
40
)
41
41
meta : dict [str , Any ] | None = Field (description = "Optional additional tool information." , default = None )
42
+ immediate_result : Callable [..., Awaitable [list [Any ]]] | None = Field (
43
+ None , exclude = True , description = "Optional immediate result function for async tools"
44
+ )
42
45
43
46
@cached_property
44
47
def output_schema (self ) -> dict [str , Any ] | None :
@@ -55,7 +58,9 @@ def from_function(
55
58
annotations : ToolAnnotations | None = None ,
56
59
structured_output : bool | None = None ,
57
60
invocation_modes : list [InvocationMode ] | None = None ,
61
+ keep_alive : int | None = None ,
58
62
meta : dict [str , Any ] | None = None ,
63
+ immediate_result : Callable [..., Awaitable [list [Any ]]] | None = None ,
59
64
) -> Tool :
60
65
"""Create a Tool from a function."""
61
66
func_name = name or fn .__name__
@@ -80,6 +85,39 @@ def from_function(
80
85
if invocation_modes is None :
81
86
invocation_modes = ["sync" ]
82
87
88
+ # Set appropriate default keep_alive based on async compatibility
89
+ # if user didn't specify custom keep_alive
90
+ if keep_alive is None and "async" in invocation_modes :
91
+ keep_alive = 3600 # Default for async-compatible tools
92
+
93
+ # Validate keep_alive is only used with async-compatible tools
94
+ if keep_alive is not None and "async" not in invocation_modes :
95
+ raise ValueError (
96
+ f"keep_alive parameter can only be used with async-compatible tools. "
97
+ f"Tool '{ func_name } ' has invocation_modes={ invocation_modes } "
98
+ f"but specifies keep_alive={ keep_alive } . "
99
+ f"Add 'async' to invocation_modes to use keep_alive."
100
+ )
101
+
102
+ # Process meta dictionary and add keep_alive if specified
103
+ meta = meta or {}
104
+ if keep_alive is not None :
105
+ meta = meta .copy () # Don't modify the original dict
106
+ meta ["_keep_alive" ] = keep_alive
107
+
108
+ # Validate immediate_result usage
109
+ if immediate_result is not None :
110
+ # Check if tool supports async invocation
111
+ if "async" not in invocation_modes :
112
+ raise ValueError (
113
+ "immediate_result can only be used with async-compatible tools. "
114
+ "Add 'async' to invocation_modes to use immediate_result."
115
+ )
116
+
117
+ # Validate that immediate_result is an async callable
118
+ if not _is_async_callable (immediate_result ):
119
+ raise ValueError ("immediate_result must be an async callable that returns list[ContentBlock]" )
120
+
83
121
return cls (
84
122
fn = fn ,
85
123
name = func_name ,
@@ -92,6 +130,7 @@ def from_function(
92
130
annotations = annotations ,
93
131
invocation_modes = invocation_modes ,
94
132
meta = meta ,
133
+ immediate_result = immediate_result ,
95
134
)
96
135
97
136
async def run (
0 commit comments