rasa-pro 3.14.0.dev20250922__py3-none-any.whl → 3.14.0rc2__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.

Files changed (304) hide show
  1. rasa/__main__.py +15 -3
  2. rasa/agents/__init__.py +0 -0
  3. rasa/agents/agent_factory.py +122 -0
  4. rasa/agents/agent_manager.py +211 -0
  5. rasa/agents/constants.py +43 -0
  6. rasa/agents/core/__init__.py +0 -0
  7. rasa/agents/core/agent_protocol.py +107 -0
  8. rasa/agents/core/types.py +81 -0
  9. rasa/agents/exceptions.py +38 -0
  10. rasa/agents/protocol/__init__.py +5 -0
  11. rasa/agents/protocol/a2a/__init__.py +0 -0
  12. rasa/agents/protocol/a2a/a2a_agent.py +879 -0
  13. rasa/agents/protocol/mcp/__init__.py +0 -0
  14. rasa/agents/protocol/mcp/mcp_base_agent.py +726 -0
  15. rasa/agents/protocol/mcp/mcp_open_agent.py +327 -0
  16. rasa/agents/protocol/mcp/mcp_task_agent.py +522 -0
  17. rasa/agents/schemas/__init__.py +13 -0
  18. rasa/agents/schemas/agent_input.py +38 -0
  19. rasa/agents/schemas/agent_output.py +26 -0
  20. rasa/agents/schemas/agent_tool_result.py +65 -0
  21. rasa/agents/schemas/agent_tool_schema.py +186 -0
  22. rasa/agents/templates/__init__.py +0 -0
  23. rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +20 -0
  24. rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +22 -0
  25. rasa/agents/utils.py +206 -0
  26. rasa/agents/validation.py +485 -0
  27. rasa/api.py +24 -9
  28. rasa/builder/config.py +6 -2
  29. rasa/builder/copilot/constants.py +4 -1
  30. rasa/builder/copilot/copilot.py +155 -79
  31. rasa/builder/copilot/models.py +304 -108
  32. rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
  33. rasa/builder/guardrails/{lakera.py → clients.py} +55 -5
  34. rasa/builder/guardrails/constants.py +3 -0
  35. rasa/builder/guardrails/models.py +45 -10
  36. rasa/builder/guardrails/policy_checker.py +324 -0
  37. rasa/builder/guardrails/utils.py +42 -276
  38. rasa/builder/jobs.py +182 -12
  39. rasa/builder/llm_service.py +32 -5
  40. rasa/builder/models.py +13 -3
  41. rasa/builder/project_generator.py +6 -1
  42. rasa/builder/service.py +31 -15
  43. rasa/builder/training_service.py +18 -24
  44. rasa/builder/validation_service.py +1 -1
  45. rasa/cli/arguments/default_arguments.py +12 -0
  46. rasa/cli/arguments/run.py +2 -0
  47. rasa/cli/arguments/train.py +2 -0
  48. rasa/cli/data.py +10 -8
  49. rasa/cli/dialogue_understanding_test.py +10 -7
  50. rasa/cli/e2e_test.py +9 -6
  51. rasa/cli/evaluate.py +4 -2
  52. rasa/cli/export.py +5 -2
  53. rasa/cli/inspect.py +8 -4
  54. rasa/cli/interactive.py +5 -4
  55. rasa/cli/llm_fine_tuning.py +11 -6
  56. rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
  57. rasa/cli/project_templates/tutorial/credentials.yml +10 -0
  58. rasa/cli/run.py +12 -10
  59. rasa/cli/scaffold.py +4 -4
  60. rasa/cli/shell.py +9 -5
  61. rasa/cli/studio/studio.py +1 -1
  62. rasa/cli/test.py +34 -14
  63. rasa/cli/train.py +41 -28
  64. rasa/cli/utils.py +1 -393
  65. rasa/cli/validation/__init__.py +0 -0
  66. rasa/cli/validation/bot_config.py +223 -0
  67. rasa/cli/validation/config_path_validation.py +257 -0
  68. rasa/cli/x.py +8 -4
  69. rasa/constants.py +7 -1
  70. rasa/core/actions/action.py +51 -10
  71. rasa/core/actions/grpc_custom_action_executor.py +1 -1
  72. rasa/core/agent.py +19 -2
  73. rasa/core/available_agents.py +229 -0
  74. rasa/core/brokers/kafka.py +5 -1
  75. rasa/core/channels/__init__.py +82 -35
  76. rasa/core/channels/development_inspector.py +3 -3
  77. rasa/core/channels/inspector/README.md +25 -13
  78. rasa/core/channels/inspector/dist/assets/{arc-35222594.js → arc-6177260a.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-a0efbfd3.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-0584c0f2.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
  82. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-39f40dbe.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-1ad755f3.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
  85. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-b0f4f0fe.js → createText-2e5e7dd3-088372e2.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-9039bff9.js → edges-e0da2a9e-58676240.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-65c9b127.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-4f08b38e.js → flowDb-956e92f1-ea63f85c.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-e95c362a.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
  91. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-703c3015.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-699328ea.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-04cf4b05.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{graph-ee94449e.js → graph-0a6f8466.js} +1 -1
  95. rasa/core/channels/inspector/dist/assets/{index-3862675e-940162b4.js → index-3862675e-7610671a.js} +1 -1
  96. rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
  97. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-c79c2866.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
  98. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-84489d30.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
  99. rasa/core/channels/inspector/dist/assets/{layout-a9aa9858.js → layout-e7fbc2bf.js} +1 -1
  100. rasa/core/channels/inspector/dist/assets/{line-eb73cf26.js → line-a8aa457c.js} +1 -1
  101. rasa/core/channels/inspector/dist/assets/{linear-b3399f9a.js → linear-3351e0d2.js} +1 -1
  102. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-b095bf1a.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
  103. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-07644b66.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
  104. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-573a3f9c.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
  105. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-d457e1e1.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
  106. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-9d26e1a2.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
  107. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3a9cde10.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
  108. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-4f3e8cec.js → stateDiagram-587899a1-0e770395.js} +1 -1
  109. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e617e5bf.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
  110. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-eab30d2f.js → styles-6aaf32cf-36a9e70d.js} +1 -1
  111. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-09994be2.js → styles-9a916d00-884a8b5b.js} +1 -1
  112. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-b7110364.js → styles-c10674c1-dc097813.js} +1 -1
  113. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-3ebc92ad.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
  114. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-7d13d2f2.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
  115. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-488385e1.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
  116. rasa/core/channels/inspector/dist/index.html +1 -1
  117. rasa/core/channels/inspector/package.json +18 -18
  118. rasa/core/channels/inspector/src/App.tsx +29 -4
  119. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  120. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
  121. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +7 -4
  122. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  123. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  124. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  125. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  126. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  127. rasa/core/channels/inspector/src/types.ts +21 -0
  128. rasa/core/channels/inspector/yarn.lock +336 -189
  129. rasa/core/channels/studio_chat.py +6 -6
  130. rasa/core/channels/telegram.py +4 -9
  131. rasa/core/channels/voice_stream/genesys.py +1 -1
  132. rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
  133. rasa/core/channels/voice_stream/twilio_media_streams.py +5 -1
  134. rasa/core/channels/voice_stream/voice_channel.py +3 -0
  135. rasa/core/concurrent_lock_store.py +38 -21
  136. rasa/core/config/__init__.py +0 -0
  137. rasa/core/{available_endpoints.py → config/available_endpoints.py} +51 -16
  138. rasa/core/config/configuration.py +260 -0
  139. rasa/core/config/credentials.py +19 -0
  140. rasa/core/config/message_procesing_config.py +34 -0
  141. rasa/core/constants.py +10 -0
  142. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +69 -4
  143. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +2 -1
  144. rasa/core/lock_store.py +4 -0
  145. rasa/core/policies/enterprise_search_policy.py +5 -3
  146. rasa/core/policies/flow_policy.py +4 -4
  147. rasa/core/policies/flows/agent_executor.py +632 -0
  148. rasa/core/policies/flows/flow_executor.py +136 -75
  149. rasa/core/policies/flows/mcp_tool_executor.py +298 -0
  150. rasa/core/policies/intentless_policy.py +1 -1
  151. rasa/core/policies/ted_policy.py +20 -12
  152. rasa/core/policies/unexpected_intent_policy.py +6 -0
  153. rasa/core/processor.py +68 -44
  154. rasa/core/redis_connection_factory.py +7 -2
  155. rasa/core/run.py +37 -8
  156. rasa/core/test.py +4 -0
  157. rasa/core/tracker_stores/redis_tracker_store.py +4 -0
  158. rasa/core/tracker_stores/sql_tracker_store.py +3 -1
  159. rasa/core/tracker_stores/tracker_store.py +3 -7
  160. rasa/core/train.py +1 -1
  161. rasa/core/training/interactive.py +20 -18
  162. rasa/core/training/story_conflict.py +5 -5
  163. rasa/core/utils.py +22 -23
  164. rasa/dialogue_understanding/commands/__init__.py +8 -0
  165. rasa/dialogue_understanding/commands/cancel_flow_command.py +19 -5
  166. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
  167. rasa/dialogue_understanding/commands/clarify_command.py +20 -2
  168. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  169. rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
  170. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  171. rasa/dialogue_understanding/commands/start_flow_command.py +68 -7
  172. rasa/dialogue_understanding/commands/utils.py +124 -2
  173. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  174. rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
  175. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  176. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
  177. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
  178. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
  179. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
  180. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
  181. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
  182. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
  183. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
  184. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  185. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  186. rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
  187. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +46 -8
  188. rasa/dialogue_understanding/processor/command_processor.py +136 -15
  189. rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
  190. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  191. rasa/dialogue_understanding/stack/utils.py +57 -3
  192. rasa/dialogue_understanding/utils.py +24 -4
  193. rasa/dialogue_understanding_test/du_test_runner.py +8 -3
  194. rasa/e2e_test/e2e_test_runner.py +13 -3
  195. rasa/engine/caching.py +2 -2
  196. rasa/engine/constants.py +1 -1
  197. rasa/engine/recipes/default_components.py +138 -49
  198. rasa/engine/recipes/default_recipe.py +108 -11
  199. rasa/engine/runner/dask.py +8 -5
  200. rasa/engine/validation.py +19 -6
  201. rasa/graph_components/validators/default_recipe_validator.py +86 -28
  202. rasa/hooks.py +5 -5
  203. rasa/llm_fine_tuning/utils.py +2 -2
  204. rasa/model_training.py +60 -47
  205. rasa/nlu/classifiers/diet_classifier.py +198 -98
  206. rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
  207. rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
  208. rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
  209. rasa/nlu/extractors/crf_entity_extractor.py +9 -10
  210. rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
  211. rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
  212. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
  213. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
  214. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
  215. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
  216. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
  217. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
  218. rasa/nlu/selectors/response_selector.py +10 -2
  219. rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
  220. rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
  221. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
  222. rasa/nlu/utils/mitie_utils.py +3 -0
  223. rasa/nlu/utils/spacy_utils.py +3 -2
  224. rasa/plugin.py +8 -8
  225. rasa/privacy/privacy_manager.py +12 -3
  226. rasa/server.py +15 -3
  227. rasa/shared/agents/__init__.py +0 -0
  228. rasa/shared/agents/auth/__init__.py +0 -0
  229. rasa/shared/agents/auth/agent_auth_factory.py +105 -0
  230. rasa/shared/agents/auth/agent_auth_manager.py +92 -0
  231. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  232. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  233. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  234. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  235. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +167 -0
  236. rasa/shared/agents/auth/constants.py +12 -0
  237. rasa/shared/agents/auth/types.py +12 -0
  238. rasa/shared/agents/utils.py +35 -0
  239. rasa/shared/constants.py +8 -0
  240. rasa/shared/core/constants.py +16 -1
  241. rasa/shared/core/domain.py +0 -7
  242. rasa/shared/core/events.py +327 -0
  243. rasa/shared/core/flows/constants.py +5 -0
  244. rasa/shared/core/flows/flows_list.py +21 -5
  245. rasa/shared/core/flows/flows_yaml_schema.json +119 -184
  246. rasa/shared/core/flows/steps/call.py +49 -5
  247. rasa/shared/core/flows/steps/collect.py +98 -13
  248. rasa/shared/core/flows/validation.py +372 -8
  249. rasa/shared/core/flows/yaml_flows_io.py +3 -2
  250. rasa/shared/core/slots.py +2 -2
  251. rasa/shared/core/trackers.py +5 -2
  252. rasa/shared/exceptions.py +16 -0
  253. rasa/shared/importers/rasa.py +1 -1
  254. rasa/shared/importers/utils.py +9 -3
  255. rasa/shared/providers/llm/_base_litellm_client.py +41 -9
  256. rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
  257. rasa/shared/providers/llm/llm_client.py +7 -3
  258. rasa/shared/providers/llm/llm_response.py +66 -0
  259. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  260. rasa/shared/utils/common.py +24 -0
  261. rasa/shared/utils/health_check/health_check.py +7 -3
  262. rasa/shared/utils/llm.py +39 -16
  263. rasa/shared/utils/mcp/__init__.py +0 -0
  264. rasa/shared/utils/mcp/server_connection.py +247 -0
  265. rasa/shared/utils/mcp/utils.py +20 -0
  266. rasa/shared/utils/schemas/events.py +42 -0
  267. rasa/shared/utils/yaml.py +3 -1
  268. rasa/studio/pull/pull.py +3 -2
  269. rasa/studio/train.py +8 -7
  270. rasa/studio/upload.py +3 -6
  271. rasa/telemetry.py +69 -5
  272. rasa/tracing/config.py +45 -12
  273. rasa/tracing/constants.py +14 -0
  274. rasa/tracing/instrumentation/attribute_extractors.py +142 -9
  275. rasa/tracing/instrumentation/instrumentation.py +626 -21
  276. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
  277. rasa/tracing/instrumentation/metrics.py +32 -0
  278. rasa/tracing/metric_instrument_provider.py +68 -0
  279. rasa/utils/common.py +92 -1
  280. rasa/utils/endpoints.py +11 -2
  281. rasa/utils/log_utils.py +96 -5
  282. rasa/utils/ml_utils.py +1 -1
  283. rasa/utils/tensorflow/__init__.py +7 -0
  284. rasa/utils/tensorflow/callback.py +136 -101
  285. rasa/utils/tensorflow/crf.py +1 -1
  286. rasa/utils/tensorflow/data_generator.py +21 -8
  287. rasa/utils/tensorflow/layers.py +21 -11
  288. rasa/utils/tensorflow/metrics.py +7 -3
  289. rasa/utils/tensorflow/models.py +56 -8
  290. rasa/utils/tensorflow/rasa_layers.py +8 -6
  291. rasa/utils/tensorflow/transformer.py +2 -3
  292. rasa/utils/train_utils.py +54 -24
  293. rasa/validator.py +5 -5
  294. rasa/version.py +1 -1
  295. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/METADATA +47 -41
  296. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/RECORD +299 -238
  297. rasa/builder/scrape_rasa_docs.py +0 -97
  298. rasa/core/channels/inspector/dist/assets/channel-8e08bed9.js +0 -1
  299. rasa/core/channels/inspector/dist/assets/clone-78c82dea.js +0 -1
  300. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b08f601.js +0 -1
  301. rasa/core/channels/inspector/dist/assets/index-c941dcb3.js +0 -1336
  302. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/NOTICE +0 -0
  303. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/WHEEL +0 -0
  304. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/entry_points.txt +0 -0
