This commit is contained in:
Christian Mantha
2026-03-02 19:10:52 -05:00
commit 2ca0b9ef7c
28907 changed files with 5233713 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
import base64
import json
import logging
from typing import Optional, Union
from mlflow.types.chat import (
ChatMessage,
ChatTool,
Function,
FunctionToolDefinition,
ImageContentPart,
ImageUrl,
TextContentPart,
ToolCall,
)
_logger = logging.getLogger(__name__)
def convert_message_to_mlflow_chat(message: dict) -> ChatMessage:
"""
Convert Bedrock Converse API's message object into MLflow's standard format (OpenAI compatible).
Ref: https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Message.html
Args:
message: Bedrock Converse API's message object.
Returns:
ChatMessage: MLflow's standard chat message object.
"""
role = message["role"]
contents = []
tool_calls = []
tool_call_id = None
for content in message["content"]:
if tool_call := content.get("toolUse"):
input = tool_call.get("input")
tool_calls.append(
ToolCall(
id=tool_call["toolUseId"],
function=Function(
name=tool_call["name"],
arguments=input if isinstance(input, str) else json.dumps(input),
),
type="function",
)
)
elif tool_result := content.get("toolResult"):
tool_call_id = tool_result["toolUseId"]
# "tool_result" content corresponds to the "tool" message in OpenAI.
# https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolResultContentBlock.html
role = "tool"
for content in tool_result["content"]:
parsed_content = _parse_content(content)
if parsed_content:
contents.append(parsed_content)
else:
parsed_content = _parse_content(content)
if parsed_content:
contents.append(parsed_content)
message = ChatMessage(role=role, content=contents)
if tool_calls:
message.tool_calls = tool_calls
if tool_call_id:
message.tool_call_id = tool_call_id
return message
def _parse_content(content: dict) -> Optional[Union[TextContentPart, ImageContentPart]]:
"""
Parse a single content block in the Bedrock message object.
Some content types like video and document are not supported by OpenAI's spec. This
function returns None for those content types.
Ref: https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ContentBlock.html
"""
if text := content.get("text"):
return TextContentPart(text=text, type="text")
elif json_content := content.get("json"):
return TextContentPart(text=json.dumps(json_content), type="text")
elif image := content.get("image"):
# Bedrock support passing images in both raw bytes and base64 encoded strings.
# OpenAI spec only supports base64 encoded images, so we encode the raw bytes to base64.
# https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ImageBlock.html
image_bytes = image["source"]["bytes"]
if isinstance(image_bytes, bytes):
data = base64.b64encode(image_bytes).decode("utf-8")
else:
data = image_bytes
format = "image/" + image["format"]
image_url = ImageUrl(url=f"data:{format};base64,{data}", detail="auto")
return ImageContentPart(type="image_url", image_url=image_url)
# NB: Video and Document content type are not supported by OpenAI's spec, so recording as text.
else:
_logger.debug(f"Received an unsupported content type: {list(content.keys())[0]}")
return None
def convert_tool_to_mlflow_chat_tool(tool: dict) -> ChatTool:
"""
Convert Bedrock tool definition into MLflow's standard format (OpenAI compatible).
Ref: https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Tool.html
Args:
tool: A dictionary represents a single tool definition in the input request.
Returns:
ChatTool: MLflow's standard tool definition object.
"""
tool_spec = tool["toolSpec"]
return ChatTool(
type="function",
function=FunctionToolDefinition(
name=tool_spec["name"],
description=tool_spec.get("description"),
parameters=tool_spec["inputSchema"].get("json"),
),
)