Tony20100/langchain_full huggingface dataset:
return attr(
                        *args,
                        config=merge_configs(self.config, kwargs.pop("config", None)),
                        **kwargs,
                    )
                return wrapper
            elif config_param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
                idx = list(inspect.signature(attr).parameters).index("config")
                @wraps(attr)
                def wrapper(*args: Any, **kwargs: Any) -> Any:
                    if len(args) >= idx + 1:
                        argsl = list(args)
                        argsl[idx] = merge_configs(self.config, argsl[idx])
                        return attr(*argsl, **kwargs)
                    else:
                        return attr(
                            *args,
                            config=merge_configs(
                                self.config, kwargs.pop("config", None)
                            ),
                            **kwargs,
                        )
                return wrapper
        return attr
RunnableLike = Union[
    Runnable[Input, Output],
    Callable[[Input], Output],
    Callable[[Input], Awaitable[Output]],
    Callable[[Iterator[Input]], Iterator[Output]],
    Callable[[AsyncIterator[Input]], AsyncIterator[Output]],
    Mapping[str, Any],
]
[docs]def coerce_to_runnable(thing: RunnableLike) -> Runnable[Input, Output]:
    """Coerce a runnable-like object into a Runnable.
    Args:
        thing: A runnable-like object.
    Returns:
        A Runnable.
    """
    if isinstance(thing, Runnable):
        return thing
    elif is_async_generator(thing) or inspect.isgeneratorfunction(thing):
        return RunnableGenerator(thing)
    elif callable(thing):
        return RunnableLambda(cast(Callable[[Input], Output], thing))
    elif isinstance(thing, dict):
        return cast(Runnable[Input, Output], RunnableParallel(thing))
return cast(Runnable[Input, Output], RunnableParallel(thing))
    else:
        raise TypeError(
            f"Expected a Runnable, callable or dict."
            f"Instead got an unsupported type: {type(thing)}"
        )
@overload
def chain(
    func: Callable[[Input], Coroutine[Any, Any, Output]],
) -> Runnable[Input, Output]:
    ...
@overload
def chain(
    func: Callable[[Input], Iterator[Output]],
) -> Runnable[Input, Output]:
    ...
@overload
def chain(
    func: Callable[[Input], AsyncIterator[Output]],
) -> Runnable[Input, Output]:
    ...
@overload
def chain(
    func: Callable[[Input], Output],
) -> Runnable[Input, Output]:
    ...
[docs]def chain(
    func: Union[
        Callable[[Input], Output],
        Callable[[Input], Iterator[Output]],
        Callable[[Input], Coroutine[Any, Any, Output]],
        Callable[[Input], AsyncIterator[Output]],
    ],
) -> Runnable[Input, Output]:
    """Decorate a function to make it a Runnable.
    Sets the name of the runnable to the name of the function.
    Any runnables called by the function will be traced as dependencies.
    Args:
        func: A callable.
    Returns:
        A Runnable.
    Example:
    .. code-block:: python
        from langchain_core.runnables import chain
        from langchain_core.prompts import PromptTemplate
        from langchain_openai import OpenAI
        @chain
        def my_func(fields):
            prompt = PromptTemplate("Hello, {name}!")
            llm = OpenAI()
            formatted = prompt.invoke(**fields)
Source code for langchain_core.runnables.graph_ascii
"""Draws DAG in ASCII.
Adapted from https://github.com/iterative/dvc/blob/main/dvc/dagascii.py"""
import math
import os
from typing import Any, Mapping, Sequence
from langchain_core.runnables.graph import Edge as LangEdge
[docs]class VertexViewer:
    """Class to define vertex box boundaries that will be accounted for during
    graph building by grandalf.
    Args:
        name (str): name of the vertex.
    """
    HEIGHT = 3  # top and bottom box edges + text
[docs]    def __init__(self, name: str) -> None:
        self._h = self.HEIGHT  # top and bottom box edges + text
        self._w = len(name) + 2  # right and left bottom edges + text
    @property
    def h(self) -> int:
        """Height of the box."""
        return self._h
    @property
    def w(self) -> int:
        """Width of the box."""
        return self._w
[docs]class AsciiCanvas:
    """Class for drawing in ASCII.
    Args:
        cols (int): number of columns in the canvas. Should be > 1.
        lines (int): number of lines in the canvas. Should be > 1.
    """
    TIMEOUT = 10
[docs]    def __init__(self, cols: int, lines: int) -> None:
        assert cols > 1
        assert lines > 1
        self.cols = cols
        self.lines = lines
        self.canvas = [[" "] * cols for line in range(lines)]
[docs]    def draw(self) -> str:
[docs]    def draw(self) -> str:
        """Draws ASCII canvas on the screen."""
        lines = map("".join, self.canvas)
        return os.linesep.join(lines)
[docs]    def point(self, x: int, y: int, char: str) -> None:
        """Create a point on ASCII canvas.
        Args:
            x (int): x coordinate. Should be >= 0 and < number of columns in
                the canvas.
            y (int): y coordinate. Should be >= 0 an < number of lines in the
                canvas.
            char (str): character to place in the specified point on the
                canvas.
        """
        assert len(char) == 1
        assert x >= 0
        assert x < self.cols
        assert y >= 0
        assert y < self.lines
        self.canvas[y][x] = char
[docs]    def line(self, x0: int, y0: int, x1: int, y1: int, char: str) -> None:
        """Create a line on ASCII canvas.
        Args:
            x0 (int): x coordinate where the line should start.
            y0 (int): y coordinate where the line should start.
            x1 (int): x coordinate where the line should end.
            y1 (int): y coordinate where the line should end.
            char (str): character to draw the line with.
        """
        if x0 > x1:
            x1, x0 = x0, x1
            y1, y0 = y0, y1
        dx = x1 - x0
        dy = y1 - y0
        if dx == 0 and dy == 0:
if dx == 0 and dy == 0:
            self.point(x0, y0, char)
        elif abs(dx) >= abs(dy):
            for x in range(x0, x1 + 1):
                if dx == 0:
                    y = y0
                else:
                    y = y0 + int(round((x - x0) * dy / float(dx)))
                self.point(x, y, char)
        elif y0 < y1:
            for y in range(y0, y1 + 1):
                if dy == 0:
                    x = x0
                else:
                    x = x0 + int(round((y - y0) * dx / float(dy)))
                self.point(x, y, char)
        else:
            for y in range(y1, y0 + 1):
                if dy == 0:
                    x = x0
                else:
                    x = x1 + int(round((y - y1) * dx / float(dy)))
                self.point(x, y, char)
[docs]    def text(self, x: int, y: int, text: str) -> None:
        """Print a text on ASCII canvas.
        Args:
            x (int): x coordinate where the text should start.
            y (int): y coordinate where the text should start.
            text (str): string that should be printed.
        """
        for i, char in enumerate(text):
            self.point(x + i, y, char)
