langgraph-sdk 0.2.8__tar.gz → 0.2.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-sdk
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: SDK for interacting with LangGraph API
5
5
  Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  from langgraph_sdk.auth import Auth
2
2
  from langgraph_sdk.client import get_client, get_sync_client
3
3
 
4
- __version__ = "0.2.8"
4
+ __version__ = "0.2.9"
5
5
 
6
6
  __all__ = ["Auth", "get_client", "get_sync_client"]
@@ -728,7 +728,7 @@ def is_studio_user(
728
728
  return (
729
729
  isinstance(user, types.StudioUser)
730
730
  or isinstance(user, dict)
731
- and user.get("kind") == "StudioUser"
731
+ and user.get("kind") == "StudioUser" # ty: ignore[invalid-argument-type]
732
732
  )
733
733
 
734
734
 
@@ -437,6 +437,54 @@ class HttpClient:
437
437
  logger.error(f"Error from langgraph-api: {body}", exc_info=e)
438
438
  raise e
439
439
 
440
+ async def request_reconnect(
441
+ self,
442
+ path: str,
443
+ method: str,
444
+ *,
445
+ json: dict[str, Any] | None = None,
446
+ params: QueryParamTypes | None = None,
447
+ headers: Mapping[str, str] | None = None,
448
+ on_response: Callable[[httpx.Response], None] | None = None,
449
+ reconnect_limit: int = 5,
450
+ ) -> Any:
451
+ """Send a request that automatically reconnects to Location header."""
452
+ request_headers, content = await _aencode_json(json)
453
+ if headers:
454
+ request_headers.update(headers)
455
+ async with self.client.stream(
456
+ method, path, headers=request_headers, content=content, params=params
457
+ ) as r:
458
+ if on_response:
459
+ on_response(r)
460
+ try:
461
+ r.raise_for_status()
462
+ except httpx.HTTPStatusError as e:
463
+ body = (await r.aread()).decode()
464
+ if sys.version_info >= (3, 11):
465
+ e.add_note(body)
466
+ else:
467
+ logger.error(f"Error from langgraph-api: {body}", exc_info=e)
468
+ raise e
469
+ loc = r.headers.get("location")
470
+ if reconnect_limit <= 0 or not loc:
471
+ return await _adecode_json(r)
472
+ try:
473
+ return await _adecode_json(r)
474
+ except httpx.HTTPError:
475
+ warnings.warn(
476
+ f"Request failed, attempting reconnect to Location: {loc}",
477
+ stacklevel=2,
478
+ )
479
+ await r.aclose()
480
+ return await self.request_reconnect(
481
+ loc,
482
+ "GET",
483
+ headers=request_headers,
484
+ # don't pass on_response so it's only called once
485
+ reconnect_limit=reconnect_limit - 1,
486
+ )
487
+
440
488
  async def stream(
441
489
  self,
442
490
  path: str,
@@ -2533,8 +2581,9 @@ class RunsClient:
2533
2581
  if on_run_created and (metadata := _get_run_metadata_from_response(res)):
2534
2582
  on_run_created(metadata)
2535
2583
 
2536
- response = await self.http.post(
2584
+ response = await self.http.request_reconnect(
2537
2585
  endpoint,
2586
+ "POST",
2538
2587
  json={k: v for k, v in payload.items() if v is not None},
2539
2588
  params=params,
2540
2589
  headers=headers,
@@ -2679,12 +2728,20 @@ class RunsClient:
2679
2728
  }
2680
2729
  if params:
2681
2730
  query_params.update(params)
2682
- return await self.http.post(
2683
- f"/threads/{thread_id}/runs/{run_id}/cancel",
2684
- json=None,
2685
- params=query_params,
2686
- headers=headers,
2687
- )
2731
+ if wait:
2732
+ return await self.http.request_reconnect(
2733
+ f"/threads/{thread_id}/runs/{run_id}/cancel",
2734
+ "POST",
2735
+ params=query_params,
2736
+ headers=headers,
2737
+ )
2738
+ else:
2739
+ return await self.http.post(
2740
+ f"/threads/{thread_id}/runs/{run_id}/cancel",
2741
+ json=None,
2742
+ params=query_params,
2743
+ headers=headers,
2744
+ )
2688
2745
 
2689
2746
  async def join(
2690
2747
  self,
@@ -2716,8 +2773,11 @@ class RunsClient:
2716
2773
  ```
2717
2774
 
2718
2775
  """ # noqa: E501
2719
- return await self.http.get(
2720
- f"/threads/{thread_id}/runs/{run_id}/join", headers=headers, params=params
2776
+ return await self.http.request_reconnect(
2777
+ f"/threads/{thread_id}/runs/{run_id}/join",
2778
+ "GET",
2779
+ headers=headers,
2780
+ params=params,
2721
2781
  )
2722
2782
 
2723
2783
  def join_stream(
@@ -3689,6 +3749,54 @@ class SyncHttpClient:
3689
3749
  logger.error(f"Error from langgraph-api: {body}", exc_info=e)
3690
3750
  raise e
3691
3751
 
3752
+ def request_reconnect(
3753
+ self,
3754
+ path: str,
3755
+ method: str,
3756
+ *,
3757
+ json: dict[str, Any] | None = None,
3758
+ params: QueryParamTypes | None = None,
3759
+ headers: Mapping[str, str] | None = None,
3760
+ on_response: Callable[[httpx.Response], None] | None = None,
3761
+ reconnect_limit: int = 5,
3762
+ ) -> Any:
3763
+ """Send a request that automatically reconnects to Location header."""
3764
+ request_headers, content = _encode_json(json)
3765
+ if headers:
3766
+ request_headers.update(headers)
3767
+ with self.client.stream(
3768
+ method, path, headers=request_headers, content=content, params=params
3769
+ ) as r:
3770
+ if on_response:
3771
+ on_response(r)
3772
+ try:
3773
+ r.raise_for_status()
3774
+ except httpx.HTTPStatusError as e:
3775
+ body = r.read().decode()
3776
+ if sys.version_info >= (3, 11):
3777
+ e.add_note(body)
3778
+ else:
3779
+ logger.error(f"Error from langgraph-api: {body}", exc_info=e)
3780
+ raise e
3781
+ loc = r.headers.get("location")
3782
+ if reconnect_limit <= 0 or not loc:
3783
+ return _decode_json(r)
3784
+ try:
3785
+ return _decode_json(r)
3786
+ except httpx.HTTPError:
3787
+ warnings.warn(
3788
+ f"Request failed, attempting reconnect to Location: {loc}",
3789
+ stacklevel=2,
3790
+ )
3791
+ r.close()
3792
+ return self.request_reconnect(
3793
+ loc,
3794
+ "GET",
3795
+ headers=request_headers,
3796
+ # don't pass on_response so it's only called once
3797
+ reconnect_limit=reconnect_limit - 1,
3798
+ )
3799
+
3692
3800
  def stream(
3693
3801
  self,
3694
3802
  path: str,
@@ -5754,8 +5862,9 @@ class SyncRunsClient:
5754
5862
  endpoint = (
5755
5863
  f"/threads/{thread_id}/runs/wait" if thread_id is not None else "/runs/wait"
5756
5864
  )
5757
- return self.http.post(
5865
+ return self.http.request_reconnect(
5758
5866
  endpoint,
5867
+ "POST",
5759
5868
  json={k: v for k, v in payload.items() if v is not None},
5760
5869
  params=params,
5761
5870
  headers=headers,
@@ -5878,11 +5987,25 @@ class SyncRunsClient:
5878
5987
  ```
5879
5988
 
5880
5989
  """ # noqa: E501
5990
+ query_params = {
5991
+ "wait": 1 if wait else 0,
5992
+ "action": action,
5993
+ }
5994
+ if params:
5995
+ query_params.update(params)
5996
+ if wait:
5997
+ return self.http.request_reconnect(
5998
+ f"/threads/{thread_id}/runs/{run_id}/cancel",
5999
+ "POST",
6000
+ json=None,
6001
+ params=query_params,
6002
+ headers=headers,
6003
+ )
5881
6004
  return self.http.post(
5882
- f"/threads/{thread_id}/runs/{run_id}/cancel?wait={1 if wait else 0}&action={action}",
6005
+ f"/threads/{thread_id}/runs/{run_id}/cancel",
5883
6006
  json=None,
6007
+ params=query_params,
5884
6008
  headers=headers,
5885
- params=params,
5886
6009
  )
5887
6010
 
5888
6011
  def join(
@@ -5915,8 +6038,11 @@ class SyncRunsClient:
5915
6038
  ```
5916
6039
 
5917
6040
  """ # noqa: E501
5918
- return self.http.get(
5919
- f"/threads/{thread_id}/runs/{run_id}/join", headers=headers, params=params
6041
+ return self.http.request_reconnect(
6042
+ f"/threads/{thread_id}/runs/{run_id}/join",
6043
+ "GET",
6044
+ headers=headers,
6045
+ params=params,
5920
6046
  )
5921
6047
 
5922
6048
  def join_stream(
File without changes
File without changes
File without changes
File without changes
File without changes