import re from dataclasses import asdict, is_dataclass from typing import Any, Dict, Optional, Union import jinja2 from pydantic import BaseModel class PromptTemplate: """prompt templates. Args: template (str): The template string. variables (Optional[Union[Dict[str, str], BaseModel, Any]]): Variables for the template. format_type (str): The format type of the template ('json' or 'jinja'). """ def __init__(self, template: str, format_type: str = 'json') -> None: self.template = template self.format_type = format_type def _convert_to_dict( self, variables: Optional[Union[Dict[str, str], BaseModel, Any]] ) -> Dict[str, str]: """ Convert variables to a dictionary. Args: variables (Optional[Union[Dict[str, str], BaseModel, Any]]): Variables to convert. Returns: Dict[str, str]: The converted dictionary. Raises: ValueError: If the variables type is unsupported. """ if variables is None: return {} if isinstance(variables, BaseModel): return variables.dict() if is_dataclass(variables): return asdict(variables) if isinstance(variables, dict): return variables raise ValueError( 'Unsupported variables type. Must be a dict, BaseModel, or ' 'dataclass.') def parse_template(self, template: str) -> Dict[str, str]: """ Extract variables from the template. Args: template (str): The template string. Returns: Dict[str, str]: A dictionary of variables with None values. """ if self.format_type == 'jinja': variables = re.findall(r'\{\{(.*?)\}\}', template) elif self.format_type == 'json': variables = re.findall(r'\{(.*?)\}', template) variables = [var for var in variables if '{' not in var] else: variables = [] return {var.strip(): None for var in variables} def format_json(self, template: str, variables: Dict[str, str]) -> str: """ Format the JSON template. Args: template (str): The JSON template string. variables (Dict[str, str]): The variables to fill in the template. Returns: str: The formatted JSON string. Raises: ValueError: If the template is not a valid JSON. """ try: return template.format(**variables) except KeyError as e: raise ValueError('Invalid JSON template') from e def format_jinja(self, template: str, variables: Dict[str, str]) -> str: """ Format the Jinja template. Args: template (str): The Jinja template string. variables (Dict[str, str]): The variables to fill in the template. Returns: str: The formatted Jinja string. Raises: ValueError: If the template is not a valid Jinja template. """ try: jinja_template = jinja2.Template(template) return jinja_template.render(variables) except jinja2.TemplateError as e: raise ValueError('Invalid Jinja template') from e def _update_variables_with_info(self) -> Dict[str, str]: """ Update variables dictionary with action_info and agents_info. Returns: Dict[str, str]: The updated variables dictionary. """ variables = self.variables.copy() if 'action_info' not in variables and self.actions_info: variables['action_info'] = self.actions_info if 'agents_info' not in variables and self.agents_info: variables['agents_info'] = self.agents_info return variables def _check_variables_match(self, parsed_variables: Dict[str, str], variables: Dict[str, str]) -> None: """ Check if all keys in variables are present in parsed_variables. Args: parsed_variables (Dict[str, str]): The parsed variables from the template. variables (Dict[str, str]): The variables to check. Raises: ValueError: If any key in variables is not present in parsed_variables. """ if not all(key in parsed_variables for key in variables.keys()): raise ValueError( 'Variables keys do not match the template variables') def format( self, **kwargs: Optional[Union[Dict[str, str], BaseModel, Any]], ) -> Any: self.variables = kwargs return str(self) def __str__(self) -> Any: """ Call the template formatting based on format_type. Returns: Any: The formatted template. Raises: ValueError: If the format_type is unsupported. """ parsed_variables = self.parse_template(self.template) updated_variables = self._update_variables_with_info() self._check_variables_match(parsed_variables, updated_variables) if self.format_type == 'json': return self.format_json(self.template, updated_variables) elif self.format_type == 'jinja': return self.format_jinja(self.template, updated_variables) else: raise ValueError('Unsupported format type') @property def actions_info(self) -> Optional[Dict[str, Any]]: """Get the action information.""" return getattr(self, '_action_info', None) @actions_info.setter def actions_info(self, value: Dict[str, Any]) -> None: """Set the action information.""" self._action_info = value @property def agents_info(self) -> Optional[Dict[str, Any]]: """Get the agent information.""" return getattr(self, '_agents_info', None) @agents_info.setter def agents_info(self, value: Dict[str, Any]) -> None: """Set the agent information.""" self._agents_info = value