def parse( self, text: Text, time: Optional[datetime.datetime] = None, only_output_properties: bool = True, ) -> Dict[Text, Any]: """Parse the input text, classify it and return pipeline result. The pipeline result usually contains intent and entities.""" if not text: # Not all components are able to handle empty strings. So we need # to prevent that... This default return will not contain all # output attributes of all components, but in the end, no one # should pass an empty string in the first place. output = self.default_output_attributes() output["text"] = "" return output message = Message(text, self.default_output_attributes(), time=time) for component in self.pipeline: component.process(message, **self.context) output = self.default_output_attributes() output.update(message.as_dict(only_output_properties=only_output_properties)) return output
def process(self, message: Message, **kwargs: Any) -> None: """ This is where we care about adding to the message, as this is where we can see it. Take each attribute in `properties_to_add` and add it to the output """ print(message) print(message.as_dict()) for prop in self.properties_to_add: print(prop) prop_val = message.get(prop) message.set(prop, prop_val, add_to_output=True)
class IncrementalInterpreter(Interpreter): """Use a trained pipeline of components to parse text messages. This is for explicitly listed incremental components""" @staticmethod def load(model_dir: Text, component_builder: Optional[ComponentBuilder] = None, skip_validation: bool = False) -> 'IncrementalInterpreter': """Create an interpreter based on a persisted model. Args: skip_validation: If set to `True`, tries to check that all required packages for the components are installed before loading them. model_dir: The path of the model to load component_builder: The :class:`ComponentBuilder` to use. Returns: An interpreter that uses the loaded model. """ model_metadata = Metadata.load(model_dir) IncrementalInterpreter.ensure_model_compatibility(model_metadata) return IncrementalInterpreter.create(model_metadata, component_builder, skip_validation) @staticmethod def create(model_metadata: Metadata, component_builder: Optional[ComponentBuilder] = None, skip_validation: bool = False) -> 'IncrementalInterpreter': """Load stored model and components defined by the provided metadata.""" context = {} if component_builder is None: # If no builder is passed, every interpreter creation will result # in a new builder. hence, no components are reused. component_builder = components.ComponentBuilder() pipeline = [] # Before instantiating the component classes, # lets check if all required packages are available if not skip_validation: components.validate_requirements(model_metadata.component_classes) for i in range(model_metadata.number_of_components): component_meta = model_metadata.for_component(i) component = component_builder.load_component( component_meta, model_metadata.model_dir, model_metadata, **context) try: updates = component.provide_context() if updates: context.update(updates) pipeline.append(component) except components.MissingArgumentError as e: raise Exception("Failed to initialize component '{}'. " "{}".format(component.name, e)) return IncrementalInterpreter(pipeline, context, model_metadata) def __init__(self, pipeline, context, model_metadata=None): super().__init__(pipeline, context, model_metadata) # self.message is initialized in init, and get modified with each parse # call until it is eventually cleared when new_utterance is called # TODO: assert that every compoenent in the pipeline is incremental self.message = Message(text="") # Call this function when creating up a new utterance # this will tell the incremental components to clear their # internal states and start clean. def new_utterance(self): """ Tells the component that a new utterance is about to begin This clears a components internal state and resets the Message """ for component in self.pipeline: component.new_utterance() self.message = Message(text="") def parse(self, text, time=None, only_output_properties=True): """ Parse the input text, classify it and return pipeline result. This should only be called for command line evaluation of an incremental component, otherwise, use parse_incremental. Text is split into word-level increments and subsequently calls parse_incremental for each word to evaluate incremental components. """ self.new_utterance() # split the text into words, and pass them through as # "add" incremental units to parse_incremental for word in text.split(): iu = (word, "add") self.parse_incremental(iu, time) output = self.default_output_attributes() output.update( self.message.as_dict( only_output_properties=only_output_properties)) return output def parse_incremental(self, iu, time=None, only_output_properties=True): """ Parse the incremental unit through the pipeline of trained incremental components. new_utterance should be called before each new query. Arg: iu: Incremental Unit. This can be anything as long as the pipeline can handle it. The default for sium and restart- incremental embeddings is a tuple of format (word, operation), Where operation is either "add" or "revoke" """ if not iu: # Not all components are able to handle empty strings. So we need # to prevent that... This default return will not contain all # output attributes of all components, but in the end, no one # should pass an empty string in the first place. output = self.default_output_attributes() output["text"] = "" return output # Initialize our iu_list if this is the first # call to it. if (self.message.get('iu_list') is None): self.message.set('iu_list', list()) # add our IU self.message.get('iu_list').append(iu) # Call each component's process() function. Recall that # incremental components should expect multiple calls to # process() per utterance, one per IU. for component in self.pipeline: component.process(self.message, **self.context) output = self.default_output_attributes() output.update( self.message.as_dict( only_output_properties=only_output_properties)) return output