Files
SIA/sia/working_memory.py

140 lines
4.4 KiB
Python

from typing import List, Optional, Callable, Set
import xml.etree.ElementTree as ET
from .entry import Entry
class WorkingMemory:
"""
Manages a collection of entries that represent the current state of the system.
The working memory stores different types of entries (scripts, reasoning, errors, etc.)
and provides methods to add, remove, and update them. It also generates XML context
representing the current state.
Notifies observers of entry additions, deletions and changes. During update(),
changes are batched and a single notification is sent after completion.
"""
def __init__(self):
"""Initialize an empty working memory."""
self._entries: List[Entry] = []
self._change_handlers: List[Callable[[], None]] = []
self._updating = False
self._changed_during_update: Set[Entry] = set()
def add_change_handler(self, handler: Callable[[], None]) -> None:
"""Add a callback for working memory changes."""
if handler not in self._change_handlers:
self._change_handlers.append(handler)
def _notify_change(self) -> None:
"""Notify all handlers of working memory changes."""
for handler in self._change_handlers:
handler()
def _handle_entry_change(self, entry: Entry) -> None:
"""Handle changes from individual entries."""
if self._updating:
self._changed_during_update.add(entry)
else:
self._notify_change()
def add_entry(self, entry: Entry) -> None:
"""
Add a new entry to working memory.
Args:
entry: Entry object to add
"""
if not isinstance(entry, Entry):
raise TypeError("Entry must be an instance of Entry class")
entry.add_change_handler(self._handle_entry_change)
self._entries.append(entry)
self._notify_change()
def remove_entry(self, id: str) -> None:
"""
Remove an entry from working memory by its ID.
Ensures cleanup is performed before removal.
Args:
id: Unique identifier of entry to remove
"""
entry = self.get_entry(id)
if entry is not None:
entry.cleanup()
self._entries = [e for e in self._entries if e.id != id]
self._notify_change()
def clear(self) -> None:
"""
Remove all entries from working memory.
Ensures cleanup is performed on all entries.
"""
for entry in self._entries:
entry.cleanup()
self._entries.clear()
self._notify_change()
def __del__(self):
"""Clean up all entries when memory is deleted."""
if hasattr(self, '_entries'):
self.clear()
def get_entry(self, id: str) -> Optional[Entry]:
"""
Get an entry by its ID.
Args:
id: Unique identifier of entry to retrieve
Returns:
Entry if found, None otherwise
"""
for entry in self._entries:
if entry.id == id:
return entry
return None
def get_entries(self) -> List[Entry]:
"""
Get all entries in working memory.
Returns:
List[Entry]: List of all entries
"""
return self._entries.copy()
def get_entries_count(self) -> int:
"""
Get the total number of entries.
Returns:
int: Number of entries in working memory
"""
return len(self._entries)
def update(self) -> None:
"""
Update all entries in working memory.
Batches change notifications and sends single update after completion.
"""
self._updating = True
self._changed_during_update.clear()
for entry in self._entries:
entry.update()
self._updating = False
if self._changed_during_update:
self._notify_change()
def generate_context(self) -> List[ET.Element]:
"""
Generate XML Elements representing all entries.
Returns:
List[ET.Element]: List of XML elements for each entry
"""
return [entry.generate_context() for entry in self._entries]