mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-25 09:02:25 +02:00
Follow-up to review on #8540, where a stale `# noqa: E402` slipped past me and Sydney spotted it by eye. This turns on the rule that catches that automatically. `RUF100` flags a `noqa` that suppresses nothing. `sdk-py` already had it through its blanket `RUF` selection; this adds it to the other seven packages and clears what it finds. ### The 33 it flags, all autofixed **Blanket `# noqa` on docstring-closing lines** (4, in `checkpoint-postgres` and `checkpoint-sqlite`). `E501` is in `lint.ignore` for those packages, so nothing was being suppressed: ```diff - """ # noqa + """ ``` **`# noqa: F821` on `anext(aiter_)`** (2). Left over from Python 3.9 support. `anext` became a builtin in 3.10, which is the floor now, so `F821` no longer fires: ```diff - anext(aiter_), # type: ignore[arg-type] # noqa: F821 + anext(aiter_), # type: ignore[arg-type] ``` **Suppressions naming rules the package does not enable** (27), across `langgraph`, `prebuilt` and `checkpoint-sqlite`: `FBT001`, `FBT002`, `TC002`, `BLE001`, `ANN001`, `ANN002`, `ANN003`, `E501`, `F401`. Mostly copied between packages whose rule sets differ. ### One measurement note If you check these numbers yourself, use `--extend-select`: ``` ruff check --select RUF100 . # 81, misleading ruff check --extend-select RUF100 . # 33, real ``` With a bare `--select`, ruff treats every other rule as disabled, so every suppression for another rule looks unused. I quoted 81 before catching that. ### Verified `checkpoint-sqlite` 118 passed, `prebuilt` 284 passed, `langgraph` 1968 passed, `checkpoint-postgres` 264 passed on PG 15 and 16. `make lint` clean in every package. Independent of #8540 and #8537, so it can land in any order.
117 lines
4.2 KiB
Python
117 lines
4.2 KiB
Python
import pytest
|
|
from langchain_core.callbacks import AsyncCallbackManager, BaseCallbackHandler
|
|
|
|
from langgraph._internal._config import get_async_callback_manager_for_config
|
|
from langgraph.graph import StateGraph
|
|
|
|
pytestmark = pytest.mark.anyio
|
|
|
|
|
|
def test_new_async_manager_includes_tags() -> None:
|
|
config = {"callbacks": None}
|
|
manager = get_async_callback_manager_for_config(config, tags=["x", "y"])
|
|
assert isinstance(manager, AsyncCallbackManager)
|
|
assert manager.inheritable_tags == ["x", "y"]
|
|
|
|
|
|
def test_new_async_manager_merges_tags_with_config() -> None:
|
|
config = {"callbacks": None, "tags": ["a"]}
|
|
manager = get_async_callback_manager_for_config(config, tags=["b"])
|
|
assert manager.inheritable_tags == ["a", "b"]
|
|
|
|
|
|
class _TrackingCallback(BaseCallbackHandler):
|
|
def __init__(self) -> None:
|
|
self.called = False
|
|
|
|
def on_chain_start(self, *args, **kwargs) -> None:
|
|
self.called = True
|
|
|
|
|
|
async def test_with_config_callbacks_preserved_in_astream_events() -> None:
|
|
"""A callback bound via .with_config(...) must survive when
|
|
astream_events injects its own internal callback handler.
|
|
|
|
Pre-fix: ensure_config overwrites the callbacks key, dropping the
|
|
bound handler. Post-fix: the handler list is merged.
|
|
"""
|
|
builder = StateGraph(dict)
|
|
builder.add_node("node", lambda state: state)
|
|
builder.add_edge("__start__", "node")
|
|
cb = _TrackingCallback()
|
|
graph = builder.compile().with_config({"callbacks": [cb]})
|
|
async for _ in graph.astream_events({}, version="v2"):
|
|
pass
|
|
assert cb.called, "user-bound callback was dropped by ensure_config overwrite"
|
|
|
|
|
|
async def test_with_config_configurable_preserved_on_invoke() -> None:
|
|
"""A configurable key bound via .with_config(...) must survive when
|
|
invoke-time config supplies a different configurable key.
|
|
|
|
Pre-fix: ensure_config overwrites the entire configurable dict.
|
|
Post-fix: the two dicts are shallow-merged per key.
|
|
"""
|
|
builder = StateGraph(dict)
|
|
captured: dict = {}
|
|
|
|
def node(state, config):
|
|
captured.update(config.get("configurable") or {})
|
|
return state
|
|
|
|
builder.add_node("node", node)
|
|
builder.add_edge("__start__", "node")
|
|
graph = builder.compile().with_config({"configurable": {"ls_agent_type": "root"}})
|
|
await graph.ainvoke({}, {"configurable": {"thread_id": "T1"}})
|
|
assert captured.get("ls_agent_type") == "root", (
|
|
"bound configurable key was dropped by ensure_config overwrite"
|
|
)
|
|
assert captured.get("thread_id") == "T1", "invoke-time key not present"
|
|
|
|
|
|
async def test_with_config_metadata_preserved_on_invoke() -> None:
|
|
"""A metadata key bound via .with_config(...) must survive when
|
|
invoke-time config supplies a different metadata key.
|
|
|
|
Pre-fix: ensure_config overwrites the entire metadata dict.
|
|
Post-fix: the two dicts are shallow-merged per key.
|
|
"""
|
|
builder = StateGraph(dict)
|
|
captured: dict = {}
|
|
|
|
def node(state, config):
|
|
captured.update(config.get("metadata") or {})
|
|
return state
|
|
|
|
builder.add_node("node", node)
|
|
builder.add_edge("__start__", "node")
|
|
graph = builder.compile().with_config({"metadata": {"user_id": "U1"}})
|
|
await graph.ainvoke({}, {"metadata": {"correlation_id": "C1"}})
|
|
assert captured.get("user_id") == "U1", (
|
|
"bound metadata key was dropped by ensure_config overwrite"
|
|
)
|
|
assert captured.get("correlation_id") == "C1", "invoke-time key not present"
|
|
|
|
|
|
async def test_with_config_tags_preserved_on_invoke() -> None:
|
|
"""Tags bound via .with_config(...) must survive when invoke-time
|
|
config supplies its own tags.
|
|
|
|
Pre-fix: ensure_config overwrites the entire tags list.
|
|
Post-fix: tags are concatenated (matching merge_configs behavior;
|
|
no deduplication, no sorting).
|
|
"""
|
|
builder = StateGraph(dict)
|
|
captured: list = []
|
|
|
|
def node(state, config):
|
|
captured.extend(config.get("tags") or [])
|
|
return state
|
|
|
|
builder.add_node("node", node)
|
|
builder.add_edge("__start__", "node")
|
|
graph = builder.compile().with_config({"tags": ["bound"]})
|
|
await graph.ainvoke({}, {"tags": ["invoke"]})
|
|
assert "bound" in captured, "bound tag was dropped by ensure_config overwrite"
|
|
assert "invoke" in captured, "invoke-time tag not present"
|