Add files using upload-large-folder tool
Browse files- SEMANTIC_NEIGHBORS.md +69 -0
- launch_semantic_neighbors.sh +4 -0
- neighbor_meta_armenian_western.json +11 -0
- neighbor_meta_bosnian.json +11 -0
- neighbor_meta_catalan.json +11 -0
- neighbor_meta_chinese_traditional.json +11 -0
- neighbor_meta_dutch.json +11 -0
- neighbor_meta_english.json +11 -0
- neighbor_meta_esperanto_h_sistemo.json +11 -0
- neighbor_meta_euskera.json +11 -0
- neighbor_meta_finnish.json +11 -0
- neighbor_meta_french.json +11 -0
- neighbor_meta_friulian.json +11 -0
- neighbor_meta_galician.json +11 -0
- neighbor_meta_georgian.json +11 -0
- neighbor_meta_greek.json +11 -0
- neighbor_meta_hausa.json +11 -0
- neighbor_meta_hawaiian.json +11 -0
- neighbor_meta_hebrew.json +11 -0
- neighbor_meta_hungarian.json +11 -0
- neighbor_meta_icelandic.json +11 -0
- neighbor_meta_indonesian.json +11 -0
- neighbor_meta_italian.json +11 -0
- neighbor_meta_japanese_romaji.json +11 -0
- neighbor_meta_kannada.json +11 -0
- neighbor_meta_kazakh.json +11 -0
- neighbor_meta_khmer.json +11 -0
- neighbor_meta_kyrgyz.json +11 -0
- neighbor_meta_lao.json +11 -0
- neighbor_meta_latin.json +11 -0
- neighbor_meta_lithuanian.json +11 -0
- neighbor_meta_macedonian.json +11 -0
- neighbor_meta_maltese.json +11 -0
- neighbor_meta_mongolian.json +11 -0
- neighbor_meta_oromo.json +11 -0
- neighbor_meta_persian.json +11 -0
- neighbor_meta_santali.json +11 -0
- neighbor_meta_serbian.json +11 -0
- neighbor_meta_sinhala.json +11 -0
- neighbor_meta_slovenian.json +11 -0
- neighbor_meta_swahili.json +11 -0
- neighbor_meta_swiss_german.json +11 -0
- neighbor_meta_tatar_crimean_cyrillic.json +11 -0
- neighbor_meta_telugu.json +11 -0
- neighbor_meta_thai.json +11 -0
- neighbor_meta_xhosa.json +11 -0
- neighbor_meta_yiddish.json +11 -0
- neighbor_meta_zulu.json +11 -0
- semantic_index_meta.json +17 -0
- semantic_neighbors_server.py +268 -0
SEMANTIC_NEIGHBORS.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# KeyboardRage semantic neighbors
|
| 2 |
+
|
| 3 |
+
This adds original-embedding semantic-neighbor inspection to `galaxy_fly_three.html`.
|
| 4 |
+
|
| 5 |
+
Note: I first prototyped the UI on `galaxy.html`, but the intended primary view is the Three.js fly-through view at:
|
| 6 |
+
|
| 7 |
+
```text
|
| 8 |
+
http://localhost:8888/galaxy/3D_galaxy/galaxy_fly_three.html
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
Important distinction:
|
| 12 |
+
- The 3D galaxy coordinates (`x/y/z`) are only the visual UMAP projection.
|
| 13 |
+
- Semantic neighbors are queried from original Granite definition embeddings.
|
| 14 |
+
- FAISS HNSW is only used for candidate retrieval.
|
| 15 |
+
- Final ranking/scores are exact cosine similarities against normalized float32 embeddings.
|
| 16 |
+
|
| 17 |
+
## One-time index build
|
| 18 |
+
|
| 19 |
+
From this directory:
|
| 20 |
+
|
| 21 |
+
```bash
|
| 22 |
+
../venv/bin/python build_semantic_index.py --resume
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Outputs:
|
| 26 |
+
- `semantic_embeddings.f32.npy`
|
| 27 |
+
- `semantic_faiss_hnsw.index`
|
| 28 |
+
- `semantic_index_meta.json`
|
| 29 |
+
|
| 30 |
+
The embeddings file is large because it keeps float32 vectors for exact reranking.
|
| 31 |
+
For ~2.84M rows x 384 dims, expect about 4.1 GiB for the `.npy` plus the FAISS HNSW index.
|
| 32 |
+
|
| 33 |
+
## Run the backend
|
| 34 |
+
|
| 35 |
+
```bash
|
| 36 |
+
./launch_semantic_neighbors.sh
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
Equivalent:
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
../venv/bin/uvicorn semantic_neighbors_server:app --host 127.0.0.1 --port 8703
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
## Run the galaxy
|
| 46 |
+
|
| 47 |
+
From the project root:
|
| 48 |
+
|
| 49 |
+
```bash
|
| 50 |
+
cd /home/ubu/Desktop/keyboardrage
|
| 51 |
+
python3 -m http.server 8888
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
Open:
|
| 55 |
+
|
| 56 |
+
```text
|
| 57 |
+
http://localhost:8888/galaxy/3D_galaxy/galaxy_fly_three.html
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
Click `Inspect semantic neighbors: off` to enable the panel. Then click a point.
|
| 61 |
+
|
| 62 |
+
Controls:
|
| 63 |
+
- `K`: number of neighbors returned.
|
| 64 |
+
- `filter`: all / same language / cross-language.
|
| 65 |
+
- `lines`: show/hide 3D lines from selected point to semantic neighbors.
|
| 66 |
+
- `table`: show/hide the Atlas-style neighbor table.
|
| 67 |
+
- clicking a table row focuses that neighbor and queries its neighbors.
|
| 68 |
+
|
| 69 |
+
If the backend/index is not ready, the browser panel shows the exact command to start the server or build the index.
|
launch_semantic_neighbors.sh
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
cd "$(dirname "$0")"
|
| 4 |
+
exec ../venv/bin/uvicorn semantic_neighbors_server:app --host 127.0.0.1 --port 8703
|
neighbor_meta_armenian_western.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "armenian_western",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 437,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_bosnian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "bosnian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 2905,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_catalan.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "catalan",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 997,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_chinese_traditional.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "chinese_traditional",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 42294,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:11Z"
|
| 11 |
+
}
|
neighbor_meta_dutch.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "dutch",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 3303,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_english.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "english",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 352781,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:58Z"
|
| 11 |
+
}
|
neighbor_meta_esperanto_h_sistemo.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "esperanto_h_sistemo",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 15460,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:09Z"
|
| 11 |
+
}
|
neighbor_meta_euskera.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "euskera",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 145,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_finnish.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "finnish",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 9975,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:09Z"
|
| 11 |
+
}
|
neighbor_meta_french.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "french",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 302443,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:43Z"
|
| 11 |
+
}
|
neighbor_meta_friulian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "friulian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 122,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_galician.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "galician",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 188,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_georgian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "georgian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 176,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_greek.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "greek",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 10543,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:09Z"
|
| 11 |
+
}
|
neighbor_meta_hausa.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "hausa",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 175,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_hawaiian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "hawaiian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 557,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_hebrew.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "hebrew",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 2921,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_hungarian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "hungarian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 2480,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_icelandic.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "icelandic",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 754,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_indonesian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "indonesian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 5635,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_italian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "italian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 231321,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:33Z"
|
| 11 |
+
}
|
neighbor_meta_japanese_romaji.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "japanese_romaji",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 799,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_kannada.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "kannada",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 71,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_kazakh.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "kazakh",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 728,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_khmer.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "khmer",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 291,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_kyrgyz.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "kyrgyz",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 412,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_lao.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "lao",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 325,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_latin.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "latin",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 360,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_lithuanian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "lithuanian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 1771,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_macedonian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "macedonian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 18976,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:10Z"
|
| 11 |
+
}
|
neighbor_meta_maltese.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "maltese",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 563,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_mongolian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "mongolian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 1298,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_oromo.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "oromo",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 430,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_persian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "persian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 8695,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_santali.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "santali",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 64,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_serbian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "serbian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 3022,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:08Z"
|
| 11 |
+
}
|
neighbor_meta_sinhala.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "sinhala",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 39,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_slovenian.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "slovenian",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 1294,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_swahili.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "swahili",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 713,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_swiss_german.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "swiss_german",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 66,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_tatar_crimean_cyrillic.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "tatar_crimean_cyrillic",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 108,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_telugu.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "telugu",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 462,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_thai.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "thai",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 12901,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:09Z"
|
| 11 |
+
}
|
neighbor_meta_xhosa.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "xhosa",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 1089,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:07Z"
|
| 11 |
+
}
|
neighbor_meta_yiddish.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "yiddish",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 139,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
neighbor_meta_zulu.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"language": "zulu",
|
| 3 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 4 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 5 |
+
"rows": 101,
|
| 6 |
+
"dim": 384,
|
| 7 |
+
"top_k": 200,
|
| 8 |
+
"computation": "pytorch_gpu_per_language",
|
| 9 |
+
"global_total": 2250636,
|
| 10 |
+
"created_at": "2026-05-23T20:40:06Z"
|
| 11 |
+
}
|
semantic_index_meta.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "ibm-granite/granite-embedding-97m-multilingual-r2",
|
| 3 |
+
"metric": "cosine_similarity_via_normalized_inner_product",
|
| 4 |
+
"rows": 2250636,
|
| 5 |
+
"dim": 384,
|
| 6 |
+
"order": "sorted words_emb/*.json, skipping rows without embedding_3d; matches galaxy_data.bin and atlas_data.parquet",
|
| 7 |
+
"embeddings": "semantic_embeddings.f32.npy",
|
| 8 |
+
"faiss_index": "semantic_faiss_hnsw.index",
|
| 9 |
+
"faiss": {
|
| 10 |
+
"type": "IndexFlatIP",
|
| 11 |
+
"exact": true,
|
| 12 |
+
"candidate_search_only": false,
|
| 13 |
+
"final_scores": "exact cosine over normalized float32 embeddings",
|
| 14 |
+
"note": "Built for immediate testing after HNSW build was too hot/slow. Queries scan all vectors but ranking is exact."
|
| 15 |
+
},
|
| 16 |
+
"created_at": "2026-05-23T00:09:17Z"
|
| 17 |
+
}
|
semantic_neighbors_server.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Local semantic-neighbor API for KeyboardRage Galaxy.
|
| 4 |
+
|
| 5 |
+
Neighbors are fully precomputed. Global mode uses neighbor_ids.npy (all 2.25M).
|
| 6 |
+
Per-language mode uses neighbor_ids_{lang}.npy (within-language top-200).
|
| 7 |
+
|
| 8 |
+
Precompute with: ../venv/bin/python precompute_neighbors.py --per-language
|
| 9 |
+
"""
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import json
|
| 13 |
+
import math
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Literal
|
| 16 |
+
|
| 17 |
+
import duckdb
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pyarrow.parquet as pq
|
| 20 |
+
from fastapi import FastAPI, HTTPException, Query
|
| 21 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 22 |
+
|
| 23 |
+
HERE = Path(__file__).resolve().parent
|
| 24 |
+
ATLAS_PARQUET = HERE.parent / "atlas" / "atlas_data.parquet"
|
| 25 |
+
NEIGHBOR_IDS_PATH = HERE / "neighbor_ids.npy"
|
| 26 |
+
NEIGHBOR_SCORES_PATH = HERE / "neighbor_scores.npy"
|
| 27 |
+
NEIGHBOR_META_PATH = HERE / "neighbor_meta.json"
|
| 28 |
+
EMBEDDINGS_PATH = HERE / "semantic_embeddings.f32.npy"
|
| 29 |
+
FAISS_INDEX_PATH = HERE / "semantic_faiss_hnsw.index"
|
| 30 |
+
INDEX_META_PATH = HERE / "semantic_index_meta.json"
|
| 31 |
+
|
| 32 |
+
app = FastAPI(title="KeyboardRage Semantic Neighbors", version="3.0")
|
| 33 |
+
app.add_middleware(
|
| 34 |
+
CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"],
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
# Cache: {language: (ids_mmap, scores_mmap, lang_index_mmap)}
|
| 38 |
+
_per_lang_cache: dict[str, tuple[np.ndarray, np.ndarray, np.ndarray | None]] = {}
|
| 39 |
+
_global_ids: np.ndarray | None = None
|
| 40 |
+
_global_scores: np.ndarray | None = None
|
| 41 |
+
_meta_table = None
|
| 42 |
+
_meta_rows: int | None = None
|
| 43 |
+
_con = duckdb.connect(database=":memory:")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _load_metadata_table():
|
| 47 |
+
global _meta_table, _meta_rows
|
| 48 |
+
if _meta_table is None:
|
| 49 |
+
if not ATLAS_PARQUET.exists():
|
| 50 |
+
raise RuntimeError(f"Missing {ATLAS_PARQUET}")
|
| 51 |
+
_meta_table = pq.read_table(
|
| 52 |
+
ATLAS_PARQUET, columns=["x", "y", "z", "word", "language", "definition"],
|
| 53 |
+
)
|
| 54 |
+
_meta_rows = _meta_table.num_rows
|
| 55 |
+
return _meta_table
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _load_global():
|
| 59 |
+
global _global_ids, _global_scores
|
| 60 |
+
if _global_ids is None:
|
| 61 |
+
if not NEIGHBOR_IDS_PATH.exists():
|
| 62 |
+
raise RuntimeError(f"Missing {NEIGHBOR_IDS_PATH.name}")
|
| 63 |
+
_global_ids = np.load(NEIGHBOR_IDS_PATH, mmap_mode="r")
|
| 64 |
+
_global_scores = np.load(NEIGHBOR_SCORES_PATH, mmap_mode="r")
|
| 65 |
+
return _global_ids, _global_scores
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _load_per_language(language: str):
|
| 69 |
+
if language in _per_lang_cache:
|
| 70 |
+
return _per_lang_cache[language]
|
| 71 |
+
|
| 72 |
+
ids_path = HERE / f"neighbor_ids_{language}.npy"
|
| 73 |
+
scores_path = HERE / f"neighbor_scores_{language}.npy"
|
| 74 |
+
index_path = HERE / f"lang_index_{language}.npy"
|
| 75 |
+
|
| 76 |
+
if not ids_path.exists():
|
| 77 |
+
# Fall back to global
|
| 78 |
+
return _load_global()[0], _load_global()[1], None
|
| 79 |
+
|
| 80 |
+
ids = np.load(ids_path, mmap_mode="r")
|
| 81 |
+
scores = np.load(scores_path, mmap_mode="r")
|
| 82 |
+
lang_index = np.load(index_path, mmap_mode="r") if index_path.exists() else None
|
| 83 |
+
|
| 84 |
+
_per_lang_cache[language] = (ids, scores, lang_index)
|
| 85 |
+
return ids, scores, lang_index
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _row(i: int) -> dict:
|
| 89 |
+
table = _load_metadata_table()
|
| 90 |
+
if i < 0 or i >= table.num_rows:
|
| 91 |
+
raise IndexError(i)
|
| 92 |
+
batch = table.slice(i, 1).to_pydict()
|
| 93 |
+
return {
|
| 94 |
+
"id": i,
|
| 95 |
+
"word": batch["word"][0],
|
| 96 |
+
"language": batch["language"][0],
|
| 97 |
+
"definition": batch["definition"][0],
|
| 98 |
+
"x": float(batch["x"][0]),
|
| 99 |
+
"y": float(batch["y"][0]),
|
| 100 |
+
"z": float(batch["z"][0]),
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _rows(ids: list[int]) -> list[dict]:
|
| 105 |
+
return [_row(i) for i in ids]
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def _projected_distance(a: dict, b: dict) -> float:
|
| 109 |
+
dx = float(a["x"]) - float(b["x"])
|
| 110 |
+
dy = float(a["y"]) - float(b["y"])
|
| 111 |
+
dz = float(a["z"]) - float(b["z"])
|
| 112 |
+
return math.sqrt(dx * dx + dy * dy + dz * dz)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
@app.get("/health")
|
| 116 |
+
def health():
|
| 117 |
+
status = {
|
| 118 |
+
"atlas_parquet": ATLAS_PARQUET.exists(),
|
| 119 |
+
"embeddings": EMBEDDINGS_PATH.exists(),
|
| 120 |
+
"precomputed_global": NEIGHBOR_IDS_PATH.exists(),
|
| 121 |
+
}
|
| 122 |
+
if ATLAS_PARQUET.exists():
|
| 123 |
+
status["rows"] = pq.ParquetFile(ATLAS_PARQUET).metadata.num_rows
|
| 124 |
+
if NEIGHBOR_META_PATH.exists():
|
| 125 |
+
status["neighbor_meta"] = json.loads(NEIGHBOR_META_PATH.read_text())
|
| 126 |
+
# List available per-language files
|
| 127 |
+
per_lang = sorted(
|
| 128 |
+
p.stem.replace("neighbor_ids_", "")
|
| 129 |
+
for p in HERE.glob("neighbor_ids_*.npy")
|
| 130 |
+
)
|
| 131 |
+
if per_lang:
|
| 132 |
+
status["per_language_available"] = per_lang
|
| 133 |
+
return status
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
@app.get("/point/{point_id}")
|
| 137 |
+
def point(point_id: int):
|
| 138 |
+
try:
|
| 139 |
+
return _row(point_id)
|
| 140 |
+
except Exception:
|
| 141 |
+
raise HTTPException(status_code=404, detail="point id out of range")
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
@app.get("/neighbors/{point_id}")
|
| 145 |
+
def neighbors(
|
| 146 |
+
point_id: int,
|
| 147 |
+
k: int = Query(10, ge=1, le=200),
|
| 148 |
+
language: str | None = Query(
|
| 149 |
+
None,
|
| 150 |
+
description="Restrict to within-language neighbors (e.g. 'french')",
|
| 151 |
+
),
|
| 152 |
+
language_filter: Literal["all", "same", "cross"] = "all",
|
| 153 |
+
visible_languages: str | None = Query(None),
|
| 154 |
+
):
|
| 155 |
+
query = _row(point_id)
|
| 156 |
+
|
| 157 |
+
if language:
|
| 158 |
+
# Per-language precomputed: map global_id → local_id
|
| 159 |
+
ids_arr, scores_arr, lang_index = _load_per_language(language)
|
| 160 |
+
if lang_index is not None:
|
| 161 |
+
local_id = int(lang_index[point_id])
|
| 162 |
+
if local_id < 0:
|
| 163 |
+
return {
|
| 164 |
+
"query": query, "metric": "cosine",
|
| 165 |
+
"space": "precomputed_per_language", "k": k,
|
| 166 |
+
"language": language, "neighbors": [],
|
| 167 |
+
}
|
| 168 |
+
all_ids = np.asarray(ids_arr[local_id], dtype=np.int64)
|
| 169 |
+
all_scores = np.asarray(scores_arr[local_id], dtype=np.float32)
|
| 170 |
+
else:
|
| 171 |
+
# No per-language file, fall back to global with same-language filter
|
| 172 |
+
ids_arr, scores_arr = _load_global()
|
| 173 |
+
all_ids = np.asarray(ids_arr[point_id], dtype=np.int64)
|
| 174 |
+
all_scores = np.asarray(scores_arr[point_id], dtype=np.float32)
|
| 175 |
+
language_filter = "same"
|
| 176 |
+
else:
|
| 177 |
+
ids_arr, scores_arr = _load_global()
|
| 178 |
+
all_ids = np.asarray(ids_arr[point_id], dtype=np.int64)
|
| 179 |
+
all_scores = np.asarray(scores_arr[point_id], dtype=np.float32)
|
| 180 |
+
|
| 181 |
+
# Drop sentinels (0 = no more neighbors)
|
| 182 |
+
valid = all_ids >= 0
|
| 183 |
+
all_ids = all_ids[valid]
|
| 184 |
+
all_scores = all_scores[valid]
|
| 185 |
+
|
| 186 |
+
if all_ids.size == 0:
|
| 187 |
+
return {
|
| 188 |
+
"query": query, "metric": "cosine",
|
| 189 |
+
"space": "precomputed", "k": k, "neighbors": [],
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
# Parse visible-language filter
|
| 193 |
+
visible_set = None
|
| 194 |
+
if visible_languages:
|
| 195 |
+
visible_set = {p.strip() for p in visible_languages.split(",") if p.strip()}
|
| 196 |
+
if not visible_set:
|
| 197 |
+
visible_set = None
|
| 198 |
+
|
| 199 |
+
has_filter = language_filter != "all" or visible_set is not None
|
| 200 |
+
rows = _rows([int(i) for i in all_ids.tolist()])
|
| 201 |
+
|
| 202 |
+
eligible_scores: list[float] = []
|
| 203 |
+
eligible_rows: list[dict] = []
|
| 204 |
+
|
| 205 |
+
for idx, (cid, row) in enumerate(zip(all_ids.tolist(), rows)):
|
| 206 |
+
if visible_set is not None and row["language"] not in visible_set:
|
| 207 |
+
continue
|
| 208 |
+
if language_filter == "same" and row["language"] != query["language"]:
|
| 209 |
+
continue
|
| 210 |
+
if language_filter == "cross" and row["language"] == query["language"]:
|
| 211 |
+
continue
|
| 212 |
+
eligible_rows.append(row)
|
| 213 |
+
eligible_scores.append(float(all_scores[idx]))
|
| 214 |
+
|
| 215 |
+
if has_filter and eligible_scores:
|
| 216 |
+
order = sorted(range(len(eligible_scores)), key=lambda i: -eligible_scores[i])[:k]
|
| 217 |
+
else:
|
| 218 |
+
order = list(range(min(k, len(eligible_scores))))
|
| 219 |
+
|
| 220 |
+
out = []
|
| 221 |
+
for rank, pos in enumerate(order, start=1):
|
| 222 |
+
row = eligible_rows[pos]
|
| 223 |
+
sim = eligible_scores[pos]
|
| 224 |
+
out.append({
|
| 225 |
+
**row, "rank": rank,
|
| 226 |
+
"cosine_similarity": sim,
|
| 227 |
+
"cosine_distance": float(1.0 - sim),
|
| 228 |
+
"projected_distance_3d": _projected_distance(query, row),
|
| 229 |
+
})
|
| 230 |
+
|
| 231 |
+
return {
|
| 232 |
+
"query": query, "metric": "cosine",
|
| 233 |
+
"space": "precomputed_per_language" if language else "precomputed_global",
|
| 234 |
+
"k": k, "language_filter": language_filter,
|
| 235 |
+
"visible_languages": sorted(visible_set) if visible_set else None,
|
| 236 |
+
"candidates_examined": int(all_ids.size),
|
| 237 |
+
"neighbors": out,
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
@app.get("/search")
|
| 242 |
+
def search(
|
| 243 |
+
q: str = Query(..., min_length=1),
|
| 244 |
+
limit: int = Query(25, ge=1, le=200),
|
| 245 |
+
language: str | None = Query(None, description="Filter by language"),
|
| 246 |
+
):
|
| 247 |
+
if not ATLAS_PARQUET.exists():
|
| 248 |
+
raise HTTPException(status_code=500, detail="atlas_data.parquet missing")
|
| 249 |
+
safe_q = q.replace("'", "''")
|
| 250 |
+
lang_clause = ""
|
| 251 |
+
if language:
|
| 252 |
+
safe_lang = language.replace("'", "''")
|
| 253 |
+
lang_clause = f"AND lower(language) = lower('{safe_lang}')"
|
| 254 |
+
sql = f"""
|
| 255 |
+
WITH t AS (
|
| 256 |
+
SELECT row_number() OVER () - 1 AS id, word, language, x, y, z, definition
|
| 257 |
+
FROM read_parquet('{ATLAS_PARQUET.as_posix()}')
|
| 258 |
+
)
|
| 259 |
+
SELECT id, word, language, x, y, z, definition
|
| 260 |
+
FROM t
|
| 261 |
+
WHERE (lower(word) = lower('{safe_q}') OR lower(word) LIKE '%' || lower('{safe_q}') || '%')
|
| 262 |
+
{lang_clause}
|
| 263 |
+
ORDER BY CASE WHEN lower(word) = lower('{safe_q}') THEN 0 ELSE 1 END, word
|
| 264 |
+
LIMIT {int(limit)}
|
| 265 |
+
"""
|
| 266 |
+
rows = _con.execute(sql).fetchall()
|
| 267 |
+
cols = ["id", "word", "language", "x", "y", "z", "definition"]
|
| 268 |
+
return {"results": [dict(zip(cols, r)) for r in rows]}
|