• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

deepset-ai / haystack / 6816263793

09 Nov 2023 07:02PM UTC coverage: 40.313% (-0.006%) from 40.319%
6816263793

push

github

web-flow
fix: make types work without installing pypdf (#6269)

* make types work without installing pypdf

* make pylint happy, keep pyright happy, hope mypy doesn't care

10498 of 26041 relevant lines covered (40.31%)

2.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
haystack/preview/components/builders/prompt_builder.py
1
from typing import Dict, Any, Optional, List
×
2

3
from jinja2 import Template, meta
×
4

5
from haystack.preview import component
×
6
from haystack.preview import default_to_dict
×
7
from haystack.preview.dataclasses.chat_message import ChatMessage, ChatRole
×
8

9

10
@component
×
11
class PromptBuilder:
×
12
    """
13
    A component for building prompts using template strings or template variables.
14

15
    The `PromptBuilder` can be initialized with a template string or template variables to dynamically build
16
    a prompt. There are two distinct use cases:
17

18
    1. **Static Template**: When initialized with a `template` string, the `PromptBuilder` will always use
19
       this template for rendering prompts throughout its lifetime.
20

21
    2. **Dynamic Templates**: When initialized with `template_variables` and receiving messages in
22
       `ChatMessage` format, it allows for different templates for each message, enabling prompt templating
23
       on a per-user message basis.
24

25
    :param template: (Optional) A template string to be rendered using Jinja2 syntax, e.g.,
26
                     "What's the weather like in {{ location }}?". This template will be used for all
27
                     prompts. Defaults to None.
28
    :param template_variables: (Optional) A list of all template variables to be used as input.
29
                              This parameter enables dynamic templating based on user messages. Defaults to None.
30

31
    :raises ValueError: If neither `template` nor `template_variables` are provided.
32

33

34
    Usage (static templating):
35

36
    ```python
37
    template = "Translate the following context to {{ target_language }}. Context: {{ snippet }}; Translation:"
38
    builder = PromptBuilder(template=template)
39
    builder.run(target_language="spanish", snippet="I can't speak spanish.")
40
    ```
41

42
    The above template is used for all messages that are passed to the `PromptBuilder`.
43

44

45
    Usage (dynamic templating):
46
    ```python
47

48
    from haystack.preview.dataclasses.chat_message import ChatMessage
49
    template = "What's the weather like in {{ location }}?"
50

51
    prompt_builder = PromptBuilder(template_variables=["location", "time"])
52
    messages = [ChatMessage.from_system("Always start response to user with Herr Blagojevic.
53
    Respond in German even if some input data is in other languages"),
54
                ChatMessage.from_user(template)]
55

56
    response = pipe.run(data={"prompt_builder": {"location": location, "messages": messages}})
57
    ```
58

59
    In this example, only the last user message is templated. The template_variables parameter in the PromptBuilder
60
    initialization specifies all potential template variables, yet only the variables utilized in the template are
61
    required in the run method. For instance, since the time variable isn't used in the template, it's not
62
    necessary in the run method invocation above.
63

64

65
    Note:
66
        The behavior of `PromptBuilder` is determined by the initialization parameters. A static template
67
        provides a consistent prompt structure, while template variables offer dynamic templating per user
68
        message.
69

70
    """
71

72
    def __init__(self, template: Optional[str] = None, template_variables: Optional[List[str]] = None):
×
73
        """
74
        Initialize the component with either a template string or template variables.
75
        If template is given PromptBuilder will parse the template string and use the template variables
76
        as input types. Conversely, if template_variables are given, PromptBuilder will directly use
77
        them as input variables.
78

79
        If neither template nor template_variables are provided, an error will be raised. If both are provided,
80
        an error will be raised as well.
81

82
        :param template: Template string to be rendered.
83
        :param template_variables: List of template variables to be used as input types.
84
        """
85
        if template_variables and template:
×
86
            raise ValueError("template and template_variables cannot be provided at the same time.")
×
87

88
        # dynamic per-user message templating
89
        if template_variables:
×
90
            # treat vars as optional input slots
91
            dynamic_input_slots = {var: Optional[Any] for var in template_variables}
×
92
            self.template = None
×
93

94
        # static templating
95
        else:
96
            if not template:
×
97
                raise ValueError("Either template or template_variables must be provided.")
×
98
            self.template = Template(template)
×
99
            ast = self.template.environment.parse(template)
×
100
            static_template_variables = meta.find_undeclared_variables(ast)
×
101
            # treat vars as required input slots - as per design
102
            dynamic_input_slots = {var: Any for var in static_template_variables}
×
103

104
        # always provide all serialized vars, so we can serialize
105
        # the component regardless of the initialization method (static vs. dynamic)
106
        self.template_variables = template_variables
×
107
        self._template_string = template
×
108

109
        optional_input_slots = {"messages": Optional[List[ChatMessage]]}
×
110
        component.set_input_types(self, **optional_input_slots, **dynamic_input_slots)
×
111

112
    def to_dict(self) -> Dict[str, Any]:
×
113
        return default_to_dict(self, template=self._template_string, template_variables=self.template_variables)
×
114

115
    @component.output_types(prompt=str)
×
116
    def run(self, messages: Optional[List[ChatMessage]] = None, **kwargs):
×
117
        """
118
        Build and return the prompt based on the provided messages and template or template variables.
119
        If `messages` are provided, the template will be applied to the last user message.
120

121
        :param messages: (Optional) List of `ChatMessage` instances, used for dynamic templating
122
        when `template_variables` are provided.
123
        :param kwargs: Additional keyword arguments representing template variables.
124
        """
125

126
        if messages:
×
127
            # apply the template to the last user message only
128
            last_message: ChatMessage = messages[-1]
×
129
            if last_message.is_from(ChatRole.USER):
×
130
                template = Template(last_message.content)
×
131
                return {"prompt": messages[:-1] + [ChatMessage.from_user(template.render(kwargs))]}
×
132
            else:
133
                return {"prompt": messages}
×
134
        else:
135
            if self.template:
×
136
                return {"prompt": self.template.render(kwargs)}
×
137
            else:
138
                raise ValueError(
×
139
                    "PromptBuilder was initialized with template_variables, but no ChatMessage(s) were provided."
140
                )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc