Ensure functions have docstrings for documentation
def decorator(func: Callable) -> Callable:
1"""Generic utility functions."""23import contextlib4import datetime5import functools6import importlib7import os8import warnings9from collections.abc import Callable, Iterator, Sequence10from importlib.metadata import version11from typing import Any, overload12from uuid import uuid41314from packaging.version import parse15from pydantic import SecretStr16from requests import HTTPError, Response17from typing_extensions import override1819from langchain_core.utils.pydantic import (20 is_pydantic_v1_subclass,21)222324def xor_args(*arg_groups: tuple[str, ...]) -> Callable:25 """Validate specified keyword args are mutually exclusive.2627 Args:28 *arg_groups: Groups of mutually exclusive keyword args.2930 Returns:31 Decorator that validates the specified keyword args are mutually exclusive.32 """3334 def decorator(func: Callable) -> Callable:35 @functools.wraps(func)36 def wrapper(*args: Any, **kwargs: Any) -> Any:37 """Validate exactly one arg in each group is not None."""38 counts = [39 sum(1 for arg in arg_group if kwargs.get(arg) is not None)40 for arg_group in arg_groups41 ]42 invalid_groups = [i for i, count in enumerate(counts) if count != 1]43 if invalid_groups:44 invalid_group_names = [", ".join(arg_groups[i]) for i in invalid_groups]45 msg = (46 "Exactly one argument in each of the following"47 " groups must be defined:"48 f" {', '.join(invalid_group_names)}"49 )50 raise ValueError(msg)51 return func(*args, **kwargs)5253 return wrapper5455 return decorator565758def raise_for_status_with_text(response: Response) -> None:59 """Raise an error with the response text.6061 Args:62 response: The response to check for errors.6364 Raises:65 ValueError: If the response has an error status code.66 """67 try:68 response.raise_for_status()69 except HTTPError as e:70 raise ValueError(response.text) from e717273@contextlib.contextmanager74def mock_now(dt_value: datetime.datetime) -> Iterator[type]:75 """Context manager for mocking out datetime.now() in unit tests.7677 Args:78 dt_value: The datetime value to use for datetime.now().7980 Yields:81 The mocked datetime class.8283 Example:84 ```python85 with mock_now(datetime.datetime(2011, 2, 3, 10, 11)):86 assert datetime.datetime.now() == datetime.datetime(2011, 2, 3, 10, 11)87 ```88 """8990 class MockDateTime(datetime.datetime):91 """Mock datetime.datetime.now() with a fixed datetime."""9293 @classmethod94 @override95 def now(cls, tz: datetime.tzinfo | None = None) -> "MockDateTime":96 # Create a copy of dt_value.97 return MockDateTime(98 dt_value.year,99 dt_value.month,100 dt_value.day,101 dt_value.hour,102 dt_value.minute,103 dt_value.second,104 dt_value.microsecond,105 dt_value.tzinfo,106 )107108 real_datetime = datetime.datetime109 datetime.datetime = MockDateTime # type: ignore[misc]110 try:111 yield datetime.datetime112 finally:113 datetime.datetime = real_datetime # type: ignore[misc]114115116def guard_import(117 module_name: str, *, pip_name: str | None = None, package: str | None = None118) -> Any:119 """Dynamically import a module.120121 Raise an exception if the module is not installed.122123 Args:124 module_name: The name of the module to import.125 pip_name: The name of the module to install with pip.126 package: The package to import the module from.127128 Returns:129 The imported module.130131 Raises:132 ImportError: If the module is not installed.133 """134 try:135 module = importlib.import_module(module_name, package)136 except (ImportError, ModuleNotFoundError) as e:137 pip_name = pip_name or module_name.split(".", maxsplit=1)[0].replace("_", "-")138 msg = (139 f"Could not import {module_name} python package. "140 f"Please install it with `pip install {pip_name}`."141 )142 raise ImportError(msg) from e143 return module144145146def check_package_version(147 package: str,148 lt_version: str | None = None,149 lte_version: str | None = None,150 gt_version: str | None = None,151 gte_version: str | None = None,152) -> None:153 """Check the version of a package.154155 Args:156 package: The name of the package.157 lt_version: The version must be less than this.158 lte_version: The version must be less than or equal to this.159 gt_version: The version must be greater than this.160 gte_version: The version must be greater than or equal to this.161162163 Raises:164 ValueError: If the package version does not meet the requirements.165 """166 imported_version = parse(version(package))167 if lt_version is not None and imported_version >= parse(lt_version):168 msg = (169 f"Expected {package} version to be < {lt_version}. Received "170 f"{imported_version}."171 )172 raise ValueError(msg)173 if lte_version is not None and imported_version > parse(lte_version):174 msg = (175 f"Expected {package} version to be <= {lte_version}. Received "176 f"{imported_version}."177 )178 raise ValueError(msg)179 if gt_version is not None and imported_version <= parse(gt_version):180 msg = (181 f"Expected {package} version to be > {gt_version}. Received "182 f"{imported_version}."183 )184 raise ValueError(msg)185 if gte_version is not None and imported_version < parse(gte_version):186 msg = (187 f"Expected {package} version to be >= {gte_version}. Received "188 f"{imported_version}."189 )190 raise ValueError(msg)191192193def get_pydantic_field_names(pydantic_cls: Any) -> set[str]:194 """Get field names, including aliases, for a pydantic class.195196 Args:197 pydantic_cls: Pydantic class.198199 Returns:200 Field names.201 """202 all_required_field_names = set()203 if is_pydantic_v1_subclass(pydantic_cls):204 for field in pydantic_cls.__fields__.values():205 all_required_field_names.add(field.name)206 if field.has_alias:207 all_required_field_names.add(field.alias)208 else: # Assuming pydantic 2 for now209 for name, field in pydantic_cls.model_fields.items():210 all_required_field_names.add(name)211 if field.alias:212 all_required_field_names.add(field.alias)213 return all_required_field_names214215216def _build_model_kwargs(217 values: dict[str, Any],218 all_required_field_names: set[str],219) -> dict[str, Any]:220 """Build `model_kwargs` param from Pydantic constructor values.221222 Args:223 values: All init args passed in by user.224 all_required_field_names: All required field names for the pydantic class.225226 Returns:227 Extra kwargs.228229 Raises:230 ValueError: If a field is specified in both `values` and `extra_kwargs`.231 ValueError: If a field is specified in `model_kwargs`.232 """233 extra_kwargs = values.get("model_kwargs", {})234 for field_name in list(values):235 if field_name in extra_kwargs:236 msg = f"Found {field_name} supplied twice."237 raise ValueError(msg)238 if field_name not in all_required_field_names:239 warnings.warn(240 f"""WARNING! {field_name} is not default parameter.241 {field_name} was transferred to model_kwargs.242 Please confirm that {field_name} is what you intended.""",243 stacklevel=7,244 )245 extra_kwargs[field_name] = values.pop(field_name)246247 invalid_model_kwargs = all_required_field_names.intersection(extra_kwargs.keys())248 if invalid_model_kwargs:249 warnings.warn(250 f"Parameters {invalid_model_kwargs} should be specified explicitly. "251 f"Instead they were passed in as part of `model_kwargs` parameter.",252 stacklevel=7,253 )254 for k in invalid_model_kwargs:255 values[k] = extra_kwargs.pop(k)256257 values["model_kwargs"] = extra_kwargs258 return values259260261# DON'T USE! Kept for backwards-compatibility but should never have been public.262def build_extra_kwargs(263 extra_kwargs: dict[str, Any],264 values: dict[str, Any],265 all_required_field_names: set[str],266) -> dict[str, Any]:267 """Build extra kwargs from values and extra_kwargs.268269 !!! danger "DON'T USE"270271 Kept for backwards-compatibility but should never have been public. Use the272 internal `_build_model_kwargs` function instead.273274 Args:275 extra_kwargs: Extra kwargs passed in by user.276 values: Values passed in by user.277 all_required_field_names: All required field names for the pydantic class.278279 Returns:280 Extra kwargs.281282 Raises:283 ValueError: If a field is specified in both `values` and `extra_kwargs`.284 ValueError: If a field is specified in `model_kwargs`.285 """286 # DON'T USE! Kept for backwards-compatibility but should never have been public.287 for field_name in list(values):288 if field_name in extra_kwargs:289 msg = f"Found {field_name} supplied twice."290 raise ValueError(msg)291 if field_name not in all_required_field_names:292 warnings.warn(293 f"""WARNING! {field_name} is not default parameter.294 {field_name} was transferred to model_kwargs.295 Please confirm that {field_name} is what you intended.""",296 stacklevel=7,297 )298 extra_kwargs[field_name] = values.pop(field_name)299300 # DON'T USE! Kept for backwards-compatibility but should never have been public.301 invalid_model_kwargs = all_required_field_names.intersection(extra_kwargs.keys())302 if invalid_model_kwargs:303 msg = (304 f"Parameters {invalid_model_kwargs} should be specified explicitly. "305 f"Instead they were passed in as part of `model_kwargs` parameter."306 )307 raise ValueError(msg)308309 # DON'T USE! Kept for backwards-compatibility but should never have been public.310 return extra_kwargs311312313def convert_to_secret_str(value: SecretStr | str) -> SecretStr:314 """Convert a string to a `SecretStr` if needed.315316 Args:317 value: The value to convert.318319 Returns:320 The `SecretStr` value.321 """322 if isinstance(value, SecretStr):323 return value324 return SecretStr(value)325326327class _NoDefaultType:328 """Type to indicate no default value is provided."""329330331_NoDefault = _NoDefaultType()332333334@overload335def from_env(key: str, /) -> Callable[[], str]: ...336337338@overload339def from_env(key: str, /, *, default: str) -> Callable[[], str]: ...340341342@overload343def from_env(key: Sequence[str], /, *, default: str) -> Callable[[], str]: ...344345346@overload347def from_env(key: str, /, *, error_message: str) -> Callable[[], str]: ...348349350@overload351def from_env(352 key: str | Sequence[str], /, *, default: str, error_message: str | None353) -> Callable[[], str]: ...354355356@overload357def from_env(358 key: str, /, *, default: None, error_message: str | None359) -> Callable[[], str | None]: ...360361362@overload363def from_env(364 key: str | Sequence[str], /, *, default: None365) -> Callable[[], str | None]: ...366367368def from_env(369 key: str | Sequence[str],370 /,371 *,372 default: str | _NoDefaultType | None = _NoDefault,373 error_message: str | None = None,374) -> Callable[[], str] | Callable[[], str | None]:375 """Create a factory method that gets a value from an environment variable.376377 Args:378 key: The environment variable to look up.379380 If a list of keys is provided, the first key found in the environment will381 be used. If no key is found, the default value will be used if set,382 otherwise an error will be raised.383 default: The default value to return if the environment variable is not set.384 error_message: The error message which will be raised if the key is not found385 and no default value is provided.386387 This will be raised as a ValueError.388389 Returns:390 Factory method that will look up the value from the environment.391 """392393 def get_from_env_fn() -> str | None:394 """Get a value from an environment variable.395396 Raises:397 ValueError: If the environment variable is not set and no default is398 provided.399400 Returns:401 The value from the environment.402 """403 if isinstance(key, (list, tuple)):404 for k in key:405 if k in os.environ:406 return os.environ[k]407 if isinstance(key, str) and key in os.environ:408 return os.environ[key]409410 if isinstance(default, (str, type(None))):411 return default412 if error_message:413 raise ValueError(error_message)414 msg = (415 f"Did not find {key}, please add an environment variable"416 f" `{key}` which contains it, or pass"417 f" `{key}` as a named parameter."418 )419 raise ValueError(msg)420421 return get_from_env_fn422423424@overload425def secret_from_env(key: str | Sequence[str], /) -> Callable[[], SecretStr]: ...426427428@overload429def secret_from_env(key: str, /, *, default: str) -> Callable[[], SecretStr]: ...430431432@overload433def secret_from_env(434 key: str | Sequence[str], /, *, default: None435) -> Callable[[], SecretStr | None]: ...436437438@overload439def secret_from_env(key: str, /, *, error_message: str) -> Callable[[], SecretStr]: ...440441442def secret_from_env(443 key: str | Sequence[str],444 /,445 *,446 default: str | _NoDefaultType | None = _NoDefault,447 error_message: str | None = None,448) -> Callable[[], SecretStr | None] | Callable[[], SecretStr]:449 """Secret from env.450451 Args:452 key: The environment variable to look up.453 default: The default value to return if the environment variable is not set.454 error_message: The error message which will be raised if the key is not found455 and no default value is provided.456457 This will be raised as a `ValueError`.458459 Returns:460 Factory method that will look up the secret from the environment.461 """462463 def get_secret_from_env() -> SecretStr | None:464 """Get a value from an environment variable.465466 Raises:467 ValueError: If the environment variable is not set and no default is468 provided.469470 Returns:471 The secret from the environment.472 """473 if isinstance(key, (list, tuple)):474 for k in key:475 if k in os.environ:476 return SecretStr(os.environ[k])477 if isinstance(key, str) and key in os.environ:478 return SecretStr(os.environ[key])479 if isinstance(default, str):480 return SecretStr(default)481 if default is None:482 return None483 if error_message:484 raise ValueError(error_message)485 msg = (486 f"Did not find {key}, please add an environment variable"487 f" `{key}` which contains it, or pass"488 f" `{key}` as a named parameter."489 )490 raise ValueError(msg)491492 return get_secret_from_env493494495LC_AUTO_PREFIX = "lc_"496"""LangChain auto-generated ID prefix for messages and content blocks."""497498LC_ID_PREFIX = "lc_run-"499"""Internal tracing/callback system identifier.500501Used for:502503- Tracing. Every LangChain operation (LLM call, chain execution, tool use, etc.)504 gets a unique run_id (UUID)505- Enables tracking parent-child relationships between operations506"""507508509def ensure_id(id_val: str | None) -> str:510 """Ensure the ID is a valid string, generating a new UUID if not provided.511512 Auto-generated UUIDs are prefixed by `'lc_'` to indicate they are513 LangChain-generated IDs.514515 Args:516 id_val: Optional string ID value to validate.517518 Returns:519 A string ID, either the validated provided value or a newly generated UUID4.520 """521 return id_val or f"{LC_AUTO_PREFIX}{uuid4()}"
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.