Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward bot messages received from /tracker/events route to output channel #6273

Merged
merged 33 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f1cf3ce
forward bot messages to output channel
Jul 23, 2020
966e176
update api docs
Jul 24, 2020
aa86487
Merge remote-tracking branch 'upstream/master' into feed-bot-messages…
Aug 17, 2020
33d1b20
Merge branch 'master' into feed-bot-messages-back-to-output-channel
pheel Aug 19, 2020
35ed144
changelog entry
Aug 19, 2020
58a409f
group action side effects in public processor method
Aug 29, 2020
24251c3
add test
Aug 29, 2020
690615e
Merge branch 'master' into feed-bot-messages-back-to-output-channel
Aug 29, 2020
8a9c89c
formatting
Aug 31, 2020
eb4b954
update reminder tests
Aug 31, 2020
27715fd
changelog
Sep 8, 2020
df0b641
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 8, 2020
3b6abf4
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 8, 2020
31be982
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 8, 2020
0bff0e2
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 9, 2020
b82ee0e
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 9, 2020
fbaf53c
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 9, 2020
c96e1e9
Fix import
federicotdn Sep 9, 2020
0185336
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 9, 2020
1bfae3c
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 9, 2020
7c63cf1
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 10, 2020
2c61917
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 11, 2020
b65fd78
Merge branch 'master' into feed-bot-messages-back-to-output-channel
rasabot Sep 17, 2020
0a13818
Merge branch 'master' into feed-bot-messages-back-to-output-channel
rasabot Sep 17, 2020
d938a7e
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 21, 2020
9ce4057
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 21, 2020
32b0af4
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 22, 2020
396d51b
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 22, 2020
214efa9
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 22, 2020
d46650c
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 23, 2020
81963aa
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 23, 2020
d9e7c4e
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 23, 2020
e694361
Merge branch 'master' into feed-bot-messages-back-to-output-channel
federicotdn Sep 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/6273.improvement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `output_channel` query param to `/conversations/<conversation_id>/tracker/events` route. Forward bot messages received by route to output channel.
federicotdn marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 13 additions & 2 deletions docs/static/spec/rasa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,22 @@ paths:
summary: Append events to a tracker
description: >-
Appends one or multiple new events to the tracker state of the conversation.
Any existing events will be kept and the new events will be
appended, updating the existing state.
Any existing events will be kept and the new events will be appended,
updating the existing state.
parameters:
- $ref: '#/components/parameters/conversation_id'
- $ref: '#/components/parameters/include_events'
- $ref: '#/components/parameters/output_channel'
- in: query
name: execute_side_effects
schema:
type: boolean
default: False
description: >-
If `true`, any ``BotUttered`` event will be forwarded to the
channel specified in the ``output_channel`` parameter. Any
``ReminderScheduled`` or ``ReminderCancelled`` event will also be
processed.
requestBody:
required: true
content:
Expand Down
21 changes: 15 additions & 6 deletions rasa/core/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ async def handle_reminder(
reminder_event: ReminderScheduled,
sender_id: Text,
output_channel: OutputChannel,
nlg: NaturalLanguageGenerator,
) -> None:
"""Handle a reminder that is triggered asynchronously."""

Expand Down Expand Up @@ -580,6 +579,19 @@ def should_predict_another_action(action_name: Text) -> bool:

return action_name not in (ACTION_LISTEN_NAME, ACTION_SESSION_START_NAME)

async def execute_side_effects(
self,
events: List[Event],
tracker: DialogueStateTracker,
output_channel: OutputChannel,
) -> None:
"""Send bot messages, schedule and cancel reminders that are logged
in the events array."""

await self._send_bot_messages(events, tracker, output_channel)
await self._schedule_reminders(events, tracker, output_channel)
await self._cancel_reminders(events, tracker)

