langchain-core 1.0.0a2__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 +27 -5
- langchain_core/language_models/base.py +33 -21
- langchain_core/language_models/chat_models.py +99 -27
- 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 +1 -1
- 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/ollama.py +3 -1
- langchain_core/messages/block_translators/openai.py +50 -20
- langchain_core/messages/content.py +23 -13
- 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 +186 -73
- 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.0a2.dist-info/METADATA +0 -106
- langchain_core-1.0.0a2.dist-info/RECORD +0 -184
- {langchain_core-1.0.0a2.dist-info → langchain_core-1.0.0a3.dist-info}/WHEEL +0 -0
- {langchain_core-1.0.0a2.dist-info → langchain_core-1.0.0a3.dist-info}/entry_points.txt +0 -0
langchain_core/messages/utils.py
CHANGED
|
@@ -47,12 +47,17 @@ from langchain_core.messages.system import SystemMessage, SystemMessageChunk
|
|
|
47
47
|
from langchain_core.messages.tool import ToolCall, ToolMessage, ToolMessageChunk
|
|
48
48
|
|
|
49
49
|
if TYPE_CHECKING:
|
|
50
|
-
from langchain_text_splitters import TextSplitter
|
|
51
|
-
|
|
52
50
|
from langchain_core.language_models import BaseLanguageModel
|
|
53
51
|
from langchain_core.prompt_values import PromptValue
|
|
54
52
|
from langchain_core.runnables.base import Runnable
|
|
55
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
|
+
|
|
56
61
|
logger = logging.getLogger(__name__)
|
|
57
62
|
|
|
58
63
|
|
|
@@ -97,7 +102,7 @@ def get_buffer_string(
|
|
|
97
102
|
messages: Messages to be converted to strings.
|
|
98
103
|
human_prefix: The prefix to prepend to contents of HumanMessages.
|
|
99
104
|
Default is "Human".
|
|
100
|
-
ai_prefix:
|
|
105
|
+
ai_prefix: The prefix to prepend to contents of AIMessages. Default is "AI".
|
|
101
106
|
|
|
102
107
|
Returns:
|
|
103
108
|
A single string concatenation of all input messages.
|
|
@@ -187,7 +192,7 @@ def messages_from_dict(messages: Sequence[dict]) -> list[BaseMessage]:
|
|
|
187
192
|
return [_message_from_dict(m) for m in messages]
|
|
188
193
|
|
|
189
194
|
|
|
190
|
-
def message_chunk_to_message(chunk:
|
|
195
|
+
def message_chunk_to_message(chunk: BaseMessage) -> BaseMessage:
|
|
191
196
|
"""Convert a message chunk to a message.
|
|
192
197
|
|
|
193
198
|
Args:
|
|
@@ -286,6 +291,9 @@ def _create_message_from_message_type(
|
|
|
286
291
|
message = FunctionMessage(content=content, **kwargs)
|
|
287
292
|
elif message_type == "tool":
|
|
288
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
|
|
289
297
|
message = ToolMessage(content=content, artifact=artifact, **kwargs)
|
|
290
298
|
elif message_type == "remove":
|
|
291
299
|
message = RemoveMessage(**kwargs)
|
|
@@ -366,7 +374,7 @@ def convert_to_messages(
|
|
|
366
374
|
list of messages (BaseMessages).
|
|
367
375
|
"""
|
|
368
376
|
# Import here to avoid circular imports
|
|
369
|
-
from langchain_core.prompt_values import PromptValue
|
|
377
|
+
from langchain_core.prompt_values import PromptValue # noqa: PLC0415
|
|
370
378
|
|
|
371
379
|
if isinstance(messages, PromptValue):
|
|
372
380
|
return messages.to_messages()
|
|
@@ -391,7 +399,8 @@ def _runnable_support(func: Callable) -> Callable:
|
|
|
391
399
|
list[BaseMessage],
|
|
392
400
|
Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]],
|
|
393
401
|
]:
|
|
394
|
-
|
|
402
|
+
# Import locally to prevent circular import.
|
|
403
|
+
from langchain_core.runnables.base import RunnableLambda # noqa: PLC0415
|
|
395
404
|
|
|
396
405
|
if messages is not None:
|
|
397
406
|
return func(messages, **kwargs)
|
|
@@ -429,11 +438,16 @@ def filter_messages(
|
|
|
429
438
|
exclude_ids: Message IDs to exclude. Default is None.
|
|
430
439
|
exclude_tool_calls: Tool call IDs to exclude. Default is None.
|
|
431
440
|
Can be one of the following:
|
|
432
|
-
|
|
441
|
+
|
|
442
|
+
- ``True``: Each ``AIMessages`` with tool calls and all ``ToolMessages``
|
|
443
|
+
will be excluded.
|
|
433
444
|
- a sequence of tool call IDs to exclude:
|
|
445
|
+
|
|
434
446
|
- ToolMessages with the corresponding tool call ID will be excluded.
|
|
435
|
-
- The
|
|
436
|
-
|
|
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.
|
|
437
451
|
|
|
438
452
|
Returns:
|
|
439
453
|
A list of Messages that meets at least one of the incl_* conditions and none
|
|
@@ -446,14 +460,25 @@ def filter_messages(
|
|
|
446
460
|
Example:
|
|
447
461
|
.. code-block:: python
|
|
448
462
|
|
|
449
|
-
from langchain_core.messages import
|
|
463
|
+
from langchain_core.messages import (
|
|
464
|
+
filter_messages,
|
|
465
|
+
AIMessage,
|
|
466
|
+
HumanMessage,
|
|
467
|
+
SystemMessage,
|
|
468
|
+
)
|
|
450
469
|
|
|
451
470
|
messages = [
|
|
452
471
|
SystemMessage("you're a good assistant."),
|
|
453
472
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
454
473
|
AIMessage("steve-o", id="bar", name="example_assistant"),
|
|
455
|
-
HumanMessage(
|
|
456
|
-
|
|
474
|
+
HumanMessage(
|
|
475
|
+
"what's your favorite color",
|
|
476
|
+
id="baz",
|
|
477
|
+
),
|
|
478
|
+
AIMessage(
|
|
479
|
+
"silicon blue",
|
|
480
|
+
id="blah",
|
|
481
|
+
),
|
|
457
482
|
]
|
|
458
483
|
|
|
459
484
|
filter_messages(
|
|
@@ -470,7 +495,7 @@ def filter_messages(
|
|
|
470
495
|
HumanMessage("what's your name", id="foo", name="example_user"),
|
|
471
496
|
]
|
|
472
497
|
|
|
473
|
-
"""
|
|
498
|
+
"""
|
|
474
499
|
messages = convert_to_messages(messages)
|
|
475
500
|
filtered: list[BaseMessage] = []
|
|
476
501
|
for msg in messages:
|
|
@@ -549,12 +574,14 @@ def merge_message_runs(
|
|
|
549
574
|
Returns:
|
|
550
575
|
list of BaseMessages with consecutive runs of message types merged into single
|
|
551
576
|
messages. By default, if two messages being merged both have string contents,
|
|
552
|
-
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.
|
|
553
579
|
The separator inserted between message chunks can be controlled by specifying
|
|
554
|
-
any string with ``chunk_separator``. If at least one of the messages has a list
|
|
555
|
-
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.
|
|
556
582
|
|
|
557
583
|
Example:
|
|
584
|
+
|
|
558
585
|
.. code-block:: python
|
|
559
586
|
|
|
560
587
|
from langchain_core.messages import (
|
|
@@ -567,16 +594,33 @@ def merge_message_runs(
|
|
|
567
594
|
|
|
568
595
|
messages = [
|
|
569
596
|
SystemMessage("you're a good assistant."),
|
|
570
|
-
HumanMessage(
|
|
571
|
-
|
|
597
|
+
HumanMessage(
|
|
598
|
+
"what's your favorite color",
|
|
599
|
+
id="foo",
|
|
600
|
+
),
|
|
601
|
+
HumanMessage(
|
|
602
|
+
"wait your favorite food",
|
|
603
|
+
id="bar",
|
|
604
|
+
),
|
|
572
605
|
AIMessage(
|
|
573
606
|
"my favorite colo",
|
|
574
|
-
tool_calls=[
|
|
607
|
+
tool_calls=[
|
|
608
|
+
ToolCall(
|
|
609
|
+
name="blah_tool", args={"x": 2}, id="123", type="tool_call"
|
|
610
|
+
)
|
|
611
|
+
],
|
|
575
612
|
id="baz",
|
|
576
613
|
),
|
|
577
614
|
AIMessage(
|
|
578
615
|
[{"type": "text", "text": "my favorite dish is lasagna"}],
|
|
579
|
-
tool_calls=[
|
|
616
|
+
tool_calls=[
|
|
617
|
+
ToolCall(
|
|
618
|
+
name="blah_tool",
|
|
619
|
+
args={"x": -10},
|
|
620
|
+
id="456",
|
|
621
|
+
type="tool_call",
|
|
622
|
+
)
|
|
623
|
+
],
|
|
580
624
|
id="blur",
|
|
581
625
|
),
|
|
582
626
|
]
|
|
@@ -587,21 +631,34 @@ def merge_message_runs(
|
|
|
587
631
|
|
|
588
632
|
[
|
|
589
633
|
SystemMessage("you're a good assistant."),
|
|
590
|
-
HumanMessage(
|
|
634
|
+
HumanMessage(
|
|
635
|
+
"what's your favorite color\\n"
|
|
636
|
+
"wait your favorite food", id="foo",
|
|
637
|
+
),
|
|
591
638
|
AIMessage(
|
|
592
639
|
[
|
|
593
640
|
"my favorite colo",
|
|
594
641
|
{"type": "text", "text": "my favorite dish is lasagna"}
|
|
595
642
|
],
|
|
596
643
|
tool_calls=[
|
|
597
|
-
ToolCall({
|
|
598
|
-
|
|
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
|
+
})
|
|
599
656
|
]
|
|
600
657
|
id="baz"
|
|
601
658
|
),
|
|
602
659
|
]
|
|
603
660
|
|
|
604
|
-
"""
|
|
661
|
+
"""
|
|
605
662
|
if not messages:
|
|
606
663
|
return []
|
|
607
664
|
messages = convert_to_messages(messages)
|
|
@@ -661,8 +718,8 @@ def trim_messages(
|
|
|
661
718
|
properties:
|
|
662
719
|
|
|
663
720
|
1. The resulting chat history should be valid. Most chat models expect that chat
|
|
664
|
-
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage``
|
|
665
|
-
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"``.
|
|
666
723
|
In addition, generally a ``ToolMessage`` can only appear after an ``AIMessage``
|
|
667
724
|
that involved a tool call.
|
|
668
725
|
Please see the following link for more information about messages:
|
|
@@ -693,9 +750,11 @@ def trim_messages(
|
|
|
693
750
|
exact token counting is not necessary.
|
|
694
751
|
|
|
695
752
|
strategy: Strategy for trimming.
|
|
753
|
+
|
|
696
754
|
- "first": Keep the first <= n_count tokens of the messages.
|
|
697
755
|
- "last": Keep the last <= n_count tokens of the messages.
|
|
698
|
-
|
|
756
|
+
|
|
757
|
+
Default is ``'last'``.
|
|
699
758
|
allow_partial: Whether to split a message if only part of the message can be
|
|
700
759
|
included. If ``strategy="last"`` then the last partial contents of a message
|
|
701
760
|
are included. If ``strategy="first"`` then the first partial contents of a
|
|
@@ -753,14 +812,18 @@ def trim_messages(
|
|
|
753
812
|
)
|
|
754
813
|
|
|
755
814
|
messages = [
|
|
756
|
-
SystemMessage(
|
|
815
|
+
SystemMessage(
|
|
816
|
+
"you're a good assistant, you always respond with a joke."
|
|
817
|
+
),
|
|
757
818
|
HumanMessage("i wonder why it's called langchain"),
|
|
758
819
|
AIMessage(
|
|
759
|
-
'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!"
|
|
760
822
|
),
|
|
761
823
|
HumanMessage("and who is harrison chasing anyways"),
|
|
762
824
|
AIMessage(
|
|
763
|
-
"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!"
|
|
764
827
|
),
|
|
765
828
|
HumanMessage("what do you call a speechless parrot"),
|
|
766
829
|
]
|
|
@@ -785,8 +848,10 @@ def trim_messages(
|
|
|
785
848
|
.. code-block:: python
|
|
786
849
|
|
|
787
850
|
[
|
|
788
|
-
SystemMessage(
|
|
789
|
-
|
|
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"),
|
|
790
855
|
]
|
|
791
856
|
|
|
792
857
|
Trim chat history based on the message count, keeping the SystemMessage if
|
|
@@ -816,10 +881,15 @@ def trim_messages(
|
|
|
816
881
|
.. code-block:: python
|
|
817
882
|
|
|
818
883
|
[
|
|
819
|
-
SystemMessage(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
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"),
|
|
823
893
|
]
|
|
824
894
|
|
|
825
895
|
|
|
@@ -830,7 +900,9 @@ def trim_messages(
|
|
|
830
900
|
|
|
831
901
|
messages = [
|
|
832
902
|
SystemMessage("This is a 4 token text. The full message is 10 tokens."),
|
|
833
|
-
HumanMessage(
|
|
903
|
+
HumanMessage(
|
|
904
|
+
"This is a 4 token text. The full message is 10 tokens.", id="first"
|
|
905
|
+
),
|
|
834
906
|
AIMessage(
|
|
835
907
|
[
|
|
836
908
|
{"type": "text", "text": "This is the FIRST 4 token block."},
|
|
@@ -838,10 +910,16 @@ def trim_messages(
|
|
|
838
910
|
],
|
|
839
911
|
id="second",
|
|
840
912
|
),
|
|
841
|
-
HumanMessage(
|
|
842
|
-
|
|
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
|
+
),
|
|
843
920
|
]
|
|
844
921
|
|
|
922
|
+
|
|
845
923
|
def dummy_token_counter(messages: list[BaseMessage]) -> int:
|
|
846
924
|
# treat each message like it adds 3 default tokens at the beginning
|
|
847
925
|
# of the message and at the end of the message. 3 + 4 + 3 = 10 tokens
|
|
@@ -854,9 +932,17 @@ def trim_messages(
|
|
|
854
932
|
count = 0
|
|
855
933
|
for msg in messages:
|
|
856
934
|
if isinstance(msg.content, str):
|
|
857
|
-
count +=
|
|
935
|
+
count += (
|
|
936
|
+
default_msg_prefix_len
|
|
937
|
+
+ default_content_len
|
|
938
|
+
+ default_msg_suffix_len
|
|
939
|
+
)
|
|
858
940
|
if isinstance(msg.content, list):
|
|
859
|
-
count +=
|
|
941
|
+
count += (
|
|
942
|
+
default_msg_prefix_len
|
|
943
|
+
+ len(msg.content) * default_content_len
|
|
944
|
+
+ default_msg_suffix_len
|
|
945
|
+
)
|
|
860
946
|
return count
|
|
861
947
|
|
|
862
948
|
First 30 tokens, allowing partial messages:
|
|
@@ -873,12 +959,20 @@ def trim_messages(
|
|
|
873
959
|
.. code-block:: python
|
|
874
960
|
|
|
875
961
|
[
|
|
876
|
-
SystemMessage(
|
|
877
|
-
|
|
878
|
-
|
|
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
|
+
),
|
|
879
973
|
]
|
|
880
974
|
|
|
881
|
-
"""
|
|
975
|
+
"""
|
|
882
976
|
# Validate arguments
|
|
883
977
|
if start_on and strategy == "first":
|
|
884
978
|
msg = "start_on parameter is only valid with strategy='last'"
|
|
@@ -909,17 +1003,12 @@ def trim_messages(
|
|
|
909
1003
|
)
|
|
910
1004
|
raise ValueError(msg)
|
|
911
1005
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
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)
|
|
916
1010
|
else:
|
|
917
|
-
|
|
918
|
-
text_splitter_fn = text_splitter.split_text
|
|
919
|
-
else:
|
|
920
|
-
text_splitter_fn = text_splitter
|
|
921
|
-
|
|
922
|
-
text_splitter_fn = text_splitter_fn or _default_text_splitter
|
|
1011
|
+
text_splitter_fn = _default_text_splitter
|
|
923
1012
|
|
|
924
1013
|
if strategy == "first":
|
|
925
1014
|
return _first_max_tokens(
|
|
@@ -957,25 +1046,30 @@ def convert_to_openai_messages(
|
|
|
957
1046
|
in OpenAI, Anthropic, Bedrock Converse, or VertexAI formats.
|
|
958
1047
|
text_format: How to format string or text block contents:
|
|
959
1048
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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.
|
|
970
1063
|
|
|
971
1064
|
Returns:
|
|
972
1065
|
The return type depends on the input type:
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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.
|
|
979
1073
|
|
|
980
1074
|
Example:
|
|
981
1075
|
|
|
@@ -990,8 +1084,27 @@ def convert_to_openai_messages(
|
|
|
990
1084
|
|
|
991
1085
|
messages = [
|
|
992
1086
|
SystemMessage([{"type": "text", "text": "foo"}]),
|
|
993
|
-
{
|
|
994
|
-
|
|
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
|
+
),
|
|
995
1108
|
ToolMessage("foobar", tool_call_id="1", name="bar"),
|
|
996
1109
|
{"role": "assistant", "content": "thats nice"},
|
|
997
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
|