feat(sprint1): показ эмбеддингов чанков на странице отладки

Расширяем просмотр документа, чтобы оператор видел не только текстовые
чанки, но и как они лежат в ChromaDB в виде векторов — по паттерну из
work-pcs-dr-cdss.

Backend:
- services/vectorstore.get_document_chunks теперь запрашивает
  include=["embeddings"] и отдаёт вектор как list[float]. Chroma
  возвращает numpy-массивы, поэтому проверка наличия embeddings
  сделана через len(), без or-шортката.
- models.ChunkDetail: поля embedding: list[float] + embedding_dim: int.
- routers/documents прокидывает вектор и размерность в ответ.

Frontend (static/index.html):
- В карточку чанка добавлен блок .chunk-card-actions с кнопкой
  «вектор (N dim)»; раскрывается в .embedding-box с полным списком
  координат (округление до 6 знаков, моноширинный шрифт, скролл).
- Функция toggleChunkText переписана через .closest + querySelector,
  чтобы не ломаться от новой обёртки кнопок.
- Добавлена toggleEmb(embId).

Проверено на загруженных документах — возвращается по 1024 координаты
(E5-large), совпадает с ожиданиями embedding-модели.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AR 15 M4
2026-04-23 09:59:24 +05:00
parent ccc3dde978
commit 4a5695ed9c
4 changed files with 62 additions and 5 deletions
+6 -2
View File
@@ -118,18 +118,22 @@ class VectorStoreService:
return list(docs.values())
def get_document_chunks(self, document_id: str) -> list[dict]:
"""Return all chunks for a document, sorted by chunk_index."""
"""Return all chunks for a document, sorted by chunk_index. Includes embeddings."""
results = self.collection.get(
where={"document_id": document_id},
include=["documents", "metadatas"],
include=["documents", "metadatas", "embeddings"],
)
items = []
if results["ids"]:
embeddings = results.get("embeddings")
has_emb = embeddings is not None and len(embeddings) > 0
for i, chunk_id in enumerate(results["ids"]):
emb = embeddings[i] if has_emb and i < len(embeddings) else None
items.append({
"chunk_id": chunk_id,
"text": results["documents"][i],
"metadata": results["metadatas"][i],
"embedding": [float(x) for x in emb] if emb is not None else [],
})
items.sort(key=lambda x: x["metadata"].get("chunk_index", 0))
return items