Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Serializable components for structured output functionality."""

from __future__ import annotations

from typing import Any


class SerializableParsingErrorHandler:
"""Serializable replacement for `lambda _: None` in parsing error scenarios.

This class provides the same functionality as the lambda function but can be
pickled for thread persistence in LangGraph Studio.
"""

def __call__(self, _: Any) -> None:
"""Return None for any input, mimicking lambda _: None behavior."""
return

def __repr__(self) -> str:
"""Provide a clear representation for debugging."""
return "SerializableParsingErrorHandler()"

def __eq__(self, other: object) -> bool:
"""Support equality comparison for testing."""
return isinstance(other, SerializableParsingErrorHandler)

def __hash__(self) -> int:
"""Support hashing for set/dict operations."""
return hash(self.__class__.__name__)

def __getstate__(self) -> dict[str, Any]:
"""Control pickle serialization for production robustness."""
# This class has no state to preserve, return empty dict
return {}

def __setstate__(self, state: dict[str, Any]) -> None:
"""Control pickle deserialization for production robustness."""
# This class has no state to restore, nothing to do


class SerializableNoneAssigner:
"""Serializable replacement for `lambda _: None` in RunnablePassthrough.assign().

Used in the fallback chain when parsing fails.
"""

def __call__(self, _: Any) -> None:
"""Return None for any input, used as fallback parsed value."""
return

def __repr__(self) -> str:
"""Provide a clear representation for debugging."""
return "SerializableNoneAssigner()"

def __eq__(self, other: object) -> bool:
"""Support equality comparison for testing."""
return isinstance(other, SerializableNoneAssigner)

def __hash__(self) -> int:
"""Support hashing for set/dict operations."""
return hash(self.__class__.__name__)

def __getstate__(self) -> dict[str, Any]:
"""Control pickle serialization for production robustness."""
# This class has no state to preserve, return empty dict
return {}

def __setstate__(self, state: dict[str, Any]) -> None:
"""Control pickle deserialization for production robustness."""
# This class has no state to restore, nothing to do


def get_serializable_error_handler() -> SerializableParsingErrorHandler:
"""Get a serializable error handler instance.

This is a convenience function that can be used directly in place of
lambda _: None in RunnablePassthrough.assign() calls.

Returns:
SerializableParsingErrorHandler instance.
"""
return SerializableParsingErrorHandler()


def get_serializable_none_assigner() -> SerializableNoneAssigner:
"""Get a serializable None assigner instance.

This is a convenience function that can be used directly in place of
lambda _: None in parsed value assignments.

Returns:
SerializableNoneAssigner instance.
"""
return SerializableNoneAssigner()
11 changes: 9 additions & 2 deletions libs/core/langchain_core/language_models/chat_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
Callbacks,
)
from langchain_core.globals import get_llm_cache
from langchain_core.language_models._serializable_structured_output import (
get_serializable_error_handler,
get_serializable_none_assigner,
)
from langchain_core.language_models._utils import (
_normalize_messages,
_update_message_content_to_blocks,
Expand Down Expand Up @@ -1633,9 +1637,12 @@ class AnswerWithJustification(BaseModel):
)
if include_raw:
parser_assign = RunnablePassthrough.assign(
parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None
parsed=itemgetter("raw") | output_parser,
parsing_error=get_serializable_error_handler(),
)
parser_none = RunnablePassthrough.assign(
parsed=get_serializable_none_assigner()
)
parser_none = RunnablePassthrough.assign(parsed=lambda _: None)
parser_with_fallback = parser_assign.with_fallbacks(
[parser_none], exception_key="parsing_error"
)
Expand Down
Loading