Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions langfuse/langchain/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def _extract_model_name(
("ChatCohere", "model", None),
("Cohere", "model", None),
("HuggingFaceHub", "model", None),
("ChatHuggingFace", "model_id", None),
("ChatAnyscale", "model_name", None),
("TextGen", "model", "text-gen"),
("Ollama", "model", None),
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/test_langchain_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Unit tests for langfuse.langchain.utils model-name extraction."""

from langfuse.langchain.utils import _extract_model_name


def _chat_huggingface_serialized(model_id: str) -> dict:
"""Mirror ``langchain_core.load.dumpd(ChatHuggingFace(...))``.

ChatHuggingFace is not LangChain-serializable, so it serializes to a
``not_implemented`` stub with no ``kwargs``; the model id is only available
inside the ``repr`` string as ``model_id='...'``.
"""
return {
"lc": 1,
"type": "not_implemented",
"id": [
"langchain_huggingface",
"chat_models",
"huggingface",
"ChatHuggingFace",
],
"repr": (
f"ChatHuggingFace(llm=HuggingFaceEndpoint(repo_id='{model_id}', "
f"model='{model_id}', task='text-generation'), model_id='{model_id}')"
),
"name": "ChatHuggingFace",
}


def test_extract_model_name_chat_huggingface():
serialized = _chat_huggingface_serialized("Qwen/Qwen2.5-Coder-32B-Instruct")

Comment on lines +30 to +43

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test only covers HuggingFaceEndpoint backend shape

The PR description correctly notes that ChatHuggingFace works with three backends — HuggingFaceEndpoint, HuggingFaceHub, and HuggingFacePipeline. HuggingFacePipeline itself has a model_id attribute in its own repr, so a wrapped repr could look like ChatHuggingFace(llm=HuggingFacePipeline(model_id='...', ...), model_id='...'). The regex uses re.search (first match), meaning it would pick up the inner HuggingFacePipeline's model_id rather than ChatHuggingFace's. In practice both should be the same value, but adding a parameterised test for each backend shape would guard against any future repr change where the two values might differ.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/unit/test_langchain_utils.py
Line: 29-32

Comment:
**Test only covers HuggingFaceEndpoint backend shape**

The PR description correctly notes that `ChatHuggingFace` works with three backends — `HuggingFaceEndpoint`, `HuggingFaceHub`, and `HuggingFacePipeline`. `HuggingFacePipeline` itself has a `model_id` attribute in its own repr, so a wrapped repr could look like `ChatHuggingFace(llm=HuggingFacePipeline(model_id='...', ...), model_id='...')`. The regex uses `re.search` (first match), meaning it would pick up the inner `HuggingFacePipeline`'s `model_id` rather than `ChatHuggingFace`'s. In practice both should be the same value, but adding a parameterised test for each backend shape would guard against any future repr change where the two values might differ.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I parameterised the test to cover all three backends (HuggingFaceEndpoint, HuggingFaceHub, HuggingFacePipeline). The HuggingFacePipeline case specifically exercises the double model_id repr you flagged, where re.search matches the inner one; since both ids resolve to the same model the extracted value is correct, and the test now guards against future drift between them.

assert _extract_model_name(serialized) == "Qwen/Qwen2.5-Coder-32B-Instruct"