@@ -1,31 +1,13 @@
1
- import asyncio
2
- from functools import lru_cache
3
- from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
1
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Type
4
2
 
5
3
  import structlog
6
4
 
7
- from rasa.builder.config import (
8
- ASSISTANT_HISTORY_GUARDRAIL_PROJECT_ID,
9
- COPILOT_HISTORY_GUARDRAIL_PROJECT_ID,
10
- )
11
- from rasa.builder.copilot.constants import ROLE_COPILOT, ROLE_USER
12
- from rasa.builder.copilot.copilot_response_handler import CopilotResponseHandler
13
- from rasa.builder.copilot.models import (
14
- CopilotChatMessage,
15
- CopilotContext,
16
- GeneratedContent,
17
- ResponseCategory,
18
- )
5
+ from rasa.builder.guardrails.clients import GuardrailsClient, LakeraAIGuardrails
19
6
  from rasa.builder.guardrails.models import (
20
- GuardrailRequestKey,
21
- GuardrailResponse,
7
+ GuardrailRequest,
22
8
  LakeraGuardrailRequest,
23
9
  )
24
- from rasa.builder.llm_service import llm_service
25
- from rasa.builder.shared.tracker_context import (
26
- AssistantConversationTurn,
27
- TrackerContext,
28
- )
10
+ from rasa.shared.constants import ROLE_USER
29
11
 
