Download code/paya/text.py from yeabwang/Paya: direct link, hf CLI and curl.
- Browser
- Download file 5.13 kB
-
https://huggingface.co/yeabwang/Paya/resolve/main/code/paya/text.py
- Command line
-
hf download hf://yeabwang/Paya/code/paya/text.py
-
curl -L -o text.py https://huggingface.co/yeabwang/Paya/resolve/main/code/paya/text.py
5.13 kB
| """Load pinned BioLORD weights and cache normalized text embeddings.""" | |
| import argparse | |
| import json | |
| import os | |
| from pathlib import Path | |
| import torch | |
| from .provenance import TEXT_ID, TEXT_REVISION, TEXT_IDENTITY, VERSION, sha256 | |
| class TextEncoder: | |
| """Load frozen BioLORD on demand and cache normalized sentence vectors on CPU.""" | |
| def __init__(self, cache=None, device="cpu", model_path=None): | |
| self.device, self.model_path, self.model = device, model_path, None | |
| self.embeddings = {} | |
| if cache is not None and Path(cache).exists(): | |
| payload = torch.load(cache, map_location="cpu", weights_only=True) | |
| if payload.get("identity") != TEXT_IDENTITY: | |
| raise ValueError("Text bank does not match pinned BioLORD processing") | |
| for text, vector in payload["embeddings"].items(): | |
| if vector.shape != (768,) or not torch.isfinite(vector).all(): | |
| raise ValueError("Invalid text bank vector") | |
| self.embeddings[text] = vector.float() | |
| def encode(self, strings): | |
| """Return separate [N,768] CPU vectors; reject texts above the token limit.""" | |
| if not isinstance(strings, list) or not strings or any(not isinstance(s, str) or not s.strip() for s in strings): | |
| raise ValueError("Encode a nonempty list of separate, nonempty text strings") | |
| missing = list(dict.fromkeys(s for s in strings if s not in self.embeddings)) | |
| if missing: | |
| if self.model is None: | |
| from sentence_transformers import SentenceTransformer | |
| local = self.model_path or ( | |
| Path("third-party/biolord") | |
| if Path("third-party/biolord").exists() | |
| else None | |
| ) | |
| if local is not None: | |
| local = Path(local) | |
| source = json.loads((local / "source.json").read_text()) | |
| if ( | |
| source.get("model_id") != TEXT_ID | |
| or source.get("revision") != TEXT_REVISION | |
| ): | |
| raise ValueError( | |
| "Local text weights do not match pinned BioLORD" | |
| ) | |
| for name, expected in source["files"].items(): | |
| path = (local / name.replace("\\", "/")).resolve() | |
| if ( | |
| not path.is_relative_to(local.resolve()) | |
| or sha256(path) != expected | |
| ): | |
| raise ValueError( | |
| "Local text weight/config checksum changed" | |
| ) | |
| self.model_path = local | |
| self.model = SentenceTransformer( | |
| str(local) if local else TEXT_ID, | |
| revision=None if local else TEXT_REVISION, | |
| device=self.device, | |
| trust_remote_code=False, | |
| cache_folder=os.environ.get("HF_HOME", "cache/huggingface"), | |
| ) | |
| self.model.eval().requires_grad_(False) | |
| for text in missing: | |
| if ( | |
| len(self.model.tokenizer.encode(text, add_special_tokens=True)) | |
| > self.model.max_seq_length | |
| ): | |
| raise ValueError( | |
| "Text exceeds BioLORD context; do not silently truncate decisive wording" | |
| ) | |
| with torch.inference_mode(): | |
| vectors = self.model.encode( | |
| missing, | |
| normalize_embeddings=True, | |
| convert_to_tensor=True, | |
| show_progress_bar=False, | |
| batch_size=32, | |
| ) | |
| for text, vector in zip(missing, vectors): | |
| if vector.shape != (768,) or not torch.isfinite(vector).all(): | |
| raise ValueError("Text encoder produced an invalid vector") | |
| self.embeddings[text] = vector.detach().cpu().float().clone() | |
| return torch.stack([self.embeddings[s] for s in strings]) | |
| def save(self, path): | |
| """Save cached vectors with their encoder identity.""" | |
| Path(path).parent.mkdir(parents=True, exist_ok=True) | |
| torch.save({"identity": TEXT_IDENTITY, "embeddings": self.embeddings}, path) | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--texts", type=Path, required=True) | |
| parser.add_argument("--output", type=Path, default=Path(f"cache/{VERSION}/text.pt")) | |
| parser.add_argument("--device", default="cpu") | |
| args = parser.parse_args() | |
| encoder = TextEncoder(args.output, args.device) | |
| encoder.encode(json.loads(args.texts.read_text())) | |
| encoder.save(args.output) | |
| print(json.dumps({"texts": len(encoder.embeddings), "identity": TEXT_IDENTITY})) | |
| if __name__ == "__main__": | |
| main() | |