Tony20100/langchain_full huggingface dataset:
runnable: The base Runnable to be wrapped. Must take as input one of:
                1. A sequence of BaseMessages
                2. A dict with one key for all messages
                3. A dict with one key for the current input string/message(s) and
                    a separate key for historical messages. If the input key points
                    to a string, it will be treated as a HumanMessage in history.
                Must return as output one of:
                1. A string which can be treated as an AIMessage
                2. A BaseMessage or sequence of BaseMessages
                3. A dict with a key for a BaseMessage or sequence of BaseMessages
            get_session_history: Function that returns a new BaseChatMessageHistory.
                This function should either take a single positional argument
                `session_id` of type string and return a corresponding
                chat message history instance.
                .. code-block:: python
                    def get_session_history(
                        session_id: str,
                        *,
                        user_id: Optional[str]=None
                    ) -> BaseChatMessageHistory:
                      ...
                Or it should take keyword arguments that match the keys of
                `session_history_config_specs` and return a corresponding
                chat message history instance.
                .. code-block:: python
                    def get_session_history(
                        *,
                        user_id: str,
                        thread_id: str,
                    ) -> BaseChatMessageHistory:
                        ...
            input_messages_key: Must be specified if the base runnable accepts a dict
                as input.
            output_messages_key: Must be specified if the base runnable returns a dict
                as output.
            history_messages_key: Must be specified if the base runnable accepts a dict
                as input and expects a separate key for historical messages.
            history_factory_config: Configure fields that should be passed to the
