def to_openai_responses_message_dict(
    message: ChatMessage,
    drop_none: bool = False,
    model: Optional[str] = None,
) -> Union[str, Dict[str, Any], List[Dict[str, Any]]]:
    """Convert a ChatMessage to an OpenAI message dict."""
    content = []
    content_txt = ""

    for block in message.blocks:
        if isinstance(block, TextBlock):
            content.append({"type": "input_text", "text": block.text})
            content_txt += block.text
        elif isinstance(block, DocumentBlock):
            if not block.data:
                file_buffer = block.resolve_document()
                b64_string = block._get_b64_string(file_buffer)
                mimetype = block._guess_mimetype()
            else:
                b64_string = block.data.decode("utf-8")
            content.append(
                {
                    "type": "input_file",
                    "filename": block.title,
                    "file_data": f"data:{mimetype};base64,{b64_string}",
                }
            )
        elif isinstance(block, ImageBlock):
            if block.url:
                content.append(
                    {
                        "type": "input_image",
                        "image_url": str(block.url),
                        "detail": block.detail or "auto",
                    }
                )
            else:
                img_bytes = block.resolve_image(as_base64=True).read()
                img_str = img_bytes.decode("utf-8")
                content.append(
                    {
                        "type": "input_image",
                        "image_url": f"data:{block.image_mimetype};base64,{img_str}",
                        "detail": block.detail or "auto",
                    }
                )
        else:
            msg = f"Unsupported content block type: {type(block).__name__}"
            raise ValueError(msg)

    # NOTE: Sending a null value (None) for Tool Message to OpenAI will cause error
    # It's only Allowed to send None if it's an Assistant Message and either a function call or tool calls were performed
    # Reference: https://platform.openai.com/docs/api-reference/chat/create
    content_txt = (
        None
        if content_txt == ""
        and message.role == MessageRole.ASSISTANT
        and (
            "function_call" in message.additional_kwargs
            or "tool_calls" in message.additional_kwargs
        )
        else content_txt
    )

    # NOTE: Despite what the openai docs say, if the role is ASSISTANT, SYSTEM
    # or TOOL, 'content' cannot be a list and must be string instead.
    # Furthermore, if all blocks are text blocks, we can use the content_txt
    # as the content. This will avoid breaking openai-like APIs.
    if message.role.value == "tool":
        call_id = message.additional_kwargs.get(
            "tool_call_id", message.additional_kwargs.get("call_id")
        )
        if call_id is None:
            raise ValueError(
                "tool_call_id or call_id is required in additional_kwargs for tool messages"
            )

        message_dict = {
            "type": "function_call_output",
            "output": content_txt,
            "call_id": call_id,
        }

        return message_dict
    elif "tool_calls" in message.additional_kwargs:
        message_dicts = [
            tool_call if isinstance(tool_call, dict) else tool_call.model_dump()
            for tool_call in message.additional_kwargs["tool_calls"]
        ]

        return message_dicts

    # there are some cases (like image generation or MCP tool call) that only support the string input
    # this is why, if context_txt is a non-empty string, all the blocks are TextBlocks and the role is user, we return directly context_txt
    elif (
        isinstance(content_txt, str)
        and len(content_txt) != 0
        and all(item["type"] == "input_text" for item in content)
        and message.role.value == "user"
    ):
        return content_txt
    else:
        message_dict = {
            "role": message.role.value,
            "content": (
                content_txt
                if message.role.value in ("system", "developer")
                or all(isinstance(block, TextBlock) for block in message.blocks)
                else content
            ),
        }

    # TODO: O1 models do not support system prompts
    if (
        model is not None
        and model in O1_MODELS
        and model not in O1_MODELS_WITHOUT_FUNCTION_CALLING
    ):
        if message_dict["role"] == "system":
            message_dict["role"] = "developer"

    null_keys = [key for key, value in message_dict.items() if value is None]
    # if drop_none is True, remove keys with None values
    if drop_none:
        for key in null_keys:
            message_dict.pop(key)

    return message_dict  # type: ignore
Convert a ChatMessage to an OpenAI message dict.
llama-index-integrations/llms/llama-index-llms-openai/llama_index/llms/openai/utils.py