Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-langgraph-sdk for
openSUSE:Factory checked in at 2026-08-29 17:43:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langgraph-sdk (Old)
and /work/SRC/openSUSE:Factory/.python-langgraph-sdk.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-langgraph-sdk"
Sat Aug 29 17:43:20 2026 rev:4 rq:1374581 version:0.4.4
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-langgraph-sdk/python-langgraph-sdk.changes
2026-08-20 16:15:32.074756915 +0200
+++
/work/SRC/openSUSE:Factory/.python-langgraph-sdk.new.1265/python-langgraph-sdk.changes
2026-08-29 17:44:22.719325104 +0200
@@ -1,0 +2,17 @@
+Sat Aug 29 07:19:43 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 0.4.4:
+ * Resource-scoped auth decorators (@auth.on.threads, ...) now
+ honour their actions= argument instead of silently registering
+ a catch-all handler, and reject empty, duplicate or unknown
+ action names
+ * A resources= selector on a resource-specific decorator must
+ name that same resource; use @auth.on(resources=...) for
+ others
+ * Note: unmatched custom-auth paths are still allowed, so
+ deployments using action-scoped handlers should add a global
+ default-deny handler
+ * Thread streams accept langsmith_tracing=, forwarded to the
+ server as the run.start langsmith_tracer parameter
+
+-------------------------------------------------------------------
Old:
----
langgraph_sdk-0.4.3.tar.gz
New:
----
langgraph_sdk-0.4.4.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-langgraph-sdk.spec ++++++
--- /var/tmp/diff_new_pack.SEwrw1/_old 2026-08-29 17:44:23.961369037 +0200
+++ /var/tmp/diff_new_pack.SEwrw1/_new 2026-08-29 17:44:23.964369143 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-langgraph-sdk
-Version: 0.4.3
+Version: 0.4.4
Release: 0
Summary: SDK for interacting with the LangGraph API
License: MIT
++++++ langgraph_sdk-0.4.3.tar.gz -> langgraph_sdk-0.4.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/CHANGELOG.md
new/langgraph_sdk-0.4.4/CHANGELOG.md
--- old/langgraph_sdk-0.4.3/CHANGELOG.md 2020-02-02 01:00:00.000000000
+0100
+++ new/langgraph_sdk-0.4.4/CHANGELOG.md 2020-02-02 01:00:00.000000000
+0100
@@ -39,6 +39,15 @@
- `client.threads.stream()` now accepts `transport="sse"` (default) or
`transport="websocket"` in place of the previous transport-agnostic default.
+### Fixed
+
+- Resource-scoped auth decorators now honor `actions=` and reject empty or
+ invalid action lists. Because unmatched custom-auth paths remain allowed,
+ deployments using action-scoped handlers should configure a global
+ default-deny handler; `langgraph-api` 0.10+ warns about uncovered paths at
+ startup. Resource-specific decorators retain matching `resources=` selectors
+ for backward compatibility; use `@auth.on(resources=...)` for other
resources.
+
### Notes
- The v3 streaming surface (`AsyncThreadStream`, `SyncThreadStream`, and all
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/PKG-INFO
new/langgraph_sdk-0.4.4/PKG-INFO
--- old/langgraph_sdk-0.4.3/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.5
Name: langgraph-sdk
-Version: 0.4.3
+Version: 0.4.4
Summary: SDK for interacting with LangGraph API
Project-URL: Source,
https://github.com/langchain-ai/langgraph/tree/main/libs/sdk-py
Project-URL: Twitter, https://x.com/langchain_oss
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/langgraph_sdk/__init__.py
new/langgraph_sdk-0.4.4/langgraph_sdk/__init__.py
--- old/langgraph_sdk-0.4.3/langgraph_sdk/__init__.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/langgraph_sdk/__init__.py 2020-02-02
01:00:00.000000000 +0100
@@ -3,7 +3,7 @@
from langgraph_sdk.encryption import Encryption
from langgraph_sdk.encryption.types import DecryptResult, EncryptionContext
-__version__ = "0.4.3"
+__version__ = "0.4.4"
__all__ = [
"Auth",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/langgraph_sdk/_async/stream.py
new/langgraph_sdk-0.4.4/langgraph_sdk/_async/stream.py
--- old/langgraph_sdk-0.4.3/langgraph_sdk/_async/stream.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/langgraph_sdk/_async/stream.py 2020-02-02
01:00:00.000000000 +0100
@@ -24,7 +24,7 @@
from langchain_protocol import Event, SubscribeParams
from langgraph_sdk._async.http import HttpClient
-from langgraph_sdk.schema import QueryParamTypes
+from langgraph_sdk.schema import LangSmithTracing, QueryParamTypes
from langgraph_sdk.stream.controller import _SeenEventIds
from langgraph_sdk.stream.decoders import (
DataDecoder,
@@ -172,6 +172,7 @@
input: Any = None,
config: dict[str, Any] | None = None,
metadata: dict[str, Any] | None = None,
+ langsmith_tracing: LangSmithTracing | None = None,
) -> dict[str, Any]:
"""Send `run.start` to the server. Returns the result (`{"run_id":
...}`)."""
params: dict[str, Any] = {"assistant_id": self._owner.assistant_id}
@@ -181,6 +182,8 @@
params["config"] = config
if metadata is not None:
params["metadata"] = metadata
+ if langsmith_tracing is not None:
+ params["langsmith_tracer"] = langsmith_tracing
loop = asyncio.get_running_loop()
gate: asyncio.Future[None] = loop.create_future()
self._owner._run_start_ready = gate
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/langgraph_sdk/_sync/stream.py
new/langgraph_sdk-0.4.4/langgraph_sdk/_sync/stream.py
--- old/langgraph_sdk-0.4.3/langgraph_sdk/_sync/stream.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/langgraph_sdk/_sync/stream.py 2020-02-02
01:00:00.000000000 +0100
@@ -23,7 +23,7 @@
from langchain_protocol import Event, SubscribeParams
from langgraph_sdk._sync.http import SyncHttpClient
-from langgraph_sdk.schema import QueryParamTypes
+from langgraph_sdk.schema import LangSmithTracing, QueryParamTypes
from langgraph_sdk.stream.decoders import (
DataDecoder,
Decoder,
@@ -215,6 +215,7 @@
input: Any = None,
config: dict[str, Any] | None = None,
metadata: dict[str, Any] | None = None,
+ langsmith_tracing: LangSmithTracing | None = None,
) -> dict[str, Any]:
"""Send `run.start` to the server. Returns the result (`{"run_id":
...}`)."""
params: dict[str, Any] = {"assistant_id": self._owner.assistant_id}
@@ -224,6 +225,8 @@
params["config"] = config
if metadata is not None:
params["metadata"] = metadata
+ if langsmith_tracing is not None:
+ params["langsmith_tracer"] = langsmith_tracing
result = self._owner._send_command("run.start", params)
self._owner._run_seen = True
controller = self._owner._controller
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/langgraph_sdk/auth/__init__.py
new/langgraph_sdk-0.4.4/langgraph_sdk/auth/__init__.py
--- old/langgraph_sdk-0.4.3/langgraph_sdk/auth/__init__.py 2020-02-02
01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/langgraph_sdk/auth/__init__.py 2020-02-02
01:00:00.000000000 +0100
@@ -341,9 +341,15 @@
VRead = typing.TypeVar("VRead", covariant=True)
VDelete = typing.TypeVar("VDelete", covariant=True)
VSearch = typing.TypeVar("VSearch", covariant=True)
+ResourceActionT = typing.TypeVar("ResourceActionT", bound=str)
+_ResourceAction = typing.Literal["create", "read", "update", "delete",
"search"]
+_ThreadAction = _ResourceAction | typing.Literal["create_run"]
-class _ResourceOn(typing.Generic[VCreate, VRead, VUpdate, VDelete, VSearch]):
+
+class _ResourceOn(
+ typing.Generic[VCreate, VRead, VUpdate, VDelete, VSearch, ResourceActionT]
+):
"""
Generic base class for resource-specific handlers.
"""
@@ -392,8 +398,8 @@
def __call__(
self,
*,
- resources: str | Sequence[str],
- actions: str | Sequence[str] | None = None,
+ resources: str | Sequence[str] | None = None,
+ actions: ResourceActionT | Sequence[ResourceActionT] | None = None,
) -> Callable[
[_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]],
_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch],
@@ -408,7 +414,7 @@
) = None,
*,
resources: str | Sequence[str] | None = None,
- actions: str | Sequence[str] | None = None,
+ actions: ResourceActionT | Sequence[ResourceActionT] | None = None,
) -> (
_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]
| Callable[
@@ -416,24 +422,66 @@
_ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch],
]
):
- if fn is not None:
- _validate_handler(fn)
- return typing.cast(
- "_ActionHandler[VCreate | VUpdate | VRead | VDelete |
VSearch]",
- _register_handler(self.auth, self.resource, "*", fn),
- )
-
def decorator(
handler: _ActionHandler[VCreate | VUpdate | VRead | VDelete |
VSearch],
) -> _ActionHandler[VCreate | VUpdate | VRead | VDelete | VSearch]:
_validate_handler(handler)
- return typing.cast(
- "_ActionHandler[VCreate | VUpdate | VRead | VDelete |
VSearch]",
- _register_handler(self.auth, self.resource, "*", handler),
+ if resources is None:
+ resource_list = [self.resource]
+ elif isinstance(resources, str):
+ resource_list = [resources]
+ elif isinstance(resources, Sequence):
+ resource_list = list(resources)
+ else:
+ raise TypeError("resources must be a string or sequence of
strings")
+ if resource_list != [self.resource]:
+ raise ValueError(
+ f"Resource-specific decorator for {self.resource!r} cannot
"
+ f"register handlers for {resource_list!r}. Use
@auth.on(...) "
+ "for other or multiple resources."
+ )
+ if actions is None:
+ action_list = ["*"]
+ elif isinstance(actions, str):
+ action_list = [actions]
+ elif isinstance(actions, Sequence):
+ action_list = list(actions)
+ else:
+ raise TypeError("actions must be a string or sequence of
strings")
+ if not action_list:
+ raise ValueError("actions must not be empty")
+ if not all(isinstance(action, str) for action in action_list):
+ raise TypeError("actions must be a string or sequence of
strings")
+ valid_actions = {
+ value.action
+ for value in vars(self).values()
+ if isinstance(value, _ResourceActionOn)
+ }
+ invalid_actions = (
+ sorted(set(action_list) - valid_actions) if actions is not
None else []
)
+ if invalid_actions:
+ raise ValueError(
+ f"Invalid action(s) for {self.resource}: {',
'.join(invalid_actions)}"
+ )
+ if len(action_list) != len(set(action_list)):
+ raise ValueError("actions must not contain duplicates")
+ for action in action_list:
+ if (self.resource, action) in self.auth._handlers:
+ raise ValueError(
+ f"types.Handler already set for {self.resource},
{action}."
+ )
+ for action in action_list:
+ _register_handler(self.auth, self.resource, action, handler)
+ return handler
- # Accept keyword-only parameters for future filtering behavior;
referenced to satisfy linters.
- _ = resources, actions
+ if fn is not None:
+ return decorator(
+ typing.cast(
+ "_ActionHandler[VCreate | VUpdate | VRead | VDelete |
VSearch]",
+ fn,
+ )
+ )
return decorator
@@ -444,6 +492,7 @@
types.AssistantsUpdate,
types.AssistantsDelete,
types.AssistantsSearch,
+ _ResourceAction,
]
):
value = (
@@ -467,6 +516,7 @@
types.ThreadsUpdate,
types.ThreadsDelete,
types.ThreadsSearch,
+ _ThreadAction,
]
):
value = (
@@ -502,6 +552,7 @@
types.CronsUpdate,
types.CronsDelete,
types.CronsSearch,
+ _ResourceAction,
]
):
value = type[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langgraph_sdk-0.4.3/tests/streaming/test_sync_thread_stream.py
new/langgraph_sdk-0.4.4/tests/streaming/test_sync_thread_stream.py
--- old/langgraph_sdk-0.4.3/tests/streaming/test_sync_thread_stream.py
2020-02-02 01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/tests/streaming/test_sync_thread_stream.py
2020-02-02 01:00:00.000000000 +0100
@@ -426,11 +426,17 @@
with httpx.Client(transport=fake.transport, base_url="http://test") as raw:
threads = SyncThreadsClient(SyncHttpClient(raw))
with threads.stream(thread_id="t-1", assistant_id="agent") as thread:
- result = thread.run.start(input={"x": 1})
+ result = thread.run.start(
+ input={"x": 1},
+ langsmith_tracing={"project_name": "replica-project"},
+ )
assert result == {"run_id": "run-1"}
assert fake.received_commands[0]["method"] == "run.start"
assert fake.received_commands[0]["params"]["assistant_id"] == "agent"
+ assert fake.received_commands[0]["params"]["langsmith_tracer"] == {
+ "project_name": "replica-project"
+ }
def test_sync_events_iterates_raw_events():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/langgraph_sdk-0.4.3/tests/streaming/test_thread_stream.py
new/langgraph_sdk-0.4.4/tests/streaming/test_thread_stream.py
--- old/langgraph_sdk-0.4.3/tests/streaming/test_thread_stream.py
2020-02-02 01:00:00.000000000 +0100
+++ new/langgraph_sdk-0.4.4/tests/streaming/test_thread_stream.py
2020-02-02 01:00:00.000000000 +0100
@@ -287,7 +287,7 @@
assert [c["id"] for c in fake.received_commands] == [1, 2]
-async def test_run_start_forwards_config_and_metadata():
+async def test_run_start_forwards_config_metadata_and_langsmith_tracing():
fake = FakeServer()
transport = httpx.ASGITransport(app=fake.app)
async with httpx.AsyncClient(transport=transport, base_url="http://test")
as raw:
@@ -297,10 +297,18 @@
input={"x": 1},
config={"recursion_limit": 5},
metadata={"trace": "abc"},
+ langsmith_tracing={
+ "project_name": "replica-project",
+ "example_id": "example-1",
+ },
)
params = fake.received_commands[0]["params"]
assert params["config"] == {"recursion_limit": 5}
assert params["metadata"] == {"trace": "abc"}
+ assert params["langsmith_tracer"] == {
+ "project_name": "replica-project",
+ "example_id": "example-1",
+ }
async def test_run_start_raises_outside_context_manager():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/langgraph_sdk-0.4.3/tests/test_auth.py
new/langgraph_sdk-0.4.4/tests/test_auth.py
--- old/langgraph_sdk-0.4.3/tests/test_auth.py 1970-01-01 01:00:00.000000000
+0100
+++ new/langgraph_sdk-0.4.4/tests/test_auth.py 2020-02-02 01:00:00.000000000
+0100
@@ -0,0 +1,132 @@
+import pytest
+
+from langgraph_sdk import Auth
+
+
+def test_handler_multiple_resources_and_actions() -> None:
+ auth = Auth()
+
+ @auth.on(resources=["threads", "assistants"], actions=["read", "search"])
+ async def allow_reads(ctx, value):
+ del value
+ return {"owner": ctx.user.identity}
+
+ assert auth._handlers == {
+ ("threads", "read"): [allow_reads],
+ ("threads", "search"): [allow_reads],
+ ("assistants", "read"): [allow_reads],
+ ("assistants", "search"): [allow_reads],
+ }
+
+
+def test_resource_handler_actions_are_scoped() -> None:
+ auth = Auth()
+
+ @auth.on
+ async def deny_all(ctx, value):
+ del ctx, value
+ return False
+
+ @auth.on.threads(actions=["create", "search"])
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ @auth.on.threads(actions="create_run")
+ async def run_handler(ctx, value):
+ del ctx, value
+ return None
+
+ assert auth._handlers == {
+ ("threads", "create"): [handler],
+ ("threads", "search"): [handler],
+ ("threads", "create_run"): [run_handler],
+ }
+ assert auth._global_handlers == [deny_all]
+
+
+def test_resource_handler_preserves_wildcard() -> None:
+ auth = Auth()
+
+ @auth.on.threads
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ assert auth._handlers == {("threads", "*"): [handler]}
+
+
+def test_resource_handler_preserves_wildcard_with_parentheses() -> None:
+ auth = Auth()
+
+ @auth.on.threads()
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ assert auth._handlers == {("threads", "*"): [handler]}
+
+
+def test_resource_handler_accepts_matching_resource() -> None:
+ auth = Auth()
+
+ @auth.on.threads(resources=["threads"], actions="read")
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ assert auth._handlers == {("threads", "read"): [handler]}
+
+
[email protected](
+ "resources", [["assistants"], ["threads", "assistants"], [], [1]]
+)
+def test_resource_handler_rejects_nonmatching_resources(resources) -> None:
+ auth = Auth()
+
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ with pytest.raises(ValueError, match=r"Use @auth\.on"):
+ auth.on.threads(resources=resources)(handler)
+ assert auth._handlers == {}
+
+
[email protected](
+ ("resource", "actions", "error"),
+ [
+ ("threads", [], ValueError),
+ ("threads", ["reed"], ValueError),
+ ("threads", ["create", "create"], ValueError),
+ ("threads", {"create": True}, TypeError),
+ ("crons", ["create_run"], ValueError),
+ ],
+)
+def test_resource_handler_rejects_invalid_actions(resource, actions, error) ->
None:
+ auth = Auth()
+
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ with pytest.raises(error):
+ getattr(auth.on, resource)(actions=actions)(handler)
+ assert auth._handlers == {}
+
+
+def test_resource_handler_registration_is_atomic() -> None:
+ auth = Auth()
+
+ @auth.on.threads.read
+ async def read_handler(ctx, value):
+ del ctx, value
+ return None
+
+ async def handler(ctx, value):
+ del ctx, value
+ return None
+
+ with pytest.raises(ValueError, match="already set"):
+ auth.on.threads(actions=["create", "read"])(handler)
+ assert auth._handlers == {("threads", "read"): [read_handler]}