history_factory_config: Configure fields that should be passed to the
                chat history factory. See ``ConfigurableFieldSpec`` for more details.
                Specifying these allows you to pass multiple config keys
                into the get_session_history factory.
            **kwargs: Arbitrary additional kwargs to pass to parent class
                ``RunnableBindingBase`` init.
        """
        history_chain: Runnable = RunnableLambda(
            self._enter_history, self._aenter_history
        ).with_config(run_name="load_history")
        messages_key = history_messages_key or input_messages_key
        if messages_key:
            history_chain = RunnablePassthrough.assign(
                **{messages_key: history_chain}
            ).with_config(run_name="insert_history")
        bound = (
            history_chain | runnable.with_listeners(on_end=self._exit_history)
        ).with_config(run_name="RunnableWithMessageHistory")
        if history_factory_config:
            _config_specs = history_factory_config
        else:
            # If not provided, then we'll use the default session_id field
            _config_specs = [
                ConfigurableFieldSpec(
                    id="session_id",
                    annotation=str,
                    name="Session ID",
                    description="Unique identifier for a session.",
                    default="",
                    is_shared=True,
                ),
            ]
        super().__init__(
            get_session_history=get_session_history,
            input_messages_key=input_messages_key,
            output_messages_key=output_messages_key,
            bound=bound,
            history_messages_key=history_messages_key,
            history_factory_config=_config_specs,
            **kwargs,
        )
    @property
    def config_specs(self) -> List[ConfigurableFieldSpec]:
        return get_unique_config_specs(
            super().config_specs + list(self.history_factory_config)
        )
super().config_specs + list(self.history_factory_config)
        )
[docs]    def get_input_schema(
        self, config: Optional[RunnableConfig] = None
    ) -> Type[BaseModel]:
        super_schema = super().get_input_schema(config)
        if super_schema.__custom_root_type__ or not super_schema.schema().get(
            "properties"
        ):
            from langchain_core.messages import BaseMessage
            fields: Dict = {}
            if self.input_messages_key and self.history_messages_key:
                fields[self.input_messages_key] = (
                    Union[str, BaseMessage, Sequence[BaseMessage]],
                    ...,
                )
            elif self.input_messages_key:
                fields[self.input_messages_key] = (Sequence[BaseMessage], ...)
            else:
                fields["__root__"] = (Sequence[BaseMessage], ...)
            return create_model(  # type: ignore[call-overload]
                "RunnableWithChatHistoryInput",
                **fields,
            )
        else:
            return super_schema
    def _get_input_messages(
        self, input_val: Union[str, BaseMessage, Sequence[BaseMessage], dict]
    ) -> List[BaseMessage]:
        from langchain_core.messages import BaseMessage
        # If dictionary, try to pluck the single key representing messages
        if isinstance(input_val, dict):
            if self.input_messages_key:
                key = self.input_messages_key
            elif len(input_val) == 1:
                key = list(input_val.keys())[0]
            else:
                key = "input"
            input_val = input_val[key]
        # If value is a string, convert to a human message
        if isinstance(input_val, str):
            from langchain_core.messages import HumanMessage
if isinstance(input_val, str):
            from langchain_core.messages import HumanMessage
            return [HumanMessage(content=input_val)]
        # If value is a single message, convert to a list
        elif isinstance(input_val, BaseMessage):
            return [input_val]
        # If value is a list or tuple...
        elif isinstance(input_val, (list, tuple)):
            # Handle empty case
            if len(input_val) == 0:
                return list(input_val)
            # If is a list of list, then return the first value
            # This occurs for chat models - since we batch inputs
            if isinstance(input_val[0], list):
                if len(input_val) != 1:
                    raise ValueError()
                return input_val[0]
            return list(input_val)
        else:
            raise ValueError(
                f"Expected str, BaseMessage, List[BaseMessage], or Tuple[BaseMessage]. "
                f"Got {input_val}."
            )
    def _get_output_messages(
        self, output_val: Union[str, BaseMessage, Sequence[BaseMessage], dict]
    ) -> List[BaseMessage]:
        from langchain_core.messages import BaseMessage
        # If dictionary, try to pluck the single key representing messages
        if isinstance(output_val, dict):
            if self.output_messages_key:
                key = self.output_messages_key
            elif len(output_val) == 1:
                key = list(output_val.keys())[0]
            else:
                key = "output"
            # If you are wrapping a chat model directly
            # The output is actually this weird generations object
            if key not in output_val and "generations" in output_val:
                output_val = output_val["generations"][0][0]["message"]
output_val = output_val["generations"][0][0]["message"]
            else:
                output_val = output_val[key]
        if isinstance(output_val, str):
            from langchain_core.messages import AIMessage
            return [AIMessage(content=output_val)]
        # If value is a single message, convert to a list
        elif isinstance(output_val, BaseMessage):
            return [output_val]
        elif isinstance(output_val, (list, tuple)):
            return list(output_val)
        else:
            raise ValueError()
    def _enter_history(self, input: Any, config: RunnableConfig) -> List[BaseMessage]:
        hist: BaseChatMessageHistory = config["configurable"]["message_history"]
        messages = hist.messages.copy()
        if not self.history_messages_key:
            # return all messages
            input_val = (
                input if not self.input_messages_key else input[self.input_messages_key]
            )
            messages += self._get_input_messages(input_val)
        return messages
    async def _aenter_history(
        self, input: Dict[str, Any], config: RunnableConfig
    ) -> List[BaseMessage]:
        hist: BaseChatMessageHistory = config["configurable"]["message_history"]
        messages = (await hist.aget_messages()).copy()
        if not self.history_messages_key:
            # return all messages
            input_val = (
                input if not self.input_messages_key else input[self.input_messages_key]
            )
            messages += self._get_input_messages(input_val)
        return messages
    def _exit_history(self, run: Run, config: RunnableConfig) -> None:
        hist: BaseChatMessageHistory = config["configurable"]["message_history"]
        # Get the input messages
        inputs = load(run.inputs)
# Get the input messages
        inputs = load(run.inputs)
        input_messages = self._get_input_messages(inputs)
        # If historic messages were prepended to the input messages, remove them to
        # avoid adding duplicate messages to history.
        if not self.history_messages_key:
            historic_messages = config["configurable"]["message_history"].messages
            input_messages = input_messages[len(historic_messages) :]
        # Get the output messages
        output_val = load(run.outputs)
        output_messages = self._get_output_messages(output_val)
        hist.add_messages(input_messages + output_messages)
    def _merge_configs(self, *configs: Optional[RunnableConfig]) -> RunnableConfig:
        config = super()._merge_configs(*configs)
        expected_keys = [field_spec.id for field_spec in self.history_factory_config]
        configurable = config.get("configurable", {})
        missing_keys = set(expected_keys) - set(configurable.keys())
        if missing_keys:
            example_input = {self.input_messages_key: "foo"}
            example_configurable = {
                missing_key: "[your-value-here]" for missing_key in missing_keys
            }
            example_config = {"configurable": example_configurable}
            raise ValueError(
                f"Missing keys {sorted(missing_keys)} in config['configurable'] "
                f"Expected keys are {sorted(expected_keys)}."
                f"When using via .invoke() or .stream(), pass in a config; "
                f"e.g., chain.invoke({example_input}, {example_config})"
            )
        parameter_names = _get_parameter_names(self.get_session_history)
        if len(expected_keys) == 1:
            # If arity = 1, then invoke function by positional arguments
            message_history = self.get_session_history(configurable[expected_keys[0]])
message_history = self.get_session_history(configurable[expected_keys[0]])
        else:
            # otherwise verify that names of keys patch and invoke by named arguments
            if set(expected_keys) != set(parameter_names):
                raise ValueError(
                    f"Expected keys {sorted(expected_keys)} do not match parameter "
                    f"names {sorted(parameter_names)} of get_session_history."
                )
            message_history = self.get_session_history(
                **{key: configurable[key] for key in expected_keys}
            )
        config["configurable"]["message_history"] = message_history
        return config
def _get_parameter_names(callable_: GetSessionHistoryCallable) -> List[str]:
    """Get the parameter names of the callable."""
    sig = inspect.signature(callable_)
    return list(sig.parameters.keys())
Source code for langchain_core.runnables.graph_png
from typing import Any, Optional
from langchain_core.runnables.graph import Graph, LabelsDict
[docs]class PngDrawer:
    """Helper class to draw a state graph into a PNG file.
    It requires graphviz and pygraphviz to be installed.
    :param fontname: The font to use for the labels
    :param labels: A dictionary of label overrides. The dictionary
        should have the following format:
        {
            "nodes": {
                "node1": "CustomLabel1",
                "node2": "CustomLabel2",
                "__end__": "End Node"
            },
            "edges": {
                "continue": "ContinueLabel",
                "end": "EndLabel"
            }
        }
        The keys are the original labels, and the values are the new labels.
    Usage:
        drawer = PngDrawer()
        drawer.draw(state_graph, 'graph.png')
    """
[docs]    def __init__(
        self, fontname: Optional[str] = None, labels: Optional[LabelsDict] = None
    ) -> None:
        """Initializes the PNG drawer.
        Args:
            fontname: The font to use for the labels
            labels: A dictionary of label overrides. The dictionary
                should have the following format:
                {
                    "nodes": {
                        "node1": "CustomLabel1",
                        "node2": "CustomLabel2",
                        "__end__": "End Node"
                    },
                    "edges": {
                        "continue": "ContinueLabel",
                        "end": "EndLabel"
                    }
                }
                The keys are the original labels, and the values are the new labels.
        """
The keys are the original labels, and the values are the new labels.
        """
        self.fontname = fontname or "arial"
        self.labels = labels or LabelsDict(nodes={}, edges={})
