Files
wehub-resource-sync dc6079821b
Docs Tests / Check for file changes (push) Has been cancelled
Docs Tests / Test Documentation (push) Has been cancelled
Docs Tests / Documentation Linting Checks (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.9) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.9) (push) Has been cancelled
Continuous Integration / Check for file changes (push) Has been cancelled
Continuous Integration / Wait for docs tests (push) Has been cancelled
Continuous Integration / Code Quality (push) Has been cancelled
Continuous Integration / Check for changelog (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Upload coverage reports to codeclimate (push) Has been cancelled
Continuous Integration / Run Non-Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Run Broker Integration Tests (push) Has been cancelled
Continuous Integration / Run Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Build Docker base images and setup environment (push) Has been cancelled
Continuous Integration / Build Docker (default) (push) Has been cancelled
Continuous Integration / Build Docker (full) (push) Has been cancelled
Continuous Integration / Build Docker (mitie-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-de) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-it) (push) Has been cancelled
Continuous Integration / Deploy to PyPI (push) Has been cancelled
Continuous Integration / Notify Slack & Publish Release Notes (push) Has been cancelled
Publish Documentation / Evaluate release tag (push) Has been cancelled
Publish Documentation / Prebuild Docs (push) Has been cancelled
Publish Documentation / Preview Docs (push) Has been cancelled
Publish Documentation / Check for file changes (push) Has been cancelled
Publish Documentation / Publish Docs (push) Has been cancelled
Automatic PR Merger / mergepal (push) Has been cancelled
CI Github Actions / Run Tests (push) Has been cancelled
Semgrep / Semgrep Workflow Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:47 +08:00

285 lines
10 KiB
Python

