rasa-pro 3.11.9__py3-none-any.whl → 3.11.11__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.

@@ -6,7 +6,18 @@ import uuid
6
6
  from collections import defaultdict
7
7
  from dataclasses import asdict
8
8
  from datetime import datetime, timedelta, timezone
9
- from typing import Any, Awaitable, Callable, Dict, List, Optional, Set, Text, Union
9
+ from typing import (
10
+ Any,
11
+ Awaitable,
12
+ Callable,
13
+ Dict,
14
+ List,
15
+ Optional,
16
+ Set,
17
+ Text,
18
+ Union,
19
+ Tuple,
20
+ )
10
21
 
11
22
  import structlog
12
23
  from jsonschema import ValidationError, validate
@@ -48,8 +59,8 @@ def map_call_params(parameters: Dict[Text, Any]) -> CallParameters:
48
59
  """Map the Audiocodes parameters to the CallParameters dataclass."""
49
60
  return CallParameters(
50
61
  call_id=parameters.get("vaigConversationId"),
51
- user_phone=parameters.get("callee"),
52
- bot_phone=parameters.get("caller"),
62
+ user_phone=parameters.get("caller"),
63
+ bot_phone=parameters.get("callee"),
53
64
  user_name=parameters.get("callerDisplayName"),
54
65
  user_host=parameters.get("callerHost"),
55
66
  bot_host=parameters.get("calleeHost"),
@@ -76,35 +87,45 @@ class Conversation:
76
87
 
77
88
  @staticmethod
78
89
  def get_metadata(activity: Dict[Text, Any]) -> Optional[Dict[Text, Any]]:
79
- """Get metadata from the activity."""
80
- return asdict(map_call_params(activity["parameters"]))
90
+ """Get metadata from the activity.
91
+
92
+ ONLY used for activities NOT for events (see _handle_event)."""
93
+ return activity.get("parameters")
81
94
 
82
95
  @staticmethod
83
- def _handle_event(event: Dict[Text, Any]) -> Text:
84
- """Handle start and DTMF event and return the corresponding text."""
96
+ def _handle_event(event: Dict[Text, Any]) -> Tuple[Text, Dict[Text, Any]]:
97
+ """Handle events and return a tuple of text and metadata.
98
+
99
+ Args:
100
+ event: The event to handle.
101
+
102
+ Returns:
103
+ Tuple of text and metadata.
104
+ text is either /session_start or /vaig_event_<event_name>
105
+ metadata is a dictionary with the event parameters.
106
+ """
85
107
  structlogger.debug("audiocodes.handle.event", event_payload=event)
86
108
  if "name" not in event:
87
109
  structlogger.warning(
88
110
  "audiocodes.handle.event.no_name_key", event_payload=event
89
111
  )
90
- return ""
112
+ return "", {}
91
113
 
92
114
  if event["name"] == EVENT_START:
93
115
  text = f"{INTENT_MESSAGE_PREFIX}{USER_INTENT_SESSION_START}"
116
+ metadata = asdict(map_call_params(event.get("parameters", {})))
94
117
  elif event["name"] == EVENT_DTMF:
95
118
  text = f"{INTENT_MESSAGE_PREFIX}vaig_event_DTMF"
96
- event_params = {"value": event["value"]}
97
- text += json.dumps(event_params)
119
+ metadata = {"value": event["value"]}
98
120
  else:
99
121
  # handle other events described by Audiocodes
100
122
  # https://techdocs.audiocodes.com/voice-ai-connect/#VAIG_Combined/inactivity-detection.htm?TocPath=Bot%2520integration%257CReceiving%2520notifications%257C_____3
101
123
  text = f"{INTENT_MESSAGE_PREFIX}vaig_event_{event['name']}"
102
- event_params = {**event.get("parameters", {})}
124
+ metadata = {**event.get("parameters", {})}
103
125
  if "value" in event:
104
- event_params["value"] = event["value"]
105
- text += json.dumps(event_params)
126
+ metadata["value"] = event["value"]
106
127
 
107
- return text
128
+ return text, metadata
108
129
 
109
130
  def is_active_conversation(self, now: datetime, delta: timedelta) -> bool:
110
131
  """Check if the conversation is active."""
@@ -139,21 +160,29 @@ class Conversation:
139
160
  structlogger.warning(
140
161
  "audiocodes.handle.activities.duplicate_activity",
141
162
  activity_id=activity[ACTIVITY_ID_KEY],
163
+ event_info=(
164
+ "Audiocodes might send duplicate activities if the bot has not "
165
+ "responded to the previous one or responded too late. Please "
166
+ "consider enabling the `use_websocket` option to use"
167
+ " Audiocodes Asynchronous API."
168
+ ),
142
169
  )
143
170
  continue
144
171
  self.activity_ids.append(activity[ACTIVITY_ID_KEY])
145
172
  if activity["type"] == ACTIVITY_MESSAGE:
146
173
  text = activity["text"]
174
+ metadata = self.get_metadata(activity)
147
175
  elif activity["type"] == ACTIVITY_EVENT:
148
- text = self._handle_event(activity)
176
+ text, metadata = self._handle_event(activity)
149
177
  else:
150
178
  structlogger.warning(
151
179
  "audiocodes.handle.activities.unknown_activity_type",
152
180
  activity=activity,
153
181
  )
182
+ continue
183
+
154
184
  if not text:
155
185
  continue
156
- metadata = self.get_metadata(activity)
157
186
  user_msg = UserMessage(
158
187
  text=text,
159
188
  input_channel=input_channel_name,
@@ -390,30 +419,41 @@ class AudiocodesInput(InputChannel):
390
419
  "audiocodes.on_activities.no_conversation", request=request.json
391
420
  )
392
421
  return response.json({})
393
- elif conversation.ws:
422
+
423
+ if self.use_websocket:
424
+ # send an empty response for this request
425
+ # activities are processed in the background
426
+ # chat response is sent via the websocket
394
427
  ac_output: Union[WebsocketOutput, AudiocodesOutput] = WebsocketOutput(
395
428
  conversation.ws, conversation_id
396
429
  )
397
- response_json = {}
398
- else:
399
- # handle non websocket case where messages get returned in json
400
- ac_output = AudiocodesOutput()
401
- response_json = {
430
+ self._create_task(
431
+ conversation_id,
432
+ conversation.handle_activities(
433
+ request.json,
434
+ input_channel_name=self.name(),
435
+ output_channel=ac_output,
436
+ on_new_message=on_new_message,
437
+ ),
438
+ )
439
+ return response.json({})
440
+
441
+ # without websockets, this becomes a blocking call
442
+ # and the response is sent back to the Audiocodes server
443
+ # after the activities are processed
444
+ ac_output = AudiocodesOutput()
445
+ await conversation.handle_activities(
446
+ request.json,
447
+ input_channel_name=self.name(),
448
+ output_channel=ac_output,
449
+ on_new_message=on_new_message,
450
+ )
451
+ return response.json(
452
+ {
402
453
  "conversation": conversation_id,
403
454
  "activities": ac_output.messages,
404
455
  }
405
-
406
- # start a background task to handle activities
407
- self._create_task(
408
- conversation_id,
409
- conversation.handle_activities(
410
- request.json,
411
- input_channel_name=self.name(),
412
- output_channel=ac_output,
413
- on_new_message=on_new_message,
414
- ),
415
456
  )
416
- return response.json(response_json)
417
457
 
418
458
  @ac_webhook.route(
419
459
  "/conversation/<conversation_id>/disconnect", methods=["POST"]
@@ -22,10 +22,11 @@ from rasa.shared.core.flows.flow_step_links import (
22
22
  from rasa.shared.core.flows.flow_step_sequence import FlowStepSequence
23
23
  from rasa.shared.core.flows.nlu_trigger import NLUTriggers
24
24
  from rasa.shared.core.flows.steps import (
25
+ ActionFlowStep,
26
+ CallFlowStep,
25
27
  CollectInformationFlowStep,
26
28
  EndFlowStep,
27
29
  StartFlowStep,
28
- ActionFlowStep,
29
30
  )
30
31
  from rasa.shared.core.flows.steps.constants import (
31
32
  CONTINUE_STEP_PREFIX,
@@ -402,161 +403,156 @@ class Flow:
402
403
  and a set of visited step IDs to prevent revisiting steps.
403
404
  It calls `go_over_steps` to recursively explore and fill the paths list.
404
405
  """
405
- flow_paths_list = FlowPathsList(self.id, paths=[])
406
- steps: List[FlowStep] = self.steps
406
+ all_paths = FlowPathsList(self.id, paths=[])
407
+ start_step: FlowStep = self.first_step_in_flow()
407
408
  current_path: FlowPath = FlowPath(flow=self.id, nodes=[])
408
- step_ids_visited: Set[str] = set()
409
-
410
- self._go_over_steps(steps, current_path, flow_paths_list, step_ids_visited)
409
+ visited_step_ids: Set[str] = set()
411
410
 
412
- if not flow_paths_list.is_path_part_of_list(current_path):
413
- flow_paths_list.paths.append(copy.deepcopy(current_path))
411
+ self._go_over_steps(start_step, current_path, all_paths, visited_step_ids)
414
412
 
415
413
  structlogger.debug(
416
414
  "shared.core.flows.flow.extract_all_paths",
417
415
  comment="Extraction complete",
418
- number_of_paths=len(flow_paths_list.paths),
416
+ number_of_paths=len(all_paths.paths),
419
417
  flow_name=self.name,
420
418
  )
421
- return flow_paths_list
419
+ return all_paths
422
420
 
423
421
  def _go_over_steps(
424
422
  self,
425
- steps_to_go: Union[str, List[FlowStep]],
423
+ current_step: FlowStep,
426
424
  current_path: FlowPath,
427
- completed_paths: FlowPathsList,
428
- step_ids_visited: Set[str],
425
+ all_paths: FlowPathsList,
426
+ visited_step_ids: Set[str],
429
427
  ) -> None:
430
428
  """Processes the flow steps recursively.
431
429
 
432
- Either following direct step IDs or handling conditions, and adds complete
433
- paths to the collected_paths.
434
-
435
430
  Args:
436
- steps_to_go: Either a direct step ID or a list of steps to process.
431
+ current_step: The current step being processed.
437
432
  current_path: The current path being constructed.
438
- completed_paths: The list where completed paths are added.
439
- step_ids_visited: A set of step IDs that have been visited to avoid cycles.
433
+ all_paths: The list where completed paths are added.
434
+ visited_step_ids: A set of steps that have been visited to avoid cycles.
440
435
 
441
436
  Returns:
442
- None: This function modifies collected_paths in place by appending new paths
437
+ None: This function modifies all_paths in place by appending new paths
443
438
  as they are found.
444
439
  """
445
- # Case 1: If the steps_to_go is a custom_id string
446
- # This happens when a "next" of, for example, a IfFlowStepLink is targeting
447
- # a specific step by id
448
- if isinstance(steps_to_go, str):
449
- for i, step in enumerate(self.steps):
450
- # We don't need to check for 'id' as a link can only happen to a
451
- # custom id.
452
- if step.custom_id == steps_to_go:
453
- self._go_over_steps(
454
- self.steps[i:], current_path, completed_paths, step_ids_visited
455
- )
456
-
457
- # Case 2: If steps_to_go is a list of steps
458
- else:
459
- for i, step in enumerate(steps_to_go):
460
- # 1. Check if the step is relevant for testable_paths extraction.
461
- # We only create new path nodes for ActionFlowStep and
462
- # CollectInformationFlowStep because these are externally visible
463
- # changes in the assistant's behaviour (trackable in the e2e tests).
464
- # For other flow steps, we only follow their links.
465
- # We decided to ignore calls to other flows in our coverage analysis.
466
- if not isinstance(step, (CollectInformationFlowStep, ActionFlowStep)):
467
- self._handle_links(
468
- step.next.links,
469
- current_path,
470
- completed_paths,
471
- step_ids_visited,
472
- )
473
- continue
474
-
475
- # 2. Check if already visited this custom step id
476
- # in order to keep track of loops
477
- if step.custom_id is not None and step.custom_id in step_ids_visited:
478
- if not completed_paths.is_path_part_of_list(current_path):
479
- completed_paths.paths.append(copy.deepcopy(current_path))
480
- return # Stop traversing this path if we've revisited a step
481
- elif step.custom_id is not None:
482
- step_ids_visited.add(step.custom_id)
483
-
484
- # 3. Append step info to the path
485
- current_path.nodes.append(
486
- PathNode(
487
- flow=current_path.flow,
488
- step_id=step.id,
489
- lines=step.metadata["line_numbers"],
490
- )
440
+ # Check if the step is relevant for testable_paths extraction.
441
+ # We only create new path nodes for ActionFlowStep, CallFlowStep and
442
+ # CollectInformationFlowStep because these are externally visible
443
+ # changes in the assistant's behaviour (trackable in the e2e tests).
444
+ # For other flow steps, we only follow their links.
445
+ # We decided to ignore calls to other flows in our coverage analysis.
446
+ should_add_node = isinstance(
447
+ current_step, (CollectInformationFlowStep, ActionFlowStep, CallFlowStep)
448
+ )
449
+ if should_add_node:
450
+ # Add current step to the current path that is being constructed.
451
+ current_path.nodes.append(
452
+ PathNode(
453
+ flow=current_path.flow,
454
+ step_id=current_step.id,
455
+ lines=current_step.metadata["line_numbers"],
491
456
  )
457
+ )
492
458
 
493
- # 4. Check if 'END' branch
494
- if (
495
- len(step.next.links) == 1
496
- and isinstance(step.next.links[0], StaticFlowStepLink)
497
- and step.next.links[0].target == END_STEP
498
- ):
499
- if not completed_paths.is_path_part_of_list(current_path):
500
- completed_paths.paths.append(copy.deepcopy(current_path))
501
- return
502
- else:
503
- self._handle_links(
504
- step.next.links,
505
- current_path,
506
- completed_paths,
507
- step_ids_visited,
508
- )
459
+ if current_step.id in visited_step_ids or self.is_end_of_path(current_step):
460
+ # Found a cycle, or reached an end step, do not proceed further.
461
+ all_paths.paths.append(copy.deepcopy(current_path))
462
+ # Remove the last node from the path if it was added.
463
+ if should_add_node:
464
+ current_path.nodes.pop()
465
+ return
466
+
467
+ # Mark current step as visited in this path.
468
+ visited_step_ids.add(current_step.id)
469
+
470
+ # Iterate over all links of the current step.
471
+ for link in current_step.next.links:
472
+ self._handle_link(
473
+ current_path,
474
+ all_paths,
475
+ visited_step_ids,
476
+ link,
477
+ )
509
478
 
510
- def _handle_links(
479
+ # Backtrack the current step and remove it from the path.
480
+ visited_step_ids.remove(current_step.id)
481
+
482
+ # Remove the last node from the path if it was added.
483
+ if should_add_node:
484
+ current_path.nodes.pop()
485
+
486
+ def _handle_link(
511
487
  self,
512
- links: List[FlowStepLink],
513
- path: FlowPath,
514
- collected_paths: FlowPathsList,
515
- step_ids_visited: set,
488
+ current_path: FlowPath,
489
+ all_paths: FlowPathsList,
490
+ visited_step_ids: Set[str],
491
+ link: FlowStepLink,
516
492
  ) -> None:
517
- """Processes the next step in a flow.
518
-
519
- Potentially recursively calling itself to handle conditional paths and
520
- branching.
493
+ """Handles the next step in a flow.
521
494
 
522
495
  Args:
523
- links: Links listed in the "next" attribute.
524
- path: The current path taken in the flow.
525
- collected_paths: A list of paths collected so far.
526
- step_ids_visited: A set of step IDs that have already been visited
527
- to avoid loops.
496
+ current_path: The current path being constructed.
497
+ all_paths: The list where completed paths are added.
498
+ visited_step_ids: A set of steps that have been visited to avoid cycles.
499
+ link: The link to be followed.
528
500
 
529
501
  Returns:
530
- None: Modifies collected_paths in place by appending new paths
531
- as they are completed.
502
+ None: This function modifies all_paths in place by appending new paths
503
+ as they are found.
532
504
  """
533
- steps = self.steps
534
-
535
- for link in links:
536
- # Direct step id reference
537
- if isinstance(link, StaticFlowStepLink):
538
- # Find this id in the flow steps and restart from there
539
- for i, step in enumerate(steps):
540
- if step.id == link.target_step_id:
541
- self._go_over_steps(
542
- steps[i:],
543
- copy.deepcopy(path),
544
- collected_paths,
545
- copy.deepcopy(step_ids_visited),
546
- )
547
-
548
- # If conditions
549
- elif isinstance(link, (IfFlowStepLink, ElseFlowStepLink)):
550
- # Handling conditional paths
551
- target_steps: Union[str, List[FlowStep]]
552
- if isinstance(link.target_reference, FlowStepSequence):
553
- target_steps = link.target_reference.child_steps
554
- else:
555
- target_steps = link.target_reference
556
-
505
+ # StaticFlowStepLink is a direct link to the next step.
506
+ if isinstance(link, StaticFlowStepLink):
507
+ # Find the step by its id and continue the path.
508
+ if step := self._get_step_by_step_id(link.target_step_id):
557
509
  self._go_over_steps(
558
- target_steps,
559
- copy.deepcopy(path),
560
- collected_paths,
561
- copy.deepcopy(step_ids_visited),
510
+ step,
511
+ current_path,
512
+ all_paths,
513
+ visited_step_ids,
562
514
  )
515
+ return
516
+ # IfFlowStepLink and ElseFlowStepLink are conditional links.
517
+ elif isinstance(link, (IfFlowStepLink, ElseFlowStepLink)):
518
+ if isinstance(link.target_reference, FlowStepSequence):
519
+ # If the target is a FlowStepSequence, we need to go over all
520
+ # child steps of the sequence.
521
+ for child_step in link.target_reference.child_steps:
522
+ self._go_over_steps(
523
+ child_step,
524
+ current_path,
525
+ all_paths,
526
+ visited_step_ids,
527
+ )
528
+ return
529
+ else:
530
+ # Find the step by its id and continue the path.
531
+ if step := self._get_step_by_step_id(link.target_reference):
532
+ self._go_over_steps(
533
+ step,
534
+ current_path,
535
+ all_paths,
536
+ visited_step_ids,
537
+ )
538
+ return
539
+
540
+ def is_end_of_path(self, step: FlowStep) -> bool:
541
+ """Check if there is no path available from the current step."""
542
+ if (
543
+ len(step.next.links) == 1
544
+ and isinstance(step.next.links[0], StaticFlowStepLink)
545
+ and step.next.links[0].target == END_STEP
546
+ ):
547
+ return True
548
+ return False
549
+
550
+ def _get_step_by_step_id(
551
+ self,
552
+ step_id: Optional[str],
553
+ ) -> Optional[FlowStep]:
554
+ """Get a step by its id from a list of steps."""
555
+ for step in self.steps:
556
+ if step.id == step_id:
557
+ return step
558
+ return None
rasa/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # this file will automatically be changed,
2
2
  # do not add anything but the version number here!
3
- __version__ = "3.11.9"
3
+ __version__ = "3.11.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rasa-pro
3
- Version: 3.11.9
3
+ Version: 3.11.11
4
4
  Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
5
5
  Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
6
6
  Author: Rasa Technologies GmbH
@@ -265,7 +265,7 @@ rasa/core/channels/telegram.py,sha256=5BrNECFM3qe9XjNpDb8Q9fbqCT5aKr5L6IH21W8sum
265
265
  rasa/core/channels/twilio.py,sha256=GsdjfplZdBj0fRB60bSggPF1DXFZ_x18V_dlcDy5VFs,5943
266
266
  rasa/core/channels/vier_cvg.py,sha256=PfvSluQqgJbP0JzZPFUvum3z7H55JPPeobcD-z5zCkw,13544
267
267
  rasa/core/channels/voice_ready/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
268
- rasa/core/channels/voice_ready/audiocodes.py,sha256=esX2BFYB8XIQB56ixERebJiVzEaKGM0EuXUJB92b9aY,21066
268
+ rasa/core/channels/voice_ready/audiocodes.py,sha256=T1Tx2z4HvDhNiehsRynQ9xaJkgP9wbpILCzI_SYyD78,22289
269
269
  rasa/core/channels/voice_ready/jambonz.py,sha256=S7yjdj7nYwQWMalqcGv8eomZGj0c2Dza-51tiNHEpVM,4784
270
270
  rasa/core/channels/voice_ready/jambonz_protocol.py,sha256=OzW-6YMU2SIc88Mur_wktbiE46igHR8vH17DCfRijIU,13139
271
271
  rasa/core/channels/voice_ready/twilio_voice.py,sha256=z2pdausxQnXQP9htGh8AL2q9AvcMIx70Y5tErWpssV4,16224
@@ -594,7 +594,7 @@ rasa/shared/core/conversation.py,sha256=tw1fD2XB3gOdQjDI8hHo5TAAmE2JYNogQGWe3rE9
594
594
  rasa/shared/core/domain.py,sha256=bGjonMV54wbwGLPjKHI2NoWwxr2wyUZwhEvjBWhP-W0,81710
595
595
  rasa/shared/core/events.py,sha256=zdGSP1bNV1RyKC9Z54S7EbQ8TfGne_n9XKj64aoghdI,85803
596
596
  rasa/shared/core/flows/__init__.py,sha256=HszhIvEARpmyxABFc1MKYvj8oy04WiZW1xmCdToakbs,181
597
- rasa/shared/core/flows/flow.py,sha256=n9vB1SKwRczlymxrY19KiWq2BXR-LKpVUr5-Zh9827s,21530
597
+ rasa/shared/core/flows/flow.py,sha256=6RR-CdOR6xS31zCiOe3Fcz-ajCQ-H0M7FxfMa63Ka7A,20820
598
598
  rasa/shared/core/flows/flow_path.py,sha256=xstwahZBU5cfMY46mREA4NoOGlKLBRAqeP_mJ3UZqOI,2283
599
599
  rasa/shared/core/flows/flow_step.py,sha256=6hoVOMXryTKHgT7-p7jzTqH2r9QREmy_6d6bX2OyxI0,4550
600
600
  rasa/shared/core/flows/flow_step_links.py,sha256=zMZV_9rWVjEa8kHIFSIbXCWA1qaUvp8r4uSCK_NsKKs,10548
@@ -779,9 +779,9 @@ rasa/utils/train_utils.py,sha256=f1NWpp5y6al0dzoQyyio4hc4Nf73DRoRSHDzEK6-C4E,212
779
779
  rasa/utils/url_tools.py,sha256=JQcHL2aLqLHu82k7_d9imUoETCm2bmlHaDpOJ-dKqBc,1218
780
780
  rasa/utils/yaml.py,sha256=KjbZq5C94ZP7Jdsw8bYYF7HASI6K4-C_kdHfrnPLpSI,2000
781
781
  rasa/validator.py,sha256=O1wjCeV7ITJ0luvb3GCWy8x1fGgzWVbClEMlPnLBowQ,67265
782
- rasa/version.py,sha256=C7MfeYYWfkktTlYVsyB5Oh2KUgU5K7MVUJ-qSvLwV5c,117
783
- rasa_pro-3.11.9.dist-info/METADATA,sha256=la5nbBfiiEqS02_8oKgOXJbTsxfoUPBPX9kdeWbCwQM,10728
784
- rasa_pro-3.11.9.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
785
- rasa_pro-3.11.9.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
786
- rasa_pro-3.11.9.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
787
- rasa_pro-3.11.9.dist-info/RECORD,,
782
+ rasa/version.py,sha256=_ZBgTfcNiibsOC-yzALLSC5ZIpzax2a9zTS3lVn3J3s,118
783
+ rasa_pro-3.11.11.dist-info/METADATA,sha256=rZj6MixMKB8347G-ug6_VB0ArvBxd2HI-s0_EHHrSMU,10729
784
+ rasa_pro-3.11.11.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
785
+ rasa_pro-3.11.11.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
786
+ rasa_pro-3.11.11.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
787
+ rasa_pro-3.11.11.dist-info/RECORD,,