[docs]    def get_node_label(self, label: str) -> str:
        """Returns the label to use for a node.
        Args:
            label: The original label
        Returns:
            The new label.
        """
        label = self.labels.get("nodes", {}).get(label, label)
        return f"<<B>{label}</B>>"
[docs]    def get_edge_label(self, label: str) -> str:
        """Returns the label to use for an edge.
        Args:
            label: The original label
        Returns:
            The new label.
        """
        label = self.labels.get("edges", {}).get(label, label)
        return f"<<U>{label}</U>>"
[docs]    def add_node(self, viz: Any, node: str) -> None:
        """Adds a node to the graph.
        Args:
            viz: The graphviz object
            node: The node to add
        Returns:
            None
        """
        viz.add_node(
            node,
            label=self.get_node_label(node),
            style="filled",
            fillcolor="yellow",
            fontsize=15,
            fontname=self.fontname,
        )
[docs]    def add_edge(
        self,
        viz: Any,
        source: str,
        target: str,
        label: Optional[str] = None,
        conditional: bool = False,
    ) -> None:
        """Adds an edge to the graph.
        Args:
            viz: The graphviz object
Args:
            viz: The graphviz object
            source: The source node
            target: The target node
            label: The label for the edge. Defaults to None.
            conditional: Whether the edge is conditional. Defaults to False.
        Returns:
            None
        """
        viz.add_edge(
            source,
            target,
            label=self.get_edge_label(label) if label else "",
            fontsize=12,
            fontname=self.fontname,
            style="dotted" if conditional else "solid",
        )