[docs]    def box(self, x0: int, y0: int, width: int, height: int) -> None:
        """Create a box on ASCII canvas.
        Args:
"""Create a box on ASCII canvas.
        Args:
            x0 (int): x coordinate of the box corner.
            y0 (int): y coordinate of the box corner.
            width (int): box width.
            height (int): box height.
        """
        assert width > 1
        assert height > 1
        width -= 1
        height -= 1
        for x in range(x0, x0 + width):
            self.point(x, y0, "-")
            self.point(x, y0 + height, "-")
        for y in range(y0, y0 + height):
            self.point(x0, y, "|")
            self.point(x0 + width, y, "|")
        self.point(x0, y0, "+")
        self.point(x0 + width, y0, "+")
        self.point(x0, y0 + height, "+")
        self.point(x0 + width, y0 + height, "+")
def _build_sugiyama_layout(
    vertices: Mapping[str, str], edges: Sequence[LangEdge]
) -> Any:
    try:
        from grandalf.graphs import Edge, Graph, Vertex  # type: ignore[import]
        from grandalf.layouts import SugiyamaLayout  # type: ignore[import]
        from grandalf.routing import (  # type: ignore[import]
            EdgeViewer,
            route_with_lines,
        )
    except ImportError as exc:
        raise ImportError(
            "Install grandalf to draw graphs: `pip install grandalf`."
        ) from exc
    #
    # Just a reminder about naming conventions:
    # +------------X
    # |
    # |
    # |
    # |
    # Y
# |
    # |
    # |
    # |
    # Y
    #
    vertices_ = {id: Vertex(f" {data} ") for id, data in vertices.items()}
    edges_ = [Edge(vertices_[s], vertices_[e], data=cond) for s, e, _, cond in edges]
    vertices_list = vertices_.values()
    graph = Graph(vertices_list, edges_)
    for vertex in vertices_list:
        vertex.view = VertexViewer(vertex.data)
    # NOTE: determine min box length to create the best layout
    minw = min(v.view.w for v in vertices_list)
    for edge in edges_:
        edge.view = EdgeViewer()
    sug = SugiyamaLayout(graph.C[0])
    graph = graph.C[0]
    roots = list(filter(lambda x: len(x.e_in()) == 0, graph.sV))
    sug.init_all(roots=roots, optimize=True)
    sug.yspace = VertexViewer.HEIGHT
    sug.xspace = minw
    sug.route_edge = route_with_lines
    sug.draw()
    return sug
[docs]def draw_ascii(vertices: Mapping[str, str], edges: Sequence[LangEdge]) -> str:
    """Build a DAG and draw it in ASCII.
    Args:
        vertices (list): list of graph vertices.
        edges (list): list of graph edges.
    Returns:
        str: ASCII representation
    Example:
        >>> vertices = [1, 2, 3, 4]
        >>> edges = [(1, 2), (2, 3), (2, 4), (1, 4)]
        >>> print(draw(vertices, edges))
        +---+     +---+
