CoolFace
Apppublic

Backup-bdg/OpenHands

sourceHugging Faceapache-2.0updated 1y agoView on Hugging Face
0likes
event_store.py176 linesDownload Raw Back to events
1import json2from dataclasses import dataclass3from typing import Iterable4 5from openhands.core.logger import openhands_logger as logger6from openhands.events.event import Event, EventSource7from openhands.events.event_filter import EventFilter8from openhands.events.event_store_abc import EventStoreABC9from openhands.events.serialization.event import event_from_dict10from openhands.storage.files import FileStore11from openhands.storage.locations import (12    get_conversation_dir,13    get_conversation_event_filename,14    get_conversation_events_dir,15)16from openhands.utils.shutdown_listener import should_continue17 18 19@dataclass(frozen=True)20class _CachePage:21    events: list[dict] | None22    start: int23    end: int24 25    def covers(self, global_index: int) -> bool:26        if global_index < self.start:27            return False28        if global_index >= self.end:29            return False30        return True31 32    def get_event(self, global_index: int) -> Event | None:33        # If there was not actually a cached page, return None34        if not self.events:35            return None36        local_index = global_index - self.start37        return event_from_dict(self.events[local_index])38 39 40_DUMMY_PAGE = _CachePage(None, 1, -1)41 42 43@dataclass44class EventStore(EventStoreABC):45    """46    A stored list of events backing a conversation47    """48 49    sid: str50    file_store: FileStore51    user_id: str | None52    cur_id: int = -1  # We fix this in post init if it is not specified53    cache_size: int = 2554 55    def __post_init__(self) -> None:56        if self.cur_id >= 0:57            return58        events = []59        try:60            events_dir = get_conversation_events_dir(self.sid, self.user_id)61            events = self.file_store.list(events_dir)62        except FileNotFoundError:63            logger.debug(f'No events found for session {self.sid} at {events_dir}')64 65        if not events:66            self.cur_id = 067            return68 69        # if we have events, we need to find the highest id to prepare for new events70        for event_str in events:71            id = self._get_id_from_filename(event_str)72            if id >= self.cur_id:73                self.cur_id = id + 174 75    def search_events(76        self,77        start_id: int = 0,78        end_id: int | None = None,79        reverse: bool = False,80        filter: EventFilter | None = None,81        limit: int | None = None,82    ) -> Iterable[Event]:83        """84        Retrieve events from the event stream, optionally filtering out events of a given type85        and events marked as hidden.86 87        Args:88            start_id: The ID of the first event to retrieve. Defaults to 0.89            end_id: The ID of the last event to retrieve. Defaults to the last event in the stream.90            reverse: Whether to retrieve events in reverse order. Defaults to False.91            filter: EventFilter to use92 93        Yields:94            Events from the stream that match the criteria.95        """96 97        if end_id is None:98            end_id = self.cur_id99        else:100            end_id += 1  # From inclusive to exclusive101 102        if reverse:103            step = -1104            start_id, end_id = end_id, start_id105            start_id -= 1106            end_id -= 1107        else:108            step = 1109 110        cache_page = _DUMMY_PAGE111        num_results = 0112        for index in range(start_id, end_id, step):113            if not should_continue():114                return115            if not cache_page.covers(index):116                cache_page = self._load_cache_page_for_index(index)117            event = cache_page.get_event(index)118            if event is None:119                try:120                    event = self.get_event(index)121                except FileNotFoundError:122                    event = None123            if event:124                if not filter or filter.include(event):125                    yield event126                    num_results += 1127                    if limit and limit <= num_results:128                        return129 130    def get_event(self, id: int) -> Event:131        filename = self._get_filename_for_id(id, self.user_id)132        content = self.file_store.read(filename)133        data = json.loads(content)134        return event_from_dict(data)135 136    def get_latest_event(self) -> Event:137        return self.get_event(self.cur_id - 1)138 139    def get_latest_event_id(self) -> int:140        return self.cur_id - 1141 142    def filtered_events_by_source(self, source: EventSource) -> Iterable[Event]:143        for event in self.get_events():144            if event.source == source:145                yield event146 147    def _get_filename_for_id(self, id: int, user_id: str | None) -> str:148        return get_conversation_event_filename(self.sid, id, user_id)149 150    def _get_filename_for_cache(self, start: int, end: int) -> str:151        return f'{get_conversation_dir(self.sid, self.user_id)}event_cache/{start}-{end}.json'152 153    def _load_cache_page(self, start: int, end: int) -> _CachePage:154        """Read a page from the cache. Reading individual events is slow when there are a lot of them, so we use pages."""155        cache_filename = self._get_filename_for_cache(start, end)156        try:157            content = self.file_store.read(cache_filename)158            events = json.loads(content)159        except FileNotFoundError:160            events = None161        page = _CachePage(events, start, end)162        return page163 164    def _load_cache_page_for_index(self, index: int) -> _CachePage:165        offset = index % self.cache_size166        index -= offset167        return self._load_cache_page(index, index + self.cache_size)168 169    @staticmethod170    def _get_id_from_filename(filename: str) -> int:171        try:172            return int(filename.split('/')[-1].split('.')[0])173        except ValueError:174            logger.warning(f'get id from filename ({filename}) failed.')175            return -1176