22
33import re
44from abc import ABC , abstractmethod
5- from typing import Any , Dict , List , Optional , Tuple , TypedDict , Union
5+ from typing import Any , Dict , List , Literal , Optional , Tuple , TypedDict , Union
66
77from langfuse .api .resources .commons .types .dataset import (
88 Dataset , # noqa: F401
@@ -54,6 +54,26 @@ class ChatMessageDict(TypedDict):
5454 content : str
5555
5656
57+ class ChatMessageWithPlaceholdersDict_Message (TypedDict ):
58+ type : Literal ["message" ]
59+ role : str
60+ content : str
61+ name : None
62+
63+
64+ class ChatMessageWithPlaceholdersDict_Placeholder (TypedDict ):
65+ type : Literal ["placeholder" ]
66+ role : None
67+ content : None
68+ name : str
69+
70+
71+ ChatMessageWithPlaceholdersDict = Union [
72+ ChatMessageWithPlaceholdersDict_Message ,
73+ ChatMessageWithPlaceholdersDict_Placeholder ,
74+ ]
75+
76+
5777class TemplateParser :
5878 OPENING = "{{"
5979 CLOSING = "}}"
@@ -208,9 +228,27 @@ def get_langchain_prompt(self, **kwargs) -> str:
208228class ChatPromptClient (BasePromptClient ):
209229 def __init__ (self , prompt : Prompt_Chat , is_fallback : bool = False ):
210230 super ().__init__ (prompt , is_fallback )
211- self .prompt = [
212- ChatMessageDict (role = p .role , content = p .content ) for p in prompt .prompt
213- ]
231+ self .prompt : List [ChatMessageWithPlaceholdersDict ] = []
232+
233+ for p in prompt .prompt :
234+ if hasattr (p , "type" ) and p .type == "placeholder" :
235+ self .prompt .append (
236+ ChatMessageWithPlaceholdersDict_Placeholder (
237+ type = "placeholder" ,
238+ role = None ,
239+ content = None ,
240+ name = p .name ,
241+ )
242+ )
243+ elif hasattr (p , "type" ) and p .type == "message" :
244+ self .prompt .append (
245+ ChatMessageWithPlaceholdersDict_Message (
246+ type = "message" ,
247+ role = p .role ,
248+ content = p .content ,
249+ name = None ,
250+ )
251+ )
214252
215253 def compile (self , ** kwargs ) -> List [ChatMessageDict ]:
216254 return [
@@ -221,6 +259,7 @@ def compile(self, **kwargs) -> List[ChatMessageDict]:
221259 role = chat_message ["role" ],
222260 )
223261 for chat_message in self .prompt
262+ if chat_message ["type" ] == "message"
224263 ]
225264
226265 @property
@@ -229,6 +268,7 @@ def variables(self) -> List[str]:
229268 return [
230269 variable
231270 for chat_message in self .prompt
271+ if chat_message ["type" ] == "message"
232272 for variable in TemplateParser .find_variable_names (chat_message ["content" ])
233273 ]
234274
@@ -246,6 +286,47 @@ def __eq__(self, other):
246286
247287 return False
248288
289+ def compileWithPlaceholders (
290+ self ,
291+ variables : Dict [str , Any ],
292+ placeholders : Dict [str , List [ChatMessage ]],
293+ ) -> List [ChatMessageDict ]:
294+ """Compile chat prompt by first replacing placeholders, then expanding variables.
295+
296+ Args:
297+ variables: Dictionary of variable names to values for template substitution
298+ placeholders: Dictionary of placeholder names to lists of ChatMessage objects
299+
300+ Returns:
301+ List[ChatMessageDict]: Compiled chat messages
302+ """
303+ messages_with_placeholders_replaced : List [ChatMessage ] = []
304+
305+ # Subsitute the placeholders for their supplied ChatMessages
306+ for item in self .prompt :
307+ if item ["type" ] == "placeholder" and item ["name" ] in placeholders :
308+ messages_with_placeholders_replaced .extend (placeholders [item ["name" ]])
309+ elif item ["type" ] == "message" :
310+ messages_with_placeholders_replaced .append (
311+ ChatMessage (
312+ role = item ["role" ],
313+ content = item ["content" ],
314+ )
315+ )
316+
317+ # Then, replace the variables in the ChatMessage content.
318+ return [
319+ ChatMessageDict (
320+ content = TemplateParser .compile_template (
321+ chat_message .content , variables ,
322+ ),
323+ role = chat_message .role ,
324+ )
325+ for chat_message in messages_with_placeholders_replaced
326+ if hasattr (chat_message , "role" ) and hasattr (chat_message , "content" )
327+ ]
328+
329+
249330 def get_langchain_prompt (self , ** kwargs ):
250331 """Convert Langfuse prompt into string compatible with Langchain ChatPromptTemplate.
251332
@@ -269,6 +350,7 @@ def get_langchain_prompt(self, **kwargs):
269350 ),
270351 )
271352 for msg in self .prompt
353+ if msg ["type" ] == "message"
272354 ]
273355
274356
0 commit comments