anywidget-vector 0.1.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anywidget_vector-0.2.1/.github/workflows/ci.yml +71 -0
- anywidget_vector-0.2.1/.github/workflows/pypi.yml +77 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/PKG-INFO +70 -3
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/README.md +67 -0
- anywidget_vector-0.2.1/examples/demo.py +274 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/pyproject.toml +3 -3
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/src/anywidget_vector/__init__.py +1 -1
- anywidget_vector-0.2.1/src/anywidget_vector/backends/__init__.py +103 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/chroma/__init__.py +27 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/chroma/client.py +60 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/chroma/converter.py +86 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/grafeo/__init__.py +20 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/grafeo/client.py +33 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/grafeo/converter.py +46 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/lancedb/__init__.py +22 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/lancedb/client.py +56 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/lancedb/converter.py +71 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/pinecone/__init__.py +21 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/pinecone/client.js +45 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/pinecone/converter.py +62 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/qdrant/__init__.py +26 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/qdrant/client.js +61 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/qdrant/converter.py +83 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/weaviate/__init__.py +33 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/weaviate/client.js +50 -0
- anywidget_vector-0.2.1/src/anywidget_vector/backends/weaviate/converter.py +81 -0
- anywidget_vector-0.2.1/src/anywidget_vector/static/icons.js +14 -0
- anywidget_vector-0.2.1/src/anywidget_vector/traitlets.py +84 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/__init__.py +206 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/canvas.js +521 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/constants.js +64 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/properties.js +158 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/settings.js +265 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/styles.css +348 -0
- anywidget_vector-0.2.1/src/anywidget_vector/ui/toolbar.js +117 -0
- anywidget_vector-0.2.1/src/anywidget_vector/widget.py +315 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/tests/test_widget.py +125 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/uv.lock +23 -24
- anywidget_vector-0.1.0/src/anywidget_vector/widget.py +0 -978
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/.gitignore +0 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/.python-version +0 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/src/anywidget_vector/py.typed +0 -0
- {anywidget_vector-0.1.0 → anywidget_vector-0.2.1}/tests/__init__.py +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: Lint
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: pip install ruff
|
|
23
|
+
|
|
24
|
+
- name: Run ruff check
|
|
25
|
+
run: ruff check .
|
|
26
|
+
|
|
27
|
+
- name: Run ruff format check
|
|
28
|
+
run: ruff format --check .
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
strategy:
|
|
34
|
+
matrix:
|
|
35
|
+
python-version: ["3.12", "3.13"]
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
+
uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ matrix.python-version }}
|
|
43
|
+
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
run: |
|
|
46
|
+
pip install -e ".[dev]"
|
|
47
|
+
|
|
48
|
+
- name: Run tests
|
|
49
|
+
run: pytest
|
|
50
|
+
|
|
51
|
+
build:
|
|
52
|
+
name: Build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
|
|
57
|
+
- name: Set up Python
|
|
58
|
+
uses: actions/setup-python@v5
|
|
59
|
+
with:
|
|
60
|
+
python-version: "3.12"
|
|
61
|
+
|
|
62
|
+
- name: Install build dependencies
|
|
63
|
+
run: pip install build
|
|
64
|
+
|
|
65
|
+
- name: Build package
|
|
66
|
+
run: python -m build
|
|
67
|
+
|
|
68
|
+
- name: Check package
|
|
69
|
+
run: |
|
|
70
|
+
pip install twine
|
|
71
|
+
twine check dist/*
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
target:
|
|
9
|
+
description: 'Publish target'
|
|
10
|
+
required: true
|
|
11
|
+
default: 'testpypi'
|
|
12
|
+
type: choice
|
|
13
|
+
options:
|
|
14
|
+
- testpypi
|
|
15
|
+
- pypi
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
name: Build distribution
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Install build dependencies
|
|
30
|
+
run: pip install build
|
|
31
|
+
|
|
32
|
+
- name: Build package
|
|
33
|
+
run: python -m build
|
|
34
|
+
|
|
35
|
+
- name: Upload distribution artifacts
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish-testpypi:
|
|
42
|
+
name: Publish to TestPyPI
|
|
43
|
+
needs: build
|
|
44
|
+
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment: testpypi
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
steps:
|
|
50
|
+
- name: Download distribution artifacts
|
|
51
|
+
uses: actions/download-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
|
|
56
|
+
- name: Publish to TestPyPI
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
58
|
+
with:
|
|
59
|
+
repository-url: https://test.pypi.org/legacy/
|
|
60
|
+
|
|
61
|
+
publish-pypi:
|
|
62
|
+
name: Publish to PyPI
|
|
63
|
+
needs: build
|
|
64
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
environment: pypi
|
|
67
|
+
permissions:
|
|
68
|
+
id-token: write
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download distribution artifacts
|
|
71
|
+
uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
|
|
76
|
+
- name: Publish to PyPI
|
|
77
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anywidget-vector
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary: Interactive vector visualization
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Interactive 3D vector visualization with query UI for vector databases
|
|
5
5
|
Project-URL: Homepage, https://grafeo.dev/
|
|
6
6
|
Project-URL: Repository, https://github.com/GrafeoDB/anywidget-vector
|
|
7
7
|
Author-email: "S.T. Grond" <widget@grafeo.dev>
|
|
@@ -30,7 +30,7 @@ Provides-Extra: dev
|
|
|
30
30
|
Requires-Dist: marimo>=0.19.7; extra == 'dev'
|
|
31
31
|
Requires-Dist: prek>=0.3.1; extra == 'dev'
|
|
32
32
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
33
|
-
Requires-Dist: ruff>=0.
|
|
33
|
+
Requires-Dist: ruff>=0.15.0; extra == 'dev'
|
|
34
34
|
Requires-Dist: ty>=0.0.14; extra == 'dev'
|
|
35
35
|
Provides-Extra: lancedb
|
|
36
36
|
Requires-Dist: lancedb>=0.1; extra == 'lancedb'
|
|
@@ -264,6 +264,73 @@ widget = VectorSpace(
|
|
|
264
264
|
)
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
+
## Distance Metrics
|
|
268
|
+
|
|
269
|
+
Compute distances and visualize similarity relationships between points.
|
|
270
|
+
|
|
271
|
+
### Supported Metrics
|
|
272
|
+
|
|
273
|
+
| Metric | Description |
|
|
274
|
+
|--------|-------------|
|
|
275
|
+
| `euclidean` | Straight-line distance (L2 norm) |
|
|
276
|
+
| `cosine` | Angle-based distance (1 - cosine similarity) |
|
|
277
|
+
| `manhattan` | Sum of absolute differences (L1 norm) |
|
|
278
|
+
| `dot_product` | Negative dot product (higher = closer) |
|
|
279
|
+
|
|
280
|
+
### Color by Distance
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
# Color points by distance from a reference
|
|
284
|
+
widget.color_by_distance("point_a")
|
|
285
|
+
widget.color_by_distance("point_a", metric="cosine")
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Find Neighbors
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
# Find k nearest neighbors
|
|
292
|
+
neighbors = widget.find_neighbors("point_a", k=5)
|
|
293
|
+
# Returns: [("point_b", 0.1), ("point_c", 0.2), ...]
|
|
294
|
+
|
|
295
|
+
# Find neighbors within distance threshold
|
|
296
|
+
neighbors = widget.find_neighbors("point_a", threshold=0.5)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Show Connections
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
# Draw lines to k-nearest neighbors
|
|
303
|
+
widget.show_neighbors("point_a", k=5)
|
|
304
|
+
|
|
305
|
+
# Draw lines to all points within threshold
|
|
306
|
+
widget.show_neighbors("point_a", threshold=0.3)
|
|
307
|
+
|
|
308
|
+
# Manual connection settings
|
|
309
|
+
widget = VectorSpace(
|
|
310
|
+
points=data,
|
|
311
|
+
show_connections=True,
|
|
312
|
+
k_neighbors=3,
|
|
313
|
+
distance_metric="cosine",
|
|
314
|
+
connection_color="#00ff00",
|
|
315
|
+
connection_opacity=0.5,
|
|
316
|
+
)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Compute Distances
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# Get distances from reference to all points
|
|
323
|
+
distances = widget.compute_distances("point_a")
|
|
324
|
+
# Returns: {"point_b": 0.1, "point_c": 0.5, ...}
|
|
325
|
+
|
|
326
|
+
# Use high-dimensional vectors (not just x,y,z)
|
|
327
|
+
distances = widget.compute_distances(
|
|
328
|
+
"point_a",
|
|
329
|
+
metric="cosine",
|
|
330
|
+
vector_field="embedding" # Use full embedding vector
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
267
334
|
## Export
|
|
268
335
|
|
|
269
336
|
```python
|
|
@@ -216,6 +216,73 @@ widget = VectorSpace(
|
|
|
216
216
|
)
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
+
## Distance Metrics
|
|
220
|
+
|
|
221
|
+
Compute distances and visualize similarity relationships between points.
|
|
222
|
+
|
|
223
|
+
### Supported Metrics
|
|
224
|
+
|
|
225
|
+
| Metric | Description |
|
|
226
|
+
|--------|-------------|
|
|
227
|
+
| `euclidean` | Straight-line distance (L2 norm) |
|
|
228
|
+
| `cosine` | Angle-based distance (1 - cosine similarity) |
|
|
229
|
+
| `manhattan` | Sum of absolute differences (L1 norm) |
|
|
230
|
+
| `dot_product` | Negative dot product (higher = closer) |
|
|
231
|
+
|
|
232
|
+
### Color by Distance
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
# Color points by distance from a reference
|
|
236
|
+
widget.color_by_distance("point_a")
|
|
237
|
+
widget.color_by_distance("point_a", metric="cosine")
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Find Neighbors
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
# Find k nearest neighbors
|
|
244
|
+
neighbors = widget.find_neighbors("point_a", k=5)
|
|
245
|
+
# Returns: [("point_b", 0.1), ("point_c", 0.2), ...]
|
|
246
|
+
|
|
247
|
+
# Find neighbors within distance threshold
|
|
248
|
+
neighbors = widget.find_neighbors("point_a", threshold=0.5)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Show Connections
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# Draw lines to k-nearest neighbors
|
|
255
|
+
widget.show_neighbors("point_a", k=5)
|
|
256
|
+
|
|
257
|
+
# Draw lines to all points within threshold
|
|
258
|
+
widget.show_neighbors("point_a", threshold=0.3)
|
|
259
|
+
|
|
260
|
+
# Manual connection settings
|
|
261
|
+
widget = VectorSpace(
|
|
262
|
+
points=data,
|
|
263
|
+
show_connections=True,
|
|
264
|
+
k_neighbors=3,
|
|
265
|
+
distance_metric="cosine",
|
|
266
|
+
connection_color="#00ff00",
|
|
267
|
+
connection_opacity=0.5,
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Compute Distances
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
# Get distances from reference to all points
|
|
275
|
+
distances = widget.compute_distances("point_a")
|
|
276
|
+
# Returns: {"point_b": 0.1, "point_c": 0.5, ...}
|
|
277
|
+
|
|
278
|
+
# Use high-dimensional vectors (not just x,y,z)
|
|
279
|
+
distances = widget.compute_distances(
|
|
280
|
+
"point_a",
|
|
281
|
+
metric="cosine",
|
|
282
|
+
vector_field="embedding" # Use full embedding vector
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
219
286
|
## Export
|
|
220
287
|
|
|
221
288
|
```python
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.12"
|
|
3
|
+
# dependencies = [
|
|
4
|
+
# "anywidget-vector==0.2.0",
|
|
5
|
+
# "marimo",
|
|
6
|
+
# ]
|
|
7
|
+
# ///
|
|
8
|
+
import marimo
|
|
9
|
+
|
|
10
|
+
__generated_with = "0.18.4"
|
|
11
|
+
app = marimo.App(width="medium")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.cell
|
|
15
|
+
def _():
|
|
16
|
+
import marimo as mo
|
|
17
|
+
|
|
18
|
+
return (mo,)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@app.cell
|
|
22
|
+
def _():
|
|
23
|
+
import random
|
|
24
|
+
|
|
25
|
+
from anywidget_vector import VectorSpace
|
|
26
|
+
|
|
27
|
+
return VectorSpace, random
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.cell(hide_code=True)
|
|
31
|
+
def _(mo):
|
|
32
|
+
mo.md("""
|
|
33
|
+
# anywidget-vector Demo
|
|
34
|
+
|
|
35
|
+
Interactive 3D vector visualization with distance metrics and query UI.
|
|
36
|
+
""")
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.cell
|
|
41
|
+
def _(VectorSpace, random):
|
|
42
|
+
# Generate sample clustered data
|
|
43
|
+
random.seed(42)
|
|
44
|
+
|
|
45
|
+
def generate_cluster(center, n=20, spread=0.15):
|
|
46
|
+
points = []
|
|
47
|
+
for i in range(n):
|
|
48
|
+
points.append(
|
|
49
|
+
{
|
|
50
|
+
"x": center[0] + random.gauss(0, spread),
|
|
51
|
+
"y": center[1] + random.gauss(0, spread),
|
|
52
|
+
"z": center[2] + random.gauss(0, spread),
|
|
53
|
+
"cluster": center[3],
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
return points
|
|
57
|
+
|
|
58
|
+
# Create 4 clusters
|
|
59
|
+
clusters = [
|
|
60
|
+
(0.3, 0.3, 0.3, "A"),
|
|
61
|
+
(0.7, 0.3, 0.7, "B"),
|
|
62
|
+
(0.3, 0.7, 0.7, "C"),
|
|
63
|
+
(0.7, 0.7, 0.3, "D"),
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
points = []
|
|
67
|
+
for center in clusters:
|
|
68
|
+
points.extend(generate_cluster(center))
|
|
69
|
+
|
|
70
|
+
# Add IDs and labels
|
|
71
|
+
for i, p in enumerate(points):
|
|
72
|
+
p["id"] = f"point_{i}"
|
|
73
|
+
p["label"] = f"Point {i} ({p['cluster']})"
|
|
74
|
+
p["importance"] = random.random()
|
|
75
|
+
|
|
76
|
+
# Create widget with color by cluster
|
|
77
|
+
widget = VectorSpace(
|
|
78
|
+
points=points,
|
|
79
|
+
color_field="cluster",
|
|
80
|
+
size_field="importance",
|
|
81
|
+
size_range=[0.02, 0.06],
|
|
82
|
+
width=800,
|
|
83
|
+
height=500,
|
|
84
|
+
background="#0f0f1a",
|
|
85
|
+
)
|
|
86
|
+
widget
|
|
87
|
+
return points, widget
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.cell(hide_code=True)
|
|
91
|
+
def _(mo):
|
|
92
|
+
mo.md("""
|
|
93
|
+
## Distance Features
|
|
94
|
+
|
|
95
|
+
Click on a point above, then run the cells below to explore distance metrics.
|
|
96
|
+
""")
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@app.cell
|
|
101
|
+
def _(widget):
|
|
102
|
+
# Get the currently selected point
|
|
103
|
+
selected = widget.selected_points
|
|
104
|
+
selected_id = selected[0] if selected else "point_0"
|
|
105
|
+
f"Selected point: {selected_id}"
|
|
106
|
+
return (selected_id,)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@app.cell
|
|
110
|
+
def _(selected_id, widget):
|
|
111
|
+
# Find 5 nearest neighbors using Euclidean distance
|
|
112
|
+
neighbors_euclidean = widget.find_neighbors(selected_id, k=5, metric="euclidean")
|
|
113
|
+
neighbors_euclidean
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@app.cell
|
|
118
|
+
def _(selected_id, widget):
|
|
119
|
+
# Find 5 nearest neighbors using Cosine distance
|
|
120
|
+
neighbors_cosine = widget.find_neighbors(selected_id, k=5, metric="cosine")
|
|
121
|
+
neighbors_cosine
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@app.cell
|
|
126
|
+
def _(VectorSpace, points):
|
|
127
|
+
# Create a second widget showing neighbor connections
|
|
128
|
+
widget2 = VectorSpace(
|
|
129
|
+
points=points,
|
|
130
|
+
color_field="cluster",
|
|
131
|
+
width=800,
|
|
132
|
+
height=500,
|
|
133
|
+
background="#0f0f1a",
|
|
134
|
+
# Enable k-nearest neighbor connections
|
|
135
|
+
show_connections=True,
|
|
136
|
+
k_neighbors=3,
|
|
137
|
+
distance_metric="euclidean",
|
|
138
|
+
connection_color="#44ff88",
|
|
139
|
+
connection_opacity=0.4,
|
|
140
|
+
)
|
|
141
|
+
widget2
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@app.cell(hide_code=True)
|
|
146
|
+
def _(mo):
|
|
147
|
+
mo.md("""
|
|
148
|
+
## Color by Distance
|
|
149
|
+
|
|
150
|
+
The widget below colors points by distance from a reference point.
|
|
151
|
+
""")
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@app.cell
|
|
156
|
+
def _(VectorSpace, points):
|
|
157
|
+
# Create widget and color by distance from first point
|
|
158
|
+
widget3 = VectorSpace(
|
|
159
|
+
points=[dict(p) for p in points], # Copy points
|
|
160
|
+
width=800,
|
|
161
|
+
height=500,
|
|
162
|
+
background="#0f0f1a",
|
|
163
|
+
color_scale="plasma",
|
|
164
|
+
)
|
|
165
|
+
widget3.color_by_distance("point_0", metric="euclidean")
|
|
166
|
+
widget3.show_neighbors("point_0", k=5)
|
|
167
|
+
widget3
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@app.cell(hide_code=True)
|
|
172
|
+
def _(mo):
|
|
173
|
+
mo.md("""
|
|
174
|
+
## Different Shapes
|
|
175
|
+
|
|
176
|
+
Using shapes to encode an additional dimension.
|
|
177
|
+
""")
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@app.cell
|
|
182
|
+
def _(VectorSpace, points):
|
|
183
|
+
# Create widget with shape mapping
|
|
184
|
+
widget4 = VectorSpace(
|
|
185
|
+
points=points,
|
|
186
|
+
color_field="cluster",
|
|
187
|
+
shape_field="cluster",
|
|
188
|
+
shape_map={
|
|
189
|
+
"A": "sphere",
|
|
190
|
+
"B": "cube",
|
|
191
|
+
"C": "cone",
|
|
192
|
+
"D": "octahedron",
|
|
193
|
+
},
|
|
194
|
+
size_range=[0.04, 0.04], # Fixed size
|
|
195
|
+
width=800,
|
|
196
|
+
height=500,
|
|
197
|
+
background="#0f0f1a",
|
|
198
|
+
)
|
|
199
|
+
widget4
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@app.cell(hide_code=True)
|
|
204
|
+
def _(mo):
|
|
205
|
+
mo.md("""
|
|
206
|
+
## Query UI
|
|
207
|
+
|
|
208
|
+
The widget below shows the query interface for vector database backends.
|
|
209
|
+
Enable `show_query_input=True` to show the toolbar.
|
|
210
|
+
|
|
211
|
+
**Supported backends:**
|
|
212
|
+
- Browser-side (REST API): Qdrant, Pinecone, Weaviate
|
|
213
|
+
- Python-side: Chroma, LanceDB, Grafeo
|
|
214
|
+
|
|
215
|
+
**Query types:**
|
|
216
|
+
- Text Search (requires embedding API)
|
|
217
|
+
- Find Similar (by vector ID)
|
|
218
|
+
- Raw Vector ([0.1, 0.2, ...])
|
|
219
|
+
- Filter (JSON filter expressions)
|
|
220
|
+
""")
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@app.cell
|
|
225
|
+
def _(VectorSpace, points):
|
|
226
|
+
# Create widget with query UI enabled
|
|
227
|
+
widget5 = VectorSpace(
|
|
228
|
+
points=points,
|
|
229
|
+
color_field="cluster",
|
|
230
|
+
width=800,
|
|
231
|
+
height=500,
|
|
232
|
+
background="#0f0f1a",
|
|
233
|
+
# Enable query UI
|
|
234
|
+
show_query_input=True,
|
|
235
|
+
show_settings=True,
|
|
236
|
+
# Default to Qdrant backend
|
|
237
|
+
backend="qdrant",
|
|
238
|
+
)
|
|
239
|
+
widget5
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@app.cell(hide_code=True)
|
|
244
|
+
def _(mo):
|
|
245
|
+
mo.md("""
|
|
246
|
+
### Python-side Backend Example
|
|
247
|
+
|
|
248
|
+
For Python-side backends like Chroma, configure the client directly:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
import chromadb
|
|
252
|
+
|
|
253
|
+
client = chromadb.Client()
|
|
254
|
+
collection = client.get_or_create_collection("my_vectors")
|
|
255
|
+
|
|
256
|
+
widget = VectorSpace(
|
|
257
|
+
show_query_input=True,
|
|
258
|
+
show_settings=True,
|
|
259
|
+
)
|
|
260
|
+
widget.set_backend("chroma", collection)
|
|
261
|
+
|
|
262
|
+
# Optional: set custom embedding function for text search
|
|
263
|
+
def my_embed(text):
|
|
264
|
+
# Your embedding logic here
|
|
265
|
+
return [0.1, 0.2, 0.3, ...]
|
|
266
|
+
|
|
267
|
+
widget.set_embedding(my_embed)
|
|
268
|
+
```
|
|
269
|
+
""")
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
app.run()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "anywidget-vector"
|
|
3
|
-
version = "0.1
|
|
4
|
-
description = "Interactive vector visualization
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "Interactive 3D vector visualization with query UI for vector databases"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Apache-2.0" }
|
|
7
7
|
requires-python = ">=3.12"
|
|
@@ -28,7 +28,7 @@ dependencies = [
|
|
|
28
28
|
dev = [
|
|
29
29
|
"prek>=0.3.1",
|
|
30
30
|
"pytest>=9.0.2",
|
|
31
|
-
"ruff>=0.
|
|
31
|
+
"ruff>=0.15.0",
|
|
32
32
|
"ty>=0.0.14",
|
|
33
33
|
"marimo>=0.19.7",
|
|
34
34
|
]
|