diff --git a/langfuse/model.py b/langfuse/model.py index 69d721597..348969d1e 100644 --- a/langfuse/model.py +++ b/langfuse/model.py @@ -163,7 +163,12 @@ def get_langchain_prompt(self) -> Any: def _get_langchain_prompt_string(content: str) -> str: json_escaped_content = BasePromptClient._escape_json_for_langchain(content) - return re.sub(r"{{\s*(\w+)\s*}}", r"{\g<1>}", json_escaped_content) + # Match any Langfuse variable name between {{ }} (Langfuse allows names + # with hyphens, spaces, unicode, etc.), not just \w+. The character class + # excludes braces and both quote styles so already-escaped JSON (which + # _escape_json_for_langchain doubles to {{"...}} or {{'...}}) is left + # untouched. + return re.sub(r"{{\s*([^{}\"']+?)\s*}}", r"{\g<1>}", json_escaped_content) @staticmethod def _escape_json_for_langchain(text: str) -> str: diff --git a/tests/unit/test_prompt_compilation.py b/tests/unit/test_prompt_compilation.py index 1b96a14dd..a010eae24 100644 --- a/tests/unit/test_prompt_compilation.py +++ b/tests/unit/test_prompt_compilation.py @@ -255,6 +255,48 @@ def test_normal_variables_with_nested_json(self): assert formatted_prompt == expected + def test_get_langchain_prompt_preserves_special_variable_names(self): + """Variable names with hyphens/spaces/unicode must convert to langchain + single-brace placeholders instead of being left as unusable {{...}}.""" + prompt = TextPromptClient( + Prompt_Text( + type="text", + name="special_var_names", + version=1, + config={}, + tags=[], + labels=[], + prompt="Hello {{user-name}}!", + ) + ) + + langchain_prompt_string = prompt.get_langchain_prompt() + + assert langchain_prompt_string == "Hello {user-name}!" + + langchain_prompt = PromptTemplate.from_template(langchain_prompt_string) + assert langchain_prompt.input_variables == ["user-name"] + assert langchain_prompt.format(**{"user-name": "Ada"}) == "Hello Ada!" + + def test_get_langchain_prompt_leaves_quoted_json_escaped(self): + """Brace-pairs wrapping JSON (single- or double-quoted) must stay doubled + for langchain, not be converted as if they were variables.""" + prompt = TextPromptClient( + Prompt_Text( + type="text", + name="json_braces", + version=1, + config={}, + tags=[], + labels=[], + prompt="Reply with {'key': 'value'} and {{name}}", + ) + ) + + result = prompt.get_langchain_prompt() + + assert result == "Reply with {{'key': 'value'}} and {name}" + def test_mixed_variables_with_nested_json(self): """Test normal variables (double braces) and Langchain variables (single braces) with nested JSON.""" prompt_string = """Normal variable: {{user_name}}