async def process_llm_response(self, response, *args, **kwargs):
        """
        Override the base process_llm_response to handle XML structured responses
        including thoughts and tool calls.
        """
        if self._completed:
            return

        message = response.choices[0].message
        finish_reason = response.choices[0].finish_reason

        if not message.content:
            # If there's no content, let the parent class handle the normal tool_calls flow
            return await super().process_llm_response(
                response, *args, **kwargs
            )

        # Get the response content
        content = message.content

        # HACK for gemini
        content = content.replace("```action", "")
        content = content.replace("```tool_code", "")
        content = content.replace("```", "")

        if (
            not content.startswith("<")
            and "deepseek" in self.rag_generation_config.model
        ):  # HACK - fix issues with adding `<think>` to the beginning
            content = "<think>" + content

        # Process any tool calls in the content
        action_matches = self.ACTION_PATTERN.findall(content)
        if action_matches:
            xml_toolcalls = "<ToolCalls>"
            for action_block in action_matches:
                tool_calls_text = []
                # Look for ToolCalls wrapper, or use the raw action block
                calls_wrapper = self.TOOLCALLS_PATTERN.findall(action_block)
                if calls_wrapper:
                    for tw in calls_wrapper:
                        tool_calls_text.append(tw)
                else:
                    tool_calls_text.append(action_block)

                # Process each ToolCall
                for calls_region in tool_calls_text:
                    calls_found = self.TOOLCALL_PATTERN.findall(calls_region)
                    for tc_block in calls_found:
                        tool_name, tool_params = self._parse_single_tool_call(
                            tc_block
                        )
                        if tool_name:
                            tool_call_id = f"call_{abs(hash(tc_block))}"
                            try:
                                tool_result = (
                                    await self.handle_function_or_tool_call(
                                        tool_name,
                                        json.dumps(tool_params),
                                        tool_id=tool_call_id,
                                        save_messages=False,
                                    )
                                )

                                # Add tool result to XML
                                xml_toolcalls += (
                                    f"<ToolCall>"
                                    f"<Name>{tool_name}</Name>"
                                    f"<Parameters>{json.dumps(tool_params)}</Parameters>"
                                    f"<Result>{tool_result.llm_formatted_result}</Result>"
                                    f"</ToolCall>"
                                )

                            except Exception as e:
                                logger.error(f"Error in tool call: {str(e)}")
                                # Add error to XML
                                xml_toolcalls += (
                                    f"<ToolCall>"
                                    f"<Name>{tool_name}</Name>"
                                    f"<Parameters>{json.dumps(tool_params)}</Parameters>"
                                    f"<Result>Error: {str(e)}</Result>"
                                    f"</ToolCall>"
                                )

            xml_toolcalls += "</ToolCalls>"
            pre_action_text = content[: content.find(action_block)]
            post_action_text = content[
                content.find(action_block) + len(action_block) :
            ]
            iteration_text = pre_action_text + xml_toolcalls + post_action_text

            # Create the assistant message
            await self.conversation.add_message(
                Message(role="assistant", content=iteration_text)
            )
        else:
            # Create an assistant message with the content as-is
            await self.conversation.add_message(
                Message(role="assistant", content=content)
            )

        # Only mark as completed if the finish_reason is "stop" or there are no action calls
        # This allows the agent to continue the conversation when tool calls are processed
        if finish_reason == "stop":
            self._completed = True

        Override the base process_llm_response to handle XML structured responses
        including thoughts and tool calls.
        
py/core/agent/base.py