025d379946
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / quality (pull_request) Successful in 17s
CI / build (pull_request) Successful in 15s
CI / typecheck (pull_request) Successful in 33s
CI / security (pull_request) Successful in 42s
CI / unit_tests (pull_request) Successful in 2m36s
CI / docker (pull_request) Successful in 40s
CI / integration_tests (pull_request) Successful in 3m17s
CI / coverage (pull_request) Successful in 4m21s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 30s
CI / typecheck (push) Successful in 33s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m36s
CI / docker (push) Successful in 40s
CI / integration_tests (push) Successful in 3m57s
CI / coverage (push) Successful in 5m22s
CI / benchmark-publish (push) Has been cancelled
CI / benchmark-regression (pull_request) Successful in 25m12s
Implemented the Backend Abstraction Layer (BAL) for the Advanced Context Management System, following the specification in docs/specification.md Section ACMS > Backend Abstraction Layer and ADR-014. Key additions: - TextBackend protocol with search(query, scope, max_results) returning list[TextResult], and TextResult frozen dataclass (uko_uri, content, score, metadata fields) - VectorBackend protocol with similarity_search(embedding, scope, top_k) returning list[VectorResult], and VectorResult frozen dataclass - GraphBackend protocol with sparql_query(query, scope), get_triples(subject), and traverse(start, depth) methods returning GraphResult frozen dataclass (triples, metadata fields) - In-memory stub backends (InMemoryTextBackend, InMemoryVectorBackend, InMemoryGraphBackend) that validate arguments and return empty results, serving as development placeholders and test doubles - DI container registration as configurable Singletons with provider selection via override_providers() - Behave BDD feature (35 scenarios / 83 steps) covering protocol compliance, argument validation, result immutability, and DI resolution - Robot Framework smoke tests (6 tests) for integration verification - ASV benchmarks for stub query overhead and instantiation time - Reference documentation at docs/reference/acms_backends.md Design decisions: - Used @runtime_checkable Protocol for structural subtyping, consistent with existing ResourceHandler pattern - Used frozen dataclasses (not Pydantic) for result types to minimize overhead in the hot path of context assembly - scope parameter typed as frozenset[str] for immutability and hashability - Stubs registered as default Singletons; production backends swap via DI ISSUES CLOSED: #498
148 lines
4.4 KiB
Python
148 lines
4.4 KiB
Python
"""ASV benchmarks for ACMS Backend Abstraction Layer.
|
|
|
|
Measures the performance of:
|
|
- InMemoryTextBackend.search() stub query overhead
|
|
- InMemoryVectorBackend.similarity_search() stub query overhead
|
|
- InMemoryGraphBackend.sparql_query() / get_triples() / traverse() overhead
|
|
- Backend instantiation time
|
|
- Result dataclass construction overhead
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# 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)
|
|
|
|
# Force-reload so ASV picks up the source tree version.
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from cleveragents.domain.models.acms.backends import ( # noqa: E402
|
|
GraphResult,
|
|
TextResult,
|
|
VectorResult,
|
|
)
|
|
from cleveragents.domain.models.acms.stubs import ( # noqa: E402
|
|
InMemoryGraphBackend,
|
|
InMemoryTextBackend,
|
|
InMemoryVectorBackend,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Backend instantiation benchmarks
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class BackendInstantiationSuite:
|
|
"""Benchmark backend object creation overhead."""
|
|
|
|
timeout = 60
|
|
|
|
def time_create_text_backend(self) -> None:
|
|
InMemoryTextBackend()
|
|
|
|
def time_create_vector_backend(self) -> None:
|
|
InMemoryVectorBackend()
|
|
|
|
def time_create_graph_backend(self) -> None:
|
|
InMemoryGraphBackend()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stub query overhead benchmarks
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TextBackendQuerySuite:
|
|
"""Benchmark InMemoryTextBackend.search() stub overhead."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.backend = InMemoryTextBackend()
|
|
self.scope: frozenset[str] = frozenset({"RES01", "RES02"})
|
|
|
|
def time_search_simple(self) -> None:
|
|
self.backend.search("auth flow", scope=self.scope)
|
|
|
|
def time_search_with_max_results(self) -> None:
|
|
self.backend.search("auth flow", scope=self.scope, max_results=5)
|
|
|
|
def time_search_large_scope(self) -> None:
|
|
large_scope: frozenset[str] = frozenset(f"RES{i:04d}" for i in range(1000))
|
|
self.backend.search("query", scope=large_scope)
|
|
|
|
|
|
class VectorBackendQuerySuite:
|
|
"""Benchmark InMemoryVectorBackend.similarity_search() stub overhead."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.backend = InMemoryVectorBackend()
|
|
self.scope: frozenset[str] = frozenset({"RES01", "RES02"})
|
|
self.embedding: list[float] = [0.1] * 768
|
|
|
|
def time_similarity_search_simple(self) -> None:
|
|
self.backend.similarity_search(self.embedding, scope=self.scope)
|
|
|
|
def time_similarity_search_with_top_k(self) -> None:
|
|
self.backend.similarity_search(self.embedding, scope=self.scope, top_k=5)
|
|
|
|
|
|
class GraphBackendQuerySuite:
|
|
"""Benchmark InMemoryGraphBackend query and traversal stub overhead."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.backend = InMemoryGraphBackend()
|
|
self.scope: frozenset[str] = frozenset({"RES01"})
|
|
|
|
def time_sparql_query(self) -> None:
|
|
self.backend.sparql_query(
|
|
"SELECT ?s WHERE { ?s a uko:Container }", scope=self.scope
|
|
)
|
|
|
|
def time_get_triples(self) -> None:
|
|
self.backend.get_triples("uko-py:class/Auth")
|
|
|
|
def time_traverse(self) -> None:
|
|
self.backend.traverse("uko-py:class/Auth", depth=3)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Result dataclass construction benchmarks
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class ResultConstructionSuite:
|
|
"""Benchmark frozen dataclass construction overhead."""
|
|
|
|
timeout = 60
|
|
|
|
def time_text_result(self) -> None:
|
|
TextResult(uko_uri="uko:test", content="hello", score=0.5)
|
|
|
|
def time_vector_result(self) -> None:
|
|
VectorResult(uko_uri="uko:vec", content="embed", score=0.8)
|
|
|
|
def time_graph_result_empty(self) -> None:
|
|
GraphResult()
|
|
|
|
def time_graph_result_with_triples(self) -> None:
|
|
GraphResult(
|
|
triples=[
|
|
("uko:A", "uko:contains", "uko:B"),
|
|
("uko:B", "uko:references", "uko:C"),
|
|
]
|
|
)
|