diff --git a/langfuse/langchain/CallbackHandler.py b/langfuse/langchain/CallbackHandler.py index 0db58cd6e..76dc3b9d4 100644 --- a/langfuse/langchain/CallbackHandler.py +++ b/langfuse/langchain/CallbackHandler.py @@ -1455,8 +1455,11 @@ def _convert_message_to_dict(self, message: BaseMessage) -> Dict[str, Any]: message_dict = {"role": message.role, "content": message.content} else: raise ValueError(f"Got unknown type {message}") - if "name" in message.additional_kwargs: - message_dict["name"] = message.additional_kwargs["name"] + # LangChain stores the name on the message itself; older integrations + # put it in additional_kwargs. + name = message.name or message.additional_kwargs.get("name") + if name: + message_dict["name"] = name if message.additional_kwargs: message_dict["additional_kwargs"] = message.additional_kwargs # type: ignore diff --git a/tests/unit/test_langchain.py b/tests/unit/test_langchain.py index 6b6143fcc..c60e2ab78 100644 --- a/tests/unit/test_langchain.py +++ b/tests/unit/test_langchain.py @@ -4,7 +4,12 @@ from uuid import uuid4 import pytest -from langchain_core.messages import AIMessage, HumanMessage, ToolMessage +from langchain_core.messages import ( + AIMessage, + FunctionMessage, + HumanMessage, + ToolMessage, +) from langchain_core.output_parsers import StrOutputParser from langchain_core.outputs import ChatGeneration, ChatResult, Generation, LLMResult from langchain_core.prompts import ChatPromptTemplate @@ -99,6 +104,42 @@ def test_chat_model_callback_exports_generation_span( } +def test_chat_model_input_keeps_message_names( + langfuse_memory_client, get_span, json_attr +): + response = ChatResult( + generations=[ChatGeneration(message=AIMessage(content="sunny"), text="sunny")] + ) + + with patch.object(ChatOpenAI, "_generate", return_value=response): + ChatOpenAI(api_key="test").invoke( + [ + HumanMessage(content="weather?", name="alice"), + AIMessage( + content="", + tool_calls=[{"name": "get_weather", "args": {}, "id": "call_1"}], + ), + ToolMessage(content="sunny", tool_call_id="call_1", name="get_weather"), + FunctionMessage(content="sunny", name="get_weather"), + HumanMessage(content="legacy", additional_kwargs={"name": "bob"}), + ], + config={"callbacks": [CallbackHandler()]}, + ) + + langfuse_memory_client.flush() + messages = json_attr( + get_span("ChatOpenAI"), LangfuseOtelSpanAttributes.OBSERVATION_INPUT + ) + + assert [(message["role"], message.get("name")) for message in messages] == [ + ("user", "alice"), + ("assistant", None), + ("tool", "get_weather"), + ("function", "get_weather"), + ("user", "bob"), + ] + + def test_llm_callback_exports_generation_span(langfuse_memory_client, get_span): response = LLMResult( generations=[[Generation(text="sockzilla")]],