langchain-core 0.3.75__py3-none-any.whl → 0.3.77__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 +22 -44
- langchain_core/_api/deprecation.py +30 -17
- 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 +28 -15
- langchain_core/callbacks/manager.py +83 -71
- langchain_core/callbacks/usage.py +6 -4
- langchain_core/chat_history.py +29 -21
- langchain_core/document_loaders/base.py +34 -9
- langchain_core/document_loaders/langsmith.py +4 -1
- 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 +88 -76
- 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 +98 -27
- langchain_core/language_models/fake_chat_models.py +10 -9
- langchain_core/language_models/llms.py +52 -18
- langchain_core/load/dump.py +2 -3
- langchain_core/load/load.py +15 -1
- langchain_core/load/serializable.py +39 -44
- langchain_core/memory.py +7 -3
- langchain_core/messages/ai.py +53 -24
- langchain_core/messages/base.py +43 -22
- langchain_core/messages/chat.py +4 -1
- langchain_core/messages/content_blocks.py +23 -2
- langchain_core/messages/function.py +9 -5
- langchain_core/messages/human.py +13 -10
- langchain_core/messages/modifier.py +1 -0
- langchain_core/messages/system.py +11 -8
- langchain_core/messages/tool.py +60 -29
- langchain_core/messages/utils.py +250 -131
- 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 +25 -10
- langchain_core/outputs/generation.py +14 -3
- langchain_core/outputs/llm_result.py +8 -1
- langchain_core/prompt_values.py +16 -6
- langchain_core/prompts/base.py +4 -9
- langchain_core/prompts/chat.py +89 -57
- langchain_core/prompts/dict.py +16 -8
- langchain_core/prompts/few_shot.py +12 -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 +406 -186
- 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 +48 -38
- langchain_core/runnables/graph_ascii.py +41 -18
- langchain_core/runnables/graph_mermaid.py +54 -25
- 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 +44 -23
- 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 +19 -31
- langchain_core/sys_info.py +9 -8
- langchain_core/tools/base.py +37 -28
- langchain_core/tools/convert.py +26 -15
- langchain_core/tools/simple.py +36 -8
- langchain_core/tools/structured.py +25 -12
- 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 +45 -34
- 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 +15 -7
- langchain_core/utils/env.py +3 -0
- langchain_core/utils/function_calling.py +50 -28
- langchain_core/utils/interactive_env.py +6 -2
- langchain_core/utils/iter.py +12 -4
- langchain_core/utils/json.py +12 -3
- langchain_core/utils/json_schema.py +156 -40
- langchain_core/utils/loading.py +5 -1
- langchain_core/utils/mustache.py +24 -15
- langchain_core/utils/pydantic.py +38 -9
- langchain_core/utils/utils.py +25 -9
- langchain_core/vectorstores/base.py +7 -20
- langchain_core/vectorstores/in_memory.py +23 -17
- langchain_core/vectorstores/utils.py +18 -12
- langchain_core/version.py +1 -1
- langchain_core-0.3.77.dist-info/METADATA +67 -0
- langchain_core-0.3.77.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.77.dist-info}/WHEEL +0 -0
- {langchain_core-0.3.75.dist-info → langchain_core-0.3.77.dist-info}/entry_points.txt +0 -0
langchain_core/messages/utils.py
CHANGED
|
@@ -5,6 +5,7 @@ Some examples of what you can do with these functions include:
|
|
|
5
5
|
* Convert messages to strings (serialization)
|
|
6
6
|
* Convert messages from dicts to Message objects (deserialization)
|
|
7
7
|
* Filter messages from a list of messages based on name, type or id etc.
|
|
8
|
+
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from __future__ import annotations
|
|
@@ -42,12 +43,17 @@ from langchain_core.messages.system import SystemMessage, SystemMessageChunk
|
|
|
42
43
|
from langchain_core.messages.tool import ToolCall, ToolMessage, ToolMessageChunk
|
|
43
44
|
|
|
44
45
|
if TYPE_CHECKING:
|
|
45
|
-
from langchain_text_splitters import TextSplitter
|
|
46
|
-
|
|
47
46
|
from langchain_core.language_models import BaseLanguageModel
|
|
48
47
|
from langchain_core.prompt_values import PromptValue
|
|
49
48
|
from langchain_core.runnables.base import Runnable
|
|
50
49
|
|
|
50
|
+
try:
|
|
51
|
+
from langchain_text_splitters import TextSplitter
|
|
52
|
+
|
|
53
|
+
_HAS_LANGCHAIN_TEXT_SPLITTERS = True
|
|
54
|
+
except ImportError:
|
|
55
|
+
_HAS_LANGCHAIN_TEXT_SPLITTERS = False
|
|
56
|
+
|
|
51
57
|
logger = logging.getLogger(__name__)
|
|
52
58
|
|
|
53
59
|
|
|
@@ -86,13 +92,14 @@ AnyMessage = Annotated[
|
|
|
86
92
|
def get_buffer_string(
|
|
87
93
|
messages: Sequence[BaseMessage], human_prefix: str = "Human", ai_prefix: str = "AI"
|
|
88
94
|
) -> str:
|
|
89
|
-
r"""Convert a sequence of
|
|
95
|
+
r"""Convert a sequence of messages to strings and concatenate them into one string.
|
|
90
96
|
|
|
91
97
|
Args:
|
|
92
98
|
messages: Messages to be converted to strings.
|
|
93
|
-
human_prefix: The prefix to prepend to contents of
|
|
94
|
-
Default is
|
|
95
|
-
ai_prefix:
|
|
99
|
+
human_prefix: The prefix to prepend to contents of ``HumanMessage``s.
|
|
100
|
+
Default is ``'Human'``.
|
|
101
|
+
ai_prefix: The prefix to prepend to contents of ``AIMessage``. Default is
|
|
102
|
+
``'AI'``.
|
|
96
103
|
|
|
97
104
|
Returns:
|
|
98
105
|
A single string concatenation of all input messages.
|
|
@@ -171,19 +178,20 @@ def _message_from_dict(message: dict) -> BaseMessage:
|
|
|
171
178
|
|
|
172
179
|
|
|
173
180
|
def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
|
|
174
|
-
"""Convert a sequence of messages from dicts to Message objects.
|
|
181
|
+
"""Convert a sequence of messages from dicts to ``Message`` objects.
|
|
175
182
|
|
|
176
183
|
Args:
|
|
177
184
|
messages: Sequence of messages (as dicts) to convert.
|
|
178
185
|
|
|
179
186
|
Returns:
|
|
180
187
|
list of messages (BaseMessages).
|
|
188
|
+
|
|
181
189
|
"""
|
|
182
190
|
return [_message_from_dict(m) for m in messages]
|
|
183
191
|
|
|
184
192
|
|
|
185
|
-
def message_chunk_to_message(chunk:
|
|
186
|
-
"""Convert a message chunk to a
|
|
193
|
+
def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
|
|
194
|
+
"""Convert a message chunk to a ``Message``.
|
|
187
195
|
|
|
188
196
|
Args:
|
|
189
197
|
chunk: Message chunk to convert.
|
|
@@ -216,10 +224,10 @@ def _create_message_from_message_type(
|
|
|
216
224
|
id: Optional[str] = None,
|
|
217
225
|
**additional_kwargs: Any,
|
|
218
226
|
) -> BaseMessage:
|
|
219
|
-
"""Create a message from a
|
|
227
|
+
"""Create a message from a ``Message`` type and content string.
|
|
220
228
|
|
|
221
229
|
Args:
|
|
222
|
-
message_type: (str) the type of the message (e.g.,
|
|
230
|
+
message_type: (str) the type of the message (e.g., ``'human'``, ``'ai'``, etc.).
|
|
223
231
|
content: (str) the content string.
|
|
224
232
|
name: (str) the name of the message. Default is None.
|
|
225
233
|
tool_call_id: (str) the tool call id. Default is None.
|
|
@@ -231,8 +239,9 @@ def _create_message_from_message_type(
|
|
|
231
239
|
a message of the appropriate type.
|
|
232
240
|
|
|
233
241
|
Raises:
|
|
234
|
-
ValueError: if the message type is not one of
|
|
235
|
-
|
|
242
|
+
ValueError: if the message type is not one of ``'human'``, ``'user'``, ``'ai'``,
|
|
243
|
+
``'assistant'``, ``'function'``, ``'tool'``, ``'system'``, or
|
|
244
|
+
``'developer'``.
|
|
236
245
|
"""
|
|
237
246
|
kwargs: dict[str, Any] = {}
|
|
238
247
|
if name is not None:
|
|
@@ -281,6 +290,9 @@ def _create_message_from_message_type(
|
|
|
281
290
|
message = FunctionMessage(content=content, **kwargs)
|
|
282
291
|
elif message_type == "tool":
|
|
283
292
|
artifact = kwargs.get("additional_kwargs", {}).pop("artifact", None)
|
|
293
|
+
status = kwargs.get("additional_kwargs", {}).pop("status", None)
|
|
294
|
+
if status is not None:
|
|
295
|
+
kwargs["status"] = status
|
|
284
296
|
message = ToolMessage(content=content, artifact=artifact, **kwargs)
|
|
285
297
|
elif message_type == "remove":
|
|
286
298
|
message = RemoveMessage(**kwargs)
|
|
@@ -295,15 +307,15 @@ def _create_message_from_message_type(
|
|
|
295
307
|
|
|
296
308
|
|
|
297
309
|
def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
|
|
298
|
-
"""Instantiate a
|
|
310
|
+
"""Instantiate a ``Message`` from a variety of message formats.
|
|
299
311
|
|
|
300
312
|
The message format can be one of the following:
|
|
301
313
|
|
|
302
|
-
- BaseMessagePromptTemplate
|
|
303
|
-
- BaseMessage
|
|
304
|
-
- 2-tuple of (role string, template); e.g., (
|
|
314
|
+
- ``BaseMessagePromptTemplate``
|
|
315
|
+
- ``BaseMessage``
|
|
316
|
+
- 2-tuple of (role string, template); e.g., (``'human'``, ``'{user_input}'``)
|
|
305
317
|
- dict: a message dict with role and content keys
|
|
306
|
-
- string: shorthand for (
|
|
318
|
+
- string: shorthand for (``'human'``, template); e.g., ``'{user_input}'``
|
|
307
319
|
|
|
308
320
|
Args:
|
|
309
321
|
message: a representation of a message in one of the supported formats.
|
|
@@ -314,6 +326,7 @@ def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
|
|
|
314
326
|
Raises:
|
|
315
327
|
NotImplementedError: if the message type is not supported.
|
|
316
328
|
ValueError: if the message dict does not contain the required keys.
|
|
329
|
+
|
|
317
330
|
"""
|
|
318
331
|
if isinstance(message, BaseMessage):
|
|
319
332
|
message_ = message
|
|
@@ -359,9 +372,10 @@ def convert_to_messages(
|
|
|
359
372
|
|
|
360
373
|
Returns:
|
|
361
374
|
list of messages (BaseMessages).
|
|
375
|
+
|
|
362
376
|
"""
|
|
363
377
|
# Import here to avoid circular imports
|
|
364
|
-
from langchain_core.prompt_values import PromptValue
|
|
378
|
+
from langchain_core.prompt_values import PromptValue # noqa: PLC0415
|
|
365
379
|
|
|
366
380
|
if isinstance(messages, PromptValue):
|
|
367
381
|
return messages.to_messages()
|
|
@@ -386,7 +400,8 @@ def _runnable_support(func: Callable) -> Callable:
|
|
|
386
400
|
list[BaseMessage],
|
|
387
401
|
Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]],
|
|
388
402
|
]:
|
|
389
|
-
|
|
403
|
+
# Import locally to prevent circular import.
|
|
404
|
+
from langchain_core.runnables.base import RunnableLambda # noqa: PLC0415
|
|
390
405
|
|
|
391
406
|
if messages is not None:
|
|
392
407
|
return func(messages, **kwargs)
|
|
@@ -408,31 +423,36 @@ def filter_messages(
|
|
|
408
423
|
exclude_ids: Optional[Sequence[str]] = None,
|
|
409
424
|
exclude_tool_calls: Optional[Sequence[str] | bool] = None,
|
|
410
425
|
) -> list[BaseMessage]:
|
|
411
|
-
"""Filter messages based on name
|
|
426
|
+
"""Filter messages based on ``name``, ``type`` or ``id``.
|
|
412
427
|
|
|
413
428
|
Args:
|
|
414
429
|
messages: Sequence Message-like objects to filter.
|
|
415
430
|
include_names: Message names to include. Default is None.
|
|
416
431
|
exclude_names: Messages names to exclude. Default is None.
|
|
417
|
-
include_types: Message types to include. Can be specified as string names
|
|
418
|
-
|
|
419
|
-
SystemMessage
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
432
|
+
include_types: Message types to include. Can be specified as string names
|
|
433
|
+
(e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or as ``BaseMessage``
|
|
434
|
+
classes (e.g. ``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...).
|
|
435
|
+
Default is None.
|
|
436
|
+
exclude_types: Message types to exclude. Can be specified as string names
|
|
437
|
+
(e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or as ``BaseMessage``
|
|
438
|
+
classes (e.g. ``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...).
|
|
439
|
+
Default is None.
|
|
423
440
|
include_ids: Message IDs to include. Default is None.
|
|
424
441
|
exclude_ids: Message IDs to exclude. Default is None.
|
|
425
442
|
exclude_tool_calls: Tool call IDs to exclude. Default is None.
|
|
426
443
|
Can be one of the following:
|
|
427
|
-
-
|
|
444
|
+
- ``True``: all ``AIMessage``s with tool calls and all
|
|
445
|
+
``ToolMessage``s will be excluded.
|
|
428
446
|
- a sequence of tool call IDs to exclude:
|
|
429
|
-
-
|
|
430
|
-
|
|
431
|
-
|
|
447
|
+
- ``ToolMessage``s with the corresponding tool call ID will be
|
|
448
|
+
excluded.
|
|
449
|
+
- The ``tool_calls`` in the AIMessage will be updated to exclude
|
|
450
|
+
matching tool calls. If all ``tool_calls`` are filtered from an
|
|
451
|
+
AIMessage, the whole message is excluded.
|
|
432
452
|
|
|
433
453
|
Returns:
|
|
434
|
-
A list of Messages that meets at least one of the incl_
|
|
435
|
-
of the excl_
|
|
454
|
+
A list of Messages that meets at least one of the ``incl_*`` conditions and none
|
|
455
|
+
of the ``excl_*`` conditions. If not ``incl_*`` conditions are specified then
|
|
436
456
|
anything that is not explicitly excluded will be included.
|
|
437
457
|
|
|
438
458
|
Raises:
|
|
@@ -441,14 +461,25 @@ def filter_messages(
|
|
|
441
461
|
Example:
|
|
442
462
|
.. code-block:: python
|
|
443
463
|
|
|
444
|
-
from langchain_core.messages import
|
|
464
|
+
from langchain_core.messages import (
|
|
465
|
+
filter_messages,
|
|
466
|
+
AIMessage,
|
|
467
|
+
HumanMessage,
|
|
468
|
+
SystemMessage,
|
|
469
|
+
)
|
|
445
470
|
|
|
446
471
|
messages = [
|
|
447
472
|
SystemMessage("you're a good assistant."),
|
|
448
473
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
449
474
|
AIMessage("steve-o", id="bar", name="example_assistant"),
|
|
450
|
-
HumanMessage(
|
|
451
|
-
|
|
475
|
+
HumanMessage(
|
|
476
|
+
"what's your favorite color",
|
|
477
|
+
id="baz",
|
|
478
|
+
),
|
|
479
|
+
AIMessage(
|
|
480
|
+
"silicon blue",
|
|
481
|
+
id="blah",
|
|
482
|
+
),
|
|
452
483
|
]
|
|
453
484
|
|
|
454
485
|
filter_messages(
|
|
@@ -465,7 +496,7 @@ def filter_messages(
|
|
|
465
496
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
466
497
|
]
|
|
467
498
|
|
|
468
|
-
"""
|
|
499
|
+
"""
|
|
469
500
|
messages = convert_to_messages(messages)
|
|
470
501
|
filtered: list[BaseMessage] = []
|
|
471
502
|
for msg in messages:
|
|
@@ -533,23 +564,26 @@ def merge_message_runs(
|
|
|
533
564
|
) -> list[BaseMessage]:
|
|
534
565
|
r"""Merge consecutive Messages of the same type.
|
|
535
566
|
|
|
536
|
-
|
|
537
|
-
|
|
567
|
+
.. note::
|
|
568
|
+
ToolMessages are not merged, as each has a distinct tool call id that can't be
|
|
569
|
+
merged.
|
|
538
570
|
|
|
539
571
|
Args:
|
|
540
572
|
messages: Sequence Message-like objects to merge.
|
|
541
573
|
chunk_separator: Specify the string to be inserted between message chunks.
|
|
542
|
-
Default is
|
|
574
|
+
Default is ``'\n'``.
|
|
543
575
|
|
|
544
576
|
Returns:
|
|
545
577
|
list of BaseMessages with consecutive runs of message types merged into single
|
|
546
578
|
messages. By default, if two messages being merged both have string contents,
|
|
547
|
-
the merged content is a concatenation of the two strings with a new-line
|
|
579
|
+
the merged content is a concatenation of the two strings with a new-line
|
|
580
|
+
separator.
|
|
548
581
|
The separator inserted between message chunks can be controlled by specifying
|
|
549
|
-
any string with ``chunk_separator``. If at least one of the messages has a list
|
|
550
|
-
content blocks, the merged content is a list of content blocks.
|
|
582
|
+
any string with ``chunk_separator``. If at least one of the messages has a list
|
|
583
|
+
of content blocks, the merged content is a list of content blocks.
|
|
551
584
|
|
|
552
585
|
Example:
|
|
586
|
+
|
|
553
587
|
.. code-block:: python
|
|
554
588
|
|
|
555
589
|
from langchain_core.messages import (
|
|
@@ -562,16 +596,33 @@ def merge_message_runs(
|
|
|
562
596
|
|
|
563
597
|
messages = [
|
|
564
598
|
SystemMessage("you're a good assistant."),
|
|
565
|
-
HumanMessage(
|
|
566
|
-
|
|
599
|
+
HumanMessage(
|
|
600
|
+
"what's your favorite color",
|
|
601
|
+
id="foo",
|
|
602
|
+
),
|
|
603
|
+
HumanMessage(
|
|
604
|
+
"wait your favorite food",
|
|
605
|
+
id="bar",
|
|
606
|
+
),
|
|
567
607
|
AIMessage(
|
|
568
608
|
"my favorite colo",
|
|
569
|
-
tool_calls=[
|
|
609
|
+
tool_calls=[
|
|
610
|
+
ToolCall(
|
|
611
|
+
name="blah_tool", args={"x": 2}, id="123", type="tool_call"
|
|
612
|
+
)
|
|
613
|
+
],
|
|
570
614
|
id="baz",
|
|
571
615
|
),
|
|
572
616
|
AIMessage(
|
|
573
617
|
[{"type": "text", "text": "my favorite dish is lasagna"}],
|
|
574
|
-
tool_calls=[
|
|
618
|
+
tool_calls=[
|
|
619
|
+
ToolCall(
|
|
620
|
+
name="blah_tool",
|
|
621
|
+
args={"x": -10},
|
|
622
|
+
id="456",
|
|
623
|
+
type="tool_call",
|
|
624
|
+
)
|
|
625
|
+
],
|
|
575
626
|
id="blur",
|
|
576
627
|
),
|
|
577
628
|
]
|
|
@@ -582,21 +633,34 @@ def merge_message_runs(
|
|
|
582
633
|
|
|
583
634
|
[
|
|
584
635
|
SystemMessage("you're a good assistant."),
|
|
585
|
-
HumanMessage(
|
|
636
|
+
HumanMessage(
|
|
637
|
+
"what's your favorite color\\n"
|
|
638
|
+
"wait your favorite food", id="foo",
|
|
639
|
+
),
|
|
586
640
|
AIMessage(
|
|
587
641
|
[
|
|
588
642
|
"my favorite colo",
|
|
589
643
|
{"type": "text", "text": "my favorite dish is lasagna"}
|
|
590
644
|
],
|
|
591
645
|
tool_calls=[
|
|
592
|
-
ToolCall({
|
|
593
|
-
|
|
646
|
+
ToolCall({
|
|
647
|
+
"name": "blah_tool",
|
|
648
|
+
"args": {"x": 2},
|
|
649
|
+
"id": "123",
|
|
650
|
+
"type": "tool_call"
|
|
651
|
+
}),
|
|
652
|
+
ToolCall({
|
|
653
|
+
"name": "blah_tool",
|
|
654
|
+
"args": {"x": -10},
|
|
655
|
+
"id": "456",
|
|
656
|
+
"type": "tool_call"
|
|
657
|
+
})
|
|
594
658
|
]
|
|
595
659
|
id="baz"
|
|
596
660
|
),
|
|
597
661
|
]
|
|
598
662
|
|
|
599
|
-
"""
|
|
663
|
+
"""
|
|
600
664
|
if not messages:
|
|
601
665
|
return []
|
|
602
666
|
messages = convert_to_messages(messages)
|
|
@@ -648,22 +712,22 @@ def trim_messages(
|
|
|
648
712
|
) -> list[BaseMessage]:
|
|
649
713
|
r"""Trim messages to be below a token count.
|
|
650
714
|
|
|
651
|
-
trim_messages can be used to reduce the size of a chat history to a specified
|
|
652
|
-
count or specified message count.
|
|
715
|
+
``trim_messages`` can be used to reduce the size of a chat history to a specified
|
|
716
|
+
token count or specified message count.
|
|
653
717
|
|
|
654
718
|
In either case, if passing the trimmed chat history back into a chat model
|
|
655
719
|
directly, the resulting chat history should usually satisfy the following
|
|
656
720
|
properties:
|
|
657
721
|
|
|
658
722
|
1. The resulting chat history should be valid. Most chat models expect that chat
|
|
659
|
-
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
|
|
660
|
-
by a ``HumanMessage``. To achieve this, set ``start_on=
|
|
723
|
+
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
|
|
724
|
+
followed by a ``HumanMessage``. To achieve this, set ``start_on='human'``.
|
|
661
725
|
In addition, generally a ``ToolMessage`` can only appear after an ``AIMessage``
|
|
662
726
|
that involved a tool call.
|
|
663
727
|
Please see the following link for more information about messages:
|
|
664
728
|
https://python.langchain.com/docs/concepts/#messages
|
|
665
729
|
2. It includes recent messages and drops old messages in the chat history.
|
|
666
|
-
To achieve this set the ``strategy=
|
|
730
|
+
To achieve this set the ``strategy='last'``.
|
|
667
731
|
3. Usually, the new chat history should include the ``SystemMessage`` if it
|
|
668
732
|
was present in the original chat history since the ``SystemMessage`` includes
|
|
669
733
|
special instructions to the chat model. The ``SystemMessage`` is almost always
|
|
@@ -677,65 +741,67 @@ def trim_messages(
|
|
|
677
741
|
Args:
|
|
678
742
|
messages: Sequence of Message-like objects to trim.
|
|
679
743
|
max_tokens: Max token count of trimmed messages.
|
|
680
|
-
token_counter: Function or llm for counting tokens in a BaseMessage or a
|
|
681
|
-
BaseMessage
|
|
682
|
-
BaseLanguageModel.get_num_tokens_from_messages() will be used.
|
|
683
|
-
Set to
|
|
744
|
+
token_counter: Function or llm for counting tokens in a ``BaseMessage`` or a
|
|
745
|
+
list of ``BaseMessage``. If a ``BaseLanguageModel`` is passed in then
|
|
746
|
+
``BaseLanguageModel.get_num_tokens_from_messages()`` will be used.
|
|
747
|
+
Set to ``len`` to count the number of **messages** in the chat history.
|
|
684
748
|
|
|
685
749
|
.. note::
|
|
686
|
-
Use
|
|
687
|
-
|
|
750
|
+
Use ``count_tokens_approximately`` to get fast, approximate token
|
|
751
|
+
counts.
|
|
752
|
+
This is recommended for using ``trim_messages`` on the hot path, where
|
|
688
753
|
exact token counting is not necessary.
|
|
689
754
|
|
|
690
755
|
strategy: Strategy for trimming.
|
|
691
|
-
-
|
|
692
|
-
-
|
|
693
|
-
Default is
|
|
756
|
+
- ``'first'``: Keep the first ``<= n_count`` tokens of the messages.
|
|
757
|
+
- ``'last'``: Keep the last ``<= n_count`` tokens of the messages.
|
|
758
|
+
Default is ``'last'``.
|
|
694
759
|
allow_partial: Whether to split a message if only part of the message can be
|
|
695
|
-
included. If ``strategy=
|
|
696
|
-
are included. If ``strategy=
|
|
760
|
+
included. If ``strategy='last'`` then the last partial contents of a message
|
|
761
|
+
are included. If ``strategy='first'`` then the first partial contents of a
|
|
697
762
|
message are included.
|
|
698
763
|
Default is False.
|
|
699
764
|
end_on: The message type to end on. If specified then every message after the
|
|
700
|
-
last occurrence of this type is ignored. If ``strategy
|
|
765
|
+
last occurrence of this type is ignored. If ``strategy='last'`` then this
|
|
701
766
|
is done before we attempt to get the last ``max_tokens``. If
|
|
702
|
-
``strategy
|
|
703
|
-
``max_tokens``. Can be specified as string names (e.g.
|
|
704
|
-
|
|
705
|
-
AIMessage
|
|
767
|
+
``strategy='first'`` then this is done after we get the first
|
|
768
|
+
``max_tokens``. Can be specified as string names (e.g. ``'system'``,
|
|
769
|
+
``'human'``, ``'ai'``, ...) or as ``BaseMessage`` classes (e.g.
|
|
770
|
+
``SystemMessage``, ``HumanMessage``, ``AIMessage``, ...). Can be a single
|
|
771
|
+
type or a list of types.
|
|
706
772
|
Default is None.
|
|
707
773
|
start_on: The message type to start on. Should only be specified if
|
|
708
|
-
``strategy=
|
|
774
|
+
``strategy='last'``. If specified then every message before
|
|
709
775
|
the first occurrence of this type is ignored. This is done after we trim
|
|
710
776
|
the initial messages to the last ``max_tokens``. Does not
|
|
711
|
-
apply to a SystemMessage at index 0 if ``include_system=True``. Can be
|
|
712
|
-
specified as string names (e.g.
|
|
713
|
-
BaseMessage classes (e.g. SystemMessage
|
|
714
|
-
be a single type or a list of types.
|
|
777
|
+
apply to a ``SystemMessage`` at index 0 if ``include_system=True``. Can be
|
|
778
|
+
specified as string names (e.g. ``'system'``, ``'human'``, ``'ai'``, ...) or
|
|
779
|
+
as ``BaseMessage`` classes (e.g. ``SystemMessage``, ``HumanMessage``,
|
|
780
|
+
``AIMessage``, ...). Can be a single type or a list of types.
|
|
715
781
|
Default is None.
|
|
716
782
|
include_system: Whether to keep the SystemMessage if there is one at index 0.
|
|
717
783
|
Should only be specified if ``strategy="last"``.
|
|
718
784
|
Default is False.
|
|
719
785
|
text_splitter: Function or ``langchain_text_splitters.TextSplitter`` for
|
|
720
786
|
splitting the string contents of a message. Only used if
|
|
721
|
-
``allow_partial=True``. If ``strategy=
|
|
722
|
-
from a partial message will be included. if ``strategy
|
|
787
|
+
``allow_partial=True``. If ``strategy='last'`` then the last split tokens
|
|
788
|
+
from a partial message will be included. if ``strategy='first'`` then the
|
|
723
789
|
first split tokens from a partial message will be included. Token splitter
|
|
724
790
|
assumes that separators are kept, so that split contents can be directly
|
|
725
791
|
concatenated to recreate the original text. Defaults to splitting on
|
|
726
792
|
newlines.
|
|
727
793
|
|
|
728
794
|
Returns:
|
|
729
|
-
list of trimmed
|
|
795
|
+
list of trimmed ``BaseMessage``.
|
|
730
796
|
|
|
731
797
|
Raises:
|
|
732
798
|
ValueError: if two incompatible arguments are specified or an unrecognized
|
|
733
799
|
``strategy`` is specified.
|
|
734
800
|
|
|
735
801
|
Example:
|
|
736
|
-
Trim chat history based on token count, keeping the SystemMessage if
|
|
737
|
-
present, and ensuring that the chat history starts with a HumanMessage (
|
|
738
|
-
or a SystemMessage followed by a HumanMessage).
|
|
802
|
+
Trim chat history based on token count, keeping the ``SystemMessage`` if
|
|
803
|
+
present, and ensuring that the chat history starts with a ``HumanMessage`` (
|
|
804
|
+
or a ``SystemMessage`` followed by a ``HumanMessage``).
|
|
739
805
|
|
|
740
806
|
.. code-block:: python
|
|
741
807
|
|
|
@@ -748,14 +814,18 @@ def trim_messages(
|
|
|
748
814
|
)
|
|
749
815
|
|
|
750
816
|
messages = [
|
|
751
|
-
SystemMessage(
|
|
817
|
+
SystemMessage(
|
|
818
|
+
"you're a good assistant, you always respond with a joke."
|
|
819
|
+
),
|
|
752
820
|
HumanMessage("i wonder why it's called langchain"),
|
|
753
821
|
AIMessage(
|
|
754
|
-
'Well, I guess they thought "WordRope" and "SentenceString" just
|
|
822
|
+
'Well, I guess they thought "WordRope" and "SentenceString" just '
|
|
823
|
+
"didn't have the same ring to it!"
|
|
755
824
|
),
|
|
756
825
|
HumanMessage("and who is harrison chasing anyways"),
|
|
757
826
|
AIMessage(
|
|
758
|
-
"Hmmm let me think.\n\nWhy, he's probably chasing after the last
|
|
827
|
+
"Hmmm let me think.\n\nWhy, he's probably chasing after the last "
|
|
828
|
+
"cup of coffee in the office!"
|
|
759
829
|
),
|
|
760
830
|
HumanMessage("what do you call a speechless parrot"),
|
|
761
831
|
]
|
|
@@ -780,13 +850,15 @@ def trim_messages(
|
|
|
780
850
|
.. code-block:: python
|
|
781
851
|
|
|
782
852
|
[
|
|
783
|
-
SystemMessage(
|
|
784
|
-
|
|
853
|
+
SystemMessage(
|
|
854
|
+
content="you're a good assistant, you always respond with a joke."
|
|
855
|
+
),
|
|
856
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
785
857
|
]
|
|
786
858
|
|
|
787
|
-
Trim chat history based on the message count, keeping the SystemMessage if
|
|
788
|
-
present, and ensuring that the chat history starts with a HumanMessage (
|
|
789
|
-
or a SystemMessage followed by a HumanMessage).
|
|
859
|
+
Trim chat history based on the message count, keeping the ``SystemMessage`` if
|
|
860
|
+
present, and ensuring that the chat history starts with a ``HumanMessage`` (
|
|
861
|
+
or a ``SystemMessage`` followed by a ``HumanMessage``).
|
|
790
862
|
|
|
791
863
|
trim_messages(
|
|
792
864
|
messages,
|
|
@@ -811,10 +883,15 @@ def trim_messages(
|
|
|
811
883
|
.. code-block:: python
|
|
812
884
|
|
|
813
885
|
[
|
|
814
|
-
SystemMessage(
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
HumanMessage(content=
|
|
886
|
+
SystemMessage(
|
|
887
|
+
content="you're a good assistant, you always respond with a joke."
|
|
888
|
+
),
|
|
889
|
+
HumanMessage(content="and who is harrison chasing anyways"),
|
|
890
|
+
AIMessage(
|
|
891
|
+
content="Hmmm let me think.\n\nWhy, he's probably chasing after "
|
|
892
|
+
"the last cup of coffee in the office!"
|
|
893
|
+
),
|
|
894
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
818
895
|
]
|
|
819
896
|
|
|
820
897
|
|
|
@@ -825,7 +902,9 @@ def trim_messages(
|
|
|
825
902
|
|
|
826
903
|
messages = [
|
|
827
904
|
SystemMessage("This is a 4 token text. The full message is 10 tokens."),
|
|
828
|
-
HumanMessage(
|
|
905
|
+
HumanMessage(
|
|
906
|
+
"This is a 4 token text. The full message is 10 tokens.", id="first"
|
|
907
|
+
),
|
|
829
908
|
AIMessage(
|
|
830
909
|
[
|
|
831
910
|
{"type": "text", "text": "This is the FIRST 4 token block."},
|
|
@@ -833,10 +912,16 @@ def trim_messages(
|
|
|
833
912
|
],
|
|
834
913
|
id="second",
|
|
835
914
|
),
|
|
836
|
-
HumanMessage(
|
|
837
|
-
|
|
915
|
+
HumanMessage(
|
|
916
|
+
"This is a 4 token text. The full message is 10 tokens.", id="third"
|
|
917
|
+
),
|
|
918
|
+
AIMessage(
|
|
919
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
920
|
+
id="fourth",
|
|
921
|
+
),
|
|
838
922
|
]
|
|
839
923
|
|
|
924
|
+
|
|
840
925
|
def dummy_token_counter(messages: list[BaseMessage]) -> int:
|
|
841
926
|
# treat each message like it adds 3 default tokens at the beginning
|
|
842
927
|
# of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
|
|
@@ -849,9 +934,17 @@ def trim_messages(
|
|
|
849
934
|
count = 0
|
|
850
935
|
for msg in messages:
|
|
851
936
|
if isinstance(msg.content, str):
|
|
852
|
-
count +=
|
|
937
|
+
count += (
|
|
938
|
+
default_msg_prefix_len
|
|
939
|
+
+ default_content_len
|
|
940
|
+
+ default_msg_suffix_len
|
|
941
|
+
)
|
|
853
942
|
if isinstance(msg.content, list):
|
|
854
|
-
count +=
|
|
943
|
+
count += (
|
|
944
|
+
default_msg_prefix_len
|
|
945
|
+
+ len(msg.content) * default_content_len
|
|
946
|
+
+ default_msg_suffix_len
|
|
947
|
+
)
|
|
855
948
|
return count
|
|
856
949
|
|
|
857
950
|
First 30 tokens, allowing partial messages:
|
|
@@ -868,12 +961,20 @@ def trim_messages(
|
|
|
868
961
|
.. code-block:: python
|
|
869
962
|
|
|
870
963
|
[
|
|
871
|
-
SystemMessage(
|
|
872
|
-
|
|
873
|
-
|
|
964
|
+
SystemMessage(
|
|
965
|
+
"This is a 4 token text. The full message is 10 tokens."
|
|
966
|
+
),
|
|
967
|
+
HumanMessage(
|
|
968
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
969
|
+
id="first",
|
|
970
|
+
),
|
|
971
|
+
AIMessage(
|
|
972
|
+
[{"type": "text", "text": "This is the FIRST 4 token block."}],
|
|
973
|
+
id="second",
|
|
974
|
+
),
|
|
874
975
|
]
|
|
875
976
|
|
|
876
|
-
"""
|
|
977
|
+
"""
|
|
877
978
|
# Validate arguments
|
|
878
979
|
if start_on and strategy == "first":
|
|
879
980
|
msg = "start_on parameter is only valid with strategy='last'"
|
|
@@ -904,17 +1005,12 @@ def trim_messages(
|
|
|
904
1005
|
)
|
|
905
1006
|
raise ValueError(msg)
|
|
906
1007
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
text_splitter_fn
|
|
1008
|
+
if _HAS_LANGCHAIN_TEXT_SPLITTERS and isinstance(text_splitter, TextSplitter):
|
|
1009
|
+
text_splitter_fn = text_splitter.split_text
|
|
1010
|
+
elif text_splitter:
|
|
1011
|
+
text_splitter_fn = cast("Callable", text_splitter)
|
|
911
1012
|
else:
|
|
912
|
-
|
|
913
|
-
text_splitter_fn = text_splitter.split_text
|
|
914
|
-
else:
|
|
915
|
-
text_splitter_fn = text_splitter
|
|
916
|
-
|
|
917
|
-
text_splitter_fn = text_splitter_fn or _default_text_splitter
|
|
1013
|
+
text_splitter_fn = _default_text_splitter
|
|
918
1014
|
|
|
919
1015
|
if strategy == "first":
|
|
920
1016
|
return _first_max_tokens(
|
|
@@ -951,26 +1047,30 @@ def convert_to_openai_messages(
|
|
|
951
1047
|
messages: Message-like object or iterable of objects whose contents are
|
|
952
1048
|
in OpenAI, Anthropic, Bedrock Converse, or VertexAI formats.
|
|
953
1049
|
text_format: How to format string or text block contents:
|
|
954
|
-
|
|
955
|
-
- "string":
|
|
1050
|
+
- ``'string'``:
|
|
956
1051
|
If a message has a string content, this is left as a string. If
|
|
957
|
-
a message has content blocks that are all of type 'text'
|
|
958
|
-
joined with a newline to make a single string. If a message has
|
|
959
|
-
content blocks and at least one isn't of type 'text'
|
|
1052
|
+
a message has content blocks that are all of type ``'text'``, these
|
|
1053
|
+
are joined with a newline to make a single string. If a message has
|
|
1054
|
+
content blocks and at least one isn't of type ``'text'``, then
|
|
960
1055
|
all blocks are left as dicts.
|
|
961
|
-
-
|
|
1056
|
+
- ``'block'``:
|
|
962
1057
|
If a message has a string content, this is turned into a list
|
|
963
|
-
with a single content block of type 'text'
|
|
964
|
-
blocks these are left as is.
|
|
1058
|
+
with a single content block of type ``'text'``. If a message has
|
|
1059
|
+
content blocks these are left as is.
|
|
1060
|
+
|
|
1061
|
+
Raises:
|
|
1062
|
+
ValueError: if an unrecognized ``text_format`` is specified, or if a message
|
|
1063
|
+
content block is missing expected keys.
|
|
965
1064
|
|
|
966
1065
|
Returns:
|
|
967
1066
|
The return type depends on the input type:
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1067
|
+
|
|
1068
|
+
- dict:
|
|
1069
|
+
If a single message-like object is passed in, a single OpenAI message
|
|
1070
|
+
dict is returned.
|
|
1071
|
+
- list[dict]:
|
|
1072
|
+
If a sequence of message-like objects are passed in, a list of OpenAI
|
|
1073
|
+
message dicts is returned.
|
|
974
1074
|
|
|
975
1075
|
Example:
|
|
976
1076
|
|
|
@@ -985,8 +1085,27 @@ def convert_to_openai_messages(
|
|
|
985
1085
|
|
|
986
1086
|
messages = [
|
|
987
1087
|
SystemMessage([{"type": "text", "text": "foo"}]),
|
|
988
|
-
{
|
|
989
|
-
|
|
1088
|
+
{
|
|
1089
|
+
"role": "user",
|
|
1090
|
+
"content": [
|
|
1091
|
+
{"type": "text", "text": "whats in this"},
|
|
1092
|
+
{
|
|
1093
|
+
"type": "image_url",
|
|
1094
|
+
"image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"},
|
|
1095
|
+
},
|
|
1096
|
+
],
|
|
1097
|
+
},
|
|
1098
|
+
AIMessage(
|
|
1099
|
+
"",
|
|
1100
|
+
tool_calls=[
|
|
1101
|
+
{
|
|
1102
|
+
"name": "analyze",
|
|
1103
|
+
"args": {"baz": "buz"},
|
|
1104
|
+
"id": "1",
|
|
1105
|
+
"type": "tool_call",
|
|
1106
|
+
}
|
|
1107
|
+
],
|
|
1108
|
+
),
|
|
990
1109
|
ToolMessage("foobar", tool_call_id="1", name="bar"),
|
|
991
1110
|
{"role": "assistant", "content": "thats nice"},
|
|
992
1111
|
]
|
|
@@ -1266,7 +1385,7 @@ def convert_to_openai_messages(
|
|
|
1266
1385
|
},
|
|
1267
1386
|
}
|
|
1268
1387
|
)
|
|
1269
|
-
elif block.get("type")
|
|
1388
|
+
elif block.get("type") in ["thinking", "reasoning"]:
|
|
1270
1389
|
content.append(block)
|
|
1271
1390
|
else:
|
|
1272
1391
|
err = (
|