@staticmethod
async def _send_bot_messages(
events: List[Event],
Expand All @@ -599,7 +611,6 @@ async def _schedule_reminders(
events: List[Event],
tracker: DialogueStateTracker,
output_channel: OutputChannel,
nlg: NaturalLanguageGenerator,
) -> None:
"""Uses the scheduler to time a job to trigger the passed reminder.

Expand All @@ -614,7 +625,7 @@ async def _schedule_reminders(
self.handle_reminder,
"date",
run_date=e.trigger_date_time,
args=[e, tracker.sender_id, output_channel, nlg],
args=[e, tracker.sender_id, output_channel],
id=e.name,
replace_existing=True,
name=e.scheduled_job_name(tracker.sender_id),
Expand Down Expand Up @@ -674,9 +685,7 @@ async def _run_action(
):
self._log_slots(tracker)

await self._send_bot_messages(events, tracker, output_channel)
await self._schedule_reminders(events, tracker, output_channel, nlg)
await self._cancel_reminders(events, tracker)
await self.execute_side_effects(events, tracker, output_channel)

return self.should_predict_another_action(action.name())

Expand Down
8 changes: 8 additions & 0 deletions rasa/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@

OUTPUT_CHANNEL_QUERY_KEY = "output_channel"
USE_LATEST_INPUT_CHANNEL_AS_OUTPUT_CHANNEL = "latest"
EXECUTE_SIDE_EFFECTS_QUERY_KEY = "execute_side_effects"


class ErrorResponse(Exception):
Expand Down Expand Up @@ -497,12 +498,19 @@ async def append_events(request: Request, conversation_id: Text):
async with app.agent.lock_store.lock(conversation_id):
processor = app.agent.create_processor()
tracker = processor.get_tracker(conversation_id)
output_channel = _get_output_channel(request, tracker)
_validate_tracker(tracker, conversation_id)

events = _get_events_from_request_body(request)

for event in events:
tracker.update(event, app.agent.domain)
if rasa.utils.endpoints.bool_arg(
request, EXECUTE_SIDE_EFFECTS_QUERY_KEY, False
):
await processor.execute_side_effects(
events, tracker, output_channel
)
app.agent.tracker_store.save(tracker)

return response.json(tracker.current_state(verbosity))
Expand Down
16 changes: 8 additions & 8 deletions tests/core/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ async def test_reminder_scheduled(
default_processor.tracker_store.save(tracker)

await default_processor.handle_reminder(
reminder, sender_id, default_channel, default_processor.nlg
reminder, sender_id, default_channel,
)

# retrieve the updated tracker
Expand Down Expand Up @@ -197,7 +197,7 @@ async def test_reminder_aborted(

default_processor.tracker_store.save(tracker)
await default_processor.handle_reminder(
reminder, sender_id, default_channel, default_processor.nlg
reminder, sender_id, default_channel,
)

# retrieve the updated tracker
Expand Down Expand Up @@ -243,7 +243,7 @@ async def test_reminder_cancelled_multi_user(
for tracker in trackers:
default_processor.tracker_store.save(tracker)
await default_processor._schedule_reminders(
tracker.events, tracker, default_channel, default_processor.nlg
tracker.events, tracker, default_channel,
)
# check that the jobs were added
assert len((await jobs.scheduler()).get_jobs()) == 2
Expand Down Expand Up @@ -334,7 +334,7 @@ async def test_reminder_cancelled_by_name(
):
tracker = tracker_with_six_scheduled_reminders
await default_processor._schedule_reminders(
tracker.events, tracker, default_channel, default_processor.nlg
tracker.events, tracker, default_channel,
)

# cancel the sixth reminder
Expand All @@ -350,7 +350,7 @@ async def test_reminder_cancelled_by_entities(
):
tracker = tracker_with_six_scheduled_reminders
await default_processor._schedule_reminders(
tracker.events, tracker, default_channel, default_processor.nlg
tracker.events, tracker, default_channel,
)

# cancel the fourth reminder
Expand All @@ -370,7 +370,7 @@ async def test_reminder_cancelled_by_intent(
):
tracker = tracker_with_six_scheduled_reminders
await default_processor._schedule_reminders(
tracker.events, tracker, default_channel, default_processor.nlg
tracker.events, tracker, default_channel,
)

# cancel the third, fifth, and sixth reminder
Expand All @@ -386,7 +386,7 @@ async def test_reminder_cancelled_all(
):
tracker = tracker_with_six_scheduled_reminders
await default_processor._schedule_reminders(
tracker.events, tracker, default_channel, default_processor.nlg
tracker.events, tracker, default_channel,
)

# cancel all reminders
Expand All @@ -411,7 +411,7 @@ async def test_reminder_restart(

default_processor.tracker_store.save(tracker)
await default_processor.handle_reminder(
reminder, sender_id, default_channel, default_processor.nlg
reminder, sender_id, default_channel,
)

# retrieve the updated tracker
Expand Down
43 changes: 42 additions & 1 deletion tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@
import rasa.server
from rasa.core import events, utils
from rasa.core.agent import Agent
from rasa.core.channels import CollectingOutputChannel, RestInput, SlackInput
from rasa.core.channels import (
channel,
CollectingOutputChannel,
RestInput,
SlackInput,
CallbackInput,
)
from rasa.core.channels.slack import SlackBot
from rasa.core.events import Event, UserUttered, SlotSet, BotUttered
from rasa.core.trackers import DialogueStateTracker
from rasa.model import unpack_model
from rasa.nlu.constants import INTENT_NAME_KEY
from rasa.utils.endpoints import EndpointConfig
from rasa import utils as rasa_utils
from tests.utilities import json_of_latest_request, latest_request
from sanic import Sanic
from sanic.testing import SanicTestClient
from tests.nlu.utilities import ResponseTest
Expand Down Expand Up @@ -889,6 +896,40 @@ def test_push_multiple_events(rasa_app: SanicTestClient):
assert tracker.get("events") == events


@pytest.mark.parametrize(
"params", ["?execute_side_effects=true&output_channel=callback", ""],
)
def test_pushing_event_while_executing_side_effects(rasa_server: Sanic, params: Text):
input_channel = CallbackInput(EndpointConfig("https://example.com/callback"))
channel.register([input_channel], rasa_server, "/webhooks/")
rasa_app = get_test_client(rasa_server)
sender_id = str(uuid.uuid1())
conversation = f"/conversations/{sender_id}"

serialized_event = test_events[1].as_dict()

with aioresponses() as mocked:
mocked.post(
"https://example.com/callback",
repeat=True,
headers={"Content-Type": "application/json"},
)
rasa_app.post(
f"{conversation}/tracker/events{params}",
json=serialized_event,
headers={"Content-Type": rasa.server.JSON_CONTENT_TYPE},
)

r = latest_request(mocked, "post", "https://example.com/callback")

if not params:
assert r is None
else:
message_received = json_of_latest_request(r)
assert message_received.get("recipient_id") == sender_id
assert message_received.get("text") == serialized_event.get("text")


def test_post_conversation_id_with_slash(rasa_app: SanicTestClient):
conversation_id = str(uuid.uuid1())
id_len = len(conversation_id) // 2
Expand Down