@@ -529,6 +529,7 @@ def streaming_tool(
529529 description_override : str | None = None ,
530530 docstring_style : DocstringStyle | None = None ,
531531 use_docstring_info : bool = True ,
532+ failure_error_function : ToolErrorFunction | None = default_tool_error_function ,
532533 strict_mode : bool = True ,
533534 is_enabled : bool | Callable [[RunContextWrapper [Any ], Agent [Any ]], MaybeAwaitable [bool ]] = True ,
534535 enable_bracketing : bool = True ,
@@ -544,6 +545,7 @@ def streaming_tool(
544545 description_override : str | None = None ,
545546 docstring_style : DocstringStyle | None = None ,
546547 use_docstring_info : bool = True ,
548+ failure_error_function : ToolErrorFunction | None = default_tool_error_function ,
547549 strict_mode : bool = True ,
548550 is_enabled : bool | Callable [[RunContextWrapper [Any ], Agent [Any ]], MaybeAwaitable [bool ]] = True ,
549551 enable_bracketing : bool = True ,
@@ -559,16 +561,49 @@ def streaming_tool(
559561 description_override : str | None = None ,
560562 docstring_style : DocstringStyle | None = None ,
561563 use_docstring_info : bool = True ,
564+ failure_error_function : ToolErrorFunction | None = default_tool_error_function ,
562565 strict_mode : bool = True ,
563566 is_enabled : bool | Callable [[RunContextWrapper [Any ], Agent [Any ]], MaybeAwaitable [bool ]] = True ,
564567 enable_bracketing : bool = True ,
565568) -> StreamingTool | Callable [[StreamingToolFunction [...]], StreamingTool ]:
566569 """
567- Decorator to create a StreamingTool from an async generator function.
570+ Decorator to create a StreamingTool from an async generator function. By default, we will:
571+ 1. Parse the function signature to create a JSON schema for the tool's parameters.
572+ 2. Use the function's docstring to populate the tool's description.
573+ 3. Use the function's docstring to populate argument descriptions.
574+ The docstring style is detected automatically, but you can override it.
575+
568576 The decorated function must be an async generator
569577 (async def a_func(...) -> AsyncIterator[StreamEvent]).
570578 It should `yield` StreamEvent objects as intermediate events,
571579 and finally `yield "..."` to return a string as the final output.
580+
581+ If the function takes a `RunContextWrapper` as the first argument, it *must* match the
582+ context type of the agent that uses the tool.
583+
584+ Args:
585+ func: The function to wrap.
586+ name_override: If provided, use this name for the tool instead of the function's name.
587+ description_override: If provided, use this description for the tool instead of the
588+ function's docstring.
589+ docstring_style: If provided, use this style for the tool's docstring. If not provided,
590+ we will attempt to auto-detect the style.
591+ use_docstring_info: If True, use the function's docstring to populate the tool's
592+ description and argument descriptions.
593+ failure_error_function: If provided, use this function to generate an error message when
594+ the tool call fails. The error message is sent to the LLM as the final yield.
595+ If you pass None, then exceptions will be re-raised.
596+ strict_mode: Whether to enable strict mode for the tool's JSON schema. We *strongly*
597+ recommend setting this to True, as it increases the likelihood of correct JSON input.
598+ If False, it allows non-strict JSON schemas. For example, if a parameter has a default
599+ value, it will be optional, additional properties are allowed, etc. See here for more:
600+ https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas
601+ is_enabled: Whether the tool is enabled. Can be a bool or a callable that takes the run
602+ context and agent and returns whether the tool is enabled. Disabled tools are hidden
603+ from the LLM at runtime.
604+ enable_bracketing: Whether to emit StreamingToolStartEvent and StreamingToolEndEvent
605+ around the tool execution. These events help track the start and end of streaming
606+ tool execution.
572607 """
573608
574609 def _create_streaming_tool (the_func : StreamingToolFunction [...]) -> StreamingTool :
@@ -615,6 +650,10 @@ async def _on_invoke_tool_impl(
615650 else :
616651 logger .debug (f"Invoking tool { schema .name } with input { input_str } " )
617652
653+ # 添加与 function_tool 一致的参数日志
654+ if not _debug .DONT_LOG_TOOL_DATA :
655+ logger .debug (f"Tool call args: { args } , kwargs: { kwargs } " )
656+
618657 try :
619658 if enable_bracketing :
620659 # 合并 args 和 kwargs 为完整的参数字典,用于显示
@@ -649,6 +688,13 @@ async def _on_invoke_tool_impl(
649688 yield StreamingToolEndEvent (
650689 tool_name = schema .name , tool_call_id = tool_call_id
651690 )
691+
692+ # 添加与 function_tool 一致的完成日志
693+ if _debug .DONT_LOG_TOOL_DATA :
694+ logger .debug (f"Tool { schema .name } completed." )
695+ else :
696+ logger .debug (f"Tool { schema .name } returned { event } " )
697+
652698 yield event
653699 return
654700
@@ -661,11 +707,34 @@ async def _on_invoke_tool_impl(
661707 if enable_bracketing :
662708 yield StreamingToolEndEvent (tool_name = schema .name , tool_call_id = tool_call_id )
663709
664- except Exception :
710+ except Exception as e :
665711 # 异常情况下也要确保发送结束事件
666712 if enable_bracketing :
667713 yield StreamingToolEndEvent (tool_name = schema .name , tool_call_id = tool_call_id )
668- raise
714+
715+ # 与 function_tool 一致的异常处理
716+ if failure_error_function is None :
717+ raise
718+
719+ # 调用错误处理函数生成错误消息
720+ error_message = failure_error_function (ctx , e )
721+ if inspect .isawaitable (error_message ):
722+ error_message = await error_message
723+
724+ # 添加错误跟踪,与 function_tool 保持一致
725+ _error_tracing .attach_error_to_current_span (
726+ SpanError (
727+ message = "Error running tool (non-fatal)" ,
728+ data = {
729+ "tool_name" : schema .name ,
730+ "error" : str (e ),
731+ },
732+ )
733+ )
734+
735+ # 将错误消息作为最终结果返回
736+ yield str (error_message )
737+ return
669738
670739 return StreamingTool (
671740 name = schema .name ,
@@ -676,7 +745,12 @@ async def _on_invoke_tool_impl(
676745 is_enabled = is_enabled ,
677746 )
678747
679- if func :
748+ # If func is actually a callable, we were used as @streaming_tool with no parentheses
749+ if callable (func ):
680750 return _create_streaming_tool (func )
681- else :
682- return _create_streaming_tool
751+
752+ # Otherwise, we were used as @streaming_tool(...), so return a decorator
753+ def decorator (real_func : StreamingToolFunction [...]) -> StreamingTool :
754+ return _create_streaming_tool (real_func )
755+
756+ return decorator
0 commit comments