/ src / python / txtai / embeddings / index / functions.py
functions.py
  1  """
  2  Functions module
  3  """
  4  
  5  from types import FunctionType, MethodType
  6  
  7  
  8  class Functions:
  9      """
 10      Resolves function configuration to function references.
 11      """
 12  
 13      def __init__(self, embeddings):
 14          """
 15          Creates a new function resolver.
 16  
 17          Args:
 18              embeddings: embeddings instance
 19          """
 20  
 21          self.embeddings = embeddings
 22  
 23          # Handle to all reference objects
 24          self.references = None
 25  
 26      def __call__(self, config):
 27          """
 28          Resolves a list of functions to function references.
 29  
 30          Args:
 31              config: configuration
 32  
 33          Returns:
 34              list of function references
 35          """
 36  
 37          # Initialize stored references array
 38          self.references = []
 39  
 40          # Resolve callable functions
 41          functions = []
 42          for fn in config["functions"]:
 43              if isinstance(fn, dict):
 44                  fn = fn.copy()
 45                  fn["function"] = self.function(fn["function"])
 46              else:
 47                  fn = self.function(fn)
 48              functions.append(fn)
 49  
 50          return functions
 51  
 52      def reset(self):
 53          """
 54          Clears all resolved references.
 55          """
 56  
 57          if self.references:
 58              for reference in self.references:
 59                  reference.reset()
 60  
 61      def function(self, function):
 62          """
 63          Resolves function configuration. If function is a string, it's split on '.' and each part
 64          is separately resolved to an object, attribute or function. Each part is resolved upon the
 65          first invocation of the function. Otherwise, the input is returned.
 66  
 67          Args:
 68              function: function configuration
 69  
 70          Returns:
 71              function reference
 72          """
 73  
 74          if isinstance(function, str):
 75              parts = function.split(".")
 76  
 77              if hasattr(self.embeddings, parts[0]):
 78                  m = Reference(self.embeddings, parts[0])
 79                  self.references.append(m)
 80              else:
 81                  module = ".".join(parts[:-1])
 82                  m = __import__(module)
 83  
 84              for comp in parts[1:]:
 85                  m = Reference(m, comp)
 86                  self.references.append(m)
 87  
 88              return m
 89  
 90          return function
 91  
 92  
 93  class Reference:
 94      """
 95      Stores a reference to an object attribute. This attribute is resolved by invoking the __call__ method.
 96      This allows for functions to be independent of the initialization order of an embeddings instance.
 97      """
 98  
 99      def __init__(self, obj, attribute):
100          """
101          Create a new reference.
102  
103          Args:
104              obj: object handle
105              attribute: attribute name
106          """
107  
108          # Object handle and attribute
109          self.obj = obj
110          self.attribute = attribute
111  
112          # Keep a handle to the original inputs
113          self.inputs = (obj, attribute)
114  
115          # True if the object and attribute have been resolved
116          self.resolved = False
117  
118          # True if the attribute is a function
119          self.function = None
120  
121      def __call__(self, *args):
122          """
123          Resolves an object attribute reference. If the attribute is a function, the function is executed.
124          Otherwise, the object attribute value is returned.
125  
126          Args:
127              args: list of function arguments to the object attribute, when attribute is a function
128  
129          Returns:
130              object attribute function result or object attribute value
131          """
132  
133          # Resolve nested function arguments, if necessary
134          if not self.resolved:
135              self.obj = self.obj() if isinstance(self.obj, Reference) else self.obj
136              self.attribute = self.attribute() if isinstance(self.attribute, Reference) else self.attribute
137              self.resolved = True
138  
139          # Lookup attribute
140          attribute = getattr(self.obj, self.attribute)
141  
142          # Determine if attribute is a function
143          if self.function is None:
144              self.function = isinstance(attribute, (FunctionType, MethodType)) or (hasattr(attribute, "__call__") and args)
145  
146          # If attribute is a function, execute and return, otherwise return attribute
147          return attribute(*args) if self.function else attribute
148  
149      def reset(self):
150          """
151          Clears resolved references.
152          """
153  
154          self.obj, self.attribute = self.inputs
155          self.resolved = False