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.aiter import aclosing, atee100from langchain_core.utils.iter import safetee101from langchain_core.utils.pydantic import (102 TypeBaseModel,103 create_model_v2,104 get_fields,105 model_json_schema,106)107108if TYPE_CHECKING:109 from pydantic.v1.fields import ModelField110111 from langchain_core.callbacks.manager import (112 AsyncCallbackManagerForChainRun,113 CallbackManagerForChainRun,114 )115 from langchain_core.prompts.base import BasePromptTemplate116 from langchain_core.runnables.fallbacks import (117 RunnableWithFallbacks as RunnableWithFallbacksT,118 )119 from langchain_core.runnables.graph import Graph120 from langchain_core.runnables.retry import ExponentialJitterParams121 from langchain_core.runnables.schema import StreamEvent122 from langchain_core.tools import BaseTool123 from langchain_core.tracers.log_stream import RunLog, RunLogPatch124 from langchain_core.tracers.root_listeners import AsyncListener125 from langchain_core.tracers.schemas import Run126127128Other = TypeVar("Other")129130_RUNNABLE_GENERIC_NUM_ARGS = 2 # Input and Output131132133class Runnable(ABC, Generic[Input, Output]):134 """A unit of work that can be invoked, batched, streamed, transformed and composed.135136 Key Methods137 ===========138139 - `invoke`/`ainvoke`: Transforms a single input into an output.140 - `batch`/`abatch`: Efficiently transforms multiple inputs into outputs.141 - `stream`/`astream`: Streams output from a single input as it's produced.142 - `astream_log`: Streams output and selected intermediate results from an143 input.144145 Built-in optimizations:146147 - **Batch**: By default, batch runs invoke() in parallel using a thread pool148 executor. Override to optimize batching.149150 - **Async**: Methods with `'a'` prefix are asynchronous. By default, they execute151 the sync counterpart using asyncio's thread pool.152 Override for native async.153154 All methods accept an optional config argument, which can be used to configure155 execution, add tags and metadata for tracing and debugging etc.156157 Runnables expose schematic information about their input, output and config via158 the `input_schema` property, the `output_schema` property and `config_schema`159 method.160161 Composition162 ===========163164 Runnable objects can be composed together to create chains in a declarative way.165166 Any chain constructed this way will automatically have sync, async, batch, and167 streaming support.168169 The main composition primitives are `RunnableSequence` and `RunnableParallel`.170171 **`RunnableSequence`** invokes a series of runnables sequentially, with172 one Runnable's output serving as the next's input. Construct using173 the `|` operator or by passing a list of runnables to `RunnableSequence`.174175 **`RunnableParallel`** invokes runnables concurrently, providing the same input176 to each. Construct it using a dict literal within a sequence or by passing a177 dict to `RunnableParallel`.178179180 For example,181182 ```python183 from langchain_core.runnables import RunnableLambda184185 # A RunnableSequence constructed using the `|` operator186 sequence = RunnableLambda(lambda x: x + 1) | RunnableLambda(lambda x: x * 2)187 sequence.invoke(1) # 4188 sequence.batch([1, 2, 3]) # [4, 6, 8]189190191 # A sequence that contains a RunnableParallel constructed using a dict literal192 sequence = RunnableLambda(lambda x: x + 1) | {193 "mul_2": RunnableLambda(lambda x: x * 2),194 "mul_5": RunnableLambda(lambda x: x * 5),195 }196 sequence.invoke(1) # {'mul_2': 4, 'mul_5': 10}197 ```198199 Standard Methods200 ================201202 All `Runnable`s expose additional methods that can be used to modify their203 behavior (e.g., add a retry policy, add lifecycle listeners, make them204 configurable, etc.).205206 These methods will work on any `Runnable`, including `Runnable` chains207 constructed by composing other `Runnable`s.208 See the individual methods for details.209210 For example,211212 ```python213 from langchain_core.runnables import RunnableLambda214215 import random216217 def add_one(x: int) -> int:218 return x + 1219220221 def buggy_double(y: int) -> int:222 \"\"\"Buggy code that will fail 70% of the time\"\"\"223 if random.random() > 0.3:224 print('This code failed, and will probably be retried!') # noqa: T201225 raise ValueError('Triggered buggy code')226 return y * 2227228 sequence = (229 RunnableLambda(add_one) |230 RunnableLambda(buggy_double).with_retry( # Retry on failure231 stop_after_attempt=10,232 wait_exponential_jitter=False233 )234 )235236 print(sequence.input_schema.model_json_schema()) # Show inferred input schema237 print(sequence.output_schema.model_json_schema()) # Show inferred output schema238 print(sequence.invoke(2)) # invoke the sequence (note the retry above!!)239 ```240241 Debugging and tracing242 =====================243244 As the chains get longer, it can be useful to be able to see intermediate results245 to debug and trace the chain.246247 You can set the global debug flag to True to enable debug output for all chains:248249 ```python250 from langchain_core.globals import set_debug251252 set_debug(True)253 ```254255 Alternatively, you can pass existing or custom callbacks to any given chain:256257 ```python258 from langchain_core.tracers import ConsoleCallbackHandler259260 chain.invoke(..., config={"callbacks": [ConsoleCallbackHandler()]})261 ```262263 For a UI (and much more) checkout [LangSmith](https://docs.langchain.com/langsmith/home).264265 """266267 name: str | None268 """The name of the `Runnable`. Used for debugging and tracing."""269270 def get_name(self, suffix: str | None = None, *, name: str | None = None) -> str:271 """Get the name of the `Runnable`.272273 Args:274 suffix: An optional suffix to append to the name.275 name: An optional name to use instead of the `Runnable`'s name.276277 Returns:278 The name of the `Runnable`.279 """280 if name:281 name_ = name282 elif hasattr(self, "name") and self.name:283 name_ = self.name284 else:285 # Here we handle a case where the runnable subclass is also a pydantic286 # model.287 cls = self.__class__288 # Then it's a pydantic sub-class, and we have to check289 # whether it's a generic, and if so recover the original name.290 if (291 hasattr(292 cls,293 "__pydantic_generic_metadata__",294 )295 and "origin" in cls.__pydantic_generic_metadata__296 and cls.__pydantic_generic_metadata__["origin"] is not None297 ):298 name_ = cls.__pydantic_generic_metadata__["origin"].__name__299 else:300 name_ = cls.__name__301302 if suffix:303 if name_[0].isupper():304 return name_ + suffix.title()305 return name_ + "_" + suffix.lower()306 return name_307308 @property309 def InputType(self) -> type[Input]: # noqa: N802310 """Input type.311312 The type of input this `Runnable` accepts specified as a type annotation.313314 Raises:315 TypeError: If the input type cannot be inferred.316 """317 # First loop through all parent classes and if any of them is318 # a Pydantic model, we will pick up the generic parameterization319 # from that model via the __pydantic_generic_metadata__ attribute.320 for base in self.__class__.mro():321 if hasattr(base, "__pydantic_generic_metadata__"):322 metadata = base.__pydantic_generic_metadata__323 if (324 "args" in metadata325 and len(metadata["args"]) == _RUNNABLE_GENERIC_NUM_ARGS326 ):327 return cast("type[Input]", metadata["args"][0])328329 # If we didn't find a Pydantic model in the parent classes,330 # then loop through __orig_bases__. This corresponds to331 # Runnables that are not pydantic models.332 for cls in self.__class__.__orig_bases__: # type: ignore[attr-defined]333 type_args = get_args(cls)334 if type_args and len(type_args) == _RUNNABLE_GENERIC_NUM_ARGS:335 return cast("type[Input]", type_args[0])336337 msg = (338 f"Runnable {self.get_name()} doesn't have an inferable InputType. "339 "Override the InputType property to specify the input type."340 )341 raise TypeError(msg)342343 @property344 def OutputType(self) -> type[Output]: # noqa: N802345 """Output Type.346347 The type of output this `Runnable` produces specified as a type annotation.348349 Raises:350 TypeError: If the output type cannot be inferred.351 """352 # First loop through bases -- this will help generic353 # any pydantic models.354 for base in self.__class__.mro():355 if hasattr(base, "__pydantic_generic_metadata__"):356 metadata = base.__pydantic_generic_metadata__357 if (358 "args" in metadata359 and len(metadata["args"]) == _RUNNABLE_GENERIC_NUM_ARGS360 ):361 return cast("type[Output]", metadata["args"][1])362363 for cls in self.__class__.__orig_bases__: # type: ignore[attr-defined]364 type_args = get_args(cls)365 if type_args and len(type_args) == _RUNNABLE_GENERIC_NUM_ARGS:366 return cast("type[Output]", type_args[1])367368 msg = (369 f"Runnable {self.get_name()} doesn't have an inferable OutputType. "370 "Override the OutputType property to specify the output type."371 )372 raise TypeError(msg)373374 @property375 def input_schema(self) -> TypeBaseModel:376 """The type of input this `Runnable` accepts specified as a Pydantic model."""377 return self.get_input_schema()378379 def get_input_schema(380 self,381 config: RunnableConfig | None = None,382 ) -> TypeBaseModel:383 """Get a Pydantic model that can be used to validate input to the `Runnable`.384385 `Runnable` objects that leverage the `configurable_fields` and386 `configurable_alternatives` methods will have a dynamic input schema that387 depends on which configuration the `Runnable` is invoked with.388389 This method allows to get an input schema for a specific configuration.390391 Args:392 config: A config to use when generating the schema.393394 Returns:395 A Pydantic model that can be used to validate input.396 """397 _ = config398 root_type = self.InputType399400 if (401 inspect.isclass(root_type)402 and not isinstance(root_type, GenericAlias)403 and issubclass(root_type, BaseModel)404 ):405 return root_type406407 return create_model_v2(408 self.get_name("Input"),409 root=root_type,410 # create model needs access to appropriate type annotations to be411 # able to construct the Pydantic model.412 # When we create the model, we pass information about the namespace413 # where the model is being created, so the type annotations can414 # be resolved correctly as well.415 # self.__class__.__module__ handles the case when the Runnable is416 # being sub-classed in a different module.417 module_name=self.__class__.__module__,418 )419420 def get_input_jsonschema(421 self, config: RunnableConfig | None = None422 ) -> dict[str, Any]:423 """Get a JSON schema that represents the input to the `Runnable`.424425 Args:426 config: A config to use when generating the schema.427428 Returns:429 A JSON schema that represents the input to the `Runnable`.430431 Example:432 ```python433 from langchain_core.runnables import RunnableLambda434435436 def add_one(x: int) -> int:437 return x + 1438439440 runnable = RunnableLambda(add_one)441442 print(runnable.get_input_jsonschema())443 ```444445 !!! version-added "Added in `langchain-core` 0.3.0"446447 """448 return model_json_schema(self.get_input_schema(config))449450 @property451 def output_schema(self) -> TypeBaseModel:452 """Output schema.453454 The type of output this `Runnable` produces specified as a Pydantic model.455 """456 return self.get_output_schema()457458 def get_output_schema(459 self,460 config: RunnableConfig | None = None,461 ) -> TypeBaseModel:462 """Get a Pydantic model that can be used to validate output to the `Runnable`.463464 `Runnable` objects that leverage the `configurable_fields` and465 `configurable_alternatives` methods will have a dynamic output schema that466 depends on which configuration the `Runnable` is invoked with.467468 This method allows to get an output schema for a specific configuration.469470 Args:471 config: A config to use when generating the schema.472473 Returns:474 A Pydantic model that can be used to validate output.475 """476 _ = config477 root_type = self.OutputType478479 if (480 inspect.isclass(root_type)481 and not isinstance(root_type, GenericAlias)482 and issubclass(root_type, BaseModel)483 ):484 return root_type485486 return create_model_v2(487 self.get_name("Output"),488 root=root_type,489 # create model needs access to appropriate type annotations to be490 # able to construct the Pydantic model.491 # When we create the model, we pass information about the namespace492 # where the model is being created, so the type annotations can493 # be resolved correctly as well.494 # self.__class__.__module__ handles the case when the Runnable is495 # being sub-classed in a different module.496 module_name=self.__class__.__module__,497 )498499 def get_output_jsonschema(500 self, config: RunnableConfig | None = None501 ) -> dict[str, Any]:502 """Get a JSON schema that represents the output of the `Runnable`.503504 Args:505 config: A config to use when generating the schema.506507 Returns:508 A JSON schema that represents the output of the `Runnable`.509510 Example:511 ```python512 from langchain_core.runnables import RunnableLambda513514515 def add_one(x: int) -> int:516 return x + 1517518519 runnable = RunnableLambda(add_one)520521 print(runnable.get_output_jsonschema())522 ```523524 !!! version-added "Added in `langchain-core` 0.3.0"525526 """527 return model_json_schema(self.get_output_schema(config))528529 @property530 def config_specs(self) -> list[ConfigurableFieldSpec]:531 """List configurable fields for this `Runnable`."""532 return []533534 def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]:535 """The type of config this `Runnable` accepts specified as a Pydantic model.536537 To mark a field as configurable, see the `configurable_fields`538 and `configurable_alternatives` methods.539540 Args:541 include: A list of fields to include in the config schema.542543 Returns:544 A Pydantic model that can be used to validate config.545546 """547 include = include or []548 config_specs = self.config_specs549 configurable = (550 create_model_v2(551 "Configurable",552 field_definitions={553 spec.id: (554 spec.annotation,555 Field(556 spec.default, title=spec.name, description=spec.description557 ),558 )559 for spec in config_specs560 },561 )562 if config_specs563 else None564 )565566 # Many need to create a typed dict instead to implement NotRequired!567 all_fields = {568 **({"configurable": (configurable, None)} if configurable else {}),569 **{570 field_name: (field_type, None)571 for field_name, field_type in get_type_hints(RunnableConfig).items()572 if field_name in [i for i in include if i != "configurable"]573 },574 }575 return create_model_v2(self.get_name("Config"), field_definitions=all_fields)576577 def get_config_jsonschema(578 self, *, include: Sequence[str] | None = None579 ) -> dict[str, Any]:580 """Get a JSON schema that represents the config of the `Runnable`.581582 Args:583 include: A list of fields to include in the config schema.584585 Returns:586 A JSON schema that represents the config of the `Runnable`.587588 !!! version-added "Added in `langchain-core` 0.3.0"589590 """591 return self.config_schema(include=include).model_json_schema()592593 def get_graph(self, config: RunnableConfig | None = None) -> Graph:594 """Return a graph representation of this `Runnable`."""595 # Import locally to prevent circular import596 from langchain_core.runnables.graph import Graph # noqa: PLC0415597598 graph = Graph()599 try:600 input_node = graph.add_node(self.get_input_schema(config))601 except TypeError:602 input_node = graph.add_node(create_model_v2(self.get_name("Input")))603 runnable_node = graph.add_node(604 self, metadata=config.get("metadata") if config else None605 )606 try:607 output_node = graph.add_node(self.get_output_schema(config))608 except TypeError:609 output_node = graph.add_node(create_model_v2(self.get_name("Output")))610 graph.add_edge(input_node, runnable_node)611 graph.add_edge(runnable_node, output_node)612 return graph613614 def get_prompts(615 self, config: RunnableConfig | None = None616 ) -> list[BasePromptTemplate[Any]]:617 """Return a list of prompts used by this `Runnable`."""618 # Import locally to prevent circular import619 from langchain_core.prompts.base import BasePromptTemplate # noqa: PLC0415620621 return [622 node.data623 for node in self.get_graph(config=config).nodes.values()624 if isinstance(node.data, BasePromptTemplate)625 ]626627 @overload628 def __or__(629 self, other: Mapping[str, Any]630 ) -> RunnableSerializable[Input, dict[str, Any]]: ...631632 @overload633 def __or__(634 self,635 other: Callable[[Output], Runnable[Output, Other]]636 | Callable[[Output], Awaitable[Runnable[Output, Other]]],637 ) -> RunnableSerializable[Input, Other]: ...638639 @overload640 def __or__(641 self,642 other: Runnable[Output, Other]643 | Callable[[Iterator[Output]], Iterator[Other]]644 | Callable[[AsyncIterator[Output]], AsyncIterator[Other]]645 | Callable[[Output], Other],646 ) -> RunnableSerializable[Input, Other]: ...647648 def __or__(649 self,650 other: Runnable[Output, Other]651 | Callable[[Iterator[Output]], Iterator[Other]]652 | Callable[[AsyncIterator[Output]], AsyncIterator[Other]]653 | Callable[[Output], Other]654 | Mapping[str, Runnable[Output, Any] | Callable[[Output], Any] | Any],655 ) -> RunnableSerializable[Input, Any]:656 """Runnable "or" operator.657658 Compose this `Runnable` with another object to create a659 `RunnableSequence`.660661 Args:662 other: Another `Runnable` or a `Runnable`-like object.663664 Returns:665 A new `Runnable`.666667 Raises:668 TypeError: If `other` cannot be coerced to a `Runnable`. This method does669 not return `NotImplemented`, to avoid delegating LCEL composition to670 unrelated reflected `|` implementations. To interoperate, implement671 `Runnable`, make the object callable, or define `__or__` on the left.672 """673 return RunnableSequence(self, coerce_to_runnable(other))674675 @overload676 def __ror__(677 self,678 other: Mapping[str, Any],679 ) -> RunnableSerializable[Any, Output]: ...680681 @overload682 def __ror__(683 self,684 other: Callable[[Other], Runnable[Other, Input]]685 | Callable[[Other], Awaitable[Runnable[Other, Input]]],686 ) -> RunnableSerializable[Other, Output]: ...687688 @overload689 def __ror__(690 self,691 other: Runnable[Other, Input]692 | Callable[[Iterator[Other]], Iterator[Input]]693 | Callable[[AsyncIterator[Other]], AsyncIterator[Input]]694 | Callable[[Other], Input],695 ) -> RunnableSerializable[Other, Output]: ...696697 def __ror__(698 self,699 other: Runnable[Other, Input]700 | Callable[[Iterator[Other]], Iterator[Input]]701 | Callable[[AsyncIterator[Other]], AsyncIterator[Input]]702 | Callable[[Other], Any]703 | Mapping[str, Runnable[Other, Input] | Callable[[Other], Any] | Any],704 ) -> RunnableSerializable[Any, Output]:705 """Runnable "reverse-or" operator.706707 Compose this `Runnable` with another object to create a708 `RunnableSequence`.709710 Args:711 other: Another `Runnable` or a `Runnable`-like object.712713 Returns:714 A new `Runnable`.715716 Raises:717 TypeError: If `other` cannot be coerced to a `Runnable`. This method does718 not return `NotImplemented`, to avoid delegating LCEL composition to719 unrelated reflected `|` implementations. To interoperate, implement720 `Runnable`, make the object callable, or define `__or__` on the left.721 """722 return RunnableSequence(coerce_to_runnable(other), self)723724 def pipe(725 self,726 *others: Runnable[Any, Other] | Callable[[Any], Other],727 name: str | None = None,728 ) -> RunnableSerializable[Input, Other]:729 """Pipe `Runnable` objects.730731 Compose this `Runnable` with `Runnable`-like objects to make a732 `RunnableSequence`.733734 Equivalent to `RunnableSequence(self, *others)` or `self | others[0] | ...`735736 Example:737 ```python738 from langchain_core.runnables import RunnableLambda739740741 def add_one(x: int) -> int:742 return x + 1743744745 def mul_two(x: int) -> int:746 return x * 2747748749 runnable_1 = RunnableLambda(add_one)750 runnable_2 = RunnableLambda(mul_two)751 sequence = runnable_1.pipe(runnable_2)752 # Or equivalently:753 # sequence = runnable_1 | runnable_2754 # sequence = RunnableSequence(first=runnable_1, last=runnable_2)755 sequence.invoke(1)756 await sequence.ainvoke(1)757 # -> 4758759 sequence.batch([1, 2, 3])760 await sequence.abatch([1, 2, 3])761 # -> [4, 6, 8]762 ```763764 Args:765 *others: Other `Runnable` or `Runnable`-like objects to compose766 name: An optional name for the resulting `RunnableSequence`.767768 Returns:769 A new `Runnable`.770 """771 return RunnableSequence(self, *others, name=name)772773 def pick(self, keys: str | list[str]) -> RunnableSerializable[Any, Any]:774 """Pick keys from the output `dict` of this `Runnable`.775776 !!! example "Pick a single key"777778 ```python779 import json780781 from langchain_core.runnables import RunnableLambda, RunnableMap782783 as_str = RunnableLambda(str)784 as_json = RunnableLambda(json.loads)785 chain = RunnableMap(str=as_str, json=as_json)786787 chain.invoke("[1, 2, 3]")788 # -> {"str": "[1, 2, 3]", "json": [1, 2, 3]}789790 json_only_chain = chain.pick("json")791 json_only_chain.invoke("[1, 2, 3]")792 # -> [1, 2, 3]793 ```794795 !!! example "Pick a list of keys"796797 ```python798 from typing import Any799800 import json801802 from langchain_core.runnables import RunnableLambda, RunnableMap803804 as_str = RunnableLambda(str)805 as_json = RunnableLambda(json.loads)806807808 def as_bytes(x: Any) -> bytes:809 return bytes(x, "utf-8")810811812 chain = RunnableMap(813 str=as_str, json=as_json, bytes=RunnableLambda(as_bytes)814 )815816 chain.invoke("[1, 2, 3]")817 # -> {"str": "[1, 2, 3]", "json": [1, 2, 3], "bytes": b"[1, 2, 3]"}818819 json_and_bytes_chain = chain.pick(["json", "bytes"])820 json_and_bytes_chain.invoke("[1, 2, 3]")821 # -> {"json": [1, 2, 3], "bytes": b"[1, 2, 3]"}822 ```823824 Args:825 keys: A key or list of keys to pick from the output dict.826827 Returns:828 a new `Runnable`.829830 """831 # Import locally to prevent circular import832 from langchain_core.runnables.passthrough import RunnablePick # noqa: PLC0415833834 return self | RunnablePick(keys) # type: ignore[operator, no-any-return]835836 def assign(837 self,838 **kwargs: Runnable[dict[str, Any], Any]839 | Callable[[dict[str, Any]], Any]840 | Mapping[str, Runnable[dict[str, Any], Any] | Callable[[dict[str, Any]], Any]],841 ) -> RunnableSerializable[Any, Any]:842 """Assigns new fields to the `dict` output of this `Runnable`.843844 ```python845 from langchain_core.language_models.fake import FakeStreamingListLLM846 from langchain_core.output_parsers import StrOutputParser847 from langchain_core.prompts import SystemMessagePromptTemplate848 from langchain_core.runnables import Runnable849 from operator import itemgetter850851 prompt = (852 SystemMessagePromptTemplate.from_template("You are a nice assistant.")853 + "{question}"854 )855 model = FakeStreamingListLLM(responses=["foo-lish"])856857 chain: Runnable = prompt | model | {"str": StrOutputParser()}858859 chain_with_assign = chain.assign(hello=itemgetter("str") | model)860861 print(chain_with_assign.input_schema.model_json_schema())862 # {'title': 'PromptInput', 'type': 'object', 'properties':863 {'question': {'title': 'Question', 'type': 'string'}}}864 print(chain_with_assign.output_schema.model_json_schema())865 # {'title': 'RunnableSequenceOutput', 'type': 'object', 'properties':866 {'str': {'title': 'Str',867 'type': 'string'}, 'hello': {'title': 'Hello', 'type': 'string'}}}868 ```869870 Args:871 **kwargs: A mapping of keys to `Runnable` or `Runnable`-like objects872 that will be invoked with the entire output dict of this `Runnable`.873874 Returns:875 A new `Runnable`.876877 """878 # Import locally to prevent circular import879 from langchain_core.runnables.passthrough import RunnableAssign # noqa: PLC0415880881 return self | RunnableAssign(RunnableParallel[dict[str, Any]](kwargs)) # type: ignore[operator, no-any-return]882883 """ --- Public API --- """884885 @abstractmethod886 def invoke(887 self,888 input: Input,889 config: RunnableConfig | None = None,890 **kwargs: Any,891 ) -> Output:892 """Transform a single input into an output.893894 Args:895 input: The input to the `Runnable`.896 config: A config to use when invoking the `Runnable`.897898 The config supports standard keys like `'tags'`, `'metadata'` for899 tracing purposes, `'max_concurrency'` for controlling how much work to900 do in parallel, and other keys.901902 Please refer to `RunnableConfig` for more details.903904 Returns:905 The output of the `Runnable`.906 """907908 async def ainvoke(909 self,910 input: Input,911 config: RunnableConfig | None = None,912 **kwargs: Any,913 ) -> Output:914 """Transform a single input into an output.915916 Args:917 input: The input to the `Runnable`.918 config: A config to use when invoking the `Runnable`.919920 The config supports standard keys like `'tags'`, `'metadata'` for921 tracing purposes, `'max_concurrency'` for controlling how much work to922 do in parallel, and other keys.923924 Please refer to `RunnableConfig` for more details.925926 Returns:927 The output of the `Runnable`.928 """929 return await run_in_executor(config, self.invoke, input, config, **kwargs)930931 def batch(932 self,933 inputs: list[Input],934 config: RunnableConfig | list[RunnableConfig] | None = None,935 *,936 return_exceptions: bool = False,937 **kwargs: Any | None,938 ) -> list[Output]:939 """Default implementation runs invoke in parallel using a thread pool executor.940941 The default implementation of batch works well for IO bound runnables.942943 Subclasses must override this method if they can batch more efficiently;944 e.g., if the underlying `Runnable` uses an API which supports a batch mode.945946 Args:947 inputs: A list of inputs to the `Runnable`.948 config: A config to use when invoking the `Runnable`. The config supports949 standard keys like `'tags'`, `'metadata'` for950 tracing purposes, `'max_concurrency'` for controlling how much work951 to do in parallel, and other keys.952953 Please refer to `RunnableConfig` for more details.954 return_exceptions: Whether to return exceptions instead of raising them.955 **kwargs: Additional keyword arguments to pass to the `Runnable`.956957 Returns:958 A list of outputs from the `Runnable`.959 """960 if not inputs:961 return []962963 configs = get_config_list(config, len(inputs))964965 def invoke(input_: Input, config: RunnableConfig) -> Output | Exception:966 if return_exceptions:967 try:968 return self.invoke(input_, config, **kwargs)969 except Exception as e:970 return e971 else:972 return self.invoke(input_, config, **kwargs)973974 # If there's only one input, don't bother with the executor975 if len(inputs) == 1:976 return cast("list[Output]", [invoke(inputs[0], configs[0])])977978 with get_executor_for_config(configs[0]) as executor:979 return cast("list[Output]", list(executor.map(invoke, inputs, configs)))980981 @overload982 def batch_as_completed(983 self,984 inputs: Sequence[Input],985 config: RunnableConfig | Sequence[RunnableConfig] | None = None,986 *,987 return_exceptions: Literal[False] = False,988 **kwargs: Any,989 ) -> Iterator[tuple[int, Output]]: ...990991 @overload992 def batch_as_completed(993 self,994 inputs: Sequence[Input],995 config: RunnableConfig | Sequence[RunnableConfig] | None = None,996 *,997 return_exceptions: Literal[True],998 **kwargs: Any,999 ) -> Iterator[tuple[int, Output | Exception]]: ...10001001 def batch_as_completed(1002 self,1003 inputs: Sequence[Input],1004 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1005 *,1006 return_exceptions: bool = False,1007 **kwargs: Any | None,1008 ) -> Iterator[tuple[int, Output | Exception]]:1009 """Run `invoke` in parallel on a list of inputs.10101011 Yields results as they complete.10121013 Args:1014 inputs: A list of inputs to the `Runnable`.1015 config: A config to use when invoking the `Runnable`.10161017 The config supports standard keys like `'tags'`, `'metadata'` for1018 tracing purposes, `'max_concurrency'` for controlling how much work to1019 do in parallel, and other keys.10201021 Please refer to `RunnableConfig` for more details.1022 return_exceptions: Whether to return exceptions instead of raising them.1023 **kwargs: Additional keyword arguments to pass to the `Runnable`.10241025 Yields:1026 Tuples of the index of the input and the output from the `Runnable`.10271028 """1029 if not inputs:1030 return10311032 configs = get_config_list(config, len(inputs))10331034 def invoke(1035 i: int, input_: Input, config: RunnableConfig1036 ) -> tuple[int, Output | Exception]:1037 if return_exceptions:1038 try:1039 out: Output | Exception = self.invoke(input_, config, **kwargs)1040 except Exception as e:1041 out = e1042 else:1043 out = self.invoke(input_, config, **kwargs)10441045 return (i, out)10461047 if len(inputs) == 1:1048 yield invoke(0, inputs[0], configs[0])1049 return10501051 with get_executor_for_config(configs[0]) as executor:1052 futures = {1053 executor.submit(invoke, i, input_, config)1054 for i, (input_, config) in enumerate(zip(inputs, configs, strict=False))1055 }10561057 try:1058 while futures:1059 done, futures = wait(futures, return_when=FIRST_COMPLETED)1060 while done:1061 yield done.pop().result()1062 finally:1063 for future in futures:1064 future.cancel()10651066 async def abatch(1067 self,1068 inputs: list[Input],1069 config: RunnableConfig | list[RunnableConfig] | None = None,1070 *,1071 return_exceptions: bool = False,1072 **kwargs: Any | None,1073 ) -> list[Output]:1074 """Default implementation runs `ainvoke` in parallel using `asyncio.gather`.10751076 The default implementation of `batch` works well for IO bound runnables.10771078 Subclasses must override this method if they can batch more efficiently;1079 e.g., if the underlying `Runnable` uses an API which supports a batch mode.10801081 Args:1082 inputs: A list of inputs to the `Runnable`.1083 config: A config to use when invoking the `Runnable`.10841085 The config supports standard keys like `'tags'`, `'metadata'` for1086 tracing purposes, `'max_concurrency'` for controlling how much work to1087 do in parallel, and other keys.10881089 Please refer to `RunnableConfig` for more details.1090 return_exceptions: Whether to return exceptions instead of raising them.1091 **kwargs: Additional keyword arguments to pass to the `Runnable`.10921093 Returns:1094 A list of outputs from the `Runnable`.10951096 """1097 if not inputs:1098 return []10991100 configs = get_config_list(config, len(inputs))11011102 async def ainvoke(value: Input, config: RunnableConfig) -> Output | Exception:1103 if return_exceptions:1104 try:1105 return await self.ainvoke(value, config, **kwargs)1106 except Exception as e:1107 return e1108 else:1109 return await self.ainvoke(value, config, **kwargs)11101111 coros = map(ainvoke, inputs, configs)1112 return await gather_with_concurrency(configs[0].get("max_concurrency"), *coros)11131114 @overload1115 def abatch_as_completed(1116 self,1117 inputs: Sequence[Input],1118 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1119 *,1120 return_exceptions: Literal[False] = False,1121 **kwargs: Any | None,1122 ) -> AsyncIterator[tuple[int, Output]]: ...11231124 @overload1125 def abatch_as_completed(1126 self,1127 inputs: Sequence[Input],1128 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1129 *,1130 return_exceptions: Literal[True],1131 **kwargs: Any | None,1132 ) -> AsyncIterator[tuple[int, Output | Exception]]: ...11331134 async def abatch_as_completed(1135 self,1136 inputs: Sequence[Input],1137 config: RunnableConfig | Sequence[RunnableConfig] | None = None,1138 *,1139 return_exceptions: bool = False,1140 **kwargs: Any | None,1141 ) -> AsyncIterator[tuple[int, Output | Exception]]:1142 """Run `ainvoke` in parallel on a list of inputs.11431144 Yields results as they complete.11451146 Args:1147 inputs: A list of inputs to the `Runnable`.1148 config: A config to use when invoking the `Runnable`.11491150 The config supports standard keys like `'tags'`, `'metadata'` for1151 tracing purposes, `'max_concurrency'` for controlling how much work to1152 do in parallel, and other keys.11531154 Please refer to `RunnableConfig` for more details.1155 return_exceptions: Whether to return exceptions instead of raising them.1156 **kwargs: Additional keyword arguments to pass to the `Runnable`.11571158 Yields:1159 A tuple of the index of the input and the output from the `Runnable`.11601161 """1162 if not inputs:1163 return11641165 configs = get_config_list(config, len(inputs))1166 # Get max_concurrency from first config, defaulting to None (unlimited)1167 max_concurrency = configs[0].get("max_concurrency") if configs else None1168 semaphore = asyncio.Semaphore(max_concurrency) if max_concurrency else None11691170 async def ainvoke_task(1171 i: int, input_: Input, config: RunnableConfig1172 ) -> tuple[int, Output | Exception]:1173 if return_exceptions:1174 try:1175 out: Output | Exception = await self.ainvoke(1176 input_, config, **kwargs1177 )1178 except Exception as e:1179 out = e1180 else:1181 out = await self.ainvoke(input_, config, **kwargs)1182 return (i, out)11831184 coros = [1185 gated_coro(semaphore, ainvoke_task(i, input_, config))1186 if semaphore1187 else ainvoke_task(i, input_, config)1188 for i, (input_, config) in enumerate(zip(inputs, configs, strict=False))1189 ]11901191 for coro in asyncio.as_completed(coros):1192 yield await coro11931194 def stream(1195 self,1196 input: Input,1197 config: RunnableConfig | None = None,1198 **kwargs: Any | None,1199 ) -> Iterator[Output]:1200 """Default implementation of `stream`, which calls `invoke`.12011202 Subclasses must override this method if they support streaming output.12031204 Args:1205 input: The input to the `Runnable`.1206 config: The config to use for the `Runnable`.1207 **kwargs: Additional keyword arguments to pass to the `Runnable`.12081209 Yields:1210 The output of the `Runnable`.12111212 """1213 yield self.invoke(input, config, **kwargs)12141215 async def astream(1216 self,1217 input: Input,1218 config: RunnableConfig | None = None,1219 **kwargs: Any | None,1220 ) -> AsyncIterator[Output]:1221 """Default implementation of `astream`, which calls `ainvoke`.12221223 Subclasses must override this method if they support streaming output.12241225 Args:1226 input: The input to the `Runnable`.1227 config: The config to use for the `Runnable`.1228 **kwargs: Additional keyword arguments to pass to the `Runnable`.12291230 Yields:1231 The output of the `Runnable`.12321233 """1234 yield await self.ainvoke(input, config, **kwargs)12351236 @overload1237 def astream_log(1238 self,1239 input: Any,1240 config: RunnableConfig | None = None,1241 *,1242 diff: Literal[True] = True,1243 with_streamed_output_list: bool = True,1244 include_names: Sequence[str] | None = None,1245 include_types: Sequence[str] | None = None,1246 include_tags: Sequence[str] | None = None,1247 exclude_names: Sequence[str] | None = None,1248 exclude_types: Sequence[str] | None = None,1249 exclude_tags: Sequence[str] | None = None,1250 **kwargs: Any,1251 ) -> AsyncIterator[RunLogPatch]: ...12521253 @overload1254 def astream_log(1255 self,1256 input: Any,1257 config: RunnableConfig | None = None,1258 *,1259 diff: Literal[False],1260 with_streamed_output_list: bool = True,1261 include_names: Sequence[str] | None = None,1262 include_types: Sequence[str] | None = None,1263 include_tags: Sequence[str] | None = None,1264 exclude_names: Sequence[str] | None = None,1265 exclude_types: Sequence[str] | None = None,1266 exclude_tags: Sequence[str] | None = None,1267 **kwargs: Any,1268 ) -> AsyncIterator[RunLog]: ...12691270 async def astream_log(1271 self,1272 input: Any,1273 config: RunnableConfig | None = None,1274 *,1275 diff: bool = True,1276 with_streamed_output_list: bool = True,1277 include_names: Sequence[str] | None = None,1278 include_types: Sequence[str] | None = None,1279 include_tags: Sequence[str] | None = None,1280 exclude_names: Sequence[str] | None = None,1281 exclude_types: Sequence[str] | None = None,1282 exclude_tags: Sequence[str] | None = None,1283 **kwargs: Any,1284 ) -> AsyncIterator[RunLogPatch] | AsyncIterator[RunLog]:1285 """Stream all output from a `Runnable`, as reported to the callback system.12861287 This includes all inner runs of LLMs, Retrievers, Tools, etc.12881289 Output is streamed as Log objects, which include a list of1290 Jsonpatch ops that describe how the state of the run has changed in each1291 step, and the final state of the run.12921293 The Jsonpatch ops can be applied in order to construct state.12941295 Args:1296 input: The input to the `Runnable`.1297 config: The config to use for the `Runnable`.1298 diff: Whether to yield diffs between each step or the current state.1299 with_streamed_output_list: Whether to yield the `streamed_output` list.1300 include_names: Only include logs with these names.1301 include_types: Only include logs with these types.1302 include_tags: Only include logs with these tags.1303 exclude_names: Exclude logs with these names.1304 exclude_types: Exclude logs with these types.1305 exclude_tags: Exclude logs with these tags.1306 **kwargs: Additional keyword arguments to pass to the `Runnable`.13071308 Yields:1309 A `RunLogPatch` or `RunLog` object.13101311 """1312 warn_deprecated(1313 since="1.3.3",1314 message=("astream_log is deprecated. Use astream instead."),1315 removal="2.0.0",1316 )1317 stream = LogStreamCallbackHandler(1318 auto_close=False,1319 include_names=include_names,1320 include_types=include_types,1321 include_tags=include_tags,1322 exclude_names=exclude_names,1323 exclude_types=exclude_types,1324 exclude_tags=exclude_tags,1325 _schema_format="original",1326 )13271328 # Mypy isn't resolving the overloads here1329 # Likely an issue b/c `self` is being passed through1330 # and it's can't map it to Runnable[Input,Output]?1331 async for item in _astream_log_implementation( # type: ignore[call-overload]1332 self,1333 input,1334 config,1335 diff=diff,1336 stream=stream,1337 with_streamed_output_list=with_streamed_output_list,1338 **kwargs,1339 ):1340 yield item13411342 @overload1343 def astream_events(1344 self,1345 input: Any,1346 config: RunnableConfig | None = None,1347 *,1348 version: Literal["v1", "v2"] = "v2",1349 include_names: Sequence[str] | None = None,1350 include_types: Sequence[str] | None = None,1351 include_tags: Sequence[str] | None = None,1352 exclude_names: Sequence[str] | None = None,1353 exclude_types: Sequence[str] | None = None,1354 exclude_tags: Sequence[str] | None = None,1355 **kwargs: Any,1356 ) -> AsyncIterator[StreamEvent]: ...13571358 @overload1359 def astream_events(1360 self,1361 input: Any,1362 config: RunnableConfig | None = None,1363 *,1364 version: Literal["v3"],1365 **kwargs: Any,1366 ) -> Awaitable[Any]: ...13671368 def astream_events(1369 self,1370 input: Any,1371 config: RunnableConfig | None = None,1372 *,1373 version: Literal["v1", "v2", "v3"] = "v2",1374 include_names: Sequence[str] | None = None,1375 include_types: Sequence[str] | None = None,1376 include_tags: Sequence[str] | None = None,1377 exclude_names: Sequence[str] | None = None,1378 exclude_types: Sequence[str] | None = None,1379 exclude_tags: Sequence[str] | None = None,1380 **kwargs: Any,1381 ) -> AsyncIterator[StreamEvent] | Awaitable[Any]:1382 """Generate a stream of events.13831384 Use to create an iterator over `StreamEvent` that provide real-time information1385 about the progress of the `Runnable`, including `StreamEvent` from intermediate1386 results.13871388 A `StreamEvent` is a dictionary with the following schema:13891390 - `event`: Event names are of the format:1391 `on_[runnable_type]_(start|stream|end)`.1392 - `name`: The name of the `Runnable` that generated the event.1393 - `run_id`: Randomly generated ID associated with the given execution of the1394 `Runnable` that emitted the event. A child `Runnable` that gets invoked as1395 part of the execution of a parent `Runnable` is assigned its own unique ID.1396 - `parent_ids`: The IDs of the parent runnables that generated the event. The1397 root `Runnable` will have an empty list. The order of the parent IDs is from1398 the root to the immediate parent. Only available for v2 version of the API.1399 The v1 version of the API will return an empty list.1400 - `tags`: The tags of the `Runnable` that generated the event.1401 - `metadata`: The metadata of the `Runnable` that generated the event.1402 - `data`: The data associated with the event. The contents of this field1403 depend on the type of event. See the table below for more details.14041405 Below is a table that illustrates some events that might be emitted by various1406 chains. Metadata fields have been omitted from the table for brevity.1407 Chain definitions have been included after the table.14081409 !!! note1410 This reference table is for the v2 version of the schema.14111412 | event | name | chunk | input | output |1413 | ---------------------- | -------------------- | ----------------------------------- | ------------------------------------------------- | --------------------------------------------------- |1414 | `on_chat_model_start` | `'[model name]'` | | `{"messages": [[SystemMessage, HumanMessage]]}` | |1415 | `on_chat_model_stream` | `'[model name]'` | `AIMessageChunk(content="hello")` | | |1416 | `on_chat_model_end` | `'[model name]'` | | `{"messages": [[SystemMessage, HumanMessage]]}` | `AIMessageChunk(content="hello world")` |1417 | `on_llm_start` | `'[model name]'` | | `{'input': 'hello'}` | |1418 | `on_llm_stream` | `'[model name]'` | `'Hello' ` | | |1419 | `on_llm_end` | `'[model name]'` | | `'Hello human!'` | |1420 | `on_chain_start` | `'format_docs'` | | | |1421 | `on_chain_stream` | `'format_docs'` | `'hello world!, goodbye world!'` | | |1422 | `on_chain_end` | `'format_docs'` | | `[Document(...)]` | `'hello world!, goodbye world!'` |1423 | `on_tool_start` | `'some_tool'` | | `{"x": 1, "y": "2"}` | |1424 | `on_tool_end` | `'some_tool'` | | | `{"x": 1, "y": "2"}` |1425 | `on_retriever_start` | `'[retriever name]'` | | `{"query": "hello"}` | |1426 | `on_retriever_end` | `'[retriever name]'` | | `{"query": "hello"}` | `[Document(...), ..]` |1427 | `on_prompt_start` | `'[template_name]'` | | `{"question": "hello"}` | |1428 | `on_prompt_end` | `'[template_name]'` | | `{"question": "hello"}` | `ChatPromptValue(messages: [SystemMessage, ...])` |14291430 In addition to the standard events, users can also dispatch custom events (see example below).14311432 Custom events will be only be surfaced with in the v2 version of the API!14331434 A custom event has following format:14351436 | Attribute | Type | Description |1437 | ----------- | ------ | --------------------------------------------------------------------------------------------------------- |1438 | `name` | `str` | A user defined name for the event. |1439 | `data` | `Any` | The data associated with the event. This can be anything, though we suggest making it JSON serializable. |14401441 Here are declarations associated with the standard events shown above:14421443 `format_docs`:14441445 ```python1446 def format_docs(docs: list[Document]) -> str:1447 '''Format the docs.'''1448 return ", ".join([doc.page_content for doc in docs])144914501451 format_docs = RunnableLambda(format_docs)1452 ```14531454 `some_tool`:14551456 ```python1457 @tool1458 def some_tool(x: int, y: str) -> dict:1459 '''Some_tool.'''1460 return {"x": x, "y": y}1461 ```14621463 `prompt`:14641465 ```python1466 template = ChatPromptTemplate.from_messages(1467 [1468 ("system", "You are Cat Agent 007"),1469 ("human", "{question}"),1470 ]1471 ).with_config({"run_name": "my_template", "tags": ["my_template"]})1472 ```14731474 !!! example14751476 ```python1477 from langchain_core.runnables import RunnableLambda147814791480 async def reverse(s: str) -> str:1481 return s[::-1]148214831484 chain = RunnableLambda(func=reverse)14851486 events = [1487 event async for event in chain.astream_events("hello", version="v2")1488 ]14891490 # Will produce the following events1491 # (run_id, and parent_ids has been omitted for brevity):1492 [1493 {1494 "data": {"input": "hello"},1495 "event": "on_chain_start",1496 "metadata": {},1497 "name": "reverse",1498 "tags": [],1499 },1500 {1501 "data": {"chunk": "olleh"},1502 "event": "on_chain_stream",1503 "metadata": {},1504 "name": "reverse",1505 "tags": [],1506 },1507 {1508 "data": {"output": "olleh"},1509 "event": "on_chain_end",1510 "metadata": {},1511 "name": "reverse",1512 "tags": [],1513 },1514 ]1515 ```15161517 ```python title="Dispatch custom event"1518 from langchain_core.callbacks.manager import (1519 adispatch_custom_event,1520 )1521 from langchain_core.runnables import RunnableLambda, RunnableConfig1522 import asyncio152315241525 async def slow_thing(some_input: str, config: RunnableConfig) -> str:1526 \"\"\"Do something that takes a long time.\"\"\"1527 await asyncio.sleep(1) # Placeholder for some slow operation1528 await adispatch_custom_event(1529 "progress_event",1530 {"message": "Finished step 1 of 3"},1531 config=config # Must be included for python < 3.101532 )1533 await asyncio.sleep(1) # Placeholder for some slow operation1534 await adispatch_custom_event(1535 "progress_event",1536 {"message": "Finished step 2 of 3"},1537 config=config # Must be included for python < 3.101538 )1539 await asyncio.sleep(1) # Placeholder for some slow operation1540 return "Done"15411542 slow_thing = RunnableLambda(slow_thing)15431544 async for event in slow_thing.astream_events("some_input", version="v2"):1545 print(event)1546 ```15471548 Args:1549 input: The input to the `Runnable`.1550 config: The config to use for the `Runnable`.1551 version: The version of the schema to use. One of `'v1'`, `'v2'`,1552 or `'v3'`.15531554 Most callers should use `'v2'` (the default), which yields1555 `StreamEvent` dicts and supports custom events.15561557 `'v3'` selects the typed, content-block-centric streaming1558 protocol and is only supported on `Runnable` subclasses that1559 implement it (currently `BaseChatModel` and1560 `langgraph.CompiledGraph`); on a generic `Runnable` it raises1561 `NotImplementedError`. The `'v3'` API is in beta and may1562 change. See the subclass override (e.g.1563 `BaseChatModel.astream_events`) for the v3 return shape.15641565 `'v1'` is retained for backwards compatibility and will be1566 deprecated in `0.4.0`. Custom events are only surfaced in1567 `'v2'` / `'v3'`.1568 include_names: Only include events from `Runnable` objects with matching names.1569 include_types: Only include events from `Runnable` objects with matching types.1570 include_tags: Only include events from `Runnable` objects with matching tags.1571 exclude_names: Exclude events from `Runnable` objects with matching names.1572 exclude_types: Exclude events from `Runnable` objects with matching types.1573 exclude_tags: Exclude events from `Runnable` objects with matching tags.1574 **kwargs: Additional keyword arguments to pass to the `Runnable`.15751576 Yields:1577 An async stream of `StreamEvent`.15781579 Raises:1580 NotImplementedError: If the version is not `'v1'`, `'v2'`, or `'v3'`, or1581 if `'v3'` is requested on a `Runnable` that does not implement the v31582 streaming protocol.15831584 """ # noqa: E5011585 if version == "v3":1586 return self._astream_events_v3_unsupported()1587 return self._astream_events_v1_v2(1588 input,1589 config=config,1590 version=version,1591 include_names=include_names,1592 include_types=include_types,1593 include_tags=include_tags,1594 exclude_names=exclude_names,1595 exclude_types=exclude_types,1596 exclude_tags=exclude_tags,1597 **kwargs,1598 )15991600 async def _astream_events_v3_unsupported(self) -> Any:1601 """Coroutine that raises when v3 isn't implemented on this Runnable.16021603 Lets the public `astream_events(version="v3")` return an awaitable1604 whose error surfaces on `await`, matching the v3 contract on1605 subclasses that do implement the protocol.1606 """1607 msg = (1608 "astream_events(version='v3') is only supported on Runnable "1609 "subclasses that implement the v3 streaming protocol "1610 "(BaseChatModel, CompiledGraph). "1611 f"Got: {type(self).__name__}"1612 )1613 raise NotImplementedError(msg)16141615 async def _astream_events_v1_v2(1616 self,1617 input: Any,1618 config: RunnableConfig | None = None,1619 *,1620 version: Literal["v1", "v2"] = "v2",1621 include_names: Sequence[str] | None = None,1622 include_types: Sequence[str] | None = None,1623 include_tags: Sequence[str] | None = None,1624 exclude_names: Sequence[str] | None = None,1625 exclude_types: Sequence[str] | None = None,1626 exclude_tags: Sequence[str] | None = None,1627 **kwargs: Any,1628 ) -> AsyncIterator[StreamEvent]:1629 if version == "v2":1630 event_stream = _astream_events_implementation_v2(1631 self,1632 input,1633 config=config,1634 include_names=include_names,1635 include_types=include_types,1636 include_tags=include_tags,1637 exclude_names=exclude_names,1638 exclude_types=exclude_types,1639 exclude_tags=exclude_tags,1640 **kwargs,1641 )1642 elif version == "v1":1643 warn_deprecated(1644 since="1.3.3",1645 message=(1646 "astream_events version='v1' is deprecated. "1647 "Use version='v2' or astream instead."1648 ),1649 removal="2.0.0",1650 )1651 # First implementation, built on top of astream_log API1652 # This implementation will be deprecated as of 0.2.01653 event_stream = _astream_events_implementation_v1(1654 self,1655 input,1656 config=config,1657 include_names=include_names,1658 include_types=include_types,1659 include_tags=include_tags,1660 exclude_names=exclude_names,1661 exclude_types=exclude_types,1662 exclude_tags=exclude_tags,1663 **kwargs,1664 )1665 else:1666 msg = f"Unsupported version: {version!r}. Expected 'v1', 'v2', or 'v3'." # type: ignore[unreachable]1667 raise NotImplementedError(msg)16681669 async with aclosing(event_stream):1670 async for event in event_stream:1671 yield event16721673 @overload1674 def stream_events(1675 self,1676 input: Any,1677 config: RunnableConfig | None = None,1678 *,1679 version: Literal["v1", "v2"] = "v2",1680 include_names: Sequence[str] | None = None,1681 include_types: Sequence[str] | None = None,1682 include_tags: Sequence[str] | None = None,1683 exclude_names: Sequence[str] | None = None,1684 exclude_types: Sequence[str] | None = None,1685 exclude_tags: Sequence[str] | None = None,1686 **kwargs: Any,1687 ) -> Iterator[StreamEvent]: ...16881689 @overload1690 def stream_events(1691 self,1692 input: Any,1693 config: RunnableConfig | None = None,1694 *,1695 version: Literal["v3"],1696 **kwargs: Any,1697 ) -> Iterator[Any]: ...16981699 def stream_events(1700 self,1701 input: Any,1702 config: RunnableConfig | None = None,1703 *,1704 version: Literal["v1", "v2", "v3"] = "v2",1705 include_names: Sequence[str] | None = None,1706 include_types: Sequence[str] | None = None,1707 include_tags: Sequence[str] | None = None,1708 exclude_names: Sequence[str] | None = None,1709 exclude_types: Sequence[str] | None = None,1710 exclude_tags: Sequence[str] | None = None,1711 **kwargs: Any,1712 ) -> Iterator[StreamEvent] | Iterator[Any]:1713 """Generate a stream of events synchronously.17141715 Synchronous counterpart to `astream_events`. For `version='v3'`, subclasses1716 that implement the v3 streaming protocol (`BaseChatModel`, `CompiledGraph`)1717 override this method. All other versions and base-class calls raise1718 `NotImplementedError`.17191720 Args:1721 input: The input to the `Runnable`.1722 config: The config to use for the `Runnable`.1723 version: The version of the schema to use. `'v3'` requires a subclass1724 that implements the v3 streaming protocol. `'v1'` and `'v2'` are not1725 supported on the sync path.1726 include_names: Only include events from `Runnable` objects with matching1727 names.1728 include_types: Only include events from `Runnable` objects with matching1729 types.1730 include_tags: Only include events from `Runnable` objects with matching1731 tags.1732 exclude_names: Exclude events from `Runnable` objects with matching names.1733 exclude_types: Exclude events from `Runnable` objects with matching types.1734 exclude_tags: Exclude events from `Runnable` objects with matching tags.1735 **kwargs: Additional keyword arguments to pass to the `Runnable`.17361737 Raises:1738 NotImplementedError: Always. Subclasses override this method for supported1739 versions.17401741 """1742 # Base impl always raises; consume args so they don't trip ARG002.1743 del input, config, include_names, include_types, include_tags1744 del exclude_names, exclude_types, exclude_tags, kwargs1745 if version == "v3":1746 msg = (1747 "stream_events(version='v3') is only supported on Runnable subclasses "1748 "that implement the v3 streaming protocol "1749 "(BaseChatModel, CompiledGraph). "1750 f"Got: {type(self).__name__}"1751 )1752 raise NotImplementedError(msg)1753 msg = (1754 f"stream_events(version={version!r}) is not supported. "1755 "Use astream_events() for v1/v2, or stream_events(version='v3') "1756 "on a supported subclass."1757 )1758 raise NotImplementedError(msg)17591760 def transform(1761 self,1762 input: Iterator[Input],1763 config: RunnableConfig | None = None,1764 **kwargs: Any | None,1765 ) -> Iterator[Output]:1766 """Transform inputs to outputs.17671768 Default implementation of transform, which buffers input and calls `astream`.17691770 Subclasses must override this method if they can start producing output while1771 input is still being generated.17721773 Args:1774 input: An iterator of inputs to the `Runnable`.1775 config: The config to use for the `Runnable`.1776 **kwargs: Additional keyword arguments to pass to the `Runnable`.17771778 Yields:1779 The output of the `Runnable`.17801781 """1782 final: Input1783 got_first_val = False17841785 for ichunk in input:1786 # The default implementation of transform is to buffer input and1787 # then call stream.1788 # It'll attempt to gather all input into a single chunk using1789 # the `+` operator.1790 # If the input is not addable, then we'll assume that we can1791 # only operate on the last chunk,1792 # and we'll iterate until we get to the last chunk.1793 if not got_first_val:1794 final = ichunk1795 got_first_val = True1796 else:1797 try:1798 final = final + ichunk # type: ignore[operator]1799 except TypeError:1800 final = ichunk18011802 if got_first_val:1803 yield from self.stream(final, config, **kwargs)18041805 async def atransform(1806 self,1807 input: AsyncIterator[Input],1808 config: RunnableConfig | None = None,1809 **kwargs: Any | None,1810 ) -> AsyncIterator[Output]:1811 """Transform inputs to outputs.18121813 Default implementation of atransform, which buffers input and calls `astream`.18141815 Subclasses must override this method if they can start producing output while1816 input is still being generated.18171818 Args:1819 input: An async iterator of inputs to the `Runnable`.1820 config: The config to use for the `Runnable`.1821 **kwargs: Additional keyword arguments to pass to the `Runnable`.18221823 Yields:1824 The output of the `Runnable`.18251826 """1827 final: Input1828 got_first_val = False18291830 async for ichunk in input:1831 # The default implementation of transform is to buffer input and1832 # then call stream.1833 # It'll attempt to gather all input into a single chunk using1834 # the `+` operator.1835 # If the input is not addable, then we'll assume that we can1836 # only operate on the last chunk,1837 # and we'll iterate until we get to the last chunk.1838 if not got_first_val:1839 final = ichunk1840 got_first_val = True1841 else:1842 try:1843 final = final + ichunk # type: ignore[operator]1844 except TypeError:1845 final = ichunk18461847 if got_first_val:1848 async for output in self.astream(final, config, **kwargs):1849 yield output18501851 def bind(self, **kwargs: Any) -> Runnable[Input, Output]:1852 """Bind arguments to a `Runnable`, returning a new `Runnable`.18531854 Useful when a `Runnable` in a chain requires an argument that is not1855 in the output of the previous `Runnable` or included in the user input.18561857 Args:1858 **kwargs: The arguments to bind to the `Runnable`.18591860 Returns:1861 A new `Runnable` with the arguments bound.18621863 Example:1864 ```python1865 from langchain_ollama import ChatOllama1866 from langchain_core.output_parsers import StrOutputParser18671868 model = ChatOllama(model="llama3.1")18691870 # Without bind1871 chain = model | StrOutputParser()18721873 chain.invoke("Repeat quoted words exactly: 'One two three four five.'")1874 # Output is 'One two three four five.'18751876 # With bind1877 chain = model.bind(stop=["three"]) | StrOutputParser()18781879 chain.invoke("Repeat quoted words exactly: 'One two three four five.'")1880 # Output is 'One two'1881 ```1882 """1883 return RunnableBinding(bound=self, kwargs=kwargs, config={})18841885 def with_config(1886 self,1887 config: RunnableConfig | None = None,1888 # Sadly Unpack is not well-supported by mypy so this will have to be untyped1889 **kwargs: Any,1890 ) -> Runnable[Input, Output]:1891 """Bind config to a `Runnable`, returning a new `Runnable`.18921893 Args:1894 config: The config to bind to the `Runnable`.1895 **kwargs: Additional keyword arguments to pass to the `Runnable`.18961897 Returns:1898 A new `Runnable` with the config bound.18991900 """1901 return RunnableBinding(1902 bound=self,1903 config=cast(1904 "RunnableConfig",1905 {**(config or {}), **kwargs},1906 ),1907 kwargs={},1908 )19091910 def with_listeners(1911 self,1912 *,1913 on_start: Callable[[Run], None]1914 | Callable[[Run, RunnableConfig], None]1915 | None = None,1916 on_end: Callable[[Run], None]1917 | Callable[[Run, RunnableConfig], None]1918 | None = None,1919 on_error: Callable[[Run], None]1920 | Callable[[Run, RunnableConfig], None]1921 | None = None,1922 ) -> Runnable[Input, Output]:1923 """Bind lifecycle listeners to a `Runnable`, returning a new `Runnable`.19241925 The Run object contains information about the run, including its `id`,1926 `type`, `input`, `output`, `error`, `start_time`, `end_time`, and1927 any tags or metadata added to the run.19281929 Args:1930 on_start: Called before the `Runnable` starts running, with the `Run`1931 object.1932 on_end: Called after the `Runnable` finishes running, with the `Run`1933 object.1934 on_error: Called if the `Runnable` throws an error, with the `Run`1935 object.19361937 Returns:1938 A new `Runnable` with the listeners bound.19391940 Example:1941 ```python1942 from langchain_core.runnables import RunnableLambda1943 from langchain_core.tracers.schemas import Run19441945 import time194619471948 def test_runnable(time_to_sleep: int):1949 time.sleep(time_to_sleep)195019511952 def fn_start(run_obj: Run):1953 print("start_time:", run_obj.start_time)195419551956 def fn_end(run_obj: Run):1957 print("end_time:", run_obj.end_time)195819591960 chain = RunnableLambda(test_runnable).with_listeners(1961 on_start=fn_start, on_end=fn_end1962 )1963 chain.invoke(2)1964 ```1965 """1966 return RunnableBinding(1967 bound=self,1968 config_factories=[1969 lambda config: {1970 "callbacks": [1971 RootListenersTracer(1972 config=config,1973 on_start=on_start,1974 on_end=on_end,1975 on_error=on_error,1976 )1977 ],1978 }1979 ],1980 )19811982 def with_alisteners(1983 self,1984 *,1985 on_start: AsyncListener | None = None,1986 on_end: AsyncListener | None = None,1987 on_error: AsyncListener | None = None,1988 ) -> Runnable[Input, Output]:1989 """Bind async lifecycle listeners to a `Runnable`.19901991 Returns a new `Runnable`.19921993 The Run object contains information about the run, including its `id`,1994 `type`, `input`, `output`, `error`, `start_time`, `end_time`, and1995 any tags or metadata added to the run.19961997 Args:1998 on_start: Called asynchronously before the `Runnable` starts running,1999 with the `Run` object.2000 on_end: Called asynchronously after the `Runnable` finishes running,
Findings
✓ No findings reported for this file.