"""LLM对话服务（支持function calling）"""

from __future__ import annotations

import asyncio
import json
import uuid
from datetime import date, datetime
from typing import List, Dict, Any, AsyncGenerator
from litellm import AllMessageValues

from agentsociety2.backend.models import ChatMessage
from agentsociety2.backend.tools.registry import get_registry
from agentsociety2.config import get_llm_router
from agentsociety2.logger import get_logger
from agentsociety2.backend.sse import (
    SSEEvent,
    MessageEvent,
    ToolEvent,
    CompleteEvent,
)

logger = get_logger()


def _serializable_data(data: Any) -> Any:
    """Ensure data is JSON-serializable (for tool result messages)."""
    if data is None:
        return None
    if hasattr(data, "model_dump"):
        return data.model_dump(mode="json")
    if isinstance(data, dict):
        return {k: _serializable_data(v) for k, v in data.items()}
    if isinstance(data, (list, tuple)):
        return [_serializable_data(x) for x in data]
    if isinstance(data, (datetime, date)):
        return data.isoformat()
    if isinstance(data, (str, int, float, bool)):
        return data
    return str(data)


class FunctionWrapper:
    """包装函数信息"""

    def __init__(self, name: str, arguments: str):
        self.name = name
        self.arguments = arguments


class ToolCallWrapper:
    """包装工具调用，统一处理对象和字典格式"""

    def __init__(self, tool_call: Any):
        if isinstance(tool_call, dict):
            self.id = tool_call.get("id", f"call_{uuid.uuid4().hex[:12]}")
            self.type = tool_call.get("type", "function")
            function_data = tool_call.get("function", {})
            self.function = FunctionWrapper(
                name=function_data.get("name", ""),
                arguments=function_data.get("arguments", "{}"),
            )
        else:
            # 已经是对象格式
            self.id = getattr(tool_call, "id", f"call_{uuid.uuid4().hex[:12]}")
            self.type = getattr(tool_call, "type", "function")
            func = getattr(tool_call, "function", None)
            if func:
                self.function = FunctionWrapper(
                    name=getattr(func, "name", ""),
                    arguments=getattr(func, "arguments", "{}"),
                )
            else:
                self.function = FunctionWrapper(name="", arguments="{}")


## Workspace Directory Structure

# The directory structure of the workspace is as follows:
# - `TOPIC.md`: The summary of the research topic.
# - `papers/`: Storing literature search results or user-uploaded literature files.
# - `user_data/`: Storing user-uploaded data.
# - `hypothesis_{id}/`: The directory for storing hypotheses.
#   - `HYPOTHESIS.md`: The summary of the hypothesis.
#   - `SIM_SETTINGS.json`: Storing the chosen environment modules and agent types for the hypothesis.
#   - `experiment_{id}/`: The directory for storing experimental designs.
#     - `EXPERIMENT.md`: The summary of the experimental design.
#     - `user_data/`: Storing user-uploaded data for this experiment.
#     - `init/`: Storing generated initial config for this experiment.
#       - `codes/`: The isolated workspace for python code execution.
#       - `temp_files/`: The isolated workspace for temporary files in code execution.
#       - `results/`: The output files of code execution for generating the initial config about agents and environment modules for starting the experiment.
#       - `steps.yaml`: The steps of the experiment, including the simulation steps and the workflow steps.
#     - `run/`: Storing the files for starting the experiment and the results of the experiment.
#       - `start.sh`: The script for starting the experiment.
#       - `pid.json`: The file for storing the process id, experiment id, experiment status, experiment start time, experiment end time, and all other information of the experiment.
#       - `run.log`: The stdout and stderr of the experiment.
#       - `sqlite.db`: The database file for storing the data generated by the experiment.
#       - `artifacts/`: The directory for storing artifacts of the experiment, which can contain any files generated by the experiment.
#     - `analysis/`: Storing the files for analyzing the experiment results.
#       - `codes/`: The isolated workspace for python code execution.
#       - `temp_files/`: The isolated workspace for temporary files in code execution.
#       - `results/`: The output files of code execution for generating the analysis.
#     - `CONCLUSION.md`: The summary of the conclusion from the analysis.

