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]}

Reply via email to