langchain-core 0.3.79__py3-none-any.whl → 1.0.0__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/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +23 -26
- langchain_core/_api/deprecation.py +52 -65
- langchain_core/_api/path.py +3 -6
- langchain_core/_import_utils.py +3 -4
- langchain_core/agents.py +19 -19
- langchain_core/caches.py +53 -63
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +323 -334
- langchain_core/callbacks/file.py +44 -44
- langchain_core/callbacks/manager.py +441 -507
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +32 -32
- langchain_core/callbacks/usage.py +60 -57
- langchain_core/chat_history.py +48 -63
- langchain_core/document_loaders/base.py +23 -23
- langchain_core/document_loaders/langsmith.py +37 -37
- langchain_core/documents/__init__.py +0 -1
- langchain_core/documents/base.py +62 -65
- langchain_core/documents/compressor.py +4 -4
- langchain_core/documents/transformers.py +28 -29
- langchain_core/embeddings/fake.py +50 -54
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +21 -25
- langchain_core/exceptions.py +10 -11
- langchain_core/globals.py +3 -151
- langchain_core/indexing/api.py +61 -66
- langchain_core/indexing/base.py +58 -58
- langchain_core/indexing/in_memory.py +3 -3
- langchain_core/language_models/__init__.py +14 -27
- langchain_core/language_models/_utils.py +270 -84
- langchain_core/language_models/base.py +55 -162
- langchain_core/language_models/chat_models.py +442 -402
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +61 -39
- langchain_core/language_models/llms.py +123 -231
- langchain_core/load/dump.py +4 -5
- langchain_core/load/load.py +18 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +39 -40
- langchain_core/messages/__init__.py +61 -22
- langchain_core/messages/ai.py +368 -163
- langchain_core/messages/base.py +214 -43
- langchain_core/messages/block_translators/__init__.py +111 -0
- langchain_core/messages/block_translators/anthropic.py +470 -0
- langchain_core/messages/block_translators/bedrock.py +94 -0
- langchain_core/messages/block_translators/bedrock_converse.py +297 -0
- langchain_core/messages/block_translators/google_genai.py +530 -0
- langchain_core/messages/block_translators/google_vertexai.py +21 -0
- langchain_core/messages/block_translators/groq.py +143 -0
- langchain_core/messages/block_translators/langchain_v0.py +301 -0
- langchain_core/messages/block_translators/openai.py +1010 -0
- langchain_core/messages/chat.py +2 -6
- langchain_core/messages/content.py +1423 -0
- langchain_core/messages/function.py +6 -10
- langchain_core/messages/human.py +41 -38
- langchain_core/messages/modifier.py +2 -2
- langchain_core/messages/system.py +38 -28
- langchain_core/messages/tool.py +96 -103
- langchain_core/messages/utils.py +478 -504
- langchain_core/output_parsers/__init__.py +1 -14
- langchain_core/output_parsers/base.py +58 -61
- langchain_core/output_parsers/json.py +7 -8
- langchain_core/output_parsers/list.py +5 -7
- langchain_core/output_parsers/openai_functions.py +49 -47
- langchain_core/output_parsers/openai_tools.py +14 -19
- langchain_core/output_parsers/pydantic.py +12 -13
- langchain_core/output_parsers/string.py +2 -2
- langchain_core/output_parsers/transform.py +15 -17
- langchain_core/output_parsers/xml.py +8 -10
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +18 -18
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +8 -8
- langchain_core/outputs/llm_result.py +10 -10
- langchain_core/prompt_values.py +12 -12
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +45 -55
- langchain_core/prompts/chat.py +254 -313
- langchain_core/prompts/dict.py +5 -5
- langchain_core/prompts/few_shot.py +81 -88
- langchain_core/prompts/few_shot_with_templates.py +11 -13
- langchain_core/prompts/image.py +12 -14
- langchain_core/prompts/loading.py +6 -8
- langchain_core/prompts/message.py +3 -3
- langchain_core/prompts/prompt.py +24 -39
- langchain_core/prompts/string.py +4 -4
- langchain_core/prompts/structured.py +42 -50
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +49 -190
- langchain_core/runnables/base.py +1484 -1709
- langchain_core/runnables/branch.py +45 -61
- langchain_core/runnables/config.py +80 -88
- langchain_core/runnables/configurable.py +117 -134
- langchain_core/runnables/fallbacks.py +83 -79
- langchain_core/runnables/graph.py +85 -95
- langchain_core/runnables/graph_ascii.py +27 -28
- langchain_core/runnables/graph_mermaid.py +38 -50
- langchain_core/runnables/graph_png.py +15 -16
- langchain_core/runnables/history.py +135 -148
- langchain_core/runnables/passthrough.py +124 -150
- langchain_core/runnables/retry.py +46 -51
- langchain_core/runnables/router.py +25 -30
- langchain_core/runnables/schema.py +79 -74
- langchain_core/runnables/utils.py +62 -68
- langchain_core/stores.py +81 -115
- langchain_core/structured_query.py +8 -8
- langchain_core/sys_info.py +27 -29
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +179 -187
- langchain_core/tools/convert.py +131 -139
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +11 -11
- langchain_core/tools/simple.py +19 -24
- langchain_core/tools/structured.py +30 -39
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/base.py +97 -99
- langchain_core/tracers/context.py +29 -52
- langchain_core/tracers/core.py +50 -60
- langchain_core/tracers/evaluation.py +11 -11
- langchain_core/tracers/event_stream.py +115 -70
- langchain_core/tracers/langchain.py +21 -21
- langchain_core/tracers/log_stream.py +43 -43
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +16 -16
- langchain_core/tracers/run_collector.py +2 -4
- langchain_core/tracers/schemas.py +0 -129
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +46 -8
- langchain_core/utils/aiter.py +57 -61
- langchain_core/utils/env.py +9 -9
- langchain_core/utils/function_calling.py +89 -191
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +1 -1
- langchain_core/utils/iter.py +37 -42
- langchain_core/utils/json.py +4 -3
- langchain_core/utils/json_schema.py +8 -8
- langchain_core/utils/mustache.py +9 -11
- langchain_core/utils/pydantic.py +33 -35
- langchain_core/utils/strings.py +5 -5
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +80 -54
- langchain_core/vectorstores/base.py +129 -164
- langchain_core/vectorstores/in_memory.py +99 -174
- langchain_core/vectorstores/utils.py +5 -5
- langchain_core/version.py +1 -1
- {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/METADATA +28 -27
- langchain_core-1.0.0.dist-info/RECORD +172 -0
- {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
- langchain_core/beta/__init__.py +0 -1
- langchain_core/beta/runnables/__init__.py +0 -1
- langchain_core/beta/runnables/context.py +0 -447
- langchain_core/memory.py +0 -120
- langchain_core/messages/content_blocks.py +0 -176
- langchain_core/prompts/pipeline.py +0 -138
- langchain_core/pydantic_v1/__init__.py +0 -30
- langchain_core/pydantic_v1/dataclasses.py +0 -23
- langchain_core/pydantic_v1/main.py +0 -23
- langchain_core/tracers/langchain_v1.py +0 -31
- langchain_core/utils/loading.py +0 -35
- langchain_core-0.3.79.dist-info/RECORD +0 -174
- langchain_core-0.3.79.dist-info/entry_points.txt +0 -4
langchain_core/messages/utils.py
CHANGED
|
@@ -5,7 +5,6 @@ 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
|
-
|
|
9
8
|
"""
|
|
10
9
|
|
|
11
10
|
from __future__ import annotations
|
|
@@ -15,16 +14,13 @@ import inspect
|
|
|
15
14
|
import json
|
|
16
15
|
import logging
|
|
17
16
|
import math
|
|
18
|
-
from collections.abc import Iterable, Sequence
|
|
17
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
19
18
|
from functools import partial
|
|
20
19
|
from typing import (
|
|
21
20
|
TYPE_CHECKING,
|
|
22
21
|
Annotated,
|
|
23
22
|
Any,
|
|
24
|
-
Callable,
|
|
25
23
|
Literal,
|
|
26
|
-
Optional,
|
|
27
|
-
Union,
|
|
28
24
|
cast,
|
|
29
25
|
overload,
|
|
30
26
|
)
|
|
@@ -32,10 +28,15 @@ from typing import (
|
|
|
32
28
|
from pydantic import Discriminator, Field, Tag
|
|
33
29
|
|
|
34
30
|
from langchain_core.exceptions import ErrorCode, create_message
|
|
35
|
-
from langchain_core.messages import convert_to_openai_data_block, is_data_content_block
|
|
36
31
|
from langchain_core.messages.ai import AIMessage, AIMessageChunk
|
|
37
32
|
from langchain_core.messages.base import BaseMessage, BaseMessageChunk
|
|
33
|
+
from langchain_core.messages.block_translators.openai import (
|
|
34
|
+
convert_to_openai_data_block,
|
|
35
|
+
)
|
|
38
36
|
from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
|
|
37
|
+
from langchain_core.messages.content import (
|
|
38
|
+
is_data_content_block,
|
|
39
|
+
)
|
|
39
40
|
from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
|
|
40
41
|
from langchain_core.messages.human import HumanMessage, HumanMessageChunk
|
|
41
42
|
from langchain_core.messages.modifier import RemoveMessage
|
|
@@ -71,22 +72,21 @@ def _get_type(v: Any) -> str:
|
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
AnyMessage = Annotated[
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
|
|
87
|
-
],
|
|
75
|
+
Annotated[AIMessage, Tag(tag="ai")]
|
|
76
|
+
| Annotated[HumanMessage, Tag(tag="human")]
|
|
77
|
+
| Annotated[ChatMessage, Tag(tag="chat")]
|
|
78
|
+
| Annotated[SystemMessage, Tag(tag="system")]
|
|
79
|
+
| Annotated[FunctionMessage, Tag(tag="function")]
|
|
80
|
+
| Annotated[ToolMessage, Tag(tag="tool")]
|
|
81
|
+
| Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")]
|
|
82
|
+
| Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")]
|
|
83
|
+
| Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")]
|
|
84
|
+
| Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")]
|
|
85
|
+
| Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")]
|
|
86
|
+
| Annotated[ToolMessageChunk, Tag(tag="ToolMessageChunk")],
|
|
88
87
|
Field(discriminator=Discriminator(_get_type)),
|
|
89
88
|
]
|
|
89
|
+
""""A type representing any defined `Message` or `MessageChunk` type."""
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def get_buffer_string(
|
|
@@ -96,10 +96,8 @@ def get_buffer_string(
|
|
|
96
96
|
|
|
97
97
|
Args:
|
|
98
98
|
messages: Messages to be converted to strings.
|
|
99
|
-
human_prefix: The prefix to prepend to contents of
|
|
100
|
-
|
|
101
|
-
ai_prefix: The prefix to prepend to contents of ``AIMessage``. Default is
|
|
102
|
-
``'AI'``.
|
|
99
|
+
human_prefix: The prefix to prepend to contents of `HumanMessage`s.
|
|
100
|
+
ai_prefix: The prefix to prepend to contents of `AIMessage`.
|
|
103
101
|
|
|
104
102
|
Returns:
|
|
105
103
|
A single string concatenation of all input messages.
|
|
@@ -108,17 +106,16 @@ def get_buffer_string(
|
|
|
108
106
|
ValueError: If an unsupported message type is encountered.
|
|
109
107
|
|
|
110
108
|
Example:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
```python
|
|
110
|
+
from langchain_core import AIMessage, HumanMessage
|
|
111
|
+
|
|
112
|
+
messages = [
|
|
113
|
+
HumanMessage(content="Hi, how are you?"),
|
|
114
|
+
AIMessage(content="Good, how are you?"),
|
|
115
|
+
]
|
|
116
|
+
get_buffer_string(messages)
|
|
117
|
+
# -> "Human: Hi, how are you?\nAI: Good, how are you?"
|
|
118
|
+
```
|
|
122
119
|
"""
|
|
123
120
|
string_messages = []
|
|
124
121
|
for m in messages:
|
|
@@ -137,7 +134,7 @@ def get_buffer_string(
|
|
|
137
134
|
else:
|
|
138
135
|
msg = f"Got unsupported message type: {m}"
|
|
139
136
|
raise ValueError(msg) # noqa: TRY004
|
|
140
|
-
message = f"{role}: {m.text
|
|
137
|
+
message = f"{role}: {m.text}"
|
|
141
138
|
if isinstance(m, AIMessage) and "function_call" in m.additional_kwargs:
|
|
142
139
|
message += f"{m.additional_kwargs['function_call']}"
|
|
143
140
|
string_messages.append(message)
|
|
@@ -178,7 +175,7 @@ def _message_from_dict(message: dict) -> BaseMessage:
|
|
|
178
175
|
|
|
179
176
|
|
|
180
177
|
def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
|
|
181
|
-
"""Convert a sequence of messages from dicts to
|
|
178
|
+
"""Convert a sequence of messages from dicts to `Message` objects.
|
|
182
179
|
|
|
183
180
|
Args:
|
|
184
181
|
messages: Sequence of messages (as dicts) to convert.
|
|
@@ -191,7 +188,7 @@ def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
|
|
|
191
188
|
|
|
192
189
|
|
|
193
190
|
def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
|
|
194
|
-
"""Convert a message chunk to a
|
|
191
|
+
"""Convert a message chunk to a `Message`.
|
|
195
192
|
|
|
196
193
|
Args:
|
|
197
194
|
chunk: Message chunk to convert.
|
|
@@ -204,44 +201,45 @@ def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
|
|
|
204
201
|
# chunk classes always have the equivalent non-chunk class as their first parent
|
|
205
202
|
ignore_keys = ["type"]
|
|
206
203
|
if isinstance(chunk, AIMessageChunk):
|
|
207
|
-
ignore_keys.
|
|
204
|
+
ignore_keys.extend(["tool_call_chunks", "chunk_position"])
|
|
208
205
|
return chunk.__class__.__mro__[1](
|
|
209
206
|
**{k: v for k, v in chunk.__dict__.items() if k not in ignore_keys}
|
|
210
207
|
)
|
|
211
208
|
|
|
212
209
|
|
|
213
|
-
MessageLikeRepresentation =
|
|
214
|
-
BaseMessage
|
|
215
|
-
|
|
210
|
+
MessageLikeRepresentation = (
|
|
211
|
+
BaseMessage | list[str] | tuple[str, str] | str | dict[str, Any]
|
|
212
|
+
)
|
|
213
|
+
"""A type representing the various ways a message can be represented."""
|
|
216
214
|
|
|
217
215
|
|
|
218
216
|
def _create_message_from_message_type(
|
|
219
217
|
message_type: str,
|
|
220
218
|
content: str,
|
|
221
|
-
name:
|
|
222
|
-
tool_call_id:
|
|
223
|
-
tool_calls:
|
|
224
|
-
id:
|
|
219
|
+
name: str | None = None,
|
|
220
|
+
tool_call_id: str | None = None,
|
|
221
|
+
tool_calls: list[dict[str, Any]] | None = None,
|
|
222
|
+
id: str | None = None,
|
|
225
223
|
**additional_kwargs: Any,
|
|
226
224
|
) -> BaseMessage:
|
|
227
|
-
"""Create a message from a
|
|
225
|
+
"""Create a message from a `Message` type and content string.
|
|
228
226
|
|
|
229
227
|
Args:
|
|
230
|
-
message_type: (str) the type of the message (e.g.,
|
|
228
|
+
message_type: (str) the type of the message (e.g., `'human'`, `'ai'`, etc.).
|
|
231
229
|
content: (str) the content string.
|
|
232
|
-
name: (str) the name of the message.
|
|
233
|
-
tool_call_id: (str) the tool call id.
|
|
234
|
-
tool_calls: (list[dict[str, Any]]) the tool calls.
|
|
235
|
-
id: (str) the id of the message.
|
|
230
|
+
name: (str) the name of the message.
|
|
231
|
+
tool_call_id: (str) the tool call id.
|
|
232
|
+
tool_calls: (list[dict[str, Any]]) the tool calls.
|
|
233
|
+
id: (str) the id of the message.
|
|
236
234
|
additional_kwargs: (dict[str, Any]) additional keyword arguments.
|
|
237
235
|
|
|
238
236
|
Returns:
|
|
239
237
|
a message of the appropriate type.
|
|
240
238
|
|
|
241
239
|
Raises:
|
|
242
|
-
ValueError: if the message type is not one of
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
ValueError: if the message type is not one of `'human'`, `'user'`, `'ai'`,
|
|
241
|
+
`'assistant'`, `'function'`, `'tool'`, `'system'`, or
|
|
242
|
+
`'developer'`.
|
|
245
243
|
"""
|
|
246
244
|
kwargs: dict[str, Any] = {}
|
|
247
245
|
if name is not None:
|
|
@@ -307,21 +305,21 @@ def _create_message_from_message_type(
|
|
|
307
305
|
|
|
308
306
|
|
|
309
307
|
def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
|
|
310
|
-
"""Instantiate a
|
|
308
|
+
"""Instantiate a `Message` from a variety of message formats.
|
|
311
309
|
|
|
312
310
|
The message format can be one of the following:
|
|
313
311
|
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
- 2-tuple of (role string, template); e.g., (
|
|
312
|
+
- `BaseMessagePromptTemplate`
|
|
313
|
+
- `BaseMessage`
|
|
314
|
+
- 2-tuple of (role string, template); e.g., (`'human'`, `'{user_input}'`)
|
|
317
315
|
- dict: a message dict with role and content keys
|
|
318
|
-
- string: shorthand for (
|
|
316
|
+
- string: shorthand for (`'human'`, template); e.g., `'{user_input}'`
|
|
319
317
|
|
|
320
318
|
Args:
|
|
321
319
|
message: a representation of a message in one of the supported formats.
|
|
322
320
|
|
|
323
321
|
Returns:
|
|
324
|
-
|
|
322
|
+
An instance of a message or a message template.
|
|
325
323
|
|
|
326
324
|
Raises:
|
|
327
325
|
NotImplementedError: if the message type is not supported.
|
|
@@ -363,7 +361,7 @@ def _convert_to_message(message: MessageLikeRepresentation) -> BaseMessage:
|
|
|
363
361
|
|
|
364
362
|
|
|
365
363
|
def convert_to_messages(
|
|
366
|
-
messages:
|
|
364
|
+
messages: Iterable[MessageLikeRepresentation] | PromptValue,
|
|
367
365
|
) -> list[BaseMessage]:
|
|
368
366
|
"""Convert a sequence of messages to a list of messages.
|
|
369
367
|
|
|
@@ -394,12 +392,12 @@ def _runnable_support(func: Callable) -> Callable:
|
|
|
394
392
|
) -> list[BaseMessage]: ...
|
|
395
393
|
|
|
396
394
|
def wrapped(
|
|
397
|
-
messages:
|
|
395
|
+
messages: Sequence[MessageLikeRepresentation] | None = None,
|
|
398
396
|
**kwargs: Any,
|
|
399
|
-
) ->
|
|
400
|
-
list[BaseMessage]
|
|
401
|
-
Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]]
|
|
402
|
-
|
|
397
|
+
) -> (
|
|
398
|
+
list[BaseMessage]
|
|
399
|
+
| Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]]
|
|
400
|
+
):
|
|
403
401
|
# Import locally to prevent circular import.
|
|
404
402
|
from langchain_core.runnables.base import RunnableLambda # noqa: PLC0415
|
|
405
403
|
|
|
@@ -413,89 +411,88 @@ def _runnable_support(func: Callable) -> Callable:
|
|
|
413
411
|
|
|
414
412
|
@_runnable_support
|
|
415
413
|
def filter_messages(
|
|
416
|
-
messages:
|
|
414
|
+
messages: Iterable[MessageLikeRepresentation] | PromptValue,
|
|
417
415
|
*,
|
|
418
|
-
include_names:
|
|
419
|
-
exclude_names:
|
|
420
|
-
include_types:
|
|
421
|
-
exclude_types:
|
|
422
|
-
include_ids:
|
|
423
|
-
exclude_ids:
|
|
424
|
-
exclude_tool_calls:
|
|
416
|
+
include_names: Sequence[str] | None = None,
|
|
417
|
+
exclude_names: Sequence[str] | None = None,
|
|
418
|
+
include_types: Sequence[str | type[BaseMessage]] | None = None,
|
|
419
|
+
exclude_types: Sequence[str | type[BaseMessage]] | None = None,
|
|
420
|
+
include_ids: Sequence[str] | None = None,
|
|
421
|
+
exclude_ids: Sequence[str] | None = None,
|
|
422
|
+
exclude_tool_calls: Sequence[str] | bool | None = None,
|
|
425
423
|
) -> list[BaseMessage]:
|
|
426
|
-
"""Filter messages based on
|
|
424
|
+
"""Filter messages based on `name`, `type` or `id`.
|
|
427
425
|
|
|
428
426
|
Args:
|
|
429
427
|
messages: Sequence Message-like objects to filter.
|
|
430
|
-
include_names: Message names to include.
|
|
431
|
-
exclude_names: Messages names to exclude.
|
|
428
|
+
include_names: Message names to include.
|
|
429
|
+
exclude_names: Messages names to exclude.
|
|
432
430
|
include_types: Message types to include. Can be specified as string names
|
|
433
|
-
(e.g.
|
|
434
|
-
classes (e.g.
|
|
435
|
-
|
|
431
|
+
(e.g. `'system'`, `'human'`, `'ai'`, ...) or as `BaseMessage`
|
|
432
|
+
classes (e.g. `SystemMessage`, `HumanMessage`, `AIMessage`, ...).
|
|
433
|
+
|
|
436
434
|
exclude_types: Message types to exclude. Can be specified as string names
|
|
437
|
-
(e.g.
|
|
438
|
-
classes (e.g.
|
|
439
|
-
|
|
440
|
-
include_ids: Message IDs to include.
|
|
441
|
-
exclude_ids: Message IDs to exclude.
|
|
442
|
-
exclude_tool_calls: Tool call IDs to exclude.
|
|
435
|
+
(e.g. `'system'`, `'human'`, `'ai'`, ...) or as `BaseMessage`
|
|
436
|
+
classes (e.g. `SystemMessage`, `HumanMessage`, `AIMessage`, ...).
|
|
437
|
+
|
|
438
|
+
include_ids: Message IDs to include.
|
|
439
|
+
exclude_ids: Message IDs to exclude.
|
|
440
|
+
exclude_tool_calls: Tool call IDs to exclude.
|
|
443
441
|
Can be one of the following:
|
|
444
|
-
-
|
|
445
|
-
|
|
442
|
+
- `True`: all `AIMessage`s with tool calls and all
|
|
443
|
+
`ToolMessage` objects will be excluded.
|
|
446
444
|
- a sequence of tool call IDs to exclude:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
445
|
+
- `ToolMessage` objects with the corresponding tool call ID will be
|
|
446
|
+
excluded.
|
|
447
|
+
- The `tool_calls` in the AIMessage will be updated to exclude
|
|
448
|
+
matching tool calls. If all `tool_calls` are filtered from an
|
|
449
|
+
AIMessage, the whole message is excluded.
|
|
452
450
|
|
|
453
451
|
Returns:
|
|
454
|
-
A list of Messages that meets at least one of the
|
|
455
|
-
of the
|
|
452
|
+
A list of Messages that meets at least one of the `incl_*` conditions and none
|
|
453
|
+
of the `excl_*` conditions. If not `incl_*` conditions are specified then
|
|
456
454
|
anything that is not explicitly excluded will be included.
|
|
457
455
|
|
|
458
456
|
Raises:
|
|
459
|
-
ValueError
|
|
457
|
+
ValueError: If two incompatible arguments are provided.
|
|
460
458
|
|
|
461
459
|
Example:
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
messages = [
|
|
472
|
-
SystemMessage("you're a good assistant."),
|
|
473
|
-
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
474
|
-
AIMessage("steve-o", id="bar", name="example_assistant"),
|
|
475
|
-
HumanMessage(
|
|
476
|
-
"what's your favorite color",
|
|
477
|
-
id="baz",
|
|
478
|
-
),
|
|
479
|
-
AIMessage(
|
|
480
|
-
"silicon blue",
|
|
481
|
-
id="blah",
|
|
482
|
-
),
|
|
483
|
-
]
|
|
484
|
-
|
|
485
|
-
filter_messages(
|
|
486
|
-
messages,
|
|
487
|
-
incl_names=("example_user", "example_assistant"),
|
|
488
|
-
incl_types=("system",),
|
|
489
|
-
excl_ids=("bar",),
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
.. code-block:: python
|
|
493
|
-
|
|
494
|
-
[
|
|
495
|
-
SystemMessage("you're a good assistant."),
|
|
496
|
-
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
497
|
-
]
|
|
460
|
+
```python
|
|
461
|
+
from langchain_core.messages import (
|
|
462
|
+
filter_messages,
|
|
463
|
+
AIMessage,
|
|
464
|
+
HumanMessage,
|
|
465
|
+
SystemMessage,
|
|
466
|
+
)
|
|
498
467
|
|
|
468
|
+
messages = [
|
|
469
|
+
SystemMessage("you're a good assistant."),
|
|
470
|
+
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
471
|
+
AIMessage("steve-o", id="bar", name="example_assistant"),
|
|
472
|
+
HumanMessage(
|
|
473
|
+
"what's your favorite color",
|
|
474
|
+
id="baz",
|
|
475
|
+
),
|
|
476
|
+
AIMessage(
|
|
477
|
+
"silicon blue",
|
|
478
|
+
id="blah",
|
|
479
|
+
),
|
|
480
|
+
]
|
|
481
|
+
|
|
482
|
+
filter_messages(
|
|
483
|
+
messages,
|
|
484
|
+
incl_names=("example_user", "example_assistant"),
|
|
485
|
+
incl_types=("system",),
|
|
486
|
+
excl_ids=("bar",),
|
|
487
|
+
)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
[
|
|
492
|
+
SystemMessage("you're a good assistant."),
|
|
493
|
+
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
494
|
+
]
|
|
495
|
+
```
|
|
499
496
|
"""
|
|
500
497
|
messages = convert_to_messages(messages)
|
|
501
498
|
filtered: list[BaseMessage] = []
|
|
@@ -558,20 +555,19 @@ def filter_messages(
|
|
|
558
555
|
|
|
559
556
|
@_runnable_support
|
|
560
557
|
def merge_message_runs(
|
|
561
|
-
messages:
|
|
558
|
+
messages: Iterable[MessageLikeRepresentation] | PromptValue,
|
|
562
559
|
*,
|
|
563
560
|
chunk_separator: str = "\n",
|
|
564
561
|
) -> list[BaseMessage]:
|
|
565
562
|
r"""Merge consecutive Messages of the same type.
|
|
566
563
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
merged.
|
|
564
|
+
!!! note
|
|
565
|
+
`ToolMessage` objects are not merged, as each has a distinct tool call id that
|
|
566
|
+
can't be merged.
|
|
570
567
|
|
|
571
568
|
Args:
|
|
572
569
|
messages: Sequence Message-like objects to merge.
|
|
573
570
|
chunk_separator: Specify the string to be inserted between message chunks.
|
|
574
|
-
Default is ``'\n'``.
|
|
575
571
|
|
|
576
572
|
Returns:
|
|
577
573
|
list of BaseMessages with consecutive runs of message types merged into single
|
|
@@ -579,87 +575,86 @@ def merge_message_runs(
|
|
|
579
575
|
the merged content is a concatenation of the two strings with a new-line
|
|
580
576
|
separator.
|
|
581
577
|
The separator inserted between message chunks can be controlled by specifying
|
|
582
|
-
any string with
|
|
578
|
+
any string with `chunk_separator`. If at least one of the messages has a list
|
|
583
579
|
of content blocks, the merged content is a list of content blocks.
|
|
584
580
|
|
|
585
581
|
Example:
|
|
582
|
+
```python
|
|
583
|
+
from langchain_core.messages import (
|
|
584
|
+
merge_message_runs,
|
|
585
|
+
AIMessage,
|
|
586
|
+
HumanMessage,
|
|
587
|
+
SystemMessage,
|
|
588
|
+
ToolCall,
|
|
589
|
+
)
|
|
586
590
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
591
|
+
messages = [
|
|
592
|
+
SystemMessage("you're a good assistant."),
|
|
593
|
+
HumanMessage(
|
|
594
|
+
"what's your favorite color",
|
|
595
|
+
id="foo",
|
|
596
|
+
),
|
|
597
|
+
HumanMessage(
|
|
598
|
+
"wait your favorite food",
|
|
599
|
+
id="bar",
|
|
600
|
+
),
|
|
601
|
+
AIMessage(
|
|
602
|
+
"my favorite colo",
|
|
603
|
+
tool_calls=[
|
|
604
|
+
ToolCall(
|
|
605
|
+
name="blah_tool", args={"x": 2}, id="123", type="tool_call"
|
|
606
|
+
)
|
|
607
|
+
],
|
|
608
|
+
id="baz",
|
|
609
|
+
),
|
|
610
|
+
AIMessage(
|
|
611
|
+
[{"type": "text", "text": "my favorite dish is lasagna"}],
|
|
612
|
+
tool_calls=[
|
|
613
|
+
ToolCall(
|
|
614
|
+
name="blah_tool",
|
|
615
|
+
args={"x": -10},
|
|
616
|
+
id="456",
|
|
617
|
+
type="tool_call",
|
|
618
|
+
)
|
|
619
|
+
],
|
|
620
|
+
id="blur",
|
|
621
|
+
),
|
|
622
|
+
]
|
|
623
|
+
|
|
624
|
+
merge_message_runs(messages)
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
```python
|
|
628
|
+
[
|
|
629
|
+
SystemMessage("you're a good assistant."),
|
|
630
|
+
HumanMessage(
|
|
631
|
+
"what's your favorite color\\n"
|
|
632
|
+
"wait your favorite food", id="foo",
|
|
633
|
+
),
|
|
634
|
+
AIMessage(
|
|
635
|
+
[
|
|
608
636
|
"my favorite colo",
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
]
|
|
629
|
-
|
|
630
|
-
merge_message_runs(messages)
|
|
631
|
-
|
|
632
|
-
.. code-block:: python
|
|
633
|
-
|
|
634
|
-
[
|
|
635
|
-
SystemMessage("you're a good assistant."),
|
|
636
|
-
HumanMessage(
|
|
637
|
-
"what's your favorite color\\n"
|
|
638
|
-
"wait your favorite food", id="foo",
|
|
639
|
-
),
|
|
640
|
-
AIMessage(
|
|
641
|
-
[
|
|
642
|
-
"my favorite colo",
|
|
643
|
-
{"type": "text", "text": "my favorite dish is lasagna"}
|
|
644
|
-
],
|
|
645
|
-
tool_calls=[
|
|
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
|
-
})
|
|
658
|
-
]
|
|
659
|
-
id="baz"
|
|
660
|
-
),
|
|
661
|
-
]
|
|
637
|
+
{"type": "text", "text": "my favorite dish is lasagna"}
|
|
638
|
+
],
|
|
639
|
+
tool_calls=[
|
|
640
|
+
ToolCall({
|
|
641
|
+
"name": "blah_tool",
|
|
642
|
+
"args": {"x": 2},
|
|
643
|
+
"id": "123",
|
|
644
|
+
"type": "tool_call"
|
|
645
|
+
}),
|
|
646
|
+
ToolCall({
|
|
647
|
+
"name": "blah_tool",
|
|
648
|
+
"args": {"x": -10},
|
|
649
|
+
"id": "456",
|
|
650
|
+
"type": "tool_call"
|
|
651
|
+
})
|
|
652
|
+
]
|
|
653
|
+
id="baz"
|
|
654
|
+
),
|
|
655
|
+
]
|
|
662
656
|
|
|
657
|
+
```
|
|
663
658
|
"""
|
|
664
659
|
if not messages:
|
|
665
660
|
return []
|
|
@@ -691,174 +686,161 @@ def merge_message_runs(
|
|
|
691
686
|
# init not at runtime.
|
|
692
687
|
@_runnable_support
|
|
693
688
|
def trim_messages(
|
|
694
|
-
messages:
|
|
689
|
+
messages: Iterable[MessageLikeRepresentation] | PromptValue,
|
|
695
690
|
*,
|
|
696
691
|
max_tokens: int,
|
|
697
|
-
token_counter:
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
BaseLanguageModel,
|
|
701
|
-
],
|
|
692
|
+
token_counter: Callable[[list[BaseMessage]], int]
|
|
693
|
+
| Callable[[BaseMessage], int]
|
|
694
|
+
| BaseLanguageModel,
|
|
702
695
|
strategy: Literal["first", "last"] = "last",
|
|
703
696
|
allow_partial: bool = False,
|
|
704
|
-
end_on:
|
|
705
|
-
|
|
706
|
-
] = None,
|
|
707
|
-
start_on: Optional[
|
|
708
|
-
Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
|
|
709
|
-
] = None,
|
|
697
|
+
end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
|
|
698
|
+
start_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
|
|
710
699
|
include_system: bool = False,
|
|
711
|
-
text_splitter:
|
|
700
|
+
text_splitter: Callable[[str], list[str]] | TextSplitter | None = None,
|
|
712
701
|
) -> list[BaseMessage]:
|
|
713
702
|
r"""Trim messages to be below a token count.
|
|
714
703
|
|
|
715
|
-
|
|
716
|
-
token
|
|
704
|
+
`trim_messages` can be used to reduce the size of a chat history to a specified
|
|
705
|
+
token or message count.
|
|
717
706
|
|
|
718
707
|
In either case, if passing the trimmed chat history back into a chat model
|
|
719
708
|
directly, the resulting chat history should usually satisfy the following
|
|
720
709
|
properties:
|
|
721
710
|
|
|
722
711
|
1. The resulting chat history should be valid. Most chat models expect that chat
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
Please see the following link for more information about messages:
|
|
728
|
-
https://python.langchain.com/docs/concepts/#messages
|
|
712
|
+
history starts with either (1) a `HumanMessage` or (2) a `SystemMessage`
|
|
713
|
+
followed by a `HumanMessage`. To achieve this, set `start_on='human'`.
|
|
714
|
+
In addition, generally a `ToolMessage` can only appear after an `AIMessage`
|
|
715
|
+
that involved a tool call.
|
|
729
716
|
2. It includes recent messages and drops old messages in the chat history.
|
|
730
|
-
|
|
731
|
-
3. Usually, the new chat history should include the
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
The examples below show how to configure
|
|
717
|
+
To achieve this set the `strategy='last'`.
|
|
718
|
+
3. Usually, the new chat history should include the `SystemMessage` if it
|
|
719
|
+
was present in the original chat history since the `SystemMessage` includes
|
|
720
|
+
special instructions to the chat model. The `SystemMessage` is almost always
|
|
721
|
+
the first message in the history if present. To achieve this set the
|
|
722
|
+
`include_system=True`.
|
|
723
|
+
|
|
724
|
+
!!! note
|
|
725
|
+
The examples below show how to configure `trim_messages` to achieve a behavior
|
|
739
726
|
consistent with the above properties.
|
|
740
727
|
|
|
741
728
|
Args:
|
|
742
729
|
messages: Sequence of Message-like objects to trim.
|
|
743
730
|
max_tokens: Max token count of trimmed messages.
|
|
744
|
-
token_counter: Function or llm for counting tokens in a
|
|
745
|
-
list of
|
|
746
|
-
|
|
747
|
-
Set to
|
|
731
|
+
token_counter: Function or llm for counting tokens in a `BaseMessage` or a
|
|
732
|
+
list of `BaseMessage`. If a `BaseLanguageModel` is passed in then
|
|
733
|
+
`BaseLanguageModel.get_num_tokens_from_messages()` will be used.
|
|
734
|
+
Set to `len` to count the number of **messages** in the chat history.
|
|
748
735
|
|
|
749
|
-
|
|
750
|
-
Use
|
|
736
|
+
!!! note
|
|
737
|
+
Use `count_tokens_approximately` to get fast, approximate token
|
|
751
738
|
counts.
|
|
752
|
-
This is recommended for using
|
|
739
|
+
This is recommended for using `trim_messages` on the hot path, where
|
|
753
740
|
exact token counting is not necessary.
|
|
754
741
|
|
|
755
742
|
strategy: Strategy for trimming.
|
|
756
|
-
-
|
|
757
|
-
-
|
|
758
|
-
Default is ``'last'``.
|
|
743
|
+
- `'first'`: Keep the first `<= n_count` tokens of the messages.
|
|
744
|
+
- `'last'`: Keep the last `<= n_count` tokens of the messages.
|
|
759
745
|
allow_partial: Whether to split a message if only part of the message can be
|
|
760
|
-
included. If
|
|
761
|
-
are included. If
|
|
746
|
+
included. If `strategy='last'` then the last partial contents of a message
|
|
747
|
+
are included. If `strategy='first'` then the first partial contents of a
|
|
762
748
|
message are included.
|
|
763
|
-
Default is False.
|
|
764
749
|
end_on: The message type to end on. If specified then every message after the
|
|
765
|
-
last occurrence of this type is ignored. If
|
|
766
|
-
is done before we attempt to get the last
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
750
|
+
last occurrence of this type is ignored. If `strategy='last'` then this
|
|
751
|
+
is done before we attempt to get the last `max_tokens`. If
|
|
752
|
+
`strategy='first'` then this is done after we get the first
|
|
753
|
+
`max_tokens`. Can be specified as string names (e.g. `'system'`,
|
|
754
|
+
`'human'`, `'ai'`, ...) or as `BaseMessage` classes (e.g.
|
|
755
|
+
`SystemMessage`, `HumanMessage`, `AIMessage`, ...). Can be a single
|
|
771
756
|
type or a list of types.
|
|
772
|
-
|
|
757
|
+
|
|
773
758
|
start_on: The message type to start on. Should only be specified if
|
|
774
|
-
|
|
759
|
+
`strategy='last'`. If specified then every message before
|
|
775
760
|
the first occurrence of this type is ignored. This is done after we trim
|
|
776
|
-
the initial messages to the last
|
|
777
|
-
apply to a
|
|
778
|
-
specified as string names (e.g.
|
|
779
|
-
as
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
include_system: Whether to keep the SystemMessage if there is one at index
|
|
783
|
-
Should only be specified if
|
|
784
|
-
|
|
785
|
-
text_splitter: Function or ``langchain_text_splitters.TextSplitter`` for
|
|
761
|
+
the initial messages to the last `max_tokens`. Does not
|
|
762
|
+
apply to a `SystemMessage` at index 0 if `include_system=True`. Can be
|
|
763
|
+
specified as string names (e.g. `'system'`, `'human'`, `'ai'`, ...) or
|
|
764
|
+
as `BaseMessage` classes (e.g. `SystemMessage`, `HumanMessage`,
|
|
765
|
+
`AIMessage`, ...). Can be a single type or a list of types.
|
|
766
|
+
|
|
767
|
+
include_system: Whether to keep the `SystemMessage` if there is one at index
|
|
768
|
+
`0`. Should only be specified if `strategy="last"`.
|
|
769
|
+
text_splitter: Function or `langchain_text_splitters.TextSplitter` for
|
|
786
770
|
splitting the string contents of a message. Only used if
|
|
787
|
-
|
|
788
|
-
from a partial message will be included. if
|
|
771
|
+
`allow_partial=True`. If `strategy='last'` then the last split tokens
|
|
772
|
+
from a partial message will be included. if `strategy='first'` then the
|
|
789
773
|
first split tokens from a partial message will be included. Token splitter
|
|
790
774
|
assumes that separators are kept, so that split contents can be directly
|
|
791
775
|
concatenated to recreate the original text. Defaults to splitting on
|
|
792
776
|
newlines.
|
|
793
777
|
|
|
794
778
|
Returns:
|
|
795
|
-
|
|
779
|
+
List of trimmed `BaseMessage`.
|
|
796
780
|
|
|
797
781
|
Raises:
|
|
798
782
|
ValueError: if two incompatible arguments are specified or an unrecognized
|
|
799
|
-
|
|
783
|
+
`strategy` is specified.
|
|
800
784
|
|
|
801
785
|
Example:
|
|
802
|
-
Trim chat history based on token count, keeping the
|
|
803
|
-
present, and ensuring that the chat history starts with a
|
|
804
|
-
or a
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
)
|
|
815
|
-
|
|
816
|
-
messages = [
|
|
817
|
-
SystemMessage(
|
|
818
|
-
"you're a good assistant, you always respond with a joke."
|
|
819
|
-
),
|
|
820
|
-
HumanMessage("i wonder why it's called langchain"),
|
|
821
|
-
AIMessage(
|
|
822
|
-
'Well, I guess they thought "WordRope" and "SentenceString" just '
|
|
823
|
-
"didn't have the same ring to it!"
|
|
824
|
-
),
|
|
825
|
-
HumanMessage("and who is harrison chasing anyways"),
|
|
826
|
-
AIMessage(
|
|
827
|
-
"Hmmm let me think.\n\nWhy, he's probably chasing after the last "
|
|
828
|
-
"cup of coffee in the office!"
|
|
829
|
-
),
|
|
830
|
-
HumanMessage("what do you call a speechless parrot"),
|
|
831
|
-
]
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
trim_messages(
|
|
835
|
-
messages,
|
|
836
|
-
max_tokens=45,
|
|
837
|
-
strategy="last",
|
|
838
|
-
token_counter=ChatOpenAI(model="gpt-4o"),
|
|
839
|
-
# Most chat models expect that chat history starts with either:
|
|
840
|
-
# (1) a HumanMessage or
|
|
841
|
-
# (2) a SystemMessage followed by a HumanMessage
|
|
842
|
-
start_on="human",
|
|
843
|
-
# Usually, we want to keep the SystemMessage
|
|
844
|
-
# if it's present in the original history.
|
|
845
|
-
# The SystemMessage has special instructions for the model.
|
|
846
|
-
include_system=True,
|
|
847
|
-
allow_partial=False,
|
|
848
|
-
)
|
|
786
|
+
Trim chat history based on token count, keeping the `SystemMessage` if
|
|
787
|
+
present, and ensuring that the chat history starts with a `HumanMessage` (
|
|
788
|
+
or a `SystemMessage` followed by a `HumanMessage`).
|
|
789
|
+
|
|
790
|
+
```python
|
|
791
|
+
from langchain_core.messages import (
|
|
792
|
+
AIMessage,
|
|
793
|
+
HumanMessage,
|
|
794
|
+
BaseMessage,
|
|
795
|
+
SystemMessage,
|
|
796
|
+
trim_messages,
|
|
797
|
+
)
|
|
849
798
|
|
|
850
|
-
|
|
799
|
+
messages = [
|
|
800
|
+
SystemMessage("you're a good assistant, you always respond with a joke."),
|
|
801
|
+
HumanMessage("i wonder why it's called langchain"),
|
|
802
|
+
AIMessage(
|
|
803
|
+
'Well, I guess they thought "WordRope" and "SentenceString" just '
|
|
804
|
+
"didn't have the same ring to it!"
|
|
805
|
+
),
|
|
806
|
+
HumanMessage("and who is harrison chasing anyways"),
|
|
807
|
+
AIMessage(
|
|
808
|
+
"Hmmm let me think.\n\nWhy, he's probably chasing after the last "
|
|
809
|
+
"cup of coffee in the office!"
|
|
810
|
+
),
|
|
811
|
+
HumanMessage("what do you call a speechless parrot"),
|
|
812
|
+
]
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
trim_messages(
|
|
816
|
+
messages,
|
|
817
|
+
max_tokens=45,
|
|
818
|
+
strategy="last",
|
|
819
|
+
token_counter=ChatOpenAI(model="gpt-4o"),
|
|
820
|
+
# Most chat models expect that chat history starts with either:
|
|
821
|
+
# (1) a HumanMessage or
|
|
822
|
+
# (2) a SystemMessage followed by a HumanMessage
|
|
823
|
+
start_on="human",
|
|
824
|
+
# Usually, we want to keep the SystemMessage
|
|
825
|
+
# if it's present in the original history.
|
|
826
|
+
# The SystemMessage has special instructions for the model.
|
|
827
|
+
include_system=True,
|
|
828
|
+
allow_partial=False,
|
|
829
|
+
)
|
|
830
|
+
```
|
|
851
831
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
832
|
+
```python
|
|
833
|
+
[
|
|
834
|
+
SystemMessage(
|
|
835
|
+
content="you're a good assistant, you always respond with a joke."
|
|
836
|
+
),
|
|
837
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
838
|
+
]
|
|
839
|
+
```
|
|
858
840
|
|
|
859
|
-
Trim chat history based on the message count, keeping the
|
|
860
|
-
present, and ensuring that the chat history starts with a
|
|
861
|
-
or a
|
|
841
|
+
Trim chat history based on the message count, keeping the `SystemMessage` if
|
|
842
|
+
present, and ensuring that the chat history starts with a `HumanMessage` (
|
|
843
|
+
or a `SystemMessage` followed by a `HumanMessage`).
|
|
862
844
|
|
|
863
845
|
trim_messages(
|
|
864
846
|
messages,
|
|
@@ -880,100 +862,95 @@ def trim_messages(
|
|
|
880
862
|
allow_partial=False,
|
|
881
863
|
)
|
|
882
864
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
865
|
+
```python
|
|
866
|
+
[
|
|
867
|
+
SystemMessage(
|
|
868
|
+
content="you're a good assistant, you always respond with a joke."
|
|
869
|
+
),
|
|
870
|
+
HumanMessage(content="and who is harrison chasing anyways"),
|
|
871
|
+
AIMessage(
|
|
872
|
+
content="Hmmm let me think.\n\nWhy, he's probably chasing after "
|
|
873
|
+
"the last cup of coffee in the office!"
|
|
874
|
+
),
|
|
875
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
876
|
+
]
|
|
877
|
+
```
|
|
898
878
|
Trim chat history using a custom token counter function that counts the
|
|
899
879
|
number of tokens in each message.
|
|
900
880
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
AIMessage(
|
|
909
|
-
[
|
|
910
|
-
{"type": "text", "text": "This is the FIRST 4 token block."},
|
|
911
|
-
{"type": "text", "text": "This is the SECOND 4 token block."},
|
|
912
|
-
],
|
|
913
|
-
id="second",
|
|
914
|
-
),
|
|
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
|
-
),
|
|
922
|
-
]
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
def dummy_token_counter(messages: list[BaseMessage]) -> int:
|
|
926
|
-
# treat each message like it adds 3 default tokens at the beginning
|
|
927
|
-
# of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
|
|
928
|
-
# per message.
|
|
929
|
-
|
|
930
|
-
default_content_len = 4
|
|
931
|
-
default_msg_prefix_len = 3
|
|
932
|
-
default_msg_suffix_len = 3
|
|
933
|
-
|
|
934
|
-
count = 0
|
|
935
|
-
for msg in messages:
|
|
936
|
-
if isinstance(msg.content, str):
|
|
937
|
-
count += (
|
|
938
|
-
default_msg_prefix_len
|
|
939
|
-
+ default_content_len
|
|
940
|
-
+ default_msg_suffix_len
|
|
941
|
-
)
|
|
942
|
-
if isinstance(msg.content, list):
|
|
943
|
-
count += (
|
|
944
|
-
default_msg_prefix_len
|
|
945
|
-
+ len(msg.content) * default_content_len
|
|
946
|
-
+ default_msg_suffix_len
|
|
947
|
-
)
|
|
948
|
-
return count
|
|
949
|
-
|
|
950
|
-
First 30 tokens, allowing partial messages:
|
|
951
|
-
.. code-block:: python
|
|
952
|
-
|
|
953
|
-
trim_messages(
|
|
954
|
-
messages,
|
|
955
|
-
max_tokens=30,
|
|
956
|
-
token_counter=dummy_token_counter,
|
|
957
|
-
strategy="first",
|
|
958
|
-
allow_partial=True,
|
|
959
|
-
)
|
|
960
|
-
|
|
961
|
-
.. code-block:: python
|
|
962
|
-
|
|
881
|
+
```python
|
|
882
|
+
messages = [
|
|
883
|
+
SystemMessage("This is a 4 token text. The full message is 10 tokens."),
|
|
884
|
+
HumanMessage(
|
|
885
|
+
"This is a 4 token text. The full message is 10 tokens.", id="first"
|
|
886
|
+
),
|
|
887
|
+
AIMessage(
|
|
963
888
|
[
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
889
|
+
{"type": "text", "text": "This is the FIRST 4 token block."},
|
|
890
|
+
{"type": "text", "text": "This is the SECOND 4 token block."},
|
|
891
|
+
],
|
|
892
|
+
id="second",
|
|
893
|
+
),
|
|
894
|
+
HumanMessage(
|
|
895
|
+
"This is a 4 token text. The full message is 10 tokens.", id="third"
|
|
896
|
+
),
|
|
897
|
+
AIMessage(
|
|
898
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
899
|
+
id="fourth",
|
|
900
|
+
),
|
|
901
|
+
]
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
def dummy_token_counter(messages: list[BaseMessage]) -> int:
|
|
905
|
+
# treat each message like it adds 3 default tokens at the beginning
|
|
906
|
+
# of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
|
|
907
|
+
# per message.
|
|
908
|
+
|
|
909
|
+
default_content_len = 4
|
|
910
|
+
default_msg_prefix_len = 3
|
|
911
|
+
default_msg_suffix_len = 3
|
|
912
|
+
|
|
913
|
+
count = 0
|
|
914
|
+
for msg in messages:
|
|
915
|
+
if isinstance(msg.content, str):
|
|
916
|
+
count += (
|
|
917
|
+
default_msg_prefix_len
|
|
918
|
+
+ default_content_len
|
|
919
|
+
+ default_msg_suffix_len
|
|
920
|
+
)
|
|
921
|
+
if isinstance(msg.content, list):
|
|
922
|
+
count += (
|
|
923
|
+
default_msg_prefix_len
|
|
924
|
+
+ len(msg.content) * default_content_len
|
|
925
|
+
+ default_msg_suffix_len
|
|
926
|
+
)
|
|
927
|
+
return count
|
|
928
|
+
```
|
|
976
929
|
|
|
930
|
+
First 30 tokens, allowing partial messages:
|
|
931
|
+
```python
|
|
932
|
+
trim_messages(
|
|
933
|
+
messages,
|
|
934
|
+
max_tokens=30,
|
|
935
|
+
token_counter=dummy_token_counter,
|
|
936
|
+
strategy="first",
|
|
937
|
+
allow_partial=True,
|
|
938
|
+
)
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
```python
|
|
942
|
+
[
|
|
943
|
+
SystemMessage("This is a 4 token text. The full message is 10 tokens."),
|
|
944
|
+
HumanMessage(
|
|
945
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
946
|
+
id="first",
|
|
947
|
+
),
|
|
948
|
+
AIMessage(
|
|
949
|
+
[{"type": "text", "text": "This is the FIRST 4 token block."}],
|
|
950
|
+
id="second",
|
|
951
|
+
),
|
|
952
|
+
]
|
|
953
|
+
```
|
|
977
954
|
"""
|
|
978
955
|
# Validate arguments
|
|
979
956
|
if start_on and strategy == "first":
|
|
@@ -1037,91 +1014,90 @@ def trim_messages(
|
|
|
1037
1014
|
|
|
1038
1015
|
|
|
1039
1016
|
def convert_to_openai_messages(
|
|
1040
|
-
messages:
|
|
1017
|
+
messages: MessageLikeRepresentation | Sequence[MessageLikeRepresentation],
|
|
1041
1018
|
*,
|
|
1042
1019
|
text_format: Literal["string", "block"] = "string",
|
|
1043
1020
|
include_id: bool = False,
|
|
1044
|
-
) ->
|
|
1021
|
+
) -> dict | list[dict]:
|
|
1045
1022
|
"""Convert LangChain messages into OpenAI message dicts.
|
|
1046
1023
|
|
|
1047
1024
|
Args:
|
|
1048
1025
|
messages: Message-like object or iterable of objects whose contents are
|
|
1049
1026
|
in OpenAI, Anthropic, Bedrock Converse, or VertexAI formats.
|
|
1050
1027
|
text_format: How to format string or text block contents:
|
|
1051
|
-
-
|
|
1028
|
+
- `'string'`:
|
|
1052
1029
|
If a message has a string content, this is left as a string. If
|
|
1053
|
-
a message has content blocks that are all of type
|
|
1030
|
+
a message has content blocks that are all of type `'text'`, these
|
|
1054
1031
|
are joined with a newline to make a single string. If a message has
|
|
1055
|
-
content blocks and at least one isn't of type
|
|
1032
|
+
content blocks and at least one isn't of type `'text'`, then
|
|
1056
1033
|
all blocks are left as dicts.
|
|
1057
|
-
-
|
|
1034
|
+
- `'block'`:
|
|
1058
1035
|
If a message has a string content, this is turned into a list
|
|
1059
|
-
with a single content block of type
|
|
1036
|
+
with a single content block of type `'text'`. If a message has
|
|
1060
1037
|
content blocks these are left as is.
|
|
1061
1038
|
include_id: Whether to include message ids in the openai messages, if they
|
|
1062
1039
|
are present in the source messages.
|
|
1063
1040
|
|
|
1064
1041
|
Raises:
|
|
1065
|
-
ValueError: if an unrecognized
|
|
1042
|
+
ValueError: if an unrecognized `text_format` is specified, or if a message
|
|
1066
1043
|
content block is missing expected keys.
|
|
1067
1044
|
|
|
1068
1045
|
Returns:
|
|
1069
1046
|
The return type depends on the input type:
|
|
1070
1047
|
|
|
1071
1048
|
- dict:
|
|
1072
|
-
|
|
1073
|
-
|
|
1049
|
+
If a single message-like object is passed in, a single OpenAI message
|
|
1050
|
+
dict is returned.
|
|
1074
1051
|
- list[dict]:
|
|
1075
|
-
|
|
1076
|
-
|
|
1052
|
+
If a sequence of message-like objects are passed in, a list of OpenAI
|
|
1053
|
+
message dicts is returned.
|
|
1077
1054
|
|
|
1078
1055
|
Example:
|
|
1056
|
+
```python
|
|
1057
|
+
from langchain_core.messages import (
|
|
1058
|
+
convert_to_openai_messages,
|
|
1059
|
+
AIMessage,
|
|
1060
|
+
SystemMessage,
|
|
1061
|
+
ToolMessage,
|
|
1062
|
+
)
|
|
1079
1063
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
# {'role': 'system', 'content': 'foo'},
|
|
1118
|
-
# {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]},
|
|
1119
|
-
# {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''},
|
|
1120
|
-
# {'role': 'tool', 'name': 'bar', 'content': 'foobar'},
|
|
1121
|
-
# {'role': 'assistant', 'content': 'thats nice'}
|
|
1122
|
-
# ]
|
|
1123
|
-
|
|
1124
|
-
.. versionadded:: 0.3.11
|
|
1064
|
+
messages = [
|
|
1065
|
+
SystemMessage([{"type": "text", "text": "foo"}]),
|
|
1066
|
+
{
|
|
1067
|
+
"role": "user",
|
|
1068
|
+
"content": [
|
|
1069
|
+
{"type": "text", "text": "whats in this"},
|
|
1070
|
+
{
|
|
1071
|
+
"type": "image_url",
|
|
1072
|
+
"image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"},
|
|
1073
|
+
},
|
|
1074
|
+
],
|
|
1075
|
+
},
|
|
1076
|
+
AIMessage(
|
|
1077
|
+
"",
|
|
1078
|
+
tool_calls=[
|
|
1079
|
+
{
|
|
1080
|
+
"name": "analyze",
|
|
1081
|
+
"args": {"baz": "buz"},
|
|
1082
|
+
"id": "1",
|
|
1083
|
+
"type": "tool_call",
|
|
1084
|
+
}
|
|
1085
|
+
],
|
|
1086
|
+
),
|
|
1087
|
+
ToolMessage("foobar", tool_call_id="1", name="bar"),
|
|
1088
|
+
{"role": "assistant", "content": "thats nice"},
|
|
1089
|
+
]
|
|
1090
|
+
oai_messages = convert_to_openai_messages(messages)
|
|
1091
|
+
# -> [
|
|
1092
|
+
# {'role': 'system', 'content': 'foo'},
|
|
1093
|
+
# {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]},
|
|
1094
|
+
# {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''},
|
|
1095
|
+
# {'role': 'tool', 'name': 'bar', 'content': 'foobar'},
|
|
1096
|
+
# {'role': 'assistant', 'content': 'thats nice'}
|
|
1097
|
+
# ]
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
!!! version-added "Added in version 0.3.11"
|
|
1125
1101
|
|
|
1126
1102
|
""" # noqa: E501
|
|
1127
1103
|
if text_format not in {"string", "block"}:
|
|
@@ -1138,7 +1114,7 @@ def convert_to_openai_messages(
|
|
|
1138
1114
|
for i, message in enumerate(messages):
|
|
1139
1115
|
oai_msg: dict = {"role": _get_message_openai_role(message)}
|
|
1140
1116
|
tool_messages: list = []
|
|
1141
|
-
content:
|
|
1117
|
+
content: str | list[dict]
|
|
1142
1118
|
|
|
1143
1119
|
if message.name:
|
|
1144
1120
|
oai_msg["name"] = message.name
|
|
@@ -1421,10 +1397,8 @@ def _first_max_tokens(
|
|
|
1421
1397
|
max_tokens: int,
|
|
1422
1398
|
token_counter: Callable[[list[BaseMessage]], int],
|
|
1423
1399
|
text_splitter: Callable[[str], list[str]],
|
|
1424
|
-
partial_strategy:
|
|
1425
|
-
end_on:
|
|
1426
|
-
Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
|
|
1427
|
-
] = None,
|
|
1400
|
+
partial_strategy: Literal["first", "last"] | None = None,
|
|
1401
|
+
end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
|
|
1428
1402
|
) -> list[BaseMessage]:
|
|
1429
1403
|
messages = list(messages)
|
|
1430
1404
|
if not messages:
|
|
@@ -1541,12 +1515,8 @@ def _last_max_tokens(
|
|
|
1541
1515
|
text_splitter: Callable[[str], list[str]],
|
|
1542
1516
|
allow_partial: bool = False,
|
|
1543
1517
|
include_system: bool = False,
|
|
1544
|
-
start_on:
|
|
1545
|
-
|
|
1546
|
-
] = None,
|
|
1547
|
-
end_on: Optional[
|
|
1548
|
-
Union[str, type[BaseMessage], Sequence[Union[str, type[BaseMessage]]]]
|
|
1549
|
-
] = None,
|
|
1518
|
+
start_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
|
|
1519
|
+
end_on: str | type[BaseMessage] | Sequence[str | type[BaseMessage]] | None = None,
|
|
1550
1520
|
) -> list[BaseMessage]:
|
|
1551
1521
|
messages = list(messages)
|
|
1552
1522
|
if len(messages) == 0:
|
|
@@ -1622,11 +1592,15 @@ def _msg_to_chunk(message: BaseMessage) -> BaseMessageChunk:
|
|
|
1622
1592
|
def _chunk_to_msg(chunk: BaseMessageChunk) -> BaseMessage:
|
|
1623
1593
|
if chunk.__class__ in _CHUNK_MSG_MAP:
|
|
1624
1594
|
return _CHUNK_MSG_MAP[chunk.__class__](
|
|
1625
|
-
**chunk.model_dump(exclude={"type", "tool_call_chunks"})
|
|
1595
|
+
**chunk.model_dump(exclude={"type", "tool_call_chunks", "chunk_position"})
|
|
1626
1596
|
)
|
|
1627
1597
|
for chunk_cls, msg_cls in _CHUNK_MSG_MAP.items():
|
|
1628
1598
|
if isinstance(chunk, chunk_cls):
|
|
1629
|
-
return msg_cls(
|
|
1599
|
+
return msg_cls(
|
|
1600
|
+
**chunk.model_dump(
|
|
1601
|
+
exclude={"type", "tool_call_chunks", "chunk_position"}
|
|
1602
|
+
)
|
|
1603
|
+
)
|
|
1630
1604
|
|
|
1631
1605
|
msg = (
|
|
1632
1606
|
f"Unrecognized message chunk class {chunk.__class__}. Supported classes are "
|
|
@@ -1643,7 +1617,7 @@ def _default_text_splitter(text: str) -> list[str]:
|
|
|
1643
1617
|
|
|
1644
1618
|
def _is_message_type(
|
|
1645
1619
|
message: BaseMessage,
|
|
1646
|
-
type_:
|
|
1620
|
+
type_: str | type[BaseMessage] | Sequence[str | type[BaseMessage]],
|
|
1647
1621
|
) -> bool:
|
|
1648
1622
|
types = [type_] if isinstance(type_, (str, type)) else type_
|
|
1649
1623
|
types_str = [t for t in types if isinstance(t, str)]
|
|
@@ -1703,27 +1677,27 @@ def count_tokens_approximately(
|
|
|
1703
1677
|
Args:
|
|
1704
1678
|
messages: List of messages to count tokens for.
|
|
1705
1679
|
chars_per_token: Number of characters per token to use for the approximation.
|
|
1706
|
-
|
|
1707
|
-
You can also specify float values for more fine-grained control.
|
|
1708
|
-
|
|
1709
|
-
extra_tokens_per_message: Number of extra tokens to add per message.
|
|
1710
|
-
|
|
1711
|
-
You can also specify float values for more fine-grained control.
|
|
1712
|
-
|
|
1680
|
+
One token corresponds to ~4 chars for common English text.
|
|
1681
|
+
You can also specify `float` values for more fine-grained control.
|
|
1682
|
+
[See more here](https://platform.openai.com/tokenizer).
|
|
1683
|
+
extra_tokens_per_message: Number of extra tokens to add per message, e.g.
|
|
1684
|
+
special tokens, including beginning/end of message.
|
|
1685
|
+
You can also specify `float` values for more fine-grained control.
|
|
1686
|
+
[See more here](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb).
|
|
1713
1687
|
count_name: Whether to include message names in the count.
|
|
1714
1688
|
Enabled by default.
|
|
1715
1689
|
|
|
1716
1690
|
Returns:
|
|
1717
1691
|
Approximate number of tokens in the messages.
|
|
1718
1692
|
|
|
1719
|
-
|
|
1693
|
+
!!! note
|
|
1720
1694
|
This is a simple approximation that may not match the exact token count used by
|
|
1721
1695
|
specific models. For accurate counts, use model-specific tokenizers.
|
|
1722
1696
|
|
|
1723
1697
|
Warning:
|
|
1724
1698
|
This function does not currently support counting image tokens.
|
|
1725
1699
|
|
|
1726
|
-
|
|
1700
|
+
!!! version-added "Added in version 0.3.46"
|
|
1727
1701
|
|
|
1728
1702
|
"""
|
|
1729
1703
|
token_count = 0.0
|