diff --git a/libs/langgraph/pyproject.toml b/libs/langgraph/pyproject.toml index dfe43e118..8aa8a6128 100644 --- a/libs/langgraph/pyproject.toml +++ b/libs/langgraph/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ dependencies = [ "langchain-core>=0.1", "langgraph-checkpoint>=2.1.0,<4.0.0", - "langgraph-sdk>=0.2.2,<0.3.0", + "langgraph-sdk>=0.3.0,<0.4.0", "langgraph-prebuilt>=1.0.2,<1.1.0", "xxhash>=3.5.0", "pydantic>=2.7.4", diff --git a/libs/langgraph/tests/test_remote_graph.py b/libs/langgraph/tests/test_remote_graph.py index 47f25d2e4..e0eacf006 100644 --- a/libs/langgraph/tests/test_remote_graph.py +++ b/libs/langgraph/tests/test_remote_graph.py @@ -567,9 +567,9 @@ def test_stream(): stream_parts.append(stream_part) assert stream_parts == [ - ("updates", {"chunk": "data3"}), - ("updates", {"chunk": "data4"}), - ("updates", {"__interrupt__": ()}), + ("updates", {"chunk": "data3"}, None), + ("updates", {"chunk": "data4"}, None), + ("updates", {"__interrupt__": ()}, None), ] # subgraphs + list modes @@ -739,9 +739,9 @@ async def test_astream(): stream_parts.append(stream_part) assert stream_parts == [ - ("updates", {"chunk": "data3"}), - ("updates", {"chunk": "data4"}), - ("updates", {"__interrupt__": ()}), + ("updates", {"chunk": "data3"}, None), + ("updates", {"chunk": "data4"}, None), + ("updates", {"__interrupt__": ()}, None), ] # subgraphs + list modes diff --git a/libs/sdk-py/langgraph_sdk/__init__.py b/libs/sdk-py/langgraph_sdk/__init__.py index b2ee4712d..33ca6a8cf 100644 --- a/libs/sdk-py/langgraph_sdk/__init__.py +++ b/libs/sdk-py/langgraph_sdk/__init__.py @@ -3,6 +3,6 @@ from langgraph_sdk.client import get_client, get_sync_client from langgraph_sdk.encryption import Encryption from langgraph_sdk.encryption.types import EncryptionContext -__version__ = "0.2.15" +__version__ = "0.3.0" __all__ = ["Auth", "Encryption", "EncryptionContext", "get_client", "get_sync_client"] diff --git a/libs/sdk-py/langgraph_sdk/schema.py b/libs/sdk-py/langgraph_sdk/schema.py index 390adadf0..bee967e2c 100644 --- a/libs/sdk-py/langgraph_sdk/schema.py +++ b/libs/sdk-py/langgraph_sdk/schema.py @@ -167,7 +167,7 @@ class Config(TypedDict, total=False): """ Runtime values for attributes previously made configurable on this Runnable, or sub-Runnables, through .configurable_fields() or .configurable_alternatives(). - Check .output_schema() for a description of the attributes that have been made + Check .output_schema() for a description of the attributes that have been made configurable. """ @@ -301,7 +301,7 @@ class ThreadState(TypedDict): values: list[dict] | dict[str, Any] """The state values.""" next: Sequence[str] - """The next nodes to execute. If empty, the thread is done until new input is + """The next nodes to execute. If empty, the thread is done until new input is received.""" checkpoint: Checkpoint """The ID of the checkpoint.""" @@ -476,7 +476,7 @@ class Item(TypedDict): """The namespace of the item. A namespace is analogous to a document's directory.""" key: str """The unique identifier of the item within its namespace. - + In general, keys needn't be globally unique. """ value: dict[str, Any] @@ -519,6 +519,8 @@ class StreamPart(NamedTuple): """The type of event for this stream part.""" data: dict """The data payload associated with the event.""" + id: str | None = None + """The ID of the event.""" class Send(TypedDict): diff --git a/libs/sdk-py/langgraph_sdk/sse.py b/libs/sdk-py/langgraph_sdk/sse.py index 72e4b5168..16aa0c8cf 100644 --- a/libs/sdk-py/langgraph_sdk/sse.py +++ b/libs/sdk-py/langgraph_sdk/sse.py @@ -103,6 +103,7 @@ class SSEDecoder: sse = StreamPart( event=self._event, data=orjson.loads(self._data) if self._data else None, # type: ignore[invalid-argument-type] + id=self.last_event_id, ) # NOTE: as per the SSE spec, do not reset last_event_id. diff --git a/libs/sdk-py/tests/test_client_stream.py b/libs/sdk-py/tests/test_client_stream.py index b8c012e97..e6b8e8865 100644 --- a/libs/sdk-py/tests/test_client_stream.py +++ b/libs/sdk-py/tests/test_client_stream.py @@ -50,7 +50,7 @@ def iter_lines_raw(payload: list[bytes]) -> Iterator[BytesLike]: yield from decoder.flush() -def test_stream_see(): +def test_stream_sse(): for groups in ( [RESPONSE_PAYLOAD], RESPONSE_PAYLOAD.splitlines(keepends=True), @@ -150,9 +150,9 @@ def test_sync_http_client_stream_recovers_after_disconnect(): assert call_count == 2 assert parts == [ - StreamPart(event="values", data={"step": 1}), - StreamPart(event="values", data={"step": 2}), - StreamPart(event="end", data=None), # ty: ignore + StreamPart(event="values", data={"step": 1}, id="1"), + StreamPart(event="values", data={"step": 2}, id="2"), + StreamPart(event="end", data=None, id="2"), ] @@ -222,9 +222,9 @@ async def test_http_client_stream_recovers_after_disconnect(): assert call_count == 2 assert parts == [ - StreamPart(event="values", data={"step": 1}), - StreamPart(event="values", data={"step": 2}), - StreamPart(event="end", data=None), + StreamPart(event="values", data={"step": 1}, id="1"), + StreamPart(event="values", data={"step": 2}, id="2"), + StreamPart(event="end", data=None, id="2"), ]