Skip to content

base.prompts

A base class for writing prompts.

AssistantMessage

Bases: TypedDict

A message with the assistant role.

Attributes:

Name Type Description
role Required[Literal['assistant']]

The role of the message's author, in this case assistant.

content Required[str]

The contents of the message.

Source code in mirascope/base/types.py
class AssistantMessage(TypedDict, total=False):
    """A message with the `assistant` role.

    Attributes:
        role: The role of the message's author, in this case `assistant`.
        content: The contents of the message.
    """

    role: Required[Literal["assistant"]]
    content: Required[str]

BasePrompt

Bases: BaseModel

The base class for working with prompts.

This class is implemented as the base for all prompting needs across various model providers.

Example:

from mirascope import BasePrompt


class BookRecommendationPrompt(BasePrompt):
    """A prompt for recommending a book."""

    prompt_template = """
    SYSTEM: You are the world's greatest librarian.
    USER: Please recommend a {genre} book.
    """

    genre: str


prompt = BookRecommendationPrompt(genre="fantasy")
print(prompt.messages())
#> [{"role": "user", "content": "Please recommend a fantasy book."}]

print(prompt)
#> Please recommend a fantasy book.
Source code in mirascope/base/prompts.py
class BasePrompt(BaseModel):
    '''The base class for working with prompts.

    This class is implemented as the base for all prompting needs across various model
    providers.

    Example:

    ```python
    from mirascope import BasePrompt


    class BookRecommendationPrompt(BasePrompt):
        """A prompt for recommending a book."""

        prompt_template = """
        SYSTEM: You are the world's greatest librarian.
        USER: Please recommend a {genre} book.
        """

        genre: str


    prompt = BookRecommendationPrompt(genre="fantasy")
    print(prompt.messages())
    #> [{"role": "user", "content": "Please recommend a fantasy book."}]

    print(prompt)
    #> Please recommend a fantasy book.
    ```
    '''

    tags: ClassVar[list[str]] = []
    prompt_template: ClassVar[str] = ""

    def __str__(self) -> str:
        """Returns the formatted template."""
        return self._format_template(self.prompt_template)

    def messages(self) -> Union[list[Message], Any]:
        """Returns the template as a formatted list of messages."""
        message_type_by_role = {
            MessageRole.SYSTEM: SystemMessage,
            MessageRole.USER: UserMessage,
            MessageRole.ASSISTANT: AssistantMessage,
            MessageRole.MODEL: ModelMessage,
            MessageRole.TOOL: ToolMessage,
        }
        return [
            message_type_by_role[MessageRole(message["role"])](**message)
            for message in self._parse_messages(list(message_type_by_role.keys()))
        ]

    def dump(
        self,
    ) -> dict[str, Any]:
        """Dumps the contents of the prompt into a dictionary."""
        return {
            "tags": self.tags,
            "template": dedent(self.prompt_template).strip("\n"),
            "inputs": self.model_dump(),
        }

    ############################## PRIVATE METHODS ###################################

    def _format_template(self, template: str):
        """Formats the given `template` with attributes matching template variables."""
        dedented_template = dedent(template).strip()
        template_vars = [
            var
            for _, var, _, _ in Formatter().parse(dedented_template)
            if var is not None
        ]

        values = {}
        for var in template_vars:
            attr = getattr(self, var)
            if attr and isinstance(attr, list):
                if isinstance(attr[0], list):
                    values[var] = "\n\n".join(
                        ["\n".join([str(subitem) for subitem in item]) for item in attr]
                    )
                else:
                    values[var] = "\n".join([str(item) for item in attr])
            else:
                values[var] = str(attr)

        return dedented_template.format(**values)

    def _parse_messages(self, roles: list[str]) -> list[Message]:
        """Returns messages parsed from the `template` ClassVar.

        Raises:
            ValueError: if the template contains an unknown role.
        """
        messages = []
        re_roles = "|".join([role.upper() for role in roles] + ["MESSAGES"])
        for match in re.finditer(
            rf"({re_roles}):((.|\n)+?)(?=({re_roles}):|\Z)",
            self.prompt_template,
        ):
            role = match.group(1).lower()
            if role == "messages":
                template_var = [
                    var
                    for _, var, _, _ in Formatter().parse(match.group(2))
                    if var is not None
                ][0]
                attribute = getattr(self, template_var)
                if attribute is None or not isinstance(attribute, list):
                    raise ValueError(
                        f"MESSAGES keyword used with attribute `{template_var}`, which "
                        "is not a `list` of messages."
                    )
                messages += attribute
            else:
                content = self._format_template(match.group(2))
                if content:
                    messages.append({"role": role, "content": content})
        if len(messages) == 0:
            messages.append(
                {
                    "role": "user",
                    "content": self._format_template(self.prompt_template),
                }
            )
        return messages

dump()

Dumps the contents of the prompt into a dictionary.

Source code in mirascope/base/prompts.py
def dump(
    self,
) -> dict[str, Any]:
    """Dumps the contents of the prompt into a dictionary."""
    return {
        "tags": self.tags,
        "template": dedent(self.prompt_template).strip("\n"),
        "inputs": self.model_dump(),
    }

messages()

Returns the template as a formatted list of messages.

