mirror of
https://github.com/hwchase17/langchain.git
synced 2026-06-09 10:17:00 +00:00
feat(openai): support openai sdk 2.0 (#33168)
This commit is contained in:
@@ -3611,6 +3611,55 @@ def _construct_responses_api_payload(
|
||||
return payload
|
||||
|
||||
|
||||
def _convert_chat_completions_blocks_to_responses(
|
||||
block: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Convert chat completions content blocks to responses API format.
|
||||
|
||||
Only handles text, image, file blocks. Others pass through.
|
||||
"""
|
||||
if block["type"] == "text":
|
||||
# chat api: {"type": "text", "text": "..."}
|
||||
# responses api: {"type": "input_text", "text": "..."}
|
||||
return {"type": "input_text", "text": block["text"]}
|
||||
if block["type"] == "image_url":
|
||||
# chat api: {"type": "image_url", "image_url": {"url": "...", "detail": "..."}} # noqa: E501
|
||||
# responses api: {"type": "image_url", "image_url": "...", "detail": "...", "file_id": "..."} # noqa: E501
|
||||
new_block = {
|
||||
"type": "input_image",
|
||||
"image_url": block["image_url"]["url"],
|
||||
}
|
||||
if block["image_url"].get("detail"):
|
||||
new_block["detail"] = block["image_url"]["detail"]
|
||||
return new_block
|
||||
if block["type"] == "file":
|
||||
return {"type": "input_file", **block["file"]}
|
||||
return block
|
||||
|
||||
|
||||
def _ensure_valid_tool_message_content(tool_output: Any) -> Union[str, list[dict]]:
|
||||
if isinstance(tool_output, str):
|
||||
return tool_output
|
||||
if isinstance(tool_output, list) and all(
|
||||
isinstance(block, dict)
|
||||
and block.get("type")
|
||||
in (
|
||||
"input_text",
|
||||
"input_image",
|
||||
"input_file",
|
||||
"text",
|
||||
"image_url",
|
||||
"file",
|
||||
)
|
||||
for block in tool_output
|
||||
):
|
||||
return [
|
||||
_convert_chat_completions_blocks_to_responses(block)
|
||||
for block in tool_output
|
||||
]
|
||||
return _stringify(tool_output)
|
||||
|
||||
|
||||
def _make_computer_call_output_from_message(message: ToolMessage) -> dict:
|
||||
computer_call_output: dict = {
|
||||
"call_id": message.tool_call_id,
|
||||
@@ -3684,8 +3733,7 @@ def _construct_responses_api_input(messages: Sequence[BaseMessage]) -> list:
|
||||
)
|
||||
input_.append(computer_call_output)
|
||||
else:
|
||||
if not isinstance(tool_output, str):
|
||||
tool_output = _stringify(tool_output)
|
||||
tool_output = _ensure_valid_tool_message_content(tool_output)
|
||||
function_call_output = {
|
||||
"type": "function_call_output",
|
||||
"output": tool_output,
|
||||
@@ -3785,23 +3833,10 @@ def _construct_responses_api_input(messages: Sequence[BaseMessage]) -> list:
|
||||
new_blocks = []
|
||||
non_message_item_types = ("mcp_approval_response",)
|
||||
for block in msg["content"]:
|
||||
# chat api: {"type": "text", "text": "..."}
|
||||
# responses api: {"type": "input_text", "text": "..."}
|
||||
if block["type"] == "text":
|
||||
new_blocks.append({"type": "input_text", "text": block["text"]})
|
||||
# chat api: {"type": "image_url", "image_url": {"url": "...", "detail": "..."}} # noqa: E501
|
||||
# responses api: {"type": "image_url", "image_url": "...", "detail": "...", "file_id": "..."} # noqa: E501
|
||||
elif block["type"] == "image_url":
|
||||
new_block = {
|
||||
"type": "input_image",
|
||||
"image_url": block["image_url"]["url"],
|
||||
}
|
||||
if block["image_url"].get("detail"):
|
||||
new_block["detail"] = block["image_url"]["detail"]
|
||||
new_blocks.append(new_block)
|
||||
elif block["type"] == "file":
|
||||
new_block = {"type": "input_file", **block["file"]}
|
||||
new_blocks.append(new_block)
|
||||
if block["type"] in ("text", "image_url", "file"):
|
||||
new_blocks.append(
|
||||
_convert_chat_completions_blocks_to_responses(block)
|
||||
)
|
||||
elif block["type"] in ("input_text", "input_image", "input_file"):
|
||||
new_blocks.append(block)
|
||||
elif block["type"] in non_message_item_types:
|
||||
|
||||
@@ -8,7 +8,7 @@ license = { text = "MIT" }
|
||||
requires-python = ">=3.9.0,<4.0.0"
|
||||
dependencies = [
|
||||
"langchain-core>=0.3.76,<2.0.0",
|
||||
"openai>=1.104.2,<2.0.0",
|
||||
"openai>=1.104.2,<3.0.0",
|
||||
"tiktoken>=0.7.0,<1.0.0",
|
||||
]
|
||||
name = "langchain-openai"
|
||||
|
||||
@@ -22,6 +22,10 @@ class TestOpenAIResponses(TestOpenAIStandard):
|
||||
def chat_model_params(self) -> dict:
|
||||
return {"model": "gpt-4o-mini", "use_responses_api": True}
|
||||
|
||||
@property
|
||||
def supports_image_tool_message(self) -> bool:
|
||||
return True
|
||||
|
||||
@pytest.mark.xfail(reason="Unsupported.")
|
||||
def test_stop_sequence(self, model: BaseChatModel) -> None:
|
||||
super().test_stop_sequence(model)
|
||||
|
||||
10
libs/partners/openai/uv.lock
generated
10
libs/partners/openai/uv.lock
generated
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
revision = 2
|
||||
requires-python = ">=3.9.0, <4.0.0"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
|
||||
@@ -647,7 +647,7 @@ typing = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "langchain-core", editable = "../../core" },
|
||||
{ name = "openai", specifier = ">=1.104.2,<2.0.0" },
|
||||
{ name = "openai", specifier = ">=1.104.2,<3.0.0" },
|
||||
{ name = "tiktoken", specifier = ">=0.7.0,<1.0.0" },
|
||||
]
|
||||
|
||||
@@ -1183,7 +1183,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.107.3"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -1195,9 +1195,9 @@ dependencies = [
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/24/7fb5749bcf66b52209e3ece05cb4eaeae2102e95f8ae77589e8afaf70ba8/openai-1.107.3.tar.gz", hash = "sha256:69bb8032b05c5f00f7660e422f70f9aabc94793b9a30c5f899360ed21e46314f", size = 564194, upload-time = "2025-09-15T20:09:20.159Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/5d/74fa2b0358ef15d113b1a6ca2323cee0034020b085a81a94eeddc6914de9/openai-2.0.0.tar.gz", hash = "sha256:6b9513b485f856b0be6bc44c518831acb58e37a12bed72fcc52b1177d1fb34a8", size = 565732, upload-time = "2025-09-30T17:35:57.632Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/16/1d/58ad0084451f64a9193de48c0afd63047682ffdedb6ae1d494a203e03fd5/openai-1.107.3-py3-none-any.whl", hash = "sha256:4ca54a847235ac04c6320da70fdc06b62d71439de9ec0aa40d5690c3064d4025", size = 947600, upload-time = "2025-09-15T20:09:18.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/41/86ddc9cdd885acc02ee50ec24ea1c5e324eea0c7a471ee841a7088653558/openai-2.0.0-py3-none-any.whl", hash = "sha256:a79f493651f9843a6c54789a83f3b2db56df0e1770f7dcbe98bcf0e967ee2148", size = 955538, upload-time = "2025-09-30T17:35:54.695Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user