Sentence Similarity
sentence-transformers
English
zephyr
zephyr-rtos
rag
retrieval
faiss
documentation
embedded
qwen
offline
Instructions to use eoinedge/zephyrproject with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use eoinedge/zephyrproject with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("eoinedge/zephyrproject") sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Notebooks
- Google Colab
- Kaggle
File size: 3,421 Bytes
c7815d1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | """Shared retrieval for the CLI and the Space, so the two cannot drift apart.
Beyond loading the index, this does one thing worth explaining: it down-weights
release notes.
Zephyr's release notes are a quarter of the corpus — 1,688 chunks of the 6,949 —
and they are written in the same vocabulary as the documentation they describe.
A question like "what does west build actually do?" retrieved
`develop/west/release-notes - v0.7.0` above `develop/west/build-flash-debug`,
because a changelog entry about west build looks a lot like documentation about
west build to an embedding model.
For a coding assistant that is the wrong answer nearly every time: a developer
asking how something works wants the reference page, not what changed in v0.7.0.
So release notes are penalised, unless the question is actually about a version
or a change — in which case they are exactly right and the penalty is lifted.
"""
from __future__ import annotations
import json
import re
from functools import lru_cache
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DEFAULT_INDEX = ROOT / "data" / "index"
# Historical rather than instructional. Not excluded — "what changed in 3.5?" is
# a fair question — just made to earn its place.
HISTORICAL = re.compile(r"^releases/|/release-notes", re.IGNORECASE)
HISTORICAL_PENALTY = 0.08
# When the question is about a version or a change, the penalty is lifted.
VERSION_QUERY = re.compile(
r"\b(v?\d+\.\d+|release|changelog|changed|new in|migration|deprecat\w*|"
r"breaking|since|upgrade|version)\b",
re.IGNORECASE,
)
@lru_cache(maxsize=4)
def load(index_dir: str):
import faiss
from sentence_transformers import SentenceTransformer
path = Path(index_dir)
if not (path / "docs.faiss").exists():
raise FileNotFoundError(
f"no index at {path} - run scripts/fetch_docs.py then scripts/build_index.py"
)
meta = json.loads((path / "meta.json").read_text(encoding="utf-8"))
chunks = [
json.loads(line)
for line in (path / "chunks.jsonl").read_text(encoding="utf-8").splitlines()
]
embedder = SentenceTransformer(meta["embedding_model"])
return faiss.read_index(str(path / "docs.faiss")), chunks, embedder, meta
def search(question: str, k: int = 6, index_dir: Path | str = DEFAULT_INDEX) -> list[dict]:
index, chunks, embedder, _ = load(str(index_dir))
vector = embedder.encode(
[question], convert_to_numpy=True, normalize_embeddings=True
).astype("float32")
# Over-fetch so re-ranking has something to promote from. Without this the
# penalty could only demote within an already-decided top k.
scores, ids = index.search(vector, min(k * 4, len(chunks)))
wants_history = bool(VERSION_QUERY.search(question))
hits: list[dict] = []
for score, position in zip(scores[0], ids[0]):
if position < 0:
continue
hit = dict(chunks[int(position)])
raw = float(score)
adjusted = raw
if not wants_history and HISTORICAL.search(hit.get("source", "")):
adjusted -= HISTORICAL_PENALTY
hit["score"] = round(adjusted, 4)
hit["raw_score"] = round(raw, 4)
hits.append(hit)
hits.sort(key=lambda item: item["score"], reverse=True)
return hits[:k]
def meta(index_dir: Path | str = DEFAULT_INDEX) -> dict:
return load(str(index_dir))[3]
|