Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import hashlib | |
| from typing import Protocol, runtime_checkable | |
| import numpy as np | |
| class Embedder(Protocol): | |
| dim: int | |
| def encode(self, texts: list[str]) -> np.ndarray: # (n, dim), L2-normalized float32 | |
| ... | |
| class FakeEmbedder: | |
| """Deterministic, dependency-free embedder for tests.""" | |
| def __init__(self, dim: int = 32): | |
| self.dim = dim | |
| def encode(self, texts: list[str]) -> np.ndarray: | |
| out = np.zeros((len(texts), self.dim), dtype="float32") | |
| for i, t in enumerate(texts): | |
| digest = hashlib.sha256(t.encode("utf-8")).digest() | |
| reps = (self.dim // len(digest)) + 1 | |
| raw = np.frombuffer((digest * reps)[: self.dim], dtype=np.uint8).astype("float32") | |
| v = raw - raw.mean() | |
| norm = float(np.linalg.norm(v)) | |
| out[i] = v / norm if norm > 0 else v | |
| return out | |
| class BGEM3Embedder: | |
| """Real embedder (BAAI/bge-m3). Lazy import so tests need no model download. | |
| Used both for offline index-building and for query embedding on the Space | |
| (weights are downloaded from the Hub on first use). An int8-ONNX build to | |
| speed cold starts is a documented follow-up, not yet implemented. | |
| """ | |
| def __init__(self, model_name: str = "BAAI/bge-m3"): | |
| from FlagEmbedding import BGEM3FlagModel | |
| import torch | |
| # fp16 only on GPU — fp16 matmul isn't implemented for CPU inference, which | |
| # is what CI runners and the free Space use; fp32 is correct (just more RAM). | |
| self._model = BGEM3FlagModel(model_name, use_fp16=torch.cuda.is_available()) | |
| self.dim = 1024 | |
| def encode(self, texts: list[str]) -> np.ndarray: | |
| dense = self._model.encode(texts, return_dense=True)["dense_vecs"] | |
| v = np.asarray(dense, dtype="float32") | |
| norms = np.linalg.norm(v, axis=1, keepdims=True) | |
| norms[norms == 0] = 1.0 | |
| return v / norms | |