[docs]    def draw(self, graph: Graph, output_path: Optional[str] = None) -> Optional[bytes]:
        """Draw the given state graph into a PNG file.
        Requires graphviz and pygraphviz to be installed.
        :param graph: The graph to draw
        :param output_path: The path to save the PNG. If None, PNG bytes are returned.
        """
        try:
            import pygraphviz as pgv  # type: ignore[import]
        except ImportError as exc:
            raise ImportError(
                "Install pygraphviz to draw graphs: `pip install pygraphviz`."
            ) from exc
        # Create a directed graph
        viz = pgv.AGraph(directed=True, nodesep=0.9, ranksep=1.0)
        # Add nodes, conditional edges, and edges to the graph
        self.add_nodes(viz, graph)
        self.add_edges(viz, graph)
        # Update entrypoint and END styles
        self.update_styles(viz, graph)
        # Save the graph as PNG
        try:
            return viz.draw(output_path, format="png", prog="dot")
        finally:
            viz.close()
finally:
            viz.close()
[docs]    def add_nodes(self, viz: Any, graph: Graph) -> None:
        for node in graph.nodes:
            self.add_node(viz, node)
[docs]    def add_edges(self, viz: Any, graph: Graph) -> None:
        for start, end, label, cond in graph.edges:
            self.add_edge(viz, start, end, label, cond)
[docs]    def update_styles(self, viz: Any, graph: Graph) -> None:
        if first := graph.first_node():
            viz.get_node(first.id).attr.update(fillcolor="lightblue")
        if last := graph.last_node():
            viz.get_node(last.id).attr.update(fillcolor="orange")
Source code for langchain_core.runnables.schema
"""Module contains typedefs that are used with runnables."""
from __future__ import annotations
from typing import Any, Dict, List, Sequence
from typing_extensions import NotRequired, TypedDict
[docs]class EventData(TypedDict, total=False):
    """Data associated with a streaming event."""
    input: Any
    """The input passed to the runnable that generated the event.
    
    Inputs will sometimes be available at the *START* of the Runnable, and 
    sometimes at the *END* of the Runnable.
    
    If a Runnable is able to stream its inputs, then its input by definition
    won't be known until the *END* of the Runnable when it has finished streaming
    its inputs.
    """
    output: Any
    """The output of the Runnable that generated the event.
    
    Outputs will only be available at the *END* of the Runnable.
    
    For most Runnables, this field can be inferred from the `chunk` field,
    though there might be some exceptions for special cased Runnables (e.g., like
    chat models), which may return more information.
    """
    chunk: Any
    """A streaming chunk from the output that generated the event.
    
    chunks support addition in general, and adding them up should result
    in the output of the Runnable that generated the event.
    """