30
12
  if TYPE_CHECKING:
31
13
  from rasa.builder.guardrails.models import GuardrailType
@@ -64,265 +46,49 @@ def map_lakera_detector_type_to_guardrail_type(
64
46
  return GuardrailType.OTHER
65
47
 
66
48
 
67
- @lru_cache(maxsize=512)
68
- def _schedule_guardrails_check(
49
+ def create_guardrail_request(
50
+ client_type: Type[GuardrailsClient],
69
51
  user_text: str,
70
52
  hello_rasa_user_id: str,
71
53
  hello_rasa_project_id: str,
72
- lakera_project_id: str,
73
- ) -> "asyncio.Task[GuardrailResponse]":
74
- """Return a cached asyncio.Task that resolves to Lakera's response.
75
-
76
- Args:
77
- user_text: The user message text to check for policy violations.
78
- hello_rasa_user_id: The user ID for the conversation.
79
- hello_rasa_project_id: The project ID for the conversation.
80
- lakera_project_id: The Lakera project ID to use for this check.
81
-
82
- Returns:
83
- An asyncio Task that resolves to a GuardrailResponse.
84
- """
85
- structlogger.debug("guardrails.cache_miss", text=user_text)
86
-
87
- loop = asyncio.get_running_loop()
88
- request = LakeraGuardrailRequest(
89
- lakera_project_id=lakera_project_id,
90
- hello_rasa_user_id=hello_rasa_user_id,
91
- hello_rasa_project_id=hello_rasa_project_id,
92
- messages=[{"role": ROLE_USER, "content": user_text}],
93
- )
94
-
95
- return loop.create_task(llm_service.guardrails.send_request(request))
96
-
97
-
98
- async def _detect_flagged_user_indices(
99
- items: List[Tuple[int, str]],
100
- *,
101
- hello_rasa_user_id: Optional[str],
102
- hello_rasa_project_id: Optional[str],
103
- lakera_project_id: str,
104
- log_prefix: str,
105
- ) -> Set[int]:
106
- """Run guardrail checks for provided (index, user_text) pairs.
107
-
108
- Args:
109
- items: List of tuples containing (index, user_text) to check.
110
- hello_rasa_user_id: The user ID for the conversation.
111
- hello_rasa_project_id: The project ID for the conversation.
112
- lakera_project_id: The Lakera project ID to use for this check.
113
- log_prefix: Prefix for logging messages.
114
-
115
- Returns:
116
- A set of indices that were flagged by the guardrails.
117
- """
118
- if not items:
119
- return set()
120
-
121
- # 1) Group indices by logical request key (hashable by value)
122
- indices_by_key: Dict[GuardrailRequestKey, List[int]] = {}
123
- for idx, text in items:
124
- key = GuardrailRequestKey(
125
- user_text=(text or "").strip(),
126
- hello_rasa_user_id=hello_rasa_user_id or "",
127
- hello_rasa_project_id=hello_rasa_project_id or "",
128
- lakera_project_id=lakera_project_id,
54
+ **kwargs: Any,
55
+ ) -> GuardrailRequest:
56
+ """Create a guardrail request."""
57
+
58
+ def _create_lakera_guardrail_request(
59
+ user_text: str,
60
+ hello_rasa_user_id: str,
61
+ hello_rasa_project_id: str,
62
+ **kwargs: Any,
63
+ ) -> LakeraGuardrailRequest:
64
+ """Create a Lakera guardrail request."""
65
+ return LakeraGuardrailRequest(
66
+ hello_rasa_user_id=hello_rasa_user_id,
67
+ hello_rasa_project_id=hello_rasa_project_id,
68
+ messages=[{"role": ROLE_USER, "content": user_text}],
69
+ **kwargs,
129
70
  )
130
- if not key.user_text:
131
- continue
132
- indices_by_key.setdefault(key, []).append(idx)
133
71
 
134
- if not indices_by_key:
135
- return set()
72
+ map_client_to_request: Dict[
73
+ Type[GuardrailsClient], Callable[..., GuardrailRequest]
74
+ ] = {
75
+ LakeraAIGuardrails: _create_lakera_guardrail_request,
76
+ }
136
77
 
137
- # 2) Create one task per logical key
138
- tasks_by_key: Dict[GuardrailRequestKey, asyncio.Task[GuardrailResponse]] = {}
139
- for key in indices_by_key:
140
- tasks_by_key[key] = _schedule_guardrails_check(
141
- user_text=key.user_text,
142
- hello_rasa_user_id=key.hello_rasa_user_id,
143
- hello_rasa_project_id=key.hello_rasa_project_id,
144
- lakera_project_id=key.lakera_project_id,
78
+ if client_type in map_client_to_request:
79
+ return map_client_to_request[client_type](
80
+ user_text,
81
+ hello_rasa_user_id,
82
+ hello_rasa_project_id,
83
+ **kwargs,
145
84
  )
146
-
147
- # 3) Await unique tasks once
148
- keys = list(tasks_by_key.keys())
149
- tasks = [tasks_by_key[k] for k in keys]
150
- responses = await asyncio.gather(*tasks, return_exceptions=True)
151
-
152
- # 4) Map results back to all corresponding indices
153
- flagged: Set[int] = set()
154
- for key, response in zip(keys, responses):
155
- if isinstance(response, Exception):
156
- structlogger.warning(f"{log_prefix}.request_failed", error=str(response))
157
- continue
158
- if response.flagged:
159
- flagged.update(indices_by_key.get(key, []))
160
-
161
- return flagged
162
-
163
-
164
- async def check_assistant_chat_for_policy_violations(
165
- tracker_context: TrackerContext,
166
- hello_rasa_user_id: Optional[str],
167
- hello_rasa_project_id: Optional[str],
168
- ) -> TrackerContext:
169
- """Return a sanitised TrackerContext with unsafe turns removed.
170
-
171
- Only user messages are moderated – assistant messages are assumed safe.
172
- LRU cache is used, so each unique user text is checked once.
173
-
174
- Args:
175
- tracker_context: The TrackerContext containing conversation turns.
176
- hello_rasa_user_id: The user ID for the conversation.
177
- hello_rasa_project_id: The project ID for the conversation.
178
-
179
- Returns:
180
- TrackerContext with unsafe turns removed.
181
- """
182
- # Collect (turn_index, user_text) for all turns with a user message
183
- items: List[Tuple[int, str]] = []
184
- for idx, turn in enumerate(tracker_context.conversation_turns):
185
- user_message = turn.user_message
186
- if not user_message:
187
- continue
188
-
189
- text = (user_message.text or "").strip()
190
- if not text:
191
- continue
192
-
193
- items.append((idx, text))
194
-
195
- flagged_turns = await _detect_flagged_user_indices(
196
- items,
197
- hello_rasa_user_id=hello_rasa_user_id,
198
- hello_rasa_project_id=hello_rasa_project_id,
199
- lakera_project_id=ASSISTANT_HISTORY_GUARDRAIL_PROJECT_ID,
200
- log_prefix="assistant_guardrails",
201
- )
202
-
203
- if not flagged_turns:
204
- return tracker_context
205
-
206
- structlogger.info(
207
- "assistant_guardrails.turns_flagged",
208
- count=len(flagged_turns),
209
- turn_indices=sorted(flagged_turns),
210
- )
211
-
212
- # Build a filtered TrackerContext
213
- safe_turns: List[AssistantConversationTurn] = [
214
- turn
215
- for idx, turn in enumerate(tracker_context.conversation_turns)
216
- if idx not in flagged_turns
217
- ]
218
-
219
- new_tracker_context = tracker_context.copy(deep=True)
220
- new_tracker_context.conversation_turns = safe_turns
221
- return new_tracker_context
222
-
223
-
224
- def _annotate_flagged_user_messages(
225
- history: List[CopilotChatMessage], flagged_user_indices: Set[int]
226
- ) -> None:
227
- """Mark flagged user messages in-place on the original history.
228
-
229
- Args:
230
- history: The copilot chat history containing messages.
231
- flagged_user_indices: Set of indices of user messages that were flagged.
232
- """
233
- if not flagged_user_indices:
234
- return
235
-
236
- total = len(history)
237
- for uidx in flagged_user_indices:
238
- if 0 <= uidx < total and history[uidx].role == ROLE_USER:
239
- history[
240
- uidx
241
- ].response_category = ResponseCategory.GUARDRAILS_POLICY_VIOLATION
242
-
243
-
244
- async def check_copilot_chat_for_policy_violations(
245
- context: CopilotContext,
246
- hello_rasa_user_id: Optional[str],
247
- hello_rasa_project_id: Optional[str],
248
- ) -> Optional[GeneratedContent]:
249
- """Check the copilot chat history for guardrail policy violations.
250
-
251
- Only user messages are moderated – assistant messages are assumed safe.
252
- LRU cache is used, so each unique user text is checked once.
253
-
254
- Args:
255
- context: The CopilotContext containing the copilot chat history.
256
- hello_rasa_user_id: The user ID for the conversation.
257
- hello_rasa_project_id: The project ID for the conversation.
258
-
259
- Returns:
260
- Returns a default violation response if the system flags any user message,
261
- otherwise return None.
262
- """
263
- history = context.copilot_chat_history
264
-
265
- # Collect (index, text) for user messages; skip ones already marked as violations
266
- items: List[Tuple[int, str]] = []
267
- for idx, message in enumerate(history):
268
- if message.response_category == ResponseCategory.GUARDRAILS_POLICY_VIOLATION:
269
- continue
270
- if message.role != ROLE_USER:
271
- continue
272
- formatted_message = message.to_openai_format()
273
- text = (formatted_message.get("content") or "").strip()
274
- if not text:
275
- continue
276
- items.append((idx, text))
277
-
278
- flagged_user_indices = await _detect_flagged_user_indices(
279
- items,
280
- hello_rasa_user_id=hello_rasa_user_id,
281
- hello_rasa_project_id=hello_rasa_project_id,
282
- lakera_project_id=COPILOT_HISTORY_GUARDRAIL_PROJECT_ID,
283
- log_prefix="copilot_guardrails",
284
- )
285
-
286
- _annotate_flagged_user_messages(history, flagged_user_indices)
287
-
288
- if not flagged_user_indices:
289
- return None
290
-
291
- # Identify the latest user message index in the current request
292
- last_user_idx: Optional[int] = None
293
- for i in range(len(history) - 1, -1, -1):
294
- if getattr(history[i], "role", None) == ROLE_USER:
295
- last_user_idx = i
296
- break
297
-
298
- # Remove flagged user messages and their next copilot messages
299
- indices_to_remove: Set[int] = set()
300
- total = len(history)
301
- for uidx in flagged_user_indices:
302
- indices_to_remove.add(uidx)
303
- next_idx = uidx + 1
304
- if (
305
- next_idx < total
306
- and getattr(history[next_idx], "role", None) == ROLE_COPILOT
307
- ):
308
- indices_to_remove.add(next_idx)
309
-
310
- # Apply sanitization
311
- filtered_history = [
312
- msg for i, msg in enumerate(history) if i not in indices_to_remove
313
- ]
314
- if len(filtered_history) != len(history):
315
- structlogger.info(
316
- "copilot_guardrails.history_sanitized",
317
- removed_indices=sorted(indices_to_remove),
318
- removed_messages=len(history) - len(filtered_history),
319
- kept_messages=len(filtered_history),
85
+ else:
86
+ message = f"Unsupported guardrail client: {type(client_type)}"
87
+ structlogger.error(
88
+ "guardrails_policy_checker"
89
+ ".create_guardrail_request"
90
+ ".unsupported_guardrail_client",
91
+ message=message,
92
+ guardrail_client=client_type,
320
93
  )
321
- context.copilot_chat_history = filtered_history
322
-
323
- # Block only if the latest user message in this request was flagged
324
- if last_user_idx is not None and last_user_idx in flagged_user_indices:
325
- return CopilotResponseHandler.respond_to_guardrail_policy_violations()
326
-
327
- # Otherwise proceed (following messages are respected)
328
- return None
94
+ raise ValueError(message)
rasa/builder/jobs.py CHANGED
@@ -1,9 +1,17 @@
1
- from typing import Any, Optional
1
+ from typing import Any, Dict, Optional
2
2
 
3
3
  import structlog
4
4
  from sanic import Sanic
5
5
 
6
6
  from rasa.builder import config
7
+ from rasa.builder.copilot.models import (
8
+ CopilotContext,
9
+ FileContent,
10
+ InternalCopilotRequestChatMessage,
11
+ LogContent,
12
+ ResponseCategory,
13
+ TrainingErrorLog,
14
+ )
7
15
  from rasa.builder.exceptions import (
8
16
  LLMGenerationError,
9
17
  ProjectGenerationError,
@@ -11,6 +19,7 @@ from rasa.builder.exceptions import (
11
19
  ValidationError,
12
20
  )
13
21
  from rasa.builder.job_manager import JobInfo, job_manager
22
+ from rasa.builder.llm_service import llm_service
14
23
  from rasa.builder.models import (
15
24
  JobStatus,
16
25
  JobStatusEvent,
@@ -28,9 +37,14 @@ structlogger = structlog.get_logger()
28
37
 
29
38
 
30
39
  async def push_job_status_event(
31
- job: JobInfo, status: JobStatus, message: Optional[str] = None
40
+ job: JobInfo,
41
+ status: JobStatus,
42
+ message: Optional[str] = None,
43
+ payload: Optional[Dict[str, Any]] = None,
32
44
  ) -> None:
33
- event = JobStatusEvent.from_status(status=status.value, message=message)
45
+ event = JobStatusEvent.from_status(
46
+ status=status.value, message=message, payload=payload
47
+ )
34
48
  job.status = status.value
35
49
  await job.put(event)
36
50
 
@@ -210,7 +224,7 @@ async def run_template_to_bot_job(
210
224
  async def run_replace_all_files_job(
211
225
  app: "Sanic",
212
226
  job: JobInfo,
213
- bot_files: dict,
227
+ bot_files: Dict[str, Any],
214
228
  ) -> None:
215
229
  """Run the replace-all-files job in the background.
216
230
 
@@ -228,7 +242,7 @@ async def run_replace_all_files_job(
228
242
  try:
229
243
  project_generator.replace_all_bot_files(bot_files)
230
244
 
231
- # 1. Validating
245
+ # Validating
232
246
  await push_job_status_event(job, JobStatus.validating)
233
247
  training_input = project_generator.get_training_input()
234
248
  validation_error = await validate_project(training_input.importer)
@@ -236,12 +250,13 @@ async def run_replace_all_files_job(
236
250
  raise ValidationError(validation_error)
237
251
  await push_job_status_event(job, JobStatus.validation_success)
238
252
 
239
- # 2. Training
253
+ # Training
240
254
  await push_job_status_event(job, JobStatus.training)
241
255
  agent = await train_and_load_agent(training_input)
242
256
  update_agent(agent, app)
243
257
  await push_job_status_event(job, JobStatus.train_success)
244
258
 
259
+ # Send final done event
245
260
  await push_job_status_event(job, JobStatus.done)
246
261
  job_manager.mark_done(job)
247
262
 
@@ -257,26 +272,181 @@ async def run_replace_all_files_job(
257
272
  included_log_levels=log_levels,
258
273
  )
259
274
  error_message = exc.get_error_message_with_logs(log_levels=log_levels)
260
- await push_job_status_event(
261
- job, JobStatus.validation_error, message=error_message
275
+ # Push error event and start copilot analysis job
276
+ await push_error_and_start_copilot_analysis(
277
+ app,
278
+ job,
279
+ JobStatus.validation_error,
280
+ error_message,
281
+ bot_files,
262
282
  )
283
+
284
+ # After error mark job as done
263
285
  job_manager.mark_done(job, error=error_message)
264
286
 
265
287
  except TrainingError as exc:
288
+ error_message = str(exc)
266
289
  structlogger.debug(
267
290
  "replace_all_files_job.train_error",
268
291
  job_id=job.id,
269
- error=str(exc),
292
+ error=error_message,
270
293
  )
271
- await push_job_status_event(job, JobStatus.train_error, message=str(exc))
272
- job_manager.mark_done(job, error=str(exc))
294
+ # Push error event and start copilot analysis job
295
+ await push_error_and_start_copilot_analysis(
296
+ app,
297
+ job,
298
+ JobStatus.train_error,
299
+ error_message,
300
+ bot_files,
301
+ )
302
+
303
+ # After error mark job as done
304
+ job_manager.mark_done(job, error=error_message)
273
305
 
274
306
  except Exception as exc:
275
307
  # Capture full traceback for anything truly unexpected
308
+ error_message = str(exc)
276
309
  structlogger.exception(
277
310
  "replace_all_files_job.unexpected_error",
278
311
  job_id=job.id,
312
+ error=error_message,
313
+ )
314
+
315
+ # Push error event and start copilot analysis job
316
+ await push_error_and_start_copilot_analysis(
317
+ app,
318
+ job,
319
+ JobStatus.error,
320
+ error_message,
321
+ bot_files,
322
+ )
323
+
324
+ # After error mark job as done
325
+ job_manager.mark_done(job, error=str(exc))
326
+
327
+
328
+ async def push_error_and_start_copilot_analysis(
329
+ app: "Sanic",
330
+ original_job: JobInfo,
331
+ original_job_status: JobStatus,
332
+ error_message: str,
333
+ bot_files: Dict[str, Any],
334
+ ) -> None:
335
+ """Start a copilot analysis job and notify the client.
336
+
337
+ Creates a copilot analysis job and sends the new job ID to the client. The new
338
+ job runs in the background.
339
+
340
+ Args:
341
+ app: The Sanic application instance
342
+ original_job: The original job that failed
343
+ original_job_status: The status of the job that failed
344
+ error_message: The error message to analyze
345
+ bot_files: The bot files to include in analysis
346
+ """
347
+ # Create a copilot analysis job. Send the new job ID to the client and
348
+ # run the Copilot Analysis job in the background.
349
+ message = "Failed to train the assistant. Starting copilot analysis."
350
+
351
+ copilot_job = job_manager.create_job()
352
+ # Push the error status event for the original job
353
+ await push_job_status_event(
354
+ original_job,
355
+ original_job_status,
356
+ message=message,
357
+ payload={"copilot_job_id": copilot_job.id},
358
+ )
359
+ # Run the copilot analysis job in the background
360
+ app.add_task(
361
+ run_copilot_training_error_analysis_job(
362
+ app, copilot_job, error_message, bot_files
363
+ )
364
+ )
365
+ structlogger.debug(
366
+ f"update_files_job.{original_job_status.value}.copilot_analysis_start",
367
+ event_info=message,
368
+ job_id=original_job.id,
369
+ error=error_message,
370
+ copilot_job_id=copilot_job.id,
371
+ )
372
+
373
+
374
+ async def run_copilot_training_error_analysis_job(
375
+ app: "Sanic",
376
+ job: JobInfo,
377
+ training_error_message: str,
378
+ bot_files: Dict[str, Any],
379
+ ) -> None:
380
+ """Run copilot training error analysis job."""
381
+ await push_job_status_event(job, JobStatus.received)
382
+
383
+ try:
384
+ # Create message content blocks with log content and available files
385
+ log_content_block = LogContent(
386
+ type="log", content=training_error_message, context="training_error"
387
+ )
388
+ file_content_blocks = [
389
+ FileContent(type="file", file_path=file_path, file_content=file_content)
390
+ for file_path, file_content in bot_files.items()
391
+ ]
392
+ context = CopilotContext(
393
+ tracker_context=None, # No conversation context needed
394
+ copilot_chat_history=[
395
+ InternalCopilotRequestChatMessage(
396
+ role="internal_copilot_request",
397
+ content=[log_content_block, *file_content_blocks],
398
+ response_category=ResponseCategory.TRAINING_ERROR_LOG_ANALYSIS,
399
+ )
400
+ ],
401
+ )
402
+
403
+ # Generate copilot response
404
+ copilot_client = llm_service.instantiate_copilot()
405
+ (
406
+ original_stream,
407
+ generation_context,
408
+ ) = await copilot_client.generate_response(context)
409
+
410
+ copilot_response_handler = llm_service.instantiate_handler(
411
+ config.COPILOT_HANDLER_ROLLING_BUFFER_SIZE
412
+ )
413
+ intercepted_stream = copilot_response_handler.handle_response(original_stream)
414
+
415
+ # Stream the copilot response as job events
416
+ async for token in intercepted_stream:
417
+ # Send each token as a job event using the same format as /copilot endpoint
418
+ await push_job_status_event(
419
+ job, JobStatus.copilot_analyzing, payload=token.sse_data
420
+ )
421
+
422
+ # Send references (if any) as part of copilot_analyzing stream
423
+ if generation_context.relevant_documents:
424
+ reference_section = copilot_response_handler.extract_references(
425
+ generation_context.relevant_documents
426
+ )
427
+ await push_job_status_event(
428
+ job, JobStatus.copilot_analyzing, payload=reference_section.sse_data
429
+ )
430
+
431
+ # Send original error log as part of copilot_analyzing stream
432
+ training_error_log = TrainingErrorLog(logs=[log_content_block])
433
+ await push_job_status_event(
434
+ job, JobStatus.copilot_analyzing, payload=training_error_log.sse_data
435
+ )
436
+
437
+ # Send success status
438
+ await push_job_status_event(job, JobStatus.copilot_analysis_success)
439
+
440
+ await push_job_status_event(job, JobStatus.done)
441
+ job_manager.mark_done(job)
442
+
443
+ except Exception as exc:
444
+ structlogger.exception(
445
+ "copilot_training_error_analysis_job.error",
446
+ job_id=job.id,
279
447
  error=str(exc),
280
448
  )
281
- await push_job_status_event(job, JobStatus.error, message=str(exc))
449
+ await push_job_status_event(
450
+ job, JobStatus.copilot_analysis_error, message=str(exc)
451
+ )
282
452
  job_manager.mark_done(job, error=str(exc))
@@ -19,7 +19,11 @@ from rasa.builder.copilot.copilot_templated_message_provider import (
19
19
  load_copilot_internal_message_templates,
20
20
  )
21
21
  from rasa.builder.exceptions import LLMGenerationError
22
- from rasa.builder.guardrails.lakera import LakeraAIGuardrails
22
+ from rasa.builder.guardrails.clients import (
23
+ GuardrailsClient,
24
+ LakeraAIGuardrails,
25
+ )
26
+ from rasa.builder.guardrails.policy_checker import GuardrailsPolicyChecker
23
27
  from rasa.constants import PACKAGE_NAME
24
28
  from rasa.shared.constants import DOMAIN_SCHEMA_FILE, RESPONSES_SCHEMA_FILE
25
29
  from rasa.shared.core.flows.yaml_flows_io import FLOWS_SCHEMA_FILE
@@ -37,7 +41,8 @@ class LLMService:
37
41
  self._domain_schema: Optional[Dict[str, Any]] = None
38
42
  self._flows_schema: Optional[Dict[str, Any]] = None
39
43
  self._copilot: Optional[Copilot] = None
40
- self._guardrails: Optional[LakeraAIGuardrails] = None
44
+ self._guardrails: Optional[GuardrailsClient] = None
45
+ self._guardrails_policy_checker: Optional[GuardrailsPolicyChecker] = None
41
46
  self._copilot_response_handler: Optional[CopilotResponseHandler] = None
42
47
  self._copilot_internal_message_templates: Optional[Dict[str, str]] = None
43
48
 
@@ -77,11 +82,14 @@ class LLMService:
77
82
  raise
78
83
 
79
84
  @property
80
- def guardrails(self) -> LakeraAIGuardrails:
85
+ def guardrails(self) -> Optional[GuardrailsClient]:
81
86
  """Get or lazy create guardrails instance."""
82
- if self._guardrails is None:
83
- self._guardrails = LakeraAIGuardrails()
87
+ if not config.ENABLE_GUARDRAILS:
88
+ return None
89
+ # TODO: Replace with Open Source guardrails implementation once it's ready
84
90
  try:
91
+ if self._guardrails is None:
92
+ self._guardrails = LakeraAIGuardrails()
85
93
  return self._guardrails
86
94
  except Exception as e:
87
95
  structlogger.error(
@@ -91,6 +99,25 @@ class LLMService:
91
99
  )
92
100
  raise
93
101
 
102
+ @property
103
+ def guardrails_policy_checker(self) -> Optional[GuardrailsPolicyChecker]:
104
+ """Get or lazy create guardrails policy checker instance."""
105
+ try:
106
+ if self._guardrails_policy_checker is None and self.guardrails is not None:
107
+ self._guardrails_policy_checker = GuardrailsPolicyChecker(
108
+ self.guardrails
109
+ )
110
+ return self._guardrails_policy_checker
111
+ except Exception as e:
112
+ structlogger.error(
113
+ "llm_service.guardrails_policy_checker.error",
114
+ event_info=(
115
+ "LLM Service: Error getting guardrails policy checker instance."
116
+ ),
117
+ error=str(e),
118
+ )
119
+ raise
120
+
94
121
  @property
95
122
  def copilot_internal_message_templates(self) -> Dict[str, str]:
96
123
  """Get or lazy load copilot internal message templates."""