Source code in mirascope/base/prompts.py
def messages(self) -> Union[list[Message], Any]:
    """Returns the template as a formatted list of messages."""
    message_type_by_role = {
        MessageRole.SYSTEM: SystemMessage,
        MessageRole.USER: UserMessage,
        MessageRole.ASSISTANT: AssistantMessage,
        MessageRole.MODEL: ModelMessage,
        MessageRole.TOOL: ToolMessage,
    }
    return [
        message_type_by_role[MessageRole(message["role"])](**message)
        for message in self._parse_messages(list(message_type_by_role.keys()))
    ]

MessageRole

Bases: _Enum

Roles that the BasePrompt messages parser can parse from the template.

SYSTEM: A system message. USER: A user message. ASSISTANT: A message response from the assistant or chat client. MODEL: A message response from the assistant or chat client. Model is used by Google's Gemini instead of assistant, which doesn't have system messages. CHATBOT: A message response from the chat client. Chatbot is used by Cohere instead of assistant. TOOL: A message representing the output of calling a tool.

Source code in mirascope/enums.py
class MessageRole(_Enum):
    """Roles that the `BasePrompt` messages parser can parse from the template.

    SYSTEM: A system message.
    USER: A user message.
    ASSISTANT: A message response from the assistant or chat client.
    MODEL: A message response from the assistant or chat client. Model is used by
        Google's Gemini instead of assistant, which doesn't have system messages.
    CHATBOT: A message response from the chat client. Chatbot is used by Cohere instead
        of assistant.
    TOOL: A message representing the output of calling a tool.
    """

    SYSTEM = "system"
    USER = "user"
    ASSISTANT = "assistant"
    MODEL = "model"
    CHATBOT = "chatbot"
    TOOL = "tool"

ModelMessage

Bases: TypedDict

A message with the model role.

Attributes:

Name Type Description
role Required[Literal['model']]

The role of the message's author, in this case model.

content Required[str]

The contents of the message.

Source code in mirascope/base/types.py
class ModelMessage(TypedDict, total=False):
    """A message with the `model` role.

    Attributes:
        role: The role of the message's author, in this case `model`.
        content: The contents of the message.
    """

    role: Required[Literal["model"]]
    content: Required[str]

SystemMessage

Bases: TypedDict

A message with the system role.

Attributes:

Name Type Description
role Required[Literal['system']]

The role of the message's author, in this case system.

content Required[str]

The contents of the message.

Source code in mirascope/base/types.py
class SystemMessage(TypedDict, total=False):
    """A message with the `system` role.

    Attributes:
        role: The role of the message's author, in this case `system`.
        content: The contents of the message.
    """

    role: Required[Literal["system"]]
    content: Required[str]

ToolMessage

Bases: TypedDict

A message with the tool role.

Attributes:

Name Type Description
role Required[Literal['tool']]

The role of the message's author, in this case tool.

content Required[str]

The contents of the message.

Source code in mirascope/base/types.py
class ToolMessage(TypedDict, total=False):
    """A message with the `tool` role.

    Attributes:
        role: The role of the message's author, in this case `tool`.
        content: The contents of the message.
    """

    role: Required[Literal["tool"]]
    content: Required[str]

UserMessage

Bases: TypedDict

A message with the user role.

Attributes:

Name Type Description
role Required[Literal['user']]

The role of the message's author, in this case user.

content Required[str]

The contents of the message.

Source code in mirascope/base/types.py
class UserMessage(TypedDict, total=False):
    """A message with the `user` role.

    Attributes:
        role: The role of the message's author, in this case `user`.
        content: The contents of the message.
    """

    role: Required[Literal["user"]]
    content: Required[str]

tags(args)

A decorator for adding tags to a BasePrompt.

Adding this decorator to a BasePrompt updates the _tags class attribute to the given value. This is useful for adding metadata to a BasePrompt that can be used for logging or filtering.

Example:

from mirascope import BasePrompt, tags


@tags(["book_recommendation", "entertainment"])
class BookRecommendationPrompt(BasePrompt):
    prompt_template = """
    SYSTEM:
    You are the world's greatest librarian.

    USER:
    I've recently read this book: {book_title}.
    What should I read next?
    """

    book_title: [str]

print(BookRecommendationPrompt.dump()["tags"])
#> ['book_recommendation', 'entertainment']

Returns:

Type Description
Callable[[Type[BasePromptT]], Type[BasePromptT]]

The decorated class with tags class attribute set.

Source code in mirascope/base/prompts.py
def tags(args: list[str]) -> Callable[[Type[BasePromptT]], Type[BasePromptT]]:
    '''A decorator for adding tags to a `BasePrompt`.

    Adding this decorator to a `BasePrompt` updates the `_tags` class attribute to the
    given value. This is useful for adding metadata to a `BasePrompt` that can be used
    for logging or filtering.

    Example:

    ```python
    from mirascope import BasePrompt, tags


    @tags(["book_recommendation", "entertainment"])
    class BookRecommendationPrompt(BasePrompt):
        prompt_template = """
        SYSTEM:
        You are the world's greatest librarian.

        USER:
        I've recently read this book: {book_title}.
        What should I read next?
        """

        book_title: [str]

    print(BookRecommendationPrompt.dump()["tags"])
    #> ['book_recommendation', 'entertainment']
    ```

    Returns:
        The decorated class with `tags` class attribute set.
    '''

    def tags_fn(model_class: Type[BasePromptT]) -> Type[BasePromptT]:
        """Updates the `tags` class attribute to the given value."""
        setattr(model_class, "tags", args)
        return model_class

    return tags_fn