[docs]defjinja2_formatter(template:str,/,**kwargs:Any)->str:"""Format a template using jinja2. *Security warning*: As of LangChain 0.0.329, this method uses Jinja2's SandboxedEnvironment by default. However, this sand-boxing should be treated as a best-effort approach rather than a guarantee of security. Do not accept jinja2 templates from untrusted sources as they may lead to arbitrary Python code execution. https://jinja.palletsprojects.com/en/3.1.x/sandbox/ Args: template: The template string. **kwargs: The variables to format the template with. Returns: The formatted string. Raises: ImportError: If jinja2 is not installed. """try:fromjinja2.sandboximportSandboxedEnvironmentexceptImportErrorase:msg=("jinja2 not installed, which is needed to use the jinja2_formatter. ""Please install it with `pip install jinja2`.""Please be cautious when using jinja2 templates. ""Do not expand jinja2 templates using unverified or user-controlled ""inputs as that can result in arbitrary Python code execution.")raiseImportError(msg)frome# This uses a sandboxed environment to prevent arbitrary code execution.# Jinja2 uses an opt-out rather than opt-in approach for sand-boxing.# Please treat this sand-boxing as a best-effort approach rather than# a guarantee of security.# We recommend to never use jinja2 templates with untrusted inputs.# https://jinja.palletsprojects.com/en/3.1.x/sandbox/# approach not a guarantee of security.returnSandboxedEnvironment().from_string(template).render(**kwargs)
[docs]defvalidate_jinja2(template:str,input_variables:list[str])->None:"""Validate that the input variables are valid for the template. Issues a warning if missing or extra variables are found. Args: template: The template string. input_variables: The input variables. """input_variables_set=set(input_variables)valid_variables=_get_jinja2_variables_from_template(template)missing_variables=valid_variables-input_variables_setextra_variables=input_variables_set-valid_variableswarning_message=""ifmissing_variables:warning_message+=f"Missing variables: {missing_variables} "ifextra_variables:warning_message+=f"Extra variables: {extra_variables}"ifwarning_message:warnings.warn(warning_message.strip(),stacklevel=7)
def_get_jinja2_variables_from_template(template:str)->set[str]:try:fromjinja2importEnvironment,metaexceptImportErrorase:msg=("jinja2 not installed, which is needed to use the jinja2_formatter. ""Please install it with `pip install jinja2`.")raiseImportError(msg)frome# noqa for insecure warning elsewhereenv=Environment()# noqa: S701ast=env.parse(template)variables=meta.find_undeclared_variables(ast)returnvariables
[docs]defmustache_formatter(template:str,/,**kwargs:Any)->str:"""Format a template using mustache. Args: template: The template string. **kwargs: The variables to format the template with. Returns: The formatted string. """returnmustache.render(template,kwargs)
[docs]defmustache_template_vars(template:str,)->set[str]:"""Get the variables from a mustache template. Args: template: The template string. Returns: The variables from the template. """vars:set[str]=set()section_depth=0fortype,keyinmustache.tokenize(template):iftype=="end":section_depth-=1elif(typein("variable","section","inverted section","no escape")andkey!="."andsection_depth==0):vars.add(key.split(".")[0])iftypein("section","inverted section"):section_depth+=1returnvars
Defs=dict[str,"Defs"]
[docs]defmustache_schema(template:str,)->type[BaseModel]:"""Get the variables from a mustache template. Args: template: The template string. Returns: The variables from the template as a Pydantic model. """fields={}prefix:tuple[str,...]=()section_stack:list[tuple[str,...]]=[]fortype,keyinmustache.tokenize(template):ifkey==".":continueiftype=="end":ifsection_stack:prefix=section_stack.pop()eliftypein("section","inverted section"):section_stack.append(prefix)prefix=prefix+tuple(key.split("."))fields[prefix]=Falseeliftypein("variable","no escape"):fields[prefix+tuple(key.split("."))]=Truedefs:Defs={}# None means leaf nodewhilefields:field,is_leaf=fields.popitem()current=defsforpartinfield[:-1]:current=current.setdefault(part,{})current.setdefault(field[-1],""ifis_leafelse{})# type: ignore[arg-type]return_create_model_recursive("PromptInput",defs)
[docs]defcheck_valid_template(template:str,template_format:str,input_variables:list[str])->None:"""Check that template string is valid. Args: template: The template string. template_format: The template format. Should be one of "f-string" or "jinja2". input_variables: The input variables. Raises: ValueError: If the template format is not supported. ValueError: If the prompt schema is invalid. """try:validator_func=DEFAULT_VALIDATOR_MAPPING[template_format]exceptKeyErrorasexc:msg=(f"Invalid template format {template_format!r}, should be one of"f" {list(DEFAULT_FORMATTER_MAPPING)}.")raiseValueError(msg)fromexctry:validator_func(template,input_variables)except(KeyError,IndexError)asexc:msg=("Invalid prompt schema; check for mismatched or missing input parameters"f" from {input_variables}.")raiseValueError(msg)fromexc
[docs]defget_template_variables(template:str,template_format:str)->list[str]:"""Get the variables from the template. Args: template: The template string. template_format: The template format. Should be one of "f-string" or "jinja2". Returns: The variables from the template. Raises: ValueError: If the template format is not supported. """iftemplate_format=="jinja2":# Get the variables for the templateinput_variables=_get_jinja2_variables_from_template(template)eliftemplate_format=="f-string":input_variables={vfor_,v,_,_inFormatter().parse(template)ifvisnotNone}eliftemplate_format=="mustache":input_variables=mustache_template_vars(template)else:msg=f"Unsupported template format: {template_format}"raiseValueError(msg)returnsorted(input_variables)
[docs]classStringPromptTemplate(BasePromptTemplate,ABC):"""String prompt that exposes the format method, returning a prompt."""@classmethoddefget_lc_namespace(cls)->list[str]:"""Get the namespace of the langchain object."""return["langchain","prompts","base"]
[docs]defformat_prompt(self,**kwargs:Any)->PromptValue:"""Format the prompt with the inputs. Args: kwargs: Any arguments to be passed to the prompt template. Returns: A formatted string. """returnStringPromptValue(text=self.format(**kwargs))
[docs]asyncdefaformat_prompt(self,**kwargs:Any)->PromptValue:"""Async format the prompt with the inputs. Args: kwargs: Any arguments to be passed to the prompt template. Returns: A formatted string. """returnStringPromptValue(text=awaitself.aformat(**kwargs))
[docs]defpretty_repr(self,html:bool=False)->str:"""Get a pretty representation of the prompt. Args: html: Whether to return an HTML-formatted string. Returns: A pretty representation of the prompt. """# TODO: handle partialsdummy_vars={input_var:"{"+f"{input_var}"+"}"forinput_varinself.input_variables}ifhtml:dummy_vars={k:get_colored_text(v,"yellow")fork,vindummy_vars.items()}returnself.format(**dummy_vars)
[docs]defpretty_print(self)->None:"""Print a pretty representation of the prompt."""print(self.pretty_repr(html=is_interactive_env()))# noqa: T201