rasa-pro 3.14.0.dev7__py3-none-any.whl → 3.14.0.dev8__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/agents/agent_manager.py +1 -1
- rasa/agents/constants.py +2 -2
- rasa/agents/protocol/a2a/a2a_agent.py +385 -227
- rasa/agents/protocol/mcp/mcp_base_agent.py +30 -13
- rasa/agents/protocol/mcp/mcp_open_agent.py +31 -8
- rasa/agents/protocol/mcp/mcp_task_agent.py +32 -9
- rasa/agents/schemas/agent_output.py +1 -1
- rasa/agents/utils.py +90 -1
- rasa/builder/README.md +120 -0
- rasa/builder/__init__.py +0 -0
- rasa/builder/auth.py +176 -0
- rasa/builder/config.py +92 -0
- rasa/builder/copilot/__init__.py +0 -0
- rasa/builder/copilot/constants.py +31 -0
- rasa/builder/copilot/copilot.py +450 -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 +500 -0
- rasa/builder/copilot/prompts/__init__.py +0 -0
- rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +766 -0
- rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +61 -0
- rasa/builder/copilot/signing.py +305 -0
- rasa/builder/copilot/telemetry.py +210 -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 +140 -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 +282 -0
- rasa/builder/llm_service.py +246 -0
- rasa/builder/logging_utils.py +265 -0
- rasa/builder/main.py +243 -0
- rasa/builder/models.py +216 -0
- rasa/builder/project_generator.py +458 -0
- rasa/builder/project_info.py +72 -0
- rasa/builder/scrape_rasa_docs.py +97 -0
- rasa/builder/service.py +1345 -0
- rasa/builder/shared/tracker_context.py +212 -0
- rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
- rasa/builder/template_cache.py +244 -0
- rasa/builder/training_service.py +194 -0
- rasa/builder/validation_service.py +97 -0
- rasa/cli/project_templates/basic/README.md +23 -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/actions/actions.md +10 -0
- rasa/cli/project_templates/basic/config.yml +29 -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 +8 -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 +67 -0
- rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
- rasa/cli/project_templates/default/config.yml +4 -0
- rasa/cli/project_templates/default/endpoints.yml +4 -0
- rasa/cli/project_templates/finance/README.md +25 -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/actions.md +15 -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 +23 -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/data.md +11 -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/domain.md +10 -0
- rasa/cli/project_templates/finance/domain/general/agent_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 +66 -0
- rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
- rasa/cli/project_templates/telco/README.md +25 -0
- rasa/cli/project_templates/telco/actions/__init__.py +0 -0
- rasa/cli/project_templates/telco/actions/actions.md +12 -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 +29 -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/data.md +11 -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/understand_bill.yml +102 -0
- rasa/cli/project_templates/telco/domain/domain.md +14 -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 +26 -0
- rasa/cli/project_templates/telco/domain/general/patterns.yml +33 -0
- rasa/cli/project_templates/telco/domain/network/reboot_router.yml +21 -0
- rasa/cli/project_templates/telco/domain/network/reset_router.yml +12 -0
- rasa/cli/project_templates/telco/domain/network/run_speed_test.yml +25 -0
- rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +75 -0
- rasa/cli/project_templates/telco/domain/shared.yml +129 -0
- rasa/cli/project_templates/telco/endpoints.yml +67 -0
- rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +40 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/billing/understand_bill.yml +67 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/bot_challenge.yml +8 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/feedback.yml +46 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/goodbye.yml +9 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/hello.yml +8 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/human_handoff.yml +35 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/general/patterns.yml +23 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +57 -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/available_agents.py +2 -0
- rasa/core/available_endpoints.py +17 -2
- rasa/core/channels/channel.py +4 -3
- rasa/core/channels/constants.py +3 -0
- rasa/core/channels/development_inspector.py +2 -22
- rasa/core/channels/inspector/README.md +26 -14
- rasa/core/channels/inspector/dist/assets/{arc-cce7e0a8.js → arc-edef10dd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-e2a49be7.js → blockDiagram-38ab4fdb-49f6762b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-3def7895.js → c4Diagram-3d4e48cf-313c08e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-63aa27d1.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-e66fe4df.js → classDiagram-70f12bd4-35e41ce9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-eb874aaa.js → classDiagram-v2-f2320105-f346068d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-5566bae8.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-cf934643.js → createText-2e5e7dd3-7a44bce8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-8fdf9155.js → edges-e0da2a9e-d7cf78c7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-6106fb96.js → erDiagram-9861fffd-9813e81c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-4c2bb040.js → flowDb-956e92f1-d8ba0870.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f0ff96af.js → flowDiagram-66a62f08-51f0db4d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-32936074.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a21707ec.js → flowchart-elk-definition-4a651766-ff9ea384.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-c165acb1.js → ganttDiagram-c361ad54-a8e13b6b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-b0564cf1.js → gitGraphDiagram-72cf32ee-3b171c6d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-e557e67a.js → graph-790ef78b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-1ce60e9e.js → index-3862675e-ecdce073.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-d705da80.js +1352 -0
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-893569e2.js → infoDiagram-f8f76790-f5a422fe.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-c29c864f.js → journeyDiagram-49397b02-3185b7ac.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-649a5eae.js → layout-837fd3aa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-0e5685ed.js → line-7e05afcb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-eaa320bd.js → linear-162eb295.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-f35df9e6.js → mindmap-definition-fc14e90a-f4978aee.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78339e96.js → pieDiagram-8a3498a8-b25d0a52.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-9b5f2f14.js → quadrantDiagram-120e2f19-63db1afa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-d05ddb3a.js → requirementDiagram-deff3bca-1b486cc9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-d9be5dfd.js → sankeyDiagram-04a897e0-7e795291.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-0f1c4348.js → sequenceDiagram-704730f1-b8aba159.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-9ddf63b3.js → stateDiagram-587899a1-41529fd5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-bc2b81ed.js → stateDiagram-v2-d93cdb3a-b241043c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0a287936.js → styles-6aaf32cf-b5b53234.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-e3941990.js → styles-9a916d00-13d138e5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-ce4eca24.js → styles-c10674c1-94cbde3f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-d822b1a8.js → svgDrawCommon-08f97a94-453ae764.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-e144c7a7.js → timeline-definition-85554ec2-8dcb88a4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-ab7f4e14.js → xychartDiagram-e933f94c-376af5f0.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 +16 -42
- rasa/core/channels/inspector/src/components/Chat.tsx +2 -3
- rasa/core/channels/inspector/src/components/DialogueHistoryStack.tsx +1 -0
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +20 -3
- rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +63 -35
- rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +14 -0
- rasa/core/channels/inspector/src/types.ts +32 -7
- rasa/core/channels/socketio.py +212 -51
- rasa/core/channels/studio_chat.py +59 -57
- rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
- rasa/core/channels/voice_stream/asr/azure.py +6 -3
- rasa/core/channels/voice_stream/asr/deepgram.py +1 -1
- rasa/core/channels/voice_stream/audiocodes.py +3 -0
- rasa/core/channels/voice_stream/browser_audio.py +53 -3
- rasa/core/channels/voice_stream/genesys.py +2 -1
- rasa/core/channels/voice_stream/jambonz.py +9 -1
- rasa/core/channels/voice_stream/twilio_media_streams.py +16 -0
- rasa/core/channels/voice_stream/voice_channel.py +66 -3
- rasa/core/constants.py +6 -0
- rasa/core/iam_credentials_providers/__init__.py +0 -0
- rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +66 -0
- rasa/core/iam_credentials_providers/credentials_provider_protocol.py +89 -0
- rasa/core/policies/enterprise_search_policy.py +4 -7
- rasa/core/policies/flows/flow_executor.py +14 -5
- rasa/core/policies/ted_policy.py +7 -5
- rasa/core/processor.py +32 -0
- rasa/core/redis_connection_factory.py +411 -0
- rasa/core/run.py +13 -3
- rasa/core/tracker_stores/redis_tracker_store.py +32 -14
- rasa/core/tracker_stores/sql_tracker_store.py +57 -1
- rasa/dialogue_understanding/generator/flow_retrieval.py +10 -9
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +10 -5
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +10 -5
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +20 -12
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +19 -12
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +6 -35
- rasa/dialogue_understanding/patterns/cancel.py +27 -6
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
- rasa/dialogue_understanding/processor/command_processor.py +35 -0
- rasa/engine/graph.py +5 -1
- rasa/engine/recipes/default_components.py +78 -10
- rasa/engine/recipes/default_recipe.py +41 -1
- rasa/engine/storage/local_model_storage.py +83 -3
- rasa/graph_components/validators/default_recipe_validator.py +153 -135
- rasa/model_manager/model_api.py +4 -5
- rasa/model_manager/runner_service.py +1 -1
- rasa/model_manager/socket_bridge.py +20 -15
- rasa/model_manager/trainer_service.py +12 -9
- rasa/model_manager/utils.py +1 -29
- rasa/model_manager/warm_rasa_process.py +1 -1
- rasa/model_training.py +14 -0
- rasa/nlu/classifiers/diet_classifier.py +22 -6
- rasa/nlu/classifiers/logistic_regression_classifier.py +18 -0
- rasa/nlu/extractors/extractor.py +1 -2
- rasa/shared/agents/auth/__init__.py +0 -0
- rasa/shared/agents/auth/agent_auth_factory.py +74 -0
- rasa/shared/agents/auth/agent_auth_manager.py +86 -0
- rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
- rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
- rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
- rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +159 -0
- rasa/shared/agents/auth/constants.py +11 -0
- rasa/shared/agents/auth/types.py +11 -0
- rasa/shared/core/constants.py +1 -0
- rasa/shared/core/domain.py +58 -11
- rasa/shared/core/events.py +2 -0
- rasa/shared/core/flows/constants.py +5 -0
- rasa/shared/core/flows/flow_step.py +7 -1
- rasa/shared/core/flows/flows_list.py +6 -0
- rasa/shared/core/flows/steps/call.py +15 -12
- rasa/shared/core/flows/validation.py +238 -44
- rasa/shared/core/flows/yaml_flows_io.py +15 -6
- rasa/shared/core/slots.py +4 -0
- rasa/shared/exceptions.py +12 -0
- rasa/shared/importers/importer.py +6 -0
- rasa/shared/importers/utils.py +77 -1
- rasa/shared/nlu/training_data/schemas/responses.yml +3 -0
- rasa/shared/providers/_utils.py +60 -44
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
- rasa/shared/providers/llm/_base_litellm_client.py +2 -2
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
- rasa/shared/providers/llm/llm_response.py +4 -4
- rasa/shared/utils/common.py +24 -0
- rasa/shared/utils/llm.py +2 -1
- rasa/shared/utils/mcp/server_connection.py +84 -23
- rasa/shared/utils/mcp/utils.py +20 -0
- rasa/studio/upload.py +16 -47
- rasa/telemetry.py +97 -23
- rasa/tracing/config.py +38 -12
- rasa/tracing/instrumentation/attribute_extractors.py +5 -1
- rasa/tracing/instrumentation/instrumentation.py +85 -8
- rasa/utils/common.py +1 -1
- 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/utils/tensorflow/__init__.py +29 -0
- rasa/utils/tensorflow/callback.py +1 -1
- rasa/utils/tensorflow/crf.py +1 -1
- rasa/utils/tensorflow/data_generator.py +21 -8
- rasa/utils/tensorflow/layers.py +11 -4
- rasa/utils/tensorflow/metrics.py +7 -3
- rasa/utils/tensorflow/models.py +41 -6
- rasa/utils/tensorflow/rasa_layers.py +6 -4
- rasa/utils/tensorflow/transformer.py +2 -3
- rasa/utils/train_utils.py +68 -38
- rasa/validator.py +18 -16
- rasa/version.py +1 -1
- rasa_pro-3.14.0.dev8.dist-info/METADATA +199 -0
- {rasa_pro-3.14.0.dev7.dist-info → rasa_pro-3.14.0.dev8.dist-info}/RECORD +466 -156
- rasa/core/channels/inspector/dist/assets/channel-858c2c20.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-4b80996c.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-16f09b7a.js +0 -1
- rasa/core/channels/inspector/dist/assets/index-996fe816.js +0 -1353
- rasa_pro-3.14.0.dev7.dist-info/METADATA +0 -190
- {rasa_pro-3.14.0.dev7.dist-info → rasa_pro-3.14.0.dev8.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0.dev7.dist-info → rasa_pro-3.14.0.dev8.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0.dev7.dist-info → rasa_pro-3.14.0.dev8.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Lakera AI guardrails provider implementation."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
from typing import Any, AsyncGenerator, Dict, Optional
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
import structlog
|
|
11
|
+
|
|
12
|
+
from rasa.builder import config
|
|
13
|
+
from rasa.builder.guardrails.constants import (
|
|
14
|
+
LAKERA_API_KEY_ENV_VAR,
|
|
15
|
+
LAKERA_GUARD_ENDPOINT,
|
|
16
|
+
LAKERA_GUARD_RESULTS_ENDPOINT,
|
|
17
|
+
)
|
|
18
|
+
from rasa.builder.guardrails.exceptions import GuardrailsError
|
|
19
|
+
from rasa.builder.guardrails.models import (
|
|
20
|
+
GuardrailResponse,
|
|
21
|
+
LakeraGuardrailRequest,
|
|
22
|
+
LakeraGuardrailResponse,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
structlogger = structlog.get_logger()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LakeraAIGuardrails:
|
|
29
|
+
"""Guardrails provider using Lakera AI."""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
api_key: Optional[str] = None,
|
|
34
|
+
base_url: Optional[str] = None,
|
|
35
|
+
):
|
|
36
|
+
"""Initialize Lakera guardrails provider.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
severity_threshold: The minimum severity level for the guardrail to flag
|
|
40
|
+
the user request as unsafe.
|
|
41
|
+
api_key: Lakera AI API key.
|
|
42
|
+
base_url: Optional base URL for the API. If not provided, the default
|
|
43
|
+
Lakera API URL (https://api.lakera.ai/v2) will be used.
|
|
44
|
+
"""
|
|
45
|
+
self.base_url: str = base_url or config.LAKERA_BASE_URL
|
|
46
|
+
self._api_key: Optional[str] = api_key or os.getenv(LAKERA_API_KEY_ENV_VAR)
|
|
47
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def guard_endpoint(self) -> str:
|
|
51
|
+
"""Get the guard endpoint for the Lakera API."""
|
|
52
|
+
return f"{self.base_url}/{LAKERA_GUARD_ENDPOINT}"
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def guard_results_endpoint(self) -> str:
|
|
56
|
+
"""Get the guard results endpoint for the Lakera API."""
|
|
57
|
+
return f"{self.base_url}/{LAKERA_GUARD_RESULTS_ENDPOINT}"
|
|
58
|
+
|
|
59
|
+
@asynccontextmanager
|
|
60
|
+
async def _get_session(self) -> AsyncGenerator[aiohttp.ClientSession, None]:
|
|
61
|
+
"""Create a fresh ClientSession, yield it, and always close it."""
|
|
62
|
+
session = aiohttp.ClientSession(headers=self._get_headers())
|
|
63
|
+
structlogger.debug("lakera_guardrails._get_session", base_url=self.base_url)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
yield session
|
|
67
|
+
except Exception as e:
|
|
68
|
+
structlogger.error("lakera_guardrails.session_error", error=str(e))
|
|
69
|
+
raise
|
|
70
|
+
finally:
|
|
71
|
+
try:
|
|
72
|
+
await session.close()
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
structlogger.warning(
|
|
75
|
+
"lakera_guardrails.session_close_error",
|
|
76
|
+
event_info="Failed to close aiohttp client session cleanly.",
|
|
77
|
+
error=str(exc),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _get_headers(self) -> Dict[str, str]:
|
|
81
|
+
"""Get the headers for the Lakera API request.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A dictionary containing the Authorization header with the API key.
|
|
85
|
+
"""
|
|
86
|
+
using_proxy = bool(config.HELLO_LLM_PROXY_BASE_URL)
|
|
87
|
+
|
|
88
|
+
if using_proxy:
|
|
89
|
+
if not config.RASA_PRO_LICENSE:
|
|
90
|
+
raise GuardrailsError(
|
|
91
|
+
"HELLO_LLM_PROXY_BASE_URL is set but RASA_PRO_LICENSE is missing. "
|
|
92
|
+
"Proxy requires a Rasa Pro license token for authentication."
|
|
93
|
+
)
|
|
94
|
+
return {"Authorization": f"Bearer {config.RASA_PRO_LICENSE}"}
|
|
95
|
+
|
|
96
|
+
if not self._api_key:
|
|
97
|
+
raise GuardrailsError(
|
|
98
|
+
"LAKERA_API_KEY is missing. Provide it via env LAKERA_API_KEY or "
|
|
99
|
+
"pass api_key= to LakeraAIGuardrails."
|
|
100
|
+
)
|
|
101
|
+
return {"Authorization": f"Bearer {self._api_key}"}
|
|
102
|
+
|
|
103
|
+
async def send_request(self, request: LakeraGuardrailRequest) -> GuardrailResponse:
|
|
104
|
+
"""Send a request to the Lakera API.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
request: The guardrail request to send to the Lakera API.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
GuardrailResponse with the results of the check.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
GuardrailsError: If the request times out or returns a non-200 status code.
|
|
114
|
+
Exception: If the request fails for any other reason.
|
|
115
|
+
"""
|
|
116
|
+
start_time = time.time()
|
|
117
|
+
try:
|
|
118
|
+
async with self._get_session() as session:
|
|
119
|
+
raw_response = await self._send_http_request(session, request)
|
|
120
|
+
response = LakeraGuardrailResponse.from_raw_response(
|
|
121
|
+
raw_response,
|
|
122
|
+
hello_rasa_user_id=request.hello_rasa_user_id,
|
|
123
|
+
hello_rasa_project_id=request.hello_rasa_project_id,
|
|
124
|
+
)
|
|
125
|
+
processing_time_ms = (time.time() - start_time) * 1000
|
|
126
|
+
response.processing_time_ms = processing_time_ms
|
|
127
|
+
return response
|
|
128
|
+
|
|
129
|
+
# Propagate the GuardrailsError if it occurs.
|
|
130
|
+
except GuardrailsError as e:
|
|
131
|
+
raise e
|
|
132
|
+
|
|
133
|
+
except asyncio.TimeoutError:
|
|
134
|
+
message = "Lakera API request timed out."
|
|
135
|
+
structlogger.error(
|
|
136
|
+
"lakera_guardrails.send_request.timeout_error",
|
|
137
|
+
event_info=message,
|
|
138
|
+
processing_time_ms=(time.time() - start_time) * 1000,
|
|
139
|
+
)
|
|
140
|
+
raise GuardrailsError(message)
|
|
141
|
+
|
|
142
|
+
# Propagate the unexpected exceptions.
|
|
143
|
+
except Exception as e:
|
|
144
|
+
message = "Lakera API request failed."
|
|
145
|
+
structlogger.error(
|
|
146
|
+
"lakera_guardrails.send_request.unexpected_error",
|
|
147
|
+
event_info="Lakera API request failed.",
|
|
148
|
+
request=request,
|
|
149
|
+
error=e,
|
|
150
|
+
processing_time_ms=(time.time() - start_time) * 1000,
|
|
151
|
+
)
|
|
152
|
+
raise e
|
|
153
|
+
|
|
154
|
+
async def _send_http_request(
|
|
155
|
+
self, session: aiohttp.ClientSession, request: LakeraGuardrailRequest
|
|
156
|
+
) -> Dict[str, Any]:
|
|
157
|
+
"""Make an HTTP request to the Lakera API.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
session: The aiohttp session to use for the request.
|
|
161
|
+
request: The guardrail request to send.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
The raw JSON response from the API.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
GuardrailsError: If the request fails or response parsing fails.
|
|
168
|
+
"""
|
|
169
|
+
# Log the request details for debugging
|
|
170
|
+
json_payload = request.to_json_payload()
|
|
171
|
+
structlogger.debug(
|
|
172
|
+
"lakera_guardrails.send_request.request",
|
|
173
|
+
url=self.guard_endpoint,
|
|
174
|
+
method="POST",
|
|
175
|
+
request_body=json_payload,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
async with session.post(
|
|
179
|
+
self.guard_endpoint,
|
|
180
|
+
json=json_payload,
|
|
181
|
+
) as client_response:
|
|
182
|
+
# Check if the response is successful. If not, raise an error.
|
|
183
|
+
if client_response.status >= 400:
|
|
184
|
+
error_text = await client_response.text()
|
|
185
|
+
message = (
|
|
186
|
+
f"Lakera API request failed with status "
|
|
187
|
+
f"`{client_response.status}`. Error: "
|
|
188
|
+
f"`{error_text}`."
|
|
189
|
+
)
|
|
190
|
+
structlogger.error(
|
|
191
|
+
"lakera_guardrails.send_request.http_error",
|
|
192
|
+
event_info=message,
|
|
193
|
+
url=self.guard_endpoint,
|
|
194
|
+
status=client_response.status,
|
|
195
|
+
error=error_text,
|
|
196
|
+
request_body=json_payload,
|
|
197
|
+
)
|
|
198
|
+
raise GuardrailsError(message)
|
|
199
|
+
|
|
200
|
+
# Parse the response as a dictionary.
|
|
201
|
+
raw_response = await client_response.json()
|
|
202
|
+
structlogger.debug(
|
|
203
|
+
"lakera_guardrails.send_request.response",
|
|
204
|
+
response_body=raw_response,
|
|
205
|
+
)
|
|
206
|
+
return raw_response
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Models for guardrails system."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GuardrailType(Enum):
|
|
12
|
+
"""Types of guardrails that can be applied with Lakera AI."""
|
|
13
|
+
|
|
14
|
+
PROMPT_ATTACK = "prompt_attack"
|
|
15
|
+
CONTENT_VIOLATION = "content_violation"
|
|
16
|
+
DATA_LEAKAGE = "data_leakage"
|
|
17
|
+
MALICIOUS_LINKS = "malicious_content"
|
|
18
|
+
CUSTOM = "custom"
|
|
19
|
+
OTHER = "other"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GuardrailRequest(BaseModel, ABC):
|
|
23
|
+
"""Request for guardrails check."""
|
|
24
|
+
|
|
25
|
+
hello_rasa_user_id: Optional[str] = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Required. User identifier for the Hello Rasa project. ",
|
|
28
|
+
)
|
|
29
|
+
hello_rasa_project_id: Optional[str] = Field(
|
|
30
|
+
default=None,
|
|
31
|
+
description="Required. Project identifier for the Hello Rasa project. ",
|
|
32
|
+
)
|
|
33
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
34
|
+
default=None, description="Additional metadata for the guardrails endpoint."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def to_json_payload(self) -> Dict[str, Any]:
|
|
39
|
+
"""Convert the request to a JSON payload."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class LakeraGuardrailRequest(GuardrailRequest):
|
|
44
|
+
"""Request for Lakera guardrails check."""
|
|
45
|
+
|
|
46
|
+
lakera_project_id: str = Field(
|
|
47
|
+
description="Required. Project identifier for the Lakera AI project."
|
|
48
|
+
)
|
|
49
|
+
payload: bool = Field(
|
|
50
|
+
default=True,
|
|
51
|
+
description=(
|
|
52
|
+
"From Lakera AI: When true the response will return a payload object "
|
|
53
|
+
"containing any PII, profanity or custom detector regex matches detected, "
|
|
54
|
+
"along with their location within the contents."
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
breakdown: bool = Field(
|
|
58
|
+
default=True,
|
|
59
|
+
description=(
|
|
60
|
+
"From Lakera AI: When true the response will return a breakdown list of "
|
|
61
|
+
"the detectors that were run, as defined in the policy, and whether each "
|
|
62
|
+
"of them detected something or not."
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
messages: List[Dict[str, Any]] = Field(
|
|
67
|
+
description=(
|
|
68
|
+
"Required. From Lakera AI: List of messages comprising the interaction "
|
|
69
|
+
"history with the LLM in OpenAI API Chat Completions format. Can be "
|
|
70
|
+
"multiple messages of any role: user, assistant, system, tool, or "
|
|
71
|
+
"developer."
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def to_json_payload(self) -> Dict[str, Any]:
|
|
76
|
+
"""Convert the request to a JSON payload to be sent to the Lakera endpoint."""
|
|
77
|
+
metadata = self.metadata or {}
|
|
78
|
+
metadata["hello_rasa_project_id"] = self.hello_rasa_project_id
|
|
79
|
+
metadata["hello_rasa_user_id"] = self.hello_rasa_user_id
|
|
80
|
+
|
|
81
|
+
json_payload: Dict[str, Any] = {
|
|
82
|
+
"messages": self.messages,
|
|
83
|
+
"project_id": self.lakera_project_id,
|
|
84
|
+
"metadata": metadata,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if self.payload:
|
|
88
|
+
json_payload["payload"] = self.payload
|
|
89
|
+
if self.breakdown:
|
|
90
|
+
json_payload["breakdown"] = self.breakdown
|
|
91
|
+
|
|
92
|
+
return json_payload
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class GuardrailDetection(BaseModel):
|
|
96
|
+
"""Represents a single guardrail detection."""
|
|
97
|
+
|
|
98
|
+
type: GuardrailType = Field(description="Type of guardrail detection.")
|
|
99
|
+
original_type: str = Field(description="Original detection from the provider.")
|
|
100
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
101
|
+
default=None, description="Additional metadata about the detection itself."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class GuardrailResponse(BaseModel):
|
|
106
|
+
"""Response from guardrails system."""
|
|
107
|
+
|
|
108
|
+
hello_rasa_user_id: Optional[str] = Field(
|
|
109
|
+
default=None,
|
|
110
|
+
description="Required. User identifier for the Hello Rasa project. ",
|
|
111
|
+
)
|
|
112
|
+
hello_rasa_project_id: Optional[str] = Field(
|
|
113
|
+
default=None,
|
|
114
|
+
description="Required. Project identifier for the Hello Rasa project. ",
|
|
115
|
+
)
|
|
116
|
+
flagged: bool = Field(description="Whether any policy violations were detected.")
|
|
117
|
+
detections: List[GuardrailDetection] = Field(
|
|
118
|
+
default_factory=list, description="List of detected policy violations."
|
|
119
|
+
)
|
|
120
|
+
processing_time_ms: Optional[float] = Field(
|
|
121
|
+
default=None, description="Processing time in milliseconds."
|
|
122
|
+
)
|
|
123
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
124
|
+
default=None, description="Additional metadata from the provider."
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class LakeraGuardrailResponse(GuardrailResponse):
|
|
129
|
+
"""Response from Lakera AI `/guard` endpoint."""
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def from_raw_response(
|
|
133
|
+
cls,
|
|
134
|
+
raw_response: Dict[str, Any],
|
|
135
|
+
hello_rasa_user_id: str,
|
|
136
|
+
hello_rasa_project_id: str,
|
|
137
|
+
) -> "LakeraGuardrailResponse":
|
|
138
|
+
"""Create a LakeraGuardrailResponse from a response."""
|
|
139
|
+
from rasa.builder.guardrails.utils import (
|
|
140
|
+
map_lakera_detector_type_to_guardrail_type,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Get the basic information from the response and create the response object.
|
|
144
|
+
flagged = raw_response.get("flagged", False)
|
|
145
|
+
metadata = raw_response.get("metadata")
|
|
146
|
+
response = cls(
|
|
147
|
+
flagged=flagged,
|
|
148
|
+
metadata=metadata,
|
|
149
|
+
hello_rasa_user_id=hello_rasa_user_id,
|
|
150
|
+
hello_rasa_project_id=hello_rasa_project_id,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# If the response is not flagged, return the response object.
|
|
154
|
+
if not flagged:
|
|
155
|
+
return response
|
|
156
|
+
|
|
157
|
+
# If the response is flagged, parse the breakdown section.
|
|
158
|
+
breakdown = raw_response.get("breakdown", [])
|
|
159
|
+
|
|
160
|
+
# Parse the breakdown.
|
|
161
|
+
detections: List[GuardrailDetection] = []
|
|
162
|
+
for detector in breakdown:
|
|
163
|
+
if detector.get("detected", True):
|
|
164
|
+
detector_type = detector.get("detector_type")
|
|
165
|
+
rasa_detection_type = map_lakera_detector_type_to_guardrail_type(
|
|
166
|
+
detector_type,
|
|
167
|
+
)
|
|
168
|
+
if not rasa_detection_type:
|
|
169
|
+
continue
|
|
170
|
+
# Remove the detector type and the detected flag from the detector.
|
|
171
|
+
# And keep the rest as part of the metadata. In case of a Lakera this
|
|
172
|
+
# will include the message_id, project_id, policy_id, detector_id, etc.
|
|
173
|
+
metadata = copy.deepcopy(detector)
|
|
174
|
+
metadata.pop("detected")
|
|
175
|
+
metadata.pop("detector_type")
|
|
176
|
+
|
|
177
|
+
detections.append(
|
|
178
|
+
GuardrailDetection(
|
|
179
|
+
type=rasa_detection_type,
|
|
180
|
+
original_type=detector_type,
|
|
181
|
+
metadata=metadata,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# If there are detections, add them to the response.
|
|
186
|
+
if detections:
|
|
187
|
+
response.detections = detections
|
|
188
|
+
|
|
189
|
+
return response
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class GuardrailRequestKey(BaseModel):
|
|
193
|
+
user_text: str
|
|
194
|
+
hello_rasa_user_id: str = ""
|
|
195
|
+
hello_rasa_project_id: str = ""
|
|
196
|
+
lakera_project_id: str
|
|
197
|
+
|
|
198
|
+
# hashable by value
|
|
199
|
+
model_config = ConfigDict(frozen=True)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class ScopeState(BaseModel):
|
|
203
|
+
blocked_until: Optional[float] = Field(
|
|
204
|
+
default=None,
|
|
205
|
+
description="UNIX timestamp in seconds until this scope is blocked.",
|
|
206
|
+
)
|
|
207
|
+
violations: List[float] = Field(
|
|
208
|
+
default_factory=list,
|
|
209
|
+
description="UNIX timestamps in seconds when violations occurred.",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def is_blocked(self, now: float) -> bool:
|
|
213
|
+
"""Return whether the scope is currently blocked.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
now: Current time as a UNIX timestamp.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
True if blocked_until is set and in the future, else False.
|
|
220
|
+
"""
|
|
221
|
+
return self.blocked_until is not None and now < self.blocked_until
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ProjectState(BaseModel):
|
|
225
|
+
project: ScopeState = Field(default_factory=ScopeState)
|
|
226
|
+
users: Dict[str, ScopeState] = Field(default_factory=dict)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class BlockResult(BaseModel):
|
|
230
|
+
user_blocked_now: bool = False
|
|
231
|
+
project_blocked_now: bool = False
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import structlog
|
|
6
|
+
|
|
7
|
+
from rasa.builder import config
|
|
8
|
+
from rasa.builder.guardrails.constants import (
|
|
9
|
+
BLOCK_SCOPE_PROJECT,
|
|
10
|
+
BLOCK_SCOPE_USER,
|
|
11
|
+
BlockScope,
|
|
12
|
+
)
|
|
13
|
+
from rasa.builder.guardrails.models import BlockResult, ProjectState, ScopeState
|
|
14
|
+
|
|
15
|
+
structlogger = structlog.get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GuardrailsInMemoryStore:
|
|
19
|
+
"""In-memory strike and block tracking for a single project instance."""
|
|
20
|
+
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self._state: ProjectState = ProjectState()
|
|
23
|
+
self._lock = asyncio.Lock()
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def _normalize_user_id(user_id: Optional[str]) -> str:
|
|
27
|
+
"""Normalize and validate user_id.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
user_id: User identifier.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Validated, non-empty user id.
|
|
34
|
+
"""
|
|
35
|
+
uid = (user_id or "").strip()
|
|
36
|
+
if not uid:
|
|
37
|
+
raise ValueError("user_id is required for guardrails tracking.")
|
|
38
|
+
return uid
|
|
39
|
+
|
|
40
|
+
def _get_or_create_user(self, user_id: str) -> ScopeState:
|
|
41
|
+
"""Return (and create if needed) the per-user scope state.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
user_id: User identifier.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Mutable ScopeState for the user.
|
|
48
|
+
"""
|
|
49
|
+
uid = self._normalize_user_id(user_id)
|
|
50
|
+
return self._state.users.setdefault(uid, ScopeState())
|
|
51
|
+
|
|
52
|
+
def _clear_expired_project_block(self, now: float) -> None:
|
|
53
|
+
"""Clear project-level block if expired.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
now: Current time as a UNIX timestamp.
|
|
57
|
+
"""
|
|
58
|
+
project = self._state.project
|
|
59
|
+
if project.blocked_until and now >= project.blocked_until:
|
|
60
|
+
project.blocked_until = None
|
|
61
|
+
|
|
62
|
+
def _clear_expired_user_block(self, user_id: str, now: float) -> None:
|
|
63
|
+
"""Clear user-level block if expired.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
user_id: User identifier.
|
|
67
|
+
now: Current time as a UNIX timestamp.
|
|
68
|
+
"""
|
|
69
|
+
try:
|
|
70
|
+
uid = self._normalize_user_id(user_id)
|
|
71
|
+
except ValueError:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
user_state = self._state.users.get(uid)
|
|
75
|
+
if user_state and user_state.blocked_until and now >= user_state.blocked_until:
|
|
76
|
+
user_state.blocked_until = None
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _apply_user_block_if_needed(user_id: str, user_state: ScopeState) -> bool:
|
|
80
|
+
"""Apply a user-level block if the threshold is reached.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
user_id: User identifier.
|
|
84
|
+
user_state: Current user's scope state.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if a block was applied during this call; otherwise False.
|
|
88
|
+
"""
|
|
89
|
+
now = time.time()
|
|
90
|
+
if user_state.is_blocked(now):
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
if len(user_state.violations) < config.GUARDRAILS_USER_MAX_STRIKES:
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
duration_seconds = config.GUARDRAILS_BLOCK_DURATION_SECONDS
|
|
97
|
+
block_until = float("inf") if duration_seconds <= 0 else now + duration_seconds
|
|
98
|
+
user_state.blocked_until = block_until
|
|
99
|
+
structlogger.info(
|
|
100
|
+
"guardrails.store.user_blocked",
|
|
101
|
+
project_id=config.HELLO_RASA_PROJECT_ID,
|
|
102
|
+
user_id=user_id,
|
|
103
|
+
strikes=len(user_state.violations),
|
|
104
|
+
duration_sec=duration_seconds,
|
|
105
|
+
)
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
def _apply_project_block_if_needed(self) -> bool:
|
|
109
|
+
"""Apply a project-level block if the threshold is reached.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if a block was applied during this call; otherwise False.
|
|
113
|
+
"""
|
|
114
|
+
now = time.time()
|
|
115
|
+
project_state = self._state.project
|
|
116
|
+
if project_state.is_blocked(now):
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
if len(project_state.violations) < config.GUARDRAILS_PROJECT_MAX_STRIKES:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
duration_seconds = config.GUARDRAILS_BLOCK_DURATION_SECONDS
|
|
123
|
+
block_until = float("inf") if duration_seconds <= 0 else now + duration_seconds
|
|
124
|
+
project_state.blocked_until = block_until
|
|
125
|
+
structlogger.info(
|
|
126
|
+
"guardrails.store.project_blocked",
|
|
127
|
+
project_id=config.HELLO_RASA_PROJECT_ID,
|
|
128
|
+
strikes=len(project_state.violations),
|
|
129
|
+
duration_sec=duration_seconds,
|
|
130
|
+
)
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
async def check_block_scope(self, user_id: str) -> Optional[BlockScope]:
|
|
134
|
+
"""Return current block scope if blocked.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
user_id: User identifier.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
'user' if user blocked, 'project' if project blocked, otherwise None.
|
|
141
|
+
"""
|
|
142
|
+
uid = self._normalize_user_id(user_id)
|
|
143
|
+
|
|
144
|
+
async with self._lock:
|
|
145
|
+
now = time.time()
|
|
146
|
+
|
|
147
|
+
# Clear expired blocks
|
|
148
|
+
self._clear_expired_project_block(now)
|
|
149
|
+
self._clear_expired_user_block(uid, now)
|
|
150
|
+
|
|
151
|
+
user_state = self._state.users.get(uid)
|
|
152
|
+
|
|
153
|
+
if user_state and user_state.is_blocked(now):
|
|
154
|
+
return BLOCK_SCOPE_USER
|
|
155
|
+
if self._state.project.is_blocked(now):
|
|
156
|
+
return BLOCK_SCOPE_PROJECT
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
async def record_violation(self, user_id: str) -> BlockResult:
|
|
160
|
+
"""Record a violation and apply thresholds/blocks.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
user_id: User identifier.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
BlockResult indicating whether a new user or project block was applied.
|
|
167
|
+
"""
|
|
168
|
+
uid = self._normalize_user_id(user_id)
|
|
169
|
+
result = BlockResult()
|
|
170
|
+
|
|
171
|
+
async with self._lock:
|
|
172
|
+
now = time.time()
|
|
173
|
+
user_state = self._get_or_create_user(uid)
|
|
174
|
+
|
|
175
|
+
# Accumulate violations (no sliding window)
|
|
176
|
+
self._state.project.violations.append(now)
|
|
177
|
+
user_state.violations.append(now)
|
|
178
|
+
|
|
179
|
+
# Apply user and project blocks
|
|
180
|
+
result.user_blocked_now = self._apply_user_block_if_needed(
|
|
181
|
+
user_id=uid,
|
|
182
|
+
user_state=user_state,
|
|
183
|
+
)
|
|
184
|
+
result.project_blocked_now = self._apply_project_block_if_needed()
|
|
185
|
+
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
async def unblock_user(self, user_id: str) -> None:
|
|
189
|
+
"""Unblock a user without altering strike history.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
user_id: User identifier.
|
|
193
|
+
"""
|
|
194
|
+
uid = self._normalize_user_id(user_id)
|
|
195
|
+
|
|
196
|
+
async with self._lock:
|
|
197
|
+
if state := self._state.users.get(uid):
|
|
198
|
+
state.blocked_until = None
|
|
199
|
+
|
|
200
|
+
async def reset_user(self, user_id: str) -> None:
|
|
201
|
+
"""Unblock a user and clear their strikes.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
user_id: User identifier.
|
|
205
|
+
"""
|
|
206
|
+
uid = self._normalize_user_id(user_id)
|
|
207
|
+
|
|
208
|
+
async with self._lock:
|
|
209
|
+
if state := self._state.users.get(uid):
|
|
210
|
+
state.blocked_until = None
|
|
211
|
+
state.violations.clear()
|
|
212
|
+
|
|
213
|
+
async def unblock_project(self) -> None:
|
|
214
|
+
"""Unblock the project without altering strike history."""
|
|
215
|
+
async with self._lock:
|
|
216
|
+
self._state.project.blocked_until = None
|
|
217
|
+
|
|
218
|
+
async def reset_project(self) -> None:
|
|
219
|
+
"""Unblock the project and clear project-wide strikes."""
|
|
220
|
+
async with self._lock:
|
|
221
|
+
self._state.project.blocked_until = None
|
|
222
|
+
self._state.project.violations.clear()
|
|
223
|
+
|
|
224
|
+
async def reset_all(self) -> None:
|
|
225
|
+
"""Reset all guardrail state (project and users)."""
|
|
226
|
+
async with self._lock:
|
|
227
|
+
# Clear project scope
|
|
228
|
+
self._state.project.blocked_until = None
|
|
229
|
+
self._state.project.violations.clear()
|
|
230
|
+
|
|
231
|
+
# Clear all users
|
|
232
|
+
for state in self._state.users.values():
|
|
233
|
+
state.blocked_until = None
|
|
234
|
+
state.violations.clear()
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# Singleton instance for application-wide use
|
|
238
|
+
guardrails_store = GuardrailsInMemoryStore()
|