kosong.tooling
1from abc import ABC, abstractmethod 2from asyncio import Future 3from typing import Any, ClassVar, Protocol, Self, cast, override, runtime_checkable 4 5import jsonschema 6import pydantic 7from pydantic import BaseModel, GetCoreSchemaHandler, model_validator 8from pydantic.json_schema import GenerateJsonSchema 9from pydantic_core import core_schema 10 11from kosong.message import ContentPart, ToolCall 12from kosong.utils.jsonschema import deref_json_schema 13from kosong.utils.typing import JsonType 14 15type ParametersType = dict[str, Any] 16 17 18class Tool(BaseModel): 19 """The definition of a tool that can be recognized by the model.""" 20 21 name: str 22 """The name of the tool.""" 23 24 description: str 25 """The description of the tool.""" 26 27 parameters: ParametersType 28 """The parameters of the tool, in JSON Schema format.""" 29 30 @model_validator(mode="after") 31 def _validate_parameters(self) -> Self: 32 jsonschema.validate(self.parameters, jsonschema.Draft202012Validator.META_SCHEMA) 33 return self 34 35 36class DisplayBlock(BaseModel, ABC): 37 """ 38 A block of content to be displayed to the user. 39 40 Similar to `ContentPart`, but scoped to user-facing UI. 41 `ContentPart` is for model-facing message content; `DisplayBlock` is for tool/UI extensions. 42 43 Unlike `ContentPart`, Kosong users may directly subclass `DisplayBlock` to define custom 44 display blocks for their applications. 45 """ 46 47 __display_block_registry: ClassVar[dict[str, type["DisplayBlock"]]] = {} 48 49 type: str 50 ... # to be added by subclasses 51 52 def __init_subclass__(cls, **kwargs: Any) -> None: 53 super().__init_subclass__(**kwargs) 54 55 invalid_subclass_error_msg = ( 56 f"DisplayBlock subclass {cls.__name__} must have a `type` field of type `str`" 57 ) 58 59 type_value = getattr(cls, "type", None) 60 if type_value is None or not isinstance(type_value, str): 61 raise ValueError(invalid_subclass_error_msg) 62 63 cls.__display_block_registry[type_value] = cls 64 65 @classmethod 66 def __get_pydantic_core_schema__( 67 cls, source_type: Any, handler: GetCoreSchemaHandler 68 ) -> core_schema.CoreSchema: 69 # If we're dealing with the base DisplayBlock class, use custom validation 70 if cls.__name__ == "DisplayBlock": 71 72 def validate_display_block(value: Any) -> Any: 73 # if it's already an instance of a DisplayBlock subclass, return it 74 if hasattr(value, "__class__") and issubclass(value.__class__, cls): 75 return value 76 77 # if it's a dict with a type field, dispatch to the appropriate subclass 78 if isinstance(value, dict) and "type" in value: 79 type_value: Any | None = cast(dict[str, Any], value).get("type") 80 if not isinstance(type_value, str): 81 raise ValueError(f"Cannot validate {value} as DisplayBlock") 82 target_class = cls.__display_block_registry.get(type_value) 83 if target_class is None: 84 data = {k: v for k, v in cast(dict[str, Any], value).items() if k != "type"} 85 return UnknownDisplayBlock.model_validate( 86 {"type": type_value, "data": data} 87 ) 88 return target_class.model_validate(value) 89 90 raise ValueError(f"Cannot validate {value} as DisplayBlock") 91 92 return core_schema.no_info_plain_validator_function(validate_display_block) 93 94 # for subclasses, use the default schema 95 return handler(source_type) 96 97 98class UnknownDisplayBlock(DisplayBlock): 99 """Fallback display block for unknown types.""" 100 101 type: str = "unknown" 102 data: JsonType 103 104 105class BriefDisplayBlock(DisplayBlock): 106 """A brief display block with plain string content.""" 107 108 type: str = "brief" 109 text: str 110 111 112class ToolReturnValue(BaseModel): 113 """The return type of a callable tool.""" 114 115 is_error: bool 116 """Whether the tool call resulted in an error.""" 117 118 # For model 119 output: str | list[ContentPart] 120 """The output content returned by the tool.""" 121 message: str 122 """An explanatory message to be given to the model.""" 123 124 # For user 125 display: list[DisplayBlock] 126 """The content blocks to be displayed to the user.""" 127 128 # For debugging/testing 129 extras: dict[str, JsonType] | None = None 130 131 @property 132 def brief(self) -> str: 133 """Get the brief display block data, if any.""" 134 for block in self.display: 135 if isinstance(block, BriefDisplayBlock): 136 return block.text 137 return "" 138 139 140class ToolOk(ToolReturnValue): 141 """Subclass of `ToolReturnValue` representing a successful tool call.""" 142 143 def __init__( 144 self, 145 *, 146 output: str | ContentPart | list[ContentPart], 147 message: str = "", 148 brief: str = "", 149 ) -> None: 150 super().__init__( 151 is_error=False, 152 output=([output] if isinstance(output, ContentPart) else output), 153 message=message, 154 display=[BriefDisplayBlock(text=brief)] if brief else [], 155 ) 156 157 158class ToolError(ToolReturnValue): 159 """Subclass of `ToolReturnValue` representing a failed tool call.""" 160 161 def __init__( 162 self, *, message: str, brief: str, output: str | ContentPart | list[ContentPart] = "" 163 ): 164 super().__init__( 165 is_error=True, 166 output=([output] if isinstance(output, ContentPart) else output), 167 message=message, 168 display=[BriefDisplayBlock(text=brief)] if brief else [], 169 ) 170 171 172class CallableTool(Tool, ABC): 173 """ 174 The abstract base class of tools that can be called as callables. 175 176 The tool will be called with the arguments provided in the `ToolCall`. 177 If the arguments are given as a JSON array, it will be unpacked into positional arguments. 178 If the arguments are given as a JSON object, it will be unpacked into keyword arguments. 179 Otherwise, the arguments will be passed as a single argument. 180 """ 181 182 @property 183 def base(self) -> Tool: 184 """The base tool definition.""" 185 return self 186 187 async def call(self, arguments: JsonType) -> ToolReturnValue: 188 from kosong.tooling.error import ToolValidateError 189 190 try: 191 jsonschema.validate(arguments, self.parameters) 192 except jsonschema.ValidationError as e: 193 return ToolValidateError(str(e)) 194 195 if isinstance(arguments, list): 196 ret = await self.__call__(*arguments) 197 elif isinstance(arguments, dict): 198 ret = await self.__call__(**arguments) 199 else: 200 ret = await self.__call__(arguments) 201 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 202 # let's do not trust the return type of the tool 203 ret = ToolError( 204 message=f"Invalid return type: {type(ret)}", 205 brief="Invalid return type", 206 ) 207 return ret 208 209 @abstractmethod 210 async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue: 211 """ 212 @public 213 214 The implementation of the callable tool. 215 """ 216 ... 217 218 219class _GenerateJsonSchemaNoTitles(GenerateJsonSchema): 220 """Custom JSON schema generator that omits titles.""" 221 222 @override 223 def field_title_should_be_set(self, schema) -> bool: # type: ignore[reportMissingParameterType] 224 return False 225 226 @override 227 def _update_class_schema(self, json_schema, cls, config) -> None: # type: ignore[reportMissingParameterType] 228 super()._update_class_schema(json_schema, cls, config) 229 json_schema.pop("title", None) 230 231 232class CallableTool2[Params: BaseModel](ABC): 233 """ 234 The abstract base class of tools that can be called as callables, with typed parameters. 235 236 The tool will be called with the arguments provided in the `ToolCall`. 237 The arguments must be a JSON object, and will be validated by Pydantic to the `Params` type. 238 """ 239 240 name: str 241 """The name of the tool.""" 242 description: str 243 """The description of the tool.""" 244 params: type[Params] 245 """The Pydantic model type of the tool parameters.""" 246 247 def __init__( 248 self, 249 name: str | None = None, 250 description: str | None = None, 251 params: type[Params] | None = None, 252 ) -> None: 253 cls = self.__class__ 254 255 self.name = name or getattr(cls, "name", "") 256 if not self.name: 257 raise ValueError( 258 "Tool name must be provided either as class variable or constructor argument" 259 ) 260 if not isinstance(self.name, str): # type: ignore[reportUnnecessaryIsInstance] 261 raise ValueError("Tool name must be a string") 262 263 self.description = description or getattr(cls, "description", "") 264 if not self.description: 265 raise ValueError( 266 "Tool description must be provided either as class variable or constructor argument" 267 ) 268 if not isinstance(self.description, str): # type: ignore[reportUnnecessaryIsInstance] 269 raise ValueError("Tool description must be a string") 270 271 self.params = params or getattr(cls, "params", None) # type: ignore 272 if not self.params: 273 raise ValueError( 274 "Tool param must be provided either as class variable or constructor argument" 275 ) 276 if not isinstance(self.params, type) or not issubclass(self.params, BaseModel): # type: ignore[reportUnnecessaryIsInstance] 277 raise ValueError("Tool params must be a subclass of pydantic.BaseModel") 278 279 self._base = Tool( 280 name=self.name, 281 description=self.description, 282 parameters=deref_json_schema( 283 self.params.model_json_schema(schema_generator=_GenerateJsonSchemaNoTitles) 284 ), 285 ) 286 287 @property 288 def base(self) -> Tool: 289 """The base tool definition.""" 290 return self._base 291 292 async def call(self, arguments: JsonType) -> ToolReturnValue: 293 from kosong.tooling.error import ToolValidateError 294 295 try: 296 params = self.params.model_validate(arguments) 297 except pydantic.ValidationError as e: 298 return ToolValidateError(str(e)) 299 300 ret = await self.__call__(params) 301 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 302 # let's do not trust the return type of the tool 303 ret = ToolError( 304 message=f"Invalid return type: {type(ret)}", 305 brief="Invalid return type", 306 ) 307 return ret 308 309 @abstractmethod 310 async def __call__(self, params: Params) -> ToolReturnValue: 311 """ 312 @public 313 314 The implementation of the callable tool. 315 """ 316 ... 317 318 319class ToolResult(BaseModel): 320 """The result of a tool call.""" 321 322 tool_call_id: str 323 """The ID of the tool call.""" 324 return_value: ToolReturnValue 325 """The actual return value of the tool call.""" 326 327 328ToolResultFuture = Future[ToolResult] 329type HandleResult = ToolResultFuture | ToolResult 330 331 332@runtime_checkable 333class Toolset(Protocol): 334 """ 335 The interface of toolsets that can register tools and handle tool calls. 336 """ 337 338 @property 339 def tools(self) -> list[Tool]: 340 """The list of tool definitions registered in this toolset.""" 341 ... 342 343 def handle(self, tool_call: ToolCall) -> HandleResult: 344 """ 345 Handle a tool call. 346 The result of the tool call, or the async future of the result, should be returned. 347 The result should be a `ToolReturnValue`. 348 349 This method MUST NOT do any blocking operations because it will be called during 350 consuming the chat response stream. 351 This method MUST NOT raise any exception except for `asyncio.CancelledError`. Any other 352 error should be returned as a `ToolReturnValue` with `is_error=True`. 353 """ 354 ...
19class Tool(BaseModel): 20 """The definition of a tool that can be recognized by the model.""" 21 22 name: str 23 """The name of the tool.""" 24 25 description: str 26 """The description of the tool.""" 27 28 parameters: ParametersType 29 """The parameters of the tool, in JSON Schema format.""" 30 31 @model_validator(mode="after") 32 def _validate_parameters(self) -> Self: 33 jsonschema.validate(self.parameters, jsonschema.Draft202012Validator.META_SCHEMA) 34 return self
The definition of a tool that can be recognized by the model.
37class DisplayBlock(BaseModel, ABC): 38 """ 39 A block of content to be displayed to the user. 40 41 Similar to `ContentPart`, but scoped to user-facing UI. 42 `ContentPart` is for model-facing message content; `DisplayBlock` is for tool/UI extensions. 43 44 Unlike `ContentPart`, Kosong users may directly subclass `DisplayBlock` to define custom 45 display blocks for their applications. 46 """ 47 48 __display_block_registry: ClassVar[dict[str, type["DisplayBlock"]]] = {} 49 50 type: str 51 ... # to be added by subclasses 52 53 def __init_subclass__(cls, **kwargs: Any) -> None: 54 super().__init_subclass__(**kwargs) 55 56 invalid_subclass_error_msg = ( 57 f"DisplayBlock subclass {cls.__name__} must have a `type` field of type `str`" 58 ) 59 60 type_value = getattr(cls, "type", None) 61 if type_value is None or not isinstance(type_value, str): 62 raise ValueError(invalid_subclass_error_msg) 63 64 cls.__display_block_registry[type_value] = cls 65 66 @classmethod 67 def __get_pydantic_core_schema__( 68 cls, source_type: Any, handler: GetCoreSchemaHandler 69 ) -> core_schema.CoreSchema: 70 # If we're dealing with the base DisplayBlock class, use custom validation 71 if cls.__name__ == "DisplayBlock": 72 73 def validate_display_block(value: Any) -> Any: 74 # if it's already an instance of a DisplayBlock subclass, return it 75 if hasattr(value, "__class__") and issubclass(value.__class__, cls): 76 return value 77 78 # if it's a dict with a type field, dispatch to the appropriate subclass 79 if isinstance(value, dict) and "type" in value: 80 type_value: Any | None = cast(dict[str, Any], value).get("type") 81 if not isinstance(type_value, str): 82 raise ValueError(f"Cannot validate {value} as DisplayBlock") 83 target_class = cls.__display_block_registry.get(type_value) 84 if target_class is None: 85 data = {k: v for k, v in cast(dict[str, Any], value).items() if k != "type"} 86 return UnknownDisplayBlock.model_validate( 87 {"type": type_value, "data": data} 88 ) 89 return target_class.model_validate(value) 90 91 raise ValueError(f"Cannot validate {value} as DisplayBlock") 92 93 return core_schema.no_info_plain_validator_function(validate_display_block) 94 95 # for subclasses, use the default schema 96 return handler(source_type)
A block of content to be displayed to the user.
Similar to ContentPart, but scoped to user-facing UI.
ContentPart is for model-facing message content; DisplayBlock is for tool/UI extensions.
Unlike ContentPart, Kosong users may directly subclass DisplayBlock to define custom
display blocks for their applications.
99class UnknownDisplayBlock(DisplayBlock): 100 """Fallback display block for unknown types.""" 101 102 type: str = "unknown" 103 data: JsonType
Fallback display block for unknown types.
106class BriefDisplayBlock(DisplayBlock): 107 """A brief display block with plain string content.""" 108 109 type: str = "brief" 110 text: str
A brief display block with plain string content.
113class ToolReturnValue(BaseModel): 114 """The return type of a callable tool.""" 115 116 is_error: bool 117 """Whether the tool call resulted in an error.""" 118 119 # For model 120 output: str | list[ContentPart] 121 """The output content returned by the tool.""" 122 message: str 123 """An explanatory message to be given to the model.""" 124 125 # For user 126 display: list[DisplayBlock] 127 """The content blocks to be displayed to the user.""" 128 129 # For debugging/testing 130 extras: dict[str, JsonType] | None = None 131 132 @property 133 def brief(self) -> str: 134 """Get the brief display block data, if any.""" 135 for block in self.display: 136 if isinstance(block, BriefDisplayBlock): 137 return block.text 138 return ""
The return type of a callable tool.
141class ToolOk(ToolReturnValue): 142 """Subclass of `ToolReturnValue` representing a successful tool call.""" 143 144 def __init__( 145 self, 146 *, 147 output: str | ContentPart | list[ContentPart], 148 message: str = "", 149 brief: str = "", 150 ) -> None: 151 super().__init__( 152 is_error=False, 153 output=([output] if isinstance(output, ContentPart) else output), 154 message=message, 155 display=[BriefDisplayBlock(text=brief)] if brief else [], 156 )
Subclass of ToolReturnValue representing a successful tool call.
159class ToolError(ToolReturnValue): 160 """Subclass of `ToolReturnValue` representing a failed tool call.""" 161 162 def __init__( 163 self, *, message: str, brief: str, output: str | ContentPart | list[ContentPart] = "" 164 ): 165 super().__init__( 166 is_error=True, 167 output=([output] if isinstance(output, ContentPart) else output), 168 message=message, 169 display=[BriefDisplayBlock(text=brief)] if brief else [], 170 )
Subclass of ToolReturnValue representing a failed tool call.
173class CallableTool(Tool, ABC): 174 """ 175 The abstract base class of tools that can be called as callables. 176 177 The tool will be called with the arguments provided in the `ToolCall`. 178 If the arguments are given as a JSON array, it will be unpacked into positional arguments. 179 If the arguments are given as a JSON object, it will be unpacked into keyword arguments. 180 Otherwise, the arguments will be passed as a single argument. 181 """ 182 183 @property 184 def base(self) -> Tool: 185 """The base tool definition.""" 186 return self 187 188 async def call(self, arguments: JsonType) -> ToolReturnValue: 189 from kosong.tooling.error import ToolValidateError 190 191 try: 192 jsonschema.validate(arguments, self.parameters) 193 except jsonschema.ValidationError as e: 194 return ToolValidateError(str(e)) 195 196 if isinstance(arguments, list): 197 ret = await self.__call__(*arguments) 198 elif isinstance(arguments, dict): 199 ret = await self.__call__(**arguments) 200 else: 201 ret = await self.__call__(arguments) 202 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 203 # let's do not trust the return type of the tool 204 ret = ToolError( 205 message=f"Invalid return type: {type(ret)}", 206 brief="Invalid return type", 207 ) 208 return ret 209 210 @abstractmethod 211 async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue: 212 """ 213 @public 214 215 The implementation of the callable tool. 216 """ 217 ...
The abstract base class of tools that can be called as callables.
The tool will be called with the arguments provided in the ToolCall.
If the arguments are given as a JSON array, it will be unpacked into positional arguments.
If the arguments are given as a JSON object, it will be unpacked into keyword arguments.
Otherwise, the arguments will be passed as a single argument.
188 async def call(self, arguments: JsonType) -> ToolReturnValue: 189 from kosong.tooling.error import ToolValidateError 190 191 try: 192 jsonschema.validate(arguments, self.parameters) 193 except jsonschema.ValidationError as e: 194 return ToolValidateError(str(e)) 195 196 if isinstance(arguments, list): 197 ret = await self.__call__(*arguments) 198 elif isinstance(arguments, dict): 199 ret = await self.__call__(**arguments) 200 else: 201 ret = await self.__call__(arguments) 202 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 203 # let's do not trust the return type of the tool 204 ret = ToolError( 205 message=f"Invalid return type: {type(ret)}", 206 brief="Invalid return type", 207 ) 208 return ret
210 @abstractmethod 211 async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue: 212 """ 213 @public 214 215 The implementation of the callable tool. 216 """ 217 ...
The implementation of the callable tool.
Inherited Members
233class CallableTool2[Params: BaseModel](ABC): 234 """ 235 The abstract base class of tools that can be called as callables, with typed parameters. 236 237 The tool will be called with the arguments provided in the `ToolCall`. 238 The arguments must be a JSON object, and will be validated by Pydantic to the `Params` type. 239 """ 240 241 name: str 242 """The name of the tool.""" 243 description: str 244 """The description of the tool.""" 245 params: type[Params] 246 """The Pydantic model type of the tool parameters.""" 247 248 def __init__( 249 self, 250 name: str | None = None, 251 description: str | None = None, 252 params: type[Params] | None = None, 253 ) -> None: 254 cls = self.__class__ 255 256 self.name = name or getattr(cls, "name", "") 257 if not self.name: 258 raise ValueError( 259 "Tool name must be provided either as class variable or constructor argument" 260 ) 261 if not isinstance(self.name, str): # type: ignore[reportUnnecessaryIsInstance] 262 raise ValueError("Tool name must be a string") 263 264 self.description = description or getattr(cls, "description", "") 265 if not self.description: 266 raise ValueError( 267 "Tool description must be provided either as class variable or constructor argument" 268 ) 269 if not isinstance(self.description, str): # type: ignore[reportUnnecessaryIsInstance] 270 raise ValueError("Tool description must be a string") 271 272 self.params = params or getattr(cls, "params", None) # type: ignore 273 if not self.params: 274 raise ValueError( 275 "Tool param must be provided either as class variable or constructor argument" 276 ) 277 if not isinstance(self.params, type) or not issubclass(self.params, BaseModel): # type: ignore[reportUnnecessaryIsInstance] 278 raise ValueError("Tool params must be a subclass of pydantic.BaseModel") 279 280 self._base = Tool( 281 name=self.name, 282 description=self.description, 283 parameters=deref_json_schema( 284 self.params.model_json_schema(schema_generator=_GenerateJsonSchemaNoTitles) 285 ), 286 ) 287 288 @property 289 def base(self) -> Tool: 290 """The base tool definition.""" 291 return self._base 292 293 async def call(self, arguments: JsonType) -> ToolReturnValue: 294 from kosong.tooling.error import ToolValidateError 295 296 try: 297 params = self.params.model_validate(arguments) 298 except pydantic.ValidationError as e: 299 return ToolValidateError(str(e)) 300 301 ret = await self.__call__(params) 302 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 303 # let's do not trust the return type of the tool 304 ret = ToolError( 305 message=f"Invalid return type: {type(ret)}", 306 brief="Invalid return type", 307 ) 308 return ret 309 310 @abstractmethod 311 async def __call__(self, params: Params) -> ToolReturnValue: 312 """ 313 @public 314 315 The implementation of the callable tool. 316 """ 317 ...
The abstract base class of tools that can be called as callables, with typed parameters.
The tool will be called with the arguments provided in the ToolCall.
The arguments must be a JSON object, and will be validated by Pydantic to the Params type.
293 async def call(self, arguments: JsonType) -> ToolReturnValue: 294 from kosong.tooling.error import ToolValidateError 295 296 try: 297 params = self.params.model_validate(arguments) 298 except pydantic.ValidationError as e: 299 return ToolValidateError(str(e)) 300 301 ret = await self.__call__(params) 302 if not isinstance(ret, ToolReturnValue): # type: ignore[reportUnnecessaryIsInstance] 303 # let's do not trust the return type of the tool 304 ret = ToolError( 305 message=f"Invalid return type: {type(ret)}", 306 brief="Invalid return type", 307 ) 308 return ret
320class ToolResult(BaseModel): 321 """The result of a tool call.""" 322 323 tool_call_id: str 324 """The ID of the tool call.""" 325 return_value: ToolReturnValue 326 """The actual return value of the tool call."""
The result of a tool call.
333@runtime_checkable 334class Toolset(Protocol): 335 """ 336 The interface of toolsets that can register tools and handle tool calls. 337 """ 338 339 @property 340 def tools(self) -> list[Tool]: 341 """The list of tool definitions registered in this toolset.""" 342 ... 343 344 def handle(self, tool_call: ToolCall) -> HandleResult: 345 """ 346 Handle a tool call. 347 The result of the tool call, or the async future of the result, should be returned. 348 The result should be a `ToolReturnValue`. 349 350 This method MUST NOT do any blocking operations because it will be called during 351 consuming the chat response stream. 352 This method MUST NOT raise any exception except for `asyncio.CancelledError`. Any other 353 error should be returned as a `ToolReturnValue` with `is_error=True`. 354 """ 355 ...
The interface of toolsets that can register tools and handle tool calls.
1866def _no_init_or_replace_init(self, *args, **kwargs): 1867 cls = type(self) 1868 1869 if cls._is_protocol: 1870 raise TypeError('Protocols cannot be instantiated') 1871 1872 # Already using a custom `__init__`. No need to calculate correct 1873 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1874 if cls.__init__ is not _no_init_or_replace_init: 1875 return 1876 1877 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1878 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1879 # searches for a proper new `__init__` in the MRO. The new `__init__` 1880 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1881 # instantiation of the protocol subclass will thus use the new 1882 # `__init__` and no longer call `_no_init_or_replace_init`. 1883 for base in cls.__mro__: 1884 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1885 if init is not _no_init_or_replace_init: 1886 cls.__init__ = init 1887 break 1888 else: 1889 # should not happen 1890 cls.__init__ = object.__init__ 1891 1892 cls.__init__(self, *args, **kwargs)
339 @property 340 def tools(self) -> list[Tool]: 341 """The list of tool definitions registered in this toolset.""" 342 ...
The list of tool definitions registered in this toolset.
344 def handle(self, tool_call: ToolCall) -> HandleResult: 345 """ 346 Handle a tool call. 347 The result of the tool call, or the async future of the result, should be returned. 348 The result should be a `ToolReturnValue`. 349 350 This method MUST NOT do any blocking operations because it will be called during 351 consuming the chat response stream. 352 This method MUST NOT raise any exception except for `asyncio.CancelledError`. Any other 353 error should be returned as a `ToolReturnValue` with `is_error=True`. 354 """ 355 ...
Handle a tool call.
The result of the tool call, or the async future of the result, should be returned.
The result should be a ToolReturnValue.
This method MUST NOT do any blocking operations because it will be called during
consuming the chat response stream.
This method MUST NOT raise any exception except for asyncio.CancelledError. Any other
error should be returned as a ToolReturnValue with is_error=True.