def test_slot_invalid_intent() -> None: """ Here, we will see that an entity will not fill an intent unless the intent has a slot for it. `intent_1` doesn't have a slot for an entity of type `entity_2`. """ intent_name = "intent_1" # ... a mock `Intent` intent = Intent(name=intent_name, score=0.8) # Setting up the slot-filler, both instantiation and plugin is created. (notice two calls). slot_filler = RuleBasedSlotFillerPlugin(rules=rules, dest="output.intents") # Create a mock `workflow` workflow = Workflow([slot_filler]) # and a mock `Entity`. body = "12th december" entity = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_1", values=[{ "key": "value" }], ) # The RuleBasedSlotFillerPlugin specifies that it expects `Tuple[Intent, List[Entity])` on `access(workflow)`. workflow.set("output.intents", [1]).set("output.entities", [entity]) with pytest.raises(AttributeError): workflow.run(Input(utterances=body))
def test_workflow_invalid_set_value(): """ We can't set invalid values in workflow. """ workflow = Workflow() with pytest.raises(ValueError): workflow.set("output.intents", 10)
def test_workflow_invalid_set_path(): """ We can't set invalid values in workflow. """ workflow = Workflow() with pytest.raises(ValueError): workflow.set("invalid.path", [])
def test_slot_filling_multiple() -> None: """ Let's try filling both the slots this time with fill_multiple=True! `intent_2` supports both `entity_1` and `entity_2`. """ intent_name = "intent_2" # Setting up the slot-filler, both instantiation and plugin is created. (notice two calls). slot_filler = RuleBasedSlotFillerPlugin(rules=rules, dest="output.intents", fill_multiple=True) # Create a mock `workflow` workflow = Workflow([slot_filler]) # ... a mock `Intent` intent = Intent(name=intent_name, score=0.8) # and mock `Entity`-ies. body = "12th december" entity_1 = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_1", values=[{ "key": "value" }], ) entity_2 = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_2", values=[{ "key": "value" }], ) # The RuleBasedSlotFillerPlugin specifies that it expects `Tuple[Intent, List[Entity])` on `access(workflow)`. workflow.set("output.intents", [intent]).set("output.entities", [entity_1, entity_2]) _, output = workflow.run(Input(utterances=body)) # `workflow.output[0]` is the `Intent` we created. # The `entity_1_slot` and `entity_2_slot` are filled. assert output[const.INTENTS][0]["slots"]["entity_1_slot"]["values"] == [ entity_1.json() ] assert output[const.INTENTS][0]["slots"]["entity_2_slot"]["values"] == [ entity_2.json() ]
def test_slot_competition_fill_multiple() -> None: """ What happens when we have two entities of the same type but different value? """ intent_name = "intent_1" # Setting up the slot-filler, both instantiation and plugin is created. (notice two calls). slot_filler = RuleBasedSlotFillerPlugin(rules=rules, dest="output.intents", fill_multiple=True) # Create a mock `workflow` workflow = Workflow([slot_filler]) # ... a mock `Intent` intent = Intent(name=intent_name, score=0.8) # Here we have two entities which compete for the same slot but have different values. body = "12th december" entity_1 = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_1", values=[{ "key": "value_1" }], ) entity_2 = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_1", values=[{ "key": "value_2" }], ) workflow.set("output.intents", [intent]).set("output.entities", [entity_1, entity_2]) _, output = workflow.run(Input(utterances=body)) # `workflow.output[0]` is the `Intent` we created. # The `entity_1_slot` and `entity_2_slot` are filled. assert output[const.INTENTS][0]["slots"]["entity_1_slot"]["values"] == [ entity_1.json(), entity_2.json(), ]
def test_slot_no_fill() -> None: """ Here, we will see that an entity will not fill an intent unless the intent has a slot for it. `intent_1` doesn't have a slot for an entity of type `entity_2`. """ intent_name = "intent_1" # Setting up the slot-filler, both instantiation and plugin is created. (notice two calls). slot_filler = RuleBasedSlotFillerPlugin(rules=rules, dest="output.intents") # Create a mock `workflow` workflow = Workflow([slot_filler]) # ... a mock `Intent` intent = Intent(name=intent_name, score=0.8) # and a mock `Entity`. body = "12th december" entity = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_2", values=[{ "key": "value" }], ) # The RuleBasedSlotFillerPlugin specifies that it expects `Tuple[Intent, List[Entity])` on `access(workflow)`. workflow.set("output.intents", [intent]).set("output.entities", [entity]) _, output = workflow.run(Input(utterances=body)) # `workflow.output[0]` is the `Intent` we created. # we can see that the `entity_2_slot` is not filled by our mock entity. assert "entity_1_slot" not in output[const.INTENTS][0]["slots"]
def test_slot_filling() -> None: """ This test case covers a trivial usage of a slot-filler. We have `rules` that demonstrate association of intents with entities and their respective slot-configuration. """ intent_name = "intent_1" slot_filler = RuleBasedSlotFillerPlugin(rules=rules, dest="output.intents") # Create a mock `workflow` workflow = Workflow([slot_filler]) # ... a mock `Intent` intent = Intent(name=intent_name, score=0.8) # and a mock `Entity`. body = "12th december" entity = BaseEntity( range={ "from": 0, "to": len(body) }, body=body, dim="default", entity_type="entity_1", values=[{ "key": "value" }], ) # The RuleBasedSlotFillerPlugin specifies that it expects `Tuple[Intent, List[Entity])` on `access(workflow)`. workflow.set("output.intents", [intent]).set("output.entities", [entity]) _, output = workflow.run(Input(utterances=body)) intent, *_ = output[const.INTENTS] # `workflow.output[0]` is the `Intent` we created. # so we are checking if the `entity_1_slot` is filled by our mock entity. assert intent["slots"]["entity_1_slot"]["values"][0] == entity.json()
def __call__(self, workflow: Workflow) -> None: """ Abstraction for plugin io. :param workflow: :type workflow: Workflow :raises TypeError: If access method is missing, we can't get inputs for transformation. """ logger.enable(str(self)) if self.debug else logger.disable(str(self)) if workflow.input is None: return if workflow.output is None: return if self.prevent(workflow.input, workflow.output): return value = self.utility(workflow.input, workflow.output) if value is not None and isinstance(self.dest, str): workflow.set(self.dest, value)