Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
prompt.py313 linesDownload Raw Back to prompts
1"""Prompt schema definition."""2 3from __future__ import annotations4 5from pathlib import Path6from typing import TYPE_CHECKING, Any7 8from pydantic import BaseModel, model_validator9from typing_extensions import override10 11from langchain_core.prompts.string import (12    DEFAULT_FORMATTER_MAPPING,13    PromptTemplateFormat,14    StringPromptTemplate,15    check_valid_template,16    get_template_variables,17    mustache_schema,18)19 20if TYPE_CHECKING:21    from langchain_core.runnables.config import RunnableConfig22 23 24class PromptTemplate(StringPromptTemplate):25    """Prompt template for a language model.26 27    A prompt template consists of a string template. It accepts a set of parameters28    from the user that can be used to generate a prompt for a language model.29 30    The template can be formatted using either f-strings (default), jinja2, or mustache31    syntax.32 33    !!! warning "Security"34 35        Prefer using `template_format='f-string'` instead of `template_format='jinja2'`,36        or make sure to NEVER accept jinja2 templates from untrusted sources as they may37        lead to arbitrary Python code execution.38 39        As of LangChain 0.0.329, Jinja2 templates will be rendered using Jinja2's40        SandboxedEnvironment by default. This sand-boxing should be treated as a41        best-effort approach rather than a guarantee of security, as it is an opt-out42        rather than opt-in approach.43 44        Despite the sandboxing, we recommend to never use jinja2 templates from45        untrusted sources.46 47    Example:48        ```python49        from langchain_core.prompts import PromptTemplate50 51        # Instantiation using from_template (recommended)52        prompt = PromptTemplate.from_template("Say {foo}")53        prompt.format(foo="bar")54 55        # Instantiation using initializer56        prompt = PromptTemplate(template="Say {foo}")57        ```58    """59 60    @property61    @override62    def lc_attributes(self) -> dict[str, Any]:63        return {64            "template_format": self.template_format,65        }66 67    @classmethod68    @override69    def get_lc_namespace(cls) -> list[str]:70        """Get the namespace of the LangChain object.71 72        Returns:73            `["langchain", "prompts", "prompt"]`74        """75        return ["langchain", "prompts", "prompt"]76 77    template: str78    """The prompt template."""79 80    template_format: PromptTemplateFormat = "f-string"81    """The format of the prompt template.82 83    Options are: `'f-string'`, `'mustache'`, `'jinja2'`.84    """85 86    validate_template: bool = False87    """Whether or not to try validating the template."""88 89    @model_validator(mode="before")90    @classmethod91    def pre_init_validation(cls, values: dict) -> Any:92        """Check that template and input variables are consistent."""93        if values.get("template") is None:94            # Will let pydantic fail with a ValidationError if template95            # is not provided.96            return values97 98        # Set some default values based on the field defaults99        values.setdefault("template_format", "f-string")100        values.setdefault("partial_variables", {})101 102        if values.get("validate_template"):103            if values["template_format"] == "mustache":104                msg = "Mustache templates cannot be validated."105                raise ValueError(msg)106 107            if "input_variables" not in values:108                msg = "Input variables must be provided to validate the template."109                raise ValueError(msg)110 111            all_inputs = values["input_variables"] + list(values["partial_variables"])112            check_valid_template(113                values["template"], values["template_format"], all_inputs114            )115 116        if values["template_format"]:117            values["input_variables"] = [118                var119                for var in get_template_variables(120                    values["template"], values["template_format"]121                )122                if var not in values["partial_variables"]123            ]124 125        return values126 127    @override128    def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]:129        """Get the input schema for the prompt.130 131        Args:132            config: The runnable configuration.133 134        Returns:135            The input schema for the prompt.136        """137        if self.template_format != "mustache":138            return super().get_input_schema(config)139 140        return mustache_schema(self.template)141 142    def __add__(self, other: Any) -> PromptTemplate:143        """Override the `+` operator to allow for combining prompt templates.144 145        Raises:146            ValueError: If the template formats are not f-string or if there are147                conflicting partial variables.148            NotImplementedError: If the other object is not a `PromptTemplate` or str.149 150        Returns:151            A new `PromptTemplate` that is the combination of the two.152        """153        # Allow for easy combining154        if isinstance(other, PromptTemplate):155            if self.template_format != other.template_format:156                msg = "Cannot add templates of different formats"157                raise ValueError(msg)158            input_variables = list(159                set(self.input_variables) | set(other.input_variables)160            )161            template = self.template + other.template162            # If any do not want to validate, then don't163            validate_template = self.validate_template and other.validate_template164            partial_variables = dict(self.partial_variables.items())165            for k, v in other.partial_variables.items():166                if k in partial_variables:167                    msg = "Cannot have same variable partialed twice."168                    raise ValueError(msg)169                partial_variables[k] = v170            return PromptTemplate(171                template=template,172                input_variables=input_variables,173                partial_variables=partial_variables,174                template_format=self.template_format,175                validate_template=validate_template,176            )177        if isinstance(other, str):178            prompt = PromptTemplate.from_template(179                other,180                template_format=self.template_format,181            )182            return self + prompt183        msg = f"Unsupported operand type for +: {type(other)}"184        raise NotImplementedError(msg)185 186    @property187    def _prompt_type(self) -> str:188        """Return the prompt type key."""189        return "prompt"190 191    def format(self, **kwargs: Any) -> str:192        """Format the prompt with the inputs.193 194        Args:195            **kwargs: Any arguments to be passed to the prompt template.196 197        Returns:198            A formatted string.199        """200        kwargs = self._merge_partial_and_user_variables(**kwargs)201        return DEFAULT_FORMATTER_MAPPING[self.template_format](self.template, **kwargs)202 203    @classmethod204    def from_examples(205        cls,206        examples: list[str],207        suffix: str,208        input_variables: list[str],209        example_separator: str = "\n\n",210        prefix: str = "",211        **kwargs: Any,212    ) -> PromptTemplate:213        """Take examples in list format with prefix and suffix to create a prompt.214 215        Intended to be used as a way to dynamically create a prompt from examples.216 217        Args:218            examples: List of examples to use in the prompt.219            suffix: String to go after the list of examples.220 221                Should generally set up the user's input.222            input_variables: A list of variable names the final prompt template will223                expect.224            example_separator: The separator to use in between examples.225            prefix: String that should go before any examples.226 227                Generally includes examples.228 229        Returns:230            The final prompt generated.231        """232        template = example_separator.join([prefix, *examples, suffix])233        return cls(input_variables=input_variables, template=template, **kwargs)234 235    @classmethod236    def from_file(237        cls,238        template_file: str | Path,239        encoding: str | None = None,240        **kwargs: Any,241    ) -> PromptTemplate:242        """Load a prompt from a file.243 244        Args:245            template_file: The path to the file containing the prompt template.246            encoding: The encoding system for opening the template file.247 248                If not provided, will use the OS default.249 250        Returns:251            The prompt loaded from the file.252        """253        template = Path(template_file).read_text(encoding=encoding)254        return cls.from_template(template=template, **kwargs)255 256    @classmethod257    def from_template(258        cls,259        template: str,260        *,261        template_format: PromptTemplateFormat = "f-string",262        partial_variables: dict[str, Any] | None = None,263        **kwargs: Any,264    ) -> PromptTemplate:265        """Load a prompt template from a template.266 267        !!! warning "Security"268 269            Prefer using `template_format='f-string'` instead of270            `template_format='jinja2'`, or make sure to NEVER accept jinja2 templates271            from untrusted sources as they may lead to arbitrary Python code execution.272 273            As of LangChain 0.0.329, Jinja2 templates will be rendered using Jinja2's274            SandboxedEnvironment by default. This sand-boxing should be treated as a275            best-effort approach rather than a guarantee of security, as it is an276            opt-out rather than opt-in approach.277 278            Despite the sandboxing, we recommend to never use jinja2 templates from279            untrusted sources.280 281        Args:282            template: The template to load.283            template_format: The format of the template.284 285                Use `jinja2` for jinja2, `mustache` for mustache, and `f-string` for286                f-strings.287            partial_variables: A dictionary of variables that can be used to partially288                fill in the template.289 290                For example, if the template is `'{variable1} {variable2}'`, and291                `partial_variables` is `{"variable1": "foo"}`, then the final prompt292                will be `'foo {variable2}'`.293            **kwargs: Any other arguments to pass to the prompt template.294 295        Returns:296            The prompt template loaded from the template.297        """298        input_variables = get_template_variables(template, template_format)299        partial_variables_ = partial_variables or {}300 301        if partial_variables_:302            input_variables = [303                var for var in input_variables if var not in partial_variables_304            ]305 306        return cls(307            input_variables=input_variables,308            template=template,309            template_format=template_format,310            partial_variables=partial_variables_,311            **kwargs,312        )313 
codekingpro/portable-devtools · Team Ai