1"""Base classes and utilities for `Runnable` objects."""23from __future__ import annotations45import asyncio6import collections7import contextlib8import functools9import inspect10import threading11from abc import ABC, abstractmethod12from collections.abc import (13 AsyncGenerator,14 AsyncIterator,15 Awaitable,16 Callable,17 Coroutine,18 Iterator,19 Mapping,20 Sequence,21)22from concurrent.futures import FIRST_COMPLETED, wait23from functools import wraps24from itertools import tee25from operator import itemgetter26from types import GenericAlias27from typing import (28 TYPE_CHECKING,29 Any,30 Generic,31 Literal,32 Protocol,33 TypeVar,34 cast,35 get_args,36 get_type_hints,37 overload,38)3940from pydantic import BaseModel, ConfigDict, Field, RootModel41from pydantic.fields import FieldInfo42from typing_extensions import override4344from langchain_core._api import beta_decorator45from langchain_core._api.deprecation import warn_deprecated46from langchain_core.callbacks.manager import AsyncCallbackManager, CallbackManager47from langchain_core.load.serializable import (48 Serializable,49 SerializedConstructor,50 SerializedNotImplemented,51)52from langchain_core.runnables.config import (53 RunnableConfig,54 acall_func_with_variable_args,55 call_func_with_variable_args,56 ensure_config,57 get_async_callback_manager_for_config,58 get_callback_manager_for_config,59 get_config_list,60 get_executor_for_config,61 merge_configs,62 patch_config,63 run_in_executor,64 set_config_context,65)66from langchain_core.runnables.utils import (67 AddableDict,68 AnyConfigurableField,69 ConfigurableField,70 ConfigurableFieldSpec,71 Input,72 Output,73 accepts_config,74 accepts_run_manager,75 coro_with_context,76 gated_coro,77 gather_with_concurrency,78 get_function_first_arg_dict_keys,79 get_function_nonlocals,80 get_lambda_source,81 get_unique_config_specs,82 indent_lines_after_first,83 is_async_callable,84 is_async_generator,85)86from langchain_core.tracers._streaming import _StreamingCallbackHandler87from langchain_core.tracers.event_stream import (88 _astream_events_implementation_v1,89 _astream_events_implementation_v2,90)91from langchain_core.tracers.log_stream import (92 LogStreamCallbackHandler,93 _astream_log_implementation,94)95from langchain_core.tracers.root_listeners import (96 AsyncRootListenersTracer,97 RootListenersTracer,98)99from langchain_core.utils._signature import _signature_parameters100from langchain_core.utils.aiter import aclosing, atee101from langchain_core.utils.iter import safetee102from langchain_core.utils.pydantic import (103 TypeBaseModel,104 create_model_v2,105 get_fields,106 model_json_schema,107)108109if TYPE_CHECKING:110 from pydantic.v1.fields import ModelField111112 from langchain_core.callbacks.manager import (113 AsyncCallbackManagerForChainRun,114 CallbackManagerForChainRun,115 )116 from langchain_core.prompts.base import BasePromptTemplate117 from langchain_core.runnables.fallbacks import (118 RunnableWithFallbacks as RunnableWithFallbacksT,119 )120 from langchain_core.runnables.graph import Graph121 from langchain_core.runnables.retry import ExponentialJitterParams122 from langchain_core.runnables.schema import StreamEvent123 from langchain_core.tools import BaseTool124 from langchain_core.tracers.log_stream import RunLog, RunLogPatch125 from langchain_core.tracers.root_listeners import AsyncListener126 from langchain_core.tracers.schemas import Run127128129Other = TypeVar("Other")130131_RUNNABLE_GENERIC_NUM_ARGS = 2 # Input and Output132133134class Runnable(ABC, Generic[Input, Output]):135 """A unit of work that can be invoked, batched, streamed, transformed and composed.136137 Key Methods138 ===========139140 - `invoke`/`ainvoke`: Transforms a single input into an output.141 - `batch`/`abatch`: Efficiently transforms multiple inputs into outputs.142 - `stream`/`astream`: Streams output from a single input as it's produced.143 - `astream_log`: Streams output and selected intermediate results from an144 input.145146 Built-in optimizations:147148 - **Batch**: By default, batch runs invoke() in parallel using a thread pool149 executor. Override to optimize batching.150151 - **Async**: Methods with `'a'` prefix are asynchronous. By default, they execute152 the sync counterpart using asyncio's thread pool.153 Override for native async.154155 All methods accept an optional config argument, which can be used to configure156 execution, add tags and metadata for tracing and debugging etc.157158 Runnables expose schematic information about their input, output and config via159 the `input_schema` property, the `output_schema` property and `config_schema`160 method.161162 Composition163 ===========164165 Runnable objects can be composed together to create chains in a declarative way.166167 Any chain constructed this way will automatically have sync, async, batch, and168 streaming support.169170 The main composition primitives are `RunnableSequence` and `RunnableParallel`.171172 **`RunnableSequence`** invokes a series of runnables sequentially, with173 one Runnable's output serving as the next's input. Construct using174 the `|` operator or by passing a list of runnables to `RunnableSequence`.175176 **`RunnableParallel`** invokes runnables concurrently, providing the same input177 to each. Construct it using a dict literal within a sequence or by passing a178 dict to `RunnableParallel`.179180181 For example,182183 ```python184 from langchain_core.runnables import RunnableLambda185186 # A RunnableSequence constructed using the `|` operator187 sequence = RunnableLambda(lambda x: x + 1) | RunnableLambda(lambda x: x * 2)188 sequence.invoke(1) # 4189 sequence.batch([1, 2, 3]) # [4, 6, 8]190191192 # A sequence that contains a RunnableParallel constructed using a dict literal193 sequence = RunnableLambda(lambda x: x + 1) | {194 "mul_2": RunnableLambda(lambda x: x * 2),195 "mul_5": RunnableLambda(lambda x: x * 5),196 }197 sequence.invoke(1) # {'mul_2': 4, 'mul_5': 10}198 ```199200 Standard Methods201 ================202203 All `Runnable`s expose additional methods that can be used to modify their204 behavior (e.g., add a retry policy, add lifecycle listeners, make them205 configurable, etc.).206207 These methods will work on any `Runnable`, including `Runnable` chains208 constructed by composing other `Runnable`s.209 See the individual methods for details.210211 For example,212213 ```python214 from langchain_core.runnables import RunnableLambda215216 import random217218 def add_one(x: int) -> int:219 return x + 1220221222 def buggy_double(y: int) -> int:223 \"\"\"Buggy code that will fail 70% of the time\"\"\"224 if random.random() > 0.3:225 print('This code failed, and will probably be retried!') # noqa: T201226 raise ValueError('Triggered buggy code')227 return y * 2228229 sequence = (230 RunnableLambda(add_one) |231 RunnableLambda(buggy_double).with_retry( # Retry on failure232 stop_after_attempt=10,233 wait_exponential_jitter=False234 )235 )236237 print(sequence.input_schema.model_json_schema()) # Show inferred input schema238 print(sequence.output_schema.model_json_schema()) # Show inferred output schema239 print(sequence.invoke(2)) # invoke the sequence (note the retry above!!)240 ```241242 Debugging and tracing243 =====================244245 As the chains get longer, it can be useful to be able to see intermediate results246 to debug and trace the chain.247248 You can set the global debug flag to True to enable debug output for all chains:249250 ```python251 from langchain_core.globals import set_debug252253 set_debug(True)254 ```255256 Alternatively, you can pass existing or custom callbacks to any given chain:257258 ```python259 from langchain_core.tracers import ConsoleCallbackHandler260261 chain.invoke(..., config={"callbacks": [ConsoleCallbackHandler()]})262 ```263264 For a UI (and much more) checkout [LangSmith](https://docs.langchain.com/langsmith/home).265266 """267268 name: str | None269 """The name of the `Runnable`. Used for debugging and tracing."""270271 def get_name(self, suffix: str | None = None, *, name: str | None = None) -> str:272 """Get the name of the `Runnable`.273274 Args:275 suffix: An optional suffix to append to the name.276 name: An optional name to use instead of the `Runnable`'s name.277278 Returns:279 The name of the `Runnable`.280 """281 if name:282 name_ = name283 elif hasattr(self, "name") and self.name:284 name_ = self.name285 else:286 # Here we handle a case where the runnable subclass is also a pydantic287 # model.288 cls = self.__class__289 # Then it's a pydantic sub-class, and we have to check290 # whether it's a generic, and if so recover the original name.291 if (292 hasattr(293 cls,294 "__pydantic_generic_metadata__",295 )296 and "origin" in cls.__pydantic_generic_metadata__297 and cls.__pydantic_generic_metadata__["origin"] is not None298 ):299 name_ = cls.__pydantic_generic_metadata__["origin"].__name__300 else:301 name_ = cls.__name__302303 if suffix:304 if name_[0].isupper():305 return name_ + suffix.title()306 return name_ + "_" + suffix.lower()307 return name_308309 @property310 def InputType(self) -> type[Input]: # noqa: N802311 """Input type.312313 The type of input this `Runnable` accepts specified as a type annotation.314315 Raises:316 TypeError: If the input type cannot be inferred.317 """318 # First loop through all parent classes and if any of them is319 # a Pydantic model, we will pick up the generic parameterization320 # from that model via the __pydantic_generic_metadata__ attribute.321 for base in self.__class__.mro():322 if hasattr(base, "__pydantic_generic_metadata__"):323 metadata = base.__pydantic_generic_metadata__324 if (325 "args" in metadata326 and len(metadata["args"]) == _RUNNABLE_GENERIC_NUM_ARGS327 ):328 return cast("type[Input]", metadata["args"][0])329330 # If we didn't find a Pydantic model in the parent classes,331 # then loop through __orig_bases__. This corresponds to332 # Runnables that are not pydantic models.333 for cls in self.__class__.__orig_bases__: # type: ignore[attr-defined]334 type_args = get_args(cls)335 if type_args and len(type_args) == _RUNNABLE_GENERIC_NUM_ARGS:336 return cast("type[Input]", type_args[0])337338 msg = (339 f"Runnable {self.get_name()} doesn't have an inferable InputType. "340 "Override the InputType property to specify the input type."341 )342 raise TypeError(msg)343344 @property345 def OutputType(self) -> type[Output]: # noqa: N802346 """Output Type.347348 The type of output this `Runnable` produces specified as a type annotation.349350 Raises:351 TypeError: If the output type cannot be inferred.352 """353 # First loop through bases -- this will help generic354 # any pydantic models.355 for base in self.__class__.mro():356 if hasattr(base, "__pydantic_generic_metadata__"):357 metadata = base.__pydantic_generic_metadata__358 if (359 "args" in metadata360 and len(metadata["args"]) == _RUNNABLE_GENERIC_NUM_ARGS361 ):362 return cast("type[Output]", metadata["args"][1])363364 for cls in self.__class__.__orig_bases__: # type: ignore[attr-defined]365 type_args = get_args(cls)366 if type_args and len(type_args) == _RUNNABLE_GENERIC_NUM_ARGS:367 return cast("type[Output]", type_args[1])368369 msg = (370 f"Runnable {self.get_name()} doesn't have an inferable OutputType. "371 "Override the OutputType property to specify the output type."372 )373 raise TypeError(msg)374375 @property376 def input_schema(self) -> TypeBaseModel:377 """The type of input this `Runnable` accepts specified as a Pydantic model."""378 return self.get_input_schema()379380 def get_input_schema(381 self,382 config: RunnableConfig | None = None,383 ) -> TypeBaseModel:384 """Get a Pydantic model that can be used to validate input to the `Runnable`.385386 `Runnable` objects that leverage the `configurable_fields` and387 `configurable_alternatives` methods will have a dynamic input schema that388 depends on which configuration the `Runnable` is invoked with.389390 This method allows to get an input schema for a specific configuration.391392 Args:393 config: A config to use when generating the schema.394395 Returns:396 A Pydantic model that can be used to validate input.397 """398 _ = config399 root_type = self.InputType400401 if (402 inspect.isclass(root_type)403 and not isinstance(root_type, GenericAlias)404 and issubclass(root_type, BaseModel)405 ):406 return root_type407408 return create_model_v2(409 self.get_name("Input"),410 root=root_type,411 # create model needs access to appropriate type annotations to be412 # able to construct the Pydantic model.413 # When we create the model, we pass information about the namespace414 # where the model is being created, so the type annotations can415 # be resolved correctly as well.416 # self.__class__.__module__ handles the case when the Runnable is417 # being sub-classed in a different module.418 module_name=self.__class__.__module__,419 )420421 def get_input_jsonschema(422 self, config: RunnableConfig | None = None423 ) -> dict[str, Any]:424 """Get a JSON schema that represents the input to the `Runnable`.425426 Args:427 config: A config to use when generating the schema.428429 Returns:430 A JSON schema that represents the input to the `Runnable`.431432 Example:433 ```python434 from langchain_core.runnables import RunnableLambda435436437 def add_one(x: int) -> int:438 return x + 1439440441 runnable = RunnableLambda(add_one)442443 print(runnable.get_input_jsonschema())444 ```445446 !!! version-added "Added in `langchain-core` 0.3.0"447448 """449 return model_json_schema(self.get_input_schema(config))450451 @property452 def output_schema(self) -> TypeBaseModel:453 """Output schema.454455 The type of output this `Runnable` produces specified as a Pydantic model.456 """457 return self.get_output_schema()458459 def get_output_schema(460 self,461 config: RunnableConfig | None = None,462 ) -> TypeBaseModel:463 """Get a Pydantic model that can be used to validate output to the `Runnable`.464465 `Runnable` objects that leverage the `configurable_fields` and466 `configurable_alternatives` methods will have a dynamic output schema that467 depends on which configuration the `Runnable` is invoked with.468469 This method allows to get an output schema for a specific configuration.470471 Args:472 config: A config to use when generating the schema.473474 Returns:475 A Pydantic model that can be used to validate output.476 """477 _ = config478 root_type = self.OutputType479480 if (481 inspect.isclass(root_type)482 and not isinstance(root_type, GenericAlias)483 and issubclass(root_type, BaseModel)484 ):485 return root_type486487 return create_model_v2(488 self.get_name("Output"),489 root=root_type,490 # create model needs access to appropriate type annotations to be491 # able to construct the Pydantic model.492 # When we create the model, we pass information about the namespace493 # where the model is being created, so the type annotations can494 # be resolved correctly as well.495 # self.__class__.__module__ handles the case when the Runnable is496 # being sub-classed in a different module.497 module_name=self.__class__.__module__,498 )499500 def get_output_jsonschema(501 self, config: RunnableConfig | None = None502 ) -> dict[str, Any]:503 """Get a JSON schema that represents the output of the `Runnable`.504505 Args:506 config: A config to use when generating the schema.507508 Returns:509 A JSON schema that represents the output of the `Runnable`.510511 Example:512 ```python513 from langchain_core.runnables import RunnableLambda514515516 def add_one(x: int) -> int:517 return x + 1518519520 runnable = RunnableLambda(add_one)521522 print(runnable.get_output_jsonschema())523 ```524525 !!! version-added "Added in `langchain-core` 0.3.0"526527 """528 return model_json_schema(self.get_output_schema(config))529530 @property531 def config_specs(self) -> list[ConfigurableFieldSpec]:532 """List configurable fields for this `Runnable`."""533 return []534535 def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]:536 """The type of config this `Runnable` accepts specified as a Pydantic model.537538 To mark a field as configurable, see the `configurable_fields`539 and `configurable_alternatives` methods.540541 Args:542 include: A list of fields to include in the config schema.543544 Returns:545 A Pydantic model that can be used to validate config.546547 """548 include = include or []549 config_specs = self.config_specs550 configurable = (551 create_model_v2(552 "Configurable",553 field_definitions={554 spec.id: (555 spec.annotation,556 Field(557 spec.default, title=spec.name, description=spec.description558 ),559 )560 for spec in config_specs561 },562 )563 if config_specs564 else None565 )566567 # Many need to create a typed dict instead to implement NotRequired!568 all_fields = {569 **({"configurable": (configurable, None)} if configurable else {}),570 **{571 field_name: (field_type, None)572 for field_name, field_type in get_type_hints(RunnableConfig).items()573 if field_name in [i for i in include if i != "configurable"]574 },575 }576 return create_model_v2(self.get_name("Config"), field_definitions=all_fields)577578 def get_config_jsonschema(579 self, *, include: Sequence[str] | None = None580 ) -> dict[str, Any]:581 """Get a JSON schema that represents the config of the `Runnable`.582583 Args:584 include: A list of fields to include in the config schema.585586 Returns:587 A JSON schema that represents the config of the `Runnable`.588589 !!! version-added "Added in `langchain-core` 0.3.0"590591 """592 return self.config_schema(include=include).model_json_schema()593594 def get_graph(self, config: RunnableConfig | None = None) -> Graph:595 """Return a graph representation of this `Runnable`."""596 # Import locally to prevent circular import597 from langchain_core.runnables.graph import Graph # noqa: PLC0415598599 graph = Graph()600 try:601 input_node = graph.add_node(self.get_input_schema(config))602 except TypeError:603 input_node = graph.add_node(create_model_v2(self.get_name("Input")))604 runnable_node = graph.add_node(605 self, metadata=config.get("metadata") if config else None606 )607 try:608 output_node = graph.add_node(self.get_output_schema(config))609 except TypeError:610 output_node = graph.add_node(create_model_v2(self.get_name("Output")))611 graph.add_edge(input_node, runnable_node)612 graph.add_edge(runnable_node, output_node)613 return graph614615 def get_prompts(616 self, config: RunnableConfig | None = None617 ) -> list[BasePromptTemplate[Any]]:618 """Return a list of prompts used by this `Runnable`."""619 # Import locally to prevent circular import620 from langchain_core.prompts.base import BasePromptTemplate # noqa: PLC0415621622 return [623 node.data624 for node in self.get_graph(config=config).nodes.values()625 if isinstance(node.data, BasePromptTemplate)626 ]627628 @overload629 def __or__(630 self, other: Mapping[str, Any]631 ) -> RunnableSerializable[Input, dict[str, Any]]: ...632633 @overload634 def __or__(635 self,636 other: Callable[[Output], Runnable[Output, Other]]637 | Callable[[Output], Awaitable[Runnable[Output, Other]]],638 ) -> RunnableSerializable[Input, Other]: ...639640 @overload641 def __or__(642 self,643 other: Runnable[Output, Other]644 | Callable[[Iterator[Output]], Iterator[Other]]645 | Callable[[AsyncIterator[Output]], AsyncIterator[Other]]646 | Callable[[Output], Other],647 ) -> RunnableSerializable[Input, Other]: ...648649 def __or__(650 self,651 other: Runnable[Output, Other]652 | Callable[[Iterator[Output]], Iterator[Other]]653 | Callable[[AsyncIterator[Output]], AsyncIterator[Other]]654 | Callable[[Output], Other]655 | Mapping[str, Runnable[Output, Any] | Callable[[Output], Any] | Any],656 ) -> RunnableSerializable[Input, Any]:657 """Runnable "or" operator.658659 Compose this `Runnable` with another object to create a660 `RunnableSequence`.661662 Args:663 other: Another `Runnable` or a `Runnable`-like object.664665 Returns:666 A new `Runnable`.667668 Raises:669 TypeError: If `other` cannot be coerced to a `Runnable`. This method does670 not return `NotImplemented`, to avoid delegating LCEL composition to671 unrelated reflected `|` implementations. To interoperate, implement672 `Runnable`, make the object callable, or define `__or__` on the left.673 """674 return RunnableSequence(self, coerce_to_runnable(other))675676 @overload677 def __ror__(678 self,679 other: Mapping[str, Any],680 ) -> RunnableSerializable[Any, Output]: ...681682 @overload683 def __ror__(684 self,685 other: Callable[[Other], Runnable[Other, Input]]686 | Callable[[Other], Awaitable[Runnable[Other, Input]]],687 ) -> RunnableSerializable[Other, Output]: ...688689 @overload690 def __ror__(691 self,692 other: Runnable[Other, Input]693 | Callable[[Iterator[Other]], Iterator[Input]]694 | Callable[[AsyncIterator[Other]], AsyncIterator[Input]]695 | Callable[[Other], Input],696 ) -> RunnableSerializable[Other, Output]: ...697698 def __ror__(699 self,700 other: Runnable[Other, Input]701 | Callable[[Iterator[Other]], Iterator[Input]]702 | Callable[[AsyncIterator[Other]], AsyncIterator[Input]]703 | Callable[[Other], Any]704 | Mapping[str, Runnable[Other, Input] | Callable[[Other], Any] | Any],705 ) -> RunnableSerializable[Any, Output]:706 """Runnable "reverse-or" operator.707708 Compose this `Runnable` with another object to create a709 `RunnableSequence`.710711 Args:712 other: Another `Runnable` or a `Runnable`-like object.713714 Returns:715 A new `Runnable`.716717 Raises:718 TypeError: If `other` cannot be coerced to a `Runnable`. This method does719 not return `NotImplemented`, to avoid delegating LCEL composition to720 unrelated reflected `|` implementations. To interoperate, implement721 `Runnable`, make the object callable, or define `__or__` on the left.722 """723 return RunnableSequence(coerce_to_runnable(other), self)724725 def pipe(726 self,727 *others: Runnable[Any, Other] | Callable[[Any], Other],728 name: str | None = None,729 ) -> RunnableSerializable[Input, Other]:730 """Pipe `Runnable` objects.731732 Compose this `Runnable` with `Runnable`-like objects to make a733 `RunnableSequence`.734735 Equivalent to `RunnableSequence(self, *others)` or `self | others[0] | ...`736737 Example:738 ```python739 from langchain_core.runnables import RunnableLambda740741742 def add_one(x: int) -> int:743 return x + 1744745746 def mul_two(x: int) -> int:747 return x * 2748749750 runnable_1 = RunnableLambda(add_one)751 runnable_2 = RunnableLambda(mul_two)752 sequence = runnable_1.pipe(runnable_2)753 # Or equivalently:754 # sequence = runnable_1 | runnable_2755 # sequence = RunnableSequence(first=runnable_1, last=runnable_2)756 sequence.invoke(1)757 await sequence.ainvoke(1)758 # -> 4759760 sequence.batch([1, 2, 3])761 await sequence.abatch([1, 2, 3])762 # -> [4, 6, 8]763 ```764765 Args:766 *others: Other `Runnable` or `Runnable`-like objects to compose767 name: An optional name for the resulting `RunnableSequence`.768769 Returns:770 A new `Runnable`.771 """772 return RunnableSequence(self, *others, name=name)773774 def pick(self, keys: str | list[str]) -> RunnableSerializable[Any, Any]:775 """Pick keys from the output `dict` of this `Runnable`.776777 !!! example "Pick a single key"778779 ```python780 import json781782 from langchain_core.runnables import RunnableLambda, RunnableMap783784 as_str = RunnableLambda(str)785 as_json = RunnableLambda(json.loads)786 chain = RunnableMap(str=as_str, json=as_json)787788 chain.invoke("[1, 2, 3]")789 # -> {"str": "[1, 2, 3]", "json": [1, 2, 3]}790791 json_only_chain = chain.pick("json")792 json_only_chain.invoke("[1, 2, 3]")793 # -> [1, 2, 3]794 ```795796 !!! example "Pick a list of keys"797798 ```python799 from typing import Any800801 import json802803 from langchain_core.runnables import RunnableLambda, RunnableMap804805 as_str = RunnableLambda(str)806 as_json = RunnableLambda(json.loads)807808809 def as_bytes(x: Any) -> bytes:810 return bytes(x, "utf-8")811812813 chain = RunnableMap(814 str=as_str, json=as_json, bytes=RunnableLambda(as_bytes)815 )816817 chain.invoke("[1, 2, 3]")818 # -> {"str": "[1, 2, 3]", "json": [1, 2, 3], "bytes": b"[1, 2, 3]"}819820 json_and_bytes_chain = chain.pick(["json", "bytes"])821 json_and_bytes_chain.invoke("[1, 2, 3]")822 # -> {"json": [1, 2, 3], "bytes": b"[1, 2, 3]"}823 ```824825 Args:826 keys: A key or list of keys to pick from the output dict.827828 Returns:829 a new `Runnable`.830831 """832 # Import locally to prevent circular import833 from langchain_core.runnables.passthrough import RunnablePick # noqa: PLC0415834835 return self | RunnablePick(keys) # type: ignore[operator, no-any-return]836837 def assign(838 self,839 **kwargs: Runnable[dict[str, Any], Any]840 | Callable[[dict[str, Any]], Any]841 | Mapping[str, Runnable[dict[str, Any], Any] | Callable[[dict[str, Any]], Any]],842 ) -> RunnableSerializable[Any, Any]:843 """Assigns new fields to the `dict` output of this `Runnable`.844845 ```python846 from langchain_core.language_models.fake import FakeStreamingListLLM847 from langchain_core.output_parsers import StrOutputParser848 from langchain_core.prompts import SystemMessagePromptTemplate849 from langchain_core.runnables import Runnable850 from operator import itemgetter851852 prompt = (853 SystemMessagePromptTemplate.from_template("You are a nice assistant.")854 + "{question}"855 )856 model = FakeStreamingListLLM(responses=["foo-lish"])857858 chain: Runnable = prompt | model | {"str": StrOutputParser()}859860 chain_with_assign = chain.assign(hello=itemgetter("str") | model)861862 print(chain_with_assign.input_schema.model_json_schema())863 # {'title': 'PromptInput', 'type': 'object', 'properties':864 {'question': {'title': 'Question', 'type': 'string'}}}865 print(chain_with_assign.output_schema.model_json_schema())866 # {'title': 'RunnableSequenceOutput', 'type': 'object', 'properties':867 {'str': {'title': 'Str',868 'type': 'string'}, 'hello': {'title': 'Hello', 'type': 'string'}}}869 ```870871 Args:872 **kwargs: A mapping of keys to `Runnable` or `Runnable`-like objects873 that will be invoked with the entire output dict of this `Runnable`.874875 Returns:876 A new `Runnable`.877878 """879 # Import locally to prevent circular import880 from langchain_core.runnables.passthrough import RunnableAssign # noqa: PLC0415881882 return self | RunnableAssign(RunnableParallel[dict[str, Any]](kwargs)) # type: ignore[operator, no-any-return]883884 """ --- Public API --- """885886 @abstractmethod887 def invoke(888 self,889 input: Input,890 config: RunnableConfig | None = None,891 **kwargs: Any,892 ) -> Output:893 """Transform a single input into an output.894895 Args:896 input: The input to the `Runnable`.897 config: A config to use when invoking the `Runnable`.898899 The config supports standard keys like `'tags'`, `'metadata'` for900 tracing purposes, `'max_concurrency'` for controlling how much work to901 do in parallel, and other keys.902903 Please refer to `RunnableConfig` for more details.904905 Returns:906 The output of the `Runnable`.907 """908909 async def ainvoke(910 self,911 input: Input,912 config: RunnableConfig | None = None,913 **kwargs: Any,914 ) -> Output:915 """Transform a single input into an output.916917 Args:918 input: The input to the `Runnable`.919 config: A config to use when invoking the `Runnable`.920921 The config supports standard keys like `'tags'`, `'metadata'` for922 tracing purposes, `'max_concurrency'` for controlling how much work to923 do in parallel, and other keys.924925 Please refer to `RunnableConfig` for more details.926927 Returns:928 The output of the `Runnable`.929 """930 return await run_in_executor(config, self.invoke, input, config, **kwargs)931932 def batch(933 self,934 inputs: list[Input],935 config: RunnableConfig | list[RunnableConfig] | None = None,936 *,937 return_exceptions: bool = False,938 **kwargs: Any | None,939 ) -> list[Output]:940 """Default implementation runs invoke in parallel using a thread pool executor.941942 The default implementation of batch works well for IO bound runnables.943944 Subclasses must override this method if they can batch more efficiently;945 e.g., if the underlying `Runnable` uses an API which supports a batch mode.946947 Args:948 inputs: A list of inputs to the `Runnable`.949 config: A config to use when invoking the `Runnable`. The config supports950 standard keys like `'tags'`, `'metadata'` for951 tracing purposes, `'max_concurrency'` for controlling how much work952 to do in parallel, and other keys.953954 Please refer to `RunnableConfig` for more details.955 return_exceptions: Whether to return exceptions instead of raising them.956 **kwargs: Additional keyword arguments to pass to the `Runnable`.957958 Returns:959 A list of outputs from the `Runnable`.960 """961 if not inputs:962 return []963964 configs = get_config_list(config, len(inputs))965966 def invoke(input_: Input, config: RunnableConfig) -> Output | Exception:967 if return_exceptions:968 try:969 return self.invoke(input_, config, **kwargs)970 except Exception as e:971 return e972 else:973 return self.invoke(input_, config, **kwargs)974975 # If there's only one input, don't bother with the executor976 if len(inputs) == 1:977 return cast("list[Output]", [invoke(inputs[0], configs[0])])978979 with get_executor_for_config(configs[0]) as executor:980 return cast("list[Output]", list(executor.map(invoke, inputs, configs)))981982 @overload983 def batch_as_completed(984 self,985 inputs: Sequence[Input],986 config: RunnableConfig | Sequence[RunnableConfig] | None = None,987 *,988 return_exceptions: Literal[False] = False,989 **kwargs: Any,990 ) -> Iterator[tuple[int, Output]]: ...991992 @overload993 def batch_as_completed(994 self,995 inputs: Sequence[Input],996 config: RunnableConfig | Sequence[RunnableConfig] | None = None,997 *,998 return_exceptions: Literal[True],999 **kwargs: Any,1000 ) -> Iterator[tuple[int, Output | Exception]]: ...10011002 def batch_as_completed(1003 self,1004 inputs: Sequence[Input],1005 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1006 *,1007 return_exceptions: bool = False,1008 **kwargs: Any | None,1009 ) -> Iterator[tuple[int, Output | Exception]]:1010 """Run `invoke` in parallel on a list of inputs.10111012 Yields results as they complete.10131014 Args:1015 inputs: A list of inputs to the `Runnable`.1016 config: A config to use when invoking the `Runnable`.10171018 The config supports standard keys like `'tags'`, `'metadata'` for1019 tracing purposes, `'max_concurrency'` for controlling how much work to1020 do in parallel, and other keys.10211022 Please refer to `RunnableConfig` for more details.1023 return_exceptions: Whether to return exceptions instead of raising them.1024 **kwargs: Additional keyword arguments to pass to the `Runnable`.10251026 Yields:1027 Tuples of the index of the input and the output from the `Runnable`.10281029 """1030 if not inputs:1031 return10321033 configs = get_config_list(config, len(inputs))10341035 def invoke(1036 i: int, input_: Input, config: RunnableConfig1037 ) -> tuple[int, Output | Exception]:1038 if return_exceptions:1039 try:1040 out: Output | Exception = self.invoke(input_, config, **kwargs)1041 except Exception as e:1042 out = e1043 else:1044 out = self.invoke(input_, config, **kwargs)10451046 return (i, out)10471048 if len(inputs) == 1:1049 yield invoke(0, inputs[0], configs[0])1050 return10511052 with get_executor_for_config(configs[0]) as executor:1053 futures = {1054 executor.submit(invoke, i, input_, config)1055 for i, (input_, config) in enumerate(zip(inputs, configs, strict=False))1056 }10571058 try:1059 while futures:1060 done, futures = wait(futures, return_when=FIRST_COMPLETED)1061 while done:1062 yield done.pop().result()1063 finally:1064 for future in futures:1065 future.cancel()10661067 async def abatch(1068 self,1069 inputs: list[Input],1070 config: RunnableConfig | list[RunnableConfig] | None = None,1071 *,1072 return_exceptions: bool = False,1073 **kwargs: Any | None,1074 ) -> list[Output]:1075 """Default implementation runs `ainvoke` in parallel using `asyncio.gather`.10761077 The default implementation of `batch` works well for IO bound runnables.10781079 Subclasses must override this method if they can batch more efficiently;1080 e.g., if the underlying `Runnable` uses an API which supports a batch mode.10811082 Args:1083 inputs: A list of inputs to the `Runnable`.1084 config: A config to use when invoking the `Runnable`.10851086 The config supports standard keys like `'tags'`, `'metadata'` for1087 tracing purposes, `'max_concurrency'` for controlling how much work to1088 do in parallel, and other keys.10891090 Please refer to `RunnableConfig` for more details.1091 return_exceptions: Whether to return exceptions instead of raising them.1092 **kwargs: Additional keyword arguments to pass to the `Runnable`.10931094 Returns:1095 A list of outputs from the `Runnable`.10961097 """1098 if not inputs:1099 return []11001101 configs = get_config_list(config, len(inputs))11021103 async def ainvoke(value: Input, config: RunnableConfig) -> Output | Exception:1104 if return_exceptions:1105 try:1106 return await self.ainvoke(value, config, **kwargs)1107 except Exception as e:1108 return e1109 else:1110 return await self.ainvoke(value, config, **kwargs)11111112 coros = map(ainvoke, inputs, configs)1113 return await gather_with_concurrency(configs[0].get("max_concurrency"), *coros)11141115 @overload1116 def abatch_as_completed(1117 self,1118 inputs: Sequence[Input],1119 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1120 *,1121 return_exceptions: Literal[False] = False,1122 **kwargs: Any | None,1123 ) -> AsyncIterator[tuple[int, Output]]: ...11241125 @overload1126 def abatch_as_completed(1127 self,1128 inputs: Sequence[Input],1129 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1130 *,1131 return_exceptions: Literal[True],1132 **kwargs: Any | None,1133 ) -> AsyncIterator[tuple[int, Output | Exception]]: ...11341135 async def abatch_as_completed(1136 self,1137 inputs: Sequence[Input],1138 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1139 *,1140 return_exceptions: bool = False,1141 **kwargs: Any | None,1142 ) -> AsyncIterator[tuple[int, Output | Exception]]:1143 """Run `ainvoke` in parallel on a list of inputs.11441145 Yields results as they complete.11461147 Args:1148 inputs: A list of inputs to the `Runnable`.1149 config: A config to use when invoking the `Runnable`.11501151 The config supports standard keys like `'tags'`, `'metadata'` for1152 tracing purposes, `'max_concurrency'` for controlling how much work to1153 do in parallel, and other keys.11541155 Please refer to `RunnableConfig` for more details.1156 return_exceptions: Whether to return exceptions instead of raising them.1157 **kwargs: Additional keyword arguments to pass to the `Runnable`.11581159 Yields:1160 A tuple of the index of the input and the output from the `Runnable`.11611162 """1163 if not inputs:1164 return11651166 configs = get_config_list(config, len(inputs))1167 # Get max_concurrency from first config, defaulting to None (unlimited)1168 max_concurrency = configs[0].get("max_concurrency") if configs else None1169 semaphore = asyncio.Semaphore(max_concurrency) if max_concurrency else None11701171 async def ainvoke_task(1172 i: int, input_: Input, config: RunnableConfig1173 ) -> tuple[int, Output | Exception]:1174 if return_exceptions:1175 try:1176 out: Output | Exception = await self.ainvoke(1177 input_, config, **kwargs1178 )1179 except Exception as e:1180 out = e1181 else:1182 out = await self.ainvoke(input_, config, **kwargs)1183 return (i, out)11841185 coros = [1186 gated_coro(semaphore, ainvoke_task(i, input_, config))1187 if semaphore1188 else ainvoke_task(i, input_, config)1189 for i, (input_, config) in enumerate(zip(inputs, configs, strict=False))1190 ]11911192 for coro in asyncio.as_completed(coros):1193 yield await coro11941195 def stream(1196 self,1197 input: Input,1198 config: RunnableConfig | None = None,1199 **kwargs: Any | None,1200 ) -> Iterator[Output]:1201 """Default implementation of `stream`, which calls `invoke`.12021203 Subclasses must override this method if they support streaming output.12041205 Args:1206 input: The input to the `Runnable`.1207 config: The config to use for the `Runnable`.1208 **kwargs: Additional keyword arguments to pass to the `Runnable`.12091210 Yields:1211 The output of the `Runnable`.12121213 """1214 yield self.invoke(input, config, **kwargs)12151216 async def astream(1217 self,1218 input: Input,1219 config: RunnableConfig | None = None,1220 **kwargs: Any | None,1221 ) -> AsyncIterator[Output]:1222 """Default implementation of `astream`, which calls `ainvoke`.12231224 Subclasses must override this method if they support streaming output.12251226 Args:1227 input: The input to the `Runnable`.1228 config: The config to use for the `Runnable`.1229 **kwargs: Additional keyword arguments to pass to the `Runnable`.12301231 Yields:1232 The output of the `Runnable`.12331234 """1235 yield await self.ainvoke(input, config, **kwargs)12361237 @overload1238 def astream_log(1239 self,1240 input: Any,1241 config: RunnableConfig | None = None,1242 *,1243 diff: Literal[True] = True,1244 with_streamed_output_list: bool = True,1245 include_names: Sequence[str] | None = None,1246 include_types: Sequence[str] | None = None,1247 include_tags: Sequence[str] | None = None,1248 exclude_names: Sequence[str] | None = None,1249 exclude_types: Sequence[str] | None = None,1250 exclude_tags: Sequence[str] | None = None,1251 **kwargs: Any,1252 ) -> AsyncIterator[RunLogPatch]: ...12531254 @overload1255 def astream_log(1256 self,1257 input: Any,1258 config: RunnableConfig | None = None,1259 *,1260 diff: Literal[False],1261 with_streamed_output_list: bool = True,1262 include_names: Sequence[str] | None = None,1263 include_types: Sequence[str] | None = None,1264 include_tags: Sequence[str] | None = None,1265 exclude_names: Sequence[str] | None = None,1266 exclude_types: Sequence[str] | None = None,1267 exclude_tags: Sequence[str] | None = None,1268 **kwargs: Any,1269 ) -> AsyncIterator[RunLog]: ...12701271 async def astream_log(1272 self,1273 input: Any,1274 config: RunnableConfig | None = None,1275 *,1276 diff: bool = True,1277 with_streamed_output_list: bool = True,1278 include_names: Sequence[str] | None = None,1279 include_types: Sequence[str] | None = None,1280 include_tags: Sequence[str] | None = None,1281 exclude_names: Sequence[str] | None = None,1282 exclude_types: Sequence[str] | None = None,1283 exclude_tags: Sequence[str] | None = None,1284 **kwargs: Any,1285 ) -> AsyncIterator[RunLogPatch] | AsyncIterator[RunLog]:1286 """Stream all output from a `Runnable`, as reported to the callback system.12871288 This includes all inner runs of LLMs, Retrievers, Tools, etc.12891290 Output is streamed as Log objects, which include a list of1291 Jsonpatch ops that describe how the state of the run has changed in each1292 step, and the final state of the run.12931294 The Jsonpatch ops can be applied in order to construct state.12951296 Args:1297 input: The input to the `Runnable`.1298 config: The config to use for the `Runnable`.1299 diff: Whether to yield diffs between each step or the current state.1300 with_streamed_output_list: Whether to yield the `streamed_output` list.1301 include_names: Only include logs with these names.1302 include_types: Only include logs with these types.1303 include_tags: Only include logs with these tags.1304 exclude_names: Exclude logs with these names.1305 exclude_types: Exclude logs with these types.1306 exclude_tags: Exclude logs with these tags.1307 **kwargs: Additional keyword arguments to pass to the `Runnable`.13081309 Yields:1310 A `RunLogPatch` or `RunLog` object.13111312 """1313 warn_deprecated(1314 since="1.3.3",1315 message=("astream_log is deprecated. Use astream instead."),1316 removal="2.0.0",1317 )1318 stream = LogStreamCallbackHandler(1319 auto_close=False,1320 include_names=include_names,1321 include_types=include_types,1322 include_tags=include_tags,1323 exclude_names=exclude_names,1324 exclude_types=exclude_types,1325 exclude_tags=exclude_tags,1326 _schema_format="original",1327 )13281329 # Mypy isn't resolving the overloads here1330 # Likely an issue b/c `self` is being passed through1331 # and it's can't map it to Runnable[Input,Output]?1332 async for item in _astream_log_implementation( # type: ignore[call-overload]1333 self,1334 input,1335 config,1336 diff=diff,1337 stream=stream,1338 with_streamed_output_list=with_streamed_output_list,1339 **kwargs,1340 ):1341 yield item13421343 @overload1344 def astream_events(1345 self,1346 input: Any,1347 config: RunnableConfig | None = None,1348 *,1349 version: Literal["v1", "v2"] = "v2",1350 include_names: Sequence[str] | None = None,1351 include_types: Sequence[str] | None = None,1352 include_tags: Sequence[str] | None = None,1353 exclude_names: Sequence[str] | None = None,1354 exclude_types: Sequence[str] | None = None,1355 exclude_tags: Sequence[str] | None = None,1356 **kwargs: Any,1357 ) -> AsyncIterator[StreamEvent]: ...13581359 @overload1360 def astream_events(1361 self,1362 input: Any,1363 config: RunnableConfig | None = None,1364 *,1365 version: Literal["v3"],1366 **kwargs: Any,1367 ) -> Awaitable[Any]: ...13681369 def astream_events(1370 self,1371 input: Any,1372 config: RunnableConfig | None = None,1373 *,1374 version: Literal["v1", "v2", "v3"] = "v2",1375 include_names: Sequence[str] | None = None,1376 include_types: Sequence[str] | None = None,1377 include_tags: Sequence[str] | None = None,1378 exclude_names: Sequence[str] | None = None,1379 exclude_types: Sequence[str] | None = None,1380 exclude_tags: Sequence[str] | None = None,1381 **kwargs: Any,1382 ) -> AsyncIterator[StreamEvent] | Awaitable[Any]:1383 """Generate a stream of events.13841385 Use to create an iterator over `StreamEvent` that provide real-time information1386 about the progress of the `Runnable`, including `StreamEvent` from intermediate1387 results.13881389 A `StreamEvent` is a dictionary with the following schema:13901391 - `event`: Event names are of the format:1392 `on_[runnable_type]_(start|stream|end)`.1393 - `name`: The name of the `Runnable` that generated the event.1394 - `run_id`: Randomly generated ID associated with the given execution of the1395 `Runnable` that emitted the event. A child `Runnable` that gets invoked as1396 part of the execution of a parent `Runnable` is assigned its own unique ID.1397 - `parent_ids`: The IDs of the parent runnables that generated the event. The1398 root `Runnable` will have an empty list. The order of the parent IDs is from1399 the root to the immediate parent. Only available for v2 version of the API.1400 The v1 version of the API will return an empty list.1401 - `tags`: The tags of the `Runnable` that generated the event.1402 - `metadata`: The metadata of the `Runnable` that generated the event.1403 - `data`: The data associated with the event. The contents of this field1404 depend on the type of event. See the table below for more details.14051406 Below is a table that illustrates some events that might be emitted by various1407 chains. Metadata fields have been omitted from the table for brevity.1408 Chain definitions have been included after the table.14091410 !!! note1411 This reference table is for the v2 version of the schema.14121413 | event | name | chunk | input | output |1414 | ---------------------- | -------------------- | ----------------------------------- | ------------------------------------------------- | --------------------------------------------------- |1415 | `on_chat_model_start` | `'[model name]'` | | `{"messages": [[SystemMessage, HumanMessage]]}` | |1416 | `on_chat_model_stream` | `'[model name]'` | `AIMessageChunk(content="hello")` | | |1417 | `on_chat_model_end` | `'[model name]'` | | `{"messages": [[SystemMessage, HumanMessage]]}` | `AIMessageChunk(content="hello world")` |1418 | `on_llm_start` | `'[model name]'` | | `{'input': 'hello'}` | |1419 | `on_llm_stream` | `'[model name]'` | `'Hello' ` | | |1420 | `on_llm_end` | `'[model name]'` | | `'Hello human!'` | |1421 | `on_chain_start` | `'format_docs'` | | | |1422 | `on_chain_stream` | `'format_docs'` | `'hello world!, goodbye world!'` | | |1423 | `on_chain_end` | `'format_docs'` | | `[Document(...)]` | `'hello world!, goodbye world!'` |1424 | `on_tool_start` | `'some_tool'` | | `{"x": 1, "y": "2"}` | |1425 | `on_tool_end` | `'some_tool'` | | | `{"x": 1, "y": "2"}` |1426 | `on_retriever_start` | `'[retriever name]'` | | `{"query": "hello"}` | |1427 | `on_retriever_end` | `'[retriever name]'` | | `{"query": "hello"}` | `[Document(...), ..]` |1428 | `on_prompt_start` | `'[template_name]'` | | `{"question": "hello"}` | |1429 | `on_prompt_end` | `'[template_name]'` | | `{"question": "hello"}` | `ChatPromptValue(messages: [SystemMessage, ...])` |14301431 In addition to the standard events, users can also dispatch custom events (see example below).14321433 Custom events will be only be surfaced with in the v2 version of the API!14341435 A custom event has following format:14361437 | Attribute | Type | Description |1438 | ----------- | ------ | --------------------------------------------------------------------------------------------------------- |1439 | `name` | `str` | A user defined name for the event. |1440 | `data` | `Any` | The data associated with the event. This can be anything, though we suggest making it JSON serializable. |14411442 Here are declarations associated with the standard events shown above:14431444 `format_docs`:14451446 ```python1447 def format_docs(docs: list[Document]) -> str:1448 '''Format the docs.'''1449 return ", ".join([doc.page_content for doc in docs])145014511452 format_docs = RunnableLambda(format_docs)1453 ```14541455 `some_tool`:14561457 ```python1458 @tool1459 def some_tool(x: int, y: str) -> dict:1460 '''Some_tool.'''1461 return {"x": x, "y": y}1462 ```14631464 `prompt`:14651466 ```python1467 template = ChatPromptTemplate.from_messages(1468 [1469 ("system", "You are Cat Agent 007"),1470 ("human", "{question}"),1471 ]1472 ).with_config({"run_name": "my_template", "tags": ["my_template"]})1473 ```14741475 !!! example14761477 ```python1478 from langchain_core.runnables import RunnableLambda147914801481 async def reverse(s: str) -> str:1482 return s[::-1]148314841485 chain = RunnableLambda(func=reverse)14861487 events = [1488 event async for event in chain.astream_events("hello", version="v2")1489 ]14901491 # Will produce the following events1492 # (run_id, and parent_ids has been omitted for brevity):1493 [1494 {1495 "data": {"input": "hello"},1496 "event": "on_chain_start",1497 "metadata": {},1498 "name": "reverse",1499 "tags": [],1500 },1501 {1502 "data": {"chunk": "olleh"},1503 "event": "on_chain_stream",1504 "metadata": {},1505 "name": "reverse",1506 "tags": [],1507 },1508 {1509 "data": {"output": "olleh"},1510 "event": "on_chain_end",1511 "metadata": {},1512 "name": "reverse",1513 "tags": [],1514 },1515 ]1516 ```15171518 ```python title="Dispatch custom event"1519 from langchain_core.callbacks.manager import (1520 adispatch_custom_event,1521 )1522 from langchain_core.runnables import RunnableLambda, RunnableConfig1523 import asyncio152415251526 async def slow_thing(some_input: str, config: RunnableConfig) -> str:1527 \"\"\"Do something that takes a long time.\"\"\"1528 await asyncio.sleep(1) # Placeholder for some slow operation1529 await adispatch_custom_event(1530 "progress_event",1531 {"message": "Finished step 1 of 3"},1532 config=config # Must be included for python < 3.101533 )1534 await asyncio.sleep(1) # Placeholder for some slow operation1535 await adispatch_custom_event(1536 "progress_event",1537 {"message": "Finished step 2 of 3"},1538 config=config # Must be included for python < 3.101539 )1540 await asyncio.sleep(1) # Placeholder for some slow operation1541 return "Done"15421543 slow_thing = RunnableLambda(slow_thing)15441545 async for event in slow_thing.astream_events("some_input", version="v2"):1546 print(event)1547 ```15481549 Args:1550 input: The input to the `Runnable`.1551 config: The config to use for the `Runnable`.1552 version: The version of the schema to use. One of `'v1'`, `'v2'`,1553 or `'v3'`.15541555 Most callers should use `'v2'` (the default), which yields1556 `StreamEvent` dicts and supports custom events.15571558 `'v3'` selects the typed, content-block-centric streaming1559 protocol and is only supported on `Runnable` subclasses that1560 implement it (currently `BaseChatModel` and1561 `langgraph.CompiledGraph`); on a generic `Runnable` it raises1562 `NotImplementedError`. The `'v3'` API is in beta and may1563 change. See the subclass override (e.g.1564 `BaseChatModel.astream_events`) for the v3 return shape.15651566 `'v1'` is retained for backwards compatibility and will be1567 deprecated in `0.4.0`. Custom events are only surfaced in1568 `'v2'` / `'v3'`.1569 include_names: Only include events from `Runnable` objects with matching names.1570 include_types: Only include events from `Runnable` objects with matching types.1571 include_tags: Only include events from `Runnable` objects with matching tags.1572 exclude_names: Exclude events from `Runnable` objects with matching names.1573 exclude_types: Exclude events from `Runnable` objects with matching types.1574 exclude_tags: Exclude events from `Runnable` objects with matching tags.1575 **kwargs: Additional keyword arguments to pass to the `Runnable`.15761577 Yields:1578 An async stream of `StreamEvent`.15791580 Raises:1581 NotImplementedError: If the version is not `'v1'`, `'v2'`, or `'v3'`, or1582 if `'v3'` is requested on a `Runnable` that does not implement the v31583 streaming protocol.15841585 """ # noqa: E5011586 if version == "v3":1587 return self._astream_events_v3_unsupported()1588 return self._astream_events_v1_v2(1589 input,1590 config=config,1591 version=version,1592 include_names=include_names,1593 include_types=include_types,1594 include_tags=include_tags,1595 exclude_names=exclude_names,1596 exclude_types=exclude_types,1597 exclude_tags=exclude_tags,1598 **kwargs,1599 )16001601 async def _astream_events_v3_unsupported(self) -> Any:1602 """Coroutine that raises when v3 isn't implemented on this Runnable.16031604 Lets the public `astream_events(version="v3")` return an awaitable1605 whose error surfaces on `await`, matching the v3 contract on1606 subclasses that do implement the protocol.1607 """1608 msg = (1609 "astream_events(version='v3') is only supported on Runnable "1610 "subclasses that implement the v3 streaming protocol "1611 "(BaseChatModel, CompiledGraph). "1612 f"Got: {type(self).__name__}"1613 )1614 raise NotImplementedError(msg)16151616 async def _astream_events_v1_v2(1617 self,1618 input: Any,1619 config: RunnableConfig | None = None,1620 *,1621 version: Literal["v1", "v2"] = "v2",1622 include_names: Sequence[str] | None = None,1623 include_types: Sequence[str] | None = None,1624 include_tags: Sequence[str] | None = None,1625 exclude_names: Sequence[str] | None = None,1626 exclude_types: Sequence[str] | None = None,1627 exclude_tags: Sequence[str] | None = None,1628 **kwargs: Any,1629 ) -> AsyncIterator[StreamEvent]:1630 if version == "v2":1631 event_stream = _astream_events_implementation_v2(1632 self,1633 input,1634 config=config,1635 include_names=include_names,1636 include_types=include_types,1637 include_tags=include_tags,1638 exclude_names=exclude_names,1639 exclude_types=exclude_types,1640 exclude_tags=exclude_tags,1641 **kwargs,1642 )1643 elif version == "v1":1644 warn_deprecated(1645 since="1.3.3",1646 message=(1647 "astream_events version='v1' is deprecated. "1648 "Use version='v2' or astream instead."1649 ),1650 removal="2.0.0",1651 )1652 # First implementation, built on top of astream_log API1653 # This implementation will be deprecated as of 0.2.01654 event_stream = _astream_events_implementation_v1(1655 self,1656 input,1657 config=config,1658 include_names=include_names,1659 include_types=include_types,1660 include_tags=include_tags,1661 exclude_names=exclude_names,1662 exclude_types=exclude_types,1663 exclude_tags=exclude_tags,1664 **kwargs,1665 )1666 else:1667 msg = f"Unsupported version: {version!r}. Expected 'v1', 'v2', or 'v3'." # type: ignore[unreachable]1668 raise NotImplementedError(msg)16691670 async with aclosing(event_stream):1671 async for event in event_stream:1672 yield event16731674 @overload1675 def stream_events(1676 self,1677 input: Any,1678 config: RunnableConfig | None = None,1679 *,1680 version: Literal["v1", "v2"] = "v2",1681 include_names: Sequence[str] | None = None,1682 include_types: Sequence[str] | None = None,1683 include_tags: Sequence[str] | None = None,1684 exclude_names: Sequence[str] | None = None,1685 exclude_types: Sequence[str] | None = None,1686 exclude_tags: Sequence[str] | None = None,1687 **kwargs: Any,1688 ) -> Iterator[StreamEvent]: ...16891690 @overload1691 def stream_events(1692 self,1693 input: Any,1694 config: RunnableConfig | None = None,1695 *,1696 version: Literal["v3"],1697 **kwargs: Any,1698 ) -> Iterator[Any]: ...16991700 def stream_events(1701 self,1702 input: Any,1703 config: RunnableConfig | None = None,1704 *,1705 version: Literal["v1", "v2", "v3"] = "v2",1706 include_names: Sequence[str] | None = None,1707 include_types: Sequence[str] | None = None,1708 include_tags: Sequence[str] | None = None,1709 exclude_names: Sequence[str] | None = None,1710 exclude_types: Sequence[str] | None = None,1711 exclude_tags: Sequence[str] | None = None,1712 **kwargs: Any,1713 ) -> Iterator[StreamEvent] | Iterator[Any]:1714 """Generate a stream of events synchronously.17151716 Synchronous counterpart to `astream_events`. For `version='v3'`, subclasses1717 that implement the v3 streaming protocol (`BaseChatModel`, `CompiledGraph`)1718 override this method. All other versions and base-class calls raise1719 `NotImplementedError`.17201721 Args:1722 input: The input to the `Runnable`.1723 config: The config to use for the `Runnable`.1724 version: The version of the schema to use. `'v3'` requires a subclass1725 that implements the v3 streaming protocol. `'v1'` and `'v2'` are not1726 supported on the sync path.1727 include_names: Only include events from `Runnable` objects with matching1728 names.1729 include_types: Only include events from `Runnable` objects with matching1730 types.1731 include_tags: Only include events from `Runnable` objects with matching1732 tags.1733 exclude_names: Exclude events from `Runnable` objects with matching names.1734 exclude_types: Exclude events from `Runnable` objects with matching types.1735 exclude_tags: Exclude events from `Runnable` objects with matching tags.1736 **kwargs: Additional keyword arguments to pass to the `Runnable`.17371738 Raises:1739 NotImplementedError: Always. Subclasses override this method for supported1740 versions.17411742 """1743 # Base impl always raises; consume args so they don't trip ARG002.1744 del input, config, include_names, include_types, include_tags1745 del exclude_names, exclude_types, exclude_tags, kwargs1746 if version == "v3":1747 msg = (1748 "stream_events(version='v3') is only supported on Runnable subclasses "1749 "that implement the v3 streaming protocol "1750 "(BaseChatModel, CompiledGraph). "1751 f"Got: {type(self).__name__}"1752 )1753 raise NotImplementedError(msg)1754 msg = (1755 f"stream_events(version={version!r}) is not supported. "1756 "Use astream_events() for v1/v2, or stream_events(version='v3') "1757 "on a supported subclass."1758 )1759 raise NotImplementedError(msg)17601761 def transform(1762 self,1763 input: Iterator[Input],1764 config: RunnableConfig | None = None,1765 **kwargs: Any | None,1766 ) -> Iterator[Output]:1767 """Transform inputs to outputs.17681769 Default implementation of transform, which buffers input and calls `astream`.17701771 Subclasses must override this method if they can start producing output while1772 input is still being generated.17731774 Args:1775 input: An iterator of inputs to the `Runnable`.1776 config: The config to use for the `Runnable`.1777 **kwargs: Additional keyword arguments to pass to the `Runnable`.17781779 Yields:1780 The output of the `Runnable`.17811782 """1783 final: Input1784 got_first_val = False17851786 for ichunk in input:1787 # The default implementation of transform is to buffer input and1788 # then call stream.1789 # It'll attempt to gather all input into a single chunk using1790 # the `+` operator.1791 # If the input is not addable, then we'll assume that we can1792 # only operate on the last chunk,1793 # and we'll iterate until we get to the last chunk.1794 if not got_first_val:1795 final = ichunk1796 got_first_val = True1797 else:1798 try:1799 final = final + ichunk # type: ignore[operator]1800 except TypeError:1801 final = ichunk18021803 if got_first_val:1804 yield from self.stream(final, config, **kwargs)18051806 async def atransform(1807 self,1808 input: AsyncIterator[Input],1809 config: RunnableConfig | None = None,1810 **kwargs: Any | None,1811 ) -> AsyncIterator[Output]:1812 """Transform inputs to outputs.18131814 Default implementation of atransform, which buffers input and calls `astream`.18151816 Subclasses must override this method if they can start producing output while1817 input is still being generated.18181819 Args:1820 input: An async iterator of inputs to the `Runnable`.1821 config: The config to use for the `Runnable`.1822 **kwargs: Additional keyword arguments to pass to the `Runnable`.18231824 Yields:1825 The output of the `Runnable`.18261827 """1828 final: Input1829 got_first_val = False18301831 async for ichunk in input:1832 # The default implementation of transform is to buffer input and1833 # then call stream.1834 # It'll attempt to gather all input into a single chunk using1835 # the `+` operator.1836 # If the input is not addable, then we'll assume that we can1837 # only operate on the last chunk,1838 # and we'll iterate until we get to the last chunk.1839 if not got_first_val:1840 final = ichunk1841 got_first_val = True1842 else:1843 try:1844 final = final + ichunk # type: ignore[operator]1845 except TypeError:1846 final = ichunk18471848 if got_first_val:1849 async for output in self.astream(final, config, **kwargs):1850 yield output18511852 def bind(self, **kwargs: Any) -> Runnable[Input, Output]:1853 """Bind arguments to a `Runnable`, returning a new `Runnable`.18541855 Useful when a `Runnable` in a chain requires an argument that is not1856 in the output of the previous `Runnable` or included in the user input.18571858 Args:1859 **kwargs: The arguments to bind to the `Runnable`.18601861 Returns:1862 A new `Runnable` with the arguments bound.18631864 Example:1865 ```python1866 from langchain_ollama import ChatOllama1867 from langchain_core.output_parsers import StrOutputParser18681869 model = ChatOllama(model="llama3.1")18701871 # Without bind1872 chain = model | StrOutputParser()18731874 chain.invoke("Repeat quoted words exactly: 'One two three four five.'")1875 # Output is 'One two three four five.'18761877 # With bind1878 chain = model.bind(stop=["three"]) | StrOutputParser()18791880 chain.invoke("Repeat quoted words exactly: 'One two three four five.'")1881 # Output is 'One two'1882 ```1883 """1884 return RunnableBinding(bound=self, kwargs=kwargs, config={})18851886 def with_config(1887 self,1888 config: RunnableConfig | None = None,1889 # Sadly Unpack is not well-supported by mypy so this will have to be untyped1890 **kwargs: Any,1891 ) -> Runnable[Input, Output]:1892 """Bind config to a `Runnable`, returning a new `Runnable`.18931894 Args:1895 config: The config to bind to the `Runnable`.1896 **kwargs: Additional keyword arguments to pass to the `Runnable`.18971898 Returns:1899 A new `Runnable` with the config bound.19001901 """1902 return RunnableBinding(1903 bound=self,1904 config=cast(1905 "RunnableConfig",1906 {**(config or {}), **kwargs},1907 ),1908 kwargs={},1909 )19101911 def with_listeners(1912 self,1913 *,1914 on_start: Callable[[Run], None]1915 | Callable[[Run, RunnableConfig], None]1916 | None = None,1917 on_end: Callable[[Run], None]1918 | Callable[[Run, RunnableConfig], None]1919 | None = None,1920 on_error: Callable[[Run], None]1921 | Callable[[Run, RunnableConfig], None]1922 | None = None,1923 ) -> Runnable[Input, Output]:1924 """Bind lifecycle listeners to a `Runnable`, returning a new `Runnable`.19251926 The Run object contains information about the run, including its `id`,1927 `type`, `input`, `output`, `error`, `start_time`, `end_time`, and1928 any tags or metadata added to the run.19291930 Args:1931 on_start: Called before the `Runnable` starts running, with the `Run`1932 object.1933 on_end: Called after the `Runnable` finishes running, with the `Run`1934 object.1935 on_error: Called if the `Runnable` throws an error, with the `Run`1936 object.19371938 Returns:1939 A new `Runnable` with the listeners bound.19401941 Example:1942 ```python1943 from langchain_core.runnables import RunnableLambda1944 from langchain_core.tracers.schemas import Run19451946 import time194719481949 def test_runnable(time_to_sleep: int):1950 time.sleep(time_to_sleep)195119521953 def fn_start(run_obj: Run):1954 print("start_time:", run_obj.start_time)195519561957 def fn_end(run_obj: Run):1958 print("end_time:", run_obj.end_time)195919601961 chain = RunnableLambda(test_runnable).with_listeners(1962 on_start=fn_start, on_end=fn_end1963 )1964 chain.invoke(2)1965 ```1966 """1967 return RunnableBinding(1968 bound=self,1969 config_factories=[1970 lambda config: {1971 "callbacks": [1972 RootListenersTracer(1973 config=config,1974 on_start=on_start,1975 on_end=on_end,1976 on_error=on_error,1977 )1978 ],1979 }1980 ],1981 )19821983 def with_alisteners(1984 self,1985 *,1986 on_start: AsyncListener | None = None,1987 on_end: AsyncListener | None = None,1988 on_error: AsyncListener | None = None,1989 ) -> Runnable[Input, Output]:1990 """Bind async lifecycle listeners to a `Runnable`.19911992 Returns a new `Runnable`.19931994 The Run object contains information about the run, including its `id`,1995 `type`, `input`, `output`, `error`, `start_time`, `end_time`, and1996 any tags or metadata added to the run.19971998 Args:1999 on_start: Called asynchronously before the `Runnable` starts running,2000 with the `Run` object.
Findings
✓ No findings reported for this file.