diff --git a/changelog/5446.improvement.rst b/changelog/5446.improvement.rst new file mode 100644 index 000000000000..e558ff098820 --- /dev/null +++ b/changelog/5446.improvement.rst @@ -0,0 +1,6 @@ +The endpoint ``PUT /conversations//tracker/events`` no longer +adds session start events (to learn more about conversation sessions, please +see :ref:`session_config`) in addition to the events which were sent in the request +payload. To achieve the old behavior send a +``GET /conversations//tracker`` +request before appending events. diff --git a/rasa/core/processor.py b/rasa/core/processor.py index 7ea78c3638fc..7fcfb5123c8e 100644 --- a/rasa/core/processor.py +++ b/rasa/core/processor.py @@ -189,7 +189,7 @@ async def get_tracker_with_session_start( Tracker for `sender_id` if available, `None` otherwise. """ - tracker = self._get_tracker(sender_id) + tracker = self.get_tracker(sender_id) if not tracker: return None @@ -197,6 +197,26 @@ async def get_tracker_with_session_start( return tracker + def get_tracker(self, conversation_id: Text) -> Optional[DialogueStateTracker]: + """Get the tracker for a conversation. + + In contrast to `get_tracker_with_session_start` this does not add any + `action_session_start` or `session_start` events at the beginning of a + conversation. + + Args: + conversation_id: The ID of the conversation for which the history should be + retrieved. + + Returns: + Tracker for the conversation. Creates an empty tracker in case it's a new + conversation. + """ + conversation_id = conversation_id or UserMessage.DEFAULT_SENDER_ID + return self.tracker_store.get_or_create_tracker( + conversation_id, append_action_listen=False + ) + async def log_message( self, message: UserMessage, should_save_tracker: bool = True ) -> Optional[DialogueStateTracker]: @@ -713,12 +733,6 @@ def _has_session_expired(self, tracker: DialogueStateTracker) -> bool: return has_expired - def _get_tracker(self, sender_id: Text) -> Optional[DialogueStateTracker]: - sender_id = sender_id or UserMessage.DEFAULT_SENDER_ID - return self.tracker_store.get_or_create_tracker( - sender_id, append_action_listen=False - ) - def _save_tracker(self, tracker: DialogueStateTracker) -> None: self.tracker_store.save(tracker) diff --git a/rasa/server.py b/rasa/server.py index b3940f11bf42..bfe0043f80c6 100644 --- a/rasa/server.py +++ b/rasa/server.py @@ -214,17 +214,25 @@ def event_verbosity_parameter( async def get_tracker( processor: "MessageProcessor", conversation_id: Text -) -> Optional[DialogueStateTracker]: +) -> DialogueStateTracker: """Get tracker object from `MessageProcessor`.""" tracker = await processor.get_tracker_with_session_start(conversation_id) + _validate_tracker(tracker, conversation_id) + + # `_validate_tracker` ensures we can't return `None` so `Optional` is not needed + return tracker # pytype: disable=bad-return-type + + +def _validate_tracker( + tracker: Optional[DialogueStateTracker], conversation_id: Text +) -> None: if not tracker: raise ErrorResponse( 409, "Conflict", - f"Could not retrieve tracker with id '{conversation_id}'. Most likely " + f"Could not retrieve tracker with ID '{conversation_id}'. Most likely " f"because there is no domain set on the agent.", ) - return tracker def validate_request_body(request: Request, error_message: Text): @@ -479,12 +487,10 @@ async def append_events(request: Request, conversation_id: Text): try: async with app.agent.lock_store.lock(conversation_id): - tracker = await get_tracker( - app.agent.create_processor(), conversation_id - ) + processor = app.agent.create_processor() + tracker = processor.get_tracker(conversation_id) + _validate_tracker(tracker, conversation_id) - # Get events after tracker initialization to ensure that generated - # timestamps are after potential session events. events = _get_events_from_request_body(request) for event in events: @@ -590,15 +596,6 @@ async def execute_action(request: Request, conversation_id: Text): {"parameter": "name", "in": "body"}, ) - # Deprecation warning - raise_warning( - "Triggering actions via the execute endpoint is deprecated. " - "Trigger an intent via the " - "`/conversations//trigger_intent` " - "endpoint instead.", - FutureWarning, - ) - policy = request_params.get("policy", None) confidence = request_params.get("confidence", None) verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART) diff --git a/tests/test_server.py b/tests/test_server.py index 9f4ad48889d9..89f0fe33c920 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -627,7 +627,7 @@ def test_requesting_non_existent_tracker(rasa_app: SanicTestClient): @pytest.mark.parametrize("event", test_events) -def test_pushing_event(rasa_app, event): +def test_pushing_event(rasa_app: SanicTestClient, event: Event): sender_id = str(uuid.uuid1()) conversation = f"/conversations/{sender_id}" @@ -635,6 +635,7 @@ def test_pushing_event(rasa_app, event): # Remove timestamp so that a new one is assigned on the server serialized_event.pop("timestamp") + time_before_adding_events = time.time() _, response = rasa_app.post( f"{conversation}/tracker/events", json=serialized_event, @@ -647,17 +648,17 @@ def test_pushing_event(rasa_app, event): tracker = tracker_response.json assert tracker is not None - assert len(tracker.get("events")) == 4 + assert len(tracker.get("events")) == 1 - evt = tracker.get("events")[3] + evt = tracker.get("events")[0] deserialised_event = Event.from_parameters(evt) assert deserialised_event == event - assert deserialised_event.timestamp > tracker.get("events")[2]["timestamp"] + assert deserialised_event.timestamp > time_before_adding_events def test_push_multiple_events(rasa_app: SanicTestClient): - cid = str(uuid.uuid1()) - conversation = f"/conversations/{cid}" + conversation_id = str(uuid.uuid1()) + conversation = f"/conversations/{conversation_id}" events = [e.as_dict() for e in test_events] _, response = rasa_app.post( @@ -668,13 +669,12 @@ def test_push_multiple_events(rasa_app: SanicTestClient): assert response.json is not None assert response.status == 200 - _, tracker_response = rasa_app.get(f"/conversations/{cid}/tracker") + _, tracker_response = rasa_app.get(f"/conversations/{conversation_id}/tracker") tracker = tracker_response.json assert tracker is not None # there is also an `ACTION_LISTEN` event at the start - assert len(tracker.get("events")) == len(test_events) + 3 - assert tracker.get("events")[3:] == events + assert tracker.get("events") == events def test_put_tracker(rasa_app: SanicTestClient):