from __future__ import annotations

import json
import time
import uuid
from typing import AsyncIterator, List, Any, Dict, Iterator, Optional, Union
from loguru import logger
from pydantic import Field

from turbo_agent_core.schema.enums import JSON, ActionStatus
from turbo_agent_core.schema.events import (
    BaseEvent,
    RunLifecycleCreatedEvent,
    RunLifecycleCreatedPayload,
    RunLifecycleCompletedEvent,
    RunLifecycleCompletedPayload,
    RunLifecycleFailedEvent,
    RunLifecycleFailedPayload,
    ContentReactStartEvent,
    ContentReactStartPayload,
    ContentReactEndEvent,
    ContentReactEndPayload,
    ContentActionStartEvent,
    ContentActionStartPayload,
    ContentActionEndEvent,
    ContentActionEndPayload,
    ContentActionResultEndEvent,
    ContentActionResultEndPayload,
    ExecutorMetadata,
    UserInfo,
)
from turbo_agent_core.schema.agents import Agent, APITool, AgentTool, BuiltinTool, LLMTool, Tool
from turbo_agent_core.schema.states import Conversation, Message, Action, MessageRole
from turbo_agent_core.utils.param_schema import parameters_to_json_schema
from turbo_agent_core.utils.event_integrator import ChildEventStreamIntegrator
from turbo_agent_core.utils.identity import build_scoped_id
from turbo_agent_runtime.utils.executor_identity import build_executor_id
from turbo_agent_runtime.utils.tool_convert import convert_tools_to_openai_functions

# from ..base import EntityRuntime
# from turbo_agent_runtime.utils.message_converter import convert_state_message_to_langchain

class AgentRuntime(Agent):
    tools: List[Union[APITool, LLMTool, AgentTool, BuiltinTool, Tool]] = Field(default_factory=list)

    def _build_react_start_event(
        self,
        *,
        trace_id: str,
        run_id: str,
        executor_type: Any,
        executor_id: str,
        executor_path: List[str],
        react_id: str,
    ) -> ContentReactStartEvent:
        return ContentReactStartEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_type=executor_type,
            executor_id=executor_id,
            executor_path=executor_path,
            react_id=react_id,
            payload=ContentReactStartPayload(),
        )

    def _build_react_end_event(
        self,
        *,
        trace_id: str,
        run_id: str,
        executor_type: Any,
        executor_id: str,
        executor_path: List[str],
        react_id: str,
        status: str = "completed",
    ) -> ContentReactEndEvent:
        return ContentReactEndEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_type=executor_type,
            executor_id=executor_id,
            executor_path=executor_path,
            react_id=react_id,
            payload=ContentReactEndPayload(status=status),
        )

    def _yield_filtered_event(self, event: BaseEvent) -> Iterator[BaseEvent]:
        filtered_event = self.filter_event_for_inner_thought(event)
        if filtered_event is not None:
            yield filtered_event

    async def _ayield_filtered_event(self, event: BaseEvent) -> AsyncIterator[BaseEvent]:
        filtered_event = self.filter_event_for_inner_thought(event)
        if filtered_event is not None:
            yield filtered_event

    def _dump_for_event(self, obj: Any) -> Any:
        if obj is None:
            return None
        if hasattr(obj, "model_dump"):
            try:
                return obj.model_dump()
            except Exception:
                return obj
        if isinstance(obj, list):
            return [self._dump_for_event(x) for x in obj]
        if isinstance(obj, dict):
            return {k: self._dump_for_event(v) for k, v in obj.items()}
        return obj

    def run(self, input: Conversation, leaf_message_id: Optional[str] = None, tools:Optional[List[Tool]] = None,  **kwargs) -> Message:
        """最小运行图：角色定义→模型推理→工具选择→工具执行→结果→继续推理/返回。"""
        # 同步版本实现
        leaf_message_id = leaf_message_id or kwargs.get("leaf_message_id")
        
        messages = self._prepare_messages(input, leaf_message_id)
        
        max_steps = 100
        step = 0
        final_response = None
        
        while step < max_steps:
            step += 1
            
            if not self.model:
                raise ValueError("Agent has no model assigned")
                
            llm_result = self.model.run(messages, tools=self.tools)
            
            ai_msg = llm_result
            messages.append(ai_msg)
            
            # 检查是否有需要执行的 Action
            # 逻辑说明：
            # 1. 遍历 ai_msg.actions，检查每个 action 的 status。
            # 2. 如果 status 为 Finished/Failed/Interrupted/Succeed/Stopped/Aborted，则视为已执行，跳过。
            # 3. 如果未执行，则执行工具，并将结果写入 action.observation (注意：Action 模型目前没有 observation 字段，
            #    通常做法是追加 ToolMessage。但根据需求，这里我们将结果写入 Message(role='tool') 并追加到 messages 列表，
            #    同时更新 action.status 为 Finished，以标记该 action 已处理)。
            # 4. 如果 ai_msg 中所有 action 都已执行过（或没有 action），则跳出循环。
            
            has_unexecuted_action = False
            
            if ai_msg.actions:
                for action in ai_msg.actions:
                    # 判断是否已执行
                    if action.status in [
                        ActionStatus.Finished, 
                        ActionStatus.Failed, 
                        ActionStatus.Aborted,
                        ActionStatus.Succeed,
                        ActionStatus.Stopped,
                        ActionStatus.Aborted,
                        ActionStatus.Waitting
                    ]:
                        continue
                    
                    has_unexecuted_action = True
                    
                    # 执行工具（同步）：返回结构化结果，包含状态与输出
                    # 说明：_execute_tool_sync 会捕获异常并返回 {status, output, error, raw}
                    result = self._execute_tool_sync(action, **kwargs)
                    action.status = result.get("status", ActionStatus.Finished)
                    action.observation = result.get("output", result)
                    # 注意：结果写入 action.observation，由 message_converter 转换为 ToolMessage
            
            if not has_unexecuted_action:
                final_response = llm_result
                break
                
        return final_response or Message(id=str(uuid.uuid4()), role="assistant", content="")

    def stream(self, input: Conversation, leaf_message_id: Optional[str] = None,  tools:Optional[List[Tool]] = None, **kwargs) -> Iterator[BaseEvent]:
        # 同步流式版本实现
        start_time = time.time()
        trace_id = str(uuid.uuid4())
        run_id = f"run_{self.id}_{int(time.time()*1000)}"
        executor_type = self.run_type
        executor_id = build_executor_id(self, executor_type)
        executor_path = [executor_id]

        total_usage = {
            "promptToken": 0, "completionToken": 0, "reasoningToken": 0, "tokenCost": 0, "moneyCost": 0.0, "timeCost": 0.0
        }

        leaf_message_id = leaf_message_id or kwargs.get("leaf_message_id")

        tools_for_event = tools if tools is not None else self.tools

        user_id = kwargs.get("user_id") or kwargs.get("userId") or "local"
        username = kwargs.get("username") or "local"

        yield RunLifecycleCreatedEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_type=executor_type,
            executor_id=executor_id,
            executor_path=executor_path,
            executor_metadata=ExecutorMetadata(
                id=self.id,
                name=self.name,
                belong_to_project_path=self.belong_to_project_path,
                name_id=self.name_id,
                description=self.description,
                avatar_uri=self.avatar_uri,
                run_type=self.run_type,
                version_id=self.version_id,
                version=self.version_tag,
            ),
            user_metadata=UserInfo(id=str(user_id), username=str(username)),
            payload=RunLifecycleCreatedPayload(
                input_data={
                    "conversation": self._dump_for_event(input),
                    "leaf_message_id": leaf_message_id,
                    "tools": self._dump_for_event(tools_for_event),
                }
            ),
        )
        
        try:
            messages = self._prepare_messages(input, leaf_message_id)
            
            max_steps = 100
            step = 0
            
            while step < max_steps:
                step += 1
                
                if not self.model:
                    raise ValueError("Agent has no model assigned")
                
                llm_output = None
                react_id = f"react_{run_id}_{step}"
                yield self._build_react_start_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                )
                integrator = ChildEventStreamIntegrator(
                    root_trace_id=trace_id,
                    parent_trace_path=[],
                    parent_executor_path=executor_path,
                    react_id=react_id,
                    ignore_child_trace_id=True,
                    parent_run_id=run_id,
                    forward_as_parent_events=True
                )
                # 注意：tools 参数优先，其次回退到 self.tools
                model_tools = tools if tools is not None else self.tools
                for event in integrator.integrate_sync(self.model.stream(messages, tools=model_tools)):
                    yield from self._yield_filtered_event(event)

                if integrator.child_usage:
                    logger.debug(f"Step {step}: child_usage = {integrator.child_usage}")
                    for k, v in integrator.child_usage.items():
                        if k in total_usage and v is not None:
                            total_usage[k] += v
                
                llm_output = integrator.child_final_result
                
                if not llm_output:
                    break
                
                ai_msg = llm_output
                messages.append(ai_msg)
                
                has_unexecuted_action = False
                
                if ai_msg.actions:
                    for i, action in enumerate(ai_msg.actions):
                        if action.status in [
                            ActionStatus.Finished, 
                            ActionStatus.Failed, 
                            ActionStatus.Aborted,
                            ActionStatus.Succeed,
                            ActionStatus.Stopped,
                            ActionStatus.Aborted
                        ]:
                            continue
                        
                        has_unexecuted_action = True
                        
                        # 事件：动作开始（用于外部监听工具调用）
                        yield ContentActionStartEvent(
                            trace_id=trace_id,
                            trace_path=[],
                            run_id=run_id,
                            run_path=[run_id],
                            executor_type=executor_type,
                            executor_id=executor_id, executor_path=executor_path,
                            react_id=react_id,
                            action_id=action.id,
                            payload=ContentActionStartPayload(
                                name=action.name,
                                call_type="TOOL",
                            )
                        )

                        # 事件：动作参数权威态（建议每次都发，便于 Store/UI 绑定）
                        args = action.input if isinstance(action.input, dict) else {"__raw__": action.input}
                        yield ContentActionEndEvent(
                            trace_id=trace_id,
                            trace_path=[],
                            run_id=run_id,
                            run_path=[run_id],
                            executor_type=executor_type,
                            executor_id=executor_id, executor_path=executor_path,
                            react_id=react_id,
                            action_id=action.id,
                            payload=ContentActionEndPayload(arguments=args)
                        )

                        # 执行工具（同步流式）：统一调用下游 tool.stream 并转发事件。
                        tool_list = tools if tools is not None else self.tools
                        tool = next(
                            (
                                t
                                for t in tool_list
                                if getattr(t, "name_id", None) == action.name or build_scoped_id(t) == action.name
                            ),
                            None,
                        )
                        if tool is None:
                            action.status = ActionStatus.Failed
                            action.observation = {"error": f"Tool {action.name} not found."}
                        else:
                            try:
                                tool_input = action.input
                                if isinstance(tool_input, str):
                                    try:
                                        tool_input = json.loads(tool_input)
                                    except Exception:
                                        pass

                                child_integrator = ChildEventStreamIntegrator(
                                    root_trace_id=trace_id,
                                    parent_trace_path=[],
                                    parent_executor_path=executor_path,
                                    action_id=action.id,
                                    react_id=react_id,
                                    parent_run_id=run_id,
                                )
                                if hasattr(tool, "stream"):
                                    for evt in child_integrator.integrate_sync(tool.stream(tool_input)):
                                        yield from self._yield_filtered_event(evt)
                                else:
                                    # 极端兜底：没有 stream 就退回 run
                                    res = tool.run(tool_input)
                                    child_integrator.child_final_result = res
                                
                                action_usage = {}
                                if child_integrator.child_usage:
                                    logger.debug(f"Step {step}: action_usage = {child_integrator.child_usage}")
                                    action_usage = child_integrator.child_usage
                                    for k, v in child_integrator.child_usage.items():
                                        if k in total_usage and v is not None:
                                            total_usage[k] += v

                                if child_integrator.child_error:
                                    action.status = ActionStatus.Failed
                                    action.observation = {"error": child_integrator.child_error}
                                else:
                                    action.status = ActionStatus.Succeed
                                    action.observation = child_integrator.child_final_result
                            except Exception as e:
                                logger.exception(f"[AgentRuntime] 工具执行失败(stream): {e}")
                                action.status = ActionStatus.Failed
                                action.observation = {"error": str(e)}
                                action_usage = {}

                        # 事件：动作结果权威态（容器关闭）
                        status = "success" if action.status == ActionStatus.Succeed else "error"
                        yield ContentActionResultEndEvent(
                            trace_id=trace_id,
                            trace_path=[],
                            run_id=run_id,
                            run_path=[run_id],
                            executor_type=executor_type,
                            executor_id=executor_id, executor_path=executor_path,
                            react_id=react_id,
                            action_id=action.id,
                            payload=ContentActionResultEndPayload(
                                full_result=action.observation, 
                                status=status,
                                usage=action_usage
                            )
                        )

                yield self._build_react_end_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                )
                
                if not has_unexecuted_action:
                    break
            
            total_usage["timeCost"] = time.time() - start_time

            logger.info(f"[AgentRuntime.stream] Completed run with trace_id={trace_id}, run_id={run_id}, total_usage={total_usage}")

            yield RunLifecycleCompletedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_type=executor_type,
                executor_id=executor_id,
                executor_path=executor_path,
                payload=RunLifecycleCompletedPayload(output=None, usage=total_usage),
            )
        except Exception as e:
            logger.exception(e)
            if 'react_id' in locals() and react_id:
                yield self._build_react_end_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                    status="failed",
                )
            yield RunLifecycleFailedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_type=executor_type,
                executor_id=executor_id,
                executor_path=executor_path,
                payload=RunLifecycleFailedPayload(error={"code": "RuntimeError", "message": str(e)}),
            )

    async def a_run(self, input: Conversation, leaf_message_id: Optional[str] = None,  tools:Optional[List[Tool]] = None, **kwargs) -> Message:
        leaf_message_id = leaf_message_id 
        messages = self._prepare_messages(input, leaf_message_id)
        
        max_steps = 100
        step = 0
        
        final_output = None
        
        while step < max_steps:
            step += 1
            
            if not self.model:
                raise ValueError("Agent has no model assigned")
            
            ai_msg = await self.model.a_run(messages, tools=self.tools)
            
            messages.append(ai_msg)
            final_output = ai_msg
            
            has_unexecuted_action = False
            
            if ai_msg.actions:
                for action in ai_msg.actions:
                    if action.status in [
                        ActionStatus.Finished, 
                        ActionStatus.Failed, 
                        ActionStatus.Aborted,
                        ActionStatus.Succeed,
                        ActionStatus.Stopped,
                        ActionStatus.Aborted
                    ]:
                        continue
                    
                    has_unexecuted_action = True
                    
                    # 执行工具（异步）：返回结构化结果，包含状态与输出
                    result = await self._execute_tool_async(action, **kwargs)
                    action.status = result.get("status", ActionStatus.Finished)
                    action.observation = result.get("output", result)
            
            if not has_unexecuted_action:
                break
                
        return final_output or Message(id=str(uuid.uuid4()), role="assistant", content="")
   
    async def a_stream(self, input: Conversation, leaf_message_id: Optional[str] = None,  tools:Optional[List[Tool]] = None, **kwargs) -> AsyncIterator[BaseEvent]:
        start_time = time.time()    
        trace_id = str(uuid.uuid4())
        run_id = f"run_{self.id}_{time.time()*1000}"
        executor_type = self.run_type
        executor_id = build_executor_id(self, executor_type)
        executor_path = [executor_id]

        total_usage = {
            "promptToken": 0, 
            "completionToken": 0, 
            "reasoningToken": 0,
            "tokenCost": 0, 
            "moneyCost": 0.0, 
            "timeCost": 0.0
        }

        leaf_message_id = leaf_message_id or kwargs.get("leaf_message_id")
        logger.debug(f"[AgentRuntime.a_stream] Starting stream with trace_id={trace_id}, run_id={run_id}, leaf_message_id={leaf_message_id}")

        tools_for_event = tools if tools is not None else self.tools

        user_id = kwargs.get("user_id") or kwargs.get("userId") or "local"
        username = kwargs.get("username") or "local"

        yield RunLifecycleCreatedEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_type=executor_type,
            executor_id=executor_id,
            executor_path=executor_path,
            executor_metadata=ExecutorMetadata(
                id=self.id,
                name=self.name,
                belong_to_project_path=self.belong_to_project_path,
                name_id=self.name_id,
                description=self.description,
                avatar_uri=self.avatar_uri,
                run_type=self.run_type,
                version_id=self.version_id,
                version=self.version_tag,
            ),
            user_metadata=UserInfo(id=str(user_id), username=str(username)),
            payload=RunLifecycleCreatedPayload(
                input_data={
                    "conversation_id": input.id,
                    "conversation": self._dump_for_event(input),
                    "leaf_message_id": leaf_message_id,
                    "tools": self._dump_for_event(tools_for_event),
                }
            ),
        )
        
        try:
            messages = self._prepare_messages(input, leaf_message_id)
            
            max_steps = 100
            step = 0
            
            while step < max_steps:
                step += 1
                
                if not self.model:
                    raise ValueError("Agent has no model assigned")
                
                llm_output = None
                react_id = f"react_{run_id}_{step}"
                yield self._build_react_start_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                )
                logger.debug(f"[AgentRuntime] Start model stream. tools count: {len(self.tools) if self.tools else 0}")
                integrator = ChildEventStreamIntegrator(
                    root_trace_id=trace_id,
                    parent_trace_path=[],
                    parent_executor_path=executor_path,
                    react_id=react_id,
                    ignore_child_trace_id=True,
                    parent_run_id=run_id,
                    forward_as_parent_events=True
                )
                model_tools = tools if tools is not None else self.tools
                async for event in integrator.integrate_async(self.model.a_stream(messages, tools=model_tools)):
                    logger.debug(f"[AgentRuntime] Model event: {event}")
                    async for filtered_event in self._ayield_filtered_event(event):
                        yield filtered_event

                if integrator.child_usage:
                    logger.debug(f"Step {step}: child_usage = {integrator.child_usage}")
                    for k, v in integrator.child_usage.items():
                        if k in total_usage and v is not None:
                            total_usage[k] += v
                
                llm_output = integrator.child_final_result
                if llm_output:
                    logger.debug(f"[AgentRuntime] Model completed. Output: {llm_output}")
                
                if not llm_output:
                    logger.warning("[AgentRuntime] No LLM output received from model stream.")
                    break
                
                ai_msg = llm_output
                messages.append(ai_msg)
                
                has_unexecuted_action = False
                
                if ai_msg.actions:
                    logger.debug(f"[AgentRuntime] Actions detected: {len(ai_msg.actions)}")
                    for i, action in enumerate(ai_msg.actions):
                        if action.status in [
                            ActionStatus.Finished, 
                            ActionStatus.Failed, 
                            ActionStatus.Aborted,
                            ActionStatus.Succeed,
                            ActionStatus.Stopped,
                            ActionStatus.Aborted
                        ]:
                            continue
                        
                        has_unexecuted_action = True
                        
                        # 事件：动作开始（用于外部监听工具调用）
                        logger.debug(f"[AgentRuntime] Yielding ContentActionStartEvent for action: {action.name}")
                        # yield ContentActionStartEvent(
                        #     trace_id=trace_id,
                        #     trace_path=[],
                        #     run_id=run_id,
                        #     run_path=[run_id],
                        #     executor_type=executor_type,
                        #     executor_id=executor_id, executor_path=executor_path,
                        #     react_id=react_id,
                        #     action_id=action.id,
                        #     payload=ContentActionStartPayload(
                        #         name=action.name,
                        #         call_type="TOOL",
                        #     )
                        # )

                        # 事件：动作参数权威态（建议每次都发，便于 Store/UI 绑定）
                        args = action.input if isinstance(action.input, dict) else {"__raw__": action.input}
                        # yield ContentActionEndEvent(
                        #     trace_id=trace_id,
                        #     trace_path=[],
                        #     run_id=run_id,
                        #     run_path=[run_id],
                        #     executor_type=executor_type,
                        #     executor_id=executor_id, executor_path=executor_path,
                        #     react_id=react_id,
                        #     action_id=action.id,
                        #     payload=ContentActionEndPayload(arguments=args)
                        # )

                        # 执行工具（异步流式）：统一调用下游 tool.a_stream 并转发事件。
                        tool_list = tools if tools is not None else self.tools
                        tool = next(
                            (
                                t
                                for t in tool_list
                                if getattr(t, "name_id", None) == action.name or build_scoped_id(t) == action.name
                            ),
                            None,
                        )
                        if tool is None:
                            action.status = ActionStatus.Failed
                            action.observation = {"error": f"Tool {action.name} not found."}
                        else:
                            try:
                                tool_input = action.input
                                if isinstance(tool_input, str):
                                    try:
                                        tool_input = json.loads(tool_input)
                                    except Exception:
                                        pass

                                child_integrator = ChildEventStreamIntegrator(
                                    root_trace_id=trace_id,
                                    parent_run_id=run_id,
                                    parent_trace_path=[],
                                    parent_executor_path=executor_path,
                                    action_id=action.id,
                                    react_id=react_id,
                                )
                                if hasattr(tool, "a_stream"):
                                    async for evt in child_integrator.integrate_async(tool.a_stream(tool_input)):
                                        async for filtered_event in self._ayield_filtered_event(evt):
                                            yield filtered_event
                                else:
                                    # 兜底：没有 a_stream 就退回 a_run/run
                                    if hasattr(tool, "a_run"):
                                        res = await tool.a_run(tool_input)
                                    else:
                                        res = tool.run(tool_input)
                                    child_integrator.child_final_result = res
                                
                                action_usage = {}
                                if child_integrator.child_usage:
                                    logger.debug(f"Step {step}: child_usage = {child_integrator.child_usage}")
                                    action_usage = child_integrator.child_usage
                                    for k, v in child_integrator.child_usage.items():
                                        if k in total_usage and v is not None:
                                            total_usage[k] += v

                                if child_integrator.child_error:
                                    action.status = ActionStatus.Failed
                                    action.observation = {"error": child_integrator.child_error}
                                else:
                                    action.status = ActionStatus.Succeed
                                    action.observation = child_integrator.child_final_result
                                
                                if child_integrator.child_usage:
                                    logger.debug(f"[AgentRuntime] Tool action usage: {child_integrator.child_usage}")
                                    
                            except Exception as e:
                                logger.exception(f"[AgentRuntime] 工具执行失败(a_stream): {e}")
                                action.status = ActionStatus.Failed
                                action.observation = {"error": str(e)}
                                action_usage = {}

                        # 事件：动作结果权威态（容器关闭）
                        # status = "success" if action.status == ActionStatus.Succeed else "error"
                        # yield ContentActionResultEndEvent(
                        #     trace_id=trace_id,
                        #     trace_path=[],
                        #     run_id=run_id,
                        #     run_path=[run_id],
                        #     executor_type=executor_type,
                        #     executor_id=executor_id, executor_path=executor_path,
                        #     react_id=react_id,
                        #     action_id=action.id,
                        #     payload=ContentActionResultEndPayload(
                        #         full_result=action.observation, 
                        #         status=status,
                        #         usage=action_usage
                        #     )
                        # )

                yield self._build_react_end_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                )

                if not has_unexecuted_action:
                    break
        
            # 更新总耗时
            total_usage["timeCost"] = time.time() - start_time
            logger.info(f"[AgentRuntime.stream] Completed run with trace_id={trace_id}, run_id={run_id}, total_usage={total_usage}")
            yield RunLifecycleCompletedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_type=executor_type,
                executor_id=executor_id,
                executor_path=executor_path,
                payload=RunLifecycleCompletedPayload(output=None, usage=total_usage),
            )
            
        except Exception as e:
            logger.exception(e)
            if 'react_id' in locals() and react_id:
                yield self._build_react_end_event(
                    trace_id=trace_id,
                    run_id=run_id,
                    executor_type=executor_type,
                    executor_id=executor_id,
                    executor_path=executor_path,
                    react_id=react_id,
                    status="failed",
                )
            yield RunLifecycleFailedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_type=executor_type,
                executor_id=executor_id,
                executor_path=executor_path,
                payload=RunLifecycleFailedPayload(error={"code": "RuntimeError", "message": str(e)}),
            )

    def _prepare_messages(self, input: Conversation, leaf_message_id: Optional[str] = None) -> List[Message]:
        messages: List[Message] = []
        
        # 1. 获取原始消息列表
        raw_messages = []
        if isinstance(input, Conversation):
            history = input.get_history(leaf_message_id)
            raw_messages = history
        elif isinstance(input, dict):
            content = input.get("text") or str(input)
            raw_messages.append(Message(id=str(uuid.uuid4()), role=MessageRole.user, content=content))
        else:
            raw_messages.append(Message(id=str(uuid.uuid4()), role=MessageRole.user, content=str(input)))
            
        # 2. 过滤掉已有的 SystemMessage
        messages = [m for m in raw_messages if m.role != MessageRole.system]
        
        # 3. 插入 Agent 的 System Prompt
        sys_prompt = self._build_system_prompt()
        if sys_prompt:
            messages.insert(0, Message(id=str(uuid.uuid4()), role=MessageRole.system, content=sys_prompt))
            
        return messages

    # ------- helpers -------
    def _get_langchain_tools(self) -> List[Dict[str, Any]]:
        return convert_tools_to_openai_functions(self.tools, parameters_to_json_schema=parameters_to_json_schema)

    async def _execute_tool_async(self, action: Action, **kwargs) -> Dict[str, Any]:
        """
        异步执行工具：
        - 输入：Action（包含工具名与参数）
        - 输出：结构化结果 dict，包含 status（ActionStatus）、output（字符串）、error（可选）、raw（原始返回）
        - 异常捕获：所有异常会被捕获并转为失败状态，便于上层逻辑判断
        """
        tool = next(
            (
                t
                for t in self.tools
                if t.name_id == action.name or build_scoped_id(t) == action.name
            ),
            None,
        )
        if not tool:
            return {"status": ActionStatus.Failed, "error": f"Tool {action.name} not found.", "output": ""}
        
        try:
            tool_input = action.input
            if isinstance(tool_input, str):
                try:
                    tool_input = json.loads(tool_input)
                except:
                    pass
            logger.debug(f"[AgentRuntime] 异步执行工具: name={action.name}, input={tool_input}")
            logger.debug(f"[AgentRuntime] Tool instance type: {type(tool)}")
            logger.debug(f"[AgentRuntime] Tool method a_run: {getattr(tool, 'a_run', 'Not Found')}")
            
            # 1. APITool / AgentTool (ToolVersion)
            if isinstance(tool, (APITool, AgentTool)):
                res = await tool.a_run(tool_input, **kwargs)
                output = res.get("output", res) if isinstance(res, dict) else res
                logger.debug(f"[AgentRuntime] 工具返回(APITool/AgentTool async): status=Succeed, output={output}")
                return {"status": ActionStatus.Succeed, "output": str(output), "raw": res}
            
            # 2. Agent / LLMTool (BasicAgent)
            if isinstance(tool, (Agent, LLMTool)):
                 res = await tool.a_run(tool_input, **kwargs)
                 output = res.content if isinstance(res, Message) else res
                 logger.debug(f"[AgentRuntime] 工具返回(Agent/LLMTool async): status=Succeed, output={output}")
                 return {"status": ActionStatus.Succeed, "output": str(output), "raw": res}
            
            if hasattr(tool, "a_run"):
                res = await tool.a_run(tool_input, **kwargs)
                logger.debug(f"[AgentRuntime] 工具返回(generic async): status=Succeed, output={res}")
                return {"status": ActionStatus.Succeed, "output": str(res), "raw": res}
            elif hasattr(tool, "run"):
                res = tool.run(tool_input, **kwargs)
                logger.debug(f"[AgentRuntime] 工具返回(generic sync-in-async): status=Succeed, output={res}")
                return {"status": ActionStatus.Succeed, "output": str(res), "raw": res}
            else:
                return {"status": ActionStatus.Failed, "error": "Tool runtime not available.", "output": ""}
        except Exception as e:
            logger.exception(f"[AgentRuntime] 工具执行失败(async): {e}")
            return {"status": ActionStatus.Failed, "error": f"Error executing tool: {e}", "output": ""}

    def _execute_tool_sync(self, action: Action, **kwargs) -> Dict[str, Any]:
        """
        同步执行工具：
        - 输入：Action（包含工具名与参数）
        - 输出：结构化结果 dict，包含 status（ActionStatus）、output（字符串）、error（可选）、raw（原始返回）
        - 异常捕获：所有异常会被捕获并转为失败状态，便于上层逻辑判断
        """
        tool = next(
            (
                t
                for t in self.tools
                if t.name_id == action.name or build_scoped_id(t) == action.name
            ),
            None,
        )
        if not tool:
            return {"status": ActionStatus.Failed, "error": f"Tool {action.name} not found.", "output": ""}
        
        try:
            tool_input = action.input
            if isinstance(tool_input, str):
                try:
                    tool_input = json.loads(tool_input)
                except:
                    pass
            logger.debug(f"[AgentRuntime] 同步执行工具: name={action.name}, input={tool_input}")
            
            # 1. APITool / AgentTool (ToolVersion)
            if isinstance(tool, (APITool, AgentTool)):
                res = tool.run(tool_input, **kwargs)
                output = res.get("output", res) if isinstance(res, dict) else res
                logger.debug(f"[AgentRuntime] 工具返回(APITool/AgentTool sync): status=Succeed, output={output}")
                return {"status": ActionStatus.Succeed, "output": str(output), "raw": res}
            
            # 2. Agent / LLMTool (BasicAgent)
            if isinstance(tool, (Agent, LLMTool)):
                 res = tool.run(tool_input, **kwargs)
                 output = res.content if isinstance(res, Message) else res
                 logger.debug(f"[AgentRuntime] 工具返回(Agent/LLMTool sync): status=Succeed, output={output}")
                 return {"status": ActionStatus.Succeed, "output": str(output), "raw": res}
            
            if hasattr(tool, "run"):
                res = tool.run(tool_input, **kwargs)
                logger.debug(f"[AgentRuntime] 工具返回(generic sync): status=Succeed, output={res}")
                return {"status": ActionStatus.Succeed, "output": str(res), "raw": res}
            else:
                return {"status": ActionStatus.Failed, "error": "Tool runtime not available (sync).", "output": ""}
        except Exception as e:
            logger.exception(f"[AgentRuntime] 工具执行失败(sync): {e}")
            return {"status": ActionStatus.Failed, "error": f"Error executing tool: {e}", "output": ""}

    def _build_system_prompt(self) -> str:
        parts = []
        name = self.name or "助手"
        setting = getattr(self, "setting", None)
        # 名称与基础身份
        if setting is None:
            parts.append(f"你是一个乐于助人的助手，你的名字叫：{name}")
        else:
            parts.append(f"你是一个乐于助人的助手，你的名字叫：{name}")
            # 业务职责（business_logic 对齐旧版）
            business_logic = getattr(setting, "target", None) or getattr(setting, "examples", None)
            # 老实现使用 setting.business_logic，这里按字段抽取并兼容为空
            if hasattr(setting, "business_logic") and getattr(setting, "business_logic"):
                parts.append("\n以下是你的主要职责：\n" + str(getattr(setting, "business_logic")))
            elif business_logic:
                parts.append("\n以下是你的主要职责：\n" + str(business_logic))
            # 业务原则（按 index 排序，激活的才加入）
            principles = getattr(setting, "principles", []) or []
            if principles:
                try:
                    principles = sorted(principles, key=lambda x: getattr(x, "index", 0))
                except Exception:
                    pass
                rules = "\n".join([
                    f"规则{getattr(rule, 'index', 0)}: {getattr(rule, 'content', '')}" 
                    for rule in principles if getattr(rule, "activate", True)
                ])
                if rules:
                    parts.append("以下是你完成任务过程中需要遵守的基本原则:\n" + rules + "\n")
        # 工具使用说明（与旧版一致的强调段落）
        if self.tools:
            parts.append(
                "特殊说明：如果用户提出的问题需要多次使用工具才能完成，请持续执行多步工具操作，直到任务完成，再停止。如果用户的问题基于当前状态就能够回答，直接回答问题，不需要进一步调用工具。如果尝试使用工具失败超过三次，则暂停使用工具，并反馈用户。"
            )
        return "\n".join(parts)