Skip to content
Merged
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
44 changes: 28 additions & 16 deletions langfuse/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,13 @@ def compile(
self, **kwargs: Union[str, Any]
) -> Union[
str,
Sequence[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict_Placeholder]],
Sequence[
Union[
Dict[str, Any],
ChatMessageDict,
ChatMessageWithPlaceholdersDict_Placeholder,
]
],
]:
pass

Expand Down Expand Up @@ -327,7 +333,11 @@ def __init__(self, prompt: Prompt_Chat, is_fallback: bool = False):
def compile(
self,
**kwargs: Union[str, Any],
) -> Sequence[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict_Placeholder]]:
) -> Sequence[
Union[
Dict[str, Any], ChatMessageDict, ChatMessageWithPlaceholdersDict_Placeholder
]
]:
"""Compile the prompt with placeholders and variables.

Args:
Expand All @@ -338,7 +348,11 @@ def compile(
List of compiled chat messages as plain dictionaries, with unresolved placeholders kept as-is.
"""
compiled_messages: List[
Union[ChatMessageDict, ChatMessageWithPlaceholdersDict_Placeholder]
Union[
Dict[str, Any],
ChatMessageDict,
ChatMessageWithPlaceholdersDict_Placeholder,
]
] = []
unresolved_placeholders: List[ChatMessageWithPlaceholdersDict_Placeholder] = []

Expand All @@ -361,20 +375,18 @@ def compile(
placeholder_value = kwargs[placeholder_name]
if isinstance(placeholder_value, list):
for msg in placeholder_value:
if (
isinstance(msg, dict)
and "role" in msg
and "content" in msg
):
compiled_messages.append(
ChatMessageDict(
role=msg["role"], # type: ignore
content=TemplateParser.compile_template(
msg["content"], # type: ignore
kwargs,
),
),
if isinstance(msg, dict):
# Preserve all fields from the original message, such as tool calls
compiled_msg = dict(msg) # type: ignore
# Ensure role and content are always present
compiled_msg["role"] = msg.get("role", "NOT_GIVEN")
compiled_msg["content"] = (
TemplateParser.compile_template(
msg.get("content", ""), # type: ignore
kwargs,
)
)
compiled_messages.append(compiled_msg)
else:
compiled_messages.append(
ChatMessageDict(
Expand Down
82 changes: 82 additions & 0 deletions tests/test_prompt_compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,3 +850,85 @@ def test_get_langchain_prompt_with_unresolved_placeholders(self):

# Third message should be the user message
assert langchain_messages[2] == ("user", "Help me with coding")


def test_tool_calls_preservation_in_message_placeholder():
"""Test that tool calls are preserved when compiling message placeholders."""
from langfuse.api.resources.prompts import Prompt_Chat

chat_messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"type": "placeholder", "name": "message_history"},
{"role": "user", "content": "Help me with {{task}}"},
]

prompt_client = ChatPromptClient(
Prompt_Chat(
type="chat",
name="tool_calls_test",
version=1,
config={},
tags=[],
labels=[],
prompt=chat_messages,
)
)

# Message history with tool calls - exactly like the bug report describes
message_history_with_tool_calls = [
{"role": "user", "content": "What's the weather like?"},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": '{"location": "San Francisco"}',
},
}
],
},
{
"role": "tool",
"content": "It's sunny, 72°F",
"tool_call_id": "call_123",
"name": "get_weather",
},
]

# Compile with message history and variables
compiled_messages = prompt_client.compile(
task="weather inquiry", message_history=message_history_with_tool_calls
)

# Should have 5 messages: system + 3 from history + user
assert len(compiled_messages) == 5

# System message
assert compiled_messages[0]["role"] == "system"
assert compiled_messages[0]["content"] == "You are a helpful assistant."

# User message from history
assert compiled_messages[1]["role"] == "user"
assert compiled_messages[1]["content"] == "What's the weather like?"

# Assistant message with TOOL CALLS
assert compiled_messages[2]["role"] == "assistant"
assert compiled_messages[2]["content"] == ""
assert "tool_calls" in compiled_messages[2]
assert len(compiled_messages[2]["tool_calls"]) == 1
assert compiled_messages[2]["tool_calls"][0]["id"] == "call_123"
assert compiled_messages[2]["tool_calls"][0]["function"]["name"] == "get_weather"

# TOOL CALL results message
assert compiled_messages[3]["role"] == "tool"
assert compiled_messages[3]["content"] == "It's sunny, 72°F"
assert compiled_messages[3]["tool_call_id"] == "call_123"
assert compiled_messages[3]["name"] == "get_weather"

# Final user message with compiled variable
assert compiled_messages[4]["role"] == "user"
assert compiled_messages[4]["content"] == "Help me with weather inquiry"