rasa-pro 3.9.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- README.md +415 -0
- rasa/__init__.py +10 -0
- rasa/__main__.py +156 -0
- rasa/anonymization/__init__.py +2 -0
- rasa/anonymization/anonymisation_rule_yaml_reader.py +91 -0
- rasa/anonymization/anonymization_pipeline.py +286 -0
- rasa/anonymization/anonymization_rule_executor.py +260 -0
- rasa/anonymization/anonymization_rule_orchestrator.py +120 -0
- rasa/anonymization/schemas/config.yml +47 -0
- rasa/anonymization/utils.py +118 -0
- rasa/api.py +146 -0
- rasa/cli/__init__.py +5 -0
- rasa/cli/arguments/__init__.py +0 -0
- rasa/cli/arguments/data.py +81 -0
- rasa/cli/arguments/default_arguments.py +165 -0
- rasa/cli/arguments/evaluate.py +65 -0
- rasa/cli/arguments/export.py +51 -0
- rasa/cli/arguments/interactive.py +74 -0
- rasa/cli/arguments/run.py +204 -0
- rasa/cli/arguments/shell.py +13 -0
- rasa/cli/arguments/test.py +211 -0
- rasa/cli/arguments/train.py +263 -0
- rasa/cli/arguments/visualize.py +34 -0
- rasa/cli/arguments/x.py +30 -0
- rasa/cli/data.py +292 -0
- rasa/cli/e2e_test.py +586 -0
- rasa/cli/evaluate.py +222 -0
- rasa/cli/export.py +250 -0
- rasa/cli/inspect.py +63 -0
- rasa/cli/interactive.py +164 -0
- rasa/cli/license.py +65 -0
- rasa/cli/markers.py +78 -0
- rasa/cli/project_templates/__init__.py +0 -0
- rasa/cli/project_templates/calm/actions/__init__.py +0 -0
- rasa/cli/project_templates/calm/actions/action_template.py +27 -0
- rasa/cli/project_templates/calm/actions/add_contact.py +30 -0
- rasa/cli/project_templates/calm/actions/db.py +57 -0
- rasa/cli/project_templates/calm/actions/list_contacts.py +22 -0
- rasa/cli/project_templates/calm/actions/remove_contact.py +35 -0
- rasa/cli/project_templates/calm/config.yml +12 -0
- rasa/cli/project_templates/calm/credentials.yml +33 -0
- rasa/cli/project_templates/calm/data/flows/add_contact.yml +31 -0
- rasa/cli/project_templates/calm/data/flows/list_contacts.yml +14 -0
- rasa/cli/project_templates/calm/data/flows/remove_contact.yml +29 -0
- rasa/cli/project_templates/calm/db/contacts.json +10 -0
- rasa/cli/project_templates/calm/domain/add_contact.yml +39 -0
- rasa/cli/project_templates/calm/domain/list_contacts.yml +17 -0
- rasa/cli/project_templates/calm/domain/remove_contact.yml +38 -0
- rasa/cli/project_templates/calm/domain/shared.yml +10 -0
- rasa/cli/project_templates/calm/e2e_tests/cancelations/user_cancels_during_a_correction.yml +16 -0
- rasa/cli/project_templates/calm/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +7 -0
- rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_handle.yml +20 -0
- rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_name.yml +19 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +15 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_lists_contacts.yml +5 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact.yml +11 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact_from_list.yml +12 -0
- rasa/cli/project_templates/calm/endpoints.yml +45 -0
- rasa/cli/project_templates/default/actions/__init__.py +0 -0
- rasa/cli/project_templates/default/actions/actions.py +27 -0
- rasa/cli/project_templates/default/config.yml +44 -0
- rasa/cli/project_templates/default/credentials.yml +33 -0
- rasa/cli/project_templates/default/data/nlu.yml +91 -0
- rasa/cli/project_templates/default/data/rules.yml +13 -0
- rasa/cli/project_templates/default/data/stories.yml +30 -0
- rasa/cli/project_templates/default/domain.yml +34 -0
- rasa/cli/project_templates/default/endpoints.yml +42 -0
- rasa/cli/project_templates/default/tests/test_stories.yml +91 -0
- rasa/cli/project_templates/tutorial/actions.py +22 -0
- rasa/cli/project_templates/tutorial/config.yml +11 -0
- rasa/cli/project_templates/tutorial/credentials.yml +33 -0
- rasa/cli/project_templates/tutorial/data/flows.yml +8 -0
- rasa/cli/project_templates/tutorial/data/patterns.yml +6 -0
- rasa/cli/project_templates/tutorial/domain.yml +21 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +45 -0
- rasa/cli/run.py +135 -0
- rasa/cli/scaffold.py +269 -0
- rasa/cli/shell.py +141 -0
- rasa/cli/studio/__init__.py +0 -0
- rasa/cli/studio/download.py +62 -0
- rasa/cli/studio/studio.py +266 -0
- rasa/cli/studio/train.py +59 -0
- rasa/cli/studio/upload.py +77 -0
- rasa/cli/telemetry.py +102 -0
- rasa/cli/test.py +280 -0
- rasa/cli/train.py +260 -0
- rasa/cli/utils.py +464 -0
- rasa/cli/visualize.py +40 -0
- rasa/cli/x.py +206 -0
- rasa/constants.py +37 -0
- rasa/core/__init__.py +17 -0
- rasa/core/actions/__init__.py +0 -0
- rasa/core/actions/action.py +1225 -0
- rasa/core/actions/action_clean_stack.py +59 -0
- rasa/core/actions/action_exceptions.py +24 -0
- rasa/core/actions/action_run_slot_rejections.py +207 -0
- rasa/core/actions/action_trigger_chitchat.py +31 -0
- rasa/core/actions/action_trigger_flow.py +109 -0
- rasa/core/actions/action_trigger_search.py +31 -0
- rasa/core/actions/constants.py +5 -0
- rasa/core/actions/custom_action_executor.py +188 -0
- rasa/core/actions/forms.py +741 -0
- rasa/core/actions/grpc_custom_action_executor.py +251 -0
- rasa/core/actions/http_custom_action_executor.py +140 -0
- rasa/core/actions/loops.py +114 -0
- rasa/core/actions/two_stage_fallback.py +186 -0
- rasa/core/agent.py +555 -0
- rasa/core/auth_retry_tracker_store.py +122 -0
- rasa/core/brokers/__init__.py +0 -0
- rasa/core/brokers/broker.py +126 -0
- rasa/core/brokers/file.py +58 -0
- rasa/core/brokers/kafka.py +322 -0
- rasa/core/brokers/pika.py +386 -0
- rasa/core/brokers/sql.py +86 -0
- rasa/core/channels/__init__.py +55 -0
- rasa/core/channels/audiocodes.py +463 -0
- rasa/core/channels/botframework.py +338 -0
- rasa/core/channels/callback.py +84 -0
- rasa/core/channels/channel.py +419 -0
- rasa/core/channels/console.py +241 -0
- rasa/core/channels/development_inspector.py +93 -0
- rasa/core/channels/facebook.py +419 -0
- rasa/core/channels/hangouts.py +329 -0
- rasa/core/channels/inspector/.eslintrc.cjs +25 -0
- rasa/core/channels/inspector/.gitignore +23 -0
- rasa/core/channels/inspector/README.md +54 -0
- rasa/core/channels/inspector/assets/favicon.ico +0 -0
- rasa/core/channels/inspector/assets/rasa-chat.js +2 -0
- rasa/core/channels/inspector/custom.d.ts +3 -0
- rasa/core/channels/inspector/dist/assets/arc-b6e548fe.js +1 -0
- rasa/core/channels/inspector/dist/assets/array-9f3ba611.js +1 -0
- rasa/core/channels/inspector/dist/assets/c4Diagram-d0fbc5ce-fa03ac9e.js +10 -0
- rasa/core/channels/inspector/dist/assets/classDiagram-936ed81e-ee67392a.js +2 -0
- rasa/core/channels/inspector/dist/assets/classDiagram-v2-c3cb15f1-9b283fae.js +2 -0
- rasa/core/channels/inspector/dist/assets/createText-62fc7601-8b6fcc2a.js +7 -0
- rasa/core/channels/inspector/dist/assets/edges-f2ad444c-22e77f4f.js +4 -0
- rasa/core/channels/inspector/dist/assets/erDiagram-9d236eb7-60ffc87f.js +51 -0
- rasa/core/channels/inspector/dist/assets/flowDb-1972c806-9dd802e4.js +6 -0
- rasa/core/channels/inspector/dist/assets/flowDiagram-7ea5b25a-5fa1912f.js +4 -0
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +1 -0
- rasa/core/channels/inspector/dist/assets/flowchart-elk-definition-abe16c3d-622a1fd2.js +139 -0
- rasa/core/channels/inspector/dist/assets/ganttDiagram-9b5ea136-e285a63a.js +266 -0
- rasa/core/channels/inspector/dist/assets/gitGraphDiagram-99d0ae7c-f237bdca.js +70 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-128cfa44.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-21dbcb97.woff +0 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-222b5e26.svg +329 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-9ad89b2a.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/index-2c4b9a3b-4b03d70e.js +1 -0
- rasa/core/channels/inspector/dist/assets/index-3ee28881.css +1 -0
- rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +1040 -0
- rasa/core/channels/inspector/dist/assets/infoDiagram-736b4530-72a0fa5f.js +7 -0
- rasa/core/channels/inspector/dist/assets/init-77b53fdd.js +1 -0
- rasa/core/channels/inspector/dist/assets/journeyDiagram-df861f2b-82218c41.js +139 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-60c05ee4.woff +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-8335d9b8.svg +438 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-9cc39c75.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-ead13ccf.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-16705655.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-5aeb07f9.woff +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9c459044.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9e2898a4.svg +435 -0
- rasa/core/channels/inspector/dist/assets/layout-78cff630.js +1 -0
- rasa/core/channels/inspector/dist/assets/line-5038b469.js +1 -0
- rasa/core/channels/inspector/dist/assets/linear-c4fc4098.js +1 -0
- rasa/core/channels/inspector/dist/assets/mindmap-definition-beec6740-c33c8ea6.js +109 -0
- rasa/core/channels/inspector/dist/assets/ordinal-ba9b4969.js +1 -0
- rasa/core/channels/inspector/dist/assets/path-53f90ab3.js +1 -0
- rasa/core/channels/inspector/dist/assets/pieDiagram-dbbf0591-a8d03059.js +35 -0
- rasa/core/channels/inspector/dist/assets/quadrantDiagram-4d7f4fd6-6a0e56b2.js +7 -0
- rasa/core/channels/inspector/dist/assets/requirementDiagram-6fc4c22a-2dc7c7bd.js +52 -0
- rasa/core/channels/inspector/dist/assets/sankeyDiagram-8f13d901-2360fe39.js +8 -0
- rasa/core/channels/inspector/dist/assets/sequenceDiagram-b655622a-41b9f9ad.js +122 -0
- rasa/core/channels/inspector/dist/assets/stateDiagram-59f0c015-0aad326f.js +1 -0
- rasa/core/channels/inspector/dist/assets/stateDiagram-v2-2b26beab-9847d984.js +1 -0
- rasa/core/channels/inspector/dist/assets/styles-080da4f6-564d890e.js +110 -0
- rasa/core/channels/inspector/dist/assets/styles-3dcbcfbf-38957613.js +159 -0
- rasa/core/channels/inspector/dist/assets/styles-9c745c82-f0fc6921.js +207 -0
- rasa/core/channels/inspector/dist/assets/svgDrawCommon-4835440b-ef3c5a77.js +1 -0
- rasa/core/channels/inspector/dist/assets/timeline-definition-5b62e21b-bf3e91c1.js +61 -0
- rasa/core/channels/inspector/dist/assets/xychartDiagram-2b33534f-4d4026c0.js +7 -0
- rasa/core/channels/inspector/dist/index.html +41 -0
- rasa/core/channels/inspector/index.html +39 -0
- rasa/core/channels/inspector/jest.config.ts +13 -0
- rasa/core/channels/inspector/package.json +48 -0
- rasa/core/channels/inspector/setupTests.ts +2 -0
- rasa/core/channels/inspector/src/App.tsx +170 -0
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +107 -0
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +187 -0
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +151 -0
- rasa/core/channels/inspector/src/components/ExpandIcon.tsx +16 -0
- rasa/core/channels/inspector/src/components/FullscreenButton.tsx +45 -0
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +19 -0
- rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +21 -0
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +32 -0
- rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +39 -0
- rasa/core/channels/inspector/src/components/Slots.tsx +91 -0
- rasa/core/channels/inspector/src/components/Welcome.tsx +54 -0
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +382 -0
- rasa/core/channels/inspector/src/helpers/formatters.ts +240 -0
- rasa/core/channels/inspector/src/helpers/utils.ts +42 -0
- rasa/core/channels/inspector/src/main.tsx +13 -0
- rasa/core/channels/inspector/src/theme/Button/Button.ts +29 -0
- rasa/core/channels/inspector/src/theme/Heading/Heading.ts +31 -0
- rasa/core/channels/inspector/src/theme/Input/Input.ts +27 -0
- rasa/core/channels/inspector/src/theme/Link/Link.ts +10 -0
- rasa/core/channels/inspector/src/theme/Modal/Modal.ts +47 -0
- rasa/core/channels/inspector/src/theme/Table/Table.tsx +38 -0
- rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +12 -0
- rasa/core/channels/inspector/src/theme/base/breakpoints.ts +8 -0
- rasa/core/channels/inspector/src/theme/base/colors.ts +88 -0
- rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +29 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.svg +329 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.svg +438 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.svg +435 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/radii.ts +9 -0
- rasa/core/channels/inspector/src/theme/base/shadows.ts +7 -0
- rasa/core/channels/inspector/src/theme/base/sizes.ts +7 -0
- rasa/core/channels/inspector/src/theme/base/space.ts +15 -0
- rasa/core/channels/inspector/src/theme/base/styles.ts +13 -0
- rasa/core/channels/inspector/src/theme/base/typography.ts +24 -0
- rasa/core/channels/inspector/src/theme/base/zIndices.ts +19 -0
- rasa/core/channels/inspector/src/theme/index.ts +101 -0
- rasa/core/channels/inspector/src/types.ts +64 -0
- rasa/core/channels/inspector/src/vite-env.d.ts +1 -0
- rasa/core/channels/inspector/tests/__mocks__/fileMock.ts +1 -0
- rasa/core/channels/inspector/tests/__mocks__/matchMedia.ts +16 -0
- rasa/core/channels/inspector/tests/__mocks__/styleMock.ts +1 -0
- rasa/core/channels/inspector/tests/renderWithProviders.tsx +14 -0
- rasa/core/channels/inspector/tsconfig.json +26 -0
- rasa/core/channels/inspector/tsconfig.node.json +10 -0
- rasa/core/channels/inspector/vite.config.ts +8 -0
- rasa/core/channels/inspector/yarn.lock +6156 -0
- rasa/core/channels/mattermost.py +229 -0
- rasa/core/channels/rasa_chat.py +126 -0
- rasa/core/channels/rest.py +225 -0
- rasa/core/channels/rocketchat.py +174 -0
- rasa/core/channels/slack.py +620 -0
- rasa/core/channels/socketio.py +274 -0
- rasa/core/channels/telegram.py +298 -0
- rasa/core/channels/twilio.py +169 -0
- rasa/core/channels/twilio_voice.py +367 -0
- rasa/core/channels/vier_cvg.py +374 -0
- rasa/core/channels/webexteams.py +134 -0
- rasa/core/concurrent_lock_store.py +210 -0
- rasa/core/constants.py +107 -0
- rasa/core/evaluation/__init__.py +0 -0
- rasa/core/evaluation/marker.py +267 -0
- rasa/core/evaluation/marker_base.py +923 -0
- rasa/core/evaluation/marker_stats.py +293 -0
- rasa/core/evaluation/marker_tracker_loader.py +103 -0
- rasa/core/exceptions.py +29 -0
- rasa/core/exporter.py +284 -0
- rasa/core/featurizers/__init__.py +0 -0
- rasa/core/featurizers/precomputation.py +410 -0
- rasa/core/featurizers/single_state_featurizer.py +421 -0
- rasa/core/featurizers/tracker_featurizers.py +1262 -0
- rasa/core/http_interpreter.py +89 -0
- rasa/core/information_retrieval/__init__.py +7 -0
- rasa/core/information_retrieval/faiss.py +121 -0
- rasa/core/information_retrieval/information_retrieval.py +129 -0
- rasa/core/information_retrieval/milvus.py +52 -0
- rasa/core/information_retrieval/qdrant.py +95 -0
- rasa/core/jobs.py +63 -0
- rasa/core/lock.py +139 -0
- rasa/core/lock_store.py +343 -0
- rasa/core/migrate.py +403 -0
- rasa/core/nlg/__init__.py +3 -0
- rasa/core/nlg/callback.py +146 -0
- rasa/core/nlg/contextual_response_rephraser.py +270 -0
- rasa/core/nlg/generator.py +230 -0
- rasa/core/nlg/interpolator.py +143 -0
- rasa/core/nlg/response.py +155 -0
- rasa/core/nlg/summarize.py +69 -0
- rasa/core/policies/__init__.py +0 -0
- rasa/core/policies/ensemble.py +329 -0
- rasa/core/policies/enterprise_search_policy.py +781 -0
- rasa/core/policies/enterprise_search_prompt_template.jinja2 +25 -0
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
- rasa/core/policies/flow_policy.py +205 -0
- rasa/core/policies/flows/__init__.py +0 -0
- rasa/core/policies/flows/flow_exceptions.py +44 -0
- rasa/core/policies/flows/flow_executor.py +705 -0
- rasa/core/policies/flows/flow_step_result.py +43 -0
- rasa/core/policies/intentless_policy.py +922 -0
- rasa/core/policies/intentless_prompt_template.jinja2 +22 -0
- rasa/core/policies/memoization.py +538 -0
- rasa/core/policies/policy.py +725 -0
- rasa/core/policies/rule_policy.py +1273 -0
- rasa/core/policies/ted_policy.py +2169 -0
- rasa/core/policies/unexpected_intent_policy.py +1022 -0
- rasa/core/processor.py +1422 -0
- rasa/core/run.py +331 -0
- rasa/core/secrets_manager/__init__.py +0 -0
- rasa/core/secrets_manager/constants.py +32 -0
- rasa/core/secrets_manager/endpoints.py +391 -0
- rasa/core/secrets_manager/factory.py +233 -0
- rasa/core/secrets_manager/secret_manager.py +262 -0
- rasa/core/secrets_manager/vault.py +574 -0
- rasa/core/test.py +1335 -0
- rasa/core/tracker_store.py +1699 -0
- rasa/core/train.py +105 -0
- rasa/core/training/__init__.py +89 -0
- rasa/core/training/converters/__init__.py +0 -0
- rasa/core/training/converters/responses_prefix_converter.py +119 -0
- rasa/core/training/interactive.py +1745 -0
- rasa/core/training/story_conflict.py +381 -0
- rasa/core/training/training.py +93 -0
- rasa/core/utils.py +339 -0
- rasa/core/visualize.py +70 -0
- rasa/dialogue_understanding/__init__.py +0 -0
- rasa/dialogue_understanding/coexistence/__init__.py +0 -0
- rasa/dialogue_understanding/coexistence/constants.py +4 -0
- rasa/dialogue_understanding/coexistence/intent_based_router.py +196 -0
- rasa/dialogue_understanding/coexistence/llm_based_router.py +260 -0
- rasa/dialogue_understanding/coexistence/router_template.jinja2 +12 -0
- rasa/dialogue_understanding/commands/__init__.py +49 -0
- rasa/dialogue_understanding/commands/can_not_handle_command.py +70 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +125 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +44 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +57 -0
- rasa/dialogue_understanding/commands/clarify_command.py +86 -0
- rasa/dialogue_understanding/commands/command.py +85 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +297 -0
- rasa/dialogue_understanding/commands/error_command.py +79 -0
- rasa/dialogue_understanding/commands/free_form_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/handle_code_change_command.py +73 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +66 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +57 -0
- rasa/dialogue_understanding/commands/noop_command.py +54 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +160 -0
- rasa/dialogue_understanding/commands/skip_question_command.py +75 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +107 -0
- rasa/dialogue_understanding/generator/__init__.py +21 -0
- rasa/dialogue_understanding/generator/command_generator.py +343 -0
- rasa/dialogue_understanding/generator/constants.py +18 -0
- rasa/dialogue_understanding/generator/flow_document_template.jinja2 +4 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +412 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +467 -0
- rasa/dialogue_understanding/generator/llm_command_generator.py +67 -0
- rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
- rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +827 -0
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +218 -0
- rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +57 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +345 -0
- rasa/dialogue_understanding/patterns/__init__.py +0 -0
- rasa/dialogue_understanding/patterns/cancel.py +111 -0
- rasa/dialogue_understanding/patterns/cannot_handle.py +43 -0
- rasa/dialogue_understanding/patterns/chitchat.py +37 -0
- rasa/dialogue_understanding/patterns/clarify.py +97 -0
- rasa/dialogue_understanding/patterns/code_change.py +41 -0
- rasa/dialogue_understanding/patterns/collect_information.py +90 -0
- rasa/dialogue_understanding/patterns/completed.py +40 -0
- rasa/dialogue_understanding/patterns/continue_interrupted.py +42 -0
- rasa/dialogue_understanding/patterns/correction.py +278 -0
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +248 -0
- rasa/dialogue_understanding/patterns/human_handoff.py +37 -0
- rasa/dialogue_understanding/patterns/internal_error.py +47 -0
- rasa/dialogue_understanding/patterns/search.py +37 -0
- rasa/dialogue_understanding/patterns/skip_question.py +38 -0
- rasa/dialogue_understanding/processor/__init__.py +0 -0
- rasa/dialogue_understanding/processor/command_processor.py +687 -0
- rasa/dialogue_understanding/processor/command_processor_component.py +39 -0
- rasa/dialogue_understanding/stack/__init__.py +0 -0
- rasa/dialogue_understanding/stack/dialogue_stack.py +178 -0
- rasa/dialogue_understanding/stack/frames/__init__.py +19 -0
- rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +27 -0
- rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +137 -0
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +157 -0
- rasa/dialogue_understanding/stack/frames/pattern_frame.py +10 -0
- rasa/dialogue_understanding/stack/frames/search_frame.py +27 -0
- rasa/dialogue_understanding/stack/utils.py +211 -0
- rasa/e2e_test/__init__.py +0 -0
- rasa/e2e_test/constants.py +11 -0
- rasa/e2e_test/e2e_test_case.py +366 -0
- rasa/e2e_test/e2e_test_result.py +34 -0
- rasa/e2e_test/e2e_test_runner.py +768 -0
- rasa/e2e_test/e2e_test_schema.yml +85 -0
- rasa/engine/__init__.py +0 -0
- rasa/engine/caching.py +463 -0
- rasa/engine/constants.py +17 -0
- rasa/engine/exceptions.py +14 -0
- rasa/engine/graph.py +637 -0
- rasa/engine/loader.py +36 -0
- rasa/engine/recipes/__init__.py +0 -0
- rasa/engine/recipes/config_files/default_config.yml +44 -0
- rasa/engine/recipes/default_components.py +99 -0
- rasa/engine/recipes/default_recipe.py +1251 -0
- rasa/engine/recipes/graph_recipe.py +79 -0
- rasa/engine/recipes/recipe.py +93 -0
- rasa/engine/runner/__init__.py +0 -0
- rasa/engine/runner/dask.py +250 -0
- rasa/engine/runner/interface.py +49 -0
- rasa/engine/storage/__init__.py +0 -0
- rasa/engine/storage/local_model_storage.py +246 -0
- rasa/engine/storage/resource.py +110 -0
- rasa/engine/storage/storage.py +203 -0
- rasa/engine/training/__init__.py +0 -0
- rasa/engine/training/components.py +176 -0
- rasa/engine/training/fingerprinting.py +64 -0
- rasa/engine/training/graph_trainer.py +256 -0
- rasa/engine/training/hooks.py +164 -0
- rasa/engine/validation.py +873 -0
- rasa/env.py +5 -0
- rasa/exceptions.py +69 -0
- rasa/graph_components/__init__.py +0 -0
- rasa/graph_components/converters/__init__.py +0 -0
- rasa/graph_components/converters/nlu_message_converter.py +48 -0
- rasa/graph_components/providers/__init__.py +0 -0
- rasa/graph_components/providers/domain_for_core_training_provider.py +87 -0
- rasa/graph_components/providers/domain_provider.py +71 -0
- rasa/graph_components/providers/flows_provider.py +74 -0
- rasa/graph_components/providers/forms_provider.py +44 -0
- rasa/graph_components/providers/nlu_training_data_provider.py +56 -0
- rasa/graph_components/providers/responses_provider.py +44 -0
- rasa/graph_components/providers/rule_only_provider.py +49 -0
- rasa/graph_components/providers/story_graph_provider.py +43 -0
- rasa/graph_components/providers/training_tracker_provider.py +55 -0
- rasa/graph_components/validators/__init__.py +0 -0
- rasa/graph_components/validators/default_recipe_validator.py +550 -0
- rasa/graph_components/validators/finetuning_validator.py +302 -0
- rasa/hooks.py +112 -0
- rasa/jupyter.py +63 -0
- rasa/markers/__init__.py +0 -0
- rasa/markers/marker.py +269 -0
- rasa/markers/marker_base.py +828 -0
- rasa/markers/upload.py +74 -0
- rasa/markers/validate.py +21 -0
- rasa/model.py +118 -0
- rasa/model_testing.py +457 -0
- rasa/model_training.py +536 -0
- rasa/nlu/__init__.py +7 -0
- rasa/nlu/classifiers/__init__.py +3 -0
- rasa/nlu/classifiers/classifier.py +5 -0
- rasa/nlu/classifiers/diet_classifier.py +1881 -0
- rasa/nlu/classifiers/fallback_classifier.py +192 -0
- rasa/nlu/classifiers/keyword_intent_classifier.py +188 -0
- rasa/nlu/classifiers/llm_intent_classifier.py +519 -0
- rasa/nlu/classifiers/logistic_regression_classifier.py +253 -0
- rasa/nlu/classifiers/mitie_intent_classifier.py +156 -0
- rasa/nlu/classifiers/regex_message_handler.py +56 -0
- rasa/nlu/classifiers/sklearn_intent_classifier.py +330 -0
- rasa/nlu/constants.py +77 -0
- rasa/nlu/convert.py +40 -0
- rasa/nlu/emulators/__init__.py +0 -0
- rasa/nlu/emulators/dialogflow.py +55 -0
- rasa/nlu/emulators/emulator.py +49 -0
- rasa/nlu/emulators/luis.py +86 -0
- rasa/nlu/emulators/no_emulator.py +10 -0
- rasa/nlu/emulators/wit.py +56 -0
- rasa/nlu/extractors/__init__.py +0 -0
- rasa/nlu/extractors/crf_entity_extractor.py +715 -0
- rasa/nlu/extractors/duckling_entity_extractor.py +206 -0
- rasa/nlu/extractors/entity_synonyms.py +178 -0
- rasa/nlu/extractors/extractor.py +470 -0
- rasa/nlu/extractors/mitie_entity_extractor.py +293 -0
- rasa/nlu/extractors/regex_entity_extractor.py +220 -0
- rasa/nlu/extractors/spacy_entity_extractor.py +95 -0
- rasa/nlu/featurizers/__init__.py +0 -0
- rasa/nlu/featurizers/dense_featurizer/__init__.py +0 -0
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +445 -0
- rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +57 -0
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +768 -0
- rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +170 -0
- rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +132 -0
- rasa/nlu/featurizers/featurizer.py +89 -0
- rasa/nlu/featurizers/sparse_featurizer/__init__.py +0 -0
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +867 -0
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +571 -0
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +271 -0
- rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +9 -0
- rasa/nlu/model.py +24 -0
- rasa/nlu/persistor.py +282 -0
- rasa/nlu/run.py +27 -0
- rasa/nlu/selectors/__init__.py +0 -0
- rasa/nlu/selectors/response_selector.py +987 -0
- rasa/nlu/test.py +1940 -0
- rasa/nlu/tokenizers/__init__.py +0 -0
- rasa/nlu/tokenizers/jieba_tokenizer.py +148 -0
- rasa/nlu/tokenizers/mitie_tokenizer.py +75 -0
- rasa/nlu/tokenizers/spacy_tokenizer.py +72 -0
- rasa/nlu/tokenizers/tokenizer.py +239 -0
- rasa/nlu/tokenizers/whitespace_tokenizer.py +106 -0
- rasa/nlu/utils/__init__.py +35 -0
- rasa/nlu/utils/bilou_utils.py +462 -0
- rasa/nlu/utils/hugging_face/__init__.py +0 -0
- rasa/nlu/utils/hugging_face/registry.py +108 -0
- rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +311 -0
- rasa/nlu/utils/mitie_utils.py +113 -0
- rasa/nlu/utils/pattern_utils.py +168 -0
- rasa/nlu/utils/spacy_utils.py +310 -0
- rasa/plugin.py +90 -0
- rasa/server.py +1551 -0
- rasa/shared/__init__.py +0 -0
- rasa/shared/constants.py +192 -0
- rasa/shared/core/__init__.py +0 -0
- rasa/shared/core/command_payload_reader.py +109 -0
- rasa/shared/core/constants.py +167 -0
- rasa/shared/core/conversation.py +46 -0
- rasa/shared/core/domain.py +2107 -0
- rasa/shared/core/events.py +2504 -0
- rasa/shared/core/flows/__init__.py +7 -0
- rasa/shared/core/flows/flow.py +362 -0
- rasa/shared/core/flows/flow_step.py +146 -0
- rasa/shared/core/flows/flow_step_links.py +319 -0
- rasa/shared/core/flows/flow_step_sequence.py +70 -0
- rasa/shared/core/flows/flows_list.py +223 -0
- rasa/shared/core/flows/flows_yaml_schema.json +217 -0
- rasa/shared/core/flows/nlu_trigger.py +117 -0
- rasa/shared/core/flows/steps/__init__.py +24 -0
- rasa/shared/core/flows/steps/action.py +56 -0
- rasa/shared/core/flows/steps/call.py +64 -0
- rasa/shared/core/flows/steps/collect.py +112 -0
- rasa/shared/core/flows/steps/constants.py +5 -0
- rasa/shared/core/flows/steps/continuation.py +36 -0
- rasa/shared/core/flows/steps/end.py +22 -0
- rasa/shared/core/flows/steps/internal.py +44 -0
- rasa/shared/core/flows/steps/link.py +51 -0
- rasa/shared/core/flows/steps/no_operation.py +48 -0
- rasa/shared/core/flows/steps/set_slots.py +50 -0
- rasa/shared/core/flows/steps/start.py +30 -0
- rasa/shared/core/flows/validation.py +527 -0
- rasa/shared/core/flows/yaml_flows_io.py +278 -0
- rasa/shared/core/generator.py +908 -0
- rasa/shared/core/slot_mappings.py +526 -0
- rasa/shared/core/slots.py +649 -0
- rasa/shared/core/trackers.py +1177 -0
- rasa/shared/core/training_data/__init__.py +0 -0
- rasa/shared/core/training_data/loading.py +89 -0
- rasa/shared/core/training_data/story_reader/__init__.py +0 -0
- rasa/shared/core/training_data/story_reader/story_reader.py +129 -0
- rasa/shared/core/training_data/story_reader/story_step_builder.py +168 -0
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +888 -0
- rasa/shared/core/training_data/story_writer/__init__.py +0 -0
- rasa/shared/core/training_data/story_writer/story_writer.py +76 -0
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +444 -0
- rasa/shared/core/training_data/structures.py +838 -0
- rasa/shared/core/training_data/visualization.html +146 -0
- rasa/shared/core/training_data/visualization.py +603 -0
- rasa/shared/data.py +249 -0
- rasa/shared/engine/__init__.py +0 -0
- rasa/shared/engine/caching.py +26 -0
- rasa/shared/exceptions.py +163 -0
- rasa/shared/importers/__init__.py +0 -0
- rasa/shared/importers/importer.py +704 -0
- rasa/shared/importers/multi_project.py +203 -0
- rasa/shared/importers/rasa.py +99 -0
- rasa/shared/importers/utils.py +34 -0
- rasa/shared/nlu/__init__.py +0 -0
- rasa/shared/nlu/constants.py +47 -0
- rasa/shared/nlu/interpreter.py +10 -0
- rasa/shared/nlu/training_data/__init__.py +0 -0
- rasa/shared/nlu/training_data/entities_parser.py +208 -0
- rasa/shared/nlu/training_data/features.py +492 -0
- rasa/shared/nlu/training_data/formats/__init__.py +10 -0
- rasa/shared/nlu/training_data/formats/dialogflow.py +163 -0
- rasa/shared/nlu/training_data/formats/luis.py +87 -0
- rasa/shared/nlu/training_data/formats/rasa.py +135 -0
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +603 -0
- rasa/shared/nlu/training_data/formats/readerwriter.py +244 -0
- rasa/shared/nlu/training_data/formats/wit.py +52 -0
- rasa/shared/nlu/training_data/loading.py +137 -0
- rasa/shared/nlu/training_data/lookup_tables_parser.py +30 -0
- rasa/shared/nlu/training_data/message.py +490 -0
- rasa/shared/nlu/training_data/schemas/__init__.py +0 -0
- rasa/shared/nlu/training_data/schemas/data_schema.py +85 -0
- rasa/shared/nlu/training_data/schemas/nlu.yml +53 -0
- rasa/shared/nlu/training_data/schemas/responses.yml +70 -0
- rasa/shared/nlu/training_data/synonyms_parser.py +42 -0
- rasa/shared/nlu/training_data/training_data.py +730 -0
- rasa/shared/nlu/training_data/util.py +223 -0
- rasa/shared/providers/__init__.py +0 -0
- rasa/shared/providers/openai/__init__.py +0 -0
- rasa/shared/providers/openai/clients.py +43 -0
- rasa/shared/providers/openai/session_handler.py +110 -0
- rasa/shared/utils/__init__.py +0 -0
- rasa/shared/utils/cli.py +72 -0
- rasa/shared/utils/common.py +308 -0
- rasa/shared/utils/constants.py +4 -0
- rasa/shared/utils/io.py +415 -0
- rasa/shared/utils/llm.py +404 -0
- rasa/shared/utils/pykwalify_extensions.py +27 -0
- rasa/shared/utils/schemas/__init__.py +0 -0
- rasa/shared/utils/schemas/config.yml +2 -0
- rasa/shared/utils/schemas/domain.yml +145 -0
- rasa/shared/utils/schemas/events.py +212 -0
- rasa/shared/utils/schemas/model_config.yml +46 -0
- rasa/shared/utils/schemas/stories.yml +173 -0
- rasa/shared/utils/yaml.py +786 -0
- rasa/studio/__init__.py +0 -0
- rasa/studio/auth.py +268 -0
- rasa/studio/config.py +127 -0
- rasa/studio/constants.py +18 -0
- rasa/studio/data_handler.py +359 -0
- rasa/studio/download.py +483 -0
- rasa/studio/results_logger.py +137 -0
- rasa/studio/train.py +135 -0
- rasa/studio/upload.py +433 -0
- rasa/telemetry.py +1737 -0
- rasa/tracing/__init__.py +0 -0
- rasa/tracing/config.py +353 -0
- rasa/tracing/constants.py +62 -0
- rasa/tracing/instrumentation/__init__.py +0 -0
- rasa/tracing/instrumentation/attribute_extractors.py +672 -0
- rasa/tracing/instrumentation/instrumentation.py +1185 -0
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +144 -0
- rasa/tracing/instrumentation/metrics.py +294 -0
- rasa/tracing/metric_instrument_provider.py +205 -0
- rasa/utils/__init__.py +0 -0
- rasa/utils/beta.py +83 -0
- rasa/utils/cli.py +28 -0
- rasa/utils/common.py +635 -0
- rasa/utils/converter.py +53 -0
- rasa/utils/endpoints.py +302 -0
- rasa/utils/io.py +260 -0
- rasa/utils/licensing.py +534 -0
- rasa/utils/log_utils.py +174 -0
- rasa/utils/mapper.py +210 -0
- rasa/utils/ml_utils.py +145 -0
- rasa/utils/plotting.py +362 -0
- rasa/utils/singleton.py +23 -0
- rasa/utils/tensorflow/__init__.py +0 -0
- rasa/utils/tensorflow/callback.py +112 -0
- rasa/utils/tensorflow/constants.py +116 -0
- rasa/utils/tensorflow/crf.py +492 -0
- rasa/utils/tensorflow/data_generator.py +440 -0
- rasa/utils/tensorflow/environment.py +161 -0
- rasa/utils/tensorflow/exceptions.py +5 -0
- rasa/utils/tensorflow/feature_array.py +366 -0
- rasa/utils/tensorflow/layers.py +1565 -0
- rasa/utils/tensorflow/layers_utils.py +113 -0
- rasa/utils/tensorflow/metrics.py +281 -0
- rasa/utils/tensorflow/model_data.py +798 -0
- rasa/utils/tensorflow/model_data_utils.py +499 -0
- rasa/utils/tensorflow/models.py +935 -0
- rasa/utils/tensorflow/rasa_layers.py +1094 -0
- rasa/utils/tensorflow/transformer.py +640 -0
- rasa/utils/tensorflow/types.py +6 -0
- rasa/utils/train_utils.py +572 -0
- rasa/utils/url_tools.py +53 -0
- rasa/utils/yaml.py +54 -0
- rasa/validator.py +1337 -0
- rasa/version.py +3 -0
- rasa_pro-3.9.18.dist-info/METADATA +563 -0
- rasa_pro-3.9.18.dist-info/NOTICE +5 -0
- rasa_pro-3.9.18.dist-info/RECORD +662 -0
- rasa_pro-3.9.18.dist-info/WHEEL +4 -0
- rasa_pro-3.9.18.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import aiohttp
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import logging
|
|
5
|
+
import structlog
|
|
6
|
+
|
|
7
|
+
from typing import Text, Dict, Any, Optional
|
|
8
|
+
|
|
9
|
+
from rasa.core import constants
|
|
10
|
+
from rasa.core.channels import UserMessage
|
|
11
|
+
from rasa.shared.nlu.constants import INTENT_NAME_KEY
|
|
12
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
structlogger = structlog.get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RasaNLUHttpInterpreter:
|
|
19
|
+
"""Allows for an HTTP endpoint to be used to parse messages."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, endpoint_config: Optional[EndpointConfig] = None) -> None:
|
|
22
|
+
"""Initializes a `RasaNLUHttpInterpreter`."""
|
|
23
|
+
self.session = aiohttp.ClientSession()
|
|
24
|
+
if endpoint_config:
|
|
25
|
+
self.endpoint_config = endpoint_config
|
|
26
|
+
else:
|
|
27
|
+
self.endpoint_config = EndpointConfig(constants.DEFAULT_SERVER_URL)
|
|
28
|
+
|
|
29
|
+
async def parse(self, message: UserMessage) -> Dict[Text, Any]:
|
|
30
|
+
"""Parse a text message.
|
|
31
|
+
|
|
32
|
+
Return a default value if the parsing of the text failed.
|
|
33
|
+
"""
|
|
34
|
+
default_return = {
|
|
35
|
+
"intent": {INTENT_NAME_KEY: "", "confidence": 0.0},
|
|
36
|
+
"entities": [],
|
|
37
|
+
"text": "",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
result = await self._rasa_http_parse(message.text, message.sender_id)
|
|
41
|
+
return result if result is not None else default_return
|
|
42
|
+
|
|
43
|
+
async def _rasa_http_parse(
|
|
44
|
+
self, text: Text, message_id: Optional[Text] = None
|
|
45
|
+
) -> Optional[Dict[Text, Any]]:
|
|
46
|
+
"""Send a text message to a running rasa NLU http server.
|
|
47
|
+
|
|
48
|
+
Return `None` on failure.
|
|
49
|
+
"""
|
|
50
|
+
if not self.endpoint_config or self.endpoint_config.url is None:
|
|
51
|
+
structlogger.error(
|
|
52
|
+
"http.parse.text",
|
|
53
|
+
text=copy.deepcopy(text),
|
|
54
|
+
event_info="No rasa NLU server specified!",
|
|
55
|
+
)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
params = {
|
|
59
|
+
"token": self.endpoint_config.token,
|
|
60
|
+
"text": text,
|
|
61
|
+
"message_id": message_id,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if self.endpoint_config.url.endswith("/"):
|
|
65
|
+
url = self.endpoint_config.url + "model/parse"
|
|
66
|
+
else:
|
|
67
|
+
url = self.endpoint_config.url + "/model/parse"
|
|
68
|
+
|
|
69
|
+
# noinspection PyBroadException
|
|
70
|
+
try:
|
|
71
|
+
async with self.session.post(url, json=params) as resp:
|
|
72
|
+
if resp.status == 200:
|
|
73
|
+
return await resp.json()
|
|
74
|
+
else:
|
|
75
|
+
response_text = await resp.text()
|
|
76
|
+
structlogger.error(
|
|
77
|
+
"http.parse.text.failure",
|
|
78
|
+
text=copy.deepcopy(text),
|
|
79
|
+
response_text=copy.deepcopy(response_text),
|
|
80
|
+
)
|
|
81
|
+
return None
|
|
82
|
+
except Exception: # skipcq: PYL-W0703
|
|
83
|
+
# need to catch all possible exceptions when doing http requests
|
|
84
|
+
# (timeouts, value errors, parser errors, ...)
|
|
85
|
+
structlogger.exception(
|
|
86
|
+
"http.parse.text.exception",
|
|
87
|
+
text=copy.deepcopy(text),
|
|
88
|
+
)
|
|
89
|
+
return None
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional, Text, Any, Dict
|
|
3
|
+
|
|
4
|
+
import structlog
|
|
5
|
+
from langchain.document_loaders import DirectoryLoader, TextLoader
|
|
6
|
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
7
|
+
from langchain.vectorstores.faiss import FAISS
|
|
8
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
9
|
+
|
|
10
|
+
from rasa.core.information_retrieval import (
|
|
11
|
+
SearchResultList,
|
|
12
|
+
InformationRetrieval,
|
|
13
|
+
InformationRetrievalException,
|
|
14
|
+
)
|
|
15
|
+
from rasa.utils.ml_utils import persist_faiss_vector_store
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from langchain.schema import Document
|
|
19
|
+
from langchain.schema.embeddings import Embeddings
|
|
20
|
+
|
|
21
|
+
logger = structlog.get_logger()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FAISS_Store(InformationRetrieval):
|
|
25
|
+
"""FAISS Store implementation."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
embeddings: "Embeddings",
|
|
30
|
+
index_path: str,
|
|
31
|
+
docs_folder: Optional[str],
|
|
32
|
+
create_index: Optional[bool] = False,
|
|
33
|
+
):
|
|
34
|
+
"""Initializes the FAISS Store."""
|
|
35
|
+
self.chunk_size = 1000
|
|
36
|
+
self.chunk_overlap = 20
|
|
37
|
+
|
|
38
|
+
path = Path(index_path) / "documents_faiss"
|
|
39
|
+
if create_index:
|
|
40
|
+
logger.info(
|
|
41
|
+
"information_retrieval.faiss_store.create_index", path=path.absolute()
|
|
42
|
+
)
|
|
43
|
+
self.index = self._create_document_index(docs_folder, embeddings)
|
|
44
|
+
self._persist(path)
|
|
45
|
+
else:
|
|
46
|
+
logger.info(
|
|
47
|
+
"information_retrieval.faiss_store.load_index", path=path.absolute()
|
|
48
|
+
)
|
|
49
|
+
self.index = FAISS.load_local(str(path), embeddings)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def load_documents(docs_folder: str) -> List["Document"]:
|
|
53
|
+
"""Loads documents from a given folder.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
docs_folder: The folder containing the documents.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
the list of documents
|
|
60
|
+
"""
|
|
61
|
+
logger.info(
|
|
62
|
+
"information_retrieval.faiss_store.load_documents",
|
|
63
|
+
docs_folder=Path(docs_folder).absolute(),
|
|
64
|
+
)
|
|
65
|
+
loader = DirectoryLoader(
|
|
66
|
+
docs_folder, glob="**/*.txt", loader_cls=TextLoader, show_progress=True
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return loader.load()
|
|
70
|
+
|
|
71
|
+
def _create_document_index(
|
|
72
|
+
self, docs_folder: Optional[str], embedding: "Embeddings"
|
|
73
|
+
) -> FAISS:
|
|
74
|
+
"""Creates a document index from the documents in the given folder.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
docs_folder: The folder containing the documents.
|
|
78
|
+
embedding: The embedding to use.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The document index.
|
|
82
|
+
"""
|
|
83
|
+
if not docs_folder:
|
|
84
|
+
raise ValueError("parameter `docs_folder` needs to be specified")
|
|
85
|
+
|
|
86
|
+
docs = self.load_documents(docs_folder)
|
|
87
|
+
splitter = RecursiveCharacterTextSplitter(
|
|
88
|
+
chunk_size=self.chunk_size,
|
|
89
|
+
chunk_overlap=self.chunk_overlap,
|
|
90
|
+
length_function=len,
|
|
91
|
+
)
|
|
92
|
+
doc_chunks = splitter.split_documents(docs)
|
|
93
|
+
|
|
94
|
+
logger.info(
|
|
95
|
+
"information_retrieval.faiss_store._create_document_index",
|
|
96
|
+
len_chunks=len(doc_chunks),
|
|
97
|
+
)
|
|
98
|
+
if doc_chunks:
|
|
99
|
+
texts = [chunk.page_content for chunk in doc_chunks]
|
|
100
|
+
metadatas = [chunk.metadata for chunk in doc_chunks]
|
|
101
|
+
return FAISS.from_texts(texts, embedding, metadatas=metadatas, ids=None)
|
|
102
|
+
else:
|
|
103
|
+
raise ValueError(f"No documents found at '{docs_folder}'.")
|
|
104
|
+
|
|
105
|
+
def _persist(self, path: Path) -> None:
|
|
106
|
+
persist_faiss_vector_store(path, self.index)
|
|
107
|
+
|
|
108
|
+
def connect(self, config: EndpointConfig) -> None:
|
|
109
|
+
"""Faiss does not need to connect to a server."""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
async def search(
|
|
113
|
+
self, query: Text, tracker_state: Dict[str, Any], threshold: float = 0.0
|
|
114
|
+
) -> SearchResultList:
|
|
115
|
+
logger.debug("information_retrieval.faiss_store.search", query=query)
|
|
116
|
+
try:
|
|
117
|
+
documents = await self.index.as_retriever().aget_relevant_documents(query)
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
raise InformationRetrievalException from exc
|
|
120
|
+
|
|
121
|
+
return SearchResultList.from_document_list(documents)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, List, Text, Any, Optional
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
8
|
+
from rasa.shared.exceptions import RasaException
|
|
9
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
10
|
+
import importlib
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from langchain.schema import Document
|
|
14
|
+
from langchain.schema.embeddings import Embeddings
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
logger = structlog.get_logger()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class SearchResult:
|
|
22
|
+
text: str
|
|
23
|
+
metadata: dict
|
|
24
|
+
score: Optional[float] = None
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_document(cls, document: Document) -> "SearchResult":
|
|
28
|
+
"""Construct a SearchResult object from Langchain Document object."""
|
|
29
|
+
return cls(text=document.page_content, metadata=document.metadata)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class SearchResultList:
|
|
34
|
+
results: List[SearchResult]
|
|
35
|
+
metadata: dict
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_document_list(cls, documents: List["Document"]) -> "SearchResultList":
|
|
39
|
+
"""
|
|
40
|
+
Convert a list of Langchain Documents to a SearchResultList object.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
documents: List of Langchain Documents.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
SearchResultList object.
|
|
47
|
+
"""
|
|
48
|
+
return cls(
|
|
49
|
+
results=[SearchResult.from_document(doc) for doc in documents],
|
|
50
|
+
metadata={"total_results": len(documents)},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class InformationRetrievalException(RasaException):
|
|
55
|
+
"""Base class for exceptions raised by InformationRetrieval operations."""
|
|
56
|
+
|
|
57
|
+
def __init__(self) -> None:
|
|
58
|
+
self.base_message = "An error occurred while searching for documents: "
|
|
59
|
+
|
|
60
|
+
def __str__(self) -> str:
|
|
61
|
+
return self.base_message + f"{self.__cause__}"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class InformationRetrieval:
|
|
65
|
+
"""Base class for any InformationRetrieval implementation."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, embeddings: "Embeddings") -> None:
|
|
68
|
+
self.embeddings = embeddings
|
|
69
|
+
|
|
70
|
+
def connect(
|
|
71
|
+
self,
|
|
72
|
+
config: EndpointConfig,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Connect to the InformationRetrieval system."""
|
|
75
|
+
raise NotImplementedError(
|
|
76
|
+
"InformationRetrieval must implement the `connect` method."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
async def search(
|
|
80
|
+
self,
|
|
81
|
+
query: Text,
|
|
82
|
+
tracker_state: dict[str, Any],
|
|
83
|
+
threshold: float = 0.0,
|
|
84
|
+
) -> SearchResultList:
|
|
85
|
+
"""Search for a document in the InformationRetrieval system."""
|
|
86
|
+
raise NotImplementedError(
|
|
87
|
+
"InformationRetrieval must implement the `search` method."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def create_from_endpoint_config(
|
|
92
|
+
config_type: Text,
|
|
93
|
+
embeddings: "Embeddings",
|
|
94
|
+
) -> InformationRetrieval:
|
|
95
|
+
"""Instantiate a vector store based on its configuration."""
|
|
96
|
+
logger.debug(
|
|
97
|
+
"information_retrieval.create_from_endpoint_config", config_type=config_type
|
|
98
|
+
)
|
|
99
|
+
if config_type == "milvus":
|
|
100
|
+
from rasa.core.information_retrieval.milvus import Milvus_Store
|
|
101
|
+
|
|
102
|
+
return Milvus_Store(embeddings=embeddings)
|
|
103
|
+
elif config_type == "qdrant":
|
|
104
|
+
from rasa.core.information_retrieval.qdrant import Qdrant_Store
|
|
105
|
+
|
|
106
|
+
return Qdrant_Store(embeddings=embeddings)
|
|
107
|
+
else:
|
|
108
|
+
# Import the module dynamically
|
|
109
|
+
try:
|
|
110
|
+
module_name, class_name = config_type.rsplit(".", 1)
|
|
111
|
+
module = importlib.import_module(module_name)
|
|
112
|
+
except ValueError:
|
|
113
|
+
logger.error(
|
|
114
|
+
"information_retrieval.create_from_endpoint_config.invalid_config",
|
|
115
|
+
config_type=config_type,
|
|
116
|
+
)
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Invalid configuration for vector store: '{config_type}'. "
|
|
119
|
+
f"Expected a module path and a class name separated by a dot."
|
|
120
|
+
)
|
|
121
|
+
except ModuleNotFoundError:
|
|
122
|
+
logger.error(
|
|
123
|
+
"information_retrieval.create_from_endpoint_config.unknown_type",
|
|
124
|
+
config_type=config_type,
|
|
125
|
+
)
|
|
126
|
+
raise ImportError(f"Cannot retrieve class from path {config_type}.")
|
|
127
|
+
|
|
128
|
+
external_class = getattr(module, class_name)
|
|
129
|
+
return external_class(embeddings=embeddings)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Text, Any, Dict
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
from langchain.vectorstores.milvus import Milvus
|
|
5
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
6
|
+
|
|
7
|
+
from rasa.core.information_retrieval import (
|
|
8
|
+
SearchResultList,
|
|
9
|
+
InformationRetrieval,
|
|
10
|
+
InformationRetrievalException,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = structlog.get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Milvus_Store(InformationRetrieval):
|
|
17
|
+
"""Milvus Store implementation."""
|
|
18
|
+
|
|
19
|
+
def connect(self, config: EndpointConfig) -> None:
|
|
20
|
+
"""Connect to the Milvus system."""
|
|
21
|
+
params = config.kwargs
|
|
22
|
+
self.client = Milvus(
|
|
23
|
+
self.embeddings,
|
|
24
|
+
connection_args={
|
|
25
|
+
"host": str(params.get("host")),
|
|
26
|
+
"port": str(params.get("port")),
|
|
27
|
+
"user": str(params.get("user")),
|
|
28
|
+
"password": str(params.get("password")),
|
|
29
|
+
},
|
|
30
|
+
collection_name=str(params.get("collection")),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
async def search(
|
|
34
|
+
self, query: Text, tracker_state: Dict[str, Any], threshold: float = 0.0
|
|
35
|
+
) -> SearchResultList:
|
|
36
|
+
"""Search for documents in the Milvus store.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
query: The query to search for.
|
|
40
|
+
threshold: minimum similarity score to consider a document a match.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A list of documents that match the query.
|
|
44
|
+
"""
|
|
45
|
+
logger.debug("information_retrieval.milvus_store.search", query=query)
|
|
46
|
+
try:
|
|
47
|
+
hits = await self.client.asimilarity_search_with_score(query, k=4)
|
|
48
|
+
except Exception as exc:
|
|
49
|
+
raise InformationRetrievalException from exc
|
|
50
|
+
|
|
51
|
+
filtered_hits = [doc for doc, score in hits if score >= threshold]
|
|
52
|
+
return SearchResultList.from_document_list(filtered_hits)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from typing import Text, Any, Dict
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
from langchain.vectorstores.qdrant import Qdrant
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
from qdrant_client import QdrantClient
|
|
7
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
8
|
+
|
|
9
|
+
from rasa.core.information_retrieval import (
|
|
10
|
+
SearchResultList,
|
|
11
|
+
InformationRetrieval,
|
|
12
|
+
InformationRetrievalException,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = structlog.get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PayloadNotFoundException(InformationRetrievalException):
|
|
19
|
+
"""Exception raised for errors in missing payloads."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, message: str) -> None:
|
|
22
|
+
self.message = message
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return self.base_message + self.message + f"{self.__cause__}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class QdrantInformationRetrievalException(InformationRetrievalException):
|
|
30
|
+
"""Exception raised for errors in the Qdrant vector store."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, message: str) -> None:
|
|
33
|
+
self.message = message
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
return self.base_message + self.message + f"{self.__cause__}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Qdrant_Store(InformationRetrieval):
|
|
41
|
+
def connect(
|
|
42
|
+
self,
|
|
43
|
+
config: EndpointConfig,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Connect to the Qdrant system."""
|
|
46
|
+
params = config.kwargs
|
|
47
|
+
self.client = Qdrant(
|
|
48
|
+
client=QdrantClient(
|
|
49
|
+
location=params.get("location"),
|
|
50
|
+
url=params.get("url"),
|
|
51
|
+
port=int(params.get("port", 6333)),
|
|
52
|
+
grpc_port=int(params.get("grpc_port", 6334)),
|
|
53
|
+
prefer_grpc=bool(params.get("prefer_grpc", False)),
|
|
54
|
+
https=bool(params.get("https")),
|
|
55
|
+
api_key=params.get("api_key"),
|
|
56
|
+
prefix=params.get("prefix"),
|
|
57
|
+
timeout=int(params.get("timeout", 5)),
|
|
58
|
+
host=params.get("host"),
|
|
59
|
+
path=params.get("path"),
|
|
60
|
+
),
|
|
61
|
+
collection_name=str(params.get("collection")),
|
|
62
|
+
embeddings=self.embeddings,
|
|
63
|
+
content_payload_key=params.get("content_payload_key", "text"),
|
|
64
|
+
metadata_payload_key=params.get("metadata_payload_key", "metadata"),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
async def search(
|
|
68
|
+
self, query: Text, tracker_state: Dict[str, Any], threshold: float = 0.0
|
|
69
|
+
) -> SearchResultList:
|
|
70
|
+
"""Search for a document in the Qdrant vector store.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
query: The query to search for.
|
|
74
|
+
threshold: minimum similarity score to consider a document a match.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A list of documents that match the query.
|
|
78
|
+
"""
|
|
79
|
+
logger.debug("information_retrieval.qdrant_store.search", query=query)
|
|
80
|
+
try:
|
|
81
|
+
hits = await self.client.asimilarity_search(
|
|
82
|
+
query, k=4, score_threshold=threshold
|
|
83
|
+
)
|
|
84
|
+
except ValidationError as e:
|
|
85
|
+
raise PayloadNotFoundException(
|
|
86
|
+
"Payload not found in the Qdrant response. Please make sure "
|
|
87
|
+
"the `content_payload_key`and `metadata_payload_key` are correct in "
|
|
88
|
+
f"the Qdrant configuration. Error: {e}"
|
|
89
|
+
""
|
|
90
|
+
) from e
|
|
91
|
+
except Exception as e:
|
|
92
|
+
raise QdrantInformationRetrievalException(
|
|
93
|
+
f"Failed to search the Qdrant vector store. Encountered error: {e}"
|
|
94
|
+
) from e
|
|
95
|
+
return SearchResultList.from_document_list(hits)
|
rasa/core/jobs.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
5
|
+
from pytz import UnknownTimeZoneError, utc
|
|
6
|
+
import rasa.shared.utils.io
|
|
7
|
+
|
|
8
|
+
__scheduler = None
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def scheduler() -> AsyncIOScheduler:
|
|
14
|
+
"""Thread global scheduler to handle all recurring tasks.
|
|
15
|
+
|
|
16
|
+
If no scheduler exists yet, this will instantiate one.
|
|
17
|
+
"""
|
|
18
|
+
global __scheduler
|
|
19
|
+
|
|
20
|
+
if not __scheduler:
|
|
21
|
+
try:
|
|
22
|
+
__scheduler = AsyncIOScheduler(event_loop=asyncio.get_event_loop())
|
|
23
|
+
__scheduler.start()
|
|
24
|
+
return __scheduler
|
|
25
|
+
except UnknownTimeZoneError:
|
|
26
|
+
rasa.shared.utils.io.raise_warning(
|
|
27
|
+
"apscheduler could not find a timezone and is "
|
|
28
|
+
"defaulting to utc. This is probably because "
|
|
29
|
+
"your system timezone is not set. "
|
|
30
|
+
'Set it with e.g. echo "Europe/Berlin" > '
|
|
31
|
+
"/etc/timezone"
|
|
32
|
+
)
|
|
33
|
+
__scheduler = AsyncIOScheduler(
|
|
34
|
+
event_loop=asyncio.get_event_loop(), timezone=utc
|
|
35
|
+
)
|
|
36
|
+
__scheduler.start()
|
|
37
|
+
return __scheduler
|
|
38
|
+
else:
|
|
39
|
+
# scheduler already created, make sure it is running on
|
|
40
|
+
# the correct loop
|
|
41
|
+
# noinspection PyProtectedMember
|
|
42
|
+
if not __scheduler._eventloop == asyncio.get_event_loop():
|
|
43
|
+
raise RuntimeError(
|
|
44
|
+
"Detected inconsistent loop usage. "
|
|
45
|
+
"Trying to schedule a task on a new event "
|
|
46
|
+
"loop, but scheduler was created with a "
|
|
47
|
+
"different event loop. Make sure there "
|
|
48
|
+
"is only one event loop in use and that the "
|
|
49
|
+
"scheduler is running on that one."
|
|
50
|
+
)
|
|
51
|
+
return __scheduler
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def kill_scheduler() -> None:
|
|
55
|
+
"""Terminate the scheduler if started.
|
|
56
|
+
|
|
57
|
+
Another call to `scheduler` will create a new scheduler.
|
|
58
|
+
"""
|
|
59
|
+
global __scheduler
|
|
60
|
+
|
|
61
|
+
if __scheduler:
|
|
62
|
+
__scheduler.shutdown()
|
|
63
|
+
__scheduler = None
|
rasa/core/lock.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from collections import deque
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Text, Optional, Union, Deque, Dict, Any
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
NO_TICKET_ISSUED = -1 # index of latest issued ticket if no tickets exist
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Ticket:
|
|
14
|
+
def __init__(self, number: int, expires: float) -> None:
|
|
15
|
+
self.number = number
|
|
16
|
+
self.expires = expires
|
|
17
|
+
|
|
18
|
+
def has_expired(self) -> bool:
|
|
19
|
+
return time.time() > self.expires
|
|
20
|
+
|
|
21
|
+
def as_dict(self) -> Dict[Text, Any]:
|
|
22
|
+
return dict(number=self.number, expires=self.expires)
|
|
23
|
+
|
|
24
|
+
def dumps(self) -> Text:
|
|
25
|
+
"""Return json dump of `Ticket` as dictionary."""
|
|
26
|
+
return json.dumps(self.as_dict())
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_dict(cls, data: Dict[Text, Union[int, float]]) -> "Ticket":
|
|
30
|
+
"""Creates `Ticket` from dictionary."""
|
|
31
|
+
return cls(number=data["number"], expires=data["expires"])
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> Text:
|
|
34
|
+
return f"Ticket(number: {self.number}, expires: {self.expires})"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TicketLock:
|
|
38
|
+
"""Locking mechanism that issues tickets managing access to conversation IDs.
|
|
39
|
+
|
|
40
|
+
Tickets are issued in the order in which they are requested. A detailed
|
|
41
|
+
explanation of the ticket lock algorithm can be found at
|
|
42
|
+
http://pages.cs.wisc.edu/~remzi/OSTEP/threads-locks.pdf#page=13
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self, conversation_id: Text, tickets: Optional[Deque[Ticket]] = None
|
|
47
|
+
) -> None:
|
|
48
|
+
self.conversation_id = conversation_id
|
|
49
|
+
self.tickets = tickets or deque()
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_dict(cls, data: Dict[Text, Any]) -> "TicketLock":
|
|
53
|
+
"""Create `TicketLock` from dictionary."""
|
|
54
|
+
tickets = [Ticket.from_dict(json.loads(d)) for d in data.get("tickets", [])]
|
|
55
|
+
return cls(data.get("conversation_id"), deque(tickets))
|
|
56
|
+
|
|
57
|
+
def dumps(self) -> Text:
|
|
58
|
+
"""Return json dump of `TicketLock`."""
|
|
59
|
+
tickets = [ticket.dumps() for ticket in self.tickets]
|
|
60
|
+
return json.dumps(dict(conversation_id=self.conversation_id, tickets=tickets))
|
|
61
|
+
|
|
62
|
+
def is_locked(self, ticket_number: int) -> bool:
|
|
63
|
+
"""Return whether `ticket_number` is locked.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if `now_serving` is not equal to `ticket`.
|
|
67
|
+
"""
|
|
68
|
+
return self.now_serving != ticket_number
|
|
69
|
+
|
|
70
|
+
def issue_ticket(self, lifetime: float) -> int:
|
|
71
|
+
"""Issue a new ticket and return its number."""
|
|
72
|
+
self.remove_expired_tickets()
|
|
73
|
+
number = self.last_issued + 1
|
|
74
|
+
ticket = Ticket(number, time.time() + lifetime)
|
|
75
|
+
self.tickets.append(ticket)
|
|
76
|
+
|
|
77
|
+
return number
|
|
78
|
+
|
|
79
|
+
def remove_expired_tickets(self) -> None:
|
|
80
|
+
"""Remove expired tickets."""
|
|
81
|
+
# iterate over copy of self.tickets so we can remove items
|
|
82
|
+
for ticket in list(self.tickets):
|
|
83
|
+
if ticket.has_expired():
|
|
84
|
+
self.tickets.remove(ticket)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def last_issued(self) -> int:
|
|
88
|
+
"""Return number of the ticket that was last added.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Number of `Ticket` that was last added. `NO_TICKET_ISSUED` if no
|
|
92
|
+
tickets exist.
|
|
93
|
+
"""
|
|
94
|
+
ticket_number = self._ticket_number_for(-1)
|
|
95
|
+
|
|
96
|
+
return ticket_number if ticket_number is not None else NO_TICKET_ISSUED
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def now_serving(self) -> Optional[int]:
|
|
100
|
+
"""Get number of the ticket to be served next.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Number of `Ticket` that is served next. 0 if no `Ticket` exists.
|
|
104
|
+
"""
|
|
105
|
+
return self._ticket_number_for(0) or 0
|
|
106
|
+
|
|
107
|
+
def _ticket_number_for(self, ticket_index: int) -> Optional[int]:
|
|
108
|
+
"""Get ticket number for `ticket_index`.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Ticket number for `Ticket` with index `ticket_index`. None if there are no
|
|
112
|
+
tickets, or if `ticket_index` is out of bounds of `self.tickets`.
|
|
113
|
+
"""
|
|
114
|
+
self.remove_expired_tickets()
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
return self.tickets[ticket_index].number
|
|
118
|
+
except IndexError:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def _ticket_for_ticket_number(self, ticket_number: int) -> Optional[Ticket]:
|
|
122
|
+
"""Return ticket for `ticket_number`."""
|
|
123
|
+
self.remove_expired_tickets()
|
|
124
|
+
|
|
125
|
+
return next((t for t in self.tickets if t.number == ticket_number), None)
|
|
126
|
+
|
|
127
|
+
def is_someone_waiting(self) -> bool:
|
|
128
|
+
"""Return whether someone is waiting for the lock to become available.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if the `self.tickets` queue has length greater than 0.
|
|
132
|
+
"""
|
|
133
|
+
return len(self.tickets) > 0
|
|
134
|
+
|
|
135
|
+
def remove_ticket_for(self, ticket_number: int) -> None:
|
|
136
|
+
"""Remove `Ticket` for `ticket_number."""
|
|
137
|
+
ticket = self._ticket_for_ticket_number(ticket_number)
|
|
138
|
+
if ticket:
|
|
139
|
+
self.tickets.remove(ticket)
|