Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions langfuse/langchain/CallbackHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1327,12 +1327,7 @@ def on_llm_end(
self._log_debug_event(
"on_llm_end", run_id, parent_run_id, response=response, kwargs=kwargs
)
response_generation = response.generations[-1][-1]
extracted_response = (
self._convert_message_to_dict(response_generation.message)
if isinstance(response_generation, ChatGeneration)
else _extract_raw_response(response_generation)
)
extracted_response = self._extract_llm_result_response(response)

llm_usage = _parse_usage(response)

Expand Down Expand Up @@ -1501,6 +1496,20 @@ def _log_debug_event(
f"Event: {event_name}, run_id: {run_id}, parent_run_id: {parent_run_id}"
)

def _extract_llm_result_response(self, response: LLMResult) -> Any:
for generation in reversed(response.generations):
if not generation:
continue

response_generation = generation[-1]
return (
self._convert_message_to_dict(response_generation.message)
if isinstance(response_generation, ChatGeneration)
else _extract_raw_response(response_generation)
)

return None


def _extract_raw_response(last_response: Any) -> Any:
"""Extract the response from the last response of the LLM call."""
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/test_langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,44 @@ def test_llm_callback_exports_generation_span(langfuse_memory_client, get_span):
)


def test_llm_callback_ends_generation_with_empty_generations(
langfuse_memory_client, get_span, json_attr
):
handler = CallbackHandler()
run_id = uuid4()
response = LLMResult(
generations=[[]],
llm_output={
"token_usage": {
"prompt_tokens": 2,
"completion_tokens": 0,
"total_tokens": 2,
},
"model_name": "empty-model",
},
)

handler.on_llm_start(
{"name": "EmptyLLM"},
["hello"],
run_id=run_id,
invocation_params={"model_name": "empty-model"},
)
handler.on_llm_end(response, run_id=run_id)

langfuse_memory_client.flush()
span = get_span("EmptyLLM")

assert (
span.attributes[LangfuseOtelSpanAttributes.OBSERVATION_MODEL] == "empty-model"
)
assert json_attr(span, LangfuseOtelSpanAttributes.OBSERVATION_USAGE_DETAILS) == {
"prompt_tokens": 2,
"completion_tokens": 0,
"total_tokens": 2,
}


def test_lcel_chain_exports_intermediate_chain_spans(
langfuse_memory_client, get_span, find_spans
):
Expand Down