Skip to content

Commit 401bf64

Browse files
committed
fix types
1 parent 3487207 commit 401bf64

3 files changed

Lines changed: 176 additions & 13 deletions

File tree

langfuse/_client/client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from langfuse.media import LangfuseMedia
5656
from langfuse.model import (
5757
ChatMessageDict,
58+
ChatMessageWithPlaceholdersDict,
5859
ChatPromptClient,
5960
CreateDatasetItemRequest,
6061
CreateDatasetRequest,
@@ -2127,7 +2128,7 @@ def create_prompt(
21272128
self,
21282129
*,
21292130
name: str,
2130-
prompt: List[ChatMessageDict],
2131+
prompt: List[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict]],
21312132
labels: List[str] = [],
21322133
tags: Optional[List[str]] = None,
21332134
type: Optional[Literal["chat"]],
@@ -2152,7 +2153,7 @@ def create_prompt(
21522153
self,
21532154
*,
21542155
name: str,
2155-
prompt: Union[str, List[ChatMessageDict]],
2156+
prompt: Union[str, List[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict]]],
21562157
labels: List[str] = [],
21572158
tags: Optional[List[str]] = None,
21582159
type: Optional[Literal["chat", "text"]] = "text",

langfuse/model.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,10 @@ class ChatMessageWithPlaceholdersDict_Message(TypedDict):
5858
type: Literal["message"]
5959
role: str
6060
content: str
61-
name: None
6261

6362

6463
class ChatMessageWithPlaceholdersDict_Placeholder(TypedDict):
6564
type: Literal["placeholder"]
66-
role: None
67-
content: None
6865
name: str
6966

7067

@@ -235,8 +232,6 @@ def __init__(self, prompt: Prompt_Chat, is_fallback: bool = False):
235232
self.prompt.append(
236233
ChatMessageWithPlaceholdersDict_Placeholder(
237234
type="placeholder",
238-
role=None,
239-
content=None,
240235
name=p.name,
241236
)
242237
)
@@ -246,7 +241,6 @@ def __init__(self, prompt: Prompt_Chat, is_fallback: bool = False):
246241
type="message",
247242
role=p.role,
248243
content=p.content,
249-
name=None,
250244
)
251245
)
252246

@@ -280,6 +274,7 @@ def __eq__(self, other):
280274
and all(
281275
m1["role"] == m2["role"] and m1["content"] == m2["content"]
282276
for m1, m2 in zip(self.prompt, other.prompt)
277+
if m1["type"] == "message" and m2["type"] == "message"
283278
)
284279
and self.config == other.config
285280
)
@@ -289,7 +284,7 @@ def __eq__(self, other):
289284
def compileWithPlaceholders(
290285
self,
291286
variables: Dict[str, Any],
292-
placeholders: Dict[str, List[ChatMessage]],
287+
placeholders: Dict[str, List[ChatMessageDict]],
293288
) -> List[ChatMessageDict]:
294289
"""Compile chat prompt by first replacing placeholders, then expanding variables.
295290
@@ -300,15 +295,15 @@ def compileWithPlaceholders(
300295
Returns:
301296
List[ChatMessageDict]: Compiled chat messages
302297
"""
303-
messages_with_placeholders_replaced: List[ChatMessage] = []
298+
messages_with_placeholders_replaced: List[ChatMessageDict] = []
304299

