"""Procedural memory: reusable skills/workflows for AGI.""" from typing import Any from fusionagi.schemas.skill import Skill from fusionagi._logger import logger class ProceduralMemory: """ Skill store: reusable workflows and procedures. Invokable by name; write/update rules enforced by caller. """ def __init__(self, max_skills: int = 5000) -> None: self._skills: dict[str, Skill] = {} self._by_name: dict[str, str] = {} # name -> skill_id (latest) self._max_skills = max_skills def add_skill(self, skill: Skill) -> None: """Register a skill (overwrites same skill_id).""" if len(self._skills) >= self._max_skills and skill.skill_id not in self._skills: self._evict_one() self._skills[skill.skill_id] = skill self._by_name[skill.name] = skill.skill_id logger.debug("Procedural memory: skill added", extra={"skill_id": skill.skill_id, "name": skill.name}) def get_skill(self, skill_id: str) -> Skill | None: """Return skill by id or None.""" return self._skills.get(skill_id) def get_skill_by_name(self, name: str) -> Skill | None: """Return latest skill with this name or None.""" sid = self._by_name.get(name) return self._skills.get(sid) if sid else None def list_skills(self, limit: int = 200) -> list[Skill]: """Return skills (e.g. for planner).""" return list(self._skills.values())[-limit:] def remove_skill(self, skill_id: str) -> bool: """Remove skill. Returns True if existed.""" if skill_id not in self._skills: return False name = self._skills[skill_id].name del self._skills[skill_id] if self._by_name.get(name) == skill_id: del self._by_name[name] return True def _evict_one(self) -> None: if not self._skills: return rid = next(iter(self._skills)) self.remove_skill(rid)