async def take_step(
        self,
        ctx: Context,
        llm_input: List[ChatMessage],
        tools: Sequence[AsyncBaseTool],
        memory: BaseMemory,
    ) -> AgentOutput:
        """
        Take a single step with the Azure Foundry agent.
        Interacts with Azure backend and returns AgentOutput (response, tool_calls, etc).
        """
        # Convert the entire llm_input to Azure content blocks
        azure_content_blocks = (
            self._llama_to_azure_content_blocks(llm_input) if llm_input else []
        )
        await self._ensure_agent(tools=tools)
        assert self._thread_id is not None, (
            "Thread ID must be set after _ensure_agent()"
        )
        assert self._agent is not None, "Agent must be set after _ensure_agent()"

        tool_calls = []
        response_msg = None
        # Only send a user message if there is new user input
        if azure_content_blocks:
            if self._verbose:
                print(
                    f"[AzureFoundryWorkflowAgent] Sending user message blocks to thread_id={self._thread_id}"
                )
            await self._client.agents.messages.create(
                thread_id=self._thread_id, role="user", content=azure_content_blocks
            )
            if self._verbose:
                print(
                    f"[AzureFoundryWorkflowAgent] Starting run for agent_id={self._agent.id} on thread_id={self._thread_id}"
                )
            run = await self._client.agents.runs.create(
                thread_id=self._thread_id, agent_id=self._agent.id
            )
            self._run_id = run.id
            current_run = run
            while current_run.status in ["queued", "in_progress", "requires_action"]:
                await asyncio.sleep(self._run_retrieve_sleep_time)
                current_run = await self._client.agents.runs.get(
                    thread_id=self._thread_id, run_id=self._run_id
                )
                if self._verbose:
                    print(
                        f"[AzureFoundryWorkflowAgent] Run status: {current_run.status}"
                    )
                if current_run.status == "requires_action":
                    if self._verbose:
                        print(
                            f"[AzureFoundryWorkflowAgent] Run requires action: {getattr(current_run, 'required_action', None)}"
                        )
                    break

            if current_run.status == "failed":
                return AgentOutput(
                    response=ChatMessage(role="assistant", content="Run failed."),
                    tool_calls=[],
                    raw=current_run,
                    current_agent_name=self.name,
                )
            required_action = getattr(current_run, "required_action", None)
            if (
                required_action
                and getattr(required_action, "type", None) == "submit_tool_outputs"
                and isinstance(required_action, SubmitToolOutputsAction)
            ):
                submit_tool_outputs = required_action.submit_tool_outputs
                for call in getattr(submit_tool_outputs, "tool_calls", []):
                    # For function tool calls
                    if isinstance(call, RequiredFunctionToolCall):
                        function = getattr(call, "function", None)
                        tool_name = getattr(function, "name", "") if function else ""
                        arguments = (
                            getattr(function, "arguments", "{}") if function else "{}"
                        )
                        try:
                            tool_kwargs = json.loads(arguments)
                        except Exception:
                            tool_kwargs = {}
                        tool_calls.append(
                            ToolSelection(
                                tool_id=getattr(call, "id", ""),
                                tool_name=tool_name,
                                tool_kwargs=tool_kwargs,
                            )
                        )
            # Get the latest assistant message if available
            latest_msg = None
            async for msg in self._client.agents.messages.list(
                thread_id=self._thread_id, run_id=self._run_id, order="desc"
            ):
                if getattr(msg, "role", None) == "assistant" and getattr(
                    msg, "content", None
                ):
                    latest_msg = self._from_azure_thread_message(msg)
                    break
            # If no assistant message found, try to get the last assistant message in the thread
            if not latest_msg:
                async for msg in self._client.agents.messages.list(
                    thread_id=self._thread_id, order="desc"
                ):
                    if getattr(msg, "role", None) == "assistant" and getattr(
                        msg, "content", None
                    ):
                        latest_msg = self._from_azure_thread_message(msg)
                        break
            response_msg = (
                latest_msg
                if latest_msg
                else ChatMessage(role="assistant", content="No response from agent.")
            )
        else:
            # No new user input: fetch the latest assistant message after tool call resolution
            latest_msg = None
            async for msg in self._client.agents.messages.list(
                thread_id=self._thread_id, order="desc"
            ):
                if getattr(msg, "role", None) == "assistant" and getattr(
                    msg, "content", None
                ):
                    latest_msg = self._from_azure_thread_message(msg)
                    break
            response_msg = (
                latest_msg
                if latest_msg
                else ChatMessage(role="assistant", content="No response from agent.")
            )

        return AgentOutput(
            response=response_msg,
            tool_calls=tool_calls,
            raw=current_run if azure_content_blocks else None,
            current_agent_name=self.name,
        )

        Take a single step with the Azure Foundry agent.
        Interacts with Azure backend and returns AgentOutput (response, tool_calls, etc).
        
llama-index-integrations/agent/llama-index-agent-azure/llama_index/agent/azure_foundry_agent/base.py