mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-02 05:08:44 +02:00
Follow-up to #8540, which turned on `PLC0415` (import-outside-top-level) for checkpoint-postgres and checkpoint-sqlite. This does the remaining six packages: checkpoint, checkpoint-conformance, langgraph, prebuilt, cli, sdk-py. Scoped to tests, per @sydney-runkle's call on #8540: library code is exempted with `per-file-ignores`, since it still has deferred imports nobody has reviewed and mixing that in would make this hard to read. ## What changed Function-level imports across 56 test files moved to module level. Nine could not move and carry an explicit `# noqa: PLC0415` with a reason: | File | Why it stays local | |---|---| | `libs/langgraph/tests/test_deprecation.py` (4) | the import has to run inside `pytest.warns` for the warning to be observed | | `libs/langgraph/tests/test_serde_allowlist.py` | try/except guard, skips when langchain_core is absent | | `libs/langgraph/tests/test_delta_channel_benchmark.py` | optional psycopg probe | | `libs/checkpoint/tests/test_conformance_delta.py` (3) | protected by a module-level `pytest.importorskip`; hoisting past the guard turns a skip into a collection error | That last one is the trap: an import moved above `pytest.importorskip` silently defeats the guard. I hit it locally and it turned the skip into a `ModuleNotFoundError` at collection. Every file with an `importorskip` or `except ImportError` was checked by hand for this. ## Verification `make lint` and `make test` in each of the six: | Package | Tests | |---|---| | checkpoint | 156 passed, 17 skipped | | checkpoint-conformance | 1 passed | | langgraph | 1968 passed, 4 skipped | | prebuilt | 284 passed | | cli | 336 passed | | sdk-py | 493 passed | Also confirmed the rule actually fires: a throwaway test file with a function-level import is flagged in all six packages, and the source exemption holds.
144 lines
4.3 KiB
Python
144 lines
4.3 KiB
Python
"""Test that langsmith_tracing parameter is correctly mapped to langsmith_tracer in payloads."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from langgraph_sdk._async.runs import RunsClient
|
|
from langgraph_sdk._sync.runs import SyncRunsClient
|
|
from langgraph_sdk.schema import LangSmithTracing
|
|
|
|
|
|
@pytest.fixture
|
|
def tracing_config() -> LangSmithTracing:
|
|
return LangSmithTracing(
|
|
project_name="my-project",
|
|
example_id="example-123",
|
|
)
|
|
|
|
|
|
class TestLangSmithTracingPayload:
|
|
"""Verify langsmith_tracing param maps to langsmith_tracer in request payload."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_create_includes_langsmith_tracer(self, tracing_config):
|
|
"""Test that async create sends langsmith_tracer in payload."""
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
async def mock_post(_path, *, json=None, **_kwargs):
|
|
captured["json"] = json
|
|
return {"run_id": "r1", "status": "pending"}
|
|
|
|
http = MagicMock()
|
|
http.post = AsyncMock(side_effect=mock_post)
|
|
client = RunsClient(http)
|
|
|
|
await client.create(
|
|
thread_id="t1",
|
|
assistant_id="a1",
|
|
langsmith_tracing=tracing_config,
|
|
)
|
|
|
|
assert "langsmith_tracer" in captured["json"]
|
|
assert captured["json"]["langsmith_tracer"] == {
|
|
"project_name": "my-project",
|
|
"example_id": "example-123",
|
|
}
|
|
|
|
def test_sync_create_includes_langsmith_tracer(self, tracing_config):
|
|
"""Test that sync create sends langsmith_tracer in payload."""
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
def mock_post(_path, *, json=None, **_kwargs):
|
|
captured["json"] = json
|
|
return {"run_id": "r1", "status": "pending"}
|
|
|
|
http = MagicMock()
|
|
http.post = MagicMock(side_effect=mock_post)
|
|
client = SyncRunsClient(http)
|
|
|
|
client.create(
|
|
thread_id="t1",
|
|
assistant_id="a1",
|
|
langsmith_tracing=tracing_config,
|
|
)
|
|
|
|
assert "langsmith_tracer" in captured["json"]
|
|
assert captured["json"]["langsmith_tracer"] == {
|
|
"project_name": "my-project",
|
|
"example_id": "example-123",
|
|
}
|
|
|
|
def test_sync_wait_includes_langsmith_tracer(self, tracing_config):
|
|
"""Test that sync wait sends langsmith_tracer in payload."""
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
def mock_request_reconnect(_path, _method, *, json=None, **_kwargs):
|
|
captured["json"] = json
|
|
return {"messages": []}
|
|
|
|
http = MagicMock()
|
|
http.request_reconnect = MagicMock(side_effect=mock_request_reconnect)
|
|
client = SyncRunsClient(http)
|
|
|
|
client.wait(
|
|
thread_id="t1",
|
|
assistant_id="a1",
|
|
langsmith_tracing=tracing_config,
|
|
)
|
|
|
|
assert "langsmith_tracer" in captured["json"]
|
|
assert captured["json"]["langsmith_tracer"] == {
|
|
"project_name": "my-project",
|
|
"example_id": "example-123",
|
|
}
|
|
|
|
def test_create_without_langsmith_tracing_excludes_key(self):
|
|
"""Test that langsmith_tracer is not in payload when not provided."""
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
def mock_post(_path, *, json=None, **_kwargs):
|
|
captured["json"] = json
|
|
return {"run_id": "r1", "status": "pending"}
|
|
|
|
http = MagicMock()
|
|
http.post = MagicMock(side_effect=mock_post)
|
|
client = SyncRunsClient(http)
|
|
|
|
client.create(
|
|
thread_id="t1",
|
|
assistant_id="a1",
|
|
)
|
|
|
|
assert "langsmith_tracer" not in captured["json"]
|
|
|
|
def test_langsmith_tracing_project_name_only(self):
|
|
"""Test that langsmith_tracing works with only project_name."""
|
|
|
|
captured: dict[str, Any] = {}
|
|
|
|
def mock_post(_path, *, json=None, **_kwargs):
|
|
captured["json"] = json
|
|
return {"run_id": "r1", "status": "pending"}
|
|
|
|
http = MagicMock()
|
|
http.post = MagicMock(side_effect=mock_post)
|
|
client = SyncRunsClient(http)
|
|
|
|
client.create(
|
|
thread_id="t1",
|
|
assistant_id="a1",
|
|
langsmith_tracing={"project_name": "my-project"},
|
|
)
|
|
|
|
assert captured["json"]["langsmith_tracer"] == {
|
|
"project_name": "my-project",
|
|
}
|