From b2af7fa9c44a47704fd68c780c7bde55fa4e3ba1 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Wed, 18 May 2022 16:52:28 +0200 Subject: [PATCH 01/11] update API spec --- docs/static/spec/rasa.yml | 492 ++++++++++++++++++++++++++++++++--- rasa/server.py | 20 +- rasa/shared/core/trackers.py | 31 +++ 3 files changed, 506 insertions(+), 37 deletions(-) diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index ab059bf75436..fb6ce60fefd3 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -81,19 +81,9 @@ paths: type: object properties: model_id: - type: object - description: Fingerprint of the loaded model - example: - config: - - 7625d69d93053ac8520a544d0852c626 - domain: - - 229b51e41876bbcbbbfbeddf79548d5a - messages: - - cf7eda7edcae128a75ee8c95d3bbd680 - stories: - - b5facea681fd00bc7ecc6818c70d9639 - trained_at: 1556527123.42784 - version: 1.0.0 + type: string + description: ID of the loaded model + example: 75a985b7b86d442ca013d61ea4781b22 model_file: type: string description: Path of the loaded model @@ -178,9 +168,7 @@ paths: schema: oneOf: - $ref: '#/components/schemas/Event' - - type: array - items: - $ref: '#/components/schemas/Event' + - $ref: '#/components/schemas/EventList' responses: 200: $ref: '#/components/responses/200Tracker' @@ -626,12 +614,6 @@ paths: requestBody: required: true content: - application/json: - schema: - type: object - properties: - rasa_nlu_data: - $ref: '#/components/schemas/RasaNLUData' application/x-yaml: schema: type: string @@ -645,6 +627,11 @@ paths: - hey - hello - hi + - intent: bye + examples: | + - goodbye + - bye + - cheers pipeline: - name: KeywordIntentClassifier @@ -893,7 +880,13 @@ components: all_sessions: in: query name: all_sessions - description: Fetch test stories for all conversation sessions + description: >- + Whether to fetch all sessions in a conversation, or only the latest session + + * `true` - fetch all conversation sessions. + + * `false` - [default] fetch only the latest conversation session. + example: false schema: type: boolean @@ -921,20 +914,33 @@ components: description: >- Specify which events of the tracker the response should contain. + + * `ALL` - every logged event. + + * `APPLIED` - only events that contribute to the trackers state. This excludes reverted utterances and actions that got undone. + + * `AFTER_RESTART` - all events since the last `restarted` event. + This includes utterances that got reverted and actions that got undone. + + * `NONE` - no events. + example: AFTER_RESTART schema: type: string default: AFTER_RESTART enum: - - AFTER_RESTART - ALL - APPLIED + - AFTER_RESTART - NONE emulation_mode: in: query name: emulation_mode description: >- - Specify the emulation mode. + Specify the emulation mode to use. + Emulation mode transforms the response JSON to the format expected by the service specified as the emulation_mode. + Requests must still be sent in the regular Rasa format. + example: LUIS schema: type: string @@ -1237,6 +1243,32 @@ components: Latest bot action. Event: + oneOf: + - $ref: '#/components/schemas/UserEvent' + - $ref: '#/components/schemas/BotEvent' + - $ref: '#/components/schemas/SessionStartedEvent' + - $ref: '#/components/schemas/ActionEvent' + - $ref: '#/components/schemas/SlotEvent' + - $ref: '#/components/schemas/ResetSlotsEvent' + - $ref: '#/components/schemas/RestartEvent' + - $ref: '#/components/schemas/ReminderEvent' + - $ref: '#/components/schemas/CancelReminderEvent' + - $ref: '#/components/schemas/PauseEvent' + - $ref: '#/components/schemas/ResumeEvent' + - $ref: '#/components/schemas/FollowupEvent' + - $ref: '#/components/schemas/ExportEvent' + - $ref: '#/components/schemas/UndoEvent' + - $ref: '#/components/schemas/RewindEvent' + - $ref: '#/components/schemas/AgentEvent' + - $ref: '#/components/schemas/EntitiesEvent' + - $ref: '#/components/schemas/UserFeaturizationEvent' + - $ref: '#/components/schemas/ActionExecutionRejectedEvent' + - $ref: '#/components/schemas/FormValidationEvent' + - $ref: '#/components/schemas/LoopInterruptedEvent' + - $ref: '#/components/schemas/FormEvent' + - $ref: '#/components/schemas/ActiveLoopEvent' + + BasicEvent: type: object properties: event: @@ -1246,9 +1278,413 @@ components: timestamp: type: integer description: Time of application - example: 1559744410 + example: null + metadata: + type: object + properties: {} + example: + arbitrary_metadata_key: "some string" + more_metadata: 1.0 required: ["event"] + UserEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + description: >- + Event for incoming user message. + properties: + event: + enum: + - user + example: "user" + text: + type: + - string + - 'null' + description: Text of user message. + input_channel: + type: + - string + - 'null' + message_id: + type: + - string + - 'null' + parse_data: + type: object + properties: + text: + type: + - string + - 'null' + intent_ranking: + type: array + items: + type: object + properties: + name: + type: string + confidence: + type: number + intent: + type: object + properties: + name: + type: string + confidence: + type: number + entities: + type: array + items: + type: object + properties: + start: + type: integer + end: + type: integer + entity: + type: string + confidence: + type: number + extractor: + type: + - string + - 'null' + value: {} + role: + type: + - string + - 'null' + group: + type: + - string + - 'null' + required: + - entity + - value + response_selector: + type: object + oneOf: + - properties: + all_retrieval_intents: + type: array + - patternProperties: + '[\w/]': + type: object + properties: + response: + type: object + properties: + responses: + type: array + items: + type: object + properties: + text: + type: string + response_templates: + type: array + items: + type: object + properties: + text: + type: string + confidence: + type: number + intent_response_key: + type: string + utter_action: + type: string + template_name: + type: string + ranking: + type: array + items: + type: object + properties: + id: + type: number + confidence: + type: number + intent_response_key: + type: string + + ActionEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - action + example: "action" + policy: + type: + - string + - 'null' + confidence: + type: + - number + - 'null' + name: + type: + - string + - 'null' + hide_rule_turn: + type: boolean + action_text: + type: + - string + - 'null' + + SlotEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - slot + example: "slot" + name: + type: string + value: {} + required: + - name + - value + + EntitiesEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - entities + example: "entities" + entities: + type: array + items: + type: object + properties: + start: + type: integer + end: + type: integer + entity: + type: string + confidence: + type: number + extractor: + type: + - string + - 'null' + value: {} + role: + type: + - string + - 'null' + group: + type: + - string + - 'null' + required: + - entity + - value + required: + - entities + + UserFeaturizationEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - user_featurization + example: "user_featurization" + + CancelReminderEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - cancel_reminder + example: "cancel_reminder" + + ReminderEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - reminder + example: "reminder" + + ActionExecutionRejectedEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - action_execution_rejected + example: "action_execution_rejected" + + FormValidationEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - form_validation + example: "form_validation" + + LoopInterruptedEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - loop_interrupted + example: "loop_interrupted" + + FormEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - form + example: "form" + + ActiveLoopEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - active_loop + example: "active_loop" + + ResetSlotsEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - reset_slots + example: "reset_slots" + + ResumeEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - resume + example: "resume" + + PauseEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - pause + example: "pause" + + FollowupEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - followup + example: "followup" + + ExportEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - export + example: "export" + + RestartEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - restart + example: "restart" + + UndoEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - undo + example: "undo" + + RewindEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - rewind + example: "rewind" + + BotEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - bot + example: "bot" + + SessionStartedEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - session_started + example: "session_started" + + AgentEvent: + allOf: + - $ref: '#/components/schemas/BasicEvent' + - type: object + properties: + event: + enum: + - agent + example: "agent" + EventList: type: array items: @@ -1358,10 +1794,8 @@ components: description: Bot is paused example: false events: - type: array description: Event history - items: - $ref: '#/components/schemas/Event' + $ref: '#/components/schemas/EventList' latest_input_channel: type: string description: Communication channel diff --git a/rasa/server.py b/rasa/server.py index a80c7f9a35e6..b28e2431056b 100644 --- a/rasa/server.py +++ b/rasa/server.py @@ -64,7 +64,11 @@ import rasa.shared.core.events from rasa.shared.core.events import Event from rasa.core.test import test -from rasa.shared.core.trackers import DialogueStateTracker, EventVerbosity +from rasa.shared.core.trackers import ( + DialogueStateTracker, + EventVerbosity, + get_events_before_restart, +) from rasa.core.utils import AvailableEndpoints from rasa.nlu.emulators.no_emulator import NoEmulator import rasa.nlu.test @@ -317,6 +321,8 @@ async def get_test_stories( else: trackers = [await processor.get_tracker(conversation_id)] + trackers = [get_events_before_restart(tracker) for tracker in trackers] + if until_time is not None: trackers = [tracker.travel_back_in_time(until_time) for tracker in trackers] # keep only non-empty trackers @@ -1152,13 +1158,11 @@ async def evaluate_intents( test_data, config_file, int(cross_validation_folds) ) else: - test_data = _test_data_file_from_payload(request, temporary_directory) - if cross_validation_folds: - raise ErrorResponse( - HTTPStatus.BAD_REQUEST, - "TestingError", - "Cross-validation is only supported for YAML data.", - ) + raise ErrorResponse( + HTTPStatus.BAD_REQUEST, + "TestingError", + "NLU evaluation is only supported for YAML data.", + ) if not cross_validation_folds: test_coroutine = _evaluate_model_using_test_set( diff --git a/rasa/shared/core/trackers.py b/rasa/shared/core/trackers.py index ab2835d36649..7a3a487873c8 100644 --- a/rasa/shared/core/trackers.py +++ b/rasa/shared/core/trackers.py @@ -62,6 +62,7 @@ ) from rasa.shared.core.domain import Domain, State from rasa.shared.core.slots import AnySlot, Slot +from rasa.shared.utils.schemas.events import RESTARTED if TYPE_CHECKING: from rasa.shared.core.events import NLUPredictionData @@ -940,3 +941,33 @@ def get_trackers_for_conversation_sessions( ) for evts in split_conversations ] + + +def get_events_before_restart( + tracker: DialogueStateTracker, +) -> DialogueStateTracker: + """Returns the tracker including all events before the first `restart` event. + + If no `restart` is present, returns tracker with all events. + + Args: + tracker: Instance of `DialogueStateTracker`. + + Returns: + The tracker up until the first restart event. + """ + split_conversations = events.split_events( + tracker.events, + Restarted, + include_splitting_event=False, + ) + if len(split_conversations) == 0: + return tracker + + events_to_include = split_conversations[0] + return DialogueStateTracker.from_events( + tracker.sender_id, + events_to_include, + tracker.slots.values(), + sender_source=tracker.sender_source, + ) From ca59bc2b7edaf2cdc0a884b43b850d0683ca5071 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Thu, 19 May 2022 12:30:07 +0200 Subject: [PATCH 02/11] update authentication methods section --- docs/docs/http-api.mdx | 43 ++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/docs/docs/http-api.mdx b/docs/docs/http-api.mdx index d9a09dd8c3dc..19ff60730181 100644 --- a/docs/docs/http-api.mdx +++ b/docs/docs/http-api.mdx @@ -50,27 +50,24 @@ The [SocketIO channel](./connectors/your-own-website.mdx#websocket-channel) does ## Security Considerations -We recommend to not expose the Rasa Server to the outside world, but -rather connect to it from your backend over a private connection (e.g. -between docker containers). +We recommend that you don't expose the Rasa Server to the outside world directly, but +rather connect to it from via e.g. Nginx as a reverse proxy. Nevertheless, there are two authentication methods built in: ### Token Based Auth -Pass in the token using `--auth-token thisismysecret` when starting +To use a plaintext token to secure your server, specify the token in the argument `--auth-token thisismysecret` when starting the server: ```bash rasa run \ - -m models \ --enable-api \ - --log-file out.log \ --auth-token thisismysecret ``` -Your requests should pass the token, in our case `thisismysecret`, -as a parameter: +Any clients sending requests to the server must pass the token +as a query parameter, or the request will be rejected. For example, to fetch a tracker from the server: ```bash curl -XGET localhost:5005/conversations/default/tracker?token=thisismysecret @@ -78,26 +75,19 @@ curl -XGET localhost:5005/conversations/default/tracker?token=thisismysecret ### JWT Based Auth -Enable JWT based authentication using `--jwt-secret thisismysecret`. -Requests to the server need to contain a valid JWT token in -the `Authorization` header that is signed using this secret -and the `HS256` algorithm. +To use JWT based authentication, specify the JWT secret in the argument `--jwt-secret thisismysecret` +on startup of the server: -The token's payload must contain an object under the `user` key, -which in turn must contain the `username` and `role` attributes. -If the `role` is `admin`, all endpoints are accessible. -If the `role` is `user`, endpoints with a `sender_id` parameter are only accessible -if the `sender_id` matches the payload's `username` property. ```bash rasa run \ - -m models \ --enable-api \ - --log-file out.log \ --jwt-secret thisismysecret ``` -Your requests should have set a proper JWT header: +Client requests to the server will need to contain a valid JWT token in +the `Authorization` header that is signed using this secret +and the `HS256` algorithm e.g. ```text "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ" @@ -106,6 +96,8 @@ Your requests should have set a proper JWT header: "Gl8eZFVfKXA6jhncgRn-I" ``` +The token's payload must contain an object under the `user` key, +which in turn must contain the `username` and `role` attributes. The following is an example payload for a JWT token: ```json @@ -117,5 +109,16 @@ The following is an example payload for a JWT token: } ``` +If the `role` is `admin`, all endpoints are accessible. +If the `role` is `user`, endpoints with a `sender_id` parameter are only accessible +if the `sender_id` matches the payload's `username` property. + +```bash +rasa run \ + -m models \ + --enable-api \ + --jwt-secret thisismysecret +``` + To create and encode the token, you can use tools such as the [JWT Debugger](https://jwt.io/), or a Python module such as [PyJWT](https://pyjwt.readthedocs.io/en/latest/). From b43e1db290335b39718e99449a9bac9ec3381fd9 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Thu, 19 May 2022 12:33:53 +0200 Subject: [PATCH 03/11] changelog --- changelog/11130.bugfix.md | 2 ++ changelog/11130.doc.md | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 changelog/11130.bugfix.md create mode 100644 changelog/11130.doc.md diff --git a/changelog/11130.bugfix.md b/changelog/11130.bugfix.md new file mode 100644 index 000000000000..897bdd4c2cf1 --- /dev/null +++ b/changelog/11130.bugfix.md @@ -0,0 +1,2 @@ +Exclude `restart` events from trackers before composing stories from them in response to requests to +`/conversations/{conversation_id}/story` to avoid returning blank stories for restarted sessions. \ No newline at end of file diff --git a/changelog/11130.doc.md b/changelog/11130.doc.md new file mode 100644 index 000000000000..662e4ebb411b --- /dev/null +++ b/changelog/11130.doc.md @@ -0,0 +1,12 @@ +Clarify aspects of the API spec +GET /status endpoint: Correct response schema for model_id - a string, not an object. + +GET /conversations/{conversation_id}/tracker: Describe each of the enum options for include_events query parameter + +POST & PUT /conversations/{conversation_id}/tracker/eventss: Events schema added for each event type + +GET /conversations/{conversation_id}/story: Clarified the all_sessions query parameter and default behaviour. + +POST /model/test/intents : Remove JSON payload option since it is not supported + +POST /model/parse: Explain what emulation_mode is and how it affects response results \ No newline at end of file From 924d3b5fb09f7a204a06823828cac2276729b824 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Thu, 19 May 2022 14:24:32 +0200 Subject: [PATCH 04/11] lint --- rasa/shared/core/trackers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rasa/shared/core/trackers.py b/rasa/shared/core/trackers.py index 7a3a487873c8..61a7b9ac7f43 100644 --- a/rasa/shared/core/trackers.py +++ b/rasa/shared/core/trackers.py @@ -62,7 +62,6 @@ ) from rasa.shared.core.domain import Domain, State from rasa.shared.core.slots import AnySlot, Slot -from rasa.shared.utils.schemas.events import RESTARTED if TYPE_CHECKING: from rasa.shared.core.events import NLUPredictionData From 02c3955f5d4489e744d9f076a7f8454e523f1b81 Mon Sep 17 00:00:00 2001 From: m-vdb Date: Fri, 20 May 2022 09:14:05 +0200 Subject: [PATCH 05/11] fix nullable properties --- docs/static/spec/rasa.yml | 70 ++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index fb6ce60fefd3..4be06dd1561f 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -1299,25 +1299,21 @@ components: - user example: "user" text: - type: - - string - - 'null' + type: string + nullable: true description: Text of user message. input_channel: - type: - - string - - 'null' + type: string + nullable: true message_id: - type: - - string - - 'null' + type: string + nullable: true parse_data: type: object properties: text: - type: - - string - - 'null' + type: string + nullable: true intent_ranking: type: array items: @@ -1348,18 +1344,15 @@ components: confidence: type: number extractor: - type: - - string - - 'null' + type: string + nullable: true value: {} role: - type: - - string - - 'null' + type: string + nullable: true group: - type: - - string - - 'null' + type: string + nullable: true required: - entity - value @@ -1420,23 +1413,19 @@ components: - action example: "action" policy: - type: - - string - - 'null' + type: string + nullable: true confidence: - type: - - number - - 'null' + type: number + nullable: true name: - type: - - string - - 'null' + type: string + nullable: true hide_rule_turn: type: boolean action_text: - type: - - string - - 'null' + type: string + nullable: true SlotEvent: allOf: @@ -1477,18 +1466,15 @@ components: confidence: type: number extractor: - type: - - string - - 'null' + type: string + nullable: true value: {} role: - type: - - string - - 'null' + type: string + nullable: true group: - type: - - string - - 'null' + type: string + nullable: true required: - entity - value From 46bc25b54ce5651e14529cf97a356cf41d0a0410 Mon Sep 17 00:00:00 2001 From: m-vdb Date: Fri, 20 May 2022 09:14:16 +0200 Subject: [PATCH 06/11] fix indentation of required blocks --- docs/static/spec/rasa.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index 4be06dd1561f..e7697b9149cc 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -1439,9 +1439,9 @@ components: name: type: string value: {} - required: - - name - - value + required: + - name + - value EntitiesEvent: allOf: @@ -1478,8 +1478,8 @@ components: required: - entity - value - required: - - entities + required: + - entities UserFeaturizationEvent: allOf: From dc4930d50b800c6a059f87b3f92f27468d995aad Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Fri, 20 May 2022 10:57:30 +0200 Subject: [PATCH 07/11] replace parse_data with ref to parse_result --- docs/static/spec/rasa.yml | 99 ++------------------------------------- 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index e7697b9149cc..696edb80794d 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -1243,7 +1243,7 @@ components: Latest bot action. Event: - oneOf: + anyOf: - $ref: '#/components/schemas/UserEvent' - $ref: '#/components/schemas/BotEvent' - $ref: '#/components/schemas/SessionStartedEvent' @@ -1285,7 +1285,8 @@ components: example: arbitrary_metadata_key: "some string" more_metadata: 1.0 - required: ["event"] + required: + - event UserEvent: allOf: @@ -1309,99 +1310,7 @@ components: type: string nullable: true parse_data: - type: object - properties: - text: - type: string - nullable: true - intent_ranking: - type: array - items: - type: object - properties: - name: - type: string - confidence: - type: number - intent: - type: object - properties: - name: - type: string - confidence: - type: number - entities: - type: array - items: - type: object - properties: - start: - type: integer - end: - type: integer - entity: - type: string - confidence: - type: number - extractor: - type: string - nullable: true - value: {} - role: - type: string - nullable: true - group: - type: string - nullable: true - required: - - entity - - value - response_selector: - type: object - oneOf: - - properties: - all_retrieval_intents: - type: array - - patternProperties: - '[\w/]': - type: object - properties: - response: - type: object - properties: - responses: - type: array - items: - type: object - properties: - text: - type: string - response_templates: - type: array - items: - type: object - properties: - text: - type: string - confidence: - type: number - intent_response_key: - type: string - utter_action: - type: string - template_name: - type: string - ranking: - type: array - items: - type: object - properties: - id: - type: number - confidence: - type: number - intent_response_key: - type: string + $ref: '#/components/schemas/ParseResult' ActionEvent: allOf: From ed6f54d5395517adc45f62c183189cc020ec706b Mon Sep 17 00:00:00 2001 From: Melinda Loubser <32034278+melindaloubser1@users.noreply.github.com> Date: Mon, 23 May 2022 11:26:55 +0200 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: Anca Lita <27920906+ancalita@users.noreply.github.com> --- docs/docs/http-api.mdx | 2 +- docs/static/spec/rasa.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/http-api.mdx b/docs/docs/http-api.mdx index 19ff60730181..77ecb0ca9f62 100644 --- a/docs/docs/http-api.mdx +++ b/docs/docs/http-api.mdx @@ -51,7 +51,7 @@ The [SocketIO channel](./connectors/your-own-website.mdx#websocket-channel) does ## Security Considerations We recommend that you don't expose the Rasa Server to the outside world directly, but -rather connect to it from via e.g. Nginx as a reverse proxy. +rather connect to it via e.g. Nginx. Nevertheless, there are two authentication methods built in: diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index 696edb80794d..a3456dd5e8eb 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -1352,7 +1352,7 @@ components: - name - value - EntitiesEvent: + EntitiesAddedEvent: allOf: - $ref: '#/components/schemas/BasicEvent' - type: object From 852386935b30589b159815d909ea3d6ccfcb047c Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Wed, 25 May 2022 14:30:37 +0200 Subject: [PATCH 09/11] review --- docs/static/spec/rasa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index a3456dd5e8eb..9b8c5efe3c6b 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -1260,7 +1260,7 @@ components: - $ref: '#/components/schemas/UndoEvent' - $ref: '#/components/schemas/RewindEvent' - $ref: '#/components/schemas/AgentEvent' - - $ref: '#/components/schemas/EntitiesEvent' + - $ref: '#/components/schemas/EntitiesAddedEvent' - $ref: '#/components/schemas/UserFeaturizationEvent' - $ref: '#/components/schemas/ActionExecutionRejectedEvent' - $ref: '#/components/schemas/FormValidationEvent' From b102fca6aeff4a250cd266902c82fbea99838697 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Fri, 27 May 2022 13:52:38 +0200 Subject: [PATCH 10/11] fix json NLU testing evaluation --- changelog/11130.bugfix.md | 2 -- docs/static/spec/rasa.yml | 58 ++++++++++++++++++++++++++++++++++-- rasa/server.py | 58 +++++++++++++++++++++++++++++++----- rasa/shared/core/trackers.py | 30 ------------------- 4 files changed, 106 insertions(+), 42 deletions(-) delete mode 100644 changelog/11130.bugfix.md diff --git a/changelog/11130.bugfix.md b/changelog/11130.bugfix.md deleted file mode 100644 index 897bdd4c2cf1..000000000000 --- a/changelog/11130.bugfix.md +++ /dev/null @@ -1,2 +0,0 @@ -Exclude `restart` events from trackers before composing stories from them in response to requests to -`/conversations/{conversation_id}/story` to avoid returning blank stories for restarted sessions. \ No newline at end of file diff --git a/docs/static/spec/rasa.yml b/docs/static/spec/rasa.yml index 9b8c5efe3c6b..a329999d0b32 100644 --- a/docs/static/spec/rasa.yml +++ b/docs/static/spec/rasa.yml @@ -635,6 +635,10 @@ paths: pipeline: - name: KeywordIntentClassifier + application/json: + schema: + $ref: '#/components/schemas/RasaNLUData' + responses: 200: description: NLU evaluation result @@ -834,11 +838,32 @@ components: type: apiKey in: query name: token + description: | + A plaintext token to secure your server, specified at startup in the argument `--auth-token thisismysecret` JWT: type: http scheme: bearer bearerFormat: JWT - + description: | + A JWT token that is signed using the JWT secret specified at startup in the argument `--jwt-secret thisismysecret`, + using the `HS256` algorithm. + + The token's payload must contain an object under the `user` key, + which in turn must contain the `username` and `role` attributes. + The following is an example payload for a JWT token: + + ```json + { + "user": { + "username": "", + "role": "user" + } + } + ``` + + If the `role` is `admin`, all endpoints are accessible. + If the `role` is `user`, endpoints with a `sender_id` parameter are only accessible + if the `sender_id` matches the payload's `username` property. parameters: @@ -2103,7 +2128,6 @@ components: conversation_accuracy: $ref: '#/components/schemas/ConversationAccuracyReport' - ConversationAccuracyReport: type: object properties: @@ -2175,6 +2199,36 @@ components: type: array items: $ref: '#/components/schemas/CommonExample' + example: + rasa_nlu_data: + common_examples: + - text: hey + intent: greet + entities: [] + - text: dear sir + intent: greet + entities: [] + - text: i'm looking for a place to eat + intent: restaurant_search + entities: [] + - text: i'm looking for a place in the north of town + intent: restaurant_search + entities: + - start: 31 + end: 36 + value: north + entity: location + - text: show me a mexican place in the centre + intent: restaurant_search + entities: + - start: 31 + end: 37 + value: centre + entity: location + - start: 10 + end: 17 + value: mexican + entity: cuisine CommonExample: type: object diff --git a/rasa/server.py b/rasa/server.py index b28e2431056b..fac1ba42ac2a 100644 --- a/rasa/server.py +++ b/rasa/server.py @@ -38,6 +38,8 @@ import rasa.utils.common import rasa.shared.utils.common import rasa.shared.utils.io +import rasa.shared.utils.validation +import rasa.shared.nlu.training_data.schemas.data_schema import rasa.utils.endpoints import rasa.utils.io from rasa.shared.core.training_data.story_writer.yaml_story_writer import ( @@ -67,7 +69,6 @@ from rasa.shared.core.trackers import ( DialogueStateTracker, EventVerbosity, - get_events_before_restart, ) from rasa.core.utils import AvailableEndpoints from rasa.nlu.emulators.no_emulator import NoEmulator @@ -321,8 +322,6 @@ async def get_test_stories( else: trackers = [await processor.get_tracker(conversation_id)] - trackers = [get_events_before_restart(tracker) for tracker in trackers] - if until_time is not None: trackers = [tracker.travel_back_in_time(until_time) for tracker in trackers] # keep only non-empty trackers @@ -1158,11 +1157,15 @@ async def evaluate_intents( test_data, config_file, int(cross_validation_folds) ) else: - raise ErrorResponse( - HTTPStatus.BAD_REQUEST, - "TestingError", - "NLU evaluation is only supported for YAML data.", - ) + payload = _nlu_training_payload_from_json(request, temporary_directory) + test_data = payload.get("training_files") + + if cross_validation_folds: + raise ErrorResponse( + HTTPStatus.BAD_REQUEST, + "TestingError", + "Cross-validation is only supported for YAML data.", + ) if not cross_validation_folds: test_coroutine = _evaluate_model_using_test_set( @@ -1476,6 +1479,33 @@ def _training_payload_from_yaml( ) +def _nlu_training_payload_from_json( + request: Request, temp_dir: Path, file_name: Text = "data.json" +) -> Dict[Text, Any]: + logger.debug("Extracting JSON training data from request body.") + + rasa.shared.utils.validation.validate_training_data( + request.json, + rasa.shared.nlu.training_data.schemas.data_schema.rasa_nlu_data_schema(), + ) + training_data = temp_dir / file_name + rasa.shared.utils.io.dump_obj_as_json_to_file(training_data, request.json) + + model_output_directory = str(temp_dir) + if rasa.utils.endpoints.bool_arg(request, "save_to_default_model_directory", True): + model_output_directory = DEFAULT_MODELS_PATH + + return dict( + domain=str(training_data), + config=str(training_data), + training_files=str(temp_dir), + output=model_output_directory, + force_training=rasa.utils.endpoints.bool_arg(request, "force_training", False), + core_additional_arguments=_extract_core_additional_arguments(request), + nlu_additional_arguments=_extract_nlu_additional_arguments(request), + ) + + def _validate_yaml_training_payload(yaml_text: Text) -> None: try: RasaYAMLReader().validate(yaml_text) @@ -1488,6 +1518,18 @@ def _validate_yaml_training_payload(yaml_text: Text) -> None: ) +def _validate_json_training_payload(json_text: Text) -> None: + try: + RasaYAMLReader + except Exception as e: + raise ErrorResponse( + HTTPStatus.BAD_REQUEST, + "BadRequest", + f"The request body does not contain valid YAML. Error: {e}", + help_url=DOCS_URL_TRAINING_DATA, + ) + + def _extract_core_additional_arguments(request: Request) -> Dict[Text, Any]: return { "augmentation_factor": rasa.utils.endpoints.int_arg(request, "augmentation", 50) diff --git a/rasa/shared/core/trackers.py b/rasa/shared/core/trackers.py index 61a7b9ac7f43..ab2835d36649 100644 --- a/rasa/shared/core/trackers.py +++ b/rasa/shared/core/trackers.py @@ -940,33 +940,3 @@ def get_trackers_for_conversation_sessions( ) for evts in split_conversations ] - - -def get_events_before_restart( - tracker: DialogueStateTracker, -) -> DialogueStateTracker: - """Returns the tracker including all events before the first `restart` event. - - If no `restart` is present, returns tracker with all events. - - Args: - tracker: Instance of `DialogueStateTracker`. - - Returns: - The tracker up until the first restart event. - """ - split_conversations = events.split_events( - tracker.events, - Restarted, - include_splitting_event=False, - ) - if len(split_conversations) == 0: - return tracker - - events_to_include = split_conversations[0] - return DialogueStateTracker.from_events( - tracker.sender_id, - events_to_include, - tracker.slots.values(), - sender_source=tracker.sender_source, - ) From d2dbd44c6aa0809f4e059179999cc4fd65ce4bd6 Mon Sep 17 00:00:00 2001 From: melindaloubser1 Date: Fri, 27 May 2022 15:59:13 +0200 Subject: [PATCH 11/11] unit test for json training data --- rasa/server.py | 12 ------------ tests/test_server.py | 23 ++++++++++++++++++++++- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/rasa/server.py b/rasa/server.py index fac1ba42ac2a..c075c7892bfc 100644 --- a/rasa/server.py +++ b/rasa/server.py @@ -1518,18 +1518,6 @@ def _validate_yaml_training_payload(yaml_text: Text) -> None: ) -def _validate_json_training_payload(json_text: Text) -> None: - try: - RasaYAMLReader - except Exception as e: - raise ErrorResponse( - HTTPStatus.BAD_REQUEST, - "BadRequest", - f"The request body does not contain valid YAML. Error: {e}", - help_url=DOCS_URL_TRAINING_DATA, - ) - - def _extract_core_additional_arguments(request: Request) -> Dict[Text, Any]: return { "augmentation_factor": rasa.utils.endpoints.int_arg(request, "augmentation", 50) diff --git a/tests/test_server.py b/tests/test_server.py index 2443c1c2e83f..0dfadd435eaf 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -721,7 +721,28 @@ def test_training_payload_from_yaml_save_to_default_model_directory( assert payload.get("output") == expected -async def xtest_evaluate_stories(rasa_app: SanicASGITestClient, stories_path: Text): +@pytest.mark.parametrize( + "headers, expected", + [ + ({}, rasa.shared.constants.DEFAULT_MODELS_PATH), + ({"save_to_default_model_directory": False}, ANY), + ( + {"save_to_default_model_directory": True}, + rasa.shared.constants.DEFAULT_MODELS_PATH, + ), + ], +) +def test_nlu_training_payload_from_json(headers: Dict, expected: Text, tmp_path: Path): + request = Mock() + request.json = {"rasa_nlu_data": {"common_examples": []}} + request.args = headers + + payload = rasa.server._nlu_training_payload_from_json(request, tmp_path) + assert payload.get("output") + assert payload.get("output") == expected + + +async def test_evaluate_stories(rasa_app: SanicASGITestClient, stories_path: Text): stories = rasa.shared.utils.io.read_file(stories_path) _, response = await rasa_app.post(