feat(core): propagate gateway information on error path (#39829)

This PR plumbs through gateway metadata information for exceptions

<img width="2380" height="956" alt="image"
src="https://github.com/user-attachments/assets/c3b3644e-3022-421c-b447-6ab6603021e4"
/>
This commit is contained in:
Eugene Yurtsev authored and GitHub committed 2026-08-21 14:33:30 -04:00
1 parent d8237e7492
commit ed0ad742e9
2 files changed
+30 -3

No files matched your search

@@ -87,7 +87,10 @@ from langchain_core.tracers._streaming import (
_StreamingCallbackHandler,
_V2StreamingCallbackHandler,
)
from langchain_core.utils._gateway import GATEWAY_METADATA_RESPONSE_KEY
from langchain_core.utils._gateway import (
GATEWAY_METADATA_RESPONSE_KEY,
_parse_gateway_metadata,
)
from langchain_core.utils.function_calling import (
convert_to_json_schema,
convert_to_openai_tool,
@@ -110,6 +113,7 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
if hasattr(error, "response"):
response = error.response
metadata: dict[str, Any] = {}
generation_info: dict[str, Any] = {}
if hasattr(response, "json"):
try:
metadata["body"] = response.json()
@@ -120,7 +124,11 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
metadata["body"] = None
if hasattr(response, "headers"):
try:
metadata["headers"] = dict(response.headers)
headers = response.headers
metadata["headers"] = dict(headers)
gateway_metadata = _parse_gateway_metadata(headers)
if gateway_metadata is not None:
generation_info[GATEWAY_METADATA_RESPONSE_KEY] = gateway_metadata
except Exception:
metadata["headers"] = None
if hasattr(response, "status_code"):
@@ -128,7 +136,10 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
if hasattr(error, "request_id"):
metadata["request_id"] = error.request_id
generations = [
ChatGeneration(message=AIMessage(content="", response_metadata=metadata))
ChatGeneration(
message=AIMessage(content="", response_metadata=metadata),
generation_info=generation_info or None,
)
]
else:
generations = []
@@ -54,6 +54,7 @@ from langchain_core.tracers.context import collect_runs
from langchain_core.tracers.event_stream import _AstreamEventsCallbackHandler
from langchain_core.tracers.langchain import LangChainTracer
from langchain_core.tracers.schemas import Run
from langchain_core.utils._gateway import GATEWAY_METADATA_RESPONSE_KEY
from langchain_core.version import VERSION
from tests.unit_tests.fake.callbacks import (
BaseFakeCallbackHandler,
@@ -1730,6 +1731,21 @@ def test_generate_response_from_error_with_valid_json() -> None:
assert metadata["status_code"] == 400
def test_generate_response_from_error_surfaces_gateway_metadata() -> None:
"""Gateway metadata from an error response is available for tracing."""
response = MockResponse(
headers={"X-LangSmith-Gateway-Metadata": '{"outcome": "blocked"}'},
)
generations = _generate_response_from_error(
MockAPIError("API Error", response=response)
)
assert generations[0].generation_info == {
GATEWAY_METADATA_RESPONSE_KEY: {"outcome": "blocked"}
}
def test_generate_response_from_error_handles_streaming_response_failure() -> None:
# Simulates scenario where accessing response.json() or response.text
# raises ResponseNotRead on streaming responses