langchain-core 0.3.75__py3-none-any.whl → 0.3.76__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of langchain-core might be problematic. Click here for more details.

Files changed (116) hide show
  1. langchain_core/_api/beta_decorator.py +17 -40
  2. langchain_core/_api/deprecation.py +19 -6
  3. langchain_core/_api/path.py +19 -2
  4. langchain_core/_import_utils.py +7 -0
  5. langchain_core/agents.py +10 -6
  6. langchain_core/beta/runnables/context.py +1 -2
  7. langchain_core/callbacks/base.py +11 -4
  8. langchain_core/callbacks/manager.py +81 -69
  9. langchain_core/callbacks/usage.py +4 -2
  10. langchain_core/chat_history.py +4 -6
  11. langchain_core/document_loaders/base.py +34 -9
  12. langchain_core/document_loaders/langsmith.py +3 -0
  13. langchain_core/documents/base.py +35 -10
  14. langchain_core/documents/transformers.py +4 -2
  15. langchain_core/embeddings/fake.py +8 -5
  16. langchain_core/env.py +2 -3
  17. langchain_core/example_selectors/base.py +12 -0
  18. langchain_core/exceptions.py +7 -0
  19. langchain_core/globals.py +17 -28
  20. langchain_core/indexing/api.py +56 -44
  21. langchain_core/indexing/base.py +5 -8
  22. langchain_core/indexing/in_memory.py +23 -3
  23. langchain_core/language_models/__init__.py +3 -2
  24. langchain_core/language_models/base.py +31 -20
  25. langchain_core/language_models/chat_models.py +94 -25
  26. langchain_core/language_models/fake_chat_models.py +5 -7
  27. langchain_core/language_models/llms.py +49 -17
  28. langchain_core/load/dump.py +2 -3
  29. langchain_core/load/load.py +15 -1
  30. langchain_core/load/serializable.py +38 -43
  31. langchain_core/memory.py +7 -3
  32. langchain_core/messages/ai.py +36 -19
  33. langchain_core/messages/base.py +13 -6
  34. langchain_core/messages/content_blocks.py +23 -2
  35. langchain_core/messages/human.py +2 -6
  36. langchain_core/messages/system.py +2 -6
  37. langchain_core/messages/tool.py +33 -13
  38. langchain_core/messages/utils.py +182 -72
  39. langchain_core/output_parsers/base.py +5 -2
  40. langchain_core/output_parsers/json.py +4 -4
  41. langchain_core/output_parsers/list.py +7 -22
  42. langchain_core/output_parsers/openai_functions.py +3 -0
  43. langchain_core/output_parsers/openai_tools.py +6 -1
  44. langchain_core/output_parsers/pydantic.py +4 -0
  45. langchain_core/output_parsers/string.py +5 -1
  46. langchain_core/output_parsers/xml.py +19 -19
  47. langchain_core/outputs/chat_generation.py +18 -7
  48. langchain_core/outputs/generation.py +14 -3
  49. langchain_core/outputs/llm_result.py +8 -1
  50. langchain_core/prompt_values.py +10 -4
  51. langchain_core/prompts/base.py +4 -9
  52. langchain_core/prompts/chat.py +87 -58
  53. langchain_core/prompts/dict.py +16 -8
  54. langchain_core/prompts/few_shot.py +9 -11
  55. langchain_core/prompts/few_shot_with_templates.py +5 -1
  56. langchain_core/prompts/image.py +12 -5
  57. langchain_core/prompts/message.py +5 -6
  58. langchain_core/prompts/pipeline.py +13 -8
  59. langchain_core/prompts/prompt.py +22 -8
  60. langchain_core/prompts/string.py +18 -10
  61. langchain_core/prompts/structured.py +7 -2
  62. langchain_core/rate_limiters.py +2 -2
  63. langchain_core/retrievers.py +7 -6
  64. langchain_core/runnables/base.py +402 -183
  65. langchain_core/runnables/branch.py +14 -19
  66. langchain_core/runnables/config.py +9 -15
  67. langchain_core/runnables/configurable.py +34 -19
  68. langchain_core/runnables/fallbacks.py +20 -13
  69. langchain_core/runnables/graph.py +44 -37
  70. langchain_core/runnables/graph_ascii.py +40 -17
  71. langchain_core/runnables/graph_mermaid.py +27 -15
  72. langchain_core/runnables/graph_png.py +27 -31
  73. langchain_core/runnables/history.py +55 -58
  74. langchain_core/runnables/passthrough.py +44 -21
  75. langchain_core/runnables/retry.py +9 -5
  76. langchain_core/runnables/router.py +9 -8
  77. langchain_core/runnables/schema.py +2 -0
  78. langchain_core/runnables/utils.py +51 -89
  79. langchain_core/stores.py +13 -25
  80. langchain_core/sys_info.py +9 -8
  81. langchain_core/tools/base.py +30 -23
  82. langchain_core/tools/convert.py +24 -13
  83. langchain_core/tools/simple.py +35 -3
  84. langchain_core/tools/structured.py +25 -2
  85. langchain_core/tracers/base.py +2 -2
  86. langchain_core/tracers/context.py +5 -1
  87. langchain_core/tracers/core.py +109 -39
  88. langchain_core/tracers/evaluation.py +22 -26
  89. langchain_core/tracers/event_stream.py +40 -27
  90. langchain_core/tracers/langchain.py +12 -3
  91. langchain_core/tracers/langchain_v1.py +10 -2
  92. langchain_core/tracers/log_stream.py +56 -17
  93. langchain_core/tracers/root_listeners.py +4 -20
  94. langchain_core/tracers/run_collector.py +6 -16
  95. langchain_core/tracers/schemas.py +5 -1
  96. langchain_core/utils/aiter.py +14 -6
  97. langchain_core/utils/env.py +3 -0
  98. langchain_core/utils/function_calling.py +37 -20
  99. langchain_core/utils/interactive_env.py +6 -2
  100. langchain_core/utils/iter.py +11 -3
  101. langchain_core/utils/json.py +5 -2
  102. langchain_core/utils/json_schema.py +15 -5
  103. langchain_core/utils/loading.py +5 -1
  104. langchain_core/utils/mustache.py +24 -15
  105. langchain_core/utils/pydantic.py +32 -4
  106. langchain_core/utils/utils.py +24 -8
  107. langchain_core/vectorstores/base.py +7 -20
  108. langchain_core/vectorstores/in_memory.py +18 -12
  109. langchain_core/vectorstores/utils.py +18 -12
  110. langchain_core/version.py +1 -1
  111. langchain_core-0.3.76.dist-info/METADATA +77 -0
  112. langchain_core-0.3.76.dist-info/RECORD +174 -0
  113. langchain_core-0.3.75.dist-info/METADATA +0 -106
  114. langchain_core-0.3.75.dist-info/RECORD +0 -174
  115. {langchain_core-0.3.75.dist-info → langchain_core-0.3.76.dist-info}/WHEEL +0 -0
  116. {langchain_core-0.3.75.dist-info → langchain_core-0.3.76.dist-info}/entry_points.txt +0 -0
@@ -21,8 +21,10 @@ from typing import (
21
21
 
22
22
  from pydantic import BaseModel
23
23
  from pydantic.v1 import BaseModel as BaseModelV1
24
+ from pydantic.v1 import Field, create_model
24
25
  from typing_extensions import TypedDict, get_args, get_origin, is_typeddict
25
26
 
27
+ import langchain_core
26
28
  from langchain_core._api import beta, deprecated
27
29
  from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage
28
30
  from langchain_core.utils.json_schema import dereference_refs
@@ -146,6 +148,9 @@ def _convert_pydantic_to_openai_function(
146
148
  of the schema will be used.
147
149
  rm_titles: Whether to remove titles from the schema. Defaults to True.
148
150
 
151
+ Raises:
152
+ TypeError: If the model is not a Pydantic model.
153
+
149
154
  Returns:
150
155
  The function description.
151
156
  """
@@ -217,10 +222,8 @@ def _convert_python_function_to_openai_function(
217
222
  Returns:
218
223
  The OpenAI function description.
219
224
  """
220
- from langchain_core.tools.base import create_schema_from_function
221
-
222
225
  func_name = _get_python_function_name(function)
223
- model = create_schema_from_function(
226
+ model = langchain_core.tools.base.create_schema_from_function(
224
227
  func_name,
225
228
  function,
226
229
  filter_args=(),
@@ -261,9 +264,6 @@ def _convert_any_typed_dicts_to_pydantic(
261
264
  visited: dict,
262
265
  depth: int = 0,
263
266
  ) -> type:
264
- from pydantic.v1 import Field as Field_v1
265
- from pydantic.v1 import create_model as create_model_v1
266
-
267
267
  if type_ in visited:
268
268
  return visited[type_]
269
269
  if depth >= _MAX_TYPED_DICT_RECURSION:
@@ -294,7 +294,7 @@ def _convert_any_typed_dicts_to_pydantic(
294
294
  raise ValueError(msg)
295
295
  if arg_desc := arg_descriptions.get(arg):
296
296
  field_kwargs["description"] = arg_desc
297
- fields[arg] = (new_arg_type, Field_v1(**field_kwargs))
297
+ fields[arg] = (new_arg_type, Field(**field_kwargs))
298
298
  else:
299
299
  new_arg_type = _convert_any_typed_dicts_to_pydantic(
300
300
  arg_type, depth=depth + 1, visited=visited
@@ -302,8 +302,8 @@ def _convert_any_typed_dicts_to_pydantic(
302
302
  field_kwargs = {"default": ...}
303
303
  if arg_desc := arg_descriptions.get(arg):
304
304
  field_kwargs["description"] = arg_desc
305
- fields[arg] = (new_arg_type, Field_v1(**field_kwargs))
306
- model = create_model_v1(typed_dict.__name__, **fields)
305
+ fields[arg] = (new_arg_type, Field(**field_kwargs))
306
+ model = create_model(typed_dict.__name__, **fields)
307
307
  model.__doc__ = description
308
308
  visited[typed_dict] = model
309
309
  return model
@@ -323,12 +323,15 @@ def _format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
323
323
  Args:
324
324
  tool: The tool to format.
325
325
 
326
+ Raises:
327
+ ValueError: If the tool call schema is not supported.
328
+
326
329
  Returns:
327
330
  The function description.
328
331
  """
329
- from langchain_core.tools import simple
330
-
331
- is_simple_oai_tool = isinstance(tool, simple.Tool) and not tool.args_schema
332
+ is_simple_oai_tool = (
333
+ isinstance(tool, langchain_core.tools.simple.Tool) and not tool.args_schema
334
+ )
332
335
  if tool.tool_call_schema and not is_simple_oai_tool:
333
336
  if isinstance(tool.tool_call_schema, dict):
334
337
  return _convert_json_schema_to_openai_function(
@@ -429,8 +432,6 @@ def convert_to_openai_function(
429
432
  'description' and 'parameters' keys are now optional. Only 'name' is
430
433
  required and guaranteed to be part of the output.
431
434
  """
432
- from langchain_core.tools import BaseTool
433
-
434
435
  # an Anthropic format tool
435
436
  if isinstance(function, dict) and all(
436
437
  k in function for k in ("name", "input_schema")
@@ -470,7 +471,7 @@ def convert_to_openai_function(
470
471
  oai_function = cast(
471
472
  "dict", _convert_typed_dict_to_openai_function(cast("type", function))
472
473
  )
473
- elif isinstance(function, BaseTool):
474
+ elif isinstance(function, langchain_core.tools.base.BaseTool):
474
475
  oai_function = cast("dict", _format_tool_to_openai_function(function))
475
476
  elif callable(function):
476
477
  oai_function = cast(
@@ -515,6 +516,7 @@ _WellKnownOpenAITools = (
515
516
  "mcp",
516
517
  "image_generation",
517
518
  "web_search_preview",
519
+ "web_search",
518
520
  )
519
521
 
520
522
 
@@ -575,7 +577,8 @@ def convert_to_openai_tool(
575
577
 
576
578
  Added support for OpenAI's image generation built-in tool.
577
579
  """
578
- from langchain_core.tools import Tool
580
+ # Import locally to prevent circular import
581
+ from langchain_core.tools import Tool # noqa: PLC0415
579
582
 
580
583
  if isinstance(tool, dict):
581
584
  if tool.get("type") in _WellKnownOpenAITools:
@@ -601,7 +604,20 @@ def convert_to_json_schema(
601
604
  *,
602
605
  strict: Optional[bool] = None,
603
606
  ) -> dict[str, Any]:
604
- """Convert a schema representation to a JSON schema."""
607
+ """Convert a schema representation to a JSON schema.
608
+
609
+ Args:
610
+ schema: The schema to convert.
611
+ strict: If True, model output is guaranteed to exactly match the JSON Schema
612
+ provided in the function definition. If None, ``strict`` argument will not
613
+ be included in function definition.
614
+
615
+ Raises:
616
+ ValueError: If the input is not a valid OpenAI-format tool.
617
+
618
+ Returns:
619
+ A JSON schema representation of the input schema.
620
+ """
605
621
  openai_tool = convert_to_openai_tool(schema, strict=strict)
606
622
  if (
607
623
  not isinstance(openai_tool, dict)
@@ -671,8 +687,10 @@ def tool_example_to_messages(
671
687
  from pydantic import BaseModel, Field
672
688
  from langchain_openai import ChatOpenAI
673
689
 
690
+
674
691
  class Person(BaseModel):
675
692
  '''Information about a person.'''
693
+
676
694
  name: Optional[str] = Field(..., description="The name of the person")
677
695
  hair_color: Optional[str] = Field(
678
696
  ..., description="The color of the person's hair if known"
@@ -681,6 +699,7 @@ def tool_example_to_messages(
681
699
  ..., description="Height in METERS"
682
700
  )
683
701
 
702
+
684
703
  examples = [
685
704
  (
686
705
  "The ocean is vast and blue. It's more than 20,000 feet deep.",
@@ -696,9 +715,7 @@ def tool_example_to_messages(
696
715
  messages = []
697
716
 
698
717
  for txt, tool_call in examples:
699
- messages.extend(
700
- tool_example_to_messages(txt, [tool_call])
701
- )
718
+ messages.extend(tool_example_to_messages(txt, [tool_call]))
702
719
 
703
720
  """
704
721
  messages: list[BaseMessage] = [HumanMessage(content=input)]
@@ -1,8 +1,12 @@
1
1
  """Utilities for working with interactive environments."""
2
2
 
3
+ import sys
4
+
3
5
 
4
6
  def is_interactive_env() -> bool:
5
- """Determine if running within IPython or Jupyter."""
6
- import sys
7
+ """Determine if running within IPython or Jupyter.
7
8
 
9
+ Returns:
10
+ True if running in an interactive environment, False otherwise.
11
+ """
8
12
  return hasattr(sys, "ps2")
@@ -31,7 +31,7 @@ class NoLock:
31
31
  exc_val: Optional[BaseException],
32
32
  exc_tb: Optional[TracebackType],
33
33
  ) -> Literal[False]:
34
- """Exception not handled."""
34
+ """Return False (exception not suppressed)."""
35
35
  return False
36
36
 
37
37
 
@@ -173,7 +173,11 @@ class Tee(Generic[T]):
173
173
  return self._children[item]
174
174
 
175
175
  def __iter__(self) -> Iterator[Iterator[T]]:
176
- """Return an iterator over the child iterators."""
176
+ """Return an iterator over the child iterators.
177
+
178
+ Yields:
179
+ The child iterators.
180
+ """
177
181
  yield from self._children
178
182
 
179
183
  def __enter__(self) -> "Tee[T]":
@@ -186,7 +190,11 @@ class Tee(Generic[T]):
186
190
  exc_val: Optional[BaseException],
187
191
  exc_tb: Optional[TracebackType],
188
192
  ) -> Literal[False]:
189
- """Close all child iterators."""
193
+ """Close all child iterators.
194
+
195
+ Returns:
196
+ False (exception not suppressed).
197
+ """
190
198
  self.close()
191
199
  return False
192
200
 
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import re
7
- from typing import Any, Callable
7
+ from typing import Any, Callable, Union
8
8
 
9
9
  from langchain_core.exceptions import OutputParserException
10
10
 
@@ -19,13 +19,16 @@ def _replace_new_line(match: re.Match[str]) -> str:
19
19
  return match.group(1) + value + match.group(3)
20
20
 
21
21
 
22
- def _custom_parser(multiline_string: str) -> str:
22
+ def _custom_parser(multiline_string: Union[str, bytes, bytearray]) -> str:
23
23
  r"""Custom parser for multiline strings.
24
24
 
25
25
  The LLM response for `action_input` may be a multiline
26
26
  string containing unescaped newlines, tabs or quotes. This function
27
27
  replaces those characters with their escaped counterparts.
28
28
  (newlines in JSON must be double-escaped: `\\n`).
29
+
30
+ Returns:
31
+ The modified string with escaped newlines, tabs and quotes.
29
32
  """
30
33
  if isinstance(multiline_string, (bytes, bytearray)):
31
34
  multiline_string = multiline_string.decode()
@@ -3,13 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from copy import deepcopy
6
- from typing import TYPE_CHECKING, Any, Optional
6
+ from typing import TYPE_CHECKING, Any, Optional, Union
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from collections.abc import Sequence
10
10
 
11
11
 
12
- def _retrieve_ref(path: str, schema: dict) -> dict:
12
+ def _retrieve_ref(path: str, schema: dict) -> Union[list, dict]:
13
13
  components = path.split("/")
14
14
  if components[0] != "#":
15
15
  msg = (
@@ -17,9 +17,12 @@ def _retrieve_ref(path: str, schema: dict) -> dict:
17
17
  "with #."
18
18
  )
19
19
  raise ValueError(msg)
20
- out = schema
20
+ out: Union[list, dict] = schema
21
21
  for component in components[1:]:
22
22
  if component in out:
23
+ if isinstance(out, list):
24
+ msg = f"Reference '{path}' not found."
25
+ raise KeyError(msg)
23
26
  out = out[component]
24
27
  elif component.isdigit():
25
28
  index = int(component)
@@ -46,10 +49,14 @@ def _dereference_refs_helper(
46
49
  """Inline every pure {'$ref':...}.
47
50
 
48
51
  But:
52
+
49
53
  - if shallow_refs=True: only break cycles, do not inline nested refs
50
54
  - if shallow_refs=False: deep-inline all nested refs
51
55
 
52
56
  Also skip recursion under any key in skip_keys.
57
+
58
+ Returns:
59
+ The object with refs dereferenced.
53
60
  """
54
61
  if processed_refs is None:
55
62
  processed_refs = set()
@@ -112,9 +119,12 @@ def dereference_refs(
112
119
  full_schema: The complete schema (defaults to schema_obj).
113
120
  skip_keys:
114
121
  - If None (the default), we skip recursion under '$defs' *and* only
115
- shallow-inline refs.
122
+ shallow-inline refs.
116
123
  - If provided (even as an empty list), we will recurse under every key and
117
- deep-inline all refs.
124
+ deep-inline all refs.
125
+
126
+ Returns:
127
+ The schema with refs dereferenced.
118
128
  """
119
129
  full = full_schema or schema_obj
120
130
  keys_to_skip = list(skip_keys) if skip_keys is not None else ["$defs"]
@@ -19,7 +19,11 @@ def try_load_from_hub(
19
19
  *args: Any, # noqa: ARG001
20
20
  **kwargs: Any, # noqa: ARG001
21
21
  ) -> Any:
22
- """[DEPRECATED] Try to load from the old Hub."""
22
+ """[DEPRECATED] Try to load from the old Hub.
23
+
24
+ Returns:
25
+ None always, indicating that we shouldn't load from the old hub.
26
+ """
23
27
  warnings.warn(
24
28
  "Loading from the deprecated github-based Hub is no longer supported. "
25
29
  "Please use the new LangChain Hub at https://smith.langchain.com/hub instead.",
@@ -82,7 +82,7 @@ def l_sa_check(
82
82
  """
83
83
  # If there is a newline, or the previous tag was a standalone
84
84
  if literal.find("\n") != -1 or is_standalone:
85
- padding = literal.split("\n")[-1]
85
+ padding = literal.rsplit("\n", maxsplit=1)[-1]
86
86
 
87
87
  # If all the characters since the last newline are spaces
88
88
  # Then the next tag could be a standalone
@@ -214,17 +214,22 @@ def tokenize(
214
214
  def_rdel: The default right delimiter
215
215
  ("}}" by default, as in spec compliant mustache)
216
216
 
217
- Returns:
218
- A generator of mustache tags in the form of a tuple (tag_type, tag_key)
219
- Where tag_type is one of:
220
- * literal
221
- * section
222
- * inverted section
223
- * end
224
- * partial
225
- * no escape
226
- And tag_key is either the key or in the case of a literal tag,
227
- the literal itself.
217
+ Yields:
218
+ Mustache tags in the form of a tuple (tag_type, tag_key)
219
+ where tag_type is one of:
220
+
221
+ * literal
222
+ * section
223
+ * inverted section
224
+ * end
225
+ * partial
226
+ * no escape
227
+
228
+ and tag_key is either the key or in the case of a literal tag,
229
+ the literal itself.
230
+
231
+ Raises:
232
+ ChevronError: If there is a syntax error in the template.
228
233
  """
229
234
  global _CURRENT_LINE, _LAST_TAG_LINE
230
235
  _CURRENT_LINE = 1
@@ -326,7 +331,7 @@ def tokenize(
326
331
 
327
332
 
328
333
  def _html_escape(string: str) -> str:
329
- """HTML escape all of these " & < >."""
334
+ """Return the HTML-escaped string with these characters escaped: ``" & < >``."""
330
335
  html_codes = {
331
336
  '"': "&quot;",
332
337
  "<": "&lt;",
@@ -349,7 +354,7 @@ def _get_key(
349
354
  def_ldel: str,
350
355
  def_rdel: str,
351
356
  ) -> Any:
352
- """Get a key from the current scope."""
357
+ """Return a key from the current scope."""
353
358
  # If the key is a dot
354
359
  if key == ".":
355
360
  # Then just return the current scope
@@ -407,7 +412,11 @@ def _get_key(
407
412
 
408
413
 
409
414
  def _get_partial(name: str, partials_dict: Mapping[str, str]) -> str:
410
- """Load a partial."""
415
+ """Load a partial.
416
+
417
+ Returns:
418
+ The partial.
419
+ """
411
420
  try:
412
421
  # Maybe the partial is in the dictionary
413
422
  return partials_dict[name]
@@ -57,6 +57,9 @@ def get_pydantic_major_version() -> int:
57
57
  """DEPRECATED - Get the major version of Pydantic.
58
58
 
59
59
  Use PYDANTIC_VERSION.major instead.
60
+
61
+ Returns:
62
+ The major version of Pydantic.
60
63
  """
61
64
  return PYDANTIC_VERSION.major
62
65
 
@@ -74,12 +77,20 @@ TBaseModel = TypeVar("TBaseModel", bound=PydanticBaseModel)
74
77
 
75
78
 
76
79
  def is_pydantic_v1_subclass(cls: type) -> bool:
77
- """Check if the installed Pydantic version is 1.x-like."""
80
+ """Check if the given class is Pydantic v1-like.
81
+
82
+ Returns:
83
+ True if the given class is a subclass of Pydantic ``BaseModel`` 1.x.
84
+ """
78
85
  return issubclass(cls, BaseModelV1)
79
86
 
80
87
 
81
88
  def is_pydantic_v2_subclass(cls: type) -> bool:
82
- """Check if the installed Pydantic version is 1.x-like."""
89
+ """Check if the given class is Pydantic v2-like.
90
+
91
+ Returns:
92
+ True if the given class is a subclass of Pydantic BaseModel 2.x.
93
+ """
83
94
  return issubclass(cls, BaseModel)
84
95
 
85
96
 
@@ -90,6 +101,9 @@ def is_basemodel_subclass(cls: type) -> bool:
90
101
 
91
102
  * pydantic.BaseModel in Pydantic 2.x
92
103
  * pydantic.v1.BaseModel in Pydantic 2.x
104
+
105
+ Returns:
106
+ True if the given class is a subclass of Pydantic ``BaseModel``.
93
107
  """
94
108
  # Before we can use issubclass on the cls we need to check if it is a class
95
109
  if not inspect.isclass(cls) or isinstance(cls, GenericAlias):
@@ -105,6 +119,9 @@ def is_basemodel_instance(obj: Any) -> bool:
105
119
 
106
120
  * pydantic.BaseModel in Pydantic 2.x
107
121
  * pydantic.v1.BaseModel in Pydantic 2.x
122
+
123
+ Returns:
124
+ True if the given class is an instance of Pydantic ``BaseModel``.
108
125
  """
109
126
  return isinstance(obj, (BaseModel, BaseModelV1))
110
127
 
@@ -262,7 +279,11 @@ def _create_subset_model(
262
279
  descriptions: Optional[dict] = None,
263
280
  fn_description: Optional[str] = None,
264
281
  ) -> type[BaseModel]:
265
- """Create subset model using the same pydantic version as the input model."""
282
+ """Create subset model using the same pydantic version as the input model.
283
+
284
+ Returns:
285
+ The created subset model.
286
+ """
266
287
  if issubclass(model, BaseModelV1):
267
288
  return _create_subset_model_v1(
268
289
  name,
@@ -299,7 +320,14 @@ def get_fields(model: BaseModelV1) -> dict[str, ModelField]: ...
299
320
  def get_fields(
300
321
  model: Union[type[Union[BaseModel, BaseModelV1]], BaseModel, BaseModelV1],
301
322
  ) -> Union[dict[str, FieldInfoV2], dict[str, ModelField]]:
302
- """Get the field names of a Pydantic model."""
323
+ """Return the field names of a Pydantic model.
324
+
325
+ Args:
326
+ model: The Pydantic model or instance.
327
+
328
+ Raises:
329
+ TypeError: If the model is not a Pydantic model.
330
+ """
303
331
  if hasattr(model, "model_fields"):
304
332
  return model.model_fields
305
333
 
@@ -21,17 +21,14 @@ from langchain_core.utils.pydantic import (
21
21
 
22
22
 
23
23
  def xor_args(*arg_groups: tuple[str, ...]) -> Callable:
24
- """Validate specified keyword args are mutually exclusive.".
24
+ """Validate specified keyword args are mutually exclusive.
25
25
 
26
26
  Args:
27
27
  *arg_groups (tuple[str, ...]): Groups of mutually exclusive keyword args.
28
28
 
29
29
  Returns:
30
30
  Callable: Decorator that validates the specified keyword args
31
- are mutually exclusive
32
-
33
- Raises:
34
- ValueError: If more than one arg in a group is defined.
31
+ are mutually exclusive.
35
32
  """
36
33
 
37
34
  def decorator(func: Callable) -> Callable:
@@ -137,7 +134,7 @@ def guard_import(
137
134
  try:
138
135
  module = importlib.import_module(module_name, package)
139
136
  except (ImportError, ModuleNotFoundError) as e:
140
- pip_name = pip_name or module_name.split(".")[0].replace("_", "-")
137
+ pip_name = pip_name or module_name.split(".", maxsplit=1)[0].replace("_", "-")
141
138
  msg = (
142
139
  f"Could not import {module_name} python package. "
143
140
  f"Please install it with `pip install {pip_name}`."
@@ -381,10 +378,21 @@ def from_env(
381
378
  error_message: the error message which will be raised if the key is not found
382
379
  and no default value is provided.
383
380
  This will be raised as a ValueError.
381
+
382
+ Returns:
383
+ factory method that will look up the value from the environment.
384
384
  """
385
385
 
386
386
  def get_from_env_fn() -> Optional[str]:
387
- """Get a value from an environment variable."""
387
+ """Get a value from an environment variable.
388
+
389
+ Raises:
390
+ ValueError: If the environment variable is not set and no default is
391
+ provided.
392
+
393
+ Returns:
394
+ The value from the environment.
395
+ """
388
396
  if isinstance(key, (list, tuple)):
389
397
  for k in key:
390
398
  if k in os.environ:
@@ -445,7 +453,15 @@ def secret_from_env(
445
453
  """
446
454
 
447
455
  def get_secret_from_env() -> Optional[SecretStr]:
448
- """Get a value from an environment variable."""
456
+ """Get a value from an environment variable.
457
+
458
+ Raises:
459
+ ValueError: If the environment variable is not set and no default is
460
+ provided.
461
+
462
+ Returns:
463
+ The secret from the environment.
464
+ """
449
465
  if isinstance(key, (list, tuple)):
450
466
  for k in key:
451
467
  if k in os.environ:
@@ -38,6 +38,7 @@ from typing import (
38
38
  from pydantic import ConfigDict, Field, model_validator
39
39
  from typing_extensions import Self, override
40
40
 
41
+ from langchain_core.documents import Document
41
42
  from langchain_core.embeddings import Embeddings
42
43
  from langchain_core.retrievers import BaseRetriever, LangSmithRetrieverParams
43
44
  from langchain_core.runnables.config import run_in_executor
@@ -49,7 +50,6 @@ if TYPE_CHECKING:
49
50
  AsyncCallbackManagerForRetrieverRun,
50
51
  CallbackManagerForRetrieverRun,
51
52
  )
52
- from langchain_core.documents import Document
53
53
 
54
54
  logger = logging.getLogger(__name__)
55
55
 
@@ -85,9 +85,6 @@ class VectorStore(ABC):
85
85
  ValueError: If the number of ids does not match the number of texts.
86
86
  """
87
87
  if type(self).add_documents != VectorStore.add_documents:
88
- # Import document in local scope to avoid circular imports
89
- from langchain_core.documents import Document
90
-
91
88
  # This condition is triggered if the subclass has provided
92
89
  # an implementation of the upsert method.
93
90
  # The existing add_texts
@@ -234,9 +231,6 @@ class VectorStore(ABC):
234
231
  # For backward compatibility
235
232
  kwargs["ids"] = ids
236
233
  if type(self).aadd_documents != VectorStore.aadd_documents:
237
- # Import document in local scope to avoid circular imports
238
- from langchain_core.documents import Document
239
-
240
234
  # This condition is triggered if the subclass has provided
241
235
  # an implementation of the upsert method.
242
236
  # The existing add_texts
@@ -270,9 +264,6 @@ class VectorStore(ABC):
270
264
 
271
265
  Returns:
272
266
  List of IDs of the added texts.
273
-
274
- Raises:
275
- ValueError: If the number of ids does not match the number of documents.
276
267
  """
277
268
  if type(self).add_texts != VectorStore.add_texts:
278
269
  if "ids" not in kwargs:
@@ -303,9 +294,6 @@ class VectorStore(ABC):
303
294
 
304
295
  Returns:
305
296
  List of IDs of the added texts.
306
-
307
- Raises:
308
- ValueError: If the number of IDs does not match the number of documents.
309
297
  """
310
298
  # If the async method has been overridden, we'll use that.
311
299
  if type(self).aadd_texts != VectorStore.aadd_texts:
@@ -435,6 +423,7 @@ class VectorStore(ABC):
435
423
  """The 'correct' relevance function.
436
424
 
437
425
  may differ depending on a few things, including:
426
+
438
427
  - the distance / similarity metric used by the VectorStore
439
428
  - the scale of your embeddings (OpenAI's are unit normed. Many others are not!)
440
429
  - embedding dimensionality
@@ -969,30 +958,28 @@ class VectorStore(ABC):
969
958
  # Retrieve more documents with higher diversity
970
959
  # Useful if your dataset has many similar documents
971
960
  docsearch.as_retriever(
972
- search_type="mmr",
973
- search_kwargs={'k': 6, 'lambda_mult': 0.25}
961
+ search_type="mmr", search_kwargs={"k": 6, "lambda_mult": 0.25}
974
962
  )
975
963
 
976
964
  # Fetch more documents for the MMR algorithm to consider
977
965
  # But only return the top 5
978
966
  docsearch.as_retriever(
979
- search_type="mmr",
980
- search_kwargs={'k': 5, 'fetch_k': 50}
967
+ search_type="mmr", search_kwargs={"k": 5, "fetch_k": 50}
981
968
  )
982
969
 
983
970
  # Only retrieve documents that have a relevance score
984
971
  # Above a certain threshold
985
972
  docsearch.as_retriever(
986
973
  search_type="similarity_score_threshold",
987
- search_kwargs={'score_threshold': 0.8}
974
+ search_kwargs={"score_threshold": 0.8},
988
975
  )
989
976
 
990
977
  # Only get the single most similar document from the dataset
991
- docsearch.as_retriever(search_kwargs={'k': 1})
978
+ docsearch.as_retriever(search_kwargs={"k": 1})
992
979
 
993
980
  # Use a filter to only retrieve documents from a specific paper
994
981
  docsearch.as_retriever(
995
- search_kwargs={'filter': {'paper_title':'GPT-4 Technical Report'}}
982
+ search_kwargs={"filter": {"paper_title": "GPT-4 Technical Report"}}
996
983
  )
997
984
 
998
985
  """