# AI Social Scientist系统提示词
SYSTEM_PROMPT = """You are an AI Social Scientist, an intelligent research assistant specialized in social science research. Your role is to help researchers conduct comprehensive social science studies using AgentSociety2 simulation platform. You are operating in a workspace directory to achieve the research topic.

## Your Capabilities

1. **Chat with Users**: You can chat with users to understand their research needs and provide guidance WITHOUT tools. The action will stop the ReAct agent loop and allow the user to continue the conversation. When you are ready to answer the user's question, provide the guidance, or ask for confirmation or suggestions, you SHOULD use this action.

2. **Information Gathering & Memory**: You can gather information about the research topic and available system components by reading high-value files in the workspace.
   - If you want to do so, read `.agentsociety/path.md` first to understand the workspace structure and find high-value file paths.
   - Related tools: `list_directory`, `read_file`, `glob`

3. **Literature Search**: You can search for academic literature related to research topics, helping researchers understand existing research and identify gaps.
   - Related tools: `search_literature`, `load_literature`
   - If `search_literature` returns errors or does not find useful information, consider using the web research tool for broader, up-to-date sources.

4. **Web Information Mining**: You can search and analyze web content when the user needs up-to-date information, or when academic literature search is insufficient.
   - Related tool: `miro_web_research`

5. **Hypothesis Management**: You can help formulate and manage testable hypotheses based on research topics and literature findings.
   - Related tools: `hypothesis` (with actions: add, get, list, delete)

6. **Experimental Design**: You can design simulation experiments using AgentSociety2, including:
   - Selecting appropriate Agent classes and Environment modules (use the information gathered from the JSON files mentioned above)
   - Configuring experimental parameters (Run `experiment_config` tool to generate the parameters)
   - Designing experimental groups and conditions

7. **Experiment Management**: You can manage simulation experiments using AgentSociety2, including:
   - Starting, monitoring, and stopping experiments (use the information gathered from the JSON files mentioned above)
   - Related tools: `run_experiment` (with actions: start, status, stop)

8. **Data Analysis**: You can analyze experimental results, generate visualizations, and draw conclusions.

9. **File Operations**: You have comprehensive file system tools to manage workspace files and organize research materials. Use these tools to read, write, search, and modify files:
   - `list_directory`: List contents of a directory with optional glob pattern filtering
   - `read_file`: Read text files with optional line range specification (offset/limit)
   - `write_file`: Create new files or overwrite existing ones (automatically creates parent directories)
   - `glob`: Find files matching glob patterns (e.g., `**/*.py`), sorted by modification time
   - `search_file_content`: Search for regex patterns within file contents, with optional file type filtering
   - `replace`: Precisely replace text in files (requires exact string matching with context)

10. **Shell Commands**: You can execute shell commands to perform complex operations that cannot be achieved with other tools.
   - Related tools: `run_shell_command`

## Your Approach

- Always think step by step and explain your reasoning
- When using tools, clearly indicate what you're doing and why
- After using tools, try to verify the results of the tools. If the results are not as expected, you can use the tools to fix the results.
- Provide summaries and guidance after completing major tasks (e.g., literature search)
- Help users navigate the research workflow described in the project structure
- Be proactive in suggesting next steps based on the current research stage

## Communication Style

- Use clear, professional language
- Provide summaries after tool calls
- Always report verification results when you verify tool execution
- Guide users on what to do next, but DO NOT directly give the results of next steps. But if the next steps are validation or come from tool results, you can directly do the following steps.
- Be helpful and supportive throughout the research process.

## Critical: Tool Usage and Hallucination Prevention

**IMPORTANT**: You MUST follow these rules strictly to avoid hallucinations and fake function calls:

2. **Only claim to have done something if you actually called a tool**: Never state that you have performed an action (e.g., "I have read the file", "I have searched the literature", "I have created the hypothesis") unless you have actually made a tool call in your response. Simply describing what you would do or what you think happened does NOT mean you have actually done it.

3. **Tool calls are explicit**: When you need to perform an action, you MUST use the appropriate tool. Your response will include tool calls that are executed by the system. If your response does not include tool calls, you have NOT performed any actions, regardless of what you say in your text.

4. **Distinguish between plans and actions**: You can describe what you plan to do, but you must clearly distinguish between:
   - What you PLAN to do (future actions)
   - What you HAVE done (actual tool calls that were executed)

5. **Verify before claiming completion**: Before stating that a task is complete, verify that you have actually received tool execution results. Do not assume that mentioning a tool name means the tool was executed.

5. **If no tool was called, be honest**: If you did not call any tools in your response, do not claim to have performed any actions. Instead, explain what you plan to do next or ask for clarification if needed.

Remember: You are working with researchers who may not be familiar with all technical details. Always explain your actions and provide context. Most importantly, be truthful about what you have actually done versus what you plan to do."""


