diff --git a/llama-index-core/.gitignore b/llama-index-core/.gitignore index d1a1c96f83f0f79ab244489bf5cc0023b445d615..09a904e848e03137e5fb007548194fa1c3a9f9ad 100644 --- a/llama-index-core/.gitignore +++ b/llama-index-core/.gitignore @@ -1,4 +1,5 @@ llama_index/core/_static +storage/ .DS_Store # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/llama-index-core/llama_index/core/agent/react/step.py b/llama-index-core/llama_index/core/agent/react/step.py index 272ea226ded76962ea2955b1a614759717b830ab..d15fb4195c35a92733d0334b7b195eb4ec325bef 100644 --- a/llama-index-core/llama_index/core/agent/react/step.py +++ b/llama-index-core/llama_index/core/agent/react/step.py @@ -232,24 +232,27 @@ class ReActAgentWorker(BaseAgentWorker): # call tool with input reasoning_step = cast(ActionReasoningStep, current_reasoning[-1]) - tool = tools_dict[reasoning_step.action] - with self.callback_manager.event( - CBEventType.FUNCTION_CALL, - payload={ - EventPayload.FUNCTION_CALL: reasoning_step.action_input, - EventPayload.TOOL: tool.metadata, - }, - ) as event: - try: - tool_output = tool.call(**reasoning_step.action_input) - except Exception as e: - tool_output = ToolOutput( - content=f"Error: {e!s}", - tool_name=tool.metadata.name, - raw_input={"kwargs": reasoning_step.action_input}, - raw_output=e, - ) - event.on_end(payload={EventPayload.FUNCTION_OUTPUT: str(tool_output)}) + if reasoning_step.action in tools_dict: + tool = tools_dict[reasoning_step.action] + with self.callback_manager.event( + CBEventType.FUNCTION_CALL, + payload={ + EventPayload.FUNCTION_CALL: reasoning_step.action_input, + EventPayload.TOOL: tool.metadata, + }, + ) as event: + try: + tool_output = tool.call(**reasoning_step.action_input) + except Exception as e: + tool_output = ToolOutput( + content=f"Error: {e!s}", + tool_name=tool.metadata.name, + raw_input={"kwargs": reasoning_step.action_input}, + raw_output=e, + ) + event.on_end(payload={EventPayload.FUNCTION_OUTPUT: str(tool_output)}) + else: + tool_output = self._handle_nonexistent_tool_name(reasoning_step) task.extra_state["sources"].append(tool_output) @@ -276,24 +279,27 @@ class ReActAgentWorker(BaseAgentWorker): # call tool with input reasoning_step = cast(ActionReasoningStep, current_reasoning[-1]) - tool = tools_dict[reasoning_step.action] - with self.callback_manager.event( - CBEventType.FUNCTION_CALL, - payload={ - EventPayload.FUNCTION_CALL: reasoning_step.action_input, - EventPayload.TOOL: tool.metadata, - }, - ) as event: - try: - tool_output = await tool.acall(**reasoning_step.action_input) - except Exception as e: - tool_output = ToolOutput( - content=f"Error: {e!s}", - tool_name=tool.metadata.name, - raw_input={"kwargs": reasoning_step.action_input}, - raw_output=e, - ) - event.on_end(payload={EventPayload.FUNCTION_OUTPUT: str(tool_output)}) + if reasoning_step.action in tools_dict: + tool = tools_dict[reasoning_step.action] + with self.callback_manager.event( + CBEventType.FUNCTION_CALL, + payload={ + EventPayload.FUNCTION_CALL: reasoning_step.action_input, + EventPayload.TOOL: tool.metadata, + }, + ) as event: + try: + tool_output = await tool.acall(**reasoning_step.action_input) + except Exception as e: + tool_output = ToolOutput( + content=f"Error: {e!s}", + tool_name=tool.metadata.name, + raw_input={"kwargs": reasoning_step.action_input}, + raw_output=e, + ) + event.on_end(payload={EventPayload.FUNCTION_OUTPUT: str(tool_output)}) + else: + tool_output = self._handle_nonexistent_tool_name(reasoning_step) task.extra_state["sources"].append(tool_output) @@ -303,6 +309,26 @@ class ReActAgentWorker(BaseAgentWorker): print_text(f"{observation_step.get_content()}\n", color="blue") return current_reasoning, False + def _handle_nonexistent_tool_name(self, reasoning_step): + # We still emit a `tool_output` object to the task, so that the LLM can know + # it has hallucinated in the next reasoning step. + with self.callback_manager.event( + CBEventType.FUNCTION_CALL, + payload={ + EventPayload.FUNCTION_CALL: reasoning_step.action_input, + }, + ) as event: + # TODO(L10N): This should be localized. + content = f"Error: No such tool named `{reasoning_step.action}`." + tool_output = ToolOutput( + content=content, + tool_name=reasoning_step.action, + raw_input={"kwargs": reasoning_step.action_input}, + raw_output=content, + ) + event.on_end(payload={EventPayload.FUNCTION_OUTPUT: str(tool_output)}) + return tool_output + def _get_response( self, current_reasoning: List[BaseReasoningStep],