diff --git a/langchain/__init__.py b/langchain/__init__.py index d7680041d6d..5ee61bf3275 100644 --- a/langchain/__init__.py +++ b/langchain/__init__.py @@ -17,7 +17,7 @@ from langchain.docstore import Wikipedia from langchain.faiss import FAISS from langchain.llms import Cohere, HuggingFaceHub, OpenAI -from langchain.prompt import Prompt +from langchain.prompt import DynamicPrompt, Prompt from langchain.sql_database import SQLDatabase __all__ = [ @@ -28,6 +28,7 @@ "SerpAPIChain", "Cohere", "OpenAI", + "DynamicPrompt", "Prompt", "ReActChain", "Wikipedia", diff --git a/langchain/prompt.py b/langchain/prompt.py index a3512ed8cc7..a78ac77b58f 100644 --- a/langchain/prompt.py +++ b/langchain/prompt.py @@ -101,3 +101,102 @@ def from_examples( example_str = example_separator.join(examples) template = prefix + example_str + suffix return cls(input_variables=input_variables, template=template) + + +class DynamicPrompt(BaseModel): + r"""Schema to represent a dynamic prompt for an LLM. + + Example: + .. code-block:: python + + from langchain import DynamicPrompt + dynamic_prompt = DynamicPrompt( + examples=["Say hi. Hi", "Say ho. Ho"], + example_separator="\n\n", + prefix="", + suffix="\n\nSay {foo}" + input_variables=["foo"] + ) + """ + + examples: List[str] + """A list of the examples that the prompt template expects.""" + + example_separator: str + """Example separator, e.g. \n\n, for the dynamic prompt creation.""" + + input_variables: List[str] + """A list of the names of the variables the prompt template expects.""" + + prefix: str + """Prefix for the prompt.""" + + suffix: str + """Suffix for the prompt.""" + + template_format: str = "f-string" + """The format of the prompt template. Options are: 'f-string'.""" + + class Config: + """Configuration for this pydantic object.""" + + extra = Extra.forbid + + def format(self, **kwargs: Any) -> str: + """Dynamically format the prompt with the inputs. + + Args: + kwargs: Any arguments to be passed to the prompt template. + + Returns: + A formatted string. + + Example: + + .. code-block:: python + + prompt.format(variable1="foo") + """ + # TODO segment self.examples based on example length & + # input_variables length here + example_str = self.example_separator.join(self.examples) + template = self.prefix + example_str + self.suffix + return _FORMATTER_MAPPING[self.template_format](template, **kwargs) + + @root_validator() + def template_is_valid(cls, values: Dict) -> Dict: + """Check that suffix and input variables are consistent.""" + input_variables = values["input_variables"] + suffix = values["suffix"] + template_format = values["template_format"] + if template_format not in _FORMATTER_MAPPING: + valid_formats = list(_FORMATTER_MAPPING) + raise ValueError( + f"Invalid template format. Got `{template_format}`;" + f" should be one of {valid_formats}" + ) + dummy_inputs = {input_variable: "foo" for input_variable in input_variables} + try: + formatter_func = _FORMATTER_MAPPING[template_format] + formatter_func(suffix, **dummy_inputs) + except KeyError: + raise ValueError("Invalid prompt schema.") + return values + + @classmethod + def from_examples( + cls, + examples: List[str], + suffix: str, + input_variables: List[str], + example_separator: str = "\n\n", + prefix: str = "", + ) -> "DynamicPrompt": + """Initialize DynamicPrompt with prefix, suffix, examples, etc.""" + return cls( + examples=examples, + example_separator=example_separator, + input_variables=input_variables, + prefix=prefix, + suffix=suffix, + )