305300
# Subsitute the placeholders for their supplied ChatMessages
306301
for item in self.prompt:
307302
if item["type"] == "placeholder" and item["name"] in placeholders:
308303
messages_with_placeholders_replaced.extend(placeholders[item["name"]])
309304
elif item["type"] == "message":
310305
messages_with_placeholders_replaced.append(
311-
ChatMessage(
306+
ChatMessageDict(
312307
role=item["role"],
313308
content=item["content"],
314309
)
@@ -318,9 +313,9 @@ def compileWithPlaceholders(
318313
return [
319314
ChatMessageDict(
320315
content=TemplateParser.compile_template(
321-
chat_message.content, variables,
316+
chat_message["content"], variables,
322317
),
323-
role=chat_message.role,
318+
role=chat_message["role"],
324319
)
325320
for chat_message in messages_with_placeholders_replaced
326321
if hasattr(chat_message, "role") and hasattr(chat_message, "content")

tests/test_prompt.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,57 @@ def test_create_chat_prompt():
9090
assert prompt_client.config == {}
9191

9292

93+
def test_create_chat_prompt_with_placeholders():
94+
langfuse = Langfuse()
95+
prompt_name = create_uuid()
96+
97+
prompt_client = langfuse.create_prompt(
98+
name=prompt_name,
99+
prompt=[
100+
{"role": "system", "content": "You are a {{role}} assistant"},
101+
{"type": "placeholder", "name": "history"},
102+
{"role": "user", "content": "Help me with {{task}}"},
103+
],
104+
labels=["production"],
105+
tags=["test"],
106+
type="chat",
107+
commit_message="initial commit",
108+
)
109+
110+
second_prompt_client = langfuse.get_prompt(prompt_name, type="chat")
111+
112+
messages = second_prompt_client.compileWithPlaceholders(
113+
variables={"role": "helpful", "task": "coding"},
114+
placeholders={
115+
"history": [
116+
{"role": "user", "content": "Example: {{task}}"},
117+
{"role": "assistant", "content": "Example response"},
118+
],
119+
},
120+
)
121+
122+
# Create a test generation using compiled messages
123+
completion = openai.OpenAI().chat.completions.create(
124+
model="gpt-4",
125+
messages=messages,
126+
)
127+
128+
assert len(completion.choices) > 0
129+
assert len(messages) == 4
130+
assert messages[0]["content"] == "You are a helpful assistant"
131+
assert messages[1]["content"] == "Example: coding"
132+
assert messages[2]["content"] == "Example response"
133+
assert messages[3]["content"] == "Help me with coding"
134+
135+
assert prompt_client.name == second_prompt_client.name
136+
assert prompt_client.version == second_prompt_client.version
137+
assert prompt_client.config == second_prompt_client.config
138+
assert prompt_client.labels == ["production", "latest"]
139+
assert prompt_client.tags == second_prompt_client.tags
140+
assert prompt_client.commit_message == second_prompt_client.commit_message
141+
assert prompt_client.config == {}
142+
143+
93144
def test_compiling_chat_prompt():
94145
langfuse = Langfuse()
95146
prompt_name = create_uuid()
@@ -1114,3 +1165,119 @@ def test_update_prompt():
11141165
expected_labels = sorted(["latest", "doe", "production", "john"])
11151166
assert sorted(fetched_prompt.labels) == expected_labels
11161167
assert sorted(updated_prompt.labels) == expected_labels
1168+
1169+
1170+
def test_compile_with_placeholders():
1171+
"""Test compileWithPlaceholders method with chat prompt."""
1172+
langfuse = Langfuse()
1173+
prompt_name = create_uuid()
1174+
1175+
prompt_client = langfuse.create_prompt(
1176+
name=prompt_name,
1177+
prompt=[
1178+
{"role": "system", "content": "You are a {{role}} assistant"},
1179+
{"role": "user", "content": "Help me with {{task}}"},
1180+
],
1181+
labels=["production"],
1182+
type="chat",
1183+
)
1184+
1185+
second_prompt_client = langfuse.get_prompt(prompt_name, type="chat")
1186+
1187+
# Test compileWithPlaceholders with no placeholders (should work like compile)
1188+
result = second_prompt_client.compileWithPlaceholders(
1189+
variables={"role": "helpful", "task": "coding"}, placeholders={}
1190+
)
1191+
1192+
assert len(result) == 2
1193+
assert result[0]["role"] == "system"
1194+
assert result[0]["content"] == "You are a helpful assistant"
1195+
assert result[1]["role"] == "user"
1196+
assert result[1]["content"] == "Help me with coding"
1197+
1198+
1199+
def test_compile_with_placeholders_valid():
1200+
"""Test compileWithPlaceholders with valid placeholders."""
1201+
from langfuse.api.resources.prompts.types import PromptChatMessage
1202+
1203+
langfuse = Langfuse()
1204+
prompt_name = create_uuid()
1205+
1206+
# Create prompt with placeholders
1207+
langfuse.create_prompt(
1208+
name=prompt_name,
1209+
prompt=[
1210+
{"role": "system", "content": "You are a {{role}} assistant"},
1211+
{"type": "placeholder", "name": "examples"},
1212+
{"role": "user", "content": "Help me with {{task}}"},
1213+
],
1214+
type="chat",
1215+
)
1216+
1217+
prompt_client = langfuse.get_prompt(prompt_name, type="chat")
1218+
1219+
# Test compileWithPlaceholders with valid placeholders
1220+
result = prompt_client.compileWithPlaceholders(
1221+
variables={"role": "helpful", "task": "coding"},
1222+
placeholders={
1223+
"examples": [
1224+
PromptChatMessage(role="user", content="Example: {{task}}"),
1225+
PromptChatMessage(role="assistant", content="Example response"),
1226+
]
1227+
},
1228+
)
1229+
1230+
assert len(result) == 4
1231+
assert result[0]["content"] == "You are a helpful assistant"
1232+
assert result[1]["content"] == "Example: coding"
1233+
assert result[2]["content"] == "Example response"
1234+
assert result[3]["content"] == "Help me with coding"
1235+
1236+
1237+
def test_create_prompt_with_placeholders():
1238+
"""Test creating a prompt with placeholder messages."""
1239+
langfuse = Langfuse()
1240+
prompt_name = create_uuid()
1241+
1242+
prompt_client = langfuse.create_prompt(
1243+
name=prompt_name,
1244+
prompt=[
1245+
{"role": "system", "content": "System message"},
1246+
{"type": "placeholder", "name": "context"},
1247+
{"role": "user", "content": "User message"},
1248+
],
1249+
type="chat",
1250+
)
1251+
1252+
# Verify prompt_with_placeholders structure
1253+
assert len(prompt_client.prompt_with_placeholders) == 3
1254+
assert prompt_client.prompt_with_placeholders[0]["type"] == "message"
1255+
assert prompt_client.prompt_with_placeholders[1]["type"] == "placeholder"
1256+
assert prompt_client.prompt_with_placeholders[1]["name"] == "context"
1257+
assert prompt_client.prompt_with_placeholders[2]["type"] == "message"
1258+
1259+
# Regular prompt should only contain messages
1260+
assert len(prompt_client.prompt) == 2
1261+
1262+
1263+
def test_get_prompt_with_placeholders():
1264+
"""Test retrieving a prompt with placeholders."""
1265+
langfuse = Langfuse()
1266+
prompt_name = create_uuid()
1267+
1268+
langfuse.create_prompt(
1269+
name=prompt_name,
1270+
prompt=[
1271+
{"role": "system", "content": "You are {{name}}"},
1272+
{"type": "placeholder", "name": "history"},
1273+
{"role": "user", "content": "{{question}}"},
1274+
],
1275+
type="chat",
1276+
)
1277+
1278+
prompt_client = langfuse.get_prompt(prompt_name, type="chat")
1279+
1280+
# Verify placeholder structure is preserved
1281+
assert len(prompt_client.prompt_with_placeholders) == 3
1282+
assert prompt_client.prompt_with_placeholders[1]["type"] == "placeholder"
1283+
assert prompt_client.prompt_with_placeholders[1]["name"] == "history"

0 commit comments

Comments
 (0)