/ src / python / txtai / workflow / task / template.py
template.py
  1  """
  2  Template module
  3  """
  4  
  5  from string import Formatter
  6  
  7  from ...util import TemplateFormatter
  8  from .file import Task
  9  
 10  
 11  class TemplateTask(Task):
 12      """
 13      Task that generates text from a template and task inputs. Templates can be used to prepare data for a number of tasks
 14      including generating large language model (LLM) prompts.
 15      """
 16  
 17      def register(self, template=None, rules=None, strict=True):
 18          """
 19          Read template parameters.
 20  
 21          Args:
 22              template: prompt template
 23              rules: parameter rules
 24              strict: requires all task inputs to be consumed by template, defaults to True
 25          """
 26  
 27          # pylint: disable=W0201
 28          # Template text
 29          self.template = template if template else self.defaulttemplate()
 30  
 31          # Template processing rules
 32          self.rules = rules if rules else self.defaultrules()
 33  
 34          # Create formatter
 35          self.formatter = TemplateFormatter() if strict else Formatter()
 36  
 37      def prepare(self, element):
 38          # Check if element matches any processing rules
 39          match = self.match(element)
 40          if match:
 41              return match
 42  
 43          # Apply template processing, if applicable
 44          if self.template:
 45              # Pass dictionary as named prompt template parameters
 46              if isinstance(element, dict):
 47                  return self.formatter.format(self.template, **element)
 48  
 49              # Pass tuple as prompt template parameters (arg0 - argN)
 50              if isinstance(element, tuple):
 51                  return self.formatter.format(self.template, **{f"arg{i}": x for i, x in enumerate(element)})
 52  
 53              # Default behavior is to use input as {text} parameter in prompt template
 54              return self.formatter.format(self.template, text=element)
 55  
 56          # Return original inputs when no prompt provided
 57          return element
 58  
 59      def defaulttemplate(self):
 60          """
 61          Generates a default template for this task. Base method returns None.
 62  
 63          Returns:
 64              default template
 65          """
 66  
 67          return None
 68  
 69      def defaultrules(self):
 70          """
 71          Generates a default rules for this task. Base method returns an empty dictionary.
 72  
 73          Returns:
 74              default rules
 75          """
 76  
 77          return {}
 78  
 79      def match(self, element):
 80          """
 81          Check if element matches any processing rules.
 82  
 83          Args:
 84              element: input element
 85  
 86          Returns:
 87              matching value if found, None otherwise
 88          """
 89  
 90          if self.rules and isinstance(element, dict):
 91              # Check if any rules are matched
 92              for key, value in self.rules.items():
 93                  if element[key] == value:
 94                      return element[key]
 95  
 96          return None
 97  
 98  
 99  class RagTask(TemplateTask):
100      """
101      Template task that prepares input for a rag pipeline.
102      """
103  
104      def prepare(self, element):
105          # Apply prompt template using all variables except "query" and use output as question
106          if isinstance(element, dict):
107              # Make a copy without query and run through template
108              params = dict(element)
109              params.pop("query", None)
110              params["text"] = params.pop("question")
111  
112              element["question"] = super().prepare(params)
113              return element
114  
115          # Default mode is to use element text for both query and question
116          return {"query": element, "question": super().prepare(element)}