This commit is contained in:
Bagatur 2024-10-28 13:09:15 -07:00
parent a1a4e9f4a5
commit 28b3598744
2 changed files with 57 additions and 39 deletions

View File

@ -12,6 +12,7 @@ from __future__ import annotations
import base64
import inspect
import json
import logging
from collections.abc import Iterable, Sequence
from functools import partial
from typing import (
@ -46,6 +47,9 @@ if TYPE_CHECKING:
from langchain_core.runnables.base import Runnable
logger = logging.getLogger(__name__)
def _get_type(v: Any) -> str:
"""Get the type associated with the object for serialization purposes."""
if isinstance(v, dict) and "type" in v:
@ -993,11 +997,14 @@ def convert_to_openai_messages(
f"but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
logger.warning(err)
text = str({k: v for k, v in block.items() if k != "type"}) if len(block) > 1 else ""
_raise_or_warn(err, coerce)
text = (
_try_json_dumps(
{k: v for k, v in block.items() if k != "type"}
)
if len(block) > 1
else ""
)
else:
text = block["text"]
content.append({"type": block["type"], "text": text})
@ -1009,10 +1016,8 @@ def convert_to_openai_messages(
f"but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
continue
_raise_or_warn(err, coerce)
continue
content.append(
{"type": "image_url", "image_url": block["image_url"]}
)
@ -1031,10 +1036,8 @@ def convert_to_openai_messages(
f"but 'source' is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
continue
_raise_or_warn(err, coerce)
continue
content.append(
{
"type": "image_url",
@ -1057,10 +1060,8 @@ def convert_to_openai_messages(
f"but 'image' is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
continue
_raise_or_warn(err, coerce)
continue
b64_image = _bytes_to_b64_str(image["source"]["bytes"])
content.append(
{
@ -1080,10 +1081,8 @@ def convert_to_openai_messages(
f"but does not have a 'source' or 'image' key. Full "
f"content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
continue
_raise_or_warn(err, coerce)
continue
elif block.get("type") == "tool_use":
if missing := [
k for k in ("id", "name", "input") if k not in block
@ -1094,10 +1093,8 @@ def convert_to_openai_messages(
f"'tool_use', but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(err)
else:
continue
_raise_or_warn(err, coerce)
continue
if not any(
tool_call["id"] == block["id"]
for tool_call in cast(AIMessage, message).tool_calls
@ -1123,10 +1120,8 @@ def convert_to_openai_messages(
f"'tool_result', but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
if not coerce:
raise ValueError(msg)
else:
continue
_raise_or_warn(err, coerce)
continue
tool_message = ToolMessage(
block["content"],
tool_call_id=block["tool_use_id"],
@ -1146,10 +1141,8 @@ def convert_to_openai_messages(
f"but does not have a 'json' key. Full "
f"content block:\n\n{block}"
)
if not coerce:
raise ValueError(msg)
else:
continue
_raise_or_warn(msg, coerce)
continue
content.append(
{"type": "text", "text": json.dumps(block["json"])}
)
@ -1167,8 +1160,16 @@ def convert_to_openai_messages(
f"messages[{i}].content[{j}]['guard_content']['text'] "
f"key. Full content block:\n\n{block}"
)
raise ValueError(msg)
text = block["guard_content"]["text"]
_raise_or_warn(msg, coerce)
text = (
_try_json_dumps(
{k: v for k, v in block.items() if k != "type"}
)
if len(block) > 1
else ""
)
else:
text = block["guard_content"]["text"]
if isinstance(text, dict):
text = text["text"]
content.append({"type": "text", "text": text})
@ -1183,14 +1184,16 @@ def convert_to_openai_messages(
f"'media' but does not have key(s) {missing}. Full "
f"content block:\n\n{block}"
)
raise ValueError(err)
_raise_or_warn(err, coerce)
continue
if "image" not in block["mime_type"]:
err = (
f"OpenAI messages can only support text and image data."
f" Received content block with media of type:"
f" {block['mime_type']}"
)
raise ValueError(err)
_raise_or_warn(err, coerce)
continue
b64_image = _bytes_to_b64_str(block["data"])
content.append(
{
@ -1209,7 +1212,8 @@ def convert_to_openai_messages(
f"Anthropic, Bedrock Converse, or VertexAI format. Full "
f"content block:\n\n{block}"
)
raise ValueError(err)
_raise_or_warn(err, coerce)
continue
if text_format == "string" and not any(
block["type"] != "text" for block in content
):
@ -1432,3 +1436,17 @@ def _convert_to_openai_tool_calls(tool_calls: list[ToolCall]) -> list[dict]:
}
for tool_call in tool_calls
]
def _try_json_dumps(o: Any) -> str:
try:
return json.dumps(o)
except Exception:
return str(o)
def _raise_or_warn(msg: str, silence: bool) -> None:
if not silence:
raise ValueError(msg)
else:
logger.warning(msg)

View File

@ -861,7 +861,7 @@ class BaseChatOpenAI(BaseChatModel):
)
def _coerce_messages(self, messages: list[BaseMessage]) -> list[BaseMessage]:
return convert_to_messages(convert_to_openai_messages(messages))
return convert_to_messages(convert_to_openai_messages(messages, coerce=True))
@property
def _identifying_params(self) -> Dict[str, Any]: