Spaces:
Build error
Build error
Upload folder using huggingface_hub (part 2)
Browse files- backend/pipeline/vector_client.py +38 -25
- docs/HISTORY.md +13 -0
backend/pipeline/vector_client.py
CHANGED
|
@@ -334,32 +334,45 @@ class PGVectorCollectionWrapper:
|
|
| 334 |
model_name,
|
| 335 |
],
|
| 336 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
else:
|
| 338 |
-
#
|
| 339 |
-
#
|
| 340 |
-
#
|
| 341 |
-
# vecteur déjà calculé). Si l'appelant A fourni un
|
| 342 |
-
# vecteur (ex. `VisualIndexService.index`, qui
|
| 343 |
-
# n'appelle jamais `upsert_items` avec des
|
| 344 |
-
# `documents`), l'écrire quand même en NULL le
|
| 345 |
-
# jetterait en silence : `strict=True` ne se
|
| 346 |
-
# déclenche jamais (aucune exception ne remonte),
|
| 347 |
-
# et l'appelant annonce un succès pour une ligne
|
| 348 |
-
# sans vecteur. Dormant sur Neon/Cloud SQL
|
| 349 |
-
# aujourd'hui ; armé le jour où quelqu'un pointe
|
| 350 |
-
# ceci sur une vraie instance AlloyDB.
|
| 351 |
-
if embeddings is not None and embeddings[i]:
|
| 352 |
-
raise ValueError(
|
| 353 |
-
f"AlloyDB AI branch: item {item_id!r} in "
|
| 354 |
-
f"collection {self.name!r} was given a "
|
| 355 |
-
"computed embedding but no `document` -- "
|
| 356 |
-
"this branch derives the embedding FROM "
|
| 357 |
-
"`documents` (`embedding(model, doc)`) and "
|
| 358 |
-
"silently writes NULL otherwise, discarding "
|
| 359 |
-
"the vector the caller already computed. "
|
| 360 |
-
"Refusing rather than writing a vectorless "
|
| 361 |
-
"row that looks like a successful upsert."
|
| 362 |
-
)
|
| 363 |
sql = """
|
| 364 |
INSERT INTO animetix_vectorrecord (collection_name, item_id, embedding, metadata, document, created_at)
|
| 365 |
VALUES (%s, %s, NULL, %s, NULL, NOW())
|
|
|
|
| 334 |
model_name,
|
| 335 |
],
|
| 336 |
)
|
| 337 |
+
elif embeddings is not None and embeddings[i]:
|
| 338 |
+
# No `document`, but the caller supplied a
|
| 339 |
+
# precomputed vector (exactly `VisualIndexService.
|
| 340 |
+
# index`'s shape: it hands over vectors it computed
|
| 341 |
+
# itself, never raw text). The `embedding(model,
|
| 342 |
+
# doc)` derivation does not apply here -- but
|
| 343 |
+
# writing NULL would silently discard the caller's
|
| 344 |
+
# vector. Persist the supplied vector directly, as a
|
| 345 |
+
# pgvector literal. Dormant on Neon/Cloud SQL today
|
| 346 |
+
# (is_alloydb_ai_supported() is False there); this is
|
| 347 |
+
# what makes the visual index buildable the day
|
| 348 |
+
# someone points this at a real AlloyDB instance.
|
| 349 |
+
vec_literal = (
|
| 350 |
+
"["
|
| 351 |
+
+ ",".join(str(float(x)) for x in embeddings[i])
|
| 352 |
+
+ "]"
|
| 353 |
+
)
|
| 354 |
+
sql = """
|
| 355 |
+
INSERT INTO animetix_vectorrecord (collection_name, item_id, embedding, metadata, document, created_at)
|
| 356 |
+
VALUES (%s, %s, %s::vector, %s, NULL, NOW())
|
| 357 |
+
ON CONFLICT (collection_name, item_id)
|
| 358 |
+
DO UPDATE SET
|
| 359 |
+
embedding = EXCLUDED.embedding,
|
| 360 |
+
metadata = EXCLUDED.metadata,
|
| 361 |
+
document = EXCLUDED.document;
|
| 362 |
+
"""
|
| 363 |
+
cursor.execute(
|
| 364 |
+
sql,
|
| 365 |
+
[
|
| 366 |
+
self.name,
|
| 367 |
+
str(item_id),
|
| 368 |
+
vec_literal,
|
| 369 |
+
json.dumps(clean_metas[i]),
|
| 370 |
+
],
|
| 371 |
+
)
|
| 372 |
else:
|
| 373 |
+
# No document AND no vector: a metadata-only row
|
| 374 |
+
# (embedding NULL) is the honest answer -- nothing
|
| 375 |
+
# is being discarded.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
sql = """
|
| 377 |
INSERT INTO animetix_vectorrecord (collection_name, item_id, embedding, metadata, document, created_at)
|
| 378 |
VALUES (%s, %s, NULL, %s, NULL, NOW())
|
docs/HISTORY.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
| 2 |
|
| 3 |
This document archives the major milestones of the project's technical evolution.
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
## [2026-07-15] Session: Technical-debt sweep — hardcoded config & data externalized, duplicated logic unified
|
| 6 |
|
| 7 |
Closure of five 🟡 items from the *audit dette 2026-07-11*.
|
|
|
|
| 2 |
|
| 3 |
This document archives the major milestones of the project's technical evolution.
|
| 4 |
|
| 5 |
+
## [2026-07-16] Session: Dockerfile hardening, Covertest→Postgres, coverage & fetch hygiene, script archive, BlindtestPage split, CCIP integration guard
|
| 6 |
+
|
| 7 |
+
- **Dockerfile hardening (safe subset).** `Dockerfile.brain` runtime → `python:3.12-slim` (bake stages left on 3.11 to preserve the ~90-min merge cache); `Dockerfile.dataflow` drops root to a non-root `appuser` after apt/pip. Brain stays root by design (Ollama installs as root, the GPU path is untestable and CI never rebuilds the brain); the dataflow non-root drop is **unverified against a real Dataflow submission** — revert the `USER` block if the runner rejects it. The `:latest` launcher is kept (Google's recommended form, with pin-by-digest instructions in-file).
|
| 8 |
+
- **Covertest — covers out of the JSON blob.** `manga_covers.json` (0.5 → ~25 MB, loaded entirely into RAM by the pgvector singleton at first call) moved to a Postgres `MangaCover` model with on-demand lookups (`sync_covers` command + migration).
|
| 9 |
+
- **`finetuning_dataset` coverage 65 → 85%.** Hermetic integration tests over the language×tier matrix (anime/manga/character), language omission, Alpaca-base load exceptions, out-of-bounds noise, and multi-turn noise.
|
| 10 |
+
- **Raw-`fetch()` toast hygiene.** Failure toasts harmonized on `MangaVoicePage`, `offlineLibrary` (via a `useChapterDownload` hook), and the OpenData download (`OpenDataPage` / `labService`); the three keep raw `fetch` on purpose (binary / cross-origin) but now surface an `error` toast.
|
| 11 |
+
- **Frontend coverage gate raised** to match reality and block regressions: 29→38 statements / 22→28 branches / 28→34 functions / 29→39 lines.
|
| 12 |
+
- **`scripts/` cleaned.** 7 unused one-offs → `scripts/archive/`, stale `test_*.pyc` purged, and a `scripts/README.md` documenting the active and archived scripts.
|
| 13 |
+
- **`BlindtestPage.tsx` 753 → 324 lines**, extracting a `useBlindtestGame` hook plus `BlindtestPlayer` / `BlindtestSecretReveal` / `BlindtestBonusRecap` components. The other >500-line components (TransparencyPage, AudioLabPage, LabHubPage, SpeechToSpeechLabPage) remain and stay tracked as open.
|
| 14 |
+
- **CCIP integration guard.** A new `integration`-marked test (`tests/adapters/test_ccip_vision_integration.py`) drives the real `deepghs/ccip_onnx` graph over real catalog portraits and pins the end-to-end property — 768-d, non-zero, deterministic, discriminating (mean cross-character ~0.33 ≪ self ~1.0), size-invariant, query-ranks-self — that a subtly-wrong preprocessing change would break while passing the mocked units. De-risks the phase-2 character build (which remains a prod job).
|
| 15 |
+
- **AlloyDB upsert persists a supplied vector.** `PGVectorCollectionWrapper.upsert`'s AlloyDB-AI branch derives embeddings from `documents` via `embedding(model, doc)`; handed a precomputed vector with no document — exactly `VisualIndexService.index`'s shape — it used to *raise* (a guard against silently writing NULL). It now persists the supplied vector directly as a pgvector literal (`%s::vector`), so the visual index is buildable the day this points at a real AlloyDB instance. The genuine no-vector/no-document case still writes NULL. Dormant on Neon/Cloud SQL (`is_alloydb_ai_supported()` is False there).
|
| 16 |
+
- **`reconcile_db` no longer issues a clean bill of health for a store it skipped.** When Neo4j was offline (the dead local URI → `driver is None`) the command skipped every graph check but still printed "[SUCCESS] All sampled items are synchronized across Django, pgvector, **and Neo4j**" — the skip went unnoticed behind a green verdict. The verdict now lists the stores actually checked and, when any was skipped, warns their synchronization is `UNVERIFIED` rather than confirmed; full SUCCESS only when every store was reached. The dead local URI itself remains an operator config action (point `NEO4J_URI` at a live instance).
|
| 17 |
+
|
| 18 |
## [2026-07-15] Session: Technical-debt sweep — hardcoded config & data externalized, duplicated logic unified
|
| 19 |
|
| 20 |
Closure of five 🟡 items from the *audit dette 2026-07-11*.
|