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.

Files changed (131) hide show
  1. langchain_core/_api/beta_decorator.py +17 -40
  2. langchain_core/_api/deprecation.py +20 -7
  3. langchain_core/_api/path.py +19 -2
  4. langchain_core/_import_utils.py +7 -0
  5. langchain_core/agents.py +10 -6
  6. langchain_core/callbacks/base.py +28 -15
  7. langchain_core/callbacks/manager.py +81 -69
  8. langchain_core/callbacks/usage.py +4 -2
  9. langchain_core/chat_history.py +29 -21
  10. langchain_core/document_loaders/base.py +34 -9
  11. langchain_core/document_loaders/langsmith.py +3 -0
  12. langchain_core/documents/base.py +35 -10
  13. langchain_core/documents/transformers.py +4 -2
  14. langchain_core/embeddings/fake.py +8 -5
  15. langchain_core/env.py +2 -3
  16. langchain_core/example_selectors/base.py +12 -0
  17. langchain_core/exceptions.py +7 -0
  18. langchain_core/globals.py +17 -28
  19. langchain_core/indexing/api.py +57 -45
  20. langchain_core/indexing/base.py +5 -8
  21. langchain_core/indexing/in_memory.py +23 -3
  22. langchain_core/language_models/__init__.py +6 -2
  23. langchain_core/language_models/_utils.py +28 -4
  24. langchain_core/language_models/base.py +33 -21
  25. langchain_core/language_models/chat_models.py +103 -29
  26. langchain_core/language_models/fake_chat_models.py +5 -7
  27. langchain_core/language_models/llms.py +54 -20
  28. langchain_core/load/dump.py +2 -3
  29. langchain_core/load/load.py +15 -1
  30. langchain_core/load/serializable.py +38 -43
  31. langchain_core/memory.py +7 -3
  32. langchain_core/messages/__init__.py +7 -17
  33. langchain_core/messages/ai.py +41 -34
  34. langchain_core/messages/base.py +16 -7
  35. langchain_core/messages/block_translators/__init__.py +10 -8
  36. langchain_core/messages/block_translators/anthropic.py +3 -1
  37. langchain_core/messages/block_translators/bedrock.py +3 -1
  38. langchain_core/messages/block_translators/bedrock_converse.py +3 -1
  39. langchain_core/messages/block_translators/google_genai.py +3 -1
  40. langchain_core/messages/block_translators/google_vertexai.py +3 -1
  41. langchain_core/messages/block_translators/groq.py +3 -1
  42. langchain_core/messages/block_translators/langchain_v0.py +3 -136
  43. langchain_core/messages/block_translators/ollama.py +3 -1
  44. langchain_core/messages/block_translators/openai.py +252 -10
  45. langchain_core/messages/content.py +26 -124
  46. langchain_core/messages/human.py +2 -13
  47. langchain_core/messages/system.py +2 -6
  48. langchain_core/messages/tool.py +34 -14
  49. langchain_core/messages/utils.py +189 -74
  50. langchain_core/output_parsers/base.py +5 -2
  51. langchain_core/output_parsers/json.py +4 -4
  52. langchain_core/output_parsers/list.py +7 -22
  53. langchain_core/output_parsers/openai_functions.py +3 -0
  54. langchain_core/output_parsers/openai_tools.py +6 -1
  55. langchain_core/output_parsers/pydantic.py +4 -0
  56. langchain_core/output_parsers/string.py +5 -1
  57. langchain_core/output_parsers/xml.py +19 -19
  58. langchain_core/outputs/chat_generation.py +18 -7
  59. langchain_core/outputs/generation.py +14 -3
  60. langchain_core/outputs/llm_result.py +8 -1
  61. langchain_core/prompt_values.py +10 -4
  62. langchain_core/prompts/base.py +6 -11
  63. langchain_core/prompts/chat.py +88 -60
  64. langchain_core/prompts/dict.py +16 -8
  65. langchain_core/prompts/few_shot.py +9 -11
  66. langchain_core/prompts/few_shot_with_templates.py +5 -1
  67. langchain_core/prompts/image.py +12 -5
  68. langchain_core/prompts/loading.py +2 -2
  69. langchain_core/prompts/message.py +5 -6
  70. langchain_core/prompts/pipeline.py +13 -8
  71. langchain_core/prompts/prompt.py +22 -8
  72. langchain_core/prompts/string.py +18 -10
  73. langchain_core/prompts/structured.py +7 -2
  74. langchain_core/rate_limiters.py +2 -2
  75. langchain_core/retrievers.py +7 -6
  76. langchain_core/runnables/base.py +387 -246
  77. langchain_core/runnables/branch.py +11 -28
  78. langchain_core/runnables/config.py +20 -17
  79. langchain_core/runnables/configurable.py +34 -19
  80. langchain_core/runnables/fallbacks.py +20 -13
  81. langchain_core/runnables/graph.py +48 -38
  82. langchain_core/runnables/graph_ascii.py +40 -17
  83. langchain_core/runnables/graph_mermaid.py +54 -25
  84. langchain_core/runnables/graph_png.py +27 -31
  85. langchain_core/runnables/history.py +55 -58
  86. langchain_core/runnables/passthrough.py +44 -21
  87. langchain_core/runnables/retry.py +44 -23
  88. langchain_core/runnables/router.py +9 -8
  89. langchain_core/runnables/schema.py +9 -0
  90. langchain_core/runnables/utils.py +53 -90
  91. langchain_core/stores.py +19 -31
  92. langchain_core/sys_info.py +9 -8
  93. langchain_core/tools/base.py +36 -27
  94. langchain_core/tools/convert.py +25 -14
  95. langchain_core/tools/simple.py +36 -8
  96. langchain_core/tools/structured.py +25 -12
  97. langchain_core/tracers/base.py +2 -2
  98. langchain_core/tracers/context.py +5 -1
  99. langchain_core/tracers/core.py +110 -46
  100. langchain_core/tracers/evaluation.py +22 -26
  101. langchain_core/tracers/event_stream.py +97 -42
  102. langchain_core/tracers/langchain.py +12 -3
  103. langchain_core/tracers/langchain_v1.py +10 -2
  104. langchain_core/tracers/log_stream.py +56 -17
  105. langchain_core/tracers/root_listeners.py +4 -20
  106. langchain_core/tracers/run_collector.py +6 -16
  107. langchain_core/tracers/schemas.py +5 -1
  108. langchain_core/utils/aiter.py +14 -6
  109. langchain_core/utils/env.py +3 -0
  110. langchain_core/utils/function_calling.py +46 -20
  111. langchain_core/utils/interactive_env.py +6 -2
  112. langchain_core/utils/iter.py +12 -5
  113. langchain_core/utils/json.py +12 -3
  114. langchain_core/utils/json_schema.py +156 -40
  115. langchain_core/utils/loading.py +5 -1
  116. langchain_core/utils/mustache.py +25 -16
  117. langchain_core/utils/pydantic.py +38 -9
  118. langchain_core/utils/utils.py +25 -9
  119. langchain_core/vectorstores/base.py +7 -20
  120. langchain_core/vectorstores/in_memory.py +20 -14
  121. langchain_core/vectorstores/utils.py +18 -12
  122. langchain_core/version.py +1 -1
  123. langchain_core-1.0.0a3.dist-info/METADATA +77 -0
  124. langchain_core-1.0.0a3.dist-info/RECORD +181 -0
  125. langchain_core/beta/__init__.py +0 -1
  126. langchain_core/beta/runnables/__init__.py +0 -1
  127. langchain_core/beta/runnables/context.py +0 -448
  128. langchain_core-1.0.0a1.dist-info/METADATA +0 -106
  129. langchain_core-1.0.0a1.dist-info/RECORD +0 -184
  130. {langchain_core-1.0.0a1.dist-info → langchain_core-1.0.0a3.dist-info}/WHEEL +0 -0
  131. {langchain_core-1.0.0a1.dist-info → langchain_core-1.0.0a3.dist-info}/entry_points.txt +0 -0
