"""ASV benchmarks for skill add persistence (issue #620). Measures the performance of: - Adding a skill with DB persistence - Listing skills after DB-backed add - Removing a skill with DB persistence """ from __future__ import annotations import importlib import os import sys import tempfile from pathlib import Path from typing import Any # Ensure the local *source* tree is importable even when ASV has an # older build of the package installed. _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) import cleveragents # noqa: E402 importlib.reload(cleveragents) from sqlalchemy import create_engine, event # noqa: E402 from sqlalchemy.orm import Session, sessionmaker # noqa: E402 from cleveragents.application.services.skill_service import ( # noqa: E402 SkillService, ) from cleveragents.infrastructure.database.models import Base # noqa: E402 from cleveragents.infrastructure.database.repositories import ( # noqa: E402 SkillRepository, ) from cleveragents.skills.schema import SkillConfigSchema # noqa: E402 _VALID_YAML = """\ name: local/bench-persist description: "Benchmark persistent skill" tools: - name: builtin/read_file - name: builtin/write_file """ def _enable_fk(dbapi_conn: Any, _rec: Any) -> None: cursor = dbapi_conn.cursor() cursor.execute("PRAGMA foreign_keys=ON") cursor.close() def _make_service(factory: Any) -> SkillService: shared = factory() def _sf() -> Session: return shared repo = SkillRepository(session_factory=_sf) return SkillService(skill_repo=repo, session_factory=_sf) class SkillAddPersistSuite: """Benchmark skill add with DB persistence.""" timeout = 60.0 def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:", echo=False) event.listen(self.engine, "connect", _enable_fk) Base.metadata.create_all(self.engine) self.factory: sessionmaker[Session] = sessionmaker( bind=self.engine, ) fd, self._path = tempfile.mkstemp(suffix=".yaml") with os.fdopen(fd, "w") as fh: fh.write(_VALID_YAML) def teardown(self) -> None: Path(self._path).unlink(missing_ok=True) self.engine.dispose() def time_add_persist(self) -> None: """Benchmark add with persistence.""" svc = _make_service(self.factory) config = SkillConfigSchema.from_yaml_file(self._path) svc.add_skill(config, config_path=self._path) class SkillListAfterPersistSuite: """Benchmark list after persistent adds.""" timeout = 60.0 def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:", echo=False) event.listen(self.engine, "connect", _enable_fk) Base.metadata.create_all(self.engine) self.factory: sessionmaker[Session] = sessionmaker( bind=self.engine, ) svc = _make_service(self.factory) for i in range(20): yaml_text = ( f"name: local/bench-list-{i}\n" f'description: "Bench {i}"\n' "tools:\n" " - name: builtin/read_file\n" ) fd, path = tempfile.mkstemp(suffix=".yaml") with os.fdopen(fd, "w") as fh: fh.write(yaml_text) config = SkillConfigSchema.from_yaml_file(path) svc.add_skill(config, config_path=path) Path(path).unlink(missing_ok=True) def teardown(self) -> None: self.engine.dispose() def time_list_persisted_skills(self) -> None: """Benchmark listing skills from a fresh service.""" svc = _make_service(self.factory) svc.list_skills()