langgraph-sdk 0.4.4__tar.gz → 0.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/.gitignore +1 -0
  2. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/PKG-INFO +1 -1
  3. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/__init__.py +1 -1
  4. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/stream.py +3 -2
  5. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/stream.py +3 -2
  6. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/__init__.py +1 -1
  7. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/schema.py +2 -0
  8. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/pyproject.toml +1 -1
  9. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_extensions_projection.py +4 -1
  10. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_thread_stream.py +2 -2
  11. langgraph_sdk-0.4.6/tests/streaming/test_thread_stream_path_encoding.py +92 -0
  12. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/uv.lock +375 -315
  13. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/CHANGELOG.md +0 -0
  14. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/LICENSE +0 -0
  15. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/MIGRATION.md +0 -0
  16. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/Makefile +0 -0
  17. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/README.md +0 -0
  18. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/Dockerfile +0 -0
  19. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/docker-compose.yml +0 -0
  20. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/__init__.py +0 -0
  21. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/deep_agent.py +0 -0
  22. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/factory_graph.py +0 -0
  23. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/streaming_graph.py +0 -0
  24. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/tools_agent.py +0 -0
  25. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/langgraph.json +0 -0
  26. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/_common.py +0 -0
  27. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_cancel.py +0 -0
  28. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_concurrent.py +0 -0
  29. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_extensions.py +0 -0
  30. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_helpers.py +0 -0
  31. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_lifecycle.py +0 -0
  32. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_messages.py +0 -0
  33. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_reconnect.py +0 -0
  34. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_subgraphs.py +0 -0
  35. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_tools.py +0 -0
  36. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_update_state.py +0 -0
  37. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_values.py +0 -0
  38. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_websocket.py +0 -0
  39. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/__init__.py +0 -0
  40. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/assistants.py +0 -0
  41. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/client.py +0 -0
  42. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/cron.py +0 -0
  43. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/http.py +0 -0
  44. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/runs.py +0 -0
  45. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/store.py +0 -0
  46. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/threads.py +0 -0
  47. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/__init__.py +0 -0
  48. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/types.py +0 -0
  49. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/utilities.py +0 -0
  50. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/__init__.py +0 -0
  51. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/assistants.py +0 -0
  52. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/client.py +0 -0
  53. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/cron.py +0 -0
  54. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/http.py +0 -0
  55. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/runs.py +0 -0
  56. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/store.py +0 -0
  57. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/threads.py +0 -0
  58. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/exceptions.py +0 -0
  59. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/types.py +0 -0
  60. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/cache.py +0 -0
  61. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/client.py +0 -0
  62. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/encryption/__init__.py +0 -0
  63. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/encryption/types.py +0 -0
  64. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/errors.py +0 -0
  65. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/py.typed +0 -0
  66. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/runtime.py +0 -0
  67. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/sse.py +0 -0
  68. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/__init__.py +0 -0
  69. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/controller.py +0 -0
  70. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/decoders.py +0 -0
  71. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/multi_cursor_buffer.py +0 -0
  72. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/subscription.py +0 -0
  73. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/sync_controller.py +0 -0
  74. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/__init__.py +0 -0
  75. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/base.py +0 -0
  76. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/http.py +0 -0
  77. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/sync_http.py +0 -0
  78. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/sync_ws.py +0 -0
  79. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/ws.py +0 -0
  80. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/fixtures/response.txt +0 -0
  81. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/__init__.py +0 -0
  82. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/conftest.py +0 -0
  83. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_assistants.py +0 -0
  84. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_cancel.py +0 -0
  85. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_concurrent.py +0 -0
  86. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_crons.py +0 -0
  87. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_extensions.py +0 -0
  88. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_factory_graph.py +0 -0
  89. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_helpers.py +0 -0
  90. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_lifecycle.py +0 -0
  91. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_messages.py +0 -0
  92. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_reconnect.py +0 -0
  93. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_remote_graph_v3.py +0 -0
  94. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_runs.py +0 -0
  95. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_store.py +0 -0
  96. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_subgraphs.py +0 -0
  97. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_threads_crud.py +0 -0
  98. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_tools.py +0 -0
  99. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_update_state.py +0 -0
  100. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_values.py +0 -0
  101. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_websocket.py +0 -0
  102. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/__init__.py +0 -0
  103. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_events.py +0 -0
  104. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_fake_server.py +0 -0
  105. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_sync_fake_server.py +0 -0
  106. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/assert_transport_replays.py +0 -0
  107. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_controller.py +0 -0
  108. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_decoders.py +0 -0
  109. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_extensions_projection.py +0 -0
  110. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_lifecycle_watcher.py +0 -0
  111. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_messages_projection.py +0 -0
  112. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_multi_cursor_buffer.py +0 -0
  113. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_output.py +0 -0
  114. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_replay_conformance.py +0 -0
  115. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_scoped_handles.py +0 -0
  116. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_shared_stream.py +0 -0
  117. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_subscription.py +0 -0
  118. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_projections.py +0 -0
  119. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_scoped_handles.py +0 -0
  120. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_shared_stream.py +0 -0
  121. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_transport_http.py +0 -0
  122. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_transport_ws.py +0 -0
  123. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_thread_stream.py +0 -0
  124. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_tool_calls_projection.py +0 -0
  125. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_http.py +0 -0
  126. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_path_encoding.py +0 -0
  127. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_ws.py +0 -0
  128. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_values_projection.py +0 -0
  129. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_api_parity.py +0 -0
  130. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_assistants_client.py +0 -0
  131. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_auth.py +0 -0
  132. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_cache.py +0 -0
  133. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_client_exports.py +0 -0
  134. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_client_stream.py +0 -0
  135. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_crons_client.py +0 -0
  136. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_encryption.py +0 -0
  137. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_errors.py +0 -0
  138. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_langsmith_tracing.py +0 -0
  139. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_path_encoding.py +0 -0
  140. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_serde.py +0 -0
  141. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_serde_schema.py +0 -0
  142. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_skip_auto_load_api_key.py +0 -0
  143. {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_threads_client.py +0 -0
@@ -76,6 +76,7 @@ __pypackages__/
76
76
 
77
77
  # Environments
78
78
  .env
79
+ .env.*
79
80
  .envrc
80
81
  *.crt
81
82
  *.key
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: langgraph-sdk
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: SDK for interacting with LangGraph API
5
5
  Project-URL: Source, https://github.com/langchain-ai/langgraph/tree/main/libs/sdk-py
6
6
  Project-URL: Twitter, https://x.com/langchain_oss
@@ -3,7 +3,7 @@ from langgraph_sdk.client import get_client, get_sync_client
3
3
  from langgraph_sdk.encryption import Encryption
4
4
  from langgraph_sdk.encryption.types import DecryptResult, EncryptionContext
5
5
 
6
- __version__ = "0.4.4"
6
+ __version__ = "0.4.6"
7
7
 
8
8
  __all__ = [
9
9
  "Auth",
@@ -24,6 +24,7 @@ from langchain_core.language_models.chat_model_stream import AsyncChatModelStrea
24
24
  from langchain_protocol import Event, SubscribeParams
25
25
 
26
26
  from langgraph_sdk._async.http import HttpClient
27
+ from langgraph_sdk._shared.utilities import _quote_path_param
27
28
  from langgraph_sdk.schema import LangSmithTracing, QueryParamTypes
28
29
  from langgraph_sdk.stream.controller import _SeenEventIds
29
30
  from langgraph_sdk.stream.decoders import (
@@ -151,7 +152,7 @@ class _AgentModule:
151
152
  query_params.update(dict(params))
152
153
  request_headers = {**self._owner._headers, **dict(headers or {})}
153
154
  return await self._owner._http.get(
154
- f"/assistants/{self._owner.assistant_id}/graph",
155
+ f"/assistants/{_quote_path_param(self._owner.assistant_id)}/graph",
155
156
  params=query_params,
156
157
  headers=request_headers or None,
157
158
  )
@@ -1902,7 +1903,7 @@ class AsyncThreadStream:
1902
1903
  async def _fetch_state(self) -> dict[str, Any]:
1903
1904
  """Fetch the current thread state from the REST endpoint."""
1904
1905
  return await self._http.get(
1905
- f"/threads/{self.thread_id}/state",
1906
+ f"/threads/{_quote_path_param(self.thread_id)}/state",
1906
1907
  headers=self._headers or None,
1907
1908
  )
1908
1909
 
@@ -22,6 +22,7 @@ from typing import Any, Literal, TypedDict, cast
22
22
  from langchain_core.language_models.chat_model_stream import ChatModelStream
23
23
  from langchain_protocol import Event, SubscribeParams
24
24
 
25
+ from langgraph_sdk._shared.utilities import _quote_path_param
25
26
  from langgraph_sdk._sync.http import SyncHttpClient
26
27
  from langgraph_sdk.schema import LangSmithTracing, QueryParamTypes
27
28
  from langgraph_sdk.stream.decoders import (
@@ -194,7 +195,7 @@ class _SyncAgentModule:
194
195
  query_params.update(dict(params))
195
196
  request_headers = {**self._owner._headers, **dict(headers or {})}
196
197
  return self._owner._http.get(
197
- f"/assistants/{self._owner.assistant_id}/graph",
198
+ f"/assistants/{_quote_path_param(self._owner.assistant_id)}/graph",
198
199
  params=query_params,
199
200
  headers=request_headers or None,
200
201
  )
@@ -1552,7 +1553,7 @@ class SyncThreadStream:
1552
1553
  def _fetch_state(self) -> dict[str, Any]:
1553
1554
  """Fetch the current thread state from the REST endpoint."""
1554
1555
  return self._http.get(
1555
- f"/threads/{self.thread_id}/state",
1556
+ f"/threads/{_quote_path_param(self.thread_id)}/state",
1556
1557
  headers=self._headers or None,
1557
1558
  )
1558
1559
 
@@ -312,7 +312,7 @@ class _ActionHandler(typing.Protocol[V]):
312
312
  ) -> types.HandlerResult: ...
313
313
 
314
314
 
315
- T = typing.TypeVar("T", covariant=True)
315
+ T = typing.TypeVar("T")
316
316
 
317
317
 
318
318
  class _ResourceActionOn(typing.Generic[T]):
@@ -295,6 +295,8 @@ class Interrupt(TypedDict):
295
295
  """The value associated with the interrupt."""
296
296
  id: str
297
297
  """The ID of the interrupt. Can be used to resume the interrupt."""
298
+ response_schema: NotRequired[dict[str, Any]]
299
+ """JSON Schema for the value expected when resuming this interrupt, if the graph provided one."""
298
300
 
299
301
 
300
302
  class Thread(TypedDict):
@@ -36,7 +36,7 @@ test = [
36
36
  "pytest-watch",
37
37
  ]
38
38
  lint = [
39
- "ruff==0.16.1",
39
+ "ruff==0.16.9",
40
40
  "codespell",
41
41
  "ty",
42
42
  "starlette",
@@ -29,7 +29,10 @@ def test_sync_extension_projection_yields_matching_custom_payloads():
29
29
  {"name": "progress", "step": 1},
30
30
  {"name": "progress", "step": 2},
31
31
  ]
32
- assert "custom:progress" in fake.stream_request_bodies[-1]["channels"]
32
+ assert any(
33
+ "custom:progress" in body.get("channels", [])
34
+ for body in fake.stream_request_bodies
35
+ )
33
36
 
34
37
 
35
38
  def test_sync_extension_projection_supports_namespace_scope_on_subgraph_handle():
@@ -590,10 +590,10 @@ def test_v3_streaming_sync_surface_smoke():
590
590
  assert results["values"] == fake.state["values"]
591
591
  messages_result = results["messages"]
592
592
  assert isinstance(messages_result, list)
593
- assert [str(m.text) for m in messages_result] == ["hi"] # ty: ignore[unresolved-attribute]
593
+ assert [str(m.text) for m in messages_result] == ["hi"]
594
594
  tools_result = results["tools"]
595
595
  assert isinstance(tools_result, list)
596
- assert tools_result[0].name == "search" # ty: ignore[unresolved-attribute]
596
+ assert tools_result[0].name == "search"
597
597
  assert results["progress"] == [{"name": "progress", "step": 1}]
598
598
  assert final == {"final": True}
599
599
 
@@ -0,0 +1,92 @@
1
+ """Thread stream REST helpers must percent-encode `thread_id` and `assistant_id`
2
+ so a value containing reserved characters or dot-segments stays an opaque path
3
+ segment instead of being normalized into a different path by the HTTP stack.
4
+
5
+ Each test drives a public entry point (`threads.stream(...)` followed by
6
+ `thread.output` or `thread.agent.get_tree()`) with an identifier containing
7
+ `..`, `/`, and `#`, and asserts the request path keeps it as one segment.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import httpx
13
+
14
+ from langgraph_sdk._async.http import HttpClient
15
+ from langgraph_sdk._async.threads import ThreadsClient
16
+ from langgraph_sdk._sync.http import SyncHttpClient
17
+ from langgraph_sdk._sync.threads import SyncThreadsClient
18
+
19
+ # Interpolated raw, httpx would collapse the `..` segments and treat `#` as a
20
+ # fragment, producing `/assistants/a/graph` and `/other/path`.
21
+ THREAD_ID = "../assistants/a/graph#"
22
+ ASSISTANT_ID = "../../other/path#"
23
+ ENCODED_STATE_PATH = "/threads/..%2Fassistants%2Fa%2Fgraph%23/state"
24
+ ENCODED_GRAPH_PATH = "/assistants/..%2F..%2Fother%2Fpath%23/graph"
25
+
26
+ TERMINAL_STATE = {"values": {"ok": True}, "next": [], "tasks": []}
27
+
28
+
29
+ def _recording_handler(gets: list[str]):
30
+ def handler(request: httpx.Request) -> httpx.Response:
31
+ if request.method == "GET":
32
+ path = request.url.raw_path.decode("ascii").split("?", 1)[0]
33
+ gets.append(path)
34
+ return httpx.Response(200, json=TERMINAL_STATE)
35
+ # Lifecycle watcher subscription: an empty event stream.
36
+ return httpx.Response(
37
+ 200, headers={"content-type": "text/event-stream"}, content=b""
38
+ )
39
+
40
+ return handler
41
+
42
+
43
+ async def test_async_output_encodes_thread_id():
44
+ gets: list[str] = []
45
+ async with httpx.AsyncClient(
46
+ transport=httpx.MockTransport(_recording_handler(gets)),
47
+ base_url="https://example.com",
48
+ ) as raw:
49
+ threads = ThreadsClient(HttpClient(raw))
50
+ async with threads.stream(thread_id=THREAD_ID, assistant_id="agent") as thread:
51
+ assert await thread.output == {"ok": True}
52
+
53
+ assert gets == [ENCODED_STATE_PATH]
54
+
55
+
56
+ async def test_async_agent_get_tree_encodes_assistant_id():
57
+ gets: list[str] = []
58
+ async with httpx.AsyncClient(
59
+ transport=httpx.MockTransport(_recording_handler(gets)),
60
+ base_url="https://example.com",
61
+ ) as raw:
62
+ threads = ThreadsClient(HttpClient(raw))
63
+ async with threads.stream(thread_id="t-1", assistant_id=ASSISTANT_ID) as thread:
64
+ await thread.agent.get_tree()
65
+
66
+ assert gets == [ENCODED_GRAPH_PATH]
67
+
68
+
69
+ def test_sync_output_encodes_thread_id():
70
+ gets: list[str] = []
71
+ with httpx.Client(
72
+ transport=httpx.MockTransport(_recording_handler(gets)),
73
+ base_url="https://example.com",
74
+ ) as raw:
75
+ threads = SyncThreadsClient(SyncHttpClient(raw))
76
+ with threads.stream(thread_id=THREAD_ID, assistant_id="agent") as thread:
77
+ assert thread.output == {"ok": True}
78
+
79
+ assert gets == [ENCODED_STATE_PATH]
80
+
81
+
82
+ def test_sync_agent_get_tree_encodes_assistant_id():
83
+ gets: list[str] = []
84
+ with httpx.Client(
85
+ transport=httpx.MockTransport(_recording_handler(gets)),
86
+ base_url="https://example.com",
87
+ ) as raw:
88
+ threads = SyncThreadsClient(SyncHttpClient(raw))
89
+ with threads.stream(thread_id="t-1", assistant_id=ASSISTANT_ID) as thread:
90
+ thread.agent.get_tree()
91
+
92
+ assert gets == [ENCODED_GRAPH_PATH]