feat(clients): LRU mesh cache for Open3DModel recipes (etag-validated)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal PastroandClaude Opus 4.7 committed 2026-05-13 10:05:05 +02:00
1 parent 16ffe6ac20
commit 684bd2de5e
2 files changed
+146

No files matched your search

@@ -0,0 +1,45 @@
"""In-memory LRU cache of parsed STL meshes, keyed by recipe_id + etag."""
from __future__ import annotations
from collections import OrderedDict
from dataclasses import dataclass
import numpy as np
from ..perception.stl_sampler import parse_stl_bytes
from .open3dmodel import Open3DModelClient
@dataclass(frozen=True)
class RecipeMesh:
vertices: np.ndarray # (M, 3) float64, mm in model frame
triangles: np.ndarray # (T, 3) int32
etag: str
class RecipeMeshCache:
"""LRU keyed by recipe_id. On hit, validates the etag against the client
and re-fetches if it changed. Singleton-friendly: thread-safe with a
simple lock is NOT implemented (presa-mano is single-process; if
concurrency becomes a need, wrap the OrderedDict ops in a lock)."""
def __init__(self, client: Open3DModelClient, capacity: int = 8):
self._client = client
self._capacity = max(1, int(capacity))
self._store: OrderedDict[str, RecipeMesh] = OrderedDict()
def get(self, recipe_id: str, client: Open3DModelClient | None = None
) -> RecipeMesh:
c = client or self._client
body, etag = c.get_stl_bytes(recipe_id)
cached = self._store.get(recipe_id)
if cached is not None and cached.etag == etag:
self._store.move_to_end(recipe_id)
return cached
verts, tris = parse_stl_bytes(body)
mesh = RecipeMesh(vertices=verts, triangles=tris, etag=etag)
self._store[recipe_id] = mesh
self._store.move_to_end(recipe_id)
while len(self._store) > self._capacity:
self._store.popitem(last=False)
return mesh