>>> print(draw(vertices, edges))
        +---+     +---+
        | 3 |     | 4 |
        +---+    *+---+
          *    **   *
          *  **     *
          * *       *
        +---+       *
        | 2 |      *
        +---+     *
             *    *
              *  *
               **
             +---+
             | 1 |
             +---+
    """
    # NOTE: coordinates might me negative, so we need to shift
    # everything to the positive plane before we actually draw it.
    Xs = []
    Ys = []
    sug = _build_sugiyama_layout(vertices, edges)
    for vertex in sug.g.sV:
        # NOTE: moving boxes w/2 to the left
        Xs.append(vertex.view.xy[0] - vertex.view.w / 2.0)
        Xs.append(vertex.view.xy[0] + vertex.view.w / 2.0)
        Ys.append(vertex.view.xy[1])
        Ys.append(vertex.view.xy[1] + vertex.view.h)
    for edge in sug.g.sE:
        for x, y in edge.view._pts:
            Xs.append(x)
            Ys.append(y)
    minx = min(Xs)
    miny = min(Ys)
    maxx = max(Xs)
    maxy = max(Ys)
    canvas_cols = int(math.ceil(math.ceil(maxx) - math.floor(minx))) + 1
    canvas_lines = int(round(maxy - miny))
    canvas = AsciiCanvas(canvas_cols, canvas_lines)
    # NOTE: first draw edges so that node boxes could overwrite them
# NOTE: first draw edges so that node boxes could overwrite them
    for edge in sug.g.sE:
        assert len(edge.view._pts) > 1
        for index in range(1, len(edge.view._pts)):
            start = edge.view._pts[index - 1]
            end = edge.view._pts[index]
            start_x = int(round(start[0] - minx))
            start_y = int(round(start[1] - miny))
            end_x = int(round(end[0] - minx))
            end_y = int(round(end[1] - miny))
            assert start_x >= 0
            assert start_y >= 0
            assert end_x >= 0
            assert end_y >= 0
            canvas.line(start_x, start_y, end_x, end_y, "." if edge.data else "*")
    for vertex in sug.g.sV:
        # NOTE: moving boxes w/2 to the left
        x = vertex.view.xy[0] - vertex.view.w / 2.0
        y = vertex.view.xy[1]
        canvas.box(
            int(round(x - minx)),
            int(round(y - miny)),
            vertex.view.w,
            vertex.view.h,
        )
        canvas.text(int(round(x - minx)) + 1, int(round(y - miny)) + 1, vertex.data)
    return canvas.draw()
Source code for langchain_core.runnables.router
from __future__ import annotations
from typing import (
    Any,
    AsyncIterator,
    Callable,
    Iterator,
    List,
    Mapping,
    Optional,
    Union,
    cast,
)
from typing_extensions import TypedDict
from langchain_core.runnables.base import (
    Input,
    Output,
    Runnable,
    RunnableSerializable,
    coerce_to_runnable,
)
from langchain_core.runnables.config import (
    RunnableConfig,
    get_config_list,
    get_executor_for_config,
)
from langchain_core.runnables.utils import (
    ConfigurableFieldSpec,
    gather_with_concurrency,
    get_unique_config_specs,
)
[docs]class RouterInput(TypedDict):
    """Router input.
    Attributes:
        key: The key to route on.
        input: The input to pass to the selected runnable.
    """
    key: str
    input: Any
[docs]class RouterRunnable(RunnableSerializable[RouterInput, Output]):
    """
    Runnable that routes to a set of Runnables based on Input['key'].
    Returns the output of the selected Runnable.
    For example,
    .. code-block:: python
        from langchain_core.runnables.router import RouterRunnable
        from langchain_core.runnables import RunnableLambda
        add = RunnableLambda(func=lambda x: x + 1)
        square = RunnableLambda(func=lambda x: x**2)
        router = RouterRunnable(runnables={"add": add, "square": square})
        router.invoke({"key": "square", "input": 3})
    """
    runnables: Mapping[str, Runnable[Any, Output]]
    @property
runnables: Mapping[str, Runnable[Any, Output]]
    @property
    def config_specs(self) -> List[ConfigurableFieldSpec]:
        return get_unique_config_specs(
            spec for step in self.runnables.values() for spec in step.config_specs
        )
    def __init__(
        self,
        runnables: Mapping[str, Union[Runnable[Any, Output], Callable[[Any], Output]]],
    ) -> None:
        super().__init__(  # type: ignore[call-arg]
            runnables={key: coerce_to_runnable(r) for key, r in runnables.items()}
        )
    class Config:
        arbitrary_types_allowed = True
[docs]    @classmethod
    def is_lc_serializable(cls) -> bool:
        """Return whether this class is serializable."""
        return True
[docs]    @classmethod
    def get_lc_namespace(cls) -> List[str]:
        """Get the namespace of the langchain object."""
        return ["langchain", "schema", "runnable"]
[docs]    def invoke(
        self, input: RouterInput, config: Optional[RunnableConfig] = None
    ) -> Output:
        key = input["key"]
        actual_input = input["input"]
        if key not in self.runnables:
            raise ValueError(f"No runnable associated with key '{key}'")
        runnable = self.runnables[key]
        return runnable.invoke(actual_input, config)
[docs]    async def ainvoke(
        self,
        input: RouterInput,
        config: Optional[RunnableConfig] = None,
        **kwargs: Optional[Any],
    ) -> Output:
        key = input["key"]
        actual_input = input["input"]
key = input["key"]
        actual_input = input["input"]
        if key not in self.runnables:
            raise ValueError(f"No runnable associated with key '{key}'")
        runnable = self.runnables[key]
        return await runnable.ainvoke(actual_input, config)
[docs]    def batch(
        self,
        inputs: List[RouterInput],
        config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None,
        *,
        return_exceptions: bool = False,
        **kwargs: Optional[Any],
    ) -> List[Output]:
        if not inputs:
            return []
        keys = [input["key"] for input in inputs]
        actual_inputs = [input["input"] for input in inputs]
        if any(key not in self.runnables for key in keys):
            raise ValueError("One or more keys do not have a corresponding runnable")
        def invoke(
            runnable: Runnable, input: Input, config: RunnableConfig
        ) -> Union[Output, Exception]:
            if return_exceptions:
                try:
                    return runnable.invoke(input, config, **kwargs)
                except Exception as e:
                    return e
            else:
                return runnable.invoke(input, config, **kwargs)
        runnables = [self.runnables[key] for key in keys]
        configs = get_config_list(config, len(inputs))
        with get_executor_for_config(configs[0]) as executor:
            return cast(
                List[Output],
                list(executor.map(invoke, runnables, actual_inputs, configs)),
            )
[docs]    async def abatch(
        self,
        inputs: List[RouterInput],
self,
        inputs: List[RouterInput],
        config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None,
        *,
        return_exceptions: bool = False,
        **kwargs: Optional[Any],
    ) -> List[Output]:
        if not inputs:
            return []
        keys = [input["key"] for input in inputs]
        actual_inputs = [input["input"] for input in inputs]
        if any(key not in self.runnables for key in keys):
            raise ValueError("One or more keys do not have a corresponding runnable")
        async def ainvoke(
            runnable: Runnable, input: Input, config: RunnableConfig
        ) -> Union[Output, Exception]:
            if return_exceptions:
                try:
                    return await runnable.ainvoke(input, config, **kwargs)
                except Exception as e:
                    return e
            else:
                return await runnable.ainvoke(input, config, **kwargs)
        runnables = [self.runnables[key] for key in keys]
        configs = get_config_list(config, len(inputs))
        return await gather_with_concurrency(
            configs[0].get("max_concurrency"),
            *(
                ainvoke(runnable, input, config)
                for runnable, input, config in zip(runnables, actual_inputs, configs)
            ),
        )
[docs]    def stream(
        self,
        input: RouterInput,
        config: Optional[RunnableConfig] = None,
        **kwargs: Optional[Any],
    ) -> Iterator[Output]:
        key = input["key"]
        actual_input = input["input"]
        if key not in self.runnables:
            raise ValueError(f"No runnable associated with key '{key}'")
raise ValueError(f"No runnable associated with key '{key}'")
        runnable = self.runnables[key]
        yield from runnable.stream(actual_input, config)
[docs]    async def astream(
        self,
        input: RouterInput,
        config: Optional[RunnableConfig] = None,
        **kwargs: Optional[Any],
    ) -> AsyncIterator[Output]:
        key = input["key"]
        actual_input = input["input"]
        if key not in self.runnables:
            raise ValueError(f"No runnable associated with key '{key}'")
        runnable = self.runnables[key]
        async for output in runnable.astream(actual_input, config):
            yield output
Source code for langchain_core.runnables.retry
from typing import (
    TYPE_CHECKING,
    Any,
    Dict,
    List,
    Optional,
    Tuple,
    Type,
    TypeVar,
    Union,
    cast,
)
from tenacity import (
    AsyncRetrying,
    RetryCallState,
    RetryError,
    Retrying,
    retry_if_exception_type,
    stop_after_attempt,
    wait_exponential_jitter,
)
from langchain_core.runnables.base import Input, Output, RunnableBindingBase
from langchain_core.runnables.config import RunnableConfig, patch_config
if TYPE_CHECKING:
    from langchain_core.callbacks.manager import (
        AsyncCallbackManagerForChainRun,
        CallbackManagerForChainRun,
    )
    T = TypeVar("T", CallbackManagerForChainRun, AsyncCallbackManagerForChainRun)
U = TypeVar("U")
[docs]class RunnableRetry(RunnableBindingBase[Input, Output]):
    """Retry a Runnable if it fails.
    RunnableRetry can be used to add retry logic to any object
    that subclasses the base Runnable.
    Such retries are especially useful for network calls that may fail
    due to transient errors.
    The RunnableRetry is implemented as a RunnableBinding. The easiest
    way to use it is through the `.with_retry()` method on all Runnables.
    Example:
    Here's an example that uses a RunnableLambda to raise an exception
        .. code-block:: python
            import time
            def foo(input) -> None:
                '''Fake function that raises an exception.'''
                raise ValueError(f"Invoking foo failed. At time {time.time()}")
            runnable = RunnableLambda(foo)
            runnable_with_retries = runnable.with_retry(
runnable = RunnableLambda(foo)
            runnable_with_retries = runnable.with_retry(
                retry_if_exception_type=(ValueError,), # Retry only on ValueError
                wait_exponential_jitter=True, # Add jitter to the exponential backoff
                stop_after_attempt=2, # Try twice
            )
            # The method invocation above is equivalent to the longer form below:
            runnable_with_retries = RunnableRetry(
                bound=runnable,
                retry_exception_types=(ValueError,),
                max_attempt_number=2,
                wait_exponential_jitter=True
            )
    This logic can be used to retry any Runnable, including a chain of Runnables,
    but in general it's best practice to keep the scope of the retry as small as
    possible. For example, if you have a chain of Runnables, you should only retry
    the Runnable that is likely to fail, not the entire chain.
    Example:
        .. code-block:: python
            from langchain_core.chat_models import ChatOpenAI
            from langchain_core.prompts import PromptTemplate
            template = PromptTemplate.from_template("tell me a joke about {topic}.")
            model = ChatOpenAI(temperature=0.5)
            # Good
            chain = template | model.with_retry()
            # Bad
            chain = template | model
            retryable_chain = chain.with_retry()
    """
    retry_exception_types: Tuple[Type[BaseException], ...] = (Exception,)
    """The exception types to retry on. By default all exceptions are retried.
    
    In general you should only retry on exceptions that are likely to be
    transient, such as network errors.
    
    Good exceptions to retry are all server errors (5xx) and selected client
Good exceptions to retry are all server errors (5xx) and selected client
    errors (4xx) such as 429 Too Many Requests.
    """
    wait_exponential_jitter: bool = True
    """Whether to add jitter to the exponential backoff."""
    max_attempt_number: int = 3
    """The maximum number of attempts to retry the runnable."""
[docs]    @classmethod
    def get_lc_namespace(cls) -> List[str]:
        """Get the namespace of the langchain object."""
        return ["langchain", "schema", "runnable"]
    @property
    def _kwargs_retrying(self) -> Dict[str, Any]:
        kwargs: Dict[str, Any] = dict()
        if self.max_attempt_number:
            kwargs["stop"] = stop_after_attempt(self.max_attempt_number)
        if self.wait_exponential_jitter:
            kwargs["wait"] = wait_exponential_jitter()
        if self.retry_exception_types:
            kwargs["retry"] = retry_if_exception_type(self.retry_exception_types)
        return kwargs
    def _sync_retrying(self, **kwargs: Any) -> Retrying:
        return Retrying(**self._kwargs_retrying, **kwargs)
    def _async_retrying(self, **kwargs: Any) -> AsyncRetrying:
        return AsyncRetrying(**self._kwargs_retrying, **kwargs)
    def _patch_config(
        self,
        config: RunnableConfig,
        run_manager: "T",
        retry_state: RetryCallState,
    ) -> RunnableConfig:
        attempt = retry_state.attempt_number
        tag = "retry:attempt:{}".format(attempt) if attempt > 1 else None
        return patch_config(config, callbacks=run_manager.get_child(tag))
    def _patch_config_list(