From ebea5e014de53b6c9c3463a91985d4d0cead54b0 Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 13 Mar 2025 12:34:12 -0400 Subject: [PATCH 1/7] standard tests: test simple agent loop (#30268) --- .../integration_tests/chat_models.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py index 3474aac5233..ea2dcf52faa 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py @@ -2139,6 +2139,71 @@ class ChatModelIntegrationTests(ChatModelTests): assert isinstance(result.content, str) assert len(result.content) > 0 + def test_agent_loop(self, model: BaseChatModel) -> None: + """Test that the model supports a simple ReAct agent loop. This test is skipped + if the ``has_tool_calling`` property on the test class is set to False. + + This test is optional and should be skipped if the model does not support + tool calling (see Configuration below). + + .. dropdown:: Configuration + + To disable tool calling tests, set ``has_tool_calling`` to False in your + test class: + + .. code-block:: python + + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + + .. dropdown:: Troubleshooting + + If this test fails, check that ``bind_tools`` is implemented to correctly + translate LangChain tool objects into the appropriate schema for your + chat model. + + Check also that all required information (e.g., tool calling identifiers) + from AIMessage objects is propagated correctly to model payloads. + + This test may fail if the chat model does not consistently generate tool + calls in response to an appropriate query. In these cases you can ``xfail`` + the test: + + .. code-block:: python + + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + def test_agent_loop(self, model: BaseChatModel) -> None: + super().test_agent_loop(model) + + """ + if not self.has_tool_calling: + pytest.skip("Test requires tool calling.") + + @tool + def get_weather(location: str) -> str: + """Call to surf the web.""" + return "It's sunny." + + llm_with_tools = model.bind_tools([get_weather]) + input_message = HumanMessage("What is the weather in San Francisco, CA?") + tool_call_message = llm_with_tools.invoke([input_message]) + assert isinstance(tool_call_message, AIMessage) + tool_calls = tool_call_message.tool_calls + assert len(tool_calls) == 1 + tool_call = tool_calls[0] + tool_message = get_weather.invoke(tool_call) + assert isinstance(tool_message, ToolMessage) + response = llm_with_tools.invoke( + [ + input_message, + tool_call_message, + tool_message, + ] + ) + assert isinstance(response, AIMessage) + def invoke_with_audio_input(self, *, stream: bool = False) -> AIMessage: """:private:""" raise NotImplementedError() From e9c1765967063404e6c9ff553831f83b2daeec34 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Thu, 13 Mar 2025 12:27:03 -0700 Subject: [PATCH 2/7] fix(core): Ignore missing secrets on deserialization (#30252) --- libs/core/langchain_core/load/load.py | 3 +-- libs/langchain/tests/unit_tests/load/test_load.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/core/langchain_core/load/load.py b/libs/core/langchain_core/load/load.py index c407a25880c..6ceb2d3d7c8 100644 --- a/libs/core/langchain_core/load/load.py +++ b/libs/core/langchain_core/load/load.py @@ -98,8 +98,7 @@ class Reviver: else: if self.secrets_from_env and key in os.environ and os.environ[key]: return os.environ[key] - msg = f'Missing key "{key}" in load(secrets_map)' - raise KeyError(msg) + return None if ( value.get("lc") == 1 diff --git a/libs/langchain/tests/unit_tests/load/test_load.py b/libs/langchain/tests/unit_tests/load/test_load.py index 24d5b81fe38..5e95b024016 100644 --- a/libs/langchain/tests/unit_tests/load/test_load.py +++ b/libs/langchain/tests/unit_tests/load/test_load.py @@ -167,3 +167,13 @@ def test_load_llmchain_with_non_serializable_arg() -> None: chain_obj = dumpd(chain) with pytest.raises(NotImplementedError): load(chain_obj, secrets_map={"OPENAI_API_KEY": "hello"}) + + +@pytest.mark.requires("openai", "langchain_openai") +def test_loads_with_missing_secrets() -> None: + import openai + + llm_string = '{"lc": 1, "type": "constructor", "id": ["langchain", "llms", "openai", "OpenAI"], "kwargs": {"model_name": "davinci", "temperature": 0.5, "max_tokens": 256, "top_p": 0.8, "n": 1, "best_of": 1, "openai_api_key": {"lc": 1, "type": "secret", "id": ["OPENAI_API_KEY"]}, "batch_size": 20, "max_retries": 2, "disallowed_special": "all"}, "name": "OpenAI"}' # noqa: E501 + # Should throw on instantiation, not deserialization + with pytest.raises(openai.OpenAIError): + loads(llm_string) From 733abcc8848f87ef33903a8c052f931d9c0d1caf Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 13 Mar 2025 17:54:34 -0400 Subject: [PATCH 3/7] mistral: release 0.2.8 (#30275) --- libs/partners/mistralai/pyproject.toml | 4 ++-- libs/partners/mistralai/uv.lock | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libs/partners/mistralai/pyproject.toml b/libs/partners/mistralai/pyproject.toml index 8612b46ebfc..189639aa1ff 100644 --- a/libs/partners/mistralai/pyproject.toml +++ b/libs/partners/mistralai/pyproject.toml @@ -7,14 +7,14 @@ authors = [] license = { text = "MIT" } requires-python = "<4.0,>=3.9" dependencies = [ - "langchain-core<1.0.0,>=0.3.37", + "langchain-core<1.0.0,>=0.3.44", "tokenizers<1,>=0.15.1", "httpx<1,>=0.25.2", "httpx-sse<1,>=0.3.1", "pydantic<3,>=2", ] name = "langchain-mistralai" -version = "0.2.7" +version = "0.2.8" description = "An integration package connecting Mistral and LangChain" readme = "README.md" diff --git a/libs/partners/mistralai/uv.lock b/libs/partners/mistralai/uv.lock index f08be986c3e..e321679f6d9 100644 --- a/libs/partners/mistralai/uv.lock +++ b/libs/partners/mistralai/uv.lock @@ -332,7 +332,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.37" +version = "0.3.45rc1" source = { editable = "../../core" } dependencies = [ { name = "jsonpatch" }, @@ -390,7 +390,7 @@ typing = [ [[package]] name = "langchain-mistralai" -version = "0.2.7" +version = "0.2.8" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -450,7 +450,7 @@ typing = [ [[package]] name = "langchain-tests" -version = "0.3.12" +version = "0.3.14" source = { editable = "../../standard-tests" } dependencies = [ { name = "httpx" }, @@ -467,8 +467,7 @@ dependencies = [ requires-dist = [ { name = "httpx", specifier = ">=0.25.0,<1" }, { name = "langchain-core", editable = "../../core" }, - { name = "numpy", marker = "python_full_version < '3.12'", specifier = ">=1.24.0,<2.0.0" }, - { name = "numpy", marker = "python_full_version >= '3.12'", specifier = ">=1.26.2,<3" }, + { name = "numpy", specifier = ">=1.26.2,<3" }, { name = "pytest", specifier = ">=7,<9" }, { name = "pytest-asyncio", specifier = ">=0.20,<1" }, { name = "pytest-socket", specifier = ">=0.6.0,<1" }, From 5e0fa2cce5a7227af083f630e9042ad3658bebbf Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 13 Mar 2025 18:10:59 -0400 Subject: [PATCH 4/7] infra: update release pipeline (#30276) Instead of attempting to conditionally `needs` job, always run job and exit successfully if not needed. --- .github/workflows/_release.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml index d02248d8006..74eb252de8b 100644 --- a/.github/workflows/_release.yml +++ b/.github/workflows/_release.yml @@ -336,7 +336,6 @@ jobs: - release-notes - test-pypi-publish - pre-release-checks - if: ${{ startsWith(inputs.working-directory, 'libs/core') }} runs-on: ubuntu-latest strategy: matrix: @@ -355,17 +354,29 @@ jobs: steps: - uses: actions/checkout@v4 + # We implement this conditional as Github Actions does not have good support + # for conditionally needing steps. https://github.com/actions/runner/issues/491 + - name: Check if libs/core + run: | + if [ "${{ startsWith(inputs.working-directory, 'libs/core') }}" != "true" ]; then + echo "Not in libs/core. Exiting successfully." + exit 0 + fi + - name: Set up Python + uv + if: startsWith(inputs.working-directory, 'libs/core') uses: "./.github/actions/uv_setup" with: python-version: ${{ env.PYTHON_VERSION }} - uses: actions/download-artifact@v4 + if: startsWith(inputs.working-directory, 'libs/core') with: name: dist path: ${{ inputs.working-directory }}/dist/ - name: Test against ${{ matrix.partner }} + if: startsWith(inputs.working-directory, 'libs/core') run: | # Identify latest tag LATEST_PACKAGE_TAG="$( @@ -401,15 +412,6 @@ jobs: - test-pypi-publish - pre-release-checks - test-prior-published-packages-against-new-core - if: > - always() && - needs.build.result == 'success' && - needs.release-notes.result == 'success' && - needs.test-pypi-publish.result == 'success' && - needs.pre-release-checks.result == 'success' && ( - (startsWith(inputs.working-directory, 'libs/core') && needs.test-prior-published-packages-against-new-core.result == 'success') - || (!startsWith(inputs.working-directory, 'libs/core')) - ) runs-on: ubuntu-latest permissions: # This permission is used for trusted publishing: From d0bfc7f82001567390d7f942fc2e49dcb9e7b370 Mon Sep 17 00:00:00 2001 From: pulvedu Date: Thu, 13 Mar 2025 18:19:31 -0400 Subject: [PATCH 5/7] community[fix] : Pass API_KEY as argument (#30272) PR Title: community: Fix Pass API_KEY as argument PR Message: Description: This PR fixes validation error "Value error, Did not find tavily_api_key, please add an environment variable `TAVILY_API_KEY` which contains it, or pass `tavily_api_key` as a named parameter." Dependencies: No new dependencies introduced. --------- Co-authored-by: pulvedu Co-authored-by: Chester Curme --- .../langchain_community/tools/tavily_search/tool.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/community/langchain_community/tools/tavily_search/tool.py b/libs/community/langchain_community/tools/tavily_search/tool.py index 765f99daecf..32cd21d4197 100644 --- a/libs/community/langchain_community/tools/tavily_search/tool.py +++ b/libs/community/langchain_community/tools/tavily_search/tool.py @@ -1,6 +1,6 @@ """Tool for the Tavily search API.""" -from typing import Dict, List, Literal, Optional, Tuple, Type, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union from langchain_core.callbacks import ( AsyncCallbackManagerForToolRun, @@ -149,6 +149,15 @@ class TavilySearchResults(BaseTool): # type: ignore[override, override] api_wrapper: TavilySearchAPIWrapper = Field(default_factory=TavilySearchAPIWrapper) # type: ignore[arg-type] response_format: Literal["content_and_artifact"] = "content_and_artifact" + def __init__(self, **kwargs: Any) -> None: + # Create api_wrapper with tavily_api_key if provided + if "tavily_api_key" in kwargs: + kwargs["api_wrapper"] = TavilySearchAPIWrapper( + tavily_api_key=kwargs["tavily_api_key"] + ) + + super().__init__(**kwargs) + def _run( self, query: str, From 315bb17ef5ce3280f439bde6664510e8a533cfe6 Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 13 Mar 2025 18:44:23 -0400 Subject: [PATCH 6/7] core: release 0.3.45 (#30277) --- libs/core/pyproject.toml | 2 +- libs/core/uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/core/pyproject.toml b/libs/core/pyproject.toml index 620a0ea23ab..1748a9a3809 100644 --- a/libs/core/pyproject.toml +++ b/libs/core/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "pydantic<3.0.0,>=2.7.4; python_full_version >= \"3.12.4\"", ] name = "langchain-core" -version = "0.3.45-rc.1" +version = "0.3.45" description = "Building applications with LLMs through composability" readme = "README.md" diff --git a/libs/core/uv.lock b/libs/core/uv.lock index bf9653da199..a26b6e0cab4 100644 --- a/libs/core/uv.lock +++ b/libs/core/uv.lock @@ -935,7 +935,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.44" +version = "0.3.45" source = { editable = "." } dependencies = [ { name = "jsonpatch" }, From bbd4b36d7617ac5b78ebdd5a4553ffb5c8bd6256 Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 13 Mar 2025 19:04:36 -0400 Subject: [PATCH 7/7] mistralai[patch]: bump core (#30278) --- libs/partners/mistralai/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/partners/mistralai/pyproject.toml b/libs/partners/mistralai/pyproject.toml index 189639aa1ff..64a9bcad770 100644 --- a/libs/partners/mistralai/pyproject.toml +++ b/libs/partners/mistralai/pyproject.toml @@ -7,7 +7,7 @@ authors = [] license = { text = "MIT" } requires-python = "<4.0,>=3.9" dependencies = [ - "langchain-core<1.0.0,>=0.3.44", + "langchain-core<1.0.0,>=0.3.45", "tokenizers<1,>=0.15.1", "httpx<1,>=0.25.2", "httpx-sse<1,>=0.3.1",