Spaces:
Build error
Build error
from __future__ import annotations | |
from typing import overload | |
from pydantic import BaseModel | |
from openhands.core.logger import openhands_logger as logger | |
from openhands.events.action.agent import CondensationAction | |
from openhands.events.event import Event | |
from openhands.events.observation.agent import AgentCondensationObservation | |
class View(BaseModel): | |
"""Linearly ordered view of events. | |
Produced by a condenser to indicate the included events are ready to process as LLM input. | |
""" | |
events: list[Event] | |
def __len__(self) -> int: | |
return len(self.events) | |
def __iter__(self): | |
return iter(self.events) | |
# To preserve list-like indexing, we ideally support slicing and position-based indexing. | |
# The only challenge with that is switching the return type based on the input type -- we | |
# can mark the different signatures for MyPy with `@overload` decorators. | |
def __getitem__(self, key: slice) -> list[Event]: ... | |
def __getitem__(self, key: int) -> Event: ... | |
def __getitem__(self, key: int | slice) -> Event | list[Event]: | |
if isinstance(key, slice): | |
start, stop, step = key.indices(len(self)) | |
return [self[i] for i in range(start, stop, step)] | |
elif isinstance(key, int): | |
return self.events[key] | |
else: | |
raise ValueError(f'Invalid key type: {type(key)}') | |
def from_events(events: list[Event]) -> View: | |
"""Create a view from a list of events, respecting the semantics of any condensation events.""" | |
forgotten_event_ids: set[int] = set() | |
for event in events: | |
if isinstance(event, CondensationAction): | |
forgotten_event_ids.update(event.forgotten) | |
# Make sure we also forget the condensation action itself | |
forgotten_event_ids.add(event.id) | |
kept_events = [event for event in events if event.id not in forgotten_event_ids] | |
# If we have a summary, insert it at the specified offset. | |
summary: str | None = None | |
summary_offset: int | None = None | |
# The relevant summary is always in the last condensation event (i.e., the most recent one). | |
for event in reversed(events): | |
if isinstance(event, CondensationAction): | |
if event.summary is not None and event.summary_offset is not None: | |
summary = event.summary | |
summary_offset = event.summary_offset | |
break | |
if summary is not None and summary_offset is not None: | |
logger.info(f'Inserting summary at offset {summary_offset}') | |
kept_events.insert( | |
summary_offset, AgentCondensationObservation(content=summary) | |
) | |
return View(events=kept_events) | |