Bound adapter assemblies and extraction caches
This commit is contained in:
parent
9cd7c4e424
commit
f00aa65a73
4 changed files with 232 additions and 8 deletions
|
|
@ -21,6 +21,8 @@ from .adapter_validation import (
|
|||
from .config_validation import ID_PATTERN
|
||||
from .errors import DocForgeError
|
||||
from .incremental import (
|
||||
MAX_EXTRACTION_CACHE_BYTES,
|
||||
MAX_EXTRACTION_CACHE_SOURCES,
|
||||
CachedSource,
|
||||
ExtractionCache,
|
||||
affected_sources,
|
||||
|
|
@ -40,6 +42,10 @@ from .models import (
|
|||
)
|
||||
from .telemetry import increment, stage
|
||||
|
||||
MAX_ADAPTER_EDGE_MULTIPLIER = 32
|
||||
MAX_ADAPTER_LOGIC_NODE_MULTIPLIER = 32
|
||||
MAX_ADAPTER_LOGIC_EDGE_MULTIPLIER = 64
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdapterNode:
|
||||
|
|
@ -464,6 +470,9 @@ class AdapterProject:
|
|||
canonical_sources,
|
||||
captured,
|
||||
)
|
||||
self._enforce_assembly_limits(
|
||||
AdapterAssembly(projection=projection, logic=self._last_logic)
|
||||
)
|
||||
return ProjectSnapshot(
|
||||
descriptor=self.descriptor,
|
||||
nodes=projection.core_nodes(),
|
||||
|
|
@ -628,11 +637,14 @@ class AdapterProject:
|
|||
validate_manifest(manifest)
|
||||
if manifest.identity() != self._identity:
|
||||
raise DocForgeError("adapter_changed", "Adapter identity changed during the operation")
|
||||
self._enforce_manifest_limits(manifest)
|
||||
cache = load_extraction_cache(
|
||||
self._cache_path,
|
||||
project_id=manifest.project_id,
|
||||
adapter_id=manifest.adapter_id,
|
||||
adapter_version=manifest.adapter_version,
|
||||
max_bytes=MAX_EXTRACTION_CACHE_BYTES,
|
||||
max_sources=MAX_EXTRACTION_CACHE_SOURCES,
|
||||
)
|
||||
cached = {source.source_id: source for source in cache.sources} if cache else {}
|
||||
current = {source.source_id: source for source in manifest.sources}
|
||||
|
|
@ -737,6 +749,7 @@ class AdapterProject:
|
|||
"Incremental assembly changed the manifest-bound project identity",
|
||||
)
|
||||
_validate_adapter_assembly(assembly)
|
||||
self._enforce_assembly_limits(assembly)
|
||||
stable = loader.load_manifest()
|
||||
validate_manifest(stable)
|
||||
if stable != manifest:
|
||||
|
|
@ -752,6 +765,8 @@ class AdapterProject:
|
|||
adapter_version=manifest.adapter_version,
|
||||
sources=tuple(cache_records),
|
||||
),
|
||||
max_bytes=MAX_EXTRACTION_CACHE_BYTES,
|
||||
max_sources=MAX_EXTRACTION_CACHE_SOURCES,
|
||||
)
|
||||
self._last_logic = logic_projections
|
||||
self._last_build_report = {
|
||||
|
|
@ -766,6 +781,40 @@ class AdapterProject:
|
|||
}
|
||||
return assembly
|
||||
|
||||
def _enforce_manifest_limits(self, manifest: AdapterManifest) -> None:
|
||||
if len(manifest.sources) > MAX_EXTRACTION_CACHE_SOURCES:
|
||||
raise DocForgeError(
|
||||
"adapter_limit",
|
||||
"Adapter manifest exceeds the bounded source limit",
|
||||
maximum=MAX_EXTRACTION_CACHE_SOURCES,
|
||||
actual=len(manifest.sources),
|
||||
)
|
||||
|
||||
def _enforce_assembly_limits(self, assembly: AdapterAssembly) -> None:
|
||||
maximum_nodes = self.descriptor.limits.max_nodes
|
||||
maximum_edges = maximum_nodes * MAX_ADAPTER_EDGE_MULTIPLIER
|
||||
maximum_logic_nodes = maximum_nodes * MAX_ADAPTER_LOGIC_NODE_MULTIPLIER
|
||||
maximum_logic_edges = maximum_nodes * MAX_ADAPTER_LOGIC_EDGE_MULTIPLIER
|
||||
actual_nodes = len(assembly.projection.nodes)
|
||||
actual_edges = len(assembly.projection.edges)
|
||||
actual_logic_nodes = sum(len(projection.nodes) for projection in assembly.logic)
|
||||
actual_logic_edges = sum(len(projection.edges) for projection in assembly.logic)
|
||||
limits = (
|
||||
("nodes", actual_nodes, maximum_nodes),
|
||||
("edges", actual_edges, maximum_edges),
|
||||
("logic_nodes", actual_logic_nodes, maximum_logic_nodes),
|
||||
("logic_edges", actual_logic_edges, maximum_logic_edges),
|
||||
)
|
||||
for label, actual, maximum in limits:
|
||||
if actual > maximum:
|
||||
raise DocForgeError(
|
||||
"adapter_limit",
|
||||
f"Adapter assembly exceeds the configured {label.replace('_', ' ')} limit",
|
||||
kind=label,
|
||||
maximum=maximum,
|
||||
actual=actual,
|
||||
)
|
||||
|
||||
def canonical_source_paths(self) -> tuple[Path, ...]:
|
||||
"""Adapters validate their own source sets before producing a projection."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue