diff --git a/letta/services/helpers/agent_manager_helper.py b/letta/services/helpers/agent_manager_helper.py index 7e4cd7c1..0b32dcbe 100644 --- a/letta/services/helpers/agent_manager_helper.py +++ b/letta/services/helpers/agent_manager_helper.py @@ -118,6 +118,27 @@ def compile_memory_metadata_block( return memory_metadata_block +class PreserveMapping(dict): + """Used to preserve (do not modify) undefined variables in the system prompt""" + + def __missing__(self, key): + return "{" + key + "}" + + +def safe_format(template: str, variables: dict) -> str: + """ + Safely formats a template string, preserving empty {} and {unknown_vars} + while substituting known variables. + + If we simply use {} in format_map, it'll be treated as a positional field + """ + # First escape any empty {} by doubling them + escaped = template.replace("{}", "{{}}") + + # Now use format_map with our custom mapping + return escaped.format_map(PreserveMapping(variables)) + + def compile_system_message( system_prompt: str, in_context_memory: Memory, @@ -169,7 +190,8 @@ def compile_system_message( # render the variables using the built-in templater try: - formatted_prompt = system_prompt.format_map(variables) + formatted_prompt = safe_format(system_prompt, variables) + print(f"Formatted system prompt:\n{formatted_prompt}") except Exception as e: raise ValueError(f"Failed to format system prompt - {str(e)}. System prompt value:\n{system_prompt}") diff --git a/tests/test_system_prompt_compiler.py b/tests/test_system_prompt_compiler.py new file mode 100644 index 00000000..d7423603 --- /dev/null +++ b/tests/test_system_prompt_compiler.py @@ -0,0 +1,59 @@ +from letta.services.helpers.agent_manager_helper import safe_format + +CORE_MEMORY_VAR = "My core memory is that I like to eat bananas" +VARS_DICT = {"CORE_MEMORY": CORE_MEMORY_VAR} + + +def test_formatter(): + + # Example system prompt that has no vars + NO_VARS = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + """ + + assert NO_VARS == safe_format(NO_VARS, VARS_DICT) + + # Example system prompt that has {CORE_MEMORY} + CORE_MEMORY_VAR = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + {CORE_MEMORY} + """ + + CORE_MEMORY_VAR_SOL = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + My core memory is that I like to eat bananas + """ + + assert CORE_MEMORY_VAR_SOL == safe_format(CORE_MEMORY_VAR, VARS_DICT) + + # Example system prompt that has {CORE_MEMORY} and {USER_MEMORY} (latter doesn't exist) + UNUSED_VAR = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + {USER_MEMORY} + {CORE_MEMORY} + """ + + UNUSED_VAR_SOL = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + {USER_MEMORY} + My core memory is that I like to eat bananas + """ + + assert UNUSED_VAR_SOL == safe_format(UNUSED_VAR, VARS_DICT) + + # Example system prompt that has {CORE_MEMORY} and {USER_MEMORY} (latter doesn't exist), AND an empty {} + UNUSED_AND_EMPRY_VAR = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + {} + {USER_MEMORY} + {CORE_MEMORY} + """ + + UNUSED_AND_EMPRY_VAR_SOL = """ + THIS IS A SYSTEM PROMPT WITH NO VARS + {} + {USER_MEMORY} + My core memory is that I like to eat bananas + """ + + assert UNUSED_AND_EMPRY_VAR_SOL == safe_format(UNUSED_AND_EMPRY_VAR, VARS_DICT)