Compare commits

...
Author SHA1 Message Date
William Fu-Hinthorn 7d86336927 Bump 2025-09-09 14:28:42 -07:00
William FH 4adbcbc13e chore(langgraph): Omit other keys from metadata by default (#6047)
Right now we include configuration as metadata (which is later passed to
tracing callbacks).

Right now we include any key that doesn't have a dunder prefix and whose
value is a string or int.

This PR proposes to also exclude keys with certain substrings (key,
secret, token).

I would also be in favor of dropping support for forwarding configurable
values to metadata entirely, but this is a smaller, less controversial
change.

Add a test.
2025-09-09 14:28:33 -07:00
5 changed files with 46 additions and 17 deletions
+18 -7
View File
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from collections import ChainMap from collections import ChainMap
from collections.abc import Sequence from collections.abc import Mapping, Sequence
from os import getenv from os import getenv
from typing import Any, cast from typing import Any, cast
@@ -312,11 +312,22 @@ def ensure_config(*configs: RunnableConfig | None) -> RunnableConfig:
for k, v in config.items(): for k, v in config.items():
if _is_not_empty(v) and k not in CONFIG_KEYS: if _is_not_empty(v) and k not in CONFIG_KEYS:
empty[CONF][k] = v empty[CONF][k] = v
_empty_metadata = empty["metadata"]
for key, value in empty[CONF].items(): for key, value in empty[CONF].items():
if ( if _exclude_as_metadata(key, value, _empty_metadata):
not key.startswith("__") continue
and isinstance(value, (str, int, float, bool)) _empty_metadata[key] = value
and key not in empty["metadata"]
):
empty["metadata"][key] = value
return empty return empty
_OMIT = ("key", "token", "secret", "password", "auth")
def _exclude_as_metadata(key: str, value: Any, metadata: Mapping[str, Any]) -> bool:
key_lower = key.casefold()
return (
key.startswith("__")
or not isinstance(value, (str, int, float, bool))
or key in metadata
or any(substr in key_lower for substr in _OMIT)
)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph" name = "langgraph"
version = "0.6.7" version = "0.6.8"
description = "Building stateful, multi-actor applications with LLMs" description = "Building stateful, multi-actor applications with LLMs"
authors = [] authors = []
requires-python = ">=3.9" requires-python = ">=3.9"
+23 -5
View File
@@ -17,16 +17,13 @@ import langsmith
import pytest import pytest
from typing_extensions import NotRequired, Required, TypedDict from typing_extensions import NotRequired, Required, TypedDict
from langgraph._internal._config import _is_not_empty from langgraph._internal._config import _is_not_empty, ensure_config
from langgraph._internal._fields import ( from langgraph._internal._fields import (
_is_optional_type, _is_optional_type,
get_enhanced_type_hints, get_enhanced_type_hints,
get_field_default, get_field_default,
) )
from langgraph._internal._runnable import ( from langgraph._internal._runnable import is_async_callable, is_async_generator
is_async_callable,
is_async_generator,
)
from langgraph.constants import END from langgraph.constants import END
from langgraph.graph import StateGraph from langgraph.graph import StateGraph
from langgraph.graph.state import CompiledStateGraph from langgraph.graph.state import CompiledStateGraph
@@ -301,3 +298,24 @@ def test_is_not_empty() -> None:
assert not _is_not_empty([]) assert not _is_not_empty([])
assert not _is_not_empty(()) assert not _is_not_empty(())
assert not _is_not_empty({}) assert not _is_not_empty({})
def test_configurable_metadata():
config = {
"configurable": {
"a-key": "foo",
"somesecretval": "bar",
"sometoken": "thetoken",
"__dontinclude": "bar",
"includeme": "hi",
"andme": 42,
"nested": {"foo": "bar"},
"nooverride": -2,
},
"metadata": {"nooverride": 18},
}
expected = {"includeme", "andme", "nooverride"}
merged = ensure_config(config)
metadata = merged["metadata"]
assert metadata.keys() == expected
assert metadata["nooverride"] == 18
+2 -2
View File
@@ -1,5 +1,5 @@
version = 1 version = 1
revision = 2 revision = 3
requires-python = ">=3.9" requires-python = ">=3.9"
resolution-markers = [ resolution-markers = [
"python_full_version >= '3.14'", "python_full_version >= '3.14'",
@@ -1269,7 +1269,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph" name = "langgraph"
version = "0.6.7" version = "0.6.8"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
+2 -2
View File
@@ -1,5 +1,5 @@
version = 1 version = 1
revision = 2 revision = 3
requires-python = ">=3.9" requires-python = ">=3.9"
[[package]] [[package]]
@@ -316,7 +316,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph" name = "langgraph"
version = "0.6.7" version = "0.6.8"
source = { editable = "../langgraph" } source = { editable = "../langgraph" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },