langchain-core 1.0.0a1__py3-none-any.whl → 1.0.0a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of langchain-core might be problematic. Click here for more details.
- langchain_core/_api/beta_decorator.py +17 -40
- langchain_core/_api/deprecation.py +20 -7
- langchain_core/_api/path.py +19 -2
- langchain_core/_import_utils.py +7 -0
- langchain_core/agents.py +10 -6
- langchain_core/callbacks/base.py +28 -15
- langchain_core/callbacks/manager.py +81 -69
- langchain_core/callbacks/usage.py +4 -2
- langchain_core/chat_history.py +29 -21
- langchain_core/document_loaders/base.py +34 -9
- langchain_core/document_loaders/langsmith.py +3 -0
- langchain_core/documents/base.py +35 -10
- langchain_core/documents/transformers.py +4 -2
- langchain_core/embeddings/fake.py +8 -5
- langchain_core/env.py +2 -3
- langchain_core/example_selectors/base.py +12 -0
- langchain_core/exceptions.py +7 -0
- langchain_core/globals.py +17 -28
- langchain_core/indexing/api.py +57 -45
- langchain_core/indexing/base.py +5 -8
- langchain_core/indexing/in_memory.py +23 -3
- langchain_core/language_models/__init__.py +6 -2
- langchain_core/language_models/_utils.py +28 -4
- langchain_core/language_models/base.py +33 -21
- langchain_core/language_models/chat_models.py +103 -29
- langchain_core/language_models/fake_chat_models.py +5 -7
- langchain_core/language_models/llms.py +54 -20
- langchain_core/load/dump.py +2 -3
- langchain_core/load/load.py +15 -1
- langchain_core/load/serializable.py +38 -43
- langchain_core/memory.py +7 -3
- langchain_core/messages/__init__.py +7 -17
- langchain_core/messages/ai.py +41 -34
- langchain_core/messages/base.py +16 -7
- langchain_core/messages/block_translators/__init__.py +10 -8
- langchain_core/messages/block_translators/anthropic.py +3 -1
- langchain_core/messages/block_translators/bedrock.py +3 -1
- langchain_core/messages/block_translators/bedrock_converse.py +3 -1
- langchain_core/messages/block_translators/google_genai.py +3 -1
- langchain_core/messages/block_translators/google_vertexai.py +3 -1
- langchain_core/messages/block_translators/groq.py +3 -1
- langchain_core/messages/block_translators/langchain_v0.py +3 -136
- langchain_core/messages/block_translators/ollama.py +3 -1
- langchain_core/messages/block_translators/openai.py +252 -10
- langchain_core/messages/content.py +26 -124
- langchain_core/messages/human.py +2 -13
- langchain_core/messages/system.py +2 -6
- langchain_core/messages/tool.py +34 -14
- langchain_core/messages/utils.py +189 -74
- langchain_core/output_parsers/base.py +5 -2
- langchain_core/output_parsers/json.py +4 -4
- langchain_core/output_parsers/list.py +7 -22
- langchain_core/output_parsers/openai_functions.py +3 -0
- langchain_core/output_parsers/openai_tools.py +6 -1
- langchain_core/output_parsers/pydantic.py +4 -0
- langchain_core/output_parsers/string.py +5 -1
- langchain_core/output_parsers/xml.py +19 -19
- langchain_core/outputs/chat_generation.py +18 -7
- langchain_core/outputs/generation.py +14 -3
- langchain_core/outputs/llm_result.py +8 -1
- langchain_core/prompt_values.py +10 -4
- langchain_core/prompts/base.py +6 -11
- langchain_core/prompts/chat.py +88 -60
- langchain_core/prompts/dict.py +16 -8
- langchain_core/prompts/few_shot.py +9 -11
- langchain_core/prompts/few_shot_with_templates.py +5 -1
- langchain_core/prompts/image.py +12 -5
- langchain_core/prompts/loading.py +2 -2
- 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 +387 -246
- langchain_core/runnables/branch.py +11 -28
- langchain_core/runnables/config.py +20 -17
- 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 +40 -17
- 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 +9 -0
- langchain_core/runnables/utils.py +53 -90
- langchain_core/stores.py +19 -31
- langchain_core/sys_info.py +9 -8
- langchain_core/tools/base.py +36 -27
- langchain_core/tools/convert.py +25 -14
- 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 +110 -46
- langchain_core/tracers/evaluation.py +22 -26
- langchain_core/tracers/event_stream.py +97 -42
- langchain_core/tracers/langchain.py +12 -3
- langchain_core/tracers/langchain_v1.py +10 -2
- langchain_core/tracers/log_stream.py +56 -17
- langchain_core/tracers/root_listeners.py +4 -20
- langchain_core/tracers/run_collector.py +6 -16
- langchain_core/tracers/schemas.py +5 -1
- langchain_core/utils/aiter.py +14 -6
- langchain_core/utils/env.py +3 -0
- langchain_core/utils/function_calling.py +46 -20
- langchain_core/utils/interactive_env.py +6 -2
- langchain_core/utils/iter.py +12 -5
- 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 +25 -16
- 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 +20 -14
- langchain_core/vectorstores/utils.py +18 -12
- langchain_core/version.py +1 -1
- langchain_core-1.0.0a3.dist-info/METADATA +77 -0
- langchain_core-1.0.0a3.dist-info/RECORD +181 -0
- langchain_core/beta/__init__.py +0 -1
- langchain_core/beta/runnables/__init__.py +0 -1
- langchain_core/beta/runnables/context.py +0 -448
- langchain_core-1.0.0a1.dist-info/METADATA +0 -106
- langchain_core-1.0.0a1.dist-info/RECORD +0 -184
- {langchain_core-1.0.0a1.dist-info → langchain_core-1.0.0a3.dist-info}/WHEEL +0 -0
- {langchain_core-1.0.0a1.dist-info → langchain_core-1.0.0a3.dist-info}/entry_points.txt +0 -0
langchain_core/messages/utils.py
CHANGED
|
@@ -33,9 +33,11 @@ from pydantic import Discriminator, Field, Tag
|
|
|
33
33
|
from langchain_core.exceptions import ErrorCode, create_message
|
|
34
34
|
from langchain_core.messages.ai import AIMessage, AIMessageChunk
|
|
35
35
|
from langchain_core.messages.base import BaseMessage, BaseMessageChunk
|
|
36
|
+
from langchain_core.messages.block_translators.openai import (
|
|
37
|
+
convert_to_openai_data_block,
|
|
38
|
+
)
|
|
36
39
|
from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
|
|
37
40
|
from langchain_core.messages.content import (
|
|
38
|
-
convert_to_openai_data_block,
|
|
39
41
|
is_data_content_block,
|
|
40
42
|
)
|
|
41
43
|
from langchain_core.messages.function import FunctionMessage, FunctionMessageChunk
|
|
@@ -45,12 +47,17 @@ from langchain_core.messages.system import SystemMessage, SystemMessageChunk
|
|
|
45
47
|
from langchain_core.messages.tool import ToolCall, ToolMessage, ToolMessageChunk
|
|
46
48
|
|
|
47
49
|
if TYPE_CHECKING:
|
|
48
|
-
from langchain_text_splitters import TextSplitter
|
|
49
|
-
|
|
50
50
|
from langchain_core.language_models import BaseLanguageModel
|
|
51
51
|
from langchain_core.prompt_values import PromptValue
|
|
52
52
|
from langchain_core.runnables.base import Runnable
|
|
53
53
|
|
|
54
|
+
try:
|
|
55
|
+
from langchain_text_splitters import TextSplitter
|
|
56
|
+
|
|
57
|
+
_HAS_LANGCHAIN_TEXT_SPLITTERS = True
|
|
58
|
+
except ImportError:
|
|
59
|
+
_HAS_LANGCHAIN_TEXT_SPLITTERS = False
|
|
60
|
+
|
|
54
61
|
logger = logging.getLogger(__name__)
|
|
55
62
|
|
|
56
63
|
|
|
@@ -95,7 +102,7 @@ def get_buffer_string(
|
|
|
95
102
|
messages: Messages to be converted to strings.
|
|
96
103
|
human_prefix: The prefix to prepend to contents of HumanMessages.
|
|
97
104
|
Default is "Human".
|
|
98
|
-
ai_prefix:
|
|
105
|
+
ai_prefix: The prefix to prepend to contents of AIMessages. Default is "AI".
|
|
99
106
|
|
|
100
107
|
Returns:
|
|
101
108
|
A single string concatenation of all input messages.
|
|
@@ -185,7 +192,7 @@ def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
|
|
|
185
192
|
return [_message_from_dict(m) for m in messages]
|
|
186
193
|
|
|
187
194
|
|
|
188
|
-
def message_chunk_to_message(chunk:
|
|
195
|
+
def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
|
|
189
196
|
"""Convert a message chunk to a message.
|
|
190
197
|
|
|
191
198
|
Args:
|
|
@@ -284,6 +291,9 @@ def _create_message_from_message_type(
|
|
|
284
291
|
message = FunctionMessage(content=content, **kwargs)
|
|
285
292
|
elif message_type == "tool":
|
|
286
293
|
artifact = kwargs.get("additional_kwargs", {}).pop("artifact", None)
|
|
294
|
+
status = kwargs.get("additional_kwargs", {}).pop("status", None)
|
|
295
|
+
if status is not None:
|
|
296
|
+
kwargs["status"] = status
|
|
287
297
|
message = ToolMessage(content=content, artifact=artifact, **kwargs)
|
|
288
298
|
elif message_type == "remove":
|
|
289
299
|
message = RemoveMessage(**kwargs)
|
|
@@ -364,7 +374,7 @@ def convert_to_messages(
|
|
|
364
374
|
list of messages (BaseMessages).
|
|
365
375
|
"""
|
|
366
376
|
# Import here to avoid circular imports
|
|
367
|
-
from langchain_core.prompt_values import PromptValue
|
|
377
|
+
from langchain_core.prompt_values import PromptValue # noqa: PLC0415
|
|
368
378
|
|
|
369
379
|
if isinstance(messages, PromptValue):
|
|
370
380
|
return messages.to_messages()
|
|
@@ -389,7 +399,8 @@ def _runnable_support(func: Callable) -> Callable:
|
|
|
389
399
|
list[BaseMessage],
|
|
390
400
|
Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]],
|
|
391
401
|
]:
|
|
392
|
-
|
|
402
|
+
# Import locally to prevent circular import.
|
|
403
|
+
from langchain_core.runnables.base import RunnableLambda # noqa: PLC0415
|
|
393
404
|
|
|
394
405
|
if messages is not None:
|
|
395
406
|
return func(messages, **kwargs)
|
|
@@ -427,11 +438,16 @@ def filter_messages(
|
|
|
427
438
|
exclude_ids: Message IDs to exclude. Default is None.
|
|
428
439
|
exclude_tool_calls: Tool call IDs to exclude. Default is None.
|
|
429
440
|
Can be one of the following:
|
|
430
|
-
|
|
441
|
+
|
|
442
|
+
- ``True``: Each ``AIMessages`` with tool calls and all ``ToolMessages``
|
|
443
|
+
will be excluded.
|
|
431
444
|
- a sequence of tool call IDs to exclude:
|
|
445
|
+
|
|
432
446
|
- ToolMessages with the corresponding tool call ID will be excluded.
|
|
433
|
-
- The
|
|
434
|
-
|
|
447
|
+
- The ``tool_calls`` in the AIMessage will be updated to exclude matching
|
|
448
|
+
tool calls.
|
|
449
|
+
If all tool_calls are filtered from an AIMessage,
|
|
450
|
+
the whole message is excluded.
|
|
435
451
|
|
|
436
452
|
Returns:
|
|
437
453
|
A list of Messages that meets at least one of the incl_* conditions and none
|
|
@@ -444,14 +460,25 @@ def filter_messages(
|
|
|
444
460
|
Example:
|
|
445
461
|
.. code-block:: python
|
|
446
462
|
|
|
447
|
-
from langchain_core.messages import
|
|
463
|
+
from langchain_core.messages import (
|
|
464
|
+
filter_messages,
|
|
465
|
+
AIMessage,
|
|
466
|
+
HumanMessage,
|
|
467
|
+
SystemMessage,
|
|
468
|
+
)
|
|
448
469
|
|
|
449
470
|
messages = [
|
|
450
471
|
SystemMessage("you're a good assistant."),
|
|
451
472
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
452
473
|
AIMessage("steve-o", id="bar", name="example_assistant"),
|
|
453
|
-
HumanMessage(
|
|
454
|
-
|
|
474
|
+
HumanMessage(
|
|
475
|
+
"what's your favorite color",
|
|
476
|
+
id="baz",
|
|
477
|
+
),
|
|
478
|
+
AIMessage(
|
|
479
|
+
"silicon blue",
|
|
480
|
+
id="blah",
|
|
481
|
+
),
|
|
455
482
|
]
|
|
456
483
|
|
|
457
484
|
filter_messages(
|
|
@@ -468,7 +495,7 @@ def filter_messages(
|
|
|
468
495
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
469
496
|
]
|
|
470
497
|
|
|
471
|
-
"""
|
|
498
|
+
"""
|
|
472
499
|
messages = convert_to_messages(messages)
|
|
473
500
|
filtered: list[BaseMessage] = []
|
|
474
501
|
for msg in messages:
|
|
@@ -547,12 +574,14 @@ def merge_message_runs(
|
|
|
547
574
|
Returns:
|
|
548
575
|
list of BaseMessages with consecutive runs of message types merged into single
|
|
549
576
|
messages. By default, if two messages being merged both have string contents,
|
|
550
|
-
the merged content is a concatenation of the two strings with a new-line
|
|
577
|
+
the merged content is a concatenation of the two strings with a new-line
|
|
578
|
+
separator.
|
|
551
579
|
The separator inserted between message chunks can be controlled by specifying
|
|
552
|
-
any string with ``chunk_separator``. If at least one of the messages has a list
|
|
553
|
-
content blocks, the merged content is a list of content blocks.
|
|
580
|
+
any string with ``chunk_separator``. If at least one of the messages has a list
|
|
581
|
+
of content blocks, the merged content is a list of content blocks.
|
|
554
582
|
|
|
555
583
|
Example:
|
|
584
|
+
|
|
556
585
|
.. code-block:: python
|
|
557
586
|
|
|
558
587
|
from langchain_core.messages import (
|
|
@@ -565,16 +594,33 @@ def merge_message_runs(
|
|
|
565
594
|
|
|
566
595
|
messages = [
|
|
567
596
|
SystemMessage("you're a good assistant."),
|
|
568
|
-
HumanMessage(
|
|
569
|
-
|
|
597
|
+
HumanMessage(
|
|
598
|
+
"what's your favorite color",
|
|
599
|
+
id="foo",
|
|
600
|
+
),
|
|
601
|
+
HumanMessage(
|
|
602
|
+
"wait your favorite food",
|
|
603
|
+
id="bar",
|
|
604
|
+
),
|
|
570
605
|
AIMessage(
|
|
571
606
|
"my favorite colo",
|
|
572
|
-
tool_calls=[
|
|
607
|
+
tool_calls=[
|
|
608
|
+
ToolCall(
|
|
609
|
+
name="blah_tool", args={"x": 2}, id="123", type="tool_call"
|
|
610
|
+
)
|
|
611
|
+
],
|
|
573
612
|
id="baz",
|
|
574
613
|
),
|
|
575
614
|
AIMessage(
|
|
576
615
|
[{"type": "text", "text": "my favorite dish is lasagna"}],
|
|
577
|
-
tool_calls=[
|
|
616
|
+
tool_calls=[
|
|
617
|
+
ToolCall(
|
|
618
|
+
name="blah_tool",
|
|
619
|
+
args={"x": -10},
|
|
620
|
+
id="456",
|
|
621
|
+
type="tool_call",
|
|
622
|
+
)
|
|
623
|
+
],
|
|
578
624
|
id="blur",
|
|
579
625
|
),
|
|
580
626
|
]
|
|
@@ -585,21 +631,34 @@ def merge_message_runs(
|
|
|
585
631
|
|
|
586
632
|
[
|
|
587
633
|
SystemMessage("you're a good assistant."),
|
|
588
|
-
HumanMessage(
|
|
634
|
+
HumanMessage(
|
|
635
|
+
"what's your favorite color\\n"
|
|
636
|
+
"wait your favorite food", id="foo",
|
|
637
|
+
),
|
|
589
638
|
AIMessage(
|
|
590
639
|
[
|
|
591
640
|
"my favorite colo",
|
|
592
641
|
{"type": "text", "text": "my favorite dish is lasagna"}
|
|
593
642
|
],
|
|
594
643
|
tool_calls=[
|
|
595
|
-
ToolCall({
|
|
596
|
-
|
|
644
|
+
ToolCall({
|
|
645
|
+
"name": "blah_tool",
|
|
646
|
+
"args": {"x": 2},
|
|
647
|
+
"id": "123",
|
|
648
|
+
"type": "tool_call"
|
|
649
|
+
}),
|
|
650
|
+
ToolCall({
|
|
651
|
+
"name": "blah_tool",
|
|
652
|
+
"args": {"x": -10},
|
|
653
|
+
"id": "456",
|
|
654
|
+
"type": "tool_call"
|
|
655
|
+
})
|
|
597
656
|
]
|
|
598
657
|
id="baz"
|
|
599
658
|
),
|
|
600
659
|
]
|
|
601
660
|
|
|
602
|
-
"""
|
|
661
|
+
"""
|
|
603
662
|
if not messages:
|
|
604
663
|
return []
|
|
605
664
|
messages = convert_to_messages(messages)
|
|
@@ -659,8 +718,8 @@ def trim_messages(
|
|
|
659
718
|
properties:
|
|
660
719
|
|
|
661
720
|
1. The resulting chat history should be valid. Most chat models expect that chat
|
|
662
|
-
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
|
|
663
|
-
by a ``HumanMessage``. To achieve this, set ``start_on="human"``.
|
|
721
|
+
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
|
|
722
|
+
followed by a ``HumanMessage``. To achieve this, set ``start_on="human"``.
|
|
664
723
|
In addition, generally a ``ToolMessage`` can only appear after an ``AIMessage``
|
|
665
724
|
that involved a tool call.
|
|
666
725
|
Please see the following link for more information about messages:
|
|
@@ -691,9 +750,11 @@ def trim_messages(
|
|
|
691
750
|
exact token counting is not necessary.
|
|
692
751
|
|
|
693
752
|
strategy: Strategy for trimming.
|
|
753
|
+
|
|
694
754
|
- "first": Keep the first <= n_count tokens of the messages.
|
|
695
755
|
- "last": Keep the last <= n_count tokens of the messages.
|
|
696
|
-
|
|
756
|
+
|
|
757
|
+
Default is ``'last'``.
|
|
697
758
|
allow_partial: Whether to split a message if only part of the message can be
|
|
698
759
|
included. If ``strategy="last"`` then the last partial contents of a message
|
|
699
760
|
are included. If ``strategy="first"`` then the first partial contents of a
|
|
@@ -751,14 +812,18 @@ def trim_messages(
|
|
|
751
812
|
)
|
|
752
813
|
|
|
753
814
|
messages = [
|
|
754
|
-
SystemMessage(
|
|
815
|
+
SystemMessage(
|
|
816
|
+
"you're a good assistant, you always respond with a joke."
|
|
817
|
+
),
|
|
755
818
|
HumanMessage("i wonder why it's called langchain"),
|
|
756
819
|
AIMessage(
|
|
757
|
-
'Well, I guess they thought "WordRope" and "SentenceString" just
|
|
820
|
+
'Well, I guess they thought "WordRope" and "SentenceString" just '
|
|
821
|
+
"didn't have the same ring to it!"
|
|
758
822
|
),
|
|
759
823
|
HumanMessage("and who is harrison chasing anyways"),
|
|
760
824
|
AIMessage(
|
|
761
|
-
"Hmmm let me think.\n\nWhy, he's probably chasing after the last
|
|
825
|
+
"Hmmm let me think.\n\nWhy, he's probably chasing after the last "
|
|
826
|
+
"cup of coffee in the office!"
|
|
762
827
|
),
|
|
763
828
|
HumanMessage("what do you call a speechless parrot"),
|
|
764
829
|
]
|
|
@@ -783,8 +848,10 @@ def trim_messages(
|
|
|
783
848
|
.. code-block:: python
|
|
784
849
|
|
|
785
850
|
[
|
|
786
|
-
SystemMessage(
|
|
787
|
-
|
|
851
|
+
SystemMessage(
|
|
852
|
+
content="you're a good assistant, you always respond with a joke."
|
|
853
|
+
),
|
|
854
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
788
855
|
]
|
|
789
856
|
|
|
790
857
|
Trim chat history based on the message count, keeping the SystemMessage if
|
|
@@ -814,10 +881,15 @@ def trim_messages(
|
|
|
814
881
|
.. code-block:: python
|
|
815
882
|
|
|
816
883
|
[
|
|
817
|
-
SystemMessage(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
HumanMessage(content=
|
|
884
|
+
SystemMessage(
|
|
885
|
+
content="you're a good assistant, you always respond with a joke."
|
|
886
|
+
),
|
|
887
|
+
HumanMessage(content="and who is harrison chasing anyways"),
|
|
888
|
+
AIMessage(
|
|
889
|
+
content="Hmmm let me think.\n\nWhy, he's probably chasing after "
|
|
890
|
+
"the last cup of coffee in the office!"
|
|
891
|
+
),
|
|
892
|
+
HumanMessage(content="what do you call a speechless parrot"),
|
|
821
893
|
]
|
|
822
894
|
|
|
823
895
|
|
|
@@ -828,7 +900,9 @@ def trim_messages(
|
|
|
828
900
|
|
|
829
901
|
messages = [
|
|
830
902
|
SystemMessage("This is a 4 token text. The full message is 10 tokens."),
|
|
831
|
-
HumanMessage(
|
|
903
|
+
HumanMessage(
|
|
904
|
+
"This is a 4 token text. The full message is 10 tokens.", id="first"
|
|
905
|
+
),
|
|
832
906
|
AIMessage(
|
|
833
907
|
[
|
|
834
908
|
{"type": "text", "text": "This is the FIRST 4 token block."},
|
|
@@ -836,10 +910,16 @@ def trim_messages(
|
|
|
836
910
|
],
|
|
837
911
|
id="second",
|
|
838
912
|
),
|
|
839
|
-
HumanMessage(
|
|
840
|
-
|
|
913
|
+
HumanMessage(
|
|
914
|
+
"This is a 4 token text. The full message is 10 tokens.", id="third"
|
|
915
|
+
),
|
|
916
|
+
AIMessage(
|
|
917
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
918
|
+
id="fourth",
|
|
919
|
+
),
|
|
841
920
|
]
|
|
842
921
|
|
|
922
|
+
|
|
843
923
|
def dummy_token_counter(messages: list[BaseMessage]) -> int:
|
|
844
924
|
# treat each message like it adds 3 default tokens at the beginning
|
|
845
925
|
# of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
|
|
@@ -852,9 +932,17 @@ def trim_messages(
|
|
|
852
932
|
count = 0
|
|
853
933
|
for msg in messages:
|
|
854
934
|
if isinstance(msg.content, str):
|
|
855
|
-
count +=
|
|
935
|
+
count += (
|
|
936
|
+
default_msg_prefix_len
|
|
937
|
+
+ default_content_len
|
|
938
|
+
+ default_msg_suffix_len
|
|
939
|
+
)
|
|
856
940
|
if isinstance(msg.content, list):
|
|
857
|
-
count +=
|
|
941
|
+
count += (
|
|
942
|
+
default_msg_prefix_len
|
|
943
|
+
+ len(msg.content) * default_content_len
|
|
944
|
+
+ default_msg_suffix_len
|
|
945
|
+
)
|
|
858
946
|
return count
|
|
859
947
|
|
|
860
948
|
First 30 tokens, allowing partial messages:
|
|
@@ -871,12 +959,20 @@ def trim_messages(
|
|
|
871
959
|
.. code-block:: python
|
|
872
960
|
|
|
873
961
|
[
|
|
874
|
-
SystemMessage(
|
|
875
|
-
|
|
876
|
-
|
|
962
|
+
SystemMessage(
|
|
963
|
+
"This is a 4 token text. The full message is 10 tokens."
|
|
964
|
+
),
|
|
965
|
+
HumanMessage(
|
|
966
|
+
"This is a 4 token text. The full message is 10 tokens.",
|
|
967
|
+
id="first",
|
|
968
|
+
),
|
|
969
|
+
AIMessage(
|
|
970
|
+
[{"type": "text", "text": "This is the FIRST 4 token block."}],
|
|
971
|
+
id="second",
|
|
972
|
+
),
|
|
877
973
|
]
|
|
878
974
|
|
|
879
|
-
"""
|
|
975
|
+
"""
|
|
880
976
|
# Validate arguments
|
|
881
977
|
if start_on and strategy == "first":
|
|
882
978
|
msg = "start_on parameter is only valid with strategy='last'"
|
|
@@ -907,17 +1003,12 @@ def trim_messages(
|
|
|
907
1003
|
)
|
|
908
1004
|
raise ValueError(msg)
|
|
909
1005
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
text_splitter_fn
|
|
1006
|
+
if _HAS_LANGCHAIN_TEXT_SPLITTERS and isinstance(text_splitter, TextSplitter):
|
|
1007
|
+
text_splitter_fn = text_splitter.split_text
|
|
1008
|
+
elif text_splitter:
|
|
1009
|
+
text_splitter_fn = cast("Callable", text_splitter)
|
|
914
1010
|
else:
|
|
915
|
-
|
|
916
|
-
text_splitter_fn = text_splitter.split_text
|
|
917
|
-
else:
|
|
918
|
-
text_splitter_fn = text_splitter
|
|
919
|
-
|
|
920
|
-
text_splitter_fn = text_splitter_fn or _default_text_splitter
|
|
1011
|
+
text_splitter_fn = _default_text_splitter
|
|
921
1012
|
|
|
922
1013
|
if strategy == "first":
|
|
923
1014
|
return _first_max_tokens(
|
|
@@ -955,25 +1046,30 @@ def convert_to_openai_messages(
|
|
|
955
1046
|
in OpenAI, Anthropic, Bedrock Converse, or VertexAI formats.
|
|
956
1047
|
text_format: How to format string or text block contents:
|
|
957
1048
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1049
|
+
- ``'string'``:
|
|
1050
|
+
If a message has a string content, this is left as a string. If
|
|
1051
|
+
a message has content blocks that are all of type 'text', these are
|
|
1052
|
+
joined with a newline to make a single string. If a message has
|
|
1053
|
+
content blocks and at least one isn't of type 'text', then
|
|
1054
|
+
all blocks are left as dicts.
|
|
1055
|
+
- ``'block'``:
|
|
1056
|
+
If a message has a string content, this is turned into a list
|
|
1057
|
+
with a single content block of type 'text'. If a message has content
|
|
1058
|
+
blocks these are left as is.
|
|
1059
|
+
|
|
1060
|
+
Raises:
|
|
1061
|
+
ValueError: if an unrecognized ``text_format`` is specified, or if a message
|
|
1062
|
+
content block is missing expected keys.
|
|
968
1063
|
|
|
969
1064
|
Returns:
|
|
970
1065
|
The return type depends on the input type:
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1066
|
+
|
|
1067
|
+
- dict:
|
|
1068
|
+
If a single message-like object is passed in, a single OpenAI message
|
|
1069
|
+
dict is returned.
|
|
1070
|
+
- list[dict]:
|
|
1071
|
+
If a sequence of message-like objects are passed in, a list of OpenAI
|
|
1072
|
+
message dicts is returned.
|
|
977
1073
|
|
|
978
1074
|
Example:
|
|
979
1075
|
|
|
@@ -988,8 +1084,27 @@ def convert_to_openai_messages(
|
|
|
988
1084
|
|
|
989
1085
|
messages = [
|
|
990
1086
|
SystemMessage([{"type": "text", "text": "foo"}]),
|
|
991
|
-
{
|
|
992
|
-
|
|
1087
|
+
{
|
|
1088
|
+
"role": "user",
|
|
1089
|
+
"content": [
|
|
1090
|
+
{"type": "text", "text": "whats in this"},
|
|
1091
|
+
{
|
|
1092
|
+
"type": "image_url",
|
|
1093
|
+
"image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"},
|
|
1094
|
+
},
|
|
1095
|
+
],
|
|
1096
|
+
},
|
|
1097
|
+
AIMessage(
|
|
1098
|
+
"",
|
|
1099
|
+
tool_calls=[
|
|
1100
|
+
{
|
|
1101
|
+
"name": "analyze",
|
|
1102
|
+
"args": {"baz": "buz"},
|
|
1103
|
+
"id": "1",
|
|
1104
|
+
"type": "tool_call",
|
|
1105
|
+
}
|
|
1106
|
+
],
|
|
1107
|
+
),
|
|
993
1108
|
ToolMessage("foobar", tool_call_id="1", name="bar"),
|
|
994
1109
|
{"role": "assistant", "content": "thats nice"},
|
|
995
1110
|
]
|
|
@@ -144,7 +144,10 @@ class BaseOutputParser(
|
|
|
144
144
|
|
|
145
145
|
def parse(self, text: str) -> bool:
|
|
146
146
|
cleaned_text = text.strip().upper()
|
|
147
|
-
if cleaned_text not in (
|
|
147
|
+
if cleaned_text not in (
|
|
148
|
+
self.true_val.upper(),
|
|
149
|
+
self.false_val.upper(),
|
|
150
|
+
):
|
|
148
151
|
raise OutputParserException(
|
|
149
152
|
f"BooleanOutputParser expected output value to either be "
|
|
150
153
|
f"{self.true_val} or {self.false_val} (case-insensitive). "
|
|
@@ -156,7 +159,7 @@ class BaseOutputParser(
|
|
|
156
159
|
def _type(self) -> str:
|
|
157
160
|
return "boolean_output_parser"
|
|
158
161
|
|
|
159
|
-
"""
|
|
162
|
+
"""
|
|
160
163
|
|
|
161
164
|
@property
|
|
162
165
|
@override
|
|
@@ -46,13 +46,13 @@ class JsonOutputParser(BaseCumulativeTransformOutputParser[Any]):
|
|
|
46
46
|
def _diff(self, prev: Optional[Any], next: Any) -> Any:
|
|
47
47
|
return jsonpatch.make_patch(prev, next).patch
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _get_schema(pydantic_object: type[TBaseModel]) -> dict[str, Any]:
|
|
50
51
|
if issubclass(pydantic_object, pydantic.BaseModel):
|
|
51
52
|
return pydantic_object.model_json_schema()
|
|
52
|
-
|
|
53
|
-
return pydantic_object.schema()
|
|
54
|
-
return None
|
|
53
|
+
return pydantic_object.schema()
|
|
55
54
|
|
|
55
|
+
@override
|
|
56
56
|
def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
|
|
57
57
|
"""Parse the result of an LLM call to a JSON object.
|
|
58
58
|
|
|
@@ -143,10 +143,7 @@ class CommaSeparatedListOutputParser(ListOutputParser):
|
|
|
143
143
|
|
|
144
144
|
@classmethod
|
|
145
145
|
def is_lc_serializable(cls) -> bool:
|
|
146
|
-
"""
|
|
147
|
-
|
|
148
|
-
Returns True.
|
|
149
|
-
"""
|
|
146
|
+
"""Return True as this class is serializable."""
|
|
150
147
|
return True
|
|
151
148
|
|
|
152
149
|
@classmethod
|
|
@@ -154,11 +151,11 @@ class CommaSeparatedListOutputParser(ListOutputParser):
|
|
|
154
151
|
"""Get the namespace of the langchain object.
|
|
155
152
|
|
|
156
153
|
Returns:
|
|
157
|
-
|
|
158
|
-
Default is ["langchain", "output_parsers", "list"].
|
|
154
|
+
``["langchain", "output_parsers", "list"]``
|
|
159
155
|
"""
|
|
160
156
|
return ["langchain", "output_parsers", "list"]
|
|
161
157
|
|
|
158
|
+
@override
|
|
162
159
|
def get_format_instructions(self) -> str:
|
|
163
160
|
"""Return the format instructions for the comma-separated list output."""
|
|
164
161
|
return (
|
|
@@ -166,6 +163,7 @@ class CommaSeparatedListOutputParser(ListOutputParser):
|
|
|
166
163
|
"eg: `foo, bar, baz` or `foo,bar,baz`"
|
|
167
164
|
)
|
|
168
165
|
|
|
166
|
+
@override
|
|
169
167
|
def parse(self, text: str) -> list[str]:
|
|
170
168
|
"""Parse the output of an LLM call.
|
|
171
169
|
|
|
@@ -213,15 +211,8 @@ class NumberedListOutputParser(ListOutputParser):
|
|
|
213
211
|
"""
|
|
214
212
|
return re.findall(self.pattern, text)
|
|
215
213
|
|
|
214
|
+
@override
|
|
216
215
|
def parse_iter(self, text: str) -> Iterator[re.Match]:
|
|
217
|
-
"""Parse the output of an LLM call.
|
|
218
|
-
|
|
219
|
-
Args:
|
|
220
|
-
text: The output of an LLM call.
|
|
221
|
-
|
|
222
|
-
Yields:
|
|
223
|
-
A match object for each part of the output.
|
|
224
|
-
"""
|
|
225
216
|
return re.finditer(self.pattern, text)
|
|
226
217
|
|
|
227
218
|
@property
|
|
@@ -235,6 +226,7 @@ class MarkdownListOutputParser(ListOutputParser):
|
|
|
235
226
|
pattern: str = r"^\s*[-*]\s([^\n]+)$"
|
|
236
227
|
"""The pattern to match a Markdown list item."""
|
|
237
228
|
|
|
229
|
+
@override
|
|
238
230
|
def get_format_instructions(self) -> str:
|
|
239
231
|
"""Return the format instructions for the Markdown list output."""
|
|
240
232
|
return "Your response should be a markdown list, eg: `- foo\n- bar\n- baz`"
|
|
@@ -250,15 +242,8 @@ class MarkdownListOutputParser(ListOutputParser):
|
|
|
250
242
|
"""
|
|
251
243
|
return re.findall(self.pattern, text, re.MULTILINE)
|
|
252
244
|
|
|
245
|
+
@override
|
|
253
246
|
def parse_iter(self, text: str) -> Iterator[re.Match]:
|
|
254
|
-
"""Parse the output of an LLM call.
|
|
255
|
-
|
|
256
|
-
Args:
|
|
257
|
-
text: The output of an LLM call.
|
|
258
|
-
|
|
259
|
-
Yields:
|
|
260
|
-
A match object for each part of the output.
|
|
261
|
-
"""
|
|
262
247
|
return re.finditer(self.pattern, text, re.MULTILINE)
|
|
263
248
|
|
|
264
249
|
@property
|
|
@@ -261,6 +261,9 @@ class PydanticOutputFunctionsParser(OutputFunctionsParser):
|
|
|
261
261
|
result: The result of the LLM call.
|
|
262
262
|
partial: Whether to parse partial JSON objects. Default is False.
|
|
263
263
|
|
|
264
|
+
Raises:
|
|
265
|
+
ValueError: If the pydantic schema is not valid.
|
|
266
|
+
|
|
264
267
|
Returns:
|
|
265
268
|
The parsed JSON object.
|
|
266
269
|
"""
|
|
@@ -231,6 +231,9 @@ class JsonOutputKeyToolsParser(JsonOutputToolsParser):
|
|
|
231
231
|
If False, the output will be the full JSON object.
|
|
232
232
|
Default is False.
|
|
233
233
|
|
|
234
|
+
Raises:
|
|
235
|
+
OutputParserException: If the generation is not a chat generation.
|
|
236
|
+
|
|
234
237
|
Returns:
|
|
235
238
|
The parsed tool calls.
|
|
236
239
|
"""
|
|
@@ -316,7 +319,9 @@ class PydanticToolsParser(JsonOutputToolsParser):
|
|
|
316
319
|
The parsed Pydantic objects.
|
|
317
320
|
|
|
318
321
|
Raises:
|
|
319
|
-
|
|
322
|
+
ValueError: If the tool call arguments are not a dict.
|
|
323
|
+
ValidationError: If the tool call arguments do not conform
|
|
324
|
+
to the Pydantic model.
|
|
320
325
|
"""
|
|
321
326
|
json_results = super().parse_result(result, partial=partial)
|
|
322
327
|
if not json_results:
|
|
@@ -54,6 +54,10 @@ class PydanticOutputParser(JsonOutputParser, Generic[TBaseModel]):
|
|
|
54
54
|
all the keys that have been returned so far.
|
|
55
55
|
Defaults to False.
|
|
56
56
|
|
|
57
|
+
Raises:
|
|
58
|
+
OutputParserException: If the result is not valid JSON
|
|
59
|
+
or does not conform to the pydantic model.
|
|
60
|
+
|
|
57
61
|
Returns:
|
|
58
62
|
The parsed pydantic object.
|
|
59
63
|
"""
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""String output parser."""
|
|
2
2
|
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
3
5
|
from langchain_core.output_parsers.transform import BaseTransformOutputParser
|
|
4
6
|
|
|
5
7
|
|
|
@@ -19,7 +21,8 @@ class StrOutputParser(BaseTransformOutputParser[str]):
|
|
|
19
21
|
def get_lc_namespace(cls) -> list[str]:
|
|
20
22
|
"""Get the namespace of the langchain object.
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
Returns:
|
|
25
|
+
``["langchain", "schema", "output_parser"]``
|
|
23
26
|
"""
|
|
24
27
|
return ["langchain", "schema", "output_parser"]
|
|
25
28
|
|
|
@@ -28,6 +31,7 @@ class StrOutputParser(BaseTransformOutputParser[str]):
|
|
|
28
31
|
"""Return the output parser type for serialization."""
|
|
29
32
|
return "default"
|
|
30
33
|
|
|
34
|
+
@override
|
|
31
35
|
def parse(self, text: str) -> str:
|
|
32
36
|
"""Returns the input text with no changes."""
|
|
33
37
|
return text
|