import logging
import uuid
import datetime
from typing import AsyncIterator, Text, Optional, List, Set, Dict, Any
from tqdm import tqdm
import rasa.shared.utils.cli
import rasa.shared.utils.io
from rasa.core.brokers.broker import EventBroker
from rasa.core.brokers.pika import PikaEventBroker
from rasa.core.constants import RASA_EXPORT_PROCESS_ID_HEADER_NAME
from rasa.core.tracker_store import TrackerStore
from rasa.shared.core.trackers import EventVerbosity
from rasa.exceptions import (
NoEventsToMigrateError,
NoConversationsInTrackerStoreError,
PublishingError,
)
logger = logging.getLogger(__name__)
class Exporter:
"""Manages the publishing of events in a tracker store to an event broker.
Attributes:
endpoints_path: Path to the endpoints file used to configure the event
broker and tracker store. If `None`, the default path ('endpoints.yml')
is used.
tracker_store: `TrackerStore` to export conversations from.
event_broker: `EventBroker` to export conversations to.
requested_conversation_ids: List of conversation IDs requested to be
processed.
minimum_timestamp: Minimum timestamp of events that are published.
If `None`, apply no such constraint.
maximum_timestamp: Maximum timestamp of events that are published.
If `None`, apply no such constraint.
"""
def __init__(
self,
tracker_store: TrackerStore,
event_broker: EventBroker,
endpoints_path: Text,
requested_conversation_ids: Optional[Text] = None,
minimum_timestamp: Optional[float] = None,
maximum_timestamp: Optional[float] = None,
offset_timestamps_by_seconds: Optional[int] = None,
) -> None:
self.endpoints_path = endpoints_path
self.tracker_store = tracker_store
self.event_broker = event_broker
self.requested_conversation_ids = requested_conversation_ids
self.minimum_timestamp = minimum_timestamp
self.maximum_timestamp = maximum_timestamp
self.offset_timestamps_by_seconds = offset_timestamps_by_seconds
async def publish_events(self) -> int:
"""Publish events in a tracker store using an event broker.
Exits if the publishing of events is interrupted due to an error. In that case,
the CLI command to continue the export where it was interrupted is printed.
Returns:
The number of successfully published events.
"""
self._print_offset_info()
published_events = 0
current_timestamp = None
headers = self._get_message_headers()
async for event in self._fetch_events_within_time_range():
# noinspection PyBroadException
try:
self._publish_with_message_headers(event, headers)
published_events += 1
current_timestamp = event["timestamp"]
except Exception as e:
logger.exception(e)
raise PublishingError(current_timestamp)
await self.event_broker.close()
return published_events
def _print_offset_info(self) -> None:
"""Output information about the offset applied to event timestamps."""
if self.offset_timestamps_by_seconds is None:
return
delta = datetime.timedelta(seconds=abs(self.offset_timestamps_by_seconds))
operator = "-" if self.offset_timestamps_by_seconds > 0 else ""
rasa.shared.utils.cli.print_info(
f"All event timestamps will be offset by {operator}{delta}! ⏰"
)
def _get_message_headers(self) -> Optional[Dict[Text, Text]]:
"""Generate a message header for publishing events to a `PikaEventBroker`.
Returns:
Message headers with a randomly generated uuid under the
`RASA_EXPORT_PROCESS_ID_HEADER_NAME` key if `self.event_broker` is a
`PikaEventBroker`, else `None`.
"""
if isinstance(self.event_broker, PikaEventBroker):
return {RASA_EXPORT_PROCESS_ID_HEADER_NAME: uuid.uuid4().hex}
return None
def _publish_with_message_headers(
self, original_event: Dict[Text, Any], headers: Optional[Dict[Text, Text]]
) -> None:
"""Publish `event` to a message broker with `headers`.
Args:
original_event: Serialized event to be published.
headers: Message headers to be published if `self.event_broker` is a
`PikaEventBroker`.
"""
if self.offset_timestamps_by_seconds is not None:
event = dict(original_event)
event["timestamp"] += self.offset_timestamps_by_seconds
else:
event = original_event
if isinstance(self.event_broker, PikaEventBroker):
self.event_broker.publish(event=event, headers=headers)
else:
self.event_broker.publish(event)
async def _get_conversation_ids_in_tracker(self) -> Set[Text]:
"""Fetch conversation IDs in `self.tracker_store`.
Returns:
A set of conversation IDs in `self.tracker_store`.
Raises:
`NoConversationsInTrackerStoreError` if
`conversation_ids_in_tracker_store` is empty.
"""
conversation_ids_in_tracker_store = set(await self.tracker_store.keys())
if conversation_ids_in_tracker_store:
return conversation_ids_in_tracker_store
raise NoConversationsInTrackerStoreError(
"Could not find any conversations in connected tracker store. "
"Please validate your `endpoints.yml` and make sure the defined "
"tracker store exists. Exiting."
)
def _validate_all_requested_ids_exist(
self, conversation_ids_in_tracker_store: Set[Text]
) -> None:
"""Warn user if `self.requested_conversation_ids` contains IDs not found in
`conversation_ids_in_tracker_store`.
Args:
conversation_ids_in_tracker_store: Set of conversation IDs contained in
the tracker store.
"""
missing_ids_in_tracker_store = (
set(self.requested_conversation_ids) - conversation_ids_in_tracker_store
)
if missing_ids_in_tracker_store:
rasa.shared.utils.cli.print_warning(
f"Could not find the following requested "
f"conversation IDs in connected tracker store: "
f"{', '.join(sorted(missing_ids_in_tracker_store))}"
)
async def _get_conversation_ids_to_process(self) -> Set[Text]:
"""Get conversation IDs that are good for processing.
Finds the intersection of events that are contained in the tracker store with
those events requested as a command-line argument.
Returns:
Conversation IDs that are both requested and contained in the tracker
store. If no conversation IDs are requested, all conversation IDs in the
tracker store are returned.
"""
conversation_ids_in_tracker_store = (
await self._get_conversation_ids_in_tracker()
)
if not self.requested_conversation_ids:
return conversation_ids_in_tracker_store
self._validate_all_requested_ids_exist(conversation_ids_in_tracker_store)
conversation_ids_to_process = conversation_ids_in_tracker_store & set(
self.requested_conversation_ids
)
if not conversation_ids_to_process:
raise NoEventsToMigrateError(
"Could not find an overlap between the requested "
"conversation IDs and those found in the tracker store. Exiting."
)
return conversation_ids_to_process
async def _fetch_events_within_time_range(self) -> AsyncIterator[Dict[Text, Any]]:
"""Fetch all events for `conversation_ids` within the supplied time range.
Returns:
Serialized events with added `sender_id` field.
"""
conversation_ids_to_process = await self._get_conversation_ids_to_process()
rasa.shared.utils.cli.print_info(
f"Fetching events for {len(conversation_ids_to_process)} "
f"conversation IDs:"
)
for conversation_id in tqdm(conversation_ids_to_process, "conversation IDs"):
tracker = await self.tracker_store.retrieve_full_tracker(conversation_id)
if not tracker:
logger.info(
f"Could not retrieve tracker for conversation ID "
f"'{conversation_id}'. Skipping."
)
continue
_events = tracker.current_state(EventVerbosity.ALL)["events"]
if not _events:
logger.info(
f"No events to migrate for conversation ID '{conversation_id}'."
)
continue
events = self._get_events_for_conversation_id(_events, conversation_id)
# the order of events was changed after ATO-2192
# more context: https://github.com/RasaHQ/rasa/pull/13019
# we should sort the events by timestamp to keep the order
events.sort(key=lambda x: x["timestamp"])
# the conversation IDs are needed in the event publishing
for event in events:
if (
self.minimum_timestamp is not None
and event["timestamp"] < self.minimum_timestamp
):
continue
if (
self.maximum_timestamp is not None
and event["timestamp"] >= self.maximum_timestamp
):
continue
yield event
@staticmethod
def _get_events_for_conversation_id(
events: List[Dict[Text, Any]], conversation_id: Text
) -> List[Dict[Text, Any]]:
"""Get serialised events with added `sender_id` key.
Args:
events: Events to modify.
conversation_id: Conversation ID to add to events.
Returns:
Events with added `sender_id` key.
"""
events_with_conversation_id = []
for event in events:
event["sender_id"] = conversation_id
events_with_conversation_id.append(event)
return events_with_conversation_id