8
8
import pytest
9
9
from typing_extensions import override
10
10
11
- from langchain_core .callbacks import CallbackManagerForLLMRun
11
+ from langchain_core .callbacks import (
12
+ AsyncCallbackManagerForLLMRun ,
13
+ CallbackManagerForLLMRun ,
14
+ )
12
15
from langchain_core .language_models import (
13
16
BaseChatModel ,
14
17
FakeListChatModel ,
23
26
AIMessage ,
24
27
AIMessageChunk ,
25
28
BaseMessage ,
26
- BaseMessageChunk ,
27
29
HumanMessage ,
28
30
SystemMessage ,
29
31
)
@@ -907,6 +909,56 @@ async def test_output_version_ainvoke(monkeypatch: Any) -> None:
907
909
assert response .response_metadata ["output_version" ] == "v1"
908
910
909
911
912
+ class _AnotherFakeChatModel (BaseChatModel ):
913
+ responses : Iterator [AIMessage ]
914
+ """Responses for _generate."""
915
+
916
+ chunks : Iterator [AIMessageChunk ]
917
+ """Responses for _stream."""
918
+
919
+ @property
920
+ def _llm_type (self ) -> str :
921
+ return "another-fake-chat-model"
922
+
923
+ def _generate (
924
+ self ,
925
+ messages : list [BaseMessage ], # noqa: ARG002
926
+ stop : list [str ] | None = None , # noqa: ARG002
927
+ run_manager : CallbackManagerForLLMRun | None = None , # noqa: ARG002
928
+ ** kwargs : Any , # noqa: ARG002
929
+ ) -> ChatResult :
930
+ return ChatResult (generations = [ChatGeneration (message = next (self .responses ))])
931
+
932
+ async def _agenerate (
933
+ self ,
934
+ messages : list [BaseMessage ], # noqa: ARG002
935
+ stop : list [str ] | None = None , # noqa: ARG002
936
+ run_manager : AsyncCallbackManagerForLLMRun | None = None , # noqa: ARG002
937
+ ** kwargs : Any , # noqa: ARG002
938
+ ) -> ChatResult :
939
+ return ChatResult (generations = [ChatGeneration (message = next (self .responses ))])
940
+
941
+ def _stream (
942
+ self ,
943
+ messages : list [BaseMessage ], # noqa: ARG002
944
+ stop : list [str ] | None = None , # noqa: ARG002
945
+ run_manager : CallbackManagerForLLMRun | None = None , # noqa: ARG002
946
+ ** kwargs : Any , # noqa: ARG002
947
+ ) -> Iterator [ChatGenerationChunk ]:
948
+ for chunk in self .chunks :
949
+ yield ChatGenerationChunk (message = chunk )
950
+
951
+ async def _astream (
952
+ self ,
953
+ messages : list [BaseMessage ], # noqa: ARG002
954
+ stop : list [str ] | None = None , # noqa: ARG002
955
+ run_manager : AsyncCallbackManagerForLLMRun | None = None , # noqa: ARG002
956
+ ** kwargs : Any , # noqa: ARG002
957
+ ) -> AsyncIterator [ChatGenerationChunk ]:
958
+ for chunk in self .chunks :
959
+ yield ChatGenerationChunk (message = chunk )
960
+
961
+
910
962
def test_output_version_stream (monkeypatch : Any ) -> None :
911
963
messages = [AIMessage ("foo bar" )]
912
964
@@ -923,7 +975,7 @@ def test_output_version_stream(monkeypatch: Any) -> None:
923
975
924
976
# v1
925
977
llm = GenericFakeChatModel (messages = iter (messages ), output_version = "v1" )
926
- full_v1 : BaseMessageChunk | None = None
978
+ full_v1 : AIMessageChunk | None = None
927
979
for chunk in llm .stream ("hello" ):
928
980
assert isinstance (chunk , AIMessageChunk )
929
981
assert isinstance (chunk .content , list )
@@ -936,6 +988,58 @@ def test_output_version_stream(monkeypatch: Any) -> None:
936
988
assert isinstance (full_v1 , AIMessageChunk )
937
989
assert full_v1 .response_metadata ["output_version" ] == "v1"
938
990
991
+ assert full_v1 .content == [{"type" : "text" , "text" : "foo bar" , "index" : 0 }]
992
+
993
+ # Test text blocks
994
+ llm_with_rich_content = _AnotherFakeChatModel (
995
+ responses = iter ([]),
996
+ chunks = iter (
997
+ [
998
+ AIMessageChunk (content = "foo " ),
999
+ AIMessageChunk (content = "bar" ),
1000
+ ]
1001
+ ),
1002
+ output_version = "v1" ,
1003
+ )
1004
+ full_v1 = None
1005
+ for chunk in llm_with_rich_content .stream ("hello" ):
1006
+ full_v1 = chunk if full_v1 is None else full_v1 + chunk
1007
+ assert isinstance (full_v1 , AIMessageChunk )
1008
+ assert full_v1 .content_blocks == [{"type" : "text" , "text" : "foo bar" , "index" : 0 }]
1009
+
1010
+ # Test content blocks of different types
1011
+ chunks = [
1012
+ AIMessageChunk (content = "" , additional_kwargs = {"reasoning_content" : "<rea" }),
1013
+ AIMessageChunk (content = "" , additional_kwargs = {"reasoning_content" : "soning>" }),
1014
+ AIMessageChunk (content = "<some " ),
1015
+ AIMessageChunk (content = "text>" ),
1016
+ ]
1017
+ llm_with_rich_content = _AnotherFakeChatModel (
1018
+ responses = iter ([]),
1019
+ chunks = iter (chunks ),
1020
+ output_version = "v1" ,
1021
+ )
1022
+ full_v1 = None
1023
+ for chunk in llm_with_rich_content .stream ("hello" ):
1024
+ full_v1 = chunk if full_v1 is None else full_v1 + chunk
1025
+ assert isinstance (full_v1 , AIMessageChunk )
1026
+ assert full_v1 .content_blocks == [
1027
+ {"type" : "reasoning" , "reasoning" : "<reasoning>" , "index" : 0 },
1028
+ {"type" : "text" , "text" : "<some text>" , "index" : 1 },
1029
+ ]
1030
+
1031
+ # Test invoke with stream=True
1032
+ llm_with_rich_content = _AnotherFakeChatModel (
1033
+ responses = iter ([]),
1034
+ chunks = iter (chunks ),
1035
+ output_version = "v1" ,
1036
+ )
1037
+ response_v1 = llm_with_rich_content .invoke ("hello" , stream = True )
1038
+ assert response_v1 .content_blocks == [
1039
+ {"type" : "reasoning" , "reasoning" : "<reasoning>" , "index" : 0 },
1040
+ {"type" : "text" , "text" : "<some text>" , "index" : 1 },
1041
+ ]
1042
+
939
1043
# v1 from env var
940
1044
monkeypatch .setenv ("LC_OUTPUT_VERSION" , "v1" )
941
1045
llm = GenericFakeChatModel (messages = iter (messages ))
@@ -969,7 +1073,7 @@ async def test_output_version_astream(monkeypatch: Any) -> None:
969
1073
970
1074
# v1
971
1075
llm = GenericFakeChatModel (messages = iter (messages ), output_version = "v1" )
972
- full_v1 : BaseMessageChunk | None = None
1076
+ full_v1 : AIMessageChunk | None = None
973
1077
async for chunk in llm .astream ("hello" ):
974
1078
assert isinstance (chunk , AIMessageChunk )
975
1079
assert isinstance (chunk .content , list )
@@ -982,6 +1086,58 @@ async def test_output_version_astream(monkeypatch: Any) -> None:
982
1086
assert isinstance (full_v1 , AIMessageChunk )
983
1087
assert full_v1 .response_metadata ["output_version" ] == "v1"
984
1088
1089
+ assert full_v1 .content == [{"type" : "text" , "text" : "foo bar" , "index" : 0 }]
1090
+
1091
+ # Test text blocks
1092
+ llm_with_rich_content = _AnotherFakeChatModel (
1093
+ responses = iter ([]),
1094
+ chunks = iter (
1095
+ [
1096
+ AIMessageChunk (content = "foo " ),
1097
+ AIMessageChunk (content = "bar" ),
1098
+ ]
1099
+ ),
1100
+ output_version = "v1" ,
1101
+ )
1102
+ full_v1 = None
1103
+ async for chunk in llm_with_rich_content .astream ("hello" ):
1104
+ full_v1 = chunk if full_v1 is None else full_v1 + chunk
1105
+ assert isinstance (full_v1 , AIMessageChunk )
1106
+ assert full_v1 .content_blocks == [{"type" : "text" , "text" : "foo bar" , "index" : 0 }]
1107
+
1108
+ # Test content blocks of different types
1109
+ chunks = [
1110
+ AIMessageChunk (content = "" , additional_kwargs = {"reasoning_content" : "<rea" }),
1111
+ AIMessageChunk (content = "" , additional_kwargs = {"reasoning_content" : "soning>" }),
1112
+ AIMessageChunk (content = "<some " ),
1113
+ AIMessageChunk (content = "text>" ),
1114
+ ]
1115
+ llm_with_rich_content = _AnotherFakeChatModel (
1116
+ responses = iter ([]),
1117
+ chunks = iter (chunks ),
1118
+ output_version = "v1" ,
1119
+ )
1120
+ full_v1 = None
1121
+ async for chunk in llm_with_rich_content .astream ("hello" ):
1122
+ full_v1 = chunk if full_v1 is None else full_v1 + chunk
1123
+ assert isinstance (full_v1 , AIMessageChunk )
1124
+ assert full_v1 .content_blocks == [
1125
+ {"type" : "reasoning" , "reasoning" : "<reasoning>" , "index" : 0 },
1126
+ {"type" : "text" , "text" : "<some text>" , "index" : 1 },
1127
+ ]
1128
+
1129
+ # Test invoke with stream=True
1130
+ llm_with_rich_content = _AnotherFakeChatModel (
1131
+ responses = iter ([]),
1132
+ chunks = iter (chunks ),
1133
+ output_version = "v1" ,
1134
+ )
1135
+ response_v1 = await llm_with_rich_content .ainvoke ("hello" , stream = True )
1136
+ assert response_v1 .content_blocks == [
1137
+ {"type" : "reasoning" , "reasoning" : "<reasoning>" , "index" : 0 },
1138
+ {"type" : "text" , "text" : "<some text>" , "index" : 1 },
1139
+ ]
1140
+
985
1141
# v1 from env var
986
1142
monkeypatch .setenv ("LC_OUTPUT_VERSION" , "v1" )
987
1143
llm = GenericFakeChatModel (messages = iter (messages ))
0 commit comments