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.
- langchain_core/_api/beta_decorator.py +17 -40
- langchain_core/_api/deprecation.py +19 -6
- langchain_core/_api/path.py +19 -2
- langchain_core/_import_utils.py +7 -0
- langchain_core/agents.py +10 -6
- langchain_core/beta/runnables/context.py +1 -2
- langchain_core/callbacks/base.py +11 -4
- langchain_core/callbacks/manager.py +81 -69
- langchain_core/callbacks/usage.py +4 -2
- langchain_core/chat_history.py +4 -6
- langchain_core/document_loaders/base.py +34 -9
- langchain_core/document_loaders/langsmith.py +3 -0
- langchain_core/documents/base.py +35 -10
- langchain_core/documents/transformers.py +4 -2
- langchain_core/embeddings/fake.py +8 -5
- langchain_core/env.py +2 -3
- langchain_core/example_selectors/base.py +12 -0
- langchain_core/exceptions.py +7 -0
- langchain_core/globals.py +17 -28
- langchain_core/indexing/api.py +56 -44
- langchain_core/indexing/base.py +5 -8
- langchain_core/indexing/in_memory.py +23 -3
- langchain_core/language_models/__init__.py +3 -2
- langchain_core/language_models/base.py +31 -20
- langchain_core/language_models/chat_models.py +94 -25
- langchain_core/language_models/fake_chat_models.py +5 -7
- langchain_core/language_models/llms.py +49 -17
- langchain_core/load/dump.py +2 -3
- langchain_core/load/load.py +15 -1
- langchain_core/load/serializable.py +38 -43
- langchain_core/memory.py +7 -3
- langchain_core/messages/ai.py +36 -19
- langchain_core/messages/base.py +13 -6
- langchain_core/messages/content_blocks.py +23 -2
- langchain_core/messages/human.py +2 -6
- langchain_core/messages/system.py +2 -6
- langchain_core/messages/tool.py +33 -13
- langchain_core/messages/utils.py +182 -72
- langchain_core/output_parsers/base.py +5 -2
- langchain_core/output_parsers/json.py +4 -4
- langchain_core/output_parsers/list.py +7 -22
- langchain_core/output_parsers/openai_functions.py +3 -0
- langchain_core/output_parsers/openai_tools.py +6 -1
- langchain_core/output_parsers/pydantic.py +4 -0
- langchain_core/output_parsers/string.py +5 -1
- langchain_core/output_parsers/xml.py +19 -19
- langchain_core/outputs/chat_generation.py +18 -7
- langchain_core/outputs/generation.py +14 -3
- langchain_core/outputs/llm_result.py +8 -1
- langchain_core/prompt_values.py +10 -4
- langchain_core/prompts/base.py +4 -9
- langchain_core/prompts/chat.py +87 -58
- langchain_core/prompts/dict.py +16 -8
- langchain_core/prompts/few_shot.py +9 -11
- langchain_core/prompts/few_shot_with_templates.py +5 -1
- langchain_core/prompts/image.py +12 -5
- langchain_core/prompts/message.py +5 -6
- langchain_core/prompts/pipeline.py +13 -8
- langchain_core/prompts/prompt.py +22 -8
- langchain_core/prompts/string.py +18 -10
- langchain_core/prompts/structured.py +7 -2
- langchain_core/rate_limiters.py +2 -2
- langchain_core/retrievers.py +7 -6
- langchain_core/runnables/base.py +402 -183
- langchain_core/runnables/branch.py +14 -19
- langchain_core/runnables/config.py +9 -15
- langchain_core/runnables/configurable.py +34 -19
- langchain_core/runnables/fallbacks.py +20 -13
- langchain_core/runnables/graph.py +44 -37
- langchain_core/runnables/graph_ascii.py +40 -17
- langchain_core/runnables/graph_mermaid.py +27 -15
- langchain_core/runnables/graph_png.py +27 -31
- langchain_core/runnables/history.py +55 -58
- langchain_core/runnables/passthrough.py +44 -21
- langchain_core/runnables/retry.py +9 -5
- langchain_core/runnables/router.py +9 -8
- langchain_core/runnables/schema.py +2 -0
- langchain_core/runnables/utils.py +51 -89
- langchain_core/stores.py +13 -25
- langchain_core/sys_info.py +9 -8
- langchain_core/tools/base.py +30 -23
- langchain_core/tools/convert.py +24 -13
- langchain_core/tools/simple.py +35 -3
- langchain_core/tools/structured.py +25 -2
- langchain_core/tracers/base.py +2 -2
- langchain_core/tracers/context.py +5 -1
- langchain_core/tracers/core.py +109 -39
- langchain_core/tracers/evaluation.py +22 -26
- langchain_core/tracers/event_stream.py +40 -27
- langchain_core/tracers/langchain.py +12 -3
- langchain_core/tracers/langchain_v1.py +10 -2
- langchain_core/tracers/log_stream.py +56 -17
- langchain_core/tracers/root_listeners.py +4 -20
- langchain_core/tracers/run_collector.py +6 -16
- langchain_core/tracers/schemas.py +5 -1
- langchain_core/utils/aiter.py +14 -6
- langchain_core/utils/env.py +3 -0
- langchain_core/utils/function_calling.py +37 -20
- langchain_core/utils/interactive_env.py +6 -2
- langchain_core/utils/iter.py +11 -3
- langchain_core/utils/json.py +5 -2
- langchain_core/utils/json_schema.py +15 -5
- langchain_core/utils/loading.py +5 -1
- langchain_core/utils/mustache.py +24 -15
- langchain_core/utils/pydantic.py +32 -4
- langchain_core/utils/utils.py +24 -8
- langchain_core/vectorstores/base.py +7 -20
- langchain_core/vectorstores/in_memory.py +18 -12
- langchain_core/vectorstores/utils.py +18 -12
- langchain_core/version.py +1 -1
- langchain_core-0.3.76.dist-info/METADATA +77 -0
- langchain_core-0.3.76.dist-info/RECORD +174 -0
- langchain_core-0.3.75.dist-info/METADATA +0 -106
- langchain_core-0.3.75.dist-info/RECORD +0 -174
- {langchain_core-0.3.75.dist-info → langchain_core-0.3.76.dist-info}/WHEEL +0 -0
- {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,
|
|
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,
|
|
306
|
-
model =
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
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")
|
langchain_core/utils/iter.py
CHANGED
|
@@ -31,7 +31,7 @@ class NoLock:
|
|
|
31
31
|
exc_val: Optional[BaseException],
|
|
32
32
|
exc_tb: Optional[TracebackType],
|
|
33
33
|
) -> Literal[False]:
|
|
34
|
-
"""
|
|
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
|
|
langchain_core/utils/json.py
CHANGED
|
@@ -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
|
-
|
|
122
|
+
shallow-inline refs.
|
|
116
123
|
- If provided (even as an empty list), we will recurse under every key and
|
|
117
|
-
|
|
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"]
|
langchain_core/utils/loading.py
CHANGED
|
@@ -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.",
|
langchain_core/utils/mustache.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
334
|
+
"""Return the HTML-escaped string with these characters escaped: ``" & < >``."""
|
|
330
335
|
html_codes = {
|
|
331
336
|
'"': """,
|
|
332
337
|
"<": "<",
|
|
@@ -349,7 +354,7 @@ def _get_key(
|
|
|
349
354
|
def_ldel: str,
|
|
350
355
|
def_rdel: str,
|
|
351
356
|
) -> Any:
|
|
352
|
-
"""
|
|
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]
|
langchain_core/utils/pydantic.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
"""
|
|
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
|
|
langchain_core/utils/utils.py
CHANGED
|
@@ -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={
|
|
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={
|
|
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={
|
|
982
|
+
search_kwargs={"filter": {"paper_title": "GPT-4 Technical Report"}}
|
|
996
983
|
)
|
|
997
984
|
|
|
998
985
|
"""
|