Spaces:
Running
Running
Devdan Schretlen
Claude Opus 5 (1M context)
Render the answer as Markdown, bound API timeouts, fix a false notice
9db5d1e Download src/embed.py from DevSchretl/UBCHelper: direct link, hf CLI and curl.
- Browser
- Download file 2.47 kB
-
https://huggingface.co/spaces/DevSchretl/UBCHelper/resolve/main/src/embed.py
- Command line
-
hf download hf://spaces/DevSchretl/UBCHelper/src/embed.py
-
curl -L -o embed.py https://huggingface.co/spaces/DevSchretl/UBCHelper/resolve/main/src/embed.py
2.47 kB
| """ | |
| Embeddings β the single source of truth for turning text into vectors. | |
| Both ingest.py (indexing documents) and retrieve.py (embedding the user's query) call | |
| `embed_texts` here. Routing every embedding through one function guarantees the golden | |
| rule of dense retrieval is never broken: the SAME model embeds the documents and the | |
| queries, so their vectors live in the same space and cosine similarity is meaningful. | |
| We talk to OpenAI's hosted `/v1/embeddings` endpoint (config.EMBED_*), so there is no | |
| local model-server or torch dependency β just the `openai` client and numpy. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| from openai import OpenAI | |
| from . import config | |
| # One shared client, created lazily on first use (so merely importing this module | |
| # doesn't require the endpoint to be reachable). | |
| _client: OpenAI | None = None | |
| def _get_client() -> OpenAI: | |
| global _client | |
| if _client is None: | |
| _client = OpenAI( | |
| base_url=config.EMBED_BASE_URL, | |
| api_key=config.EMBED_API_KEY, | |
| timeout=config.API_TIMEOUT, | |
| ) | |
| return _client | |
| def embed_texts(texts: list[str], batch_size: int = 64) -> np.ndarray: | |
| """Embed a list of strings into an (N, dim) float32 matrix of UNIT vectors. | |
| The vectors are L2-normalized, which means cosine similarity between any two of | |
| them is just their dot product. That lets retrieval score the whole corpus with a | |
| single fast matrix multiply (see retrieve.py). | |
| Texts are sent in batches so we make a handful of requests instead of one per | |
| document β much faster for a few hundred documents. | |
| """ | |
| if not texts: | |
| raise ValueError("embed_texts called with no texts") | |
| client = _get_client() | |
| vectors: list[list[float]] = [] | |
| for start in range(0, len(texts), batch_size): | |
| batch = texts[start : start + batch_size] | |
| response = client.embeddings.create(model=config.EMBEDDING_MODEL, input=batch) | |
| # The API guarantees one embedding per input, in order. | |
| vectors.extend(item.embedding for item in response.data) | |
| matrix = np.asarray(vectors, dtype=np.float32) | |
| return _normalize(matrix) | |
| def _normalize(matrix: np.ndarray) -> np.ndarray: | |
| """Scale each row to unit length. Guards against divide-by-zero on empty vectors.""" | |
| norms = np.linalg.norm(matrix, axis=1, keepdims=True) | |
| norms[norms == 0] = 1.0 # avoid NaN if a vector is all zeros | |
| return matrix / norms | |