pgvector 0.4.0__tar.gz → 0.4.2__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.
- {pgvector-0.4.0/pgvector.egg-info → pgvector-0.4.2}/PKG-INFO +40 -8
- pgvector-0.4.0/PKG-INFO → pgvector-0.4.2/README.md +36 -17
- pgvector-0.4.2/pgvector/django/extensions.py +11 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sparsevec.py +1 -1
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/bit.py +12 -0
- pgvector-0.4.0/README.md → pgvector-0.4.2/pgvector.egg-info/PKG-INFO +49 -5
- {pgvector-0.4.0 → pgvector-0.4.2}/pyproject.toml +2 -2
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_sparse_vector.py +19 -1
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_sqlalchemy.py +23 -4
- pgvector-0.4.0/pgvector/django/extensions.py +0 -6
- {pgvector-0.4.0 → pgvector-0.4.2}/LICENSE.txt +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/asyncpg/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/asyncpg/register.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/bit.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/bit.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/functions.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/indexes.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/sparsevec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/django/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/peewee/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/peewee/bit.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/peewee/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/peewee/sparsevec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/peewee/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/pg8000/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/pg8000/register.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/bit.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/register.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/sparsevec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg2/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg2/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg2/register.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg2/sparsevec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/psycopg2/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/functions.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/halfvec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/sparsevec.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/sqlalchemy/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/utils/__init__.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector/vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector.egg-info/SOURCES.txt +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector.egg-info/dependency_links.txt +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector.egg-info/requires.txt +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/pgvector.egg-info/top_level.txt +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/setup.cfg +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_asyncpg.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_bit.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_django.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_half_vector.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_peewee.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_pg8000.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_psycopg.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_psycopg2.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_sqlmodel.py +0 -0
- {pgvector-0.4.0 → pgvector-0.4.2}/tests/test_vector.py +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pgvector
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: pgvector support for Python
|
|
5
5
|
Author-email: Andrew Kane <andrew@ankane.org>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/pgvector/pgvector-python
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
11
|
Requires-Dist: numpy
|
|
12
|
+
Dynamic: license-file
|
|
12
13
|
|
|
13
14
|
# pgvector-python
|
|
14
15
|
|
|
@@ -39,6 +40,7 @@ And follow the instructions for your database library:
|
|
|
39
40
|
|
|
40
41
|
Or check out some examples:
|
|
41
42
|
|
|
43
|
+
- [Retrieval-augmented generation](https://github.com/pgvector/pgvector-python/blob/master/examples/rag/example.py) with Ollama
|
|
42
44
|
- [Embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/openai/example.py) with OpenAI
|
|
43
45
|
- [Binary embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/cohere/example.py) with Cohere
|
|
44
46
|
- [Sentence embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/sentence_transformers/example.py) with SentenceTransformers
|
|
@@ -270,7 +272,6 @@ index = Index(
|
|
|
270
272
|
'my_index',
|
|
271
273
|
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
272
274
|
postgresql_using='hnsw',
|
|
273
|
-
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
274
275
|
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
275
276
|
)
|
|
276
277
|
```
|
|
@@ -282,6 +283,37 @@ order = func.cast(Item.embedding, HALFVEC(3)).l2_distance([3, 1, 2])
|
|
|
282
283
|
session.scalars(select(Item).order_by(order).limit(5))
|
|
283
284
|
```
|
|
284
285
|
|
|
286
|
+
#### Binary Quantization
|
|
287
|
+
|
|
288
|
+
Use expression indexing for binary quantization
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from pgvector.sqlalchemy import BIT
|
|
292
|
+
from sqlalchemy.sql import func
|
|
293
|
+
|
|
294
|
+
index = Index(
|
|
295
|
+
'my_index',
|
|
296
|
+
func.cast(func.binary_quantize(Item.embedding), BIT(3)).label('embedding'),
|
|
297
|
+
postgresql_using='hnsw',
|
|
298
|
+
postgresql_ops={'embedding': 'bit_hamming_ops'}
|
|
299
|
+
)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Get the nearest neighbors by Hamming distance
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
306
|
+
session.scalars(select(Item).order_by(order).limit(5))
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Re-rank by the original vectors for better recall
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
313
|
+
subquery = session.query(Item).order_by(order).limit(20).subquery()
|
|
314
|
+
session.scalars(select(subquery).order_by(subquery.c.embedding.cosine_distance([3, -1, 2])).limit(5))
|
|
315
|
+
```
|
|
316
|
+
|
|
285
317
|
#### Arrays
|
|
286
318
|
|
|
287
319
|
Add an array column
|
|
@@ -420,7 +452,7 @@ Enable the extension
|
|
|
420
452
|
conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
421
453
|
```
|
|
422
454
|
|
|
423
|
-
Register the
|
|
455
|
+
Register the types with your connection
|
|
424
456
|
|
|
425
457
|
```python
|
|
426
458
|
from pgvector.psycopg import register_vector
|
|
@@ -483,7 +515,7 @@ cur = conn.cursor()
|
|
|
483
515
|
cur.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
484
516
|
```
|
|
485
517
|
|
|
486
|
-
Register the
|
|
518
|
+
Register the types with your connection or cursor
|
|
487
519
|
|
|
488
520
|
```python
|
|
489
521
|
from pgvector.psycopg2 import register_vector
|
|
@@ -529,7 +561,7 @@ Enable the extension
|
|
|
529
561
|
await conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
530
562
|
```
|
|
531
563
|
|
|
532
|
-
Register the
|
|
564
|
+
Register the types with your connection
|
|
533
565
|
|
|
534
566
|
```python
|
|
535
567
|
from pgvector.asyncpg import register_vector
|
|
@@ -583,7 +615,7 @@ Enable the extension
|
|
|
583
615
|
conn.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
584
616
|
```
|
|
585
617
|
|
|
586
|
-
Register the
|
|
618
|
+
Register the types with your connection
|
|
587
619
|
|
|
588
620
|
```python
|
|
589
621
|
from pgvector.pg8000 import register_vector
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.2
|
|
2
|
-
Name: pgvector
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: pgvector support for Python
|
|
5
|
-
Author-email: Andrew Kane <andrew@ankane.org>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/pgvector/pgvector-python
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE.txt
|
|
11
|
-
Requires-Dist: numpy
|
|
12
|
-
|
|
13
1
|
# pgvector-python
|
|
14
2
|
|
|
15
3
|
[pgvector](https://github.com/pgvector/pgvector) support for Python
|
|
@@ -39,6 +27,7 @@ And follow the instructions for your database library:
|
|
|
39
27
|
|
|
40
28
|
Or check out some examples:
|
|
41
29
|
|
|
30
|
+
- [Retrieval-augmented generation](https://github.com/pgvector/pgvector-python/blob/master/examples/rag/example.py) with Ollama
|
|
42
31
|
- [Embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/openai/example.py) with OpenAI
|
|
43
32
|
- [Binary embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/cohere/example.py) with Cohere
|
|
44
33
|
- [Sentence embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/sentence_transformers/example.py) with SentenceTransformers
|
|
@@ -270,7 +259,6 @@ index = Index(
|
|
|
270
259
|
'my_index',
|
|
271
260
|
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
272
261
|
postgresql_using='hnsw',
|
|
273
|
-
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
274
262
|
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
275
263
|
)
|
|
276
264
|
```
|
|
@@ -282,6 +270,37 @@ order = func.cast(Item.embedding, HALFVEC(3)).l2_distance([3, 1, 2])
|
|
|
282
270
|
session.scalars(select(Item).order_by(order).limit(5))
|
|
283
271
|
```
|
|
284
272
|
|
|
273
|
+
#### Binary Quantization
|
|
274
|
+
|
|
275
|
+
Use expression indexing for binary quantization
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
from pgvector.sqlalchemy import BIT
|
|
279
|
+
from sqlalchemy.sql import func
|
|
280
|
+
|
|
281
|
+
index = Index(
|
|
282
|
+
'my_index',
|
|
283
|
+
func.cast(func.binary_quantize(Item.embedding), BIT(3)).label('embedding'),
|
|
284
|
+
postgresql_using='hnsw',
|
|
285
|
+
postgresql_ops={'embedding': 'bit_hamming_ops'}
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Get the nearest neighbors by Hamming distance
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
293
|
+
session.scalars(select(Item).order_by(order).limit(5))
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Re-rank by the original vectors for better recall
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
300
|
+
subquery = session.query(Item).order_by(order).limit(20).subquery()
|
|
301
|
+
session.scalars(select(subquery).order_by(subquery.c.embedding.cosine_distance([3, -1, 2])).limit(5))
|
|
302
|
+
```
|
|
303
|
+
|
|
285
304
|
#### Arrays
|
|
286
305
|
|
|
287
306
|
Add an array column
|
|
@@ -420,7 +439,7 @@ Enable the extension
|
|
|
420
439
|
conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
421
440
|
```
|
|
422
441
|
|
|
423
|
-
Register the
|
|
442
|
+
Register the types with your connection
|
|
424
443
|
|
|
425
444
|
```python
|
|
426
445
|
from pgvector.psycopg import register_vector
|
|
@@ -483,7 +502,7 @@ cur = conn.cursor()
|
|
|
483
502
|
cur.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
484
503
|
```
|
|
485
504
|
|
|
486
|
-
Register the
|
|
505
|
+
Register the types with your connection or cursor
|
|
487
506
|
|
|
488
507
|
```python
|
|
489
508
|
from pgvector.psycopg2 import register_vector
|
|
@@ -529,7 +548,7 @@ Enable the extension
|
|
|
529
548
|
await conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
530
549
|
```
|
|
531
550
|
|
|
532
|
-
Register the
|
|
551
|
+
Register the types with your connection
|
|
533
552
|
|
|
534
553
|
```python
|
|
535
554
|
from pgvector.asyncpg import register_vector
|
|
@@ -583,7 +602,7 @@ Enable the extension
|
|
|
583
602
|
conn.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
584
603
|
```
|
|
585
604
|
|
|
586
|
-
Register the
|
|
605
|
+
Register the types with your connection
|
|
587
606
|
|
|
588
607
|
```python
|
|
589
608
|
from pgvector.pg8000 import register_vector
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django import VERSION
|
|
2
|
+
from django.contrib.postgres.operations import CreateExtension
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class VectorExtension(CreateExtension):
|
|
6
|
+
if VERSION[0] >= 6:
|
|
7
|
+
def __init__(self, hints=None):
|
|
8
|
+
super().__init__('vector', hints=hints)
|
|
9
|
+
else:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.name = 'vector'
|
|
@@ -14,6 +14,18 @@ class BIT(UserDefinedType):
|
|
|
14
14
|
return 'BIT'
|
|
15
15
|
return 'BIT(%d)' % self.length
|
|
16
16
|
|
|
17
|
+
def bind_processor(self, dialect):
|
|
18
|
+
if dialect.__class__.__name__ == 'PGDialect_asyncpg':
|
|
19
|
+
import asyncpg
|
|
20
|
+
|
|
21
|
+
def process(value):
|
|
22
|
+
if isinstance(value, str):
|
|
23
|
+
return asyncpg.BitString(value)
|
|
24
|
+
return value
|
|
25
|
+
return process
|
|
26
|
+
else:
|
|
27
|
+
return super().bind_processor(dialect)
|
|
28
|
+
|
|
17
29
|
class comparator_factory(UserDefinedType.Comparator):
|
|
18
30
|
def hamming_distance(self, other):
|
|
19
31
|
return self.op('<~>', return_type=Float)(other)
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pgvector
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: pgvector support for Python
|
|
5
|
+
Author-email: Andrew Kane <andrew@ankane.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pgvector/pgvector-python
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
1
14
|
# pgvector-python
|
|
2
15
|
|
|
3
16
|
[pgvector](https://github.com/pgvector/pgvector) support for Python
|
|
@@ -27,6 +40,7 @@ And follow the instructions for your database library:
|
|
|
27
40
|
|
|
28
41
|
Or check out some examples:
|
|
29
42
|
|
|
43
|
+
- [Retrieval-augmented generation](https://github.com/pgvector/pgvector-python/blob/master/examples/rag/example.py) with Ollama
|
|
30
44
|
- [Embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/openai/example.py) with OpenAI
|
|
31
45
|
- [Binary embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/cohere/example.py) with Cohere
|
|
32
46
|
- [Sentence embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/sentence_transformers/example.py) with SentenceTransformers
|
|
@@ -258,7 +272,6 @@ index = Index(
|
|
|
258
272
|
'my_index',
|
|
259
273
|
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
260
274
|
postgresql_using='hnsw',
|
|
261
|
-
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
262
275
|
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
263
276
|
)
|
|
264
277
|
```
|
|
@@ -270,6 +283,37 @@ order = func.cast(Item.embedding, HALFVEC(3)).l2_distance([3, 1, 2])
|
|
|
270
283
|
session.scalars(select(Item).order_by(order).limit(5))
|
|
271
284
|
```
|
|
272
285
|
|
|
286
|
+
#### Binary Quantization
|
|
287
|
+
|
|
288
|
+
Use expression indexing for binary quantization
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from pgvector.sqlalchemy import BIT
|
|
292
|
+
from sqlalchemy.sql import func
|
|
293
|
+
|
|
294
|
+
index = Index(
|
|
295
|
+
'my_index',
|
|
296
|
+
func.cast(func.binary_quantize(Item.embedding), BIT(3)).label('embedding'),
|
|
297
|
+
postgresql_using='hnsw',
|
|
298
|
+
postgresql_ops={'embedding': 'bit_hamming_ops'}
|
|
299
|
+
)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Get the nearest neighbors by Hamming distance
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
306
|
+
session.scalars(select(Item).order_by(order).limit(5))
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Re-rank by the original vectors for better recall
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
order = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
313
|
+
subquery = session.query(Item).order_by(order).limit(20).subquery()
|
|
314
|
+
session.scalars(select(subquery).order_by(subquery.c.embedding.cosine_distance([3, -1, 2])).limit(5))
|
|
315
|
+
```
|
|
316
|
+
|
|
273
317
|
#### Arrays
|
|
274
318
|
|
|
275
319
|
Add an array column
|
|
@@ -408,7 +452,7 @@ Enable the extension
|
|
|
408
452
|
conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
409
453
|
```
|
|
410
454
|
|
|
411
|
-
Register the
|
|
455
|
+
Register the types with your connection
|
|
412
456
|
|
|
413
457
|
```python
|
|
414
458
|
from pgvector.psycopg import register_vector
|
|
@@ -471,7 +515,7 @@ cur = conn.cursor()
|
|
|
471
515
|
cur.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
472
516
|
```
|
|
473
517
|
|
|
474
|
-
Register the
|
|
518
|
+
Register the types with your connection or cursor
|
|
475
519
|
|
|
476
520
|
```python
|
|
477
521
|
from pgvector.psycopg2 import register_vector
|
|
@@ -517,7 +561,7 @@ Enable the extension
|
|
|
517
561
|
await conn.execute('CREATE EXTENSION IF NOT EXISTS vector')
|
|
518
562
|
```
|
|
519
563
|
|
|
520
|
-
Register the
|
|
564
|
+
Register the types with your connection
|
|
521
565
|
|
|
522
566
|
```python
|
|
523
567
|
from pgvector.asyncpg import register_vector
|
|
@@ -571,7 +615,7 @@ Enable the extension
|
|
|
571
615
|
conn.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
572
616
|
```
|
|
573
617
|
|
|
574
|
-
Register the
|
|
618
|
+
Register the types with your connection
|
|
575
619
|
|
|
576
620
|
```python
|
|
577
621
|
from pgvector.pg8000 import register_vector
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pgvector"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "pgvector support for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
11
11
|
{name = "Andrew Kane", email = "andrew@ankane.org"}
|
|
12
12
|
]
|
|
13
|
-
license =
|
|
13
|
+
license = "MIT"
|
|
14
14
|
requires-python = ">= 3.9"
|
|
15
15
|
dependencies = [
|
|
16
16
|
"numpy"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from pgvector import SparseVector
|
|
3
3
|
import pytest
|
|
4
|
-
from scipy.sparse import coo_array
|
|
4
|
+
from scipy.sparse import coo_array, coo_matrix, csr_array, csr_matrix
|
|
5
5
|
from struct import pack
|
|
6
6
|
|
|
7
7
|
|
|
@@ -43,12 +43,30 @@ class TestSparseVector:
|
|
|
43
43
|
SparseVector(coo_array(np.array([1, 0, 2, 0, 3, 0])), 6)
|
|
44
44
|
assert str(error.value) == 'extra argument'
|
|
45
45
|
|
|
46
|
+
def test_coo_matrix(self):
|
|
47
|
+
mat = coo_matrix(np.array([1, 0, 2, 0, 3, 0]))
|
|
48
|
+
vec = SparseVector(mat)
|
|
49
|
+
assert vec.to_list() == [1, 0, 2, 0, 3, 0]
|
|
50
|
+
assert vec.indices() == [0, 2, 4]
|
|
51
|
+
|
|
46
52
|
def test_dok_array(self):
|
|
47
53
|
arr = coo_array(np.array([1, 0, 2, 0, 3, 0])).todok()
|
|
48
54
|
vec = SparseVector(arr)
|
|
49
55
|
assert vec.to_list() == [1, 0, 2, 0, 3, 0]
|
|
50
56
|
assert vec.indices() == [0, 2, 4]
|
|
51
57
|
|
|
58
|
+
def test_csr_array(self):
|
|
59
|
+
arr = csr_array(np.array([[1, 0, 2, 0, 3, 0]]))
|
|
60
|
+
vec = SparseVector(arr)
|
|
61
|
+
assert vec.to_list() == [1, 0, 2, 0, 3, 0]
|
|
62
|
+
assert vec.indices() == [0, 2, 4]
|
|
63
|
+
|
|
64
|
+
def test_csr_matrix(self):
|
|
65
|
+
mat = csr_matrix(np.array([1, 0, 2, 0, 3, 0]))
|
|
66
|
+
vec = SparseVector(mat)
|
|
67
|
+
assert vec.to_list() == [1, 0, 2, 0, 3, 0]
|
|
68
|
+
assert vec.indices() == [0, 2, 4]
|
|
69
|
+
|
|
52
70
|
def test_repr(self):
|
|
53
71
|
assert repr(SparseVector([1, 0, 2, 0, 3, 0])) == 'SparseVector({0: 1.0, 2: 2.0, 4: 3.0}, 6)'
|
|
54
72
|
assert str(SparseVector([1, 0, 2, 0, 3, 0])) == 'SparseVector({0: 1.0, 2: 2.0, 4: 3.0}, 6)'
|
|
@@ -43,7 +43,7 @@ if sqlalchemy_version > 1:
|
|
|
43
43
|
psycopg_async_type_engine = create_async_engine('postgresql+psycopg://localhost/pgvector_python_test')
|
|
44
44
|
|
|
45
45
|
@event.listens_for(psycopg_async_type_engine.sync_engine, "connect")
|
|
46
|
-
def
|
|
46
|
+
def psycopg_async_connect(dbapi_connection, connection_record):
|
|
47
47
|
from pgvector.psycopg import register_vector_async
|
|
48
48
|
dbapi_connection.run_async(register_vector_async)
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ if sqlalchemy_version > 1:
|
|
|
51
51
|
asyncpg_type_engine = create_async_engine('postgresql+asyncpg://localhost/pgvector_python_test')
|
|
52
52
|
|
|
53
53
|
@event.listens_for(asyncpg_type_engine.sync_engine, "connect")
|
|
54
|
-
def
|
|
54
|
+
def asyncpg_connect(dbapi_connection, connection_record):
|
|
55
55
|
from pgvector.asyncpg import register_vector
|
|
56
56
|
dbapi_connection.run_async(register_vector)
|
|
57
57
|
|
|
@@ -103,7 +103,6 @@ half_precision_index = Index(
|
|
|
103
103
|
'sqlalchemy_orm_half_precision_index',
|
|
104
104
|
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
105
105
|
postgresql_using='hnsw',
|
|
106
|
-
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
107
106
|
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
108
107
|
)
|
|
109
108
|
half_precision_index.create(setup_engine)
|
|
@@ -112,7 +111,6 @@ binary_quantize_index = Index(
|
|
|
112
111
|
'sqlalchemy_orm_binary_quantize_index',
|
|
113
112
|
func.cast(func.binary_quantize(Item.embedding), BIT(3)).label('embedding'),
|
|
114
113
|
postgresql_using='hnsw',
|
|
115
|
-
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
116
114
|
postgresql_ops={'embedding': 'bit_hamming_ops'}
|
|
117
115
|
)
|
|
118
116
|
binary_quantize_index.create(setup_engine)
|
|
@@ -528,6 +526,22 @@ class TestSqlalchemy:
|
|
|
528
526
|
items = session.query(Item).order_by(distance).all()
|
|
529
527
|
assert [v.id for v in items] == [2, 3, 1]
|
|
530
528
|
|
|
529
|
+
def test_binary_quantize_reranking(self, engine):
|
|
530
|
+
# recreate index (could also vacuum table)
|
|
531
|
+
binary_quantize_index.drop(setup_engine)
|
|
532
|
+
binary_quantize_index.create(setup_engine)
|
|
533
|
+
|
|
534
|
+
with Session(engine) as session:
|
|
535
|
+
session.add(Item(id=1, embedding=[-1, -2, -3]))
|
|
536
|
+
session.add(Item(id=2, embedding=[1, -2, 3]))
|
|
537
|
+
session.add(Item(id=3, embedding=[1, 2, 3]))
|
|
538
|
+
session.commit()
|
|
539
|
+
|
|
540
|
+
distance = func.cast(func.binary_quantize(Item.embedding), BIT(3)).hamming_distance(func.binary_quantize(func.cast([3, -1, 2], VECTOR(3))))
|
|
541
|
+
subquery = session.query(Item).order_by(distance).limit(20).subquery()
|
|
542
|
+
items = session.query(subquery).order_by(subquery.c.embedding.cosine_distance([3, -1, 2])).limit(5).all()
|
|
543
|
+
assert [v.id for v in items] == [2, 3, 1]
|
|
544
|
+
|
|
531
545
|
|
|
532
546
|
@pytest.mark.parametrize('engine', array_engines)
|
|
533
547
|
class TestSqlalchemyArray:
|
|
@@ -596,6 +610,11 @@ class TestSqlalchemyAsync:
|
|
|
596
610
|
item = await session.get(Item, 1)
|
|
597
611
|
assert item.binary_embedding == embedding
|
|
598
612
|
|
|
613
|
+
if engine == asyncpg_engine:
|
|
614
|
+
session.add(Item(id=2, binary_embedding='101'))
|
|
615
|
+
item = await session.get(Item, 2)
|
|
616
|
+
assert item.binary_embedding == embedding
|
|
617
|
+
|
|
599
618
|
await engine.dispose()
|
|
600
619
|
|
|
601
620
|
@pytest.mark.asyncio
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|