[docs]class StreamEvent(TypedDict):
    """Streaming event.
    Schema of a streaming event which is produced from the astream_events method.
    Example:
        .. code-block:: python
            from langchain_core.runnables import RunnableLambda
            async def reverse(s: str) -> str:
                return s[::-1]
async def reverse(s: str) -> str:
                return s[::-1]
            chain = RunnableLambda(func=reverse)
            events = [event async for event in chain.astream_events("hello")]
            # will produce the following events
            # (where some fields have been omitted for brevity):
            [
                {
                    "data": {"input": "hello"},
                    "event": "on_chain_start",
                    "metadata": {},
                    "name": "reverse",
                    "tags": [],
                },
                {
                    "data": {"chunk": "olleh"},
                    "event": "on_chain_stream",
                    "metadata": {},
                    "name": "reverse",
                    "tags": [],
                },
                {
                    "data": {"output": "olleh"},
                    "event": "on_chain_end",
                    "metadata": {},
                    "name": "reverse",
                    "tags": [],
                },
            ]
    """
    event: str
    """Event names are of the format: on_[runnable_type]_(start|stream|end).
    
    Runnable types are one of: 
    * llm - used by non chat models
    * chat_model - used by chat models
    * prompt --  e.g., ChatPromptTemplate
    * tool -- from tools defined via @tool decorator or inheriting from Tool/BaseTool
    * chain - most Runnables are of this type
    
    Further, the events are categorized as one of:
    * start - when the runnable starts
    * stream - when the runnable is streaming
    * end - when the runnable ends
    
    start, stream and end are associated with slightly different `data` payload.
start, stream and end are associated with slightly different `data` payload.
    
    Please see the documentation for `EventData` for more details.
    """
    name: str
    """The name of the runnable that generated the event."""
    run_id: str
    """An randomly generated ID to keep track of the execution of the given runnable.
    
    Each child runnable that gets invoked as part of the execution of a parent runnable
    is assigned its own unique ID.
    """
    tags: NotRequired[List[str]]
    """Tags associated with the runnable that generated this event.
    
    Tags are always inherited from parent runnables.
    
    Tags can either be bound to a runnable using `.with_config({"tags":  ["hello"]})`
    or passed at run time using `.astream_events(..., {"tags": ["hello"]})`.
    """
    metadata: NotRequired[Dict[str, Any]]
    """Metadata associated with the runnable that generated this event.
    
    Metadata can either be bound to a runnable using 
    
        `.with_config({"metadata": { "foo": "bar" }})`
        
    or passed at run time using 
    
        `.astream_events(..., {"metadata": {"foo": "bar"}})`.
    """
    data: EventData
    """Event data.
    The contents of the event data depend on the event type.
    """
    parent_ids: Sequence[str]
    """A list of the parent IDs associated with this event.
    Root Events will have an empty list.
    For example, if a Runnable A calls Runnable B, then the event generated by Runnable 
    B will have Runnable A's ID in the parent_ids field.
Source code for langchain_core.runnables.graph
from __future__ import annotations
import inspect
from dataclasses import dataclass, field
from enum import Enum
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Dict,
    List,
    NamedTuple,
    Optional,
    Tuple,
    Type,
    TypedDict,
    Union,
    overload,
)
from uuid import UUID, uuid4
from langchain_core.pydantic_v1 import BaseModel
if TYPE_CHECKING:
    from langchain_core.runnables.base import Runnable as RunnableType
[docs]class LabelsDict(TypedDict):
    """Dictionary of labels for nodes and edges in a graph."""
    nodes: dict[str, str]
    """Labels for nodes."""
    edges: dict[str, str]
    """Labels for edges."""
[docs]def is_uuid(value: str) -> bool:
    """Check if a string is a valid UUID.
    Args:
        value: The string to check.
    Returns:
        True if the string is a valid UUID, False otherwise.
    """
    try:
        UUID(value)
        return True
    except ValueError:
        return False
[docs]class Edge(NamedTuple):
    """Edge in a graph."""
    source: str
    target: str
    data: Optional[str] = None
    conditional: bool = False
[docs]class Node(NamedTuple):
    """Node in a graph."""
    id: str
    data: Union[Type[BaseModel], RunnableType]
[docs]class Branch(NamedTuple):
    """Branch in a graph."""
    condition: Callable[..., str]
    ends: Optional[dict[str, str]]
[docs]class CurveStyle(Enum):