langfuse.model
@private
1"""@private""" 2 3import re 4from abc import ABC, abstractmethod 5from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union 6 7from langfuse.api.resources.commons.types.dataset import ( 8 Dataset, # noqa: F401 9) 10 11# these imports need to stay here, otherwise imports from our clients wont work 12from langfuse.api.resources.commons.types.dataset_item import DatasetItem # noqa: F401 13 14# noqa: F401 15from langfuse.api.resources.commons.types.dataset_run import DatasetRun # noqa: F401 16 17# noqa: F401 18from langfuse.api.resources.commons.types.dataset_status import ( # noqa: F401 19 DatasetStatus, 20) 21from langfuse.api.resources.commons.types.map_value import MapValue # noqa: F401 22from langfuse.api.resources.commons.types.observation import Observation # noqa: F401 23from langfuse.api.resources.commons.types.trace_with_full_details import ( # noqa: F401 24 TraceWithFullDetails, 25) 26 27# noqa: F401 28from langfuse.api.resources.dataset_items.types.create_dataset_item_request import ( # noqa: F401 29 CreateDatasetItemRequest, 30) 31from langfuse.api.resources.dataset_run_items.types.create_dataset_run_item_request import ( # noqa: F401 32 CreateDatasetRunItemRequest, 33) 34 35# noqa: F401 36from langfuse.api.resources.datasets.types.create_dataset_request import ( # noqa: F401 37 CreateDatasetRequest, 38) 39from langfuse.api.resources.prompts import ChatMessage, Prompt, Prompt_Chat, Prompt_Text 40 41 42class ModelUsage(TypedDict): 43 unit: Optional[str] 44 input: Optional[int] 45 output: Optional[int] 46 total: Optional[int] 47 input_cost: Optional[float] 48 output_cost: Optional[float] 49 total_cost: Optional[float] 50 51 52class ChatMessageDict(TypedDict): 53 role: str 54 content: str 55 56 57class TemplateParser: 58 OPENING = "{{" 59 CLOSING = "}}" 60 61 @staticmethod 62 def _parse_next_variable( 63 content: str, start_idx: int 64 ) -> Optional[Tuple[str, int, int]]: 65 """Returns (variable_name, start_pos, end_pos) or None if no variable found""" 66 var_start = content.find(TemplateParser.OPENING, start_idx) 67 if var_start == -1: 68 return None 69 70 var_end = content.find(TemplateParser.CLOSING, var_start) 71 if var_end == -1: 72 return None 73 74 variable_name = content[ 75 var_start + len(TemplateParser.OPENING) : var_end 76 ].strip() 77 return (variable_name, var_start, var_end + len(TemplateParser.CLOSING)) 78 79 @staticmethod 80 def find_variable_names(content: str) -> List[str]: 81 names = [] 82 curr_idx = 0 83 84 while curr_idx < len(content): 85 result = TemplateParser._parse_next_variable(content, curr_idx) 86 if not result: 87 break 88 names.append(result[0]) 89 curr_idx = result[2] 90 91 return names 92 93 @staticmethod 94 def compile_template(content: str, data: Optional[Dict[str, Any]] = None) -> str: 95 if data is None: 96 return content 97 98 result_list = [] 99 curr_idx = 0 100 101 while curr_idx < len(content): 102 result = TemplateParser._parse_next_variable(content, curr_idx) 103 104 if not result: 105 result_list.append(content[curr_idx:]) 106 break 107 108 variable_name, var_start, var_end = result 109 result_list.append(content[curr_idx:var_start]) 110 111 if variable_name in data: 112 result_list.append( 113 str(data[variable_name]) if data[variable_name] is not None else "" 114 ) 115 else: 116 result_list.append(content[var_start:var_end]) 117 118 curr_idx = var_end 119 120 return "".join(result_list) 121 122 123class BasePromptClient(ABC): 124 name: str 125 version: int 126 config: Dict[str, Any] 127 labels: List[str] 128 tags: List[str] 129 commit_message: Optional[str] 130 131 def __init__(self, prompt: Prompt, is_fallback: bool = False): 132 self.name = prompt.name 133 self.version = prompt.version 134 self.config = prompt.config 135 self.labels = prompt.labels 136 self.tags = prompt.tags 137 self.commit_message = prompt.commit_message 138 self.is_fallback = is_fallback 139 140 @abstractmethod 141 def compile(self, **kwargs) -> Union[str, List[ChatMessage]]: 142 pass 143 144 @property 145 @abstractmethod 146 def variables(self) -> List[str]: 147 pass 148 149 @abstractmethod 150 def __eq__(self, other): 151 pass 152 153 @abstractmethod 154 def get_langchain_prompt(self): 155 pass 156 157 @staticmethod 158 def _get_langchain_prompt_string(content: str): 159 return re.sub(r"{{\s*(\w+)\s*}}", r"{\g<1>}", content) 160 161 162class TextPromptClient(BasePromptClient): 163 def __init__(self, prompt: Prompt_Text, is_fallback: bool = False): 164 super().__init__(prompt, is_fallback) 165 self.prompt = prompt.prompt 166 167 def compile(self, **kwargs) -> str: 168 return TemplateParser.compile_template(self.prompt, kwargs) 169 170 @property 171 def variables(self) -> List[str]: 172 """Return all the variable names in the prompt template.""" 173 return TemplateParser.find_variable_names(self.prompt) 174 175 def __eq__(self, other): 176 if isinstance(self, other.__class__): 177 return ( 178 self.name == other.name 179 and self.version == other.version 180 and self.prompt == other.prompt 181 and self.config == other.config 182 ) 183 184 return False 185 186 def get_langchain_prompt(self, **kwargs) -> str: 187 """Convert Langfuse prompt into string compatible with Langchain PromptTemplate. 188 189 This method adapts the mustache-style double curly braces {{variable}} used in Langfuse 190 to the single curly brace {variable} format expected by Langchain. 191 192 kwargs: Optional keyword arguments to precompile the template string. Variables that match 193 the provided keyword arguments will be precompiled. Remaining variables must then be 194 handled by Langchain's prompt template. 195 196 Returns: 197 str: The string that can be plugged into Langchain's PromptTemplate. 198 """ 199 prompt = ( 200 TemplateParser.compile_template(self.prompt, kwargs) 201 if kwargs 202 else self.prompt 203 ) 204 205 return self._get_langchain_prompt_string(prompt) 206 207 208class ChatPromptClient(BasePromptClient): 209 def __init__(self, prompt: Prompt_Chat, is_fallback: bool = False): 210 super().__init__(prompt, is_fallback) 211 self.prompt = [ 212 ChatMessageDict(role=p.role, content=p.content) for p in prompt.prompt 213 ] 214 215 def compile(self, **kwargs) -> List[ChatMessageDict]: 216 return [ 217 ChatMessageDict( 218 content=TemplateParser.compile_template( 219 chat_message["content"], kwargs 220 ), 221 role=chat_message["role"], 222 ) 223 for chat_message in self.prompt 224 ] 225 226 @property 227 def variables(self) -> List[str]: 228 """Return all the variable names in the chat prompt template.""" 229 return [ 230 variable 231 for chat_message in self.prompt 232 for variable in TemplateParser.find_variable_names(chat_message["content"]) 233 ] 234 235 def __eq__(self, other): 236 if isinstance(self, other.__class__): 237 return ( 238 self.name == other.name 239 and self.version == other.version 240 and all( 241 m1["role"] == m2["role"] and m1["content"] == m2["content"] 242 for m1, m2 in zip(self.prompt, other.prompt) 243 ) 244 and self.config == other.config 245 ) 246 247 return False 248 249 def get_langchain_prompt(self, **kwargs): 250 """Convert Langfuse prompt into string compatible with Langchain ChatPromptTemplate. 251 252 It specifically adapts the mustache-style double curly braces {{variable}} used in Langfuse 253 to the single curly brace {variable} format expected by Langchain. 254 255 kwargs: Optional keyword arguments to precompile the template string. Variables that match 256 the provided keyword arguments will be precompiled. Remaining variables must then be 257 handled by Langchain's prompt template. 258 259 Returns: 260 List of messages in the format expected by Langchain's ChatPromptTemplate: (role, content) tuple. 261 """ 262 return [ 263 ( 264 msg["role"], 265 self._get_langchain_prompt_string( 266 TemplateParser.compile_template(msg["content"], kwargs) 267 if kwargs 268 else msg["content"] 269 ), 270 ) 271 for msg in self.prompt 272 ] 273 274 275PromptClient = Union[TextPromptClient, ChatPromptClient]
58class TemplateParser: 59 OPENING = "{{" 60 CLOSING = "}}" 61 62 @staticmethod 63 def _parse_next_variable( 64 content: str, start_idx: int 65 ) -> Optional[Tuple[str, int, int]]: 66 """Returns (variable_name, start_pos, end_pos) or None if no variable found""" 67 var_start = content.find(TemplateParser.OPENING, start_idx) 68 if var_start == -1: 69 return None 70 71 var_end = content.find(TemplateParser.CLOSING, var_start) 72 if var_end == -1: 73 return None 74 75 variable_name = content[ 76 var_start + len(TemplateParser.OPENING) : var_end 77 ].strip() 78 return (variable_name, var_start, var_end + len(TemplateParser.CLOSING)) 79 80 @staticmethod 81 def find_variable_names(content: str) -> List[str]: 82 names = [] 83 curr_idx = 0 84 85 while curr_idx < len(content): 86 result = TemplateParser._parse_next_variable(content, curr_idx) 87 if not result: 88 break 89 names.append(result[0]) 90 curr_idx = result[2] 91 92 return names 93 94 @staticmethod 95 def compile_template(content: str, data: Optional[Dict[str, Any]] = None) -> str: 96 if data is None: 97 return content 98 99 result_list = [] 100 curr_idx = 0 101 102 while curr_idx < len(content): 103 result = TemplateParser._parse_next_variable(content, curr_idx) 104 105 if not result: 106 result_list.append(content[curr_idx:]) 107 break 108 109 variable_name, var_start, var_end = result 110 result_list.append(content[curr_idx:var_start]) 111 112 if variable_name in data: 113 result_list.append( 114 str(data[variable_name]) if data[variable_name] is not None else "" 115 ) 116 else: 117 result_list.append(content[var_start:var_end]) 118 119 curr_idx = var_end 120 121 return "".join(result_list)
80 @staticmethod 81 def find_variable_names(content: str) -> List[str]: 82 names = [] 83 curr_idx = 0 84 85 while curr_idx < len(content): 86 result = TemplateParser._parse_next_variable(content, curr_idx) 87 if not result: 88 break 89 names.append(result[0]) 90 curr_idx = result[2] 91 92 return names
94 @staticmethod 95 def compile_template(content: str, data: Optional[Dict[str, Any]] = None) -> str: 96 if data is None: 97 return content 98 99 result_list = [] 100 curr_idx = 0 101 102 while curr_idx < len(content): 103 result = TemplateParser._parse_next_variable(content, curr_idx) 104 105 if not result: 106 result_list.append(content[curr_idx:]) 107 break 108 109 variable_name, var_start, var_end = result 110 result_list.append(content[curr_idx:var_start]) 111 112 if variable_name in data: 113 result_list.append( 114 str(data[variable_name]) if data[variable_name] is not None else "" 115 ) 116 else: 117 result_list.append(content[var_start:var_end]) 118 119 curr_idx = var_end 120 121 return "".join(result_list)
124class BasePromptClient(ABC): 125 name: str 126 version: int 127 config: Dict[str, Any] 128 labels: List[str] 129 tags: List[str] 130 commit_message: Optional[str] 131 132 def __init__(self, prompt: Prompt, is_fallback: bool = False): 133 self.name = prompt.name 134 self.version = prompt.version 135 self.config = prompt.config 136 self.labels = prompt.labels 137 self.tags = prompt.tags 138 self.commit_message = prompt.commit_message 139 self.is_fallback = is_fallback 140 141 @abstractmethod 142 def compile(self, **kwargs) -> Union[str, List[ChatMessage]]: 143 pass 144 145 @property 146 @abstractmethod 147 def variables(self) -> List[str]: 148 pass 149 150 @abstractmethod 151 def __eq__(self, other): 152 pass 153 154 @abstractmethod 155 def get_langchain_prompt(self): 156 pass 157 158 @staticmethod 159 def _get_langchain_prompt_string(content: str): 160 return re.sub(r"{{\s*(\w+)\s*}}", r"{\g<1>}", content)
Helper class that provides a standard way to create an ABC using inheritance.
163class TextPromptClient(BasePromptClient): 164 def __init__(self, prompt: Prompt_Text, is_fallback: bool = False): 165 super().__init__(prompt, is_fallback) 166 self.prompt = prompt.prompt 167 168 def compile(self, **kwargs) -> str: 169 return TemplateParser.compile_template(self.prompt, kwargs) 170 171 @property 172 def variables(self) -> List[str]: 173 """Return all the variable names in the prompt template.""" 174 return TemplateParser.find_variable_names(self.prompt) 175 176 def __eq__(self, other): 177 if isinstance(self, other.__class__): 178 return ( 179 self.name == other.name 180 and self.version == other.version 181 and self.prompt == other.prompt 182 and self.config == other.config 183 ) 184 185 return False 186 187 def get_langchain_prompt(self, **kwargs) -> str: 188 """Convert Langfuse prompt into string compatible with Langchain PromptTemplate. 189 190 This method adapts the mustache-style double curly braces {{variable}} used in Langfuse 191 to the single curly brace {variable} format expected by Langchain. 192 193 kwargs: Optional keyword arguments to precompile the template string. Variables that match 194 the provided keyword arguments will be precompiled. Remaining variables must then be 195 handled by Langchain's prompt template. 196 197 Returns: 198 str: The string that can be plugged into Langchain's PromptTemplate. 199 """ 200 prompt = ( 201 TemplateParser.compile_template(self.prompt, kwargs) 202 if kwargs 203 else self.prompt 204 ) 205 206 return self._get_langchain_prompt_string(prompt)
Helper class that provides a standard way to create an ABC using inheritance.
171 @property 172 def variables(self) -> List[str]: 173 """Return all the variable names in the prompt template.""" 174 return TemplateParser.find_variable_names(self.prompt)
Return all the variable names in the prompt template.
187 def get_langchain_prompt(self, **kwargs) -> str: 188 """Convert Langfuse prompt into string compatible with Langchain PromptTemplate. 189 190 This method adapts the mustache-style double curly braces {{variable}} used in Langfuse 191 to the single curly brace {variable} format expected by Langchain. 192 193 kwargs: Optional keyword arguments to precompile the template string. Variables that match 194 the provided keyword arguments will be precompiled. Remaining variables must then be 195 handled by Langchain's prompt template. 196 197 Returns: 198 str: The string that can be plugged into Langchain's PromptTemplate. 199 """ 200 prompt = ( 201 TemplateParser.compile_template(self.prompt, kwargs) 202 if kwargs 203 else self.prompt 204 ) 205 206 return self._get_langchain_prompt_string(prompt)
Convert Langfuse prompt into string compatible with Langchain PromptTemplate.
This method adapts the mustache-style double curly braces {{variable}} used in Langfuse to the single curly brace {variable} format expected by Langchain.
kwargs: Optional keyword arguments to precompile the template string. Variables that match the provided keyword arguments will be precompiled. Remaining variables must then be handled by Langchain's prompt template.
Returns:
str: The string that can be plugged into Langchain's PromptTemplate.
Inherited Members
209class ChatPromptClient(BasePromptClient): 210 def __init__(self, prompt: Prompt_Chat, is_fallback: bool = False): 211 super().__init__(prompt, is_fallback) 212 self.prompt = [ 213 ChatMessageDict(role=p.role, content=p.content) for p in prompt.prompt 214 ] 215 216 def compile(self, **kwargs) -> List[ChatMessageDict]: 217 return [ 218 ChatMessageDict( 219 content=TemplateParser.compile_template( 220 chat_message["content"], kwargs 221 ), 222 role=chat_message["role"], 223 ) 224 for chat_message in self.prompt 225 ] 226 227 @property 228 def variables(self) -> List[str]: 229 """Return all the variable names in the chat prompt template.""" 230 return [ 231 variable 232 for chat_message in self.prompt 233 for variable in TemplateParser.find_variable_names(chat_message["content"]) 234 ] 235 236 def __eq__(self, other): 237 if isinstance(self, other.__class__): 238 return ( 239 self.name == other.name 240 and self.version == other.version 241 and all( 242 m1["role"] == m2["role"] and m1["content"] == m2["content"] 243 for m1, m2 in zip(self.prompt, other.prompt) 244 ) 245 and self.config == other.config 246 ) 247 248 return False 249 250 def get_langchain_prompt(self, **kwargs): 251 """Convert Langfuse prompt into string compatible with Langchain ChatPromptTemplate. 252 253 It specifically adapts the mustache-style double curly braces {{variable}} used in Langfuse 254 to the single curly brace {variable} format expected by Langchain. 255 256 kwargs: Optional keyword arguments to precompile the template string. Variables that match 257 the provided keyword arguments will be precompiled. Remaining variables must then be 258 handled by Langchain's prompt template. 259 260 Returns: 261 List of messages in the format expected by Langchain's ChatPromptTemplate: (role, content) tuple. 262 """ 263 return [ 264 ( 265 msg["role"], 266 self._get_langchain_prompt_string( 267 TemplateParser.compile_template(msg["content"], kwargs) 268 if kwargs 269 else msg["content"] 270 ), 271 ) 272 for msg in self.prompt 273 ]
Helper class that provides a standard way to create an ABC using inheritance.
227 @property 228 def variables(self) -> List[str]: 229 """Return all the variable names in the chat prompt template.""" 230 return [ 231 variable 232 for chat_message in self.prompt 233 for variable in TemplateParser.find_variable_names(chat_message["content"]) 234 ]
Return all the variable names in the chat prompt template.
250 def get_langchain_prompt(self, **kwargs): 251 """Convert Langfuse prompt into string compatible with Langchain ChatPromptTemplate. 252 253 It specifically adapts the mustache-style double curly braces {{variable}} used in Langfuse 254 to the single curly brace {variable} format expected by Langchain. 255 256 kwargs: Optional keyword arguments to precompile the template string. Variables that match 257 the provided keyword arguments will be precompiled. Remaining variables must then be 258 handled by Langchain's prompt template. 259 260 Returns: 261 List of messages in the format expected by Langchain's ChatPromptTemplate: (role, content) tuple. 262 """ 263 return [ 264 ( 265 msg["role"], 266 self._get_langchain_prompt_string( 267 TemplateParser.compile_template(msg["content"], kwargs) 268 if kwargs 269 else msg["content"] 270 ), 271 ) 272 for msg in self.prompt 273 ]
Convert Langfuse prompt into string compatible with Langchain ChatPromptTemplate.
It specifically adapts the mustache-style double curly braces {{variable}} used in Langfuse to the single curly brace {variable} format expected by Langchain.
kwargs: Optional keyword arguments to precompile the template string. Variables that match the provided keyword arguments will be precompiled. Remaining variables must then be handled by Langchain's prompt template.
Returns:
List of messages in the format expected by Langchain's ChatPromptTemplate: (role, content) tuple.