Source code for sherpa_ai.prompts.prompt_template_loader
"""Prompt template loading and formatting module for Sherpa AI.
This module provides functionality for loading prompt templates and formatting
them with variables. It extends the base PromptLoader to add variable
substitution capabilities for different prompt types.
"""
from typing import Dict, List, Optional, Union
from sherpa_ai.prompts.Base import ChatPromptVersion, TextPromptVersion, JsonPromptVersion
from sherpa_ai.prompts.prompt_loader import PromptLoader
[docs]
class PromptTemplate(PromptLoader):
"""Template loader and formatter for prompts.
This class extends PromptLoader to add variable substitution capabilities.
It can format text, chat, and JSON prompts by replacing placeholders
with actual values.
Example:
>>> template = PromptTemplate("prompts.json")
>>> formatted = template.format_prompt(
... prompt_parent_id="chat",
... prompt_id="greeting",
... version="1.0",
... variables={"name": "Alice"}
... )
>>> print(formatted[0]["content"])
'Hello Alice!'
"""
def __init__(self, json_file_path: str):
"""Initialize the prompt template loader.
Args:
json_file_path (str): Path to JSON file containing prompt templates.
"""
super().__init__(json_file_path)
[docs]
def format_prompt(
self,
prompt_parent_id: str,
prompt_id: str, # Changed from name to prompt_id
version: str,
variables: Optional[Dict[str, Union[str, int, float]]] = None
) -> Optional[Union[str, List[Dict[str, str]], Dict]]:
"""Format a prompt by replacing variables with values.
This method loads a prompt template and replaces any variable
placeholders with provided values. It handles different prompt types
(text, chat, JSON) appropriately.
Args:
prompt_parent_id (str): Name of the wrapper containing the prompt.
prompt_id (str): ID of the prompt to format.
version (str): Version of the prompt to format.
variables (Optional[Dict[str, Union[str, int, float]]]): Values to
substitute in the prompt. If None, uses defaults from JSON.
Returns:
Optional[Union[str, List[Dict[str, str]], Dict]]: Formatted prompt
content if found and successfully formatted, None otherwise.
Example:
>>> template = PromptTemplate("prompts.json")
>>> formatted = template.format_prompt(
... prompt_parent_id="text",
... prompt_id="search",
... version="1.0",
... variables={"query": "python programming"}
... )
>>> print(formatted)
'Search for: python programming'
"""
# We need to get the specific prompt version object
prompt_version_obj = self.get_prompt_version(prompt_parent_id, prompt_id, version)
if not prompt_version_obj:
return None
prompt_variables = prompt_version_obj.variables or {}
final_variables = prompt_variables.copy()
if variables:
final_variables.update(variables)
if isinstance(prompt_version_obj, ChatPromptVersion):
formatted_prompt = []
for message in prompt_version_obj.content:
role = message.get("role")
text = message.get("content", "")
# Replace placeholders with variables
for var_name, var_value in final_variables.items():
placeholder = f"{{{var_name}}}"
if placeholder in text:
text = text.replace(placeholder, str(var_value))
formatted_prompt.append({"role": role, "content": text})
return formatted_prompt
elif isinstance(prompt_version_obj, TextPromptVersion):
text = prompt_version_obj.content
for var_name, var_value in final_variables.items():
placeholder = f"{{{var_name}}}"
if placeholder in text:
text = text.replace(placeholder, str(var_value))
return text
elif isinstance(prompt_version_obj, JsonPromptVersion):
import copy
formatted_prompt = copy.deepcopy(prompt_version_obj.content)
def replace_in_dict(data: Dict) -> Dict:
"""Recursively replace variables in dictionary values.
Args:
data (Dict): Dictionary to process.
Returns:
Dict: Dictionary with variables replaced.
"""
for key, value in data.items():
if isinstance(value, str):
for var_name, var_value in final_variables.items():
placeholder = f"{{{var_name}}}"
if placeholder in value:
data[key] = value.replace(placeholder, str(var_value))
elif isinstance(value, dict):
data[key] = replace_in_dict(value)
return data
return replace_in_dict(formatted_prompt)
else:
raise ValueError(f"Unknown prompt version type: {type(prompt_version_obj)}")
[docs]
def get_full_formatted_prompt(
self,
prompt_parent_id: str,
prompt_id: str, # Changed from name to prompt_id
version: str,
variables: Optional[Dict[str, Union[str, int, float]]] = None
) -> Optional[Dict[str, Union[str, List[Dict[str, str]], Dict]]]:
"""Get a formatted prompt with metadata.
This method formats a prompt and returns it along with its description
and output schema. It's useful when you need the complete prompt
context, not just the formatted content.
Args:
prompt_parent_id (str): Name of the wrapper containing the prompt.
prompt_id (str): ID of the prompt to format.
version (str): Version of the prompt to format.
variables (Optional[Dict[str, Union[str, int, float]]]): Values to
substitute in the prompt. If None, uses defaults from JSON.
Returns:
Optional[Dict[str, Union[str, List[Dict[str, str]], Dict]]]:
Dictionary containing formatted content, description, and schema,
or None if prompt not found.
Example:
>>> template = PromptTemplate("prompts.json")
>>> result = template.get_full_formatted_prompt(
... prompt_parent_id="text",
... prompt_id="search",
... version="1.0",
... variables={"query": "python"}
... )
>>> print(result["description"])
'Search query template'
"""
target_prompt = None
for pg in self.prompts:
if pg.prompt_parent_id == prompt_parent_id:
for p in pg.prompts:
if p.prompt_id == prompt_id:
target_prompt = p
break
if target_prompt:
break
if not target_prompt:
return None
prompt_version_obj = self.get_prompt_version(prompt_parent_id, prompt_id, version)
if not prompt_version_obj:
return None
formatted_content = self.format_prompt(prompt_parent_id, prompt_id, version, variables)
if not formatted_content:
return None
return {
"description": target_prompt.description,
"content": formatted_content,
"output_schema": prompt_version_obj.response_format
}