rasa-pro 3.14.0.dev20250825__py3-none-any.whl → 3.14.0.dev20250901__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.
- rasa/builder/README.md +120 -0
- rasa/builder/__init__.py +0 -0
- rasa/builder/auth.py +176 -0
- rasa/builder/config.py +91 -0
- rasa/builder/copilot/__init__.py +0 -0
- rasa/builder/copilot/constants.py +28 -0
- rasa/builder/copilot/copilot.py +376 -0
- rasa/builder/copilot/copilot_response_handler.py +522 -0
- rasa/builder/copilot/copilot_templated_message_provider.py +58 -0
- rasa/builder/copilot/exceptions.py +32 -0
- rasa/builder/copilot/models.py +464 -0
- rasa/builder/copilot/prompts/__init__.py +0 -0
- rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +771 -0
- rasa/builder/copilot/signing.py +305 -0
- rasa/builder/copilot/telemetry.py +200 -0
- rasa/builder/copilot/templated_messages/__init__.py +0 -0
- rasa/builder/copilot/templated_messages/copilot_internal_messages_templates.yml +16 -0
- rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +38 -0
- rasa/builder/document_retrieval/__init__.py +0 -0
- rasa/builder/document_retrieval/constants.py +15 -0
- rasa/builder/document_retrieval/inkeep-rag-response-schema.json +64 -0
- rasa/builder/document_retrieval/inkeep_document_retrieval.py +238 -0
- rasa/builder/document_retrieval/models.py +62 -0
- rasa/builder/download.py +147 -0
- rasa/builder/exceptions.py +91 -0
- rasa/builder/guardrails/__init__.py +1 -0
- rasa/builder/guardrails/constants.py +9 -0
- rasa/builder/guardrails/exceptions.py +4 -0
- rasa/builder/guardrails/lakera.py +206 -0
- rasa/builder/guardrails/models.py +231 -0
- rasa/builder/guardrails/store.py +238 -0
- rasa/builder/guardrails/utils.py +328 -0
- rasa/builder/job_manager.py +87 -0
- rasa/builder/jobs.py +270 -0
- rasa/builder/llm_service.py +246 -0
- rasa/builder/logging_utils.py +265 -0
- rasa/builder/main.py +258 -0
- rasa/builder/models.py +216 -0
- rasa/builder/project_generator.py +462 -0
- rasa/builder/project_info.py +72 -0
- rasa/builder/scrape_rasa_docs.py +97 -0
- rasa/builder/service.py +1335 -0
- rasa/builder/shared/tracker_context.py +212 -0
- rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
- rasa/builder/training_service.py +124 -0
- rasa/builder/validation_service.py +97 -0
- rasa/cli/project_templates/basic/actions/__init__ +0 -0
- rasa/cli/project_templates/basic/actions/action_human_handoff.py +40 -0
- rasa/cli/project_templates/basic/config.yml +27 -0
- rasa/cli/project_templates/basic/credentials.yml +33 -0
- rasa/cli/project_templates/basic/data/data.md +9 -0
- rasa/cli/project_templates/basic/data/general/feedback.yml +21 -0
- rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
- rasa/cli/project_templates/basic/data/general/hello.yml +6 -0
- rasa/cli/project_templates/basic/data/general/help.yml +6 -0
- rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
- rasa/cli/project_templates/basic/data/general/show_faqs.yml +6 -0
- rasa/cli/project_templates/basic/data/system/patterns/pattern_cannot_handle.yml +7 -0
- rasa/cli/project_templates/basic/data/system/patterns/pattern_completed.yml +7 -0
- rasa/cli/project_templates/basic/data/system/patterns/pattern_correction.yml +7 -0
- rasa/cli/project_templates/basic/data/system/patterns/pattern_search.yml +8 -0
- rasa/cli/project_templates/basic/data/system/patterns/pattern_session_start.yml +8 -0
- rasa/cli/project_templates/basic/docs/docs.md +5 -0
- rasa/cli/project_templates/basic/docs/template.txt +28 -0
- rasa/cli/project_templates/basic/domain/domain.md +9 -0
- rasa/cli/project_templates/basic/domain/general/feedback.yml +25 -0
- rasa/cli/project_templates/basic/domain/general/goodbye.yml +9 -0
- rasa/cli/project_templates/basic/domain/general/hello.yml +7 -0
- rasa/cli/project_templates/basic/domain/general/help.yml +21 -0
- rasa/cli/project_templates/basic/domain/general/human_handoff.yml +32 -0
- rasa/cli/project_templates/basic/domain/general/show_faqs.yml +14 -0
- rasa/cli/project_templates/basic/domain/system/patterns/pattern_cannot_handle.yml +5 -0
- rasa/cli/project_templates/basic/domain/system/patterns/pattern_session_start.yml +19 -0
- rasa/cli/project_templates/basic/endpoints.yml +63 -0
- rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
- rasa/cli/project_templates/finance/actions/__init__.py +46 -0
- rasa/cli/project_templates/finance/actions/accounts/__init__.py +0 -0
- rasa/cli/project_templates/finance/actions/accounts/action_ask_account.py +47 -0
- rasa/cli/project_templates/finance/actions/accounts/action_check_balance.py +40 -0
- rasa/cli/project_templates/finance/actions/action_session_start.py +74 -0
- rasa/cli/project_templates/finance/actions/cards/__init__.py +0 -0
- rasa/cli/project_templates/finance/actions/cards/action_ask_card.py +48 -0
- rasa/cli/project_templates/finance/actions/cards/action_check_card_existence.py +36 -0
- rasa/cli/project_templates/finance/actions/cards/action_update_card_status.py +54 -0
- rasa/cli/project_templates/finance/actions/database.py +277 -0
- rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
- rasa/cli/project_templates/finance/actions/transfers/action_add_payee.py +52 -0
- rasa/cli/project_templates/finance/actions/transfers/action_ask_account_from.py +51 -0
- rasa/cli/project_templates/finance/actions/transfers/action_check_payee_existence.py +40 -0
- rasa/cli/project_templates/finance/actions/transfers/action_check_sufficient_funds.py +40 -0
- rasa/cli/project_templates/finance/actions/transfers/action_list_payees.py +46 -0
- rasa/cli/project_templates/finance/actions/transfers/action_process_immediate_payment.py +18 -0
- rasa/cli/project_templates/finance/actions/transfers/action_remove_payee.py +49 -0
- rasa/cli/project_templates/finance/actions/transfers/action_schedule_payment.py +19 -0
- rasa/cli/project_templates/finance/actions/transfers/action_validate_payment_date.py +36 -0
- rasa/cli/project_templates/finance/config.yml +21 -0
- rasa/cli/project_templates/finance/credentials.yml +32 -0
- rasa/cli/project_templates/finance/csvs/accounts.csv +8 -0
- rasa/cli/project_templates/finance/csvs/advisors.csv +7 -0
- rasa/cli/project_templates/finance/csvs/appointments.csv +211 -0
- rasa/cli/project_templates/finance/csvs/branches.csv +10 -0
- rasa/cli/project_templates/finance/csvs/cards.csv +11 -0
- rasa/cli/project_templates/finance/csvs/payees.csv +11 -0
- rasa/cli/project_templates/finance/csvs/transactions.csv +71 -0
- rasa/cli/project_templates/finance/csvs/users.csv +4 -0
- rasa/cli/project_templates/finance/data/accounts/check_balance.yml +10 -0
- rasa/cli/project_templates/finance/data/cards/block_card.yml +66 -0
- rasa/cli/project_templates/finance/data/cards/select_card.yml +12 -0
- rasa/cli/project_templates/finance/data/general/bot_identity.yml +6 -0
- rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
- rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
- rasa/cli/project_templates/finance/data/general/hello.yml +7 -0
- rasa/cli/project_templates/finance/data/general/help.yml +9 -0
- rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
- rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
- rasa/cli/project_templates/finance/data/system/patterns/pattern_chitchat.yml +5 -0
- rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
- rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
- rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
- rasa/cli/project_templates/finance/data/system/patterns/pattern_session_start.yml +8 -0
- rasa/cli/project_templates/finance/data/system/source/accounts.json +51 -0
- rasa/cli/project_templates/finance/data/system/source/advisors.json +44 -0
- rasa/cli/project_templates/finance/data/system/source/appointments.json +1474 -0
- rasa/cli/project_templates/finance/data/system/source/branches.json +47 -0
- rasa/cli/project_templates/finance/data/system/source/cards.json +72 -0
- rasa/cli/project_templates/finance/data/system/source/payees.json +74 -0
- rasa/cli/project_templates/finance/data/system/source/transactions.json +492 -0
- rasa/cli/project_templates/finance/data/system/source/users.json +29 -0
- rasa/cli/project_templates/finance/data/transfers/add_payee.yml +29 -0
- rasa/cli/project_templates/finance/data/transfers/list_payees.yml +5 -0
- rasa/cli/project_templates/finance/data/transfers/remove_payee.yml +21 -0
- rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +67 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/consequences_of_blocking_card.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/reasons_to_block_card.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/recovering_from_card_fraud.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/tips_for_card_security.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/what_to_do_if_card_is_lost.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/account_balance_security.txt +7 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/common_balance_inquiries.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/methods_to_check_balance.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/understanding_balance_updates.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/what_to_do_if_balance_is_incorrect.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/benefits_of_authorised_payees.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/common_issues_with_payees.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/general_payee_information.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/payee_management_tips.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/understanding_payee_types.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/common_transfer_errors.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/fees_for_transfers.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/general_transfer_information.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/security_tips_for_transfers.txt +8 -0
- rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/transfer_processing_times.txt +8 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part1.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part10.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part11.txt +48 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part12.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part13.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part14.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part15.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part16.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part17.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part18.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part19.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part2.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part20.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part21.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part22.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part23.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part24.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part25.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part26.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part27.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part28.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part29.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part3.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part30.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part31.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part32.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part33.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part34.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part35.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part36.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part37.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part38.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part39.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part4.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part40.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part41.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part42.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part43.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part44.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part45.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part46.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part47.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part48.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part49.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part5.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part50.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part51.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part52.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part53.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part54.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part55.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part56.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part57.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part58.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part59.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part6.txt +47 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part60.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part61.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part7.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part8.txt +50 -0
- rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part9.txt +47 -0
- rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +11 -0
- rasa/cli/project_templates/finance/domain/cards/block_card.yml +101 -0
- rasa/cli/project_templates/finance/domain/cards/select_card.yml +12 -0
- rasa/cli/project_templates/finance/domain/general/assistant_details.yml +12 -0
- rasa/cli/project_templates/finance/domain/general/bot_identity.yml +5 -0
- rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +5 -0
- rasa/cli/project_templates/finance/domain/general/defaults.yml +24 -0
- rasa/cli/project_templates/finance/domain/general/feedback.yml +28 -0
- rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
- rasa/cli/project_templates/finance/domain/general/help.yml +5 -0
- rasa/cli/project_templates/finance/domain/general/human_handoff.yml +30 -0
- rasa/cli/project_templates/finance/domain/general/utils.yml +13 -0
- rasa/cli/project_templates/finance/domain/general/welcome.yml +8 -0
- rasa/cli/project_templates/finance/domain/transfers/add_payee.yml +47 -0
- rasa/cli/project_templates/finance/domain/transfers/list_payees.yml +4 -0
- rasa/cli/project_templates/finance/domain/transfers/remove_payee.yml +16 -0
- rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +79 -0
- rasa/cli/project_templates/finance/endpoints.yml +63 -0
- rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
- rasa/cli/project_templates/telco/actions/__init__.py +0 -0
- rasa/cli/project_templates/telco/actions/billing/__init__.py +0 -0
- rasa/cli/project_templates/telco/actions/billing/actions_billing.py +204 -0
- rasa/cli/project_templates/telco/actions/general/__init__.py +0 -0
- rasa/cli/project_templates/telco/actions/general/action_human_handoff.py +49 -0
- rasa/cli/project_templates/telco/actions/network/__init__.py +0 -0
- rasa/cli/project_templates/telco/actions/network/actions_get_data_from_db.py +48 -0
- rasa/cli/project_templates/telco/actions/network/actions_run_diagnostics.py +28 -0
- rasa/cli/project_templates/telco/actions/network/actions_session_start.py +18 -0
- rasa/cli/project_templates/telco/config.yml +27 -0
- rasa/cli/project_templates/telco/credentials.yml +33 -0
- rasa/cli/project_templates/telco/csvs/billing.csv +19 -0
- rasa/cli/project_templates/telco/csvs/customers.csv +5 -0
- rasa/cli/project_templates/telco/data/billing/flow_understand_bill.yml +45 -0
- rasa/cli/project_templates/telco/data/general/bot_challenge.yml +6 -0
- rasa/cli/project_templates/telco/data/general/feedback.yml +20 -0
- rasa/cli/project_templates/telco/data/general/goodbye.yml +6 -0
- rasa/cli/project_templates/telco/data/general/hello.yml +6 -0
- rasa/cli/project_templates/telco/data/general/human_handoff.yml +16 -0
- rasa/cli/project_templates/telco/data/general/patterns.yml +30 -0
- rasa/cli/project_templates/telco/data/network/flow_reboot_router.yml +8 -0
- rasa/cli/project_templates/telco/data/network/flow_reset_router.yml +7 -0
- rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +73 -0
- rasa/cli/project_templates/telco/docs/docs.md +5 -0
- rasa/cli/project_templates/telco/docs/network/reset_vs_rboot_router.txt +1 -0
- rasa/cli/project_templates/telco/docs/network/restart_router.txt +6 -0
- rasa/cli/project_templates/telco/docs/network/run_speed_test.txt +6 -0
- rasa/cli/project_templates/telco/domain/billing/domain_undertand_bill.yml +102 -0
- rasa/cli/project_templates/telco/domain/general/bot_challenge.yml +4 -0
- rasa/cli/project_templates/telco/domain/general/feedback.yml +25 -0
- rasa/cli/project_templates/telco/domain/general/goodbye.yml +7 -0
- rasa/cli/project_templates/telco/domain/general/hello.yml +5 -0
- rasa/cli/project_templates/telco/domain/general/human_handoff.yml +29 -0
- rasa/cli/project_templates/telco/domain/general/patterns.yml +33 -0
- rasa/cli/project_templates/telco/domain/network/domain_reboot_router.yml +21 -0
- rasa/cli/project_templates/telco/domain/network/domain_reset_router.yml +12 -0
- rasa/cli/project_templates/telco/domain/network/domain_run_speed_test.yml +25 -0
- rasa/cli/project_templates/telco/domain/network/domain_solve_internet_issue.yml +75 -0
- rasa/cli/project_templates/telco/domain/shared.yml +129 -0
- rasa/cli/project_templates/telco/endpoints.yml +63 -0
- rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +40 -0
- rasa/cli/project_templates/tutorial/config.yml +2 -1
- rasa/cli/scaffold.py +46 -2
- rasa/core/actions/action.py +0 -1
- rasa/core/channels/inspector/dist/assets/{arc-1ddec37b.js → arc-18042c22.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-18af387c.js → blockDiagram-38ab4fdb-fdd6bcfa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-250127a3.js → c4Diagram-3d4e48cf-f5ae6786.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-b9b536fc.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-c3388b34.js → classDiagram-70f12bd4-81efba3e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-9c893a82.js → classDiagram-v2-f2320105-3b6b6a92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-78d2ddcf.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-c111213b.js → createText-2e5e7dd3-31422447.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-812a729d.js → edges-e0da2a9e-518a90db.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-fd5051bc.js → erDiagram-9861fffd-a6d3c25a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-3287ac02.js → flowDb-956e92f1-e048c2be.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-692fb0b2.js → flowDiagram-66a62f08-c7474c91.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-8b09c060.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-008376f1.js → flowchart-elk-definition-4a651766-cb4d8723.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-df330a69.js → ganttDiagram-c361ad54-346636a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-e03676fb.js → gitGraphDiagram-72cf32ee-7c508874.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-46fad2ba.js → graph-14702d8a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-a484ac55.js → index-3862675e-f18b534b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-a003633f.js → index-4d4bdf3a.js} +231 -231
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-3f9e6ec2.js → infoDiagram-f8f76790-64154b83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-79f72383.js → journeyDiagram-49397b02-833a5f95.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-aad098e5.js → layout-5a3b2123.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-219ab7ae.js → line-2272a8c7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-2cddbe62.js → linear-35bcf273.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-1d41ed99.js → mindmap-definition-fc14e90a-92dcb0e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-cc496ee8.js → pieDiagram-8a3498a8-94dbc900.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-84d32884.js → quadrantDiagram-120e2f19-8b7a9c33.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-c0deb984.js → requirementDiagram-deff3bca-6f7eab81.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-b9d7fd62.js → sankeyDiagram-04a897e0-f43e581d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-7d517565.js → sequenceDiagram-704730f1-0bcbefc3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-98ef9b27.js → stateDiagram-587899a1-b8a74083.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-cee70748.js → stateDiagram-v2-d93cdb3a-2070218f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-3f9d1c96.js → styles-6aaf32cf-f1d54e34.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-67471923.js → styles-9a916d00-980de489.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-bd093fb7.js → styles-c10674c1-3c03abde.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-675794e8.js → svgDrawCommon-08f97a94-46ba068f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0ac67617.js → timeline-definition-85554ec2-901f5e3d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-c018dc37.js → xychartDiagram-e933f94c-acbc628a.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +2 -2
- rasa/core/channels/inspector/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +10 -11
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +12 -3
- rasa/core/channels/studio_chat.py +25 -7
- rasa/core/policies/enterprise_search_policy.py +4 -7
- rasa/core/policies/flows/flow_executor.py +8 -1
- rasa/dialogue_understanding/generator/flow_retrieval.py +10 -9
- rasa/engine/storage/local_model_storage.py +45 -2
- rasa/model_manager/model_api.py +1 -2
- rasa/model_manager/runner_service.py +1 -1
- rasa/model_manager/trainer_service.py +12 -9
- rasa/model_manager/utils.py +1 -29
- rasa/shared/core/domain.py +62 -15
- rasa/shared/core/flows/flow_step.py +7 -1
- rasa/shared/core/flows/steps/call.py +8 -1
- rasa/shared/core/flows/yaml_flows_io.py +16 -8
- rasa/shared/core/slots.py +4 -0
- rasa/shared/importers/importer.py +6 -0
- rasa/shared/importers/utils.py +77 -1
- rasa/studio/upload.py +12 -46
- rasa/telemetry.py +97 -23
- rasa/utils/io.py +27 -9
- rasa/utils/json_utils.py +6 -1
- rasa/utils/log_utils.py +5 -1
- rasa/utils/openapi.py +144 -0
- rasa/validator.py +7 -3
- rasa/version.py +1 -1
- {rasa_pro-3.14.0.dev20250825.dist-info → rasa_pro-3.14.0.dev20250901.dist-info}/METADATA +9 -10
- {rasa_pro-3.14.0.dev20250825.dist-info → rasa_pro-3.14.0.dev20250901.dist-info}/RECORD +347 -73
- rasa/core/channels/inspector/dist/assets/channel-59f6d54b.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-26177ddb.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-29c03f5a.js +0 -1
- {rasa_pro-3.14.0.dev20250825.dist-info → rasa_pro-3.14.0.dev20250901.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0.dev20250825.dist-info → rasa_pro-3.14.0.dev20250901.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0.dev20250825.dist-info → rasa_pro-3.14.0.dev20250901.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"""Service for generating Rasa projects from prompts."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import tarfile
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from textwrap import dedent
|
|
10
|
+
from typing import Any, Dict, Generator, List, Optional
|
|
11
|
+
|
|
12
|
+
import aiofiles
|
|
13
|
+
import aiohttp
|
|
14
|
+
import structlog
|
|
15
|
+
|
|
16
|
+
import rasa.version
|
|
17
|
+
from rasa.builder import config
|
|
18
|
+
from rasa.builder.exceptions import ProjectGenerationError, ValidationError
|
|
19
|
+
from rasa.builder.llm_service import get_skill_generation_messages, llm_service
|
|
20
|
+
from rasa.builder.logging_utils import capture_exception_with_context
|
|
21
|
+
from rasa.builder.models import BotFiles
|
|
22
|
+
from rasa.builder.project_info import ProjectInfo, ensure_first_used, load_project_info
|
|
23
|
+
from rasa.builder.training_service import TrainingInput
|
|
24
|
+
from rasa.builder.validation_service import validate_project
|
|
25
|
+
from rasa.cli.scaffold import ProjectTemplateName, create_initial_project
|
|
26
|
+
from rasa.shared.core.flows import yaml_flows_io
|
|
27
|
+
from rasa.shared.importers.importer import TrainingDataImporter
|
|
28
|
+
from rasa.shared.utils.yaml import dump_obj_as_yaml_to_string
|
|
29
|
+
from rasa.utils.io import subpath
|
|
30
|
+
|
|
31
|
+
structlogger = structlog.get_logger()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ProjectGenerator:
|
|
35
|
+
"""Service for generating Rasa projects from skill descriptions."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, project_folder: str) -> None:
|
|
38
|
+
"""Initialize the project generator with a folder for file persistence.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
project_folder: Path to the folder where project files will be stored
|
|
42
|
+
"""
|
|
43
|
+
self.project_folder = Path(project_folder)
|
|
44
|
+
self.project_folder.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def project_info(self) -> ProjectInfo:
|
|
48
|
+
"""Get the project info."""
|
|
49
|
+
return load_project_info(self.project_folder)
|
|
50
|
+
|
|
51
|
+
async def init_from_template(self, template: ProjectTemplateName) -> None:
|
|
52
|
+
"""Create the initial project files."""
|
|
53
|
+
self.cleanup()
|
|
54
|
+
create_initial_project(self.project_folder.as_posix(), template)
|
|
55
|
+
await download_cache_for_template(template, self.project_folder.as_posix())
|
|
56
|
+
# needs to happen after caching, as we download .rasa and that would
|
|
57
|
+
# overwrite the project info file in .rasa
|
|
58
|
+
ensure_first_used(self.project_folder)
|
|
59
|
+
|
|
60
|
+
async def generate_project_with_retries(
|
|
61
|
+
self,
|
|
62
|
+
skill_description: str,
|
|
63
|
+
template: ProjectTemplateName,
|
|
64
|
+
max_retries: Optional[int] = None,
|
|
65
|
+
) -> Dict[str, Optional[str]]:
|
|
66
|
+
"""Generate a Rasa project with retry logic for validation failures.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
skill_description: Natural language description of the skill
|
|
70
|
+
rasa_config: Rasa configuration dictionary
|
|
71
|
+
template: Project template to use for the initial project
|
|
72
|
+
max_retries: Maximum number of retry attempts
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Dictionary of generated file contents (filename -> content)
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ProjectGenerationError: If generation fails after all retries
|
|
79
|
+
"""
|
|
80
|
+
if max_retries is None:
|
|
81
|
+
max_retries = config.MAX_RETRIES
|
|
82
|
+
|
|
83
|
+
# if ever we do not use the template, we need to ensure first_used is set
|
|
84
|
+
# separately!
|
|
85
|
+
await self.init_from_template(template)
|
|
86
|
+
|
|
87
|
+
project_data = self._get_bot_data_for_llm()
|
|
88
|
+
|
|
89
|
+
initial_messages = get_skill_generation_messages(
|
|
90
|
+
skill_description, project_data
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
async def _generate_with_retry(
|
|
94
|
+
messages: List[Dict[str, Any]], attempts_left: int
|
|
95
|
+
) -> Dict[str, Optional[str]]:
|
|
96
|
+
try:
|
|
97
|
+
# Generate project data using LLM
|
|
98
|
+
project_data = await llm_service.generate_rasa_project(messages)
|
|
99
|
+
|
|
100
|
+
# Update stored bot data
|
|
101
|
+
self._update_bot_files_from_llm_response(project_data)
|
|
102
|
+
|
|
103
|
+
bot_files = self.get_bot_files()
|
|
104
|
+
structlogger.info(
|
|
105
|
+
"project_generator.generated_project",
|
|
106
|
+
attempts_left=attempts_left,
|
|
107
|
+
files=list(bot_files.keys()),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Validate the generated project
|
|
111
|
+
await self._validate_generated_project()
|
|
112
|
+
|
|
113
|
+
structlogger.info(
|
|
114
|
+
"project_generator.validation_success", attempts_left=attempts_left
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return bot_files
|
|
118
|
+
|
|
119
|
+
except ValidationError as e:
|
|
120
|
+
structlogger.error(
|
|
121
|
+
"project_generator.validation_error",
|
|
122
|
+
error=str(e),
|
|
123
|
+
attempts_left=attempts_left,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if attempts_left <= 0:
|
|
127
|
+
raise ProjectGenerationError(
|
|
128
|
+
f"Failed to generate valid Rasa project: {e}", max_retries
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Create error feedback for next attempt
|
|
132
|
+
error_feedback_messages = messages + [
|
|
133
|
+
{
|
|
134
|
+
"role": "assistant",
|
|
135
|
+
"content": json.dumps(project_data),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"role": "user",
|
|
139
|
+
"content": dedent(f"""
|
|
140
|
+
Previous attempt failed validation with error: {e}
|
|
141
|
+
|
|
142
|
+
Please fix the issues and generate a valid Rasa project.
|
|
143
|
+
Pay special attention to:
|
|
144
|
+
- Proper YAML syntax
|
|
145
|
+
- Required fields in domain and flows
|
|
146
|
+
- Consistent naming between flows and domain
|
|
147
|
+
- Valid slot types and mappings
|
|
148
|
+
""").strip(),
|
|
149
|
+
},
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
return await _generate_with_retry(
|
|
153
|
+
error_feedback_messages, attempts_left - 1
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
structlogger.error(
|
|
158
|
+
"project_generator.generation_error",
|
|
159
|
+
error=str(e),
|
|
160
|
+
attempts_left=attempts_left,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if attempts_left <= 0:
|
|
164
|
+
raise ProjectGenerationError(
|
|
165
|
+
f"Failed to generate Rasa project: {e}", max_retries
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# For non-validation errors, retry with original messages
|
|
169
|
+
return await _generate_with_retry(initial_messages, attempts_left - 1)
|
|
170
|
+
|
|
171
|
+
return await _generate_with_retry(initial_messages, max_retries)
|
|
172
|
+
|
|
173
|
+
async def _validate_generated_project(self) -> None:
|
|
174
|
+
"""Validate the generated project using the validation service."""
|
|
175
|
+
importer = self._create_importer()
|
|
176
|
+
validation_error = await validate_project(importer)
|
|
177
|
+
|
|
178
|
+
if validation_error:
|
|
179
|
+
raise ValidationError(validation_error)
|
|
180
|
+
|
|
181
|
+
def _get_endpoints_file(self) -> Path:
|
|
182
|
+
"""Get the endpoints file."""
|
|
183
|
+
return self.project_folder / "endpoints.yml"
|
|
184
|
+
|
|
185
|
+
def get_training_input(self) -> TrainingInput:
|
|
186
|
+
"""Get the training input."""
|
|
187
|
+
return TrainingInput(
|
|
188
|
+
importer=self._create_importer(),
|
|
189
|
+
endpoints_file=self._get_endpoints_file(),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _create_importer(self) -> TrainingDataImporter:
|
|
193
|
+
"""Create a training data importer from the current bot files."""
|
|
194
|
+
try:
|
|
195
|
+
if (self.project_folder / "domain.yml").exists():
|
|
196
|
+
domain_path = self.project_folder / "domain.yml"
|
|
197
|
+
else:
|
|
198
|
+
domain_path = self.project_folder / "domain"
|
|
199
|
+
|
|
200
|
+
return TrainingDataImporter.load_from_config(
|
|
201
|
+
config_path=str(self.project_folder / "config.yml"),
|
|
202
|
+
domain_path=str(domain_path),
|
|
203
|
+
training_data_paths=[str(self.project_folder / "data")],
|
|
204
|
+
args={},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
except Exception as e:
|
|
208
|
+
raise ValidationError(f"Failed to create importer: {e}")
|
|
209
|
+
|
|
210
|
+
def get_bot_files(
|
|
211
|
+
self,
|
|
212
|
+
allowed_file_extensions: Optional[List[str]] = None,
|
|
213
|
+
exclude_docs_directory: bool = False,
|
|
214
|
+
) -> BotFiles:
|
|
215
|
+
"""Get the current bot files by reading from disk.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
allowed_file_extensions: Optional list of file extensions to include.
|
|
219
|
+
If None, fetch all files. If provided, only fetch files with matching
|
|
220
|
+
extensions. Use `""` empty string to allow files with no extensions.
|
|
221
|
+
exclude_docs_directory: Optional boolean indicating whether to exclude.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Dictionary of file contents with relative paths as keys
|
|
225
|
+
"""
|
|
226
|
+
bot_files: BotFiles = {}
|
|
227
|
+
|
|
228
|
+
for file in self.project_folder.glob("**/*"):
|
|
229
|
+
# Skip directories
|
|
230
|
+
if not file.is_file():
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
relative_path = file.relative_to(self.project_folder)
|
|
234
|
+
|
|
235
|
+
# Skip hidden files and directories (any path component starting with '.')
|
|
236
|
+
# as well as `__pycache__` folders
|
|
237
|
+
if any(part.startswith(".") for part in relative_path.parts):
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
if "__pycache__" in relative_path.parts:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
# exclude the project_folder / models folder
|
|
244
|
+
if relative_path.parts[0] == "models":
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
# Exclude the docs directory if specified
|
|
248
|
+
if exclude_docs_directory and relative_path.parts[0] == "docs":
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
# Exclude the files by file extensions if specified
|
|
252
|
+
if allowed_file_extensions is not None:
|
|
253
|
+
allowed_file_extensions = [
|
|
254
|
+
ext.lower() for ext in allowed_file_extensions
|
|
255
|
+
]
|
|
256
|
+
if file.suffix.lstrip(".").lower() not in allowed_file_extensions:
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
# Read file content and store with relative path as key
|
|
260
|
+
try:
|
|
261
|
+
bot_files[relative_path.as_posix()] = file.read_text(encoding="utf-8")
|
|
262
|
+
except Exception as e:
|
|
263
|
+
structlogger.debug(
|
|
264
|
+
"project_generator.get_bot_files.error",
|
|
265
|
+
error=str(e),
|
|
266
|
+
file_path=file.as_posix(),
|
|
267
|
+
)
|
|
268
|
+
bot_files[relative_path.as_posix()] = None
|
|
269
|
+
|
|
270
|
+
return bot_files
|
|
271
|
+
|
|
272
|
+
def _get_bot_data_for_llm(self) -> Dict[str, Any]:
|
|
273
|
+
"""Get the current bot data for the LLM."""
|
|
274
|
+
file_importer = self._create_importer()
|
|
275
|
+
|
|
276
|
+
# only include data created by the user (or the builder llm)
|
|
277
|
+
# avoid including to many defaults that are not customized
|
|
278
|
+
domain = file_importer.get_user_domain()
|
|
279
|
+
flows = file_importer.get_user_flows()
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
"domain": domain.as_dict(should_clean_json=True),
|
|
283
|
+
"flows": yaml_flows_io.get_flows_as_json(flows, should_clean_json=True),
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
def _path_for_flow(self, flow_id: str) -> str:
|
|
287
|
+
"""Get the path for a flow."""
|
|
288
|
+
if flow_id.startswith("pattern_"):
|
|
289
|
+
return f"data/patterns/{flow_id}.yml"
|
|
290
|
+
else:
|
|
291
|
+
return f"data/flows/{flow_id}.yml"
|
|
292
|
+
|
|
293
|
+
def _update_bot_files_from_llm_response(self, project_data: Dict[str, Any]) -> None:
|
|
294
|
+
"""Update the bot files with generated data by writing to disk."""
|
|
295
|
+
files = {"domain.yml": dump_obj_as_yaml_to_string(project_data["domain"])}
|
|
296
|
+
# split up flows into one file per flow in the /flows folder
|
|
297
|
+
for flow_id, flow_data in project_data["flows"].get("flows", {}).items():
|
|
298
|
+
flow_file_path = self._path_for_flow(flow_id)
|
|
299
|
+
single_flow_file_data = {"flows": {flow_id: flow_data}}
|
|
300
|
+
files[flow_file_path] = dump_obj_as_yaml_to_string(single_flow_file_data)
|
|
301
|
+
|
|
302
|
+
# removes any other flows that the LLM didn't generate
|
|
303
|
+
self._cleanup_flows()
|
|
304
|
+
self.update_bot_files(files)
|
|
305
|
+
|
|
306
|
+
def _cleanup_flows(self) -> None:
|
|
307
|
+
"""Cleanup the flows folder."""
|
|
308
|
+
flows_folder = self.project_folder / "data" / "flows"
|
|
309
|
+
if flows_folder.exists():
|
|
310
|
+
shutil.rmtree(flows_folder)
|
|
311
|
+
flows_folder.mkdir(parents=True, exist_ok=True)
|
|
312
|
+
|
|
313
|
+
def update_bot_files(self, files: Dict[str, Optional[str]]) -> None:
|
|
314
|
+
"""Update bot files with new content by writing to disk."""
|
|
315
|
+
for filename, content in files.items():
|
|
316
|
+
file_path = Path(subpath(self.project_folder, filename))
|
|
317
|
+
# Disallow updates inside .rasa project metadata directory
|
|
318
|
+
if any(
|
|
319
|
+
part.startswith(".")
|
|
320
|
+
for part in file_path.relative_to(self.project_folder).parts
|
|
321
|
+
):
|
|
322
|
+
# silently ignore hidden paths
|
|
323
|
+
continue
|
|
324
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
325
|
+
file_path.write_text(content, encoding="utf-8")
|
|
326
|
+
|
|
327
|
+
def cleanup(self) -> None:
|
|
328
|
+
"""Cleanup the project folder."""
|
|
329
|
+
# remove all the files and folders in the project folder resulting
|
|
330
|
+
# in an empty folder
|
|
331
|
+
for filename in os.listdir(self.project_folder):
|
|
332
|
+
file_path = os.path.join(self.project_folder, filename)
|
|
333
|
+
try:
|
|
334
|
+
if os.path.isfile(file_path) or os.path.islink(file_path):
|
|
335
|
+
os.unlink(file_path)
|
|
336
|
+
elif os.path.isdir(file_path):
|
|
337
|
+
shutil.rmtree(file_path)
|
|
338
|
+
except Exception as e:
|
|
339
|
+
structlogger.error(
|
|
340
|
+
"project_generator.cleanup_error",
|
|
341
|
+
error=str(e),
|
|
342
|
+
file_path=file_path,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
CACHE_BUCKET_URL = "https://trained-templates.s3.us-east-1.amazonaws.com"
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _safe_tar_members(
|
|
350
|
+
tar: tarfile.TarFile, destination_directory: Path
|
|
351
|
+
) -> Generator[tarfile.TarInfo, None, None]:
|
|
352
|
+
"""Yield safe members for extraction to prevent path traversal and links.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
tar: Open tar file handle
|
|
356
|
+
destination_directory: Directory to which files will be extracted
|
|
357
|
+
|
|
358
|
+
Yields:
|
|
359
|
+
Members that are safe to extract within destination_directory
|
|
360
|
+
"""
|
|
361
|
+
base_path = destination_directory.resolve()
|
|
362
|
+
|
|
363
|
+
for member in tar.getmembers():
|
|
364
|
+
name = member.name
|
|
365
|
+
# Skip empty names and absolute paths
|
|
366
|
+
if not name or name.startswith("/") or name.startswith("\\"):
|
|
367
|
+
continue
|
|
368
|
+
|
|
369
|
+
# Disallow symlinks and hardlinks
|
|
370
|
+
if member.issym() or member.islnk():
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
# Compute the final path and ensure it's within base_path
|
|
374
|
+
target_path = (base_path / name).resolve()
|
|
375
|
+
try:
|
|
376
|
+
target_path.relative_to(base_path)
|
|
377
|
+
except ValueError:
|
|
378
|
+
# Member would escape the destination directory
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
yield member
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
async def download_cache_for_template(
|
|
385
|
+
template: ProjectTemplateName, project_folder: str
|
|
386
|
+
) -> None:
|
|
387
|
+
# get a temp path for the cache file download
|
|
388
|
+
temporary_cache_file = tempfile.NamedTemporaryFile(suffix=".tar.gz", delete=False)
|
|
389
|
+
|
|
390
|
+
try:
|
|
391
|
+
url = f"{CACHE_BUCKET_URL}/{rasa.version.__version__}-{template.value}.tar.gz"
|
|
392
|
+
async with aiohttp.ClientSession() as session:
|
|
393
|
+
async with session.get(url) as response:
|
|
394
|
+
response.raise_for_status()
|
|
395
|
+
async with aiofiles.open(temporary_cache_file.name, "wb") as f:
|
|
396
|
+
async for chunk in response.content.iter_chunked(1024 * 1024):
|
|
397
|
+
await f.write(chunk)
|
|
398
|
+
|
|
399
|
+
# extract the cache to the project folder using safe member filtering
|
|
400
|
+
with tarfile.open(temporary_cache_file.name, "r:gz") as tar:
|
|
401
|
+
destination = Path(project_folder)
|
|
402
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
403
|
+
tar.extractall(
|
|
404
|
+
path=destination,
|
|
405
|
+
members=_safe_tar_members(tar, destination),
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
structlogger.info(
|
|
409
|
+
"project_generator.download_cache_for_template.success",
|
|
410
|
+
template=template,
|
|
411
|
+
event_info=(
|
|
412
|
+
f"Downloaded cache for template, extracted to {project_folder}."
|
|
413
|
+
),
|
|
414
|
+
)
|
|
415
|
+
except aiohttp.ClientResponseError as e:
|
|
416
|
+
if e.status == 403:
|
|
417
|
+
structlogger.debug(
|
|
418
|
+
"project_generator.download_cache_for_template.no_cache_found",
|
|
419
|
+
template=template,
|
|
420
|
+
event_info=("No cache found for template, continuing without it."),
|
|
421
|
+
)
|
|
422
|
+
else:
|
|
423
|
+
structlogger.debug(
|
|
424
|
+
"project_generator.download_cache_for_template.response_error",
|
|
425
|
+
error=str(e),
|
|
426
|
+
status=e.status,
|
|
427
|
+
template=template,
|
|
428
|
+
event_info=(
|
|
429
|
+
"Failed to download cache for template, continuing without it."
|
|
430
|
+
),
|
|
431
|
+
)
|
|
432
|
+
capture_exception_with_context(
|
|
433
|
+
e,
|
|
434
|
+
"project_generator.download_cache_for_template.response_error",
|
|
435
|
+
tags={"template": template.value, "status": str(e.status)},
|
|
436
|
+
)
|
|
437
|
+
except Exception as exc:
|
|
438
|
+
structlogger.debug(
|
|
439
|
+
"project_generator.download_cache_for_template.unexpected_error",
|
|
440
|
+
error=str(exc),
|
|
441
|
+
template=template,
|
|
442
|
+
event_info=(
|
|
443
|
+
"Unexpected error when downloading cache for template, "
|
|
444
|
+
"continuing without it."
|
|
445
|
+
),
|
|
446
|
+
)
|
|
447
|
+
capture_exception_with_context(
|
|
448
|
+
exc,
|
|
449
|
+
"project_generator.download_cache_for_template.unexpected_error",
|
|
450
|
+
tags={"template": template.value},
|
|
451
|
+
)
|
|
452
|
+
finally:
|
|
453
|
+
# Clean up the temporary file
|
|
454
|
+
try:
|
|
455
|
+
Path(temporary_cache_file.name).unlink(missing_ok=True)
|
|
456
|
+
except Exception as exc:
|
|
457
|
+
structlogger.debug(
|
|
458
|
+
"project_generator.download_cache_for_template.cleanup_error",
|
|
459
|
+
error=str(exc),
|
|
460
|
+
template=template,
|
|
461
|
+
event_info=("Failed to cleanup cache for template, ignoring."),
|
|
462
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import asdict, dataclass
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from rasa.constants import RASA_DIR_NAME
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ProjectInfo:
|
|
14
|
+
"""Metadata persisted for the builder about the current project.
|
|
15
|
+
|
|
16
|
+
- first_used_utc_iso: RFC3339/ISO8601 string of when the project was first used.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
first_used_utc_iso: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_json(cls, data: str) -> "ProjectInfo":
|
|
23
|
+
payload = json.loads(data) if data else {}
|
|
24
|
+
return cls(**payload)
|
|
25
|
+
|
|
26
|
+
def to_json(self) -> str:
|
|
27
|
+
return json.dumps(asdict(self))
|
|
28
|
+
|
|
29
|
+
def first_used_dt(self) -> Optional[datetime]:
|
|
30
|
+
if not self.first_used_utc_iso:
|
|
31
|
+
return None
|
|
32
|
+
value = self.first_used_utc_iso.strip()
|
|
33
|
+
if value.endswith("Z"):
|
|
34
|
+
value = value[:-1] + "+00:00"
|
|
35
|
+
try:
|
|
36
|
+
dt = datetime.fromisoformat(value)
|
|
37
|
+
except Exception:
|
|
38
|
+
return None
|
|
39
|
+
if dt.tzinfo is None:
|
|
40
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
41
|
+
return dt.astimezone(timezone.utc)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _project_info_path(project_folder: Path) -> Path:
|
|
45
|
+
return project_folder / RASA_DIR_NAME / "project_info.json"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def load_project_info(project_folder: Path) -> ProjectInfo:
|
|
49
|
+
path = _project_info_path(project_folder)
|
|
50
|
+
try:
|
|
51
|
+
if path.exists():
|
|
52
|
+
return ProjectInfo.from_json(path.read_text(encoding="utf-8"))
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
return ProjectInfo()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def save_project_info(project_folder: Path, info: ProjectInfo) -> None:
|
|
59
|
+
path = _project_info_path(project_folder)
|
|
60
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
path.write_text(info.to_json(), encoding="utf-8")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ensure_first_used(project_folder: Path) -> datetime:
|
|
65
|
+
info = load_project_info(project_folder)
|
|
66
|
+
if existing := info.first_used_dt():
|
|
67
|
+
return existing
|
|
68
|
+
|
|
69
|
+
now = datetime.now(timezone.utc)
|
|
70
|
+
info.first_used_utc_iso = now.isoformat()
|
|
71
|
+
save_project_info(project_folder, info)
|
|
72
|
+
return now
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from urllib.parse import urljoin, urlparse
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from bs4 import BeautifulSoup
|
|
8
|
+
|
|
9
|
+
BASE_URL = "https://rasa.com"
|
|
10
|
+
DOCS_ROOT = "https://rasa.com/docs"
|
|
11
|
+
OUTPUT_DIR = "rasa_docs_md"
|
|
12
|
+
MAX_PAGES = 100 # Optional limit for safety
|
|
13
|
+
|
|
14
|
+
visited = set()
|
|
15
|
+
to_visit = [DOCS_ROOT]
|
|
16
|
+
|
|
17
|
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_valid_doc_url(url: str) -> bool:
|
|
21
|
+
return url.startswith(DOCS_ROOT) and not any(
|
|
22
|
+
[url.endswith(".pdf"), "#" in url, "mailto:" in url]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def slugify_url(url: str) -> str:
|
|
27
|
+
path = urlparse(url).path.strip("/").replace("/", "_")
|
|
28
|
+
return path if path else "index"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clean_text(html: str) -> str:
|
|
32
|
+
soup = BeautifulSoup(html, "html.parser")
|
|
33
|
+
|
|
34
|
+
# Remove navs, footers, and code tabs (customize if needed)
|
|
35
|
+
for tag in soup(["nav", "footer", "script", "style", "form", "button"]):
|
|
36
|
+
tag.decompose()
|
|
37
|
+
|
|
38
|
+
main = soup.find("main") or soup.body
|
|
39
|
+
if not main:
|
|
40
|
+
return ""
|
|
41
|
+
|
|
42
|
+
# Replace <code> with backticks
|
|
43
|
+
for code in main.find_all("code"):
|
|
44
|
+
code.string = f"`{code.get_text(strip=True)}`"
|
|
45
|
+
|
|
46
|
+
text = main.get_text(separator="\n", strip=True)
|
|
47
|
+
return text
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def save_as_markdown(text: str, url: str) -> str:
|
|
51
|
+
slug = slugify_url(url)
|
|
52
|
+
file_name = f"{slug}.md"
|
|
53
|
+
md_path = Path(OUTPUT_DIR) / file_name
|
|
54
|
+
with open(md_path, "w", encoding="utf-8") as f:
|
|
55
|
+
f.write(text)
|
|
56
|
+
|
|
57
|
+
print(f"✅ Saved: {md_path}")
|
|
58
|
+
return file_name
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
pages_scraped = 0
|
|
62
|
+
markdown_to_url = {}
|
|
63
|
+
|
|
64
|
+
while to_visit and pages_scraped < MAX_PAGES:
|
|
65
|
+
url = to_visit.pop(0)
|
|
66
|
+
if url in visited:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
print(f"Scraping: {url}")
|
|
71
|
+
response = requests.get(url)
|
|
72
|
+
response.raise_for_status()
|
|
73
|
+
|
|
74
|
+
html = response.text
|
|
75
|
+
text = clean_text(html)
|
|
76
|
+
if len(text) < 200: # skip very short pages
|
|
77
|
+
print("⏭️ Skipped (too short)")
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
file_name = save_as_markdown(text, url)
|
|
81
|
+
markdown_to_url[file_name] = url
|
|
82
|
+
pages_scraped += 1
|
|
83
|
+
|
|
84
|
+
soup = BeautifulSoup(html, "html.parser")
|
|
85
|
+
for link_tag in soup.find_all("a", href=True):
|
|
86
|
+
link = urljoin(url, link_tag["href"])
|
|
87
|
+
if is_valid_doc_url(link) and link not in visited:
|
|
88
|
+
to_visit.append(link)
|
|
89
|
+
|
|
90
|
+
visited.add(url)
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"⚠️ Failed to scrape {url}: {e}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
with open("markdown_to_url.json", "w") as f:
|
|
97
|
+
json.dump(markdown_to_url, f, indent=2)
|