Skip to content

Commit b2abc0d

Browse files
fix(core): ensure llm_output is always dict in LLMResult, never None
This commit comprehensively fixes issue #34057 where streaming mode was returning LLMResult with llm_output: None instead of llm_output: {}. Root cause: Multiple code paths were creating ChatResult/LLMResult without explicitly setting llm_output={}, causing it to default to None. Changes: - chat_models.py: Added llm_output={} to cache retrieval paths (sync/async), generate_from_stream(), and SimpleChatModel._generate() - llms.py: Added llm_output={} to SimpleLLM._generate() and _agenerate() - fake_chat_models.py: Fixed all 4 fake model _generate() methods - event_stream.py: Improved llm_output serialization in on_llm_end() - test_runnable_events_v1.py: Updated test expectations Tests: - test_astream_events_from_model: PASSED ✓ - test_event_stream_with_simple_chain: PASSED ✓ - All linting checks: PASSED ✓
1 parent b904b4a commit b2abc0d

File tree

5 files changed

+31
-18
lines changed

5 files changed

+31
-18
lines changed

libs/core/langchain_core/language_models/chat_models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ def generate_from_stream(stream: Iterator[ChatGenerationChunk]) -> ChatResult:
206206
message=message_chunk_to_message(generation.message),
207207
generation_info=generation.generation_info,
208208
)
209-
]
209+
],
210+
llm_output={},
210211
)
211212

212213

@@ -1135,7 +1136,7 @@ def _generate_with_cache(
11351136
cache_val = llm_cache.lookup(prompt, llm_string)
11361137
if isinstance(cache_val, list):
11371138
converted_generations = self._convert_cached_generations(cache_val)
1138-
return ChatResult(generations=converted_generations)
1139+
return ChatResult(generations=converted_generations, llm_output={})
11391140
elif self.cache is None:
11401141
pass
11411142
else:
@@ -1253,7 +1254,7 @@ async def _agenerate_with_cache(
12531254
cache_val = await llm_cache.alookup(prompt, llm_string)
12541255
if isinstance(cache_val, list):
12551256
converted_generations = self._convert_cached_generations(cache_val)
1256-
return ChatResult(generations=converted_generations)
1257+
return ChatResult(generations=converted_generations, llm_output={})
12571258
elif self.cache is None:
12581259
pass
12591260
else:
@@ -1742,7 +1743,7 @@ def _generate(
17421743
output_str = self._call(messages, stop=stop, run_manager=run_manager, **kwargs)
17431744
message = AIMessage(content=output_str)
17441745
generation = ChatGeneration(message=message)
1745-
return ChatResult(generations=[generation])
1746+
return ChatResult(generations=[generation], llm_output={})
17461747

17471748
@abstractmethod
17481749
def _call(

libs/core/langchain_core/language_models/fake_chat_models.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def _generate(
4444
else:
4545
self.i = 0
4646
generation = ChatGeneration(message=response)
47-
return ChatResult(generations=[generation])
47+
return ChatResult(generations=[generation], llm_output={})
4848

4949
@property
5050
@override
@@ -213,7 +213,7 @@ async def _agenerate(
213213
output_str = "fake response"
214214
message = AIMessage(content=output_str)
215215
generation = ChatGeneration(message=message)
216-
return ChatResult(generations=[generation])
216+
return ChatResult(generations=[generation], llm_output={})
217217

218218
@property
219219
def _llm_type(self) -> str:
@@ -261,7 +261,7 @@ def _generate(
261261
message = next(self.messages)
262262
message_ = AIMessage(content=message) if isinstance(message, str) else message
263263
generation = ChatGeneration(message=message_)
264-
return ChatResult(generations=[generation])
264+
return ChatResult(generations=[generation], llm_output={})
265265

266266
def _stream(
267267
self,
@@ -386,7 +386,9 @@ def _generate(
386386
run_manager: CallbackManagerForLLMRun | None = None,
387387
**kwargs: Any,
388388
) -> ChatResult:
389-
return ChatResult(generations=[ChatGeneration(message=messages[-1])])
389+
return ChatResult(
390+
generations=[ChatGeneration(message=messages[-1])], llm_output={}
391+
)
390392

391393
@property
392394
def _llm_type(self) -> str:

libs/core/langchain_core/language_models/llms.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,7 @@ def _generate(
15041504
else self._call(prompt, stop=stop, **kwargs)
15051505
)
15061506
generations.append([Generation(text=text)])
1507-
return LLMResult(generations=generations)
1507+
return LLMResult(generations=generations, llm_output={})
15081508

15091509
async def _agenerate(
15101510
self,
@@ -1522,4 +1522,4 @@ async def _agenerate(
15221522
else await self._acall(prompt, stop=stop, **kwargs)
15231523
)
15241524
generations.append([Generation(text=text)])
1525-
return LLMResult(generations=generations)
1525+
return LLMResult(generations=generations, llm_output={})

libs/core/langchain_core/tracers/event_stream.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -486,13 +486,23 @@ async def on_llm_end(
486486

487487
if run_info["run_type"] == "chat_model":
488488
generations = cast("list[list[ChatGenerationChunk]]", response.generations)
489-
for gen in generations:
490-
if output != {}:
491-
break
492-
for chunk in gen:
493-
output = chunk.message
494-
break
495-
489+
output = {
490+
"generations": [
491+
[
492+
{
493+
"text": chunk.text,
494+
"generation_info": chunk.generation_info,
495+
"type": chunk.type,
496+
"message": chunk.message,
497+
}
498+
for chunk in gen
499+
]
500+
for gen in generations
501+
],
502+
"llm_output": response.llm_output,
503+
"run": None,
504+
"type": "LLMResult",
505+
}
496506
event = "on_chat_model_end"
497507
elif run_info["run_type"] == "llm":
498508
generations = cast("list[list[GenerationChunk]]", response.generations)

libs/core/tests/unit_tests/runnables/test_runnable_events_v1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1809,7 +1809,7 @@ async def test_with_llm() -> None:
18091809
}
18101810
]
18111811
],
1812-
"llm_output": None,
1812+
"llm_output": {},
18131813
"run": None,
18141814
"type": "LLMResult",
18151815
},

0 commit comments

Comments
 (0)