Code-review follow-ups on the pgvector spike: - self-similarity sanity check now compares to the seed's own self-distance (0 for cosine/L2, ~-1 for inner product <#>) instead of a hardcoded ~0, so it no longer misfires if Immich ever uses a vector_ip_ops index (correctness P3). - mark the deliberate raw-SQLite read in the probe as a spike-only exception and add an M1.5 TODO that the real pgvector reader belongs in shared/photoflow/immich and SQLite access in shared/photoflow/core (project-standards P2 x2). - document join_and_coverage's return shape; extract a _pct() helper to dedupe the coverage-percentage formatting (maintainability P3 x2). Findings doc refreshed from the latest live run (coverage now ~46%, re-run ongoing).
50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
import os
|
|
from dataclasses import dataclass
|
|
from typing import Mapping, Optional
|
|
|
|
REQUIRED = ["IMMICH_URL", "IMMICH_API_KEY"]
|
|
|
|
|
|
class ConfigError(Exception):
|
|
def __init__(self, missing: list):
|
|
self.missing = missing
|
|
super().__init__(f"Missing required environment variables: {', '.join(missing)}")
|
|
|
|
|
|
@dataclass
|
|
class Config:
|
|
immich_url: str
|
|
immich_api_key: str
|
|
anthropic_api_key: str
|
|
data_dir: str
|
|
# Postgres DSN for read-only access to Immich's pgvector embeddings.
|
|
# Optional: only the pgvector spike / M1.5 visual-similarity work needs it;
|
|
# REST creds (immich_url/api_key) stay required.
|
|
# M1.5 TODO: the actual pgvector reader belongs in shared/photoflow/immich
|
|
# (the only Immich client, per CLAUDE.md) — this field only carries the DSN.
|
|
immich_db_url: Optional[str] = None
|
|
|
|
@property
|
|
def db_path(self) -> str:
|
|
return os.path.join(self.data_dir, "trip-cluster.db")
|
|
|
|
@property
|
|
def thumbs_dir(self) -> str:
|
|
return os.path.join(self.data_dir, "thumbs")
|
|
|
|
|
|
def load_config(env: Optional[Mapping] = None) -> Config:
|
|
env = env if env is not None else os.environ
|
|
missing = [k for k in REQUIRED if not (env.get(k) or "").strip()]
|
|
if missing:
|
|
raise ConfigError(missing)
|
|
data_dir = (env.get("DATA_DIR") or "").strip() or os.path.join(os.getcwd(), "data")
|
|
immich_db_url = (env.get("IMMICH_DB_URL") or "").strip() or None
|
|
return Config(
|
|
immich_url=env["IMMICH_URL"].strip().rstrip("/"),
|
|
immich_api_key=env["IMMICH_API_KEY"].strip(),
|
|
anthropic_api_key=(env.get("ANTHROPIC_API_KEY") or "").strip(),
|
|
data_dir=data_dir,
|
|
immich_db_url=immich_db_url,
|
|
)
|