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
|
@@ -3,12 +3,24 @@
|
|
|
3
3
|
Adapted from https://github.com/iterative/dvc/blob/main/dvc/dagascii.py.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import math
|
|
7
9
|
import os
|
|
8
10
|
from collections.abc import Mapping, Sequence
|
|
9
|
-
from typing import Any
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from grandalf.graphs import Edge, Graph, Vertex # type: ignore[import-untyped]
|
|
15
|
+
from grandalf.layouts import SugiyamaLayout # type: ignore[import-untyped]
|
|
16
|
+
from grandalf.routing import route_with_lines # type: ignore[import-untyped]
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
_HAS_GRANDALF = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
_HAS_GRANDALF = False
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from langchain_core.runnables.graph import Edge as LangEdge
|
|
12
24
|
|
|
13
25
|
|
|
14
26
|
class VertexViewer:
|
|
@@ -50,8 +62,11 @@ class AsciiCanvas:
|
|
|
50
62
|
"""Create an ASCII canvas.
|
|
51
63
|
|
|
52
64
|
Args:
|
|
53
|
-
cols
|
|
54
|
-
lines
|
|
65
|
+
cols: number of columns in the canvas. Should be ``> 1``.
|
|
66
|
+
lines: number of lines in the canvas. Should be ``> 1``.
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ValueError: if canvas dimensions are invalid.
|
|
55
70
|
"""
|
|
56
71
|
if cols <= 1 or lines <= 1:
|
|
57
72
|
msg = "Canvas dimensions should be > 1"
|
|
@@ -63,7 +78,11 @@ class AsciiCanvas:
|
|
|
63
78
|
self.canvas = [[" "] * cols for line in range(lines)]
|
|
64
79
|
|
|
65
80
|
def draw(self) -> str:
|
|
66
|
-
"""Draws ASCII canvas on the screen.
|
|
81
|
+
"""Draws ASCII canvas on the screen.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The ASCII canvas string.
|
|
85
|
+
"""
|
|
67
86
|
lines = map("".join, self.canvas)
|
|
68
87
|
return os.linesep.join(lines)
|
|
69
88
|
|
|
@@ -71,12 +90,16 @@ class AsciiCanvas:
|
|
|
71
90
|
"""Create a point on ASCII canvas.
|
|
72
91
|
|
|
73
92
|
Args:
|
|
74
|
-
x
|
|
93
|
+
x: x coordinate. Should be ``>= 0`` and ``<`` number of columns in
|
|
75
94
|
the canvas.
|
|
76
|
-
y
|
|
95
|
+
y: y coordinate. Should be ``>= 0`` an ``<`` number of lines in the
|
|
77
96
|
canvas.
|
|
78
|
-
char
|
|
97
|
+
char: character to place in the specified point on the
|
|
79
98
|
canvas.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
ValueError: if char is not a single character or if
|
|
102
|
+
coordinates are out of bounds.
|
|
80
103
|
"""
|
|
81
104
|
if len(char) != 1:
|
|
82
105
|
msg = "char should be a single character"
|
|
@@ -174,13 +197,9 @@ class _EdgeViewer:
|
|
|
174
197
|
def _build_sugiyama_layout(
|
|
175
198
|
vertices: Mapping[str, str], edges: Sequence[LangEdge]
|
|
176
199
|
) -> Any:
|
|
177
|
-
|
|
178
|
-
from grandalf.graphs import Edge, Graph, Vertex # type: ignore[import-untyped]
|
|
179
|
-
from grandalf.layouts import SugiyamaLayout # type: ignore[import-untyped]
|
|
180
|
-
from grandalf.routing import route_with_lines # type: ignore[import-untyped]
|
|
181
|
-
except ImportError as exc:
|
|
200
|
+
if not _HAS_GRANDALF:
|
|
182
201
|
msg = "Install grandalf to draw graphs: `pip install grandalf`."
|
|
183
|
-
raise ImportError(msg)
|
|
202
|
+
raise ImportError(msg)
|
|
184
203
|
|
|
185
204
|
#
|
|
186
205
|
# Just a reminder about naming conventions:
|
|
@@ -225,11 +244,15 @@ def draw_ascii(vertices: Mapping[str, str], edges: Sequence[LangEdge]) -> str:
|
|
|
225
244
|
"""Build a DAG and draw it in ASCII.
|
|
226
245
|
|
|
227
246
|
Args:
|
|
228
|
-
vertices
|
|
229
|
-
edges
|
|
247
|
+
vertices: list of graph vertices.
|
|
248
|
+
edges: list of graph edges.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
ValueError: if the canvas dimensions are invalid or if
|
|
252
|
+
edge coordinates are invalid.
|
|
230
253
|
|
|
231
254
|
Returns:
|
|
232
|
-
|
|
255
|
+
ASCII representation
|
|
233
256
|
|
|
234
257
|
Example:
|
|
235
258
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Mermaid graph drawing utilities."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import asyncio
|
|
4
6
|
import base64
|
|
5
7
|
import random
|
|
@@ -7,18 +9,34 @@ import re
|
|
|
7
9
|
import time
|
|
8
10
|
from dataclasses import asdict
|
|
9
11
|
from pathlib import Path
|
|
10
|
-
from typing import Any, Literal, Optional
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
11
13
|
|
|
12
14
|
import yaml
|
|
13
15
|
|
|
14
16
|
from langchain_core.runnables.graph import (
|
|
15
17
|
CurveStyle,
|
|
16
|
-
Edge,
|
|
17
18
|
MermaidDrawMethod,
|
|
18
|
-
Node,
|
|
19
19
|
NodeStyles,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from langchain_core.runnables.graph import Edge, Node
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import requests
|
|
28
|
+
|
|
29
|
+
_HAS_REQUESTS = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
_HAS_REQUESTS = False
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
from pyppeteer import launch # type: ignore[import-not-found]
|
|
35
|
+
|
|
36
|
+
_HAS_PYPPETEER = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
_HAS_PYPPETEER = False
|
|
39
|
+
|
|
22
40
|
MARKDOWN_SPECIAL_CHARS = "*_`"
|
|
23
41
|
|
|
24
42
|
|
|
@@ -155,7 +173,7 @@ def draw_mermaid(
|
|
|
155
173
|
nonlocal mermaid_graph
|
|
156
174
|
self_loop = len(edges) == 1 and edges[0].source == edges[0].target
|
|
157
175
|
if prefix and not self_loop:
|
|
158
|
-
subgraph = prefix.
|
|
176
|
+
subgraph = prefix.rsplit(":", maxsplit=1)[-1]
|
|
159
177
|
if subgraph in seen_subgraphs:
|
|
160
178
|
msg = (
|
|
161
179
|
f"Found duplicate subgraph '{subgraph}' -- this likely means that "
|
|
@@ -214,7 +232,7 @@ def draw_mermaid(
|
|
|
214
232
|
|
|
215
233
|
# Add remaining subgraphs with edges
|
|
216
234
|
for prefix, edges_ in edge_groups.items():
|
|
217
|
-
if ":" in prefix
|
|
235
|
+
if not prefix or ":" in prefix:
|
|
218
236
|
continue
|
|
219
237
|
add_subgraph(edges_, prefix)
|
|
220
238
|
seen_subgraphs.add(prefix)
|
|
@@ -283,8 +301,6 @@ def draw_mermaid_png(
|
|
|
283
301
|
ValueError: If an invalid draw method is provided.
|
|
284
302
|
"""
|
|
285
303
|
if draw_method == MermaidDrawMethod.PYPPETEER:
|
|
286
|
-
import asyncio
|
|
287
|
-
|
|
288
304
|
img_bytes = asyncio.run(
|
|
289
305
|
_render_mermaid_using_pyppeteer(
|
|
290
306
|
mermaid_syntax, output_file_path, background_color, padding
|
|
@@ -317,11 +333,9 @@ async def _render_mermaid_using_pyppeteer(
|
|
|
317
333
|
device_scale_factor: int = 3,
|
|
318
334
|
) -> bytes:
|
|
319
335
|
"""Renders Mermaid graph using Pyppeteer."""
|
|
320
|
-
|
|
321
|
-
from pyppeteer import launch # type: ignore[import-not-found]
|
|
322
|
-
except ImportError as e:
|
|
336
|
+
if not _HAS_PYPPETEER:
|
|
323
337
|
msg = "Install Pyppeteer to use the Pyppeteer method: `pip install pyppeteer`."
|
|
324
|
-
raise ImportError(msg)
|
|
338
|
+
raise ImportError(msg)
|
|
325
339
|
|
|
326
340
|
browser = await launch()
|
|
327
341
|
page = await browser.newPage()
|
|
@@ -392,14 +406,12 @@ def _render_mermaid_using_api(
|
|
|
392
406
|
retry_delay: float = 1.0,
|
|
393
407
|
) -> bytes:
|
|
394
408
|
"""Renders Mermaid graph using the Mermaid.INK API."""
|
|
395
|
-
|
|
396
|
-
import requests
|
|
397
|
-
except ImportError as e:
|
|
409
|
+
if not _HAS_REQUESTS:
|
|
398
410
|
msg = (
|
|
399
411
|
"Install the `requests` module to use the Mermaid.INK API: "
|
|
400
412
|
"`pip install requests`."
|
|
401
413
|
)
|
|
402
|
-
raise ImportError(msg)
|
|
414
|
+
raise ImportError(msg)
|
|
403
415
|
|
|
404
416
|
# Use Mermaid API to render the image
|
|
405
417
|
mermaid_syntax_encoded = base64.b64encode(mermaid_syntax.encode("utf8")).decode(
|
|
@@ -4,29 +4,25 @@ from typing import Any, Optional
|
|
|
4
4
|
|
|
5
5
|
from langchain_core.runnables.graph import Graph, LabelsDict
|
|
6
6
|
|
|
7
|
+
try:
|
|
8
|
+
import pygraphviz as pgv # type: ignore[import-not-found]
|
|
9
|
+
|
|
10
|
+
_HAS_PYGRAPHVIZ = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
_HAS_PYGRAPHVIZ = False
|
|
13
|
+
|
|
7
14
|
|
|
8
15
|
class PngDrawer:
|
|
9
16
|
"""Helper class to draw a state graph into a PNG file.
|
|
10
17
|
|
|
11
|
-
It requires
|
|
12
|
-
|
|
13
|
-
:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"__end__": "End Node"
|
|
20
|
-
},
|
|
21
|
-
"edges": {
|
|
22
|
-
"continue": "ContinueLabel",
|
|
23
|
-
"end": "EndLabel"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
The keys are the original labels, and the values are the new labels.
|
|
27
|
-
Usage:
|
|
28
|
-
drawer = PngDrawer()
|
|
29
|
-
drawer.draw(state_graph, 'graph.png')
|
|
18
|
+
It requires ``graphviz`` and ``pygraphviz`` to be installed.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
|
|
22
|
+
.. code-block:: python
|
|
23
|
+
|
|
24
|
+
drawer = PngDrawer()
|
|
25
|
+
drawer.draw(state_graph, "graph.png")
|
|
30
26
|
"""
|
|
31
27
|
|
|
32
28
|
def __init__(
|
|
@@ -85,9 +81,6 @@ class PngDrawer:
|
|
|
85
81
|
Args:
|
|
86
82
|
viz: The graphviz object.
|
|
87
83
|
node: The node to add.
|
|
88
|
-
|
|
89
|
-
Returns:
|
|
90
|
-
None
|
|
91
84
|
"""
|
|
92
85
|
viz.add_node(
|
|
93
86
|
node,
|
|
@@ -114,9 +107,6 @@ class PngDrawer:
|
|
|
114
107
|
target: The target node.
|
|
115
108
|
label: The label for the edge. Defaults to None.
|
|
116
109
|
conditional: Whether the edge is conditional. Defaults to False.
|
|
117
|
-
|
|
118
|
-
Returns:
|
|
119
|
-
None
|
|
120
110
|
"""
|
|
121
111
|
viz.add_edge(
|
|
122
112
|
source,
|
|
@@ -131,14 +121,20 @@ class PngDrawer:
|
|
|
131
121
|
"""Draw the given state graph into a PNG file.
|
|
132
122
|
|
|
133
123
|
Requires `graphviz` and `pygraphviz` to be installed.
|
|
134
|
-
|
|
135
|
-
:
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
graph: The graph to draw
|
|
127
|
+
output_path: The path to save the PNG. If None, PNG bytes are returned.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
ImportError: If ``pygraphviz`` is not installed.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
The PNG bytes if ``output_path`` is None, else None.
|
|
136
134
|
"""
|
|
137
|
-
|
|
138
|
-
import pygraphviz as pgv # type: ignore[import-not-found]
|
|
139
|
-
except ImportError as exc:
|
|
135
|
+
if not _HAS_PYGRAPHVIZ:
|
|
140
136
|
msg = "Install pygraphviz to draw graphs: `pip install pygraphviz`."
|
|
141
|
-
raise ImportError(msg)
|
|
137
|
+
raise ImportError(msg)
|
|
142
138
|
|
|
143
139
|
# Create a directed graph
|
|
144
140
|
viz = pgv.AGraph(directed=True, nodesep=0.9, ranksep=1.0)
|
|
@@ -18,6 +18,7 @@ from typing_extensions import override
|
|
|
18
18
|
|
|
19
19
|
from langchain_core.chat_history import BaseChatMessageHistory
|
|
20
20
|
from langchain_core.load.load import load
|
|
21
|
+
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
|
21
22
|
from langchain_core.runnables.base import Runnable, RunnableBindingBase, RunnableLambda
|
|
22
23
|
from langchain_core.runnables.passthrough import RunnablePassthrough
|
|
23
24
|
from langchain_core.runnables.utils import (
|
|
@@ -29,7 +30,6 @@ from langchain_core.utils.pydantic import create_model_v2
|
|
|
29
30
|
|
|
30
31
|
if TYPE_CHECKING:
|
|
31
32
|
from langchain_core.language_models.base import LanguageModelLike
|
|
32
|
-
from langchain_core.messages.base import BaseMessage
|
|
33
33
|
from langchain_core.runnables.config import RunnableConfig
|
|
34
34
|
from langchain_core.tracers.schemas import Run
|
|
35
35
|
|
|
@@ -72,20 +72,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
72
72
|
For production use cases, you will want to use a persistent implementation
|
|
73
73
|
of chat message history, such as ``RedisChatMessageHistory``.
|
|
74
74
|
|
|
75
|
-
Parameters:
|
|
76
|
-
get_session_history: Function that returns a new BaseChatMessageHistory.
|
|
77
|
-
This function should either take a single positional argument
|
|
78
|
-
`session_id` of type string and return a corresponding
|
|
79
|
-
chat message history instance.
|
|
80
|
-
input_messages_key: Must be specified if the base runnable accepts a dict
|
|
81
|
-
as input. The key in the input dict that contains the messages.
|
|
82
|
-
output_messages_key: Must be specified if the base Runnable returns a dict
|
|
83
|
-
as output. The key in the output dict that contains the messages.
|
|
84
|
-
history_messages_key: Must be specified if the base runnable accepts a dict
|
|
85
|
-
as input and expects a separate key for historical messages.
|
|
86
|
-
history_factory_config: Configure fields that should be passed to the
|
|
87
|
-
chat history factory. See ``ConfigurableFieldSpec`` for more details.
|
|
88
|
-
|
|
89
75
|
Example: Chat message history with an in-memory implementation for testing.
|
|
90
76
|
|
|
91
77
|
.. code-block:: python
|
|
@@ -145,11 +131,13 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
145
131
|
from langchain_core.runnables.history import RunnableWithMessageHistory
|
|
146
132
|
|
|
147
133
|
|
|
148
|
-
prompt = ChatPromptTemplate.from_messages(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
134
|
+
prompt = ChatPromptTemplate.from_messages(
|
|
135
|
+
[
|
|
136
|
+
("system", "You're an assistant who's good at {ability}"),
|
|
137
|
+
MessagesPlaceholder(variable_name="history"),
|
|
138
|
+
("human", "{question}"),
|
|
139
|
+
]
|
|
140
|
+
)
|
|
153
141
|
|
|
154
142
|
chain = prompt | ChatAnthropic(model="claude-2")
|
|
155
143
|
|
|
@@ -162,18 +150,22 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
162
150
|
history_messages_key="history",
|
|
163
151
|
)
|
|
164
152
|
|
|
165
|
-
print(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
153
|
+
print(
|
|
154
|
+
chain_with_history.invoke( # noqa: T201
|
|
155
|
+
{"ability": "math", "question": "What does cosine mean?"},
|
|
156
|
+
config={"configurable": {"session_id": "foo"}},
|
|
157
|
+
)
|
|
158
|
+
)
|
|
169
159
|
|
|
170
160
|
# Uses the store defined in the example above.
|
|
171
161
|
print(store) # noqa: T201
|
|
172
162
|
|
|
173
|
-
print(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
print(
|
|
164
|
+
chain_with_history.invoke( # noqa: T201
|
|
165
|
+
{"ability": "math", "question": "What's its inverse"},
|
|
166
|
+
config={"configurable": {"session_id": "foo"}},
|
|
167
|
+
)
|
|
168
|
+
)
|
|
177
169
|
|
|
178
170
|
print(store) # noqa: T201
|
|
179
171
|
|
|
@@ -184,6 +176,7 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
184
176
|
|
|
185
177
|
store = {}
|
|
186
178
|
|
|
179
|
+
|
|
187
180
|
def get_session_history(
|
|
188
181
|
user_id: str, conversation_id: str
|
|
189
182
|
) -> BaseChatMessageHistory:
|
|
@@ -191,11 +184,14 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
191
184
|
store[(user_id, conversation_id)] = InMemoryHistory()
|
|
192
185
|
return store[(user_id, conversation_id)]
|
|
193
186
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
187
|
+
|
|
188
|
+
prompt = ChatPromptTemplate.from_messages(
|
|
189
|
+
[
|
|
190
|
+
("system", "You're an assistant who's good at {ability}"),
|
|
191
|
+
MessagesPlaceholder(variable_name="history"),
|
|
192
|
+
("human", "{question}"),
|
|
193
|
+
]
|
|
194
|
+
)
|
|
199
195
|
|
|
200
196
|
chain = prompt | ChatAnthropic(model="claude-2")
|
|
201
197
|
|
|
@@ -226,16 +222,27 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
226
222
|
|
|
227
223
|
with_message_history.invoke(
|
|
228
224
|
{"ability": "math", "question": "What does cosine mean?"},
|
|
229
|
-
config={"configurable": {"user_id": "123", "conversation_id": "1"}}
|
|
225
|
+
config={"configurable": {"user_id": "123", "conversation_id": "1"}},
|
|
230
226
|
)
|
|
231
227
|
|
|
232
228
|
"""
|
|
233
229
|
|
|
234
230
|
get_session_history: GetSessionHistoryCallable
|
|
231
|
+
"""Function that returns a new BaseChatMessageHistory.
|
|
232
|
+
This function should either take a single positional argument ``session_id`` of type
|
|
233
|
+
string and return a corresponding chat message history instance"""
|
|
235
234
|
input_messages_key: Optional[str] = None
|
|
235
|
+
"""Must be specified if the base runnable accepts a dict as input.
|
|
236
|
+
The key in the input dict that contains the messages."""
|
|
236
237
|
output_messages_key: Optional[str] = None
|
|
238
|
+
"""Must be specified if the base Runnable returns a dict as output.
|
|
239
|
+
The key in the output dict that contains the messages."""
|
|
237
240
|
history_messages_key: Optional[str] = None
|
|
241
|
+
"""Must be specified if the base runnable accepts a dict as input and expects a
|
|
242
|
+
separate key for historical messages."""
|
|
238
243
|
history_factory_config: Sequence[ConfigurableFieldSpec]
|
|
244
|
+
"""Configure fields that should be passed to the chat history factory.
|
|
245
|
+
See ``ConfigurableFieldSpec`` for more details."""
|
|
239
246
|
|
|
240
247
|
def __init__(
|
|
241
248
|
self,
|
|
@@ -261,17 +268,21 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
261
268
|
"""Initialize RunnableWithMessageHistory.
|
|
262
269
|
|
|
263
270
|
Args:
|
|
264
|
-
runnable: The base Runnable to be wrapped.
|
|
265
|
-
|
|
271
|
+
runnable: The base Runnable to be wrapped.
|
|
272
|
+
Must take as input one of:
|
|
273
|
+
|
|
274
|
+
1. A list of ``BaseMessage``
|
|
266
275
|
2. A dict with one key for all messages
|
|
267
276
|
3. A dict with one key for the current input string/message(s) and
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
a separate key for historical messages. If the input key points
|
|
278
|
+
to a string, it will be treated as a ``HumanMessage`` in history.
|
|
270
279
|
|
|
271
280
|
Must return as output one of:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
|
|
282
|
+
1. A string which can be treated as an ``AIMessage``
|
|
283
|
+
2. A ``BaseMessage`` or sequence of ``BaseMessage``
|
|
284
|
+
3. A dict with a key for a ``BaseMessage`` or sequence of
|
|
285
|
+
``BaseMessage``
|
|
275
286
|
|
|
276
287
|
get_session_history: Function that returns a new BaseChatMessageHistory.
|
|
277
288
|
This function should either take a single positional argument
|
|
@@ -280,11 +291,8 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
280
291
|
.. code-block:: python
|
|
281
292
|
|
|
282
293
|
def get_session_history(
|
|
283
|
-
session_id: str,
|
|
284
|
-
|
|
285
|
-
user_id: Optional[str]=None
|
|
286
|
-
) -> BaseChatMessageHistory:
|
|
287
|
-
...
|
|
294
|
+
session_id: str, *, user_id: Optional[str] = None
|
|
295
|
+
) -> BaseChatMessageHistory: ...
|
|
288
296
|
|
|
289
297
|
Or it should take keyword arguments that match the keys of
|
|
290
298
|
`session_history_config_specs` and return a corresponding
|
|
@@ -296,8 +304,7 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
296
304
|
*,
|
|
297
305
|
user_id: str,
|
|
298
306
|
thread_id: str,
|
|
299
|
-
) -> BaseChatMessageHistory:
|
|
300
|
-
...
|
|
307
|
+
) -> BaseChatMessageHistory: ...
|
|
301
308
|
|
|
302
309
|
input_messages_key: Must be specified if the base runnable accepts a dict
|
|
303
310
|
as input. Default is None.
|
|
@@ -377,8 +384,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
377
384
|
def get_input_schema(
|
|
378
385
|
self, config: Optional[RunnableConfig] = None
|
|
379
386
|
) -> type[BaseModel]:
|
|
380
|
-
from langchain_core.messages import BaseMessage
|
|
381
|
-
|
|
382
387
|
fields: dict = {}
|
|
383
388
|
if self.input_messages_key and self.history_messages_key:
|
|
384
389
|
fields[self.input_messages_key] = (
|
|
@@ -440,8 +445,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
440
445
|
def _get_input_messages(
|
|
441
446
|
self, input_val: Union[str, BaseMessage, Sequence[BaseMessage], dict]
|
|
442
447
|
) -> list[BaseMessage]:
|
|
443
|
-
from langchain_core.messages import BaseMessage
|
|
444
|
-
|
|
445
448
|
# If dictionary, try to pluck the single key representing messages
|
|
446
449
|
if isinstance(input_val, dict):
|
|
447
450
|
if self.input_messages_key:
|
|
@@ -454,8 +457,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
454
457
|
|
|
455
458
|
# If value is a string, convert to a human message
|
|
456
459
|
if isinstance(input_val, str):
|
|
457
|
-
from langchain_core.messages import HumanMessage
|
|
458
|
-
|
|
459
460
|
return [HumanMessage(content=input_val)]
|
|
460
461
|
# If value is a single message, convert to a list
|
|
461
462
|
if isinstance(input_val, BaseMessage):
|
|
@@ -482,8 +483,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
482
483
|
def _get_output_messages(
|
|
483
484
|
self, output_val: Union[str, BaseMessage, Sequence[BaseMessage], dict]
|
|
484
485
|
) -> list[BaseMessage]:
|
|
485
|
-
from langchain_core.messages import BaseMessage
|
|
486
|
-
|
|
487
486
|
# If dictionary, try to pluck the single key representing messages
|
|
488
487
|
if isinstance(output_val, dict):
|
|
489
488
|
if self.output_messages_key:
|
|
@@ -500,8 +499,6 @@ class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
|
|
500
499
|
output_val = output_val[key]
|
|
501
500
|
|
|
502
501
|
if isinstance(output_val, str):
|
|
503
|
-
from langchain_core.messages import AIMessage
|
|
504
|
-
|
|
505
502
|
return [AIMessage(content=output_val)]
|
|
506
503
|
# If value is a single message, convert to a list
|
|
507
504
|
if isinstance(output_val, BaseMessage):
|