@@ -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: THe prefix to prepend to contents of AIMessages. Default is "AI".
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: BaseMessageChunk) -> BaseMessage:
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
- from langchain_core.runnables.base import RunnableLambda
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
- - `True`: all AIMessages with tool calls and all ToolMessages will be excluded.
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 `tool_calls` in the AIMessage will be updated to exclude matching tool calls.
434
- If all tool_calls are filtered from an AIMessage, the whole message is excluded.
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 filter_messages, AIMessage, HumanMessage, SystemMessage
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("what's your favorite color", id="baz",),
454
- AIMessage("silicon blue", id="blah",),
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
- """ # noqa: E501
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 separator.
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 of
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("what's your favorite color", id="foo",),
569
- HumanMessage("wait your favorite food", id="bar",),
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=[ToolCall(name="blah_tool", args={"x": 2}, id="123", type="tool_call")],
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=[ToolCall(name="blah_tool", args={"x": -10}, id="456", type="tool_call")],
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("what's your favorite color\\nwait your favorite food", id="foo",),
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({"name": "blah_tool", "args": {"x": 2}, "id": "123", "type": "tool_call"}),
596
- ToolCall({"name": "blah_tool", "args": {"x": -10}, "id": "456", "type": "tool_call"})
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
- """ # noqa: E501
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`` followed
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
- Default is "last".
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("you're a good assistant, you always respond with a joke."),
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 didn\'t have the same ring to it!'
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 cup of coffee in the office!"
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(content="you're a good assistant, you always respond with a joke."),
787
- HumanMessage(content='what do you call a speechless parrot'),
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(content="you're a good assistant, you always respond with a joke."),
818
- HumanMessage(content='and who is harrison chasing anyways'),
819
- AIMessage(content="Hmmm let me think.\n\nWhy, he's probably chasing after the last cup of coffee in the office!"),
820
- HumanMessage(content='what do you call a speechless parrot'),
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("This is a 4 token text. The full message is 10 tokens.", id="first"),
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("This is a 4 token text. The full message is 10 tokens.", id="third"),
840
- AIMessage("This is a 4 token text. The full message is 10 tokens.", id="fourth"),
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 += default_msg_prefix_len + default_content_len + default_msg_suffix_len
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 += default_msg_prefix_len + len(msg.content) * default_content_len + default_msg_suffix_len
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("This is a 4 token text. The full message is 10 tokens."),
875
- HumanMessage("This is a 4 token text. The full message is 10 tokens.", id="first"),
876
- AIMessage( [{"type": "text", "text": "This is the FIRST 4 token block."}], id="second"),
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
- """ # noqa: E501
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
- try:
911
- from langchain_text_splitters import TextSplitter
912
- except ImportError:
913
- text_splitter_fn: Optional[Callable] = cast("Optional[Callable]", text_splitter)
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
- if isinstance(text_splitter, TextSplitter):
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
- - "string":
959
- If a message has a string content, this is left as a string. If
960
- a message has content blocks that are all of type 'text', these are
961
- joined with a newline to make a single string. If a message has
962
- content blocks and at least one isn't of type 'text', then
963
- all blocks are left as dicts.
964
- - "block":
965
- If a message has a string content, this is turned into a list
966
- with a single content block of type 'text'. If a message has content
967
- blocks these are left as is.
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
- - dict:
972
- If a single message-like object is passed in, a single OpenAI message
973
- dict is returned.
974
- - list[dict]:
975
- If a sequence of message-like objects are passed in, a list of OpenAI
976
- message dicts is returned.
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
- {"role": "user", "content": [{"type": "text", "text": "whats in this"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"}}]},
992
- AIMessage("", tool_calls=[{"name": "analyze", "args": {"baz": "buz"}, "id": "1", "type": "tool_call"}]),
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 (self.true_val.upper(), self.false_val.upper()):
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
- """ # noqa: E501
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
- def _get_schema(self, pydantic_object: type[TBaseModel]) -> dict[str, Any]:
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
- if issubclass(pydantic_object, pydantic.v1.BaseModel):
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
- """Check if the langchain object is serializable.
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
- A list of strings.
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
- OutputParserException: If the output is not valid JSON.
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
- Default is ["langchain", "schema", "output_parser"].
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