mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-24 00:22:25 +02:00
Bumps the minor-and-patch group with 1 update in the /libs/sdk-py directory: [ty](https://github.com/astral-sh/ty). Updates `ty` from 0.0.23 to 0.0.33 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ty/releases">ty's releases</a>.</em></p> <blockquote> <h2>0.0.33</h2> <h2>Release Notes</h2> <p>Released on 2026-04-28.</p> <h3>Notable changes</h3> <ul> <li> <p>ty now prefers the declared type of an annotated assignment in more situations (<a href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>). Consider this example:</p> <pre lang="py"><code>from some_library import untyped_function <p>threshold: int | None = 0 result: str = untyped_function() </code></pre></p> <p>ty previously favored the <em>inferred</em> type of the right hand side expression when <code>threshold</code> and <code>result</code> were used. This is useful for <code>threshold</code>, as it allows something like <code>threshold += 1</code> to work without an error: we know that <code>threshold</code> could later become <code>None</code>, but <em>right now</em>, we see that it is an <code>int</code>. However, for <code>result</code>, the inferred type is <code>Unknown</code>. This is <em>not</em> a useful type and it can lead to false negatives. Starting with this release, ty will therefore prefer the declared type <em>if the inferred and declared types are mutually assignable</em>. In the above example, <code>threshold</code> will still be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but <code>result</code> will now be inferred as <code>str</code>. If you previously added <code>cast</code>s to work around this behavior, you should be able to remove them after upgrading.</p> </li> </ul> <h3>Bug fixes</h3> <ul> <li>Fix reporting of annotation-only locals as unused (<a href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li> <li>Fix project and workspace selection (<a href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li> <li>Fix go-to definition for generic classes (<a href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li> <li>Fix receiver coloring for aliased decorators (<a href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li> </ul> <h3>LSP server</h3> <ul> <li>Add support for go-to definition in literal enum member inlay hints (<a href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li> <li>Add support for "baking" keyword argument inlay hints into the source code (<a href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li> <li>Don't allow inlay hint edits when introducing a non global scope symbol (<a href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li> <li>Omit semantic highlighting for unresolved symbols (<a href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li> </ul> <h3>Core type checking</h3> <ul> <li>Support narrowing with aliased conditional expressions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li> <li>Model short-circuiting control flow in Boolean expressions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li> <li>Handle <code>finally</code> blocks where all <code>try</code>/<code>except</code> blocks are terminal (<a href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li> <li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides (<a href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li> <li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li> <li>Infer lambda parameter types with <code>Callable</code> type context (<a href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li> <li>Support <code>**</code> unpacking of <code>TypedDict</code> in dict-literal assignments (<a href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li> <li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code> signatures (<a href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li> <li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on <code>Sequence</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li> <li>Improve generics solving for unions in invariant positions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li> <li>Improve generics solving for unions when matching against protocols (<a href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li> </ul> <h3>Diagnostics</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/ty/blob/main/CHANGELOG.md">ty's changelog</a>.</em></p> <blockquote> <h2>0.0.33</h2> <p>Released on 2026-04-28.</p> <h3>Notable changes</h3> <ul> <li> <p>ty now prefers the declared type of an annotated assignment in more situations (<a href="https://redirect.github.com/astral-sh/ruff/pull/24802">#24802</a>). Consider this example:</p> <pre lang="py"><code>from some_library import untyped_function <p>threshold: int | None = 0 result: str = untyped_function() </code></pre></p> <p>ty previously favored the <em>inferred</em> type of the right hand side expression when <code>threshold</code> and <code>result</code> were used. This is useful for <code>threshold</code>, as it allows something like <code>threshold += 1</code> to work without an error: we know that <code>threshold</code> could later become <code>None</code>, but <em>right now</em>, we see that it is an <code>int</code>. However, for <code>result</code>, the inferred type is <code>Unknown</code>. This is <em>not</em> a useful type and it can lead to false negatives. Starting with this release, ty will therefore prefer the declared type <em>if the inferred and declared types are mutually assignable</em>. In the above example, <code>threshold</code> will still be inferred as <code>int</code> (or rather <code>Literal[1]</code>), but <code>result</code> will now be inferred as <code>str</code>. If you previously added <code>cast</code>s to work around this behavior, you should be able to remove them after upgrading.</p> </li> </ul> <h3>Bug fixes</h3> <ul> <li>Fix reporting of annotation-only locals as unused (<a href="https://redirect.github.com/astral-sh/ruff/pull/24811">#24811</a>)</li> <li>Fix project and workspace selection (<a href="https://redirect.github.com/astral-sh/ruff/pull/24824">#24824</a>)</li> <li>Fix go-to definition for generic classes (<a href="https://redirect.github.com/astral-sh/ruff/pull/24714">#24714</a>)</li> <li>Fix receiver coloring for aliased decorators (<a href="https://redirect.github.com/astral-sh/ruff/pull/24884">#24884</a>)</li> </ul> <h3>LSP server</h3> <ul> <li>Add support for go-to definition in literal enum member inlay hints (<a href="https://redirect.github.com/astral-sh/ruff/pull/24792">#24792</a>)</li> <li>Add support for "baking" keyword argument inlay hints into the source code (<a href="https://redirect.github.com/astral-sh/ruff/pull/24667">#24667</a>)</li> <li>Don't allow inlay hint edits when introducing a non global scope symbol (<a href="https://redirect.github.com/astral-sh/ruff/pull/24797">#24797</a>)</li> <li>Omit semantic highlighting for unresolved symbols (<a href="https://redirect.github.com/astral-sh/ruff/pull/24718">#24718</a>)</li> </ul> <h3>Core type checking</h3> <ul> <li>Support narrowing with aliased conditional expressions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24302">#24302</a>)</li> <li>Model short-circuiting control flow in Boolean expressions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24458">#24458</a>)</li> <li>Handle <code>finally</code> blocks where all <code>try</code>/<code>except</code> blocks are terminal (<a href="https://redirect.github.com/astral-sh/ruff/pull/24882">#24882</a>)</li> <li>Detect invalid <code>ClassVar</code> vs instance-attribute overrides (<a href="https://redirect.github.com/astral-sh/ruff/pull/24767">#24767</a>)</li> <li>Emit diagnostic for invalid uses of <code>Unpack[...]</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/24868">#24868</a>)</li> <li>Infer lambda parameter types with <code>Callable</code> type context (<a href="https://redirect.github.com/astral-sh/ruff/pull/24317">#24317</a>)</li> <li>Support <code>**</code> unpacking of <code>TypedDict</code> in dict-literal assignments (<a href="https://redirect.github.com/astral-sh/ruff/pull/24703">#24703</a>)</li> <li>Support <code>Unpack[TypedDict]</code> in <code>**kwargs</code> signatures (<a href="https://redirect.github.com/astral-sh/ruff/pull/24653">#24653</a>)</li> <li>Treat <code>[*xs]</code> as an irrefutable pattern when matching on <code>Sequence</code> (<a href="https://redirect.github.com/astral-sh/ruff/pull/24787">#24787</a>)</li> <li>Improve generics solving for unions in invariant positions (<a href="https://redirect.github.com/astral-sh/ruff/pull/24698">#24698</a>)</li> <li>Improve generics solving for unions when matching against protocols (<a href="https://redirect.github.com/astral-sh/ruff/pull/24837">#24837</a>)</li> </ul> <h3>Diagnostics</h3> <ul> <li>Add error context to <code>invalid-return-type</code> diagnostics, <code>invalid-yield</code> diagnostics, attribute assignment diagnostics (<a href="https://redirect.github.com/astral-sh/ruff/pull/24770">#24770</a>, <a href="https://redirect.github.com/astral-sh/ruff/pull/24771">#24771</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/astral-sh/ty/commit/c512d8425418a2170e92aa7fbbd70952d4e04118"><code>c512d84</code></a> Bump version to 0.0.33 (<a href="https://redirect.github.com/astral-sh/ty/issues/3368">#3368</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/4cd7b334b90eba09042700e7b654044c2d6bcd15"><code>4cd7b33</code></a> Upgrade Depot runners from macOS 14 to 15 (<a href="https://redirect.github.com/astral-sh/ty/issues/3363">#3363</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/c78b8324515bf15a662f61e1d74fd85484d67e8c"><code>c78b832</code></a> Update rui314/setup-mold digest to 9c9c13b (<a href="https://redirect.github.com/astral-sh/ty/issues/3342">#3342</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/dea338134aef96d741a48886e414f622dbf10426"><code>dea3381</code></a> Update actions/cache action to v5.0.5 (<a href="https://redirect.github.com/astral-sh/ty/issues/3343">#3343</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/d451af477bd5517e132609c3b016799d697f4182"><code>d451af4</code></a> update typing-features and faqs (<a href="https://redirect.github.com/astral-sh/ty/issues/3335">#3335</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/052d70bc1a7457f50c5acbafe8de7a846f7d5e77"><code>052d70b</code></a> Update prek dependencies (<a href="https://redirect.github.com/astral-sh/ty/issues/3344">#3344</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/66b5e878163ce4ab4810a45a2679a98011932b8b"><code>66b5e87</code></a> Update astral-sh/setup-uv action to v8.1.0 (<a href="https://redirect.github.com/astral-sh/ty/issues/3345">#3345</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/7ec6712a6f02d0255de4c7f0066d5bb23987a9bb"><code>7ec6712</code></a> Add a 'Diagnostics improvements' section to the changelogs (<a href="https://redirect.github.com/astral-sh/ty/issues/3309">#3309</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/978dfdb38dfb568943f73779d769a965c1d5a397"><code>978dfdb</code></a> Add version metadata publishing to the release process (<a href="https://redirect.github.com/astral-sh/ty/issues/3292">#3292</a>)</li> <li><a href="https://github.com/astral-sh/ty/commit/4d1e1fc57ca8bfdcbcee513ba92135d2932eb279"><code>4d1e1fc</code></a> Bump version to 0.0.32 (<a href="https://redirect.github.com/astral-sh/ty/issues/3302">#3302</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/ty/compare/0.0.23...0.0.33">compare view</a></li> </ul> </details> <br /> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: John Kennedy <65985482+jkennedyvz@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
232 lines
6.5 KiB
Python
232 lines
6.5 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
from typing import Any, Literal, cast
|
|
|
|
import httpx
|
|
import orjson
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class LangGraphError(Exception):
|
|
pass
|
|
|
|
|
|
class APIError(httpx.HTTPStatusError, LangGraphError):
|
|
message: str
|
|
request: httpx.Request
|
|
|
|
body: object | None
|
|
code: str | None
|
|
param: str | None
|
|
type: str | None
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
response_or_request: httpx.Response | httpx.Request,
|
|
*,
|
|
body: object | None,
|
|
) -> None:
|
|
if isinstance(response_or_request, httpx.Response):
|
|
req = response_or_request.request
|
|
response = response_or_request
|
|
else:
|
|
req = response_or_request
|
|
response = None
|
|
|
|
httpx.HTTPStatusError.__init__(self, message, request=req, response=response) # ty: ignore[invalid-argument-type]
|
|
LangGraphError.__init__(self, message)
|
|
|
|
self.request = req
|
|
self.message = message
|
|
self.body = body
|
|
|
|
if isinstance(body, dict):
|
|
b = cast("dict[str, Any]", body)
|
|
# Best-effort extraction of common fields if present
|
|
code_val = b.get("code")
|
|
self.code = code_val if isinstance(code_val, str) else None
|
|
param_val = b.get("param")
|
|
self.param = param_val if isinstance(param_val, str) else None
|
|
t = b.get("type")
|
|
self.type = t if isinstance(t, str) else None
|
|
else:
|
|
self.code = None
|
|
self.param = None
|
|
self.type = None
|
|
|
|
|
|
class APIResponseValidationError(APIError):
|
|
response: httpx.Response
|
|
status_code: int
|
|
|
|
def __init__(
|
|
self,
|
|
response: httpx.Response,
|
|
body: object | None,
|
|
*,
|
|
message: str | None = None,
|
|
) -> None:
|
|
super().__init__(
|
|
message or "Data returned by API invalid for expected schema.",
|
|
response,
|
|
body=body,
|
|
)
|
|
self.response = response
|
|
self.status_code = response.status_code
|
|
|
|
|
|
class APIStatusError(APIError):
|
|
response: httpx.Response
|
|
status_code: int
|
|
request_id: str | None
|
|
|
|
def __init__(
|
|
self, message: str, *, response: httpx.Response, body: object | None
|
|
) -> None:
|
|
super().__init__(message, response, body=body)
|
|
self.response = response
|
|
self.status_code = response.status_code
|
|
self.request_id = response.headers.get("x-request-id")
|
|
|
|
|
|
class APIConnectionError(APIError):
|
|
def __init__(
|
|
self, *, message: str = "Connection error.", request: httpx.Request
|
|
) -> None:
|
|
super().__init__(message, response_or_request=request, body=None)
|
|
|
|
|
|
class APITimeoutError(APIConnectionError):
|
|
def __init__(self, request: httpx.Request) -> None:
|
|
super().__init__(message="Request timed out.", request=request)
|
|
|
|
|
|
class BadRequestError(APIStatusError):
|
|
status_code: Literal[400] = 400
|
|
|
|
|
|
class AuthenticationError(APIStatusError):
|
|
status_code: Literal[401] = 401
|
|
|
|
|
|
class PermissionDeniedError(APIStatusError):
|
|
status_code: Literal[403] = 403
|
|
|
|
|
|
class NotFoundError(APIStatusError):
|
|
status_code: Literal[404] = 404
|
|
|
|
|
|
class ConflictError(APIStatusError):
|
|
status_code: Literal[409] = 409
|
|
|
|
|
|
class UnprocessableEntityError(APIStatusError):
|
|
status_code: Literal[422] = 422
|
|
|
|
|
|
class RateLimitError(APIStatusError):
|
|
status_code: Literal[429] = 429
|
|
|
|
|
|
class InternalServerError(APIStatusError):
|
|
pass
|
|
|
|
|
|
def _extract_error_message(body: object | None, fallback: str) -> str:
|
|
if isinstance(body, dict):
|
|
b = cast("dict[str, Any]", body)
|
|
for key in ("message", "detail", "error"):
|
|
val = b.get(key)
|
|
if isinstance(val, str) and val:
|
|
return val
|
|
# Sometimes errors are structured like {"error": {"message": "..."}}
|
|
err = b.get("error")
|
|
if isinstance(err, dict):
|
|
e = cast("dict[str, Any]", err)
|
|
for key in ("message", "detail"):
|
|
val = e.get(key)
|
|
if isinstance(val, str) and val:
|
|
return val
|
|
return fallback
|
|
|
|
|
|
async def _adecode_error_body(r: httpx.Response) -> object | None:
|
|
try:
|
|
data = await r.aread()
|
|
except Exception:
|
|
return None
|
|
if not data:
|
|
return None
|
|
try:
|
|
return orjson.loads(data)
|
|
except Exception:
|
|
try:
|
|
return data.decode()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _decode_error_body(r: httpx.Response) -> object | None:
|
|
try:
|
|
data = r.read()
|
|
except Exception:
|
|
return None
|
|
if not data:
|
|
return None
|
|
try:
|
|
return orjson.loads(data)
|
|
except Exception:
|
|
try:
|
|
return data.decode()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _map_status_error(response: httpx.Response, body: object | None) -> APIStatusError:
|
|
status = response.status_code
|
|
reason = response.reason_phrase or "HTTP Error"
|
|
message = _extract_error_message(body, f"{status} {reason}")
|
|
if status == 400:
|
|
return BadRequestError(message, response=response, body=body)
|
|
if status == 401:
|
|
return AuthenticationError(message, response=response, body=body)
|
|
if status == 403:
|
|
return PermissionDeniedError(message, response=response, body=body)
|
|
if status == 404:
|
|
return NotFoundError(message, response=response, body=body)
|
|
if status == 409:
|
|
return ConflictError(message, response=response, body=body)
|
|
if status == 422:
|
|
return UnprocessableEntityError(message, response=response, body=body)
|
|
if status == 429:
|
|
return RateLimitError(message, response=response, body=body)
|
|
if status >= 500:
|
|
return InternalServerError(message, response=response, body=body)
|
|
return APIStatusError(message, response=response, body=body)
|
|
|
|
|
|
async def _araise_for_status_typed(r: httpx.Response) -> None:
|
|
if r.status_code < 400:
|
|
return
|
|
body = await _adecode_error_body(r)
|
|
err = _map_status_error(r, body)
|
|
# Log for older Python versions without Exception notes
|
|
if not (sys.version_info >= (3, 11)):
|
|
logger.error(f"Error from langgraph-api: {getattr(err, 'message', '')}")
|
|
raise err
|
|
|
|
|
|
def _raise_for_status_typed(r: httpx.Response) -> None:
|
|
if r.status_code < 400:
|
|
return
|
|
body = _decode_error_body(r)
|
|
err = _map_status_error(r, body)
|
|
if not (sys.version_info >= (3, 11)):
|
|
logger.error(f"Error from langgraph-api: {getattr(err, 'message', '')}")
|
|
raise err
|