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.
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/.gitignore +1 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/PKG-INFO +1 -1
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/__init__.py +1 -1
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/stream.py +3 -2
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/stream.py +3 -2
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/__init__.py +1 -1
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/schema.py +2 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/pyproject.toml +1 -1
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_extensions_projection.py +4 -1
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_thread_stream.py +2 -2
- langgraph_sdk-0.4.6/tests/streaming/test_thread_stream_path_encoding.py +92 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/uv.lock +375 -315
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/CHANGELOG.md +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/LICENSE +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/MIGRATION.md +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/Makefile +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/README.md +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/Dockerfile +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/docker-compose.yml +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/deep_agent.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/factory_graph.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/streaming_graph.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/graph/tools_agent.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/langgraph.json +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/_common.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_cancel.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_concurrent.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_extensions.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_helpers.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_lifecycle.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_messages.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_reconnect.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_subgraphs.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_tools.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_update_state.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_values.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/integration/scripts/test_websocket.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/assistants.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/client.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/cron.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/runs.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/store.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_async/threads.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/types.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_shared/utilities.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/assistants.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/client.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/cron.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/runs.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/store.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/_sync/threads.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/exceptions.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/auth/types.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/cache.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/client.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/encryption/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/encryption/types.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/errors.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/py.typed +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/runtime.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/sse.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/controller.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/decoders.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/multi_cursor_buffer.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/subscription.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/sync_controller.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/base.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/sync_http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/sync_ws.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/langgraph_sdk/stream/transport/ws.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/fixtures/response.txt +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/conftest.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_assistants.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_cancel.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_concurrent.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_crons.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_extensions.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_factory_graph.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_helpers.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_lifecycle.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_messages.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_reconnect.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_remote_graph_v3.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_runs.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_store.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_subgraphs.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_threads_crud.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_tools.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_update_state.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_values.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/integration/test_websocket.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/__init__.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_events.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_fake_server.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/_sync_fake_server.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/assert_transport_replays.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_controller.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_decoders.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_extensions_projection.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_lifecycle_watcher.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_messages_projection.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_multi_cursor_buffer.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_output.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_replay_conformance.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_scoped_handles.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_shared_stream.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_subscription.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_projections.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_scoped_handles.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_shared_stream.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_transport_http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_transport_ws.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_thread_stream.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_tool_calls_projection.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_http.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_path_encoding.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_transport_ws.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_values_projection.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_api_parity.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_assistants_client.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_auth.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_cache.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_client_exports.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_client_stream.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_crons_client.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_encryption.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_errors.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_langsmith_tracing.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_path_encoding.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_serde.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_serde_schema.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_skip_auto_load_api_key.py +0 -0
- {langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/test_threads_client.py +0 -0
|
@@ -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
|
|
|
@@ -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):
|
{langgraph_sdk-0.4.4 → langgraph_sdk-0.4.6}/tests/streaming/test_sync_extensions_projection.py
RENAMED
|
@@ -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
|
|
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"]
|
|
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"
|
|
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]
|