mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 14:17:08 +00:00
fix: embeddings registry edge cases and chunk-size accounting
Follow-up to the embeddings work, from a review pass over the branch. - Route the OpenAI/Azure key handling through the model registry instead of matching the canonical name literally, so the `text-embedding-ada-002` alias the registry now accepts also reaches the Azure deployment name rather than failing every embed with DeploymentNotFound. - Fall back to a default width where the embeddings model reports no dimension. A model outside the registry returns None rather than no attribute, so `getattr` with a default did not catch it and the width reached the DDL as `vector(None)` / `list_size=None`. - Point HF_HUB_CACHE at the prefetch directory. Chunking loads the tokenizer through `tokenizers`, which reads the hub cache, so a fresh container fetched over the network on first ingest and an offline one silently fell back to cl100k. - Charge a token that collapses a long unbroken run by its character span. WordPiece emits one [UNK] for any word over its character limit, which made base64 and minified content count as near-zero tokens, so nothing split it and oversized chunks reached the embedding server. - Preserve chunk ids and honour --batch-size when rebuilding a FAISS index. Fresh uuids orphaned GraphRAG's graph_node_chunks rows, and the whole index went out in a single embed call on remote servers. - Document that granite runs an int8-quantised graph, and scope the SentenceTransformer parity claim to mpnet's fp32 graph, which is where it was measured. - Correct the embeddings docs: a matching dimension is not a matching model, so a same-width swap raises nothing and silently degrades retrieval.
This commit is contained in:
1 parent
869d87452b
commit
25e07f5cee
15 files changed
+296
-20
No files matched your search
@@ -202,3 +202,50 @@ class TestOffsetsWithoutSpans:
|
||||
def test_all_span_less_offsets_still_return_the_text(self):
|
||||
counter = self._counter([(0, 0), (0, 0), (0, 0)])
|
||||
assert "".join(counter.split("abc", 1)) == "abc"
|
||||
|
||||
|
||||
class TestUnknownTokenCollapse:
|
||||
"""A tokenizer that folds a long unbroken run into one ``[UNK]``.
|
||||
|
||||
WordPiece gives up on any word longer than ``max_input_chars_per_word``
|
||||
and emits a single unknown token for it. Counting that as one token makes
|
||||
a base64 blob or a minified bundle look tiny, so the chunker never splits
|
||||
it and an oversized chunk reaches the embedding server.
|
||||
"""
|
||||
|
||||
class _CollapsingEncoding:
|
||||
"""One token per whitespace-separated word, however long the word."""
|
||||
|
||||
def __init__(self, text):
|
||||
self.ids = []
|
||||
self.offsets = []
|
||||
cursor = 0
|
||||
for word in text.split(" "):
|
||||
if word:
|
||||
self.ids.append(0)
|
||||
self.offsets.append((cursor, cursor + len(word)))
|
||||
cursor += len(word) + 1
|
||||
|
||||
class _CollapsingTokenizer:
|
||||
def encode(self, text, add_special_tokens=False):
|
||||
return TestUnknownTokenCollapse._CollapsingEncoding(text)
|
||||
|
||||
def _counter(self):
|
||||
return tokenization.HuggingFaceCounter(self._CollapsingTokenizer(), "stub")
|
||||
|
||||
def test_long_unbroken_run_is_charged_by_its_span(self):
|
||||
counter = self._counter()
|
||||
assert counter.count("a" * 9000) > 100
|
||||
|
||||
def test_ordinary_prose_is_unaffected(self):
|
||||
counter = self._counter()
|
||||
text = "the quick brown fox jumps over the lazy dog"
|
||||
assert counter.count(text) == 9
|
||||
|
||||
def test_split_bounds_a_collapsed_run(self):
|
||||
counter = self._counter()
|
||||
text = "a" * 9000
|
||||
pieces = counter.split(text, 20)
|
||||
assert "".join(pieces) == text, "split must not lose or alter text"
|
||||
assert len(pieces) > 1, "a collapsed run must still be cut into pieces"
|
||||
assert all(counter.count(p) <= 20 for p in pieces)
|
||||
Reference in new issue
Block a user