Coverage for node / src / stigmem_node / recall / fuzzy_resolver.py: 100%

19 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-06-18 05:34 +0000

1"""Fuzzy entity resolver — spec §2.6 Phase 6. 

2 

33-layer resolver: 

4 Layer 1: strict normalizer (entity_normalizer.py) — deterministic case + whitespace. 

5 Layer 2: explicit alias table lookup — user-defined semantic equivalences stored in 

6 entity_aliases (e.g. user:alice ≡ user:a.smith). 

7 Layer 3: passthrough — returns the Layer 1 result when no alias is registered. 

8 

9The strict normalizer is stateless and import-time only; this module adds the 

10DB-backed Layer 2 on top and exposes helpers for alias registration / lookup. 

11 

12Ingest contract: callers MUST apply normalize_entity_uri first, then call 

13resolve_entity with the already-normalized URI and an open connection. This 

14keeps the two concerns separable and avoids a second DB round-trip on the hot 

15normalization path when no alias table exists. 

16""" 

17 

18from __future__ import annotations 

19 

20import sqlite3 

21from datetime import UTC, datetime 

22from typing import Any 

23 

24from ..utility.entity_normalizer import NormalizationError, normalize_entity_uri 

25 

26 

27def resolve_entity( 

28 conn: sqlite3.Connection, normalized_uri: str, tenant_id: str = "default" 

29) -> str: 

30 """Layer 2 alias lookup. Input MUST already be Layer 1–normalized. 

31 

32 Returns canonical_uri from the caller's tenant aliases if a registered alias 

33 exists, otherwise returns normalized_uri unchanged (Layer 3 passthrough). 

34 """ 

35 row = conn.execute( 

36 "SELECT canonical_uri FROM entity_aliases WHERE raw_uri = ? AND tenant_id = ?", 

37 (normalized_uri, tenant_id), 

38 ).fetchone() 

39 return str(row["canonical_uri"]) if row else normalized_uri 

40 

41 

42def register_alias( 

43 conn: sqlite3.Connection, 

44 raw_uri: str, 

45 canonical_uri: str, 

46 *, 

47 kind: str = "user", 

48 tenant_id: str = "default", 

49) -> dict[str, Any]: 

50 """Register or replace a semantic alias (raw_uri resolves to canonical_uri). 

51 

52 Both URIs are Layer 1–normalized before storage so the caller need not 

53 pre-normalize them. The alias is scoped to ``tenant_id``. Raises ValueError 

54 on empty input or identical endpoints. 

55 

56 Returns the stored alias record as a plain dict. 

57 """ 

58 try: 

59 norm_raw = normalize_entity_uri(raw_uri) 

60 norm_canonical = normalize_entity_uri(canonical_uri) 

61 except NormalizationError as exc: 

62 raise ValueError(str(exc)) from exc 

63 

64 if norm_raw == norm_canonical: 

65 raise ValueError(f"raw_uri and canonical_uri must differ after normalization: {norm_raw!r}") 

66 

67 now = datetime.now(UTC).isoformat() 

68 conn.execute( 

69 "INSERT OR REPLACE INTO entity_aliases" 

70 " (raw_uri, canonical_uri, kind, created_at, tenant_id) VALUES (?, ?, ?, ?, ?)", 

71 (norm_raw, norm_canonical, kind, now, tenant_id), 

72 ) 

73 return { 

74 "raw_uri": norm_raw, 

75 "canonical_uri": norm_canonical, 

76 "kind": kind, 

77 "created_at": now, 

78 }