Repository: jerryjliu/gpt_index
File: llama-index-core/llama_index/core/tools/function_tool.py (Python)

Code before:
        return self._async_fn

    @property
    def real_fn(self) -> Union[Callable[..., Any], AsyncCallable]:
        """Real function."""
        if self._real_fn is None:
            raise ValueError("Real function is not set!")

        return self._real_fn

    def _parse_tool_output(self, raw_output: Any) -> List[ContentBlock]:
        """Parse tool output into content blocks."""
        if isinstance(
            raw_output, (TextBlock, ImageBlock, AudioBlock, CitableBlock, CitationBlock)
        ):
            return [raw_output]
        elif isinstance(raw_output, list) and all(
            isinstance(
                item, (TextBlock, ImageBlock, AudioBlock, CitableBlock, CitationBlock)
            )
            for item in raw_output
        ):
            return raw_output
        else:
            return [TextBlock(text=raw_output.get_content())]

    def __call__(self, *args: Any, **kwargs: Any) -> ToolOutput:
        all_kwargs = {**self.partial_params, **kwargs}
        return self.call(*args, **all_kwargs)

    def call(self, *args: Any, **kwargs: Any) -> ToolOutput:
        """Sync Call."""
        all_kwargs = {**self.partial_params, **kwargs}
        if self.requires_context and self.ctx_param_name is not None:
            if self.ctx_param_name not in all_kwargs:
                raise ValueError("Context is required for this tool")

        raw_output = self._fn(*args, **all_kwargs)

        # Exclude the Context param from the tool output so that the Context can be serialized
        tool_output_kwargs = {
            k: v for k, v in all_kwargs.items() if k != self.ctx_param_name
        }

        # Parse tool output into content blocks
        output_blocks = self._parse_tool_output(raw_output)

        # Default ToolOutput based on the raw output
        default_output = ToolOutput(
            blocks=output_blocks,
            tool_name=self.metadata.get_name(),

Review comment (bug):
This will break any time the output is not a document/node object. We need an additional if/else check rather than doing this in the final `else` block (since here, the type is still "any")

Code after:
        """Async function."""
        return self._async_fn

    @property
    def real_fn(self) -> Union[Callable[..., Any], AsyncCallable]:
        """Real function."""
        if self._real_fn is None:
            raise ValueError("Real function is not set!")

        return self._real_fn

    def _parse_tool_output(self, raw_output: Any) -> List[ContentBlock]:
        """Parse tool output into content blocks."""
        if isinstance(
            raw_output, (TextBlock, ImageBlock, AudioBlock, CitableBlock, CitationBlock)
        ):
            return [raw_output]
        elif isinstance(raw_output, list) and all(
            isinstance(
                item, (TextBlock, ImageBlock, AudioBlock, CitableBlock, CitationBlock)
            )
            for item in raw_output
        ):
            return raw_output
        elif isinstance(raw_output, (BaseNode, Document)):
            return [TextBlock(text=raw_output.get_content())]
        elif isinstance(raw_output, list) and all(
            isinstance(item, (BaseNode, Document)) for item in raw_output
        ):
            return [TextBlock(text=item.get_content()) for item in raw_output]
        else:
            return [TextBlock(text=str(raw_output))]

    def __call__(self, *args: Any, **kwargs: Any) -> ToolOutput:
        all_kwargs = {**self.partial_params, **kwargs}
        return self.call(*args, **all_kwargs)

    def call(self, *args: Any, **kwargs: Any) -> ToolOutput:
        """Sync Call."""
        all_kwargs = {**self.partial_params, **kwargs}
        if self.requires_context and self.ctx_param_name is not None:
            if self.ctx_param_name not in all_kwargs:
                raise ValueError("Context is required for this tool")

        raw_output = self._fn(*args, **all_kwargs)

        # Exclude the Context param from the tool output so that the Context can be serialized
        tool_output_kwargs = {
            k: v for k, v in all_kwargs.items() if k != self.ctx_param_name
        }