Avoid global variables; use function parameters or class attributes for better scope management
will be printed to the console. Defaults to the global `verbose` value,
1"""Base interface that all chains should implement."""23import builtins4import contextlib5import inspect6import json7import logging8import warnings9from abc import ABC, abstractmethod10from pathlib import Path11from typing import Any, cast1213import yaml14from langchain_core._api import deprecated15from langchain_core.callbacks import (16 AsyncCallbackManager,17 AsyncCallbackManagerForChainRun,18 BaseCallbackManager,19 CallbackManager,20 CallbackManagerForChainRun,21 Callbacks,22)23from langchain_core.outputs import RunInfo24from langchain_core.runnables import (25 RunnableConfig,26 RunnableSerializable,27 ensure_config,28 run_in_executor,29)30from langchain_core.utils.pydantic import create_model31from pydantic import (32 BaseModel,33 ConfigDict,34 Field,35 field_validator,36 model_validator,37)38from typing_extensions import override3940from langchain_classic.base_memory import BaseMemory41from langchain_classic.schema import RUN_KEY4243logger = logging.getLogger(__name__)444546def _get_verbosity() -> bool:47 from langchain_classic.globals import get_verbose4849 return get_verbose()505152class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC):53 """Abstract base class for creating structured sequences of calls to components.5455 Chains should be used to encode a sequence of calls to components like56 models, document retrievers, other chains, etc., and provide a simple interface57 to this sequence.5859 The Chain interface makes it easy to create apps that are:60 - Stateful: add Memory to any Chain to give it state,61 - Observable: pass Callbacks to a Chain to execute additional functionality,62 like logging, outside the main sequence of component calls,63 - Composable: the Chain API is flexible enough that it is easy to combine64 Chains with other components, including other Chains.6566 The main methods exposed by chains are:67 - `__call__`: Chains are callable. The `__call__` method is the primary way to68 execute a Chain. This takes inputs as a dictionary and returns a69 dictionary output.70 - `run`: A convenience method that takes inputs as args/kwargs and returns the71 output as a string or object. This method can only be used for a subset of72 chains and cannot return as rich of an output as `__call__`.73 """7475 memory: BaseMemory | None = None76 """Optional memory object.77 Memory is a class that gets called at the start78 and at the end of every chain. At the start, memory loads variables and passes79 them along in the chain. At the end, it saves any returned variables.80 There are many different types of memory - please see memory docs81 for the full catalog."""82 callbacks: Callbacks = Field(default=None, exclude=True)83 """Optional list of callback handlers (or callback manager).84 Callback handlers are called throughout the lifecycle of a call to a chain,85 starting with on_chain_start, ending with on_chain_end or on_chain_error.86 Each custom chain can optionally call additional callback methods, see Callback docs87 for full details."""88 verbose: bool = Field(default_factory=_get_verbosity)89 """Whether or not run in verbose mode. In verbose mode, some intermediate logs90 will be printed to the console. Defaults to the global `verbose` value,91 accessible via `langchain.globals.get_verbose()`."""92 tags: list[str] | None = None93 """Optional list of tags associated with the chain.94 These tags will be associated with each call to this chain,95 and passed as arguments to the handlers defined in `callbacks`.96 You can use these to eg identify a specific instance of a chain with its use case.97 """98 metadata: builtins.dict[str, Any] | None = None99 """Optional metadata associated with the chain.100 This metadata will be associated with each call to this chain,101 and passed as arguments to the handlers defined in `callbacks`.102 You can use these to eg identify a specific instance of a chain with its use case.103 """104 callback_manager: BaseCallbackManager | None = Field(default=None, exclude=True)105 """[DEPRECATED] Use `callbacks` instead."""106107 model_config = ConfigDict(108 arbitrary_types_allowed=True,109 )110111 @override112 def get_input_schema(113 self,114 config: RunnableConfig | None = None,115 ) -> type[BaseModel]:116 # This is correct, but pydantic typings/mypy don't think so.117 return create_model("ChainInput", **dict.fromkeys(self.input_keys, (Any, None)))118119 @override120 def get_output_schema(121 self,122 config: RunnableConfig | None = None,123 ) -> type[BaseModel]:124 # This is correct, but pydantic typings/mypy don't think so.125 return create_model(126 "ChainOutput",127 **dict.fromkeys(self.output_keys, (Any, None)),128 )129130 @override131 def invoke(132 self,133 input: dict[str, Any],134 config: RunnableConfig | None = None,135 **kwargs: Any,136 ) -> dict[str, Any]:137 config = ensure_config(config)138 callbacks = config.get("callbacks")139 tags = config.get("tags")140 metadata = config.get("metadata")141 run_name = config.get("run_name") or self.get_name()142 run_id = config.get("run_id")143 include_run_info = kwargs.get("include_run_info", False)144 return_only_outputs = kwargs.get("return_only_outputs", False)145146 inputs = self.prep_inputs(input)147 callback_manager = CallbackManager.configure(148 callbacks,149 self.callbacks,150 self.verbose,151 tags,152 self.tags,153 metadata,154 self.metadata,155 )156 new_arg_supported = inspect.signature(self._call).parameters.get("run_manager")157158 run_manager = callback_manager.on_chain_start(159 None,160 inputs,161 run_id,162 name=run_name,163 )164 try:165 self._validate_inputs(inputs)166 outputs = (167 self._call(inputs, run_manager=run_manager)168 if new_arg_supported169 else self._call(inputs)170 )171172 final_outputs: dict[str, Any] = self.prep_outputs(173 inputs,174 outputs,175 return_only_outputs,176 )177 except BaseException as e:178 run_manager.on_chain_error(e)179 raise180 run_manager.on_chain_end(outputs)181182 if include_run_info:183 final_outputs[RUN_KEY] = RunInfo(run_id=run_manager.run_id)184 return final_outputs185186 @override187 async def ainvoke(188 self,189 input: dict[str, Any],190 config: RunnableConfig | None = None,191 **kwargs: Any,192 ) -> dict[str, Any]:193 config = ensure_config(config)194 callbacks = config.get("callbacks")195 tags = config.get("tags")196 metadata = config.get("metadata")197 run_name = config.get("run_name") or self.get_name()198 run_id = config.get("run_id")199 include_run_info = kwargs.get("include_run_info", False)200 return_only_outputs = kwargs.get("return_only_outputs", False)201202 inputs = await self.aprep_inputs(input)203 callback_manager = AsyncCallbackManager.configure(204 callbacks,205 self.callbacks,206 self.verbose,207 tags,208 self.tags,209 metadata,210 self.metadata,211 )212 new_arg_supported = inspect.signature(self._acall).parameters.get("run_manager")213 run_manager = await callback_manager.on_chain_start(214 None,215 inputs,216 run_id,217 name=run_name,218 )219 try:220 self._validate_inputs(inputs)221 outputs = (222 await self._acall(inputs, run_manager=run_manager)223 if new_arg_supported224 else await self._acall(inputs)225 )226 final_outputs: dict[str, Any] = await self.aprep_outputs(227 inputs,228 outputs,229 return_only_outputs,230 )231 except BaseException as e:232 await run_manager.on_chain_error(e)233 raise234 await run_manager.on_chain_end(outputs)235236 if include_run_info:237 final_outputs[RUN_KEY] = RunInfo(run_id=run_manager.run_id)238 return final_outputs239240 @property241 def _chain_type(self) -> str:242 msg = "Saving not supported for this chain type."243 raise NotImplementedError(msg)244245 @model_validator(mode="before")246 @classmethod247 def raise_callback_manager_deprecation(cls, values: dict) -> Any:248 """Raise deprecation warning if callback_manager is used."""249 if values.get("callback_manager") is not None:250 if values.get("callbacks") is not None:251 msg = (252 "Cannot specify both callback_manager and callbacks. "253 "callback_manager is deprecated, callbacks is the preferred "254 "parameter to pass in."255 )256 raise ValueError(msg)257 warnings.warn(258 "callback_manager is deprecated. Please use callbacks instead.",259 DeprecationWarning,260 stacklevel=4,261 )262 values["callbacks"] = values.pop("callback_manager", None)263 return values264265 @field_validator("verbose", mode="before")266 @classmethod267 def set_verbose(268 cls,269 verbose: bool | None, # noqa: FBT001270 ) -> bool:271 """Set the chain verbosity.272273 Defaults to the global setting if not specified by the user.274 """275 if verbose is None:276 return _get_verbosity()277 return verbose278279 @property280 @abstractmethod281 def input_keys(self) -> list[str]:282 """Keys expected to be in the chain input."""283284 @property285 @abstractmethod286 def output_keys(self) -> list[str]:287 """Keys expected to be in the chain output."""288289 def _validate_inputs(self, inputs: Any) -> None:290 """Check that all inputs are present."""291 if not isinstance(inputs, dict):292 _input_keys = set(self.input_keys)293 if self.memory is not None:294 # If there are multiple input keys, but some get set by memory so that295 # only one is not set, we can still figure out which key it is.296 _input_keys = _input_keys.difference(self.memory.memory_variables)297 if len(_input_keys) != 1:298 msg = (299 f"A single string input was passed in, but this chain expects "300 f"multiple inputs ({_input_keys}). When a chain expects "301 f"multiple inputs, please call it by passing in a dictionary, "302 "eg `chain({'foo': 1, 'bar': 2})`"303 )304 raise ValueError(msg)305306 missing_keys = set(self.input_keys).difference(inputs)307 if missing_keys:308 msg = f"Missing some input keys: {missing_keys}"309 raise ValueError(msg)310311 def _validate_outputs(self, outputs: dict[str, Any]) -> None:312 missing_keys = set(self.output_keys).difference(outputs)313 if missing_keys:314 msg = f"Missing some output keys: {missing_keys}"315 raise ValueError(msg)316317 @abstractmethod318 def _call(319 self,320 inputs: builtins.dict[str, Any],321 run_manager: CallbackManagerForChainRun | None = None,322 ) -> builtins.dict[str, Any]:323 """Execute the chain.324325 This is a private method that is not user-facing. It is only called within326 `Chain.__call__`, which is the user-facing wrapper method that handles327 callbacks configuration and some input/output processing.328329 Args:330 inputs: A dict of named inputs to the chain. Assumed to contain all inputs331 specified in `Chain.input_keys`, including any inputs added by memory.332 run_manager: The callbacks manager that contains the callback handlers for333 this run of the chain.334335 Returns:336 A dict of named outputs. Should contain all outputs specified in337 `Chain.output_keys`.338 """339340 async def _acall(341 self,342 inputs: builtins.dict[str, Any],343 run_manager: AsyncCallbackManagerForChainRun | None = None,344 ) -> builtins.dict[str, Any]:345 """Asynchronously execute the chain.346347 This is a private method that is not user-facing. It is only called within348 `Chain.acall`, which is the user-facing wrapper method that handles349 callbacks configuration and some input/output processing.350351 Args:352 inputs: A dict of named inputs to the chain. Assumed to contain all inputs353 specified in `Chain.input_keys`, including any inputs added by memory.354 run_manager: The callbacks manager that contains the callback handlers for355 this run of the chain.356357 Returns:358 A dict of named outputs. Should contain all outputs specified in359 `Chain.output_keys`.360 """361 return await run_in_executor(362 None,363 self._call,364 inputs,365 run_manager.get_sync() if run_manager else None,366 )367368 @deprecated("0.1.0", alternative="invoke", removal="2.0.0")369 def __call__(370 self,371 inputs: dict[str, Any] | Any,372 return_only_outputs: bool = False, # noqa: FBT001,FBT002373 callbacks: Callbacks = None,374 *,375 tags: list[str] | None = None,376 metadata: dict[str, Any] | None = None,377 run_name: str | None = None,378 include_run_info: bool = False,379 ) -> dict[str, Any]:380 """Execute the chain.381382 Args:383 inputs: Dictionary of inputs, or single input if chain expects384 only one param. Should contain all inputs specified in385 `Chain.input_keys` except for inputs that will be set by the chain's386 memory.387 return_only_outputs: Whether to return only outputs in the388 response. If `True`, only new keys generated by this chain will be389 returned. If `False`, both input keys and new keys generated by this390 chain will be returned.391 callbacks: Callbacks to use for this chain run. These will be called in392 addition to callbacks passed to the chain during construction, but only393 these runtime callbacks will propagate to calls to other objects.394 tags: List of string tags to pass to all callbacks. These will be passed in395 addition to tags passed to the chain during construction, but only396 these runtime tags will propagate to calls to other objects.397 metadata: Optional metadata associated with the chain.398 run_name: Optional name for this run of the chain.399 include_run_info: Whether to include run info in the response. Defaults400 to False.401402 Returns:403 A dict of named outputs. Should contain all outputs specified in404 `Chain.output_keys`.405 """406 config = {407 "callbacks": callbacks,408 "tags": tags,409 "metadata": metadata,410 "run_name": run_name,411 }412413 return self.invoke(414 inputs,415 cast("RunnableConfig", {k: v for k, v in config.items() if v is not None}),416 return_only_outputs=return_only_outputs,417 include_run_info=include_run_info,418 )419420 @deprecated("0.1.0", alternative="ainvoke", removal="2.0.0")421 async def acall(422 self,423 inputs: dict[str, Any] | Any,424 return_only_outputs: bool = False, # noqa: FBT001,FBT002425 callbacks: Callbacks = None,426 *,427 tags: list[str] | None = None,428 metadata: dict[str, Any] | None = None,429 run_name: str | None = None,430 include_run_info: bool = False,431 ) -> dict[str, Any]:432 """Asynchronously execute the chain.433434 Args:435 inputs: Dictionary of inputs, or single input if chain expects436 only one param. Should contain all inputs specified in437 `Chain.input_keys` except for inputs that will be set by the chain's438 memory.439 return_only_outputs: Whether to return only outputs in the440 response. If `True`, only new keys generated by this chain will be441 returned. If `False`, both input keys and new keys generated by this442 chain will be returned.443 callbacks: Callbacks to use for this chain run. These will be called in444 addition to callbacks passed to the chain during construction, but only445 these runtime callbacks will propagate to calls to other objects.446 tags: List of string tags to pass to all callbacks. These will be passed in447 addition to tags passed to the chain during construction, but only448 these runtime tags will propagate to calls to other objects.449 metadata: Optional metadata associated with the chain.450 run_name: Optional name for this run of the chain.451 include_run_info: Whether to include run info in the response. Defaults452 to False.453454 Returns:455 A dict of named outputs. Should contain all outputs specified in456 `Chain.output_keys`.457 """458 config = {459 "callbacks": callbacks,460 "tags": tags,461 "metadata": metadata,462 "run_name": run_name,463 }464 return await self.ainvoke(465 inputs,466 cast("RunnableConfig", {k: v for k, v in config.items() if k is not None}),467 return_only_outputs=return_only_outputs,468 include_run_info=include_run_info,469 )470471 def prep_outputs(472 self,473 inputs: dict[str, str],474 outputs: dict[str, str],475 return_only_outputs: bool = False, # noqa: FBT001,FBT002476 ) -> dict[str, str]:477 """Validate and prepare chain outputs, and save info about this run to memory.478479 Args:480 inputs: Dictionary of chain inputs, including any inputs added by chain481 memory.482 outputs: Dictionary of initial chain outputs.483 return_only_outputs: Whether to only return the chain outputs. If `False`,484 inputs are also added to the final outputs.485486 Returns:487 A dict of the final chain outputs.488 """489 self._validate_outputs(outputs)490 if self.memory is not None:491 self.memory.save_context(inputs, outputs)492 if return_only_outputs:493 return outputs494 return {**inputs, **outputs}495496 async def aprep_outputs(497 self,498 inputs: dict[str, str],499 outputs: dict[str, str],500 return_only_outputs: bool = False, # noqa: FBT001,FBT002501 ) -> dict[str, str]:502 """Validate and prepare chain outputs, and save info about this run to memory.503504 Args:505 inputs: Dictionary of chain inputs, including any inputs added by chain506 memory.507 outputs: Dictionary of initial chain outputs.508 return_only_outputs: Whether to only return the chain outputs. If `False`,509 inputs are also added to the final outputs.510511 Returns:512 A dict of the final chain outputs.513 """514 self._validate_outputs(outputs)515 if self.memory is not None:516 await self.memory.asave_context(inputs, outputs)517 if return_only_outputs:518 return outputs519 return {**inputs, **outputs}520521 def prep_inputs(self, inputs: dict[str, Any] | Any) -> dict[str, str]:522 """Prepare chain inputs, including adding inputs from memory.523524 Args:525 inputs: Dictionary of raw inputs, or single input if chain expects526 only one param. Should contain all inputs specified in527 `Chain.input_keys` except for inputs that will be set by the chain's528 memory.529530 Returns:531 A dictionary of all inputs, including those added by the chain's memory.532 """533 if not isinstance(inputs, dict):534 _input_keys = set(self.input_keys)535 if self.memory is not None:536 # If there are multiple input keys, but some get set by memory so that537 # only one is not set, we can still figure out which key it is.538 _input_keys = _input_keys.difference(self.memory.memory_variables)539 inputs = {next(iter(_input_keys)): inputs}540 if self.memory is not None:541 external_context = self.memory.load_memory_variables(inputs)542 inputs = dict(inputs, **external_context)543 return inputs544545 async def aprep_inputs(self, inputs: dict[str, Any] | Any) -> dict[str, str]:546 """Prepare chain inputs, including adding inputs from memory.547548 Args:549 inputs: Dictionary of raw inputs, or single input if chain expects550 only one param. Should contain all inputs specified in551 `Chain.input_keys` except for inputs that will be set by the chain's552 memory.553554 Returns:555 A dictionary of all inputs, including those added by the chain's memory.556 """557 if not isinstance(inputs, dict):558 _input_keys = set(self.input_keys)559 if self.memory is not None:560 # If there are multiple input keys, but some get set by memory so that561 # only one is not set, we can still figure out which key it is.562 _input_keys = _input_keys.difference(self.memory.memory_variables)563 inputs = {next(iter(_input_keys)): inputs}564 if self.memory is not None:565 external_context = await self.memory.aload_memory_variables(inputs)566 inputs = dict(inputs, **external_context)567 return inputs568569 @property570 def _run_output_key(self) -> str:571 if len(self.output_keys) != 1:572 msg = (573 f"`run` not supported when there is not exactly "574 f"one output key. Got {self.output_keys}."575 )576 raise ValueError(msg)577 return self.output_keys[0]578579 @deprecated("0.1.0", alternative="invoke", removal="2.0.0")580 def run(581 self,582 *args: Any,583 callbacks: Callbacks = None,584 tags: list[str] | None = None,585 metadata: dict[str, Any] | None = None,586 **kwargs: Any,587 ) -> Any:588 """Convenience method for executing chain.589590 The main difference between this method and `Chain.__call__` is that this591 method expects inputs to be passed directly in as positional arguments or592 keyword arguments, whereas `Chain.__call__` expects a single input dictionary593 with all the inputs594595 Args:596 *args: If the chain expects a single input, it can be passed in as the597 sole positional argument.598 callbacks: Callbacks to use for this chain run. These will be called in599 addition to callbacks passed to the chain during construction, but only600 these runtime callbacks will propagate to calls to other objects.601 tags: List of string tags to pass to all callbacks. These will be passed in602 addition to tags passed to the chain during construction, but only603 these runtime tags will propagate to calls to other objects.604 metadata: Optional metadata associated with the chain.605 **kwargs: If the chain expects multiple inputs, they can be passed in606 directly as keyword arguments.607608 Returns:609 The chain output.610611 Example:612 ```python613 # Suppose we have a single-input chain that takes a 'question' string:614 chain.run("What's the temperature in Boise, Idaho?")615 # -> "The temperature in Boise is..."616617 # Suppose we have a multi-input chain that takes a 'question' string618 # and 'context' string:619 question = "What's the temperature in Boise, Idaho?"620 context = "Weather report for Boise, Idaho on 07/03/23..."621 chain.run(question=question, context=context)622 # -> "The temperature in Boise is..."623 ```624 """625 # Run at start to make sure this is possible/defined626 _output_key = self._run_output_key627628 if args and not kwargs:629 if len(args) != 1:630 msg = "`run` supports only one positional argument."631 raise ValueError(msg)632 return self(args[0], callbacks=callbacks, tags=tags, metadata=metadata)[633 _output_key634 ]635636 if kwargs and not args:637 return self(kwargs, callbacks=callbacks, tags=tags, metadata=metadata)[638 _output_key639 ]640641 if not kwargs and not args:642 msg = (643 "`run` supported with either positional arguments or keyword arguments,"644 " but none were provided."645 )646 raise ValueError(msg)647 msg = (648 f"`run` supported with either positional arguments or keyword arguments"649 f" but not both. Got args: {args} and kwargs: {kwargs}."650 )651 raise ValueError(msg)652653 @deprecated("0.1.0", alternative="ainvoke", removal="2.0.0")654 async def arun(655 self,656 *args: Any,657 callbacks: Callbacks = None,658 tags: list[str] | None = None,659 metadata: dict[str, Any] | None = None,660 **kwargs: Any,661 ) -> Any:662 """Convenience method for executing chain.663664 The main difference between this method and `Chain.__call__` is that this665 method expects inputs to be passed directly in as positional arguments or666 keyword arguments, whereas `Chain.__call__` expects a single input dictionary667 with all the inputs668669670 Args:671 *args: If the chain expects a single input, it can be passed in as the672 sole positional argument.673 callbacks: Callbacks to use for this chain run. These will be called in674 addition to callbacks passed to the chain during construction, but only675 these runtime callbacks will propagate to calls to other objects.676 tags: List of string tags to pass to all callbacks. These will be passed in677 addition to tags passed to the chain during construction, but only678 these runtime tags will propagate to calls to other objects.679 metadata: Optional metadata associated with the chain.680 **kwargs: If the chain expects multiple inputs, they can be passed in681 directly as keyword arguments.682683 Returns:684 The chain output.685686 Example:687 ```python688 # Suppose we have a single-input chain that takes a 'question' string:689 await chain.arun("What's the temperature in Boise, Idaho?")690 # -> "The temperature in Boise is..."691692 # Suppose we have a multi-input chain that takes a 'question' string693 # and 'context' string:694 question = "What's the temperature in Boise, Idaho?"695 context = "Weather report for Boise, Idaho on 07/03/23..."696 await chain.arun(question=question, context=context)697 # -> "The temperature in Boise is..."698 ```699 """700 if len(self.output_keys) != 1:701 msg = (702 f"`run` not supported when there is not exactly "703 f"one output key. Got {self.output_keys}."704 )705 raise ValueError(msg)706 if args and not kwargs:707 if len(args) != 1:708 msg = "`run` supports only one positional argument."709 raise ValueError(msg)710 return (711 await self.acall(712 args[0],713 callbacks=callbacks,714 tags=tags,715 metadata=metadata,716 )717 )[self.output_keys[0]]718719 if kwargs and not args:720 return (721 await self.acall(722 kwargs,723 callbacks=callbacks,724 tags=tags,725 metadata=metadata,726 )727 )[self.output_keys[0]]728729 msg = (730 f"`run` supported with either positional arguments or keyword arguments"731 f" but not both. Got args: {args} and kwargs: {kwargs}."732 )733 raise ValueError(msg)734735 def model_dump(self, **kwargs: Any) -> dict:736 """Dictionary representation of chain.737738 Expects `Chain._chain_type` property to be implemented and for memory to be739 null.740741 Args:742 **kwargs: Keyword arguments passed to default743 `pydantic.BaseModel.model_dump` method.744745 Returns:746 A dictionary representation of the chain.747748 Example:749 ```python750 chain.model_dump(exclude_unset=True)751 # -> {"_type": "foo", "verbose": False, ...}752 ```753 """754 _dict = super().model_dump(**kwargs)755 with contextlib.suppress(NotImplementedError):756 _dict["_type"] = self._chain_type757 return _dict758759 def save(self, file_path: Path | str) -> None:760 """Save the chain.761762 Expects `Chain._chain_type` property to be implemented and for memory to be763 null.764765 Args:766 file_path: Path to file to save the chain to.767768 Example:769 ```python770 chain.save(file_path="path/chain.yaml")771 ```772 """773 if self.memory is not None:774 msg = "Saving of memory is not yet supported."775 raise ValueError(msg)776777 # Fetch dictionary to save778 chain_dict = self.model_dump()779 if "_type" not in chain_dict:780 msg = f"Chain {self} does not support saving."781 raise NotImplementedError(msg)782783 # Convert file to Path object.784 save_path = Path(file_path) if isinstance(file_path, str) else file_path785786 directory_path = save_path.parent787 directory_path.mkdir(parents=True, exist_ok=True)788789 if save_path.suffix == ".json":790 with save_path.open("w") as f:791 json.dump(chain_dict, f, indent=4)792 elif save_path.suffix.endswith((".yaml", ".yml")):793 with save_path.open("w") as f:794 yaml.dump(chain_dict, f, default_flow_style=False)795 else:796 msg = f"{save_path} must be json or yaml"797 raise ValueError(msg)798799 @deprecated("0.1.0", alternative="batch", removal="2.0.0")800 def apply(801 self,802 input_list: list[builtins.dict[str, Any]],803 callbacks: Callbacks = None,804 ) -> list[builtins.dict[str, str]]:805 """Call the chain on all inputs in the list."""806 return [self(inputs, callbacks=callbacks) for inputs in input_list]
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.