class CompletionService:
    """LLM对话服务，支持function calling"""

    def __init__(self):
        """初始化对话服务"""
        self._router = get_llm_router("coder")
        self._tool_registry = get_registry()
        logger.info("Completion service initialized")

    async def chat_stream(
        self,
        messages: List[ChatMessage],
        max_turns: int = 50,
        workspace_path: str = "",
    ) -> AsyncGenerator[SSEEvent, None]:
        """
        流式执行LLM对话，支持function calling和SSE

        Args:
            messages: 对话消息列表
            max_turns: 最大对话轮数
            workspace_path: 工作区路径

        Yields:
            流式事件字典
        """
        try:
            logger.info(
                f"[SSE] 开始流式对话，消息数: {len(messages)}, 最大轮数: {max_turns}"
            )

            # 获取所有工具
            openai_tools = self._tool_registry.get_openai_tools()
            logger.info(
                f"[SSE] 获取到的OpenAI工具数量: {len(openai_tools) if openai_tools else 0}"
            )

            # 转换消息格式
            litellm_messages = self._convert_messages(messages)
            logger.info(f"[SSE] 转换后的消息数: {len(litellm_messages)}")

            # 添加系统提示词
            if not litellm_messages or litellm_messages[0].get("role") != "system":
                litellm_messages.insert(0, {"role": "system", "content": SYSTEM_PROMPT})
                logger.info("[SSE] 已添加系统提示词")

            turn_count = 0
            all_messages = litellm_messages.copy()

            while turn_count < max_turns:
                turn_count += 1
                logger.info(f"[SSE] 对话轮次 {turn_count}/{max_turns}")

                # 发送思考事件
                thinking_event = MessageEvent(
                    content=f"正在思考... (轮次 {turn_count}/{max_turns})",
                    is_thinking=True,
                )
                logger.info(f"[SSE] 发送thinking事件: {thinking_event.model_dump()}")
                yield thinking_event

                # Prepare call parameters
                model_name = self._router.model_list[0]["model_name"]
                call_kwargs = {
                    "model": model_name,
                    "messages": all_messages,
                    "stream": False,
                }

                # 提供tools，允许模型在任何轮次调用工具
                if openai_tools:
                    call_kwargs["tools"] = openai_tools
                    call_kwargs["tool_choice"] = "auto"
                    logger.info(f"[SSE] 第{turn_count}轮，已添加工具到调用参数")

                # 调用LLM
                try:
                    logger.info(
                        f"[SSE] 调用LLM，模型: {model_name}, 消息数: {len(all_messages)}"
                    )
                    response = await self._router.acompletion(**call_kwargs)
                    logger.info(
                        f"[SSE] LLM调用成功，响应消息数: {len(response.choices)}"
                    )
                except Exception as e:
                    logger.error(f"[SSE] LLM调用失败: {e}", exc_info=True)
                    error_event = MessageEvent(
                        content=f"LLM调用失败: {str(e)}",
                        is_error=True,
                    )
                    logger.info(f"[SSE] 发送error事件: {error_event.model_dump()}")
                    yield error_event
                    return

                # 获取响应消息
                response_message = response.choices[0].message
                assistant_content = response_message.content or ""
                tool_calls_raw = getattr(response_message, "tool_calls", None) or []

                # 统一包装tool_calls，支持对象和字典格式
                tool_calls = (
                    [ToolCallWrapper(tc) for tc in tool_calls_raw]
                    if tool_calls_raw
                    else []
                )

                logger.info(
                    f"[SSE] 响应内容长度: {len(assistant_content)}, 工具调用数: {len(tool_calls)}"
                )

                # 发送assistant响应
                if assistant_content:
                    assistant_event = MessageEvent(
                        content=assistant_content,
                        is_thinking=False,
                    )
                    logger.info(
                        f"[SSE] 发送assistant_message事件，内容长度: {len(assistant_content)}"
                    )
                    yield assistant_event

                # 添加assistant消息
                assistant_msg: Dict[str, Any] = {
                    "role": "assistant",
                    "content": assistant_content,
                }
                if tool_calls:
                    assistant_msg["tool_calls"] = [
                        {
                            "id": tc.id,
                            "type": tc.type,
                            "function": {
                                "name": tc.function.name,
                                "arguments": tc.function.arguments,
                            },
                        }
                        for tc in tool_calls
                    ]
                all_messages.append(assistant_msg)  # type: ignore

                # 如果没有工具调用，说明对话的一个阶段完成
                # 当LLM返回content并不进行工具调用时，结束agent循环
                if not tool_calls:
                    complete_content = (
                        assistant_content if assistant_content else "对话已完成"
                    )
                    complete_event = CompleteEvent(
                        content=complete_content,
                    )
                    if assistant_content:
                        logger.info(
                            f"[SSE] LLM返回了内容且无工具调用，对话完成，发送complete事件: {complete_event.model_dump()}"
                        )
                    else:
                        logger.info(
                            f"[SSE] 无内容也无工具调用，模型停止，发送complete事件: {complete_event.model_dump()}"
                        )
                    yield complete_event
                    logger.info("[SSE] 流式对话正常结束")
                    return

                # 执行tool calls
                tool_results = []
                for tool_call in tool_calls:
                    tool_name = tool_call.function.name
                    tool_id = tool_call.id
                    logger.info(f"[SSE] 处理工具调用: {tool_name}, ID: {tool_id}")

                    # 解析参数
                    try:
                        arguments = json.loads(tool_call.function.arguments)
                    except json.JSONDecodeError as e:
                        error_msg = f"工具参数解析失败: {e}"
                        logger.warning(error_msg)
                        tool_results.append(
                            {
                                "role": "tool",
                                "content": f"错误: {error_msg}",
                                "tool_call_id": tool_id,
                                "name": tool_name,
                            }
                        )
                        tool_error_event = ToolEvent(
                            tool_name=tool_name,
                            tool_id=tool_id or "",
                            status="error",
                            content=f"工具参数解析失败: {error_msg}",
                        )
                        yield tool_error_event
                        continue

                    # 执行工具
                    logger.info(f"[SSE] 执行工具: {tool_name}, 参数: {arguments}")

                    # 不再发送工具开始（简化前端呈现）
                    # tool_executing_event = ToolEvent(
                    #     tool_name=tool_name,
                    #     tool_id=tool_id or "",
                    #     status="start",
                    #     content=f"开始执行工具: {tool_name}",
                    # )
                    # logger.info(
                    #     f"[SSE] 发送tool_executing事件: {tool_executing_event.model_dump()}"
                    # )
                    # yield tool_executing_event

                    # 设置进度回调，用于工具发送进度更新
                    # 使用asyncio.Queue收集进度事件，实现实时流式发送
                    progress_queue: asyncio.Queue[ToolEvent] = asyncio.Queue()

                    async def progress_callback_wrapper(tool_event: ToolEvent) -> None:
                        """包装进度回调，将事件放入队列以便实时yield"""
                        await progress_queue.put(tool_event)
                        logger.info(
                            f"[SSE] 工具进度事件已入队: {tool_event.model_dump()}"
                        )

                    # 获取工具（传入workspace_path、progress_callback和tool_id）
                    tool = self._tool_registry.get_tool(
                        tool_name,
                        workspace_path=workspace_path,
                        progress_callback=progress_callback_wrapper,
                        tool_id=tool_id or "",
                    )
                    if not tool:
                        error_msg = f"工具 '{tool_name}' 不存在"
                        logger.warning(error_msg)
                        tool_results.append(
                            {
                                "role": "tool",
                                "content": f"错误: {error_msg}",
                                "tool_call_id": tool_id,
                                "name": tool_name,
                            }
                        )
                        tool_error_event = ToolEvent(
                            tool_name=tool_name,
                            tool_id=tool_id or "",
                            status="error",
                            content=f"工具执行失败: {error_msg}",
                        )
                        yield tool_error_event
                        continue

                    try:
                        logger.info(f"[SSE] 调用工具execute方法: {tool_name}")

                        # 启动工具执行任务
                        execute_task = asyncio.create_task(tool.execute(arguments))

                        # 在工具执行期间，实时从队列中取出事件并yield
                        while not execute_task.done():
                            try:
                                # 使用超时避免阻塞，允许检查工具执行状态
                                event = await asyncio.wait_for(
                                    progress_queue.get(), timeout=0.1
                                )
                                logger.info(
                                    f"[SSE] 实时发送工具进度事件: {event.model_dump()}"
                                )
                                yield event
                                progress_queue.task_done()
                            except asyncio.TimeoutError:
                                # 超时后继续检查工具执行状态
                                continue

                        # 工具执行完成后，处理队列中剩余的事件
                        while not progress_queue.empty():
                            event = progress_queue.get_nowait()
                            logger.info(
                                f"[SSE] 发送剩余工具进度事件: {event.model_dump()}"
                            )
                            yield event
                            progress_queue.task_done()

                        # 获取工具执行结果
                        result = await execute_task
                        logger.info(
                            f"[SSE] 工具执行成功: {tool_name}, 结果内容长度: {len(result.content) if result.content else 0}"
                        )

                        tool_result_msg = {
                            "role": "tool",
                            "content": result.content,
                            "tool_call_id": tool_id,
                            "name": tool_name,
                        }
                        if result.data:
                            tool_result_msg["data"] = _serializable_data(result.data)
                        tool_results.append(tool_result_msg)

                        # 发送工具结果事件（暂时取消）
                        # tool_result_event = ToolEvent(
                        #     tool_name=tool_name,
                        #     tool_id=tool_id or "",
                        #     status="success",
                        #     content="工具执行成功",
                        # )
                        # logger.info(f"[SSE] 发送tool_result事件: {tool_name}")
                        # yield tool_result_event
                    except Exception as e:
                        error_msg = f"工具执行失败: {str(e)}"
                        logger.error(
                            f"[SSE] 工具 '{tool_name}' 执行失败: {e}", exc_info=True
                        )
                        tool_results.append(
                            {
                                "role": "tool",
                                "content": f"错误: {error_msg}",
                                "tool_call_id": tool_id,
                                "name": tool_name,
                            }
                        )
                        tool_error_event = ToolEvent(
                            tool_name=tool_name,
                            tool_id=tool_id or "",
                            status="error",
                            content=f"工具执行失败: {error_msg}",
                        )
                        logger.info(
                            f"[SSE] 发送tool_error事件: {tool_error_event.model_dump()}"
                        )
                        yield tool_error_event

                # 添加tool结果到消息列表
                all_messages.extend(tool_results)
                logger.info(
                    f"[SSE] 已添加{len(tool_results)}个工具结果到消息列表，当前总消息数: {len(all_messages)}"
                )

            # 达到最大轮数，生成总结
            logger.info(f"[SSE] 达到最大轮数 {max_turns}，开始生成总结")

            # 发送思考事件
            thinking_event = MessageEvent(
                content=f"已达到最大对话轮数 {max_turns}，正在生成总结...",
                is_thinking=True,
            )
            logger.info(f"[SSE] 发送thinking事件: {thinking_event.model_dump()}")
            yield thinking_event

            # 构建总结请求消息
            summary_messages = all_messages.copy()
            summary_prompt = f"""由于已达到最大对话轮数限制（{max_turns}轮），对话需要在此结束。请基于当前的对话历史，生成一个总结，包括：

1. **已完成的工作**：总结到目前为止已经完成的主要任务和取得的进展
2. **当前状态**：描述对话的当前状态，包括最后执行的操作
3. **未完成的部分**：如果有未完成的任务，简要说明还需要做什么
4. **建议**：为后续继续对话提供建议

请用中文生成一个清晰、结构化的总结。"""

            summary_messages.append({"role": "user", "content": summary_prompt})

            # 调用LLM生成总结
            try:
                model_name = self._router.model_list[0]["model_name"]
                logger.info(
                    f"[SSE] 调用LLM生成总结，模型: {model_name}, 消息数: {len(summary_messages)}"
                )
                summary_response = await self._router.acompletion(
                    model=model_name,
                    messages=summary_messages,
                    stream=False,
                )

                summary_content = (
                    getattr(summary_response.choices[0].message, "content", None) or ""  # type: ignore
                )
                logger.info(f"[SSE] 总结生成成功，内容长度: {len(summary_content)}")

                # 发送达到最大轮数完成事件（使用CompleteEvent）
                max_turns_content = (
                    summary_content or f"达到最大轮数 {max_turns}，对话未完成"
                )
                max_turns_event = CompleteEvent(
                    content=max_turns_content,
                )
                logger.info("[SSE] 达到最大轮数，发送complete事件")
                yield max_turns_event

            except Exception as e:
                logger.error(f"[SSE] 生成总结失败: {e}", exc_info=True)
                # 如果生成总结失败，至少发送一个基本的提示
                max_turns_event = CompleteEvent(
                    content=f"达到最大轮数 {max_turns}，对话未完成。由于生成总结时出错，无法提供详细总结。",
                )
                logger.info("[SSE] 达到最大轮数，发送complete事件（总结生成失败）")
                yield max_turns_event

        except Exception as e:
            logger.error(f"[SSE] 流式对话服务错误: {e}", exc_info=True)
            error_event = MessageEvent(
                content=f"对话服务错误: {str(e)}",
                is_error=True,
            )
            logger.info(f"[SSE] 发送error事件: {error_event.model_dump()}")
            yield error_event

    def _convert_messages(self, messages: List[ChatMessage]) -> List[AllMessageValues]:
        """转换消息格式为 litellm 格式，保证 content/data 等可 JSON 序列化。"""
        result = []
        for msg in messages:
            litellm_msg: Dict[str, Any] = {"role": msg.role}
            if msg.content is not None:
                litellm_msg["content"] = msg.content if isinstance(msg.content, str) else str(msg.content)
            if msg.tool_calls:
                litellm_msg["tool_calls"] = msg.tool_calls
            if msg.tool_call_id:
                litellm_msg["tool_call_id"] = msg.tool_call_id
            if msg.name:
                litellm_msg["name"] = msg.name
            if msg.data is not None:
                litellm_msg["data"] = _serializable_data(msg.data)
            result.append(litellm_msg)
        return result

    def _convert_litellm_messages(self, messages: List[Any]) -> List[ChatMessage]:
        """转换litellm消息格式为ChatMessage"""
        result = []
        for msg in messages:
            # Convert to dict for easier access
            if isinstance(msg, dict):
                msg_dict = msg
            else:
                # Convert AllMessageValues or other types to dict
                msg_dict = {
                    "role": getattr(msg, "role", None)
                    or (msg.get("role") if isinstance(msg, dict) else None),
                    "content": getattr(msg, "content", None)
                    or (msg.get("content") if isinstance(msg, dict) else None),
                    "tool_calls": getattr(msg, "tool_calls", None)
                    or (msg.get("tool_calls") if isinstance(msg, dict) else None),
                    "tool_call_id": getattr(msg, "tool_call_id", None)
                    or (msg.get("tool_call_id") if isinstance(msg, dict) else None),
                    "name": getattr(msg, "name", None)
                    or (msg.get("name") if isinstance(msg, dict) else None),
                    "data": getattr(msg, "data", None)
                    or (msg.get("data") if isinstance(msg, dict) else None),
                }

            # Convert content to string if needed
            content = msg_dict.get("content")
            if content is not None and not isinstance(content, str):
                if isinstance(content, list):
                    # Handle list content (e.g., from OpenAI)
                    content = " ".join(str(item) for item in content)
                else:
                    content = str(content)

            # Convert tool_calls to list of dicts if needed
            tool_calls = msg_dict.get("tool_calls")
            if tool_calls is not None:
                if not isinstance(tool_calls, list):
                    tool_calls = [tool_calls]
                tool_calls = [
                    (
                        tc
                        if isinstance(tc, dict)
                        else {
                            "id": getattr(tc, "id", None),
                            "type": getattr(tc, "type", None),
                            "function": {
                                "name": (
                                    getattr(tc.function, "name", None)
                                    if hasattr(tc, "function")
                                    else None
                                ),
                                "arguments": (
                                    getattr(tc.function, "arguments", None)
                                    if hasattr(tc, "function")
                                    else None
                                ),
                            },
                        }
                    )
                    for tc in tool_calls
                ]

            chat_msg = ChatMessage(
                role=str(msg_dict.get("role", "user")),  # type: ignore
                content=content,
                tool_calls=tool_calls,  # type: ignore
                tool_call_id=msg_dict.get("tool_call_id"),
                name=msg_dict.get("name"),
                data=msg_dict.get("data"),
            )
            result.append(chat_msg)
        return result
