diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index dc625ea0983..810ac6240a9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,28 +1,34 @@ Thank you for contributing to LangChain! -- [ ] **PR title**: "package: description" - - Where "package" is whichever of langchain, core, etc. is being modified. Use "docs: ..." for purely docs changes, "infra: ..." for CI changes. - - Example: "core: add foobar LLM" - +- [ ] **PR title**: Follows the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/): {TYPE}({SCOPE}): {DESCRIPTION} + - The `{DESCRIPTION}` must not start with an uppercase letter. + - Examples: + - feat(core): add multi-tenant support + - fix(cli): resolve flag parsing error + - docs(openai): update API usage examples + - Allowed `{TYPE}` values: + - feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, release + - Allowed `{SCOPE}` values (optional): + - core, cli, langchain, standard-tests, docs, anthropic, chroma, deepseek, exa, fireworks, groq, huggingface, mistralai, nomic, ollama, openai, perplexity, prompty, qdrant, xai - [ ] **PR message**: ***Delete this entire checklist*** and replace with - - **Description:** a description of the change + - **Description:** a description of the change. Include a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) if applicable. - **Issue:** the issue # it fixes, if applicable - **Dependencies:** any dependencies required for this change - **Twitter handle:** if your PR gets announced, and you'd like a mention, we'll gladly shout you out! - [ ] **Add tests and docs**: If you're adding a new integration, please include - 1. a test for the integration, preferably unit tests that do not rely on network access, - 2. an example notebook showing its use. It lives in `docs/docs/integrations` directory. + 1. A test for the integration, preferably unit tests that do not rely on network access, + 2. An example notebook showing its use. It lives in `docs/docs/integrations` directory. -- [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. See contribution guidelines for more: https://python.langchain.com/docs/contributing/ +- [ ] **Lint and test**: Run `make format`, `make lint` and `make test` from the root of the package(s) you've modified. We will not consider a PR unless these three are passing in CI. See contribution guidelines for more: https://python.langchain.com/docs/contributing/ Additional guidelines: - Make sure optional dependencies are imported within a function. -- Please do not add dependencies to pyproject.toml files (even optional ones) unless they are required for unit tests. +- Please do not add dependencies to `pyproject.toml` files (even optional ones) unless they are **required** for unit tests. - Most PRs should not touch more than one package. - Changes should be backwards compatible. -If no one reviews your PR within a few days, please @-mention one of baskaryan, eyurtsev, ccurme, vbarda, hwchase17. +If no one reviews your PR within a few days, please @-mention one of eyurtsev, ccurme, mdrxy. diff --git a/libs/partners/openai/langchain_openai/chat_models/base.py b/libs/partners/openai/langchain_openai/chat_models/base.py index a262a53b76e..84403aecd1d 100644 --- a/libs/partners/openai/langchain_openai/chat_models/base.py +++ b/libs/partners/openai/langchain_openai/chat_models/base.py @@ -3465,6 +3465,10 @@ def _make_computer_call_output_from_message(message: ToolMessage) -> dict: # string, assume image_url output = {"type": "input_image", "image_url": message.content} computer_call_output["output"] = output + if "acknowledged_safety_checks" in message.additional_kwargs: + computer_call_output["acknowledged_safety_checks"] = message.additional_kwargs[ + "acknowledged_safety_checks" + ] return computer_call_output diff --git a/libs/partners/openai/pyproject.toml b/libs/partners/openai/pyproject.toml index 596dddb9f1b..5bffdabcf44 100644 --- a/libs/partners/openai/pyproject.toml +++ b/libs/partners/openai/pyproject.toml @@ -7,12 +7,12 @@ authors = [] license = { text = "MIT" } requires-python = ">=3.9" dependencies = [ - "langchain-core<1.0.0,>=0.3.66", + "langchain-core<1.0.0,>=0.3.68", "openai<2.0.0,>=1.86.0", "tiktoken<1,>=0.7", ] name = "langchain-openai" -version = "0.3.27" +version = "0.3.28" description = "An integration package connecting OpenAI and LangChain" readme = "README.md" diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_base.py b/libs/partners/openai/tests/unit_tests/chat_models/test_base.py index 4e8f57ce7e0..d387ee92170 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_base.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_base.py @@ -63,6 +63,7 @@ from langchain_openai.chat_models.base import ( _create_usage_metadata_responses, _format_message_content, _get_last_messages, + _make_computer_call_output_from_message, _oai_structured_outputs_parser, ) @@ -2454,3 +2455,76 @@ def test_get_request_payload_use_previous_response_id() -> None: payload = llm._get_request_payload(messages) assert "previous_response_id" not in payload assert len(payload["input"]) == 1 + + +def test_make_computer_call_output_from_message() -> None: + # List content + tool_message = ToolMessage( + content=[ + {"type": "input_image", "image_url": "data:image/png;base64,"} + ], + tool_call_id="call_abc123", + additional_kwargs={"type": "computer_call_output"}, + ) + result = _make_computer_call_output_from_message(tool_message) + + assert result == { + "type": "computer_call_output", + "call_id": "call_abc123", + "output": { + "type": "input_image", + "image_url": "data:image/png;base64,", + }, + } + + # String content + tool_message = ToolMessage( + content="data:image/png;base64,", + tool_call_id="call_abc123", + additional_kwargs={"type": "computer_call_output"}, + ) + result = _make_computer_call_output_from_message(tool_message) + + assert result == { + "type": "computer_call_output", + "call_id": "call_abc123", + "output": { + "type": "input_image", + "image_url": "data:image/png;base64,", + }, + } + + # Safety checks + tool_message = ToolMessage( + content=[ + {"type": "input_image", "image_url": "data:image/png;base64,"} + ], + tool_call_id="call_abc123", + additional_kwargs={ + "type": "computer_call_output", + "acknowledged_safety_checks": [ + { + "id": "cu_sc_abc234", + "code": "malicious_instructions", + "message": "Malicious instructions detected.", + } + ], + }, + ) + result = _make_computer_call_output_from_message(tool_message) + + assert result == { + "type": "computer_call_output", + "call_id": "call_abc123", + "output": { + "type": "input_image", + "image_url": "data:image/png;base64,", + }, + "acknowledged_safety_checks": [ + { + "id": "cu_sc_abc234", + "code": "malicious_instructions", + "message": "Malicious instructions detected.", + } + ], + } diff --git a/libs/partners/openai/uv.lock b/libs/partners/openai/uv.lock index 6e7d9f28d10..3bcdd8f46a2 100644 --- a/libs/partners/openai/uv.lock +++ b/libs/partners/openai/uv.lock @@ -538,7 +538,7 @@ typing = [ [[package]] name = "langchain-openai" -version = "0.3.27" +version = "0.3.28" source = { editable = "." } dependencies = [ { name = "langchain-core" }, @@ -995,7 +995,7 @@ wheels = [ [[package]] name = "openai" -version = "1.95.0" +version = "1.95.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1007,9 +1007,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/2f/0c6f509a1585545962bfa6e201d7fb658eb2a6f52fb8c26765632d91706c/openai-1.95.0.tar.gz", hash = "sha256:54bc42df9f7142312647dd485d34cca5df20af825fa64a30ca55164be2cf4cc9", size = 488144, upload-time = "2025-07-10T18:35:49.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/a3/70cd57c7d71086c532ce90de5fdef4165dc6ae9dbf346da6737ff9ebafaa/openai-1.95.1.tar.gz", hash = "sha256:f089b605282e2a2b6776090b4b46563ac1da77f56402a222597d591e2dcc1086", size = 488271, upload-time = "2025-07-11T20:47:24.437Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/a5/57d0bb58b938a3e3f352ff26e645da1660436402a6ad1b29780d261cc5a5/openai-1.95.0-py3-none-any.whl", hash = "sha256:a7afc9dca7e7d616371842af8ea6dbfbcb739a85d183f5f664ab1cc311b9ef18", size = 755572, upload-time = "2025-07-10T18:35:47.507Z" }, + { url = "https://files.pythonhosted.org/packages/02/1d/0432ea635097f4dbb34641a3650803d8a4aa29d06bafc66583bf1adcceb4/openai-1.95.1-py3-none-any.whl", hash = "sha256:8bbdfeceef231b1ddfabbc232b179d79f8b849aab5a7da131178f8d10e0f162f", size = 755613, upload-time = "2025-07-11T20:47:22.629Z" }, ] [[package]]