about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pgvector
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/pgvector')
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/asyncpg/__init__.py9
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/asyncpg/register.py31
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/__init__.py26
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/bit.py32
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/extensions.py6
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/functions.py55
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/halfvec.py60
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/indexes.py46
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/sparsevec.py55
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/django/vector.py73
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/peewee/__init__.py14
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/peewee/bit.py21
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/peewee/halfvec.py34
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/peewee/sparsevec.py34
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/peewee/vector.py34
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/__init__.py11
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/bit.py31
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/halfvec.py53
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/register.py37
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/sparsevec.py53
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg/vector.py58
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg2/__init__.py8
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg2/halfvec.py25
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg2/register.py29
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg2/sparsevec.py25
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/psycopg2/vector.py26
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/__init__.py19
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/bit.py26
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/functions.py14
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/halfvec.py51
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/sparsevec.py51
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/vector.py51
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/utils/__init__.py11
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/utils/bit.py61
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/utils/halfvec.py78
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/utils/sparsevec.py156
-rw-r--r--.venv/lib/python3.12/site-packages/pgvector/utils/vector.py78
37 files changed, 1482 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pgvector/asyncpg/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/asyncpg/__init__.py
new file mode 100644
index 00000000..543b8823
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/asyncpg/__init__.py
@@ -0,0 +1,9 @@
+from .register import register_vector
+from ..utils import Vector, HalfVector, SparseVector
+
+__all__ = [
+    'register_vector',
+    'Vector',
+    'HalfVector',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/asyncpg/register.py b/.venv/lib/python3.12/site-packages/pgvector/asyncpg/register.py
new file mode 100644
index 00000000..a3880585
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/asyncpg/register.py
@@ -0,0 +1,31 @@
+from ..utils import Vector, HalfVector, SparseVector
+
+
+async def register_vector(conn, schema='public'):
+    await conn.set_type_codec(
+        'vector',
+        schema=schema,
+        encoder=Vector._to_db_binary,
+        decoder=Vector._from_db_binary,
+        format='binary'
+    )
+
+    try:
+        await conn.set_type_codec(
+            'halfvec',
+            schema=schema,
+            encoder=HalfVector._to_db_binary,
+            decoder=HalfVector._from_db_binary,
+            format='binary'
+        )
+
+        await conn.set_type_codec(
+            'sparsevec',
+            schema=schema,
+            encoder=SparseVector._to_db_binary,
+            decoder=SparseVector._from_db_binary,
+            format='binary'
+        )
+    except ValueError as e:
+        if not str(e).startswith('unknown type:'):
+            raise e
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/django/__init__.py
new file mode 100644
index 00000000..09978a92
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/__init__.py
@@ -0,0 +1,26 @@
+from .bit import BitField
+from .extensions import VectorExtension
+from .functions import L2Distance, MaxInnerProduct, CosineDistance, L1Distance, HammingDistance, JaccardDistance
+from .halfvec import HalfVectorField
+from .indexes import IvfflatIndex, HnswIndex
+from .sparsevec import SparseVectorField
+from .vector import VectorField
+from ..utils import HalfVector, SparseVector
+
+__all__ = [
+    'VectorExtension',
+    'VectorField',
+    'HalfVectorField',
+    'BitField',
+    'SparseVectorField',
+    'IvfflatIndex',
+    'HnswIndex',
+    'L2Distance',
+    'MaxInnerProduct',
+    'CosineDistance',
+    'L1Distance',
+    'HammingDistance',
+    'JaccardDistance',
+    'HalfVector',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/bit.py b/.venv/lib/python3.12/site-packages/pgvector/django/bit.py
new file mode 100644
index 00000000..2cc847ad
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/bit.py
@@ -0,0 +1,32 @@
+from django import forms
+from django.db.models import Field
+
+
+# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
+class BitField(Field):
+    description = 'Bit string'
+
+    def __init__(self, *args, length=None, **kwargs):
+        self.length = length
+        super().__init__(*args, **kwargs)
+
+    def deconstruct(self):
+        name, path, args, kwargs = super().deconstruct()
+        if self.length is not None:
+            kwargs['length'] = self.length
+        return name, path, args, kwargs
+
+    def db_type(self, connection):
+        if self.length is None:
+            return 'bit'
+        return 'bit(%d)' % self.length
+
+    def formfield(self, **kwargs):
+        return super().formfield(form_class=BitFormField, **kwargs)
+
+
+class BitFormField(forms.CharField):
+    def to_python(self, value):
+        if isinstance(value, str) and value == '':
+            return None
+        return super().to_python(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/extensions.py b/.venv/lib/python3.12/site-packages/pgvector/django/extensions.py
new file mode 100644
index 00000000..0573f72b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/extensions.py
@@ -0,0 +1,6 @@
+from django.contrib.postgres.operations import CreateExtension
+
+
+class VectorExtension(CreateExtension):
+    def __init__(self):
+        self.name = 'vector'
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/functions.py b/.venv/lib/python3.12/site-packages/pgvector/django/functions.py
new file mode 100644
index 00000000..da9fbf83
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/functions.py
@@ -0,0 +1,55 @@
+from django.db.models import FloatField, Func, Value
+from ..utils import Vector, HalfVector, SparseVector
+
+
+class DistanceBase(Func):
+    output_field = FloatField()
+
+    def __init__(self, expression, vector, **extra):
+        if not hasattr(vector, 'resolve_expression'):
+            if isinstance(vector, HalfVector):
+                vector = Value(HalfVector._to_db(vector))
+            elif isinstance(vector, SparseVector):
+                vector = Value(SparseVector._to_db(vector))
+            else:
+                vector = Value(Vector._to_db(vector))
+        super().__init__(expression, vector, **extra)
+
+
+class BitDistanceBase(Func):
+    output_field = FloatField()
+
+    def __init__(self, expression, vector, **extra):
+        if not hasattr(vector, 'resolve_expression'):
+            vector = Value(vector)
+        super().__init__(expression, vector, **extra)
+
+
+class L2Distance(DistanceBase):
+    function = ''
+    arg_joiner = ' <-> '
+
+
+class MaxInnerProduct(DistanceBase):
+    function = ''
+    arg_joiner = ' <#> '
+
+
+class CosineDistance(DistanceBase):
+    function = ''
+    arg_joiner = ' <=> '
+
+
+class L1Distance(DistanceBase):
+    function = ''
+    arg_joiner = ' <+> '
+
+
+class HammingDistance(BitDistanceBase):
+    function = ''
+    arg_joiner = ' <~> '
+
+
+class JaccardDistance(BitDistanceBase):
+    function = ''
+    arg_joiner = ' <%%> '
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/django/halfvec.py
new file mode 100644
index 00000000..6b59a7fa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/halfvec.py
@@ -0,0 +1,60 @@
+from django import forms
+from django.db.models import Field
+from ..utils import HalfVector
+
+
+# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
+class HalfVectorField(Field):
+    description = 'Half vector'
+    empty_strings_allowed = False
+
+    def __init__(self, *args, dimensions=None, **kwargs):
+        self.dimensions = dimensions
+        super().__init__(*args, **kwargs)
+
+    def deconstruct(self):
+        name, path, args, kwargs = super().deconstruct()
+        if self.dimensions is not None:
+            kwargs['dimensions'] = self.dimensions
+        return name, path, args, kwargs
+
+    def db_type(self, connection):
+        if self.dimensions is None:
+            return 'halfvec'
+        return 'halfvec(%d)' % self.dimensions
+
+    def from_db_value(self, value, expression, connection):
+        return HalfVector._from_db(value)
+
+    def to_python(self, value):
+        if value is None or isinstance(value, HalfVector):
+            return value
+        elif isinstance(value, str):
+            return HalfVector._from_db(value)
+        else:
+            return HalfVector(value)
+
+    def get_prep_value(self, value):
+        return HalfVector._to_db(value)
+
+    def value_to_string(self, obj):
+        return self.get_prep_value(self.value_from_object(obj))
+
+    def formfield(self, **kwargs):
+        return super().formfield(form_class=HalfVectorFormField, **kwargs)
+
+
+class HalfVectorWidget(forms.TextInput):
+    def format_value(self, value):
+        if isinstance(value, HalfVector):
+            value = value.to_list()
+        return super().format_value(value)
+
+
+class HalfVectorFormField(forms.CharField):
+    widget = HalfVectorWidget
+
+    def to_python(self, value):
+        if isinstance(value, str) and value == '':
+            return None
+        return super().to_python(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/indexes.py b/.venv/lib/python3.12/site-packages/pgvector/django/indexes.py
new file mode 100644
index 00000000..5bec0eba
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/indexes.py
@@ -0,0 +1,46 @@
+from django.contrib.postgres.indexes import PostgresIndex
+
+
+class IvfflatIndex(PostgresIndex):
+    suffix = 'ivfflat'
+
+    def __init__(self, *expressions, lists=None, **kwargs):
+        self.lists = lists
+        super().__init__(*expressions, **kwargs)
+
+    def deconstruct(self):
+        path, args, kwargs = super().deconstruct()
+        if self.lists is not None:
+            kwargs['lists'] = self.lists
+        return path, args, kwargs
+
+    def get_with_params(self):
+        with_params = []
+        if self.lists is not None:
+            with_params.append('lists = %d' % self.lists)
+        return with_params
+
+
+class HnswIndex(PostgresIndex):
+    suffix = 'hnsw'
+
+    def __init__(self, *expressions, m=None, ef_construction=None, **kwargs):
+        self.m = m
+        self.ef_construction = ef_construction
+        super().__init__(*expressions, **kwargs)
+
+    def deconstruct(self):
+        path, args, kwargs = super().deconstruct()
+        if self.m is not None:
+            kwargs['m'] = self.m
+        if self.ef_construction is not None:
+            kwargs['ef_construction'] = self.ef_construction
+        return path, args, kwargs
+
+    def get_with_params(self):
+        with_params = []
+        if self.m is not None:
+            with_params.append('m = %d' % self.m)
+        if self.ef_construction is not None:
+            with_params.append('ef_construction = %d' % self.ef_construction)
+        return with_params
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/django/sparsevec.py
new file mode 100644
index 00000000..d0d2d073
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/sparsevec.py
@@ -0,0 +1,55 @@
+from django import forms
+from django.db.models import Field
+from ..utils import SparseVector
+
+
+# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
+class SparseVectorField(Field):
+    description = 'Sparse vector'
+    empty_strings_allowed = False
+
+    def __init__(self, *args, dimensions=None, **kwargs):
+        self.dimensions = dimensions
+        super().__init__(*args, **kwargs)
+
+    def deconstruct(self):
+        name, path, args, kwargs = super().deconstruct()
+        if self.dimensions is not None:
+            kwargs['dimensions'] = self.dimensions
+        return name, path, args, kwargs
+
+    def db_type(self, connection):
+        if self.dimensions is None:
+            return 'sparsevec'
+        return 'sparsevec(%d)' % self.dimensions
+
+    def from_db_value(self, value, expression, connection):
+        return SparseVector._from_db(value)
+
+    def to_python(self, value):
+        return SparseVector._from_db(value)
+
+    def get_prep_value(self, value):
+        return SparseVector._to_db(value)
+
+    def value_to_string(self, obj):
+        return self.get_prep_value(self.value_from_object(obj))
+
+    def formfield(self, **kwargs):
+        return super().formfield(form_class=SparseVectorFormField, **kwargs)
+
+
+class SparseVectorWidget(forms.TextInput):
+    def format_value(self, value):
+        if isinstance(value, SparseVector):
+            value = value.to_text()
+        return super().format_value(value)
+
+
+class SparseVectorFormField(forms.CharField):
+    widget = SparseVectorWidget
+
+    def to_python(self, value):
+        if isinstance(value, str) and value == '':
+            return None
+        return super().to_python(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/django/vector.py b/.venv/lib/python3.12/site-packages/pgvector/django/vector.py
new file mode 100644
index 00000000..a89d5408
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/django/vector.py
@@ -0,0 +1,73 @@
+from django import forms
+from django.db.models import Field
+import numpy as np
+from ..utils import Vector
+
+
+# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
+class VectorField(Field):
+    description = 'Vector'
+    empty_strings_allowed = False
+
+    def __init__(self, *args, dimensions=None, **kwargs):
+        self.dimensions = dimensions
+        super().__init__(*args, **kwargs)
+
+    def deconstruct(self):
+        name, path, args, kwargs = super().deconstruct()
+        if self.dimensions is not None:
+            kwargs['dimensions'] = self.dimensions
+        return name, path, args, kwargs
+
+    def db_type(self, connection):
+        if self.dimensions is None:
+            return 'vector'
+        return 'vector(%d)' % self.dimensions
+
+    def from_db_value(self, value, expression, connection):
+        return Vector._from_db(value)
+
+    def to_python(self, value):
+        if isinstance(value, list):
+            return np.array(value, dtype=np.float32)
+        return Vector._from_db(value)
+
+    def get_prep_value(self, value):
+        return Vector._to_db(value)
+
+    def value_to_string(self, obj):
+        return self.get_prep_value(self.value_from_object(obj))
+
+    def validate(self, value, model_instance):
+        if isinstance(value, np.ndarray):
+            value = value.tolist()
+        super().validate(value, model_instance)
+
+    def run_validators(self, value):
+        if isinstance(value, np.ndarray):
+            value = value.tolist()
+        super().run_validators(value)
+
+    def formfield(self, **kwargs):
+        return super().formfield(form_class=VectorFormField, **kwargs)
+
+
+class VectorWidget(forms.TextInput):
+    def format_value(self, value):
+        if isinstance(value, np.ndarray):
+            value = value.tolist()
+        return super().format_value(value)
+
+
+class VectorFormField(forms.CharField):
+    widget = VectorWidget
+
+    def has_changed(self, initial, data):
+        if isinstance(initial, np.ndarray):
+            initial = initial.tolist()
+        return super().has_changed(initial, data)
+
+    def to_python(self, value):
+        if isinstance(value, str) and value == '':
+            return None
+        return super().to_python(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/peewee/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/peewee/__init__.py
new file mode 100644
index 00000000..945e0dca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/peewee/__init__.py
@@ -0,0 +1,14 @@
+from .bit import FixedBitField
+from .halfvec import HalfVectorField
+from .sparsevec import SparseVectorField
+from .vector import VectorField
+from ..utils import HalfVector, SparseVector
+
+__all__ = [
+    'VectorField',
+    'HalfVectorField',
+    'FixedBitField',
+    'SparseVectorField',
+    'HalfVector',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/peewee/bit.py b/.venv/lib/python3.12/site-packages/pgvector/peewee/bit.py
new file mode 100644
index 00000000..ee5f12fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/peewee/bit.py
@@ -0,0 +1,21 @@
+from peewee import Expression, Field
+
+
+class FixedBitField(Field):
+    field_type = 'bit'
+
+    def __init__(self, max_length=None, *args, **kwargs):
+        self.max_length = max_length
+        super(FixedBitField, self).__init__(*args, **kwargs)
+
+    def get_modifiers(self):
+        return self.max_length and [self.max_length] or None
+
+    def _distance(self, op, vector):
+        return Expression(lhs=self, op=op, rhs=self.to_value(vector))
+
+    def hamming_distance(self, vector):
+        return self._distance('<~>', vector)
+
+    def jaccard_distance(self, vector):
+        return self._distance('<%%>', vector)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/peewee/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/peewee/halfvec.py
new file mode 100644
index 00000000..deaa14da
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/peewee/halfvec.py
@@ -0,0 +1,34 @@
+from peewee import Expression, Field
+from ..utils import HalfVector
+
+
+class HalfVectorField(Field):
+    field_type = 'halfvec'
+
+    def __init__(self, dimensions=None, *args, **kwargs):
+        self.dimensions = dimensions
+        super(HalfVectorField, self).__init__(*args, **kwargs)
+
+    def get_modifiers(self):
+        return self.dimensions and [self.dimensions] or None
+
+    def db_value(self, value):
+        return HalfVector._to_db(value)
+
+    def python_value(self, value):
+        return HalfVector._from_db(value)
+
+    def _distance(self, op, vector):
+        return Expression(lhs=self, op=op, rhs=self.to_value(vector))
+
+    def l2_distance(self, vector):
+        return self._distance('<->', vector)
+
+    def max_inner_product(self, vector):
+        return self._distance('<#>', vector)
+
+    def cosine_distance(self, vector):
+        return self._distance('<=>', vector)
+
+    def l1_distance(self, vector):
+        return self._distance('<+>', vector)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/peewee/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/peewee/sparsevec.py
new file mode 100644
index 00000000..67f7d1b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/peewee/sparsevec.py
@@ -0,0 +1,34 @@
+from peewee import Expression, Field
+from ..utils import SparseVector
+
+
+class SparseVectorField(Field):
+    field_type = 'sparsevec'
+
+    def __init__(self, dimensions=None, *args, **kwargs):
+        self.dimensions = dimensions
+        super(SparseVectorField, self).__init__(*args, **kwargs)
+
+    def get_modifiers(self):
+        return self.dimensions and [self.dimensions] or None
+
+    def db_value(self, value):
+        return SparseVector._to_db(value)
+
+    def python_value(self, value):
+        return SparseVector._from_db(value)
+
+    def _distance(self, op, vector):
+        return Expression(lhs=self, op=op, rhs=self.to_value(vector))
+
+    def l2_distance(self, vector):
+        return self._distance('<->', vector)
+
+    def max_inner_product(self, vector):
+        return self._distance('<#>', vector)
+
+    def cosine_distance(self, vector):
+        return self._distance('<=>', vector)
+
+    def l1_distance(self, vector):
+        return self._distance('<+>', vector)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/peewee/vector.py b/.venv/lib/python3.12/site-packages/pgvector/peewee/vector.py
new file mode 100644
index 00000000..22a87e53
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/peewee/vector.py
@@ -0,0 +1,34 @@
+from peewee import Expression, Field
+from ..utils import Vector
+
+
+class VectorField(Field):
+    field_type = 'vector'
+
+    def __init__(self, dimensions=None, *args, **kwargs):
+        self.dimensions = dimensions
+        super(VectorField, self).__init__(*args, **kwargs)
+
+    def get_modifiers(self):
+        return self.dimensions and [self.dimensions] or None
+
+    def db_value(self, value):
+        return Vector._to_db(value)
+
+    def python_value(self, value):
+        return Vector._from_db(value)
+
+    def _distance(self, op, vector):
+        return Expression(lhs=self, op=op, rhs=self.to_value(vector))
+
+    def l2_distance(self, vector):
+        return self._distance('<->', vector)
+
+    def max_inner_product(self, vector):
+        return self._distance('<#>', vector)
+
+    def cosine_distance(self, vector):
+        return self._distance('<=>', vector)
+
+    def l1_distance(self, vector):
+        return self._distance('<+>', vector)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/__init__.py
new file mode 100644
index 00000000..9007c374
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/__init__.py
@@ -0,0 +1,11 @@
+from .register import register_vector, register_vector_async
+from ..utils import Bit, HalfVector, SparseVector, Vector
+
+__all__ = [
+    'register_vector',
+    'register_vector_async',
+    'Vector',
+    'HalfVector',
+    'Bit',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/bit.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/bit.py
new file mode 100644
index 00000000..f8eeb610
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/bit.py
@@ -0,0 +1,31 @@
+from psycopg.adapt import Dumper
+from psycopg.pq import Format
+from ..utils import Bit
+
+
+class BitDumper(Dumper):
+
+    format = Format.TEXT
+
+    def dump(self, obj):
+        return Bit._to_db(obj).encode('utf8')
+
+
+class BitBinaryDumper(BitDumper):
+
+    format = Format.BINARY
+
+    def dump(self, obj):
+        return Bit._to_db_binary(obj)
+
+
+def register_bit_info(context, info):
+    info.register(context)
+
+    # add oid to anonymous class for set_types
+    text_dumper = type('', (BitDumper,), {'oid': info.oid})
+    binary_dumper = type('', (BitBinaryDumper,), {'oid': info.oid})
+
+    adapters = context.adapters
+    adapters.register_dumper(Bit, text_dumper)
+    adapters.register_dumper(Bit, binary_dumper)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/halfvec.py
new file mode 100644
index 00000000..351d2cb7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/halfvec.py
@@ -0,0 +1,53 @@
+from psycopg.adapt import Loader, Dumper
+from psycopg.pq import Format
+from ..utils import HalfVector
+
+
+class HalfVectorDumper(Dumper):
+
+    format = Format.TEXT
+
+    def dump(self, obj):
+        return HalfVector._to_db(obj).encode('utf8')
+
+
+class HalfVectorBinaryDumper(HalfVectorDumper):
+
+    format = Format.BINARY
+
+    def dump(self, obj):
+        return HalfVector._to_db_binary(obj)
+
+
+class HalfVectorLoader(Loader):
+
+    format = Format.TEXT
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return HalfVector._from_db(data.decode('utf8'))
+
+
+class HalfVectorBinaryLoader(HalfVectorLoader):
+
+    format = Format.BINARY
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return HalfVector._from_db_binary(data)
+
+
+def register_halfvec_info(context, info):
+    info.register(context)
+
+    # add oid to anonymous class for set_types
+    text_dumper = type('', (HalfVectorDumper,), {'oid': info.oid})
+    binary_dumper = type('', (HalfVectorBinaryDumper,), {'oid': info.oid})
+
+    adapters = context.adapters
+    adapters.register_dumper(HalfVector, text_dumper)
+    adapters.register_dumper(HalfVector, binary_dumper)
+    adapters.register_loader(info.oid, HalfVectorLoader)
+    adapters.register_loader(info.oid, HalfVectorBinaryLoader)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/register.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/register.py
new file mode 100644
index 00000000..b93fd3ee
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/register.py
@@ -0,0 +1,37 @@
+from psycopg.types import TypeInfo
+from .bit import register_bit_info
+from .halfvec import register_halfvec_info
+from .sparsevec import register_sparsevec_info
+from .vector import register_vector_info
+
+
+def register_vector(context):
+    info = TypeInfo.fetch(context, 'vector')
+    register_vector_info(context, info)
+
+    info = TypeInfo.fetch(context, 'bit')
+    register_bit_info(context, info)
+
+    info = TypeInfo.fetch(context, 'halfvec')
+    if info is not None:
+        register_halfvec_info(context, info)
+
+    info = TypeInfo.fetch(context, 'sparsevec')
+    if info is not None:
+        register_sparsevec_info(context, info)
+
+
+async def register_vector_async(context):
+    info = await TypeInfo.fetch(context, 'vector')
+    register_vector_info(context, info)
+
+    info = await TypeInfo.fetch(context, 'bit')
+    register_bit_info(context, info)
+
+    info = await TypeInfo.fetch(context, 'halfvec')
+    if info is not None:
+        register_halfvec_info(context, info)
+
+    info = await TypeInfo.fetch(context, 'sparsevec')
+    if info is not None:
+        register_sparsevec_info(context, info)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/sparsevec.py
new file mode 100644
index 00000000..435fd067
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/sparsevec.py
@@ -0,0 +1,53 @@
+from psycopg.adapt import Loader, Dumper
+from psycopg.pq import Format
+from ..utils import SparseVector
+
+
+class SparseVectorDumper(Dumper):
+
+    format = Format.TEXT
+
+    def dump(self, obj):
+        return SparseVector._to_db(obj).encode('utf8')
+
+
+class SparseVectorBinaryDumper(SparseVectorDumper):
+
+    format = Format.BINARY
+
+    def dump(self, obj):
+        return SparseVector._to_db_binary(obj)
+
+
+class SparseVectorLoader(Loader):
+
+    format = Format.TEXT
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return SparseVector._from_db(data.decode('utf8'))
+
+
+class SparseVectorBinaryLoader(SparseVectorLoader):
+
+    format = Format.BINARY
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return SparseVector._from_db_binary(data)
+
+
+def register_sparsevec_info(context, info):
+    info.register(context)
+
+    # add oid to anonymous class for set_types
+    text_dumper = type('', (SparseVectorDumper,), {'oid': info.oid})
+    binary_dumper = type('', (SparseVectorBinaryDumper,), {'oid': info.oid})
+
+    adapters = context.adapters
+    adapters.register_dumper(SparseVector, text_dumper)
+    adapters.register_dumper(SparseVector, binary_dumper)
+    adapters.register_loader(info.oid, SparseVectorLoader)
+    adapters.register_loader(info.oid, SparseVectorBinaryLoader)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg/vector.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg/vector.py
new file mode 100644
index 00000000..0f62ca98
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg/vector.py
@@ -0,0 +1,58 @@
+import psycopg
+from psycopg.adapt import Loader, Dumper
+from psycopg.pq import Format
+from ..utils import Vector
+
+
+class VectorDumper(Dumper):
+
+    format = Format.TEXT
+
+    def dump(self, obj):
+        return Vector._to_db(obj).encode('utf8')
+
+
+class VectorBinaryDumper(VectorDumper):
+
+    format = Format.BINARY
+
+    def dump(self, obj):
+        return Vector._to_db_binary(obj)
+
+
+class VectorLoader(Loader):
+
+    format = Format.TEXT
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return Vector._from_db(data.decode('utf8'))
+
+
+class VectorBinaryLoader(VectorLoader):
+
+    format = Format.BINARY
+
+    def load(self, data):
+        if isinstance(data, memoryview):
+            data = bytes(data)
+        return Vector._from_db_binary(data)
+
+
+def register_vector_info(context, info):
+    if info is None:
+        raise psycopg.ProgrammingError('vector type not found in the database')
+    info.register(context)
+
+    # add oid to anonymous class for set_types
+    text_dumper = type('', (VectorDumper,), {'oid': info.oid})
+    binary_dumper = type('', (VectorBinaryDumper,), {'oid': info.oid})
+
+    adapters = context.adapters
+    adapters.register_dumper('numpy.ndarray', text_dumper)
+    adapters.register_dumper('numpy.ndarray', binary_dumper)
+    adapters.register_dumper(Vector, text_dumper)
+    adapters.register_dumper(Vector, binary_dumper)
+    adapters.register_loader(info.oid, VectorLoader)
+    adapters.register_loader(info.oid, VectorBinaryLoader)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg2/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/__init__.py
new file mode 100644
index 00000000..7c952951
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/__init__.py
@@ -0,0 +1,8 @@
+from .register import register_vector
+from ..utils import HalfVector, SparseVector
+
+__all__ = [
+    'register_vector',
+    'HalfVector',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg2/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/halfvec.py
new file mode 100644
index 00000000..b50e89b6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/halfvec.py
@@ -0,0 +1,25 @@
+from psycopg2.extensions import adapt, new_array_type, new_type, register_adapter, register_type
+from ..utils import HalfVector
+
+
+class HalfvecAdapter:
+    def __init__(self, value):
+        self._value = value
+
+    def getquoted(self):
+        return adapt(HalfVector._to_db(self._value)).getquoted()
+
+
+def cast_halfvec(value, cur):
+    return HalfVector._from_db(value)
+
+
+def register_halfvec_info(oid, array_oid, scope):
+    halfvec = new_type((oid,), 'HALFVEC', cast_halfvec)
+    register_type(halfvec, scope)
+
+    if array_oid is not None:
+        halfvecarray = new_array_type((array_oid,), 'HALFVECARRAY', halfvec)
+        register_type(halfvecarray, scope)
+
+    register_adapter(HalfVector, HalfvecAdapter)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg2/register.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/register.py
new file mode 100644
index 00000000..77528520
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/register.py
@@ -0,0 +1,29 @@
+import psycopg2
+from psycopg2.extensions import cursor
+from .halfvec import register_halfvec_info
+from .sparsevec import register_sparsevec_info
+from .vector import register_vector_info
+
+
+# TODO make globally False by default in 0.4.0
+# note: register_adapter is always global
+# TODO make arrays True by defalt in 0.4.0
+def register_vector(conn_or_curs=None, globally=True, arrays=False):
+    conn = conn_or_curs if hasattr(conn_or_curs, 'cursor') else conn_or_curs.connection
+    cur = conn.cursor(cursor_factory=cursor)
+    scope = None if globally else conn_or_curs
+
+    # use to_regtype to get first matching type in search path
+    cur.execute("SELECT typname, oid FROM pg_type WHERE oid IN (to_regtype('vector'), to_regtype('_vector'), to_regtype('halfvec'), to_regtype('_halfvec'), to_regtype('sparsevec'), to_regtype('_sparsevec'))")
+    type_info = dict(cur.fetchall())
+
+    if 'vector' not in type_info:
+        raise psycopg2.ProgrammingError('vector type not found in the database')
+
+    register_vector_info(type_info['vector'], type_info['_vector'] if arrays else None, scope)
+
+    if 'halfvec' in type_info:
+        register_halfvec_info(type_info['halfvec'], type_info['_halfvec'] if arrays else None, scope)
+
+    if 'sparsevec' in type_info:
+        register_sparsevec_info(type_info['sparsevec'], type_info['_sparsevec'] if arrays else None, scope)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg2/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/sparsevec.py
new file mode 100644
index 00000000..a542807b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/sparsevec.py
@@ -0,0 +1,25 @@
+from psycopg2.extensions import adapt, new_array_type, new_type, register_adapter, register_type
+from ..utils import SparseVector
+
+
+class SparsevecAdapter:
+    def __init__(self, value):
+        self._value = value
+
+    def getquoted(self):
+        return adapt(SparseVector._to_db(self._value)).getquoted()
+
+
+def cast_sparsevec(value, cur):
+    return SparseVector._from_db(value)
+
+
+def register_sparsevec_info(oid, array_oid, scope):
+    sparsevec = new_type((oid,), 'SPARSEVEC', cast_sparsevec)
+    register_type(sparsevec, scope)
+
+    if array_oid is not None:
+        sparsevecarray = new_array_type((array_oid,), 'SPARSEVECARRAY', sparsevec)
+        register_type(sparsevecarray, scope)
+
+    register_adapter(SparseVector, SparsevecAdapter)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/psycopg2/vector.py b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/vector.py
new file mode 100644
index 00000000..9861f01f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/psycopg2/vector.py
@@ -0,0 +1,26 @@
+import numpy as np
+from psycopg2.extensions import adapt, new_array_type, new_type, register_adapter, register_type
+from ..utils import Vector
+
+
+class VectorAdapter:
+    def __init__(self, value):
+        self._value = value
+
+    def getquoted(self):
+        return adapt(Vector._to_db(self._value)).getquoted()
+
+
+def cast_vector(value, cur):
+    return Vector._from_db(value)
+
+
+def register_vector_info(oid, array_oid, scope):
+    vector = new_type((oid,), 'VECTOR', cast_vector)
+    register_type(vector, scope)
+
+    if array_oid is not None:
+        vectorarray = new_array_type((array_oid,), 'VECTORARRAY', vector)
+        register_type(vectorarray, scope)
+
+    register_adapter(np.ndarray, VectorAdapter)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/__init__.py
new file mode 100644
index 00000000..4955eeb9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/__init__.py
@@ -0,0 +1,19 @@
+from .bit import BIT
+from .functions import avg, sum
+from .halfvec import HALFVEC
+from .sparsevec import SPARSEVEC
+from .vector import VECTOR
+from .vector import VECTOR as Vector
+from ..utils import HalfVector, SparseVector
+
+__all__ = [
+    'Vector',
+    'VECTOR',
+    'HALFVEC',
+    'BIT',
+    'SPARSEVEC',
+    'HalfVector',
+    'SparseVector',
+    'avg',
+    'sum'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/bit.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/bit.py
new file mode 100644
index 00000000..0f83f3c6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/bit.py
@@ -0,0 +1,26 @@
+from sqlalchemy.dialects.postgresql.base import ischema_names
+from sqlalchemy.types import UserDefinedType, Float
+
+
+class BIT(UserDefinedType):
+    cache_ok = True
+
+    def __init__(self, length=None):
+        super(UserDefinedType, self).__init__()
+        self.length = length
+
+    def get_col_spec(self, **kw):
+        if self.length is None:
+            return 'BIT'
+        return 'BIT(%d)' % self.length
+
+    class comparator_factory(UserDefinedType.Comparator):
+        def hamming_distance(self, other):
+            return self.op('<~>', return_type=Float)(other)
+
+        def jaccard_distance(self, other):
+            return self.op('<%>', return_type=Float)(other)
+
+
+# for reflection
+ischema_names['bit'] = BIT
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/functions.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/functions.py
new file mode 100644
index 00000000..72e3ca7e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/functions.py
@@ -0,0 +1,14 @@
+# https://docs.sqlalchemy.org/en/20/core/functions.html
+# include sum for a consistent API
+from sqlalchemy.sql.functions import ReturnTypeFromArgs, sum
+
+
+class avg(ReturnTypeFromArgs):
+    inherit_cache = True
+    package = 'pgvector'
+
+
+__all__ = [
+    'avg',
+    'sum'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/halfvec.py
new file mode 100644
index 00000000..639f77bd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/halfvec.py
@@ -0,0 +1,51 @@
+from sqlalchemy.dialects.postgresql.base import ischema_names
+from sqlalchemy.types import UserDefinedType, Float, String
+from ..utils import HalfVector
+
+
+class HALFVEC(UserDefinedType):
+    cache_ok = True
+    _string = String()
+
+    def __init__(self, dim=None):
+        super(UserDefinedType, self).__init__()
+        self.dim = dim
+
+    def get_col_spec(self, **kw):
+        if self.dim is None:
+            return 'HALFVEC'
+        return 'HALFVEC(%d)' % self.dim
+
+    def bind_processor(self, dialect):
+        def process(value):
+            return HalfVector._to_db(value, self.dim)
+        return process
+
+    def literal_processor(self, dialect):
+        string_literal_processor = self._string._cached_literal_processor(dialect)
+
+        def process(value):
+            return string_literal_processor(HalfVector._to_db(value, self.dim))
+        return process
+
+    def result_processor(self, dialect, coltype):
+        def process(value):
+            return HalfVector._from_db(value)
+        return process
+
+    class comparator_factory(UserDefinedType.Comparator):
+        def l2_distance(self, other):
+            return self.op('<->', return_type=Float)(other)
+
+        def max_inner_product(self, other):
+            return self.op('<#>', return_type=Float)(other)
+
+        def cosine_distance(self, other):
+            return self.op('<=>', return_type=Float)(other)
+
+        def l1_distance(self, other):
+            return self.op('<+>', return_type=Float)(other)
+
+
+# for reflection
+ischema_names['halfvec'] = HALFVEC
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/sparsevec.py
new file mode 100644
index 00000000..370f5d14
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/sparsevec.py
@@ -0,0 +1,51 @@
+from sqlalchemy.dialects.postgresql.base import ischema_names
+from sqlalchemy.types import UserDefinedType, Float, String
+from ..utils import SparseVector
+
+
+class SPARSEVEC(UserDefinedType):
+    cache_ok = True
+    _string = String()
+
+    def __init__(self, dim=None):
+        super(UserDefinedType, self).__init__()
+        self.dim = dim
+
+    def get_col_spec(self, **kw):
+        if self.dim is None:
+            return 'SPARSEVEC'
+        return 'SPARSEVEC(%d)' % self.dim
+
+    def bind_processor(self, dialect):
+        def process(value):
+            return SparseVector._to_db(value, self.dim)
+        return process
+
+    def literal_processor(self, dialect):
+        string_literal_processor = self._string._cached_literal_processor(dialect)
+
+        def process(value):
+            return string_literal_processor(SparseVector._to_db(value, self.dim))
+        return process
+
+    def result_processor(self, dialect, coltype):
+        def process(value):
+            return SparseVector._from_db(value)
+        return process
+
+    class comparator_factory(UserDefinedType.Comparator):
+        def l2_distance(self, other):
+            return self.op('<->', return_type=Float)(other)
+
+        def max_inner_product(self, other):
+            return self.op('<#>', return_type=Float)(other)
+
+        def cosine_distance(self, other):
+            return self.op('<=>', return_type=Float)(other)
+
+        def l1_distance(self, other):
+            return self.op('<+>', return_type=Float)(other)
+
+
+# for reflection
+ischema_names['sparsevec'] = SPARSEVEC
diff --git a/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/vector.py b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/vector.py
new file mode 100644
index 00000000..f57a045d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/sqlalchemy/vector.py
@@ -0,0 +1,51 @@
+from sqlalchemy.dialects.postgresql.base import ischema_names
+from sqlalchemy.types import UserDefinedType, Float, String
+from ..utils import Vector
+
+
+class VECTOR(UserDefinedType):
+    cache_ok = True
+    _string = String()
+
+    def __init__(self, dim=None):
+        super(UserDefinedType, self).__init__()
+        self.dim = dim
+
+    def get_col_spec(self, **kw):
+        if self.dim is None:
+            return 'VECTOR'
+        return 'VECTOR(%d)' % self.dim
+
+    def bind_processor(self, dialect):
+        def process(value):
+            return Vector._to_db(value, self.dim)
+        return process
+
+    def literal_processor(self, dialect):
+        string_literal_processor = self._string._cached_literal_processor(dialect)
+
+        def process(value):
+            return string_literal_processor(Vector._to_db(value, self.dim))
+        return process
+
+    def result_processor(self, dialect, coltype):
+        def process(value):
+            return Vector._from_db(value)
+        return process
+
+    class comparator_factory(UserDefinedType.Comparator):
+        def l2_distance(self, other):
+            return self.op('<->', return_type=Float)(other)
+
+        def max_inner_product(self, other):
+            return self.op('<#>', return_type=Float)(other)
+
+        def cosine_distance(self, other):
+            return self.op('<=>', return_type=Float)(other)
+
+        def l1_distance(self, other):
+            return self.op('<+>', return_type=Float)(other)
+
+
+# for reflection
+ischema_names['vector'] = VECTOR
diff --git a/.venv/lib/python3.12/site-packages/pgvector/utils/__init__.py b/.venv/lib/python3.12/site-packages/pgvector/utils/__init__.py
new file mode 100644
index 00000000..3c011605
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/utils/__init__.py
@@ -0,0 +1,11 @@
+from .bit import Bit
+from .halfvec import HalfVector
+from .sparsevec import SparseVector
+from .vector import Vector
+
+__all__ = [
+    'Vector',
+    'HalfVector',
+    'Bit',
+    'SparseVector'
+]
diff --git a/.venv/lib/python3.12/site-packages/pgvector/utils/bit.py b/.venv/lib/python3.12/site-packages/pgvector/utils/bit.py
new file mode 100644
index 00000000..51f75568
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/utils/bit.py
@@ -0,0 +1,61 @@
+import numpy as np
+from struct import pack, unpack_from
+
+
+class Bit:
+    def __init__(self, value):
+        if isinstance(value, str):
+            self._value = self.from_text(value)._value
+        else:
+            # TODO change in 0.4.0
+            # TODO raise if dtype not bool or uint8
+            # if isinstance(value, np.ndarray) and value.dtype == np.uint8:
+            #     value = np.unpackbits(value)
+            # else:
+            #     value = np.asarray(value, dtype=bool)
+
+            value = np.asarray(value, dtype=bool)
+
+            if value.ndim != 1:
+                raise ValueError('expected ndim to be 1')
+
+            self._value = value
+
+    def __repr__(self):
+        return f'Bit({self.to_text()})'
+
+    def to_list(self):
+        return self._value.tolist()
+
+    def to_numpy(self):
+        return self._value
+
+    def to_text(self):
+        return ''.join(self._value.astype(np.uint8).astype(str))
+
+    def to_binary(self):
+        return pack('>i', len(self._value)) + np.packbits(self._value).tobytes()
+
+    @classmethod
+    def from_text(cls, value):
+        return cls(np.asarray([v != '0' for v in value], dtype=bool))
+
+    @classmethod
+    def from_binary(cls, value):
+        count = unpack_from('>i', value)[0]
+        buf = np.frombuffer(value, dtype=np.uint8, offset=4)
+        return cls(np.unpackbits(buf, count=count).astype(bool))
+
+    @classmethod
+    def _to_db(cls, value):
+        if not isinstance(value, cls):
+            raise ValueError('expected bit')
+
+        return value.to_text()
+
+    @classmethod
+    def _to_db_binary(cls, value):
+        if not isinstance(value, cls):
+            raise ValueError('expected bit')
+
+        return value.to_binary()
diff --git a/.venv/lib/python3.12/site-packages/pgvector/utils/halfvec.py b/.venv/lib/python3.12/site-packages/pgvector/utils/halfvec.py
new file mode 100644
index 00000000..e1e50514
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/utils/halfvec.py
@@ -0,0 +1,78 @@
+import numpy as np
+from struct import pack, unpack_from
+
+
+class HalfVector:
+    def __init__(self, value):
+        # asarray still copies if same dtype
+        if not isinstance(value, np.ndarray) or value.dtype != '>f2':
+            value = np.asarray(value, dtype='>f2')
+
+        if value.ndim != 1:
+            raise ValueError('expected ndim to be 1')
+
+        self._value = value
+
+    def __repr__(self):
+        return f'HalfVector({self.to_list()})'
+
+    def dimensions(self):
+        return len(self._value)
+
+    def to_list(self):
+        return self._value.tolist()
+
+    def to_numpy(self):
+        return self._value
+
+    def to_text(self):
+        return '[' + ','.join([str(float(v)) for v in self._value]) + ']'
+
+    def to_binary(self):
+        return pack('>HH', self.dimensions(), 0) + self._value.tobytes()
+
+    @classmethod
+    def from_text(cls, value):
+        return cls([float(v) for v in value[1:-1].split(',')])
+
+    @classmethod
+    def from_binary(cls, value):
+        dim, unused = unpack_from('>HH', value)
+        return cls(np.frombuffer(value, dtype='>f2', count=dim, offset=4))
+
+    @classmethod
+    def _to_db(cls, value, dim=None):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        if dim is not None and value.dimensions() != dim:
+            raise ValueError('expected %d dimensions, not %d' % (dim, value.dimensions()))
+
+        return value.to_text()
+
+    @classmethod
+    def _to_db_binary(cls, value):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        return value.to_binary()
+
+    @classmethod
+    def _from_db(cls, value):
+        if value is None or isinstance(value, cls):
+            return value
+
+        return cls.from_text(value)
+
+    @classmethod
+    def _from_db_binary(cls, value):
+        if value is None or isinstance(value, cls):
+            return value
+
+        return cls.from_binary(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/utils/sparsevec.py b/.venv/lib/python3.12/site-packages/pgvector/utils/sparsevec.py
new file mode 100644
index 00000000..fd9ccff3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/utils/sparsevec.py
@@ -0,0 +1,156 @@
+import numpy as np
+from struct import pack, unpack_from
+
+NO_DEFAULT = object()
+
+
+class SparseVector:
+    def __init__(self, value, dimensions=NO_DEFAULT, /):
+        if value.__class__.__module__.startswith('scipy.sparse.'):
+            if dimensions is not NO_DEFAULT:
+                raise ValueError('extra argument')
+
+            self._from_sparse(value)
+        elif isinstance(value, dict):
+            if dimensions is NO_DEFAULT:
+                raise ValueError('missing dimensions')
+
+            self._from_dict(value, dimensions)
+        else:
+            if dimensions is not NO_DEFAULT:
+                raise ValueError('extra argument')
+
+            self._from_dense(value)
+
+    def __repr__(self):
+        elements = dict(zip(self._indices, self._values))
+        return f'SparseVector({elements}, {self._dim})'
+
+    def dimensions(self):
+        return self._dim
+
+    def indices(self):
+        return self._indices
+
+    def values(self):
+        return self._values
+
+    def to_coo(self):
+        from scipy.sparse import coo_array
+
+        coords = ([0] * len(self._indices), self._indices)
+        return coo_array((self._values, coords), shape=(1, self._dim))
+
+    def to_list(self):
+        vec = [0.0] * self._dim
+        for i, v in zip(self._indices, self._values):
+            vec[i] = v
+        return vec
+
+    def to_numpy(self):
+        vec = np.repeat(0.0, self._dim).astype(np.float32)
+        for i, v in zip(self._indices, self._values):
+            vec[i] = v
+        return vec
+
+    def to_text(self):
+        return '{' + ','.join([f'{int(i) + 1}:{float(v)}' for i, v in zip(self._indices, self._values)]) + '}/' + str(int(self._dim))
+
+    def to_binary(self):
+        nnz = len(self._indices)
+        return pack(f'>iii{nnz}i{nnz}f', self._dim, nnz, 0, *self._indices, *self._values)
+
+    def _from_dict(self, d, dim):
+        elements = [(i, v) for i, v in d.items() if v != 0]
+        elements.sort()
+
+        self._dim = int(dim)
+        self._indices = [int(v[0]) for v in elements]
+        self._values = [float(v[1]) for v in elements]
+
+    def _from_sparse(self, value):
+        value = value.tocoo()
+
+        if value.ndim == 1:
+            self._dim = value.shape[0]
+        elif value.ndim == 2 and value.shape[0] == 1:
+            self._dim = value.shape[1]
+        else:
+            raise ValueError('expected ndim to be 1')
+
+        if hasattr(value, 'coords'):
+            # scipy 1.13+
+            self._indices = value.coords[0].tolist()
+        else:
+            self._indices = value.col.tolist()
+        self._values = value.data.tolist()
+
+    def _from_dense(self, value):
+        self._dim = len(value)
+        self._indices = [i for i, v in enumerate(value) if v != 0]
+        self._values = [float(value[i]) for i in self._indices]
+
+    @classmethod
+    def from_text(cls, value):
+        elements, dim = value.split('/', 2)
+        indices = []
+        values = []
+        # split on empty string returns single element list
+        if len(elements) > 2:
+            for e in elements[1:-1].split(','):
+                i, v = e.split(':', 2)
+                indices.append(int(i) - 1)
+                values.append(float(v))
+        return cls._from_parts(int(dim), indices, values)
+
+    @classmethod
+    def from_binary(cls, value):
+        dim, nnz, unused = unpack_from('>iii', value)
+        indices = unpack_from(f'>{nnz}i', value, 12)
+        values = unpack_from(f'>{nnz}f', value, 12 + nnz * 4)
+        return cls._from_parts(int(dim), indices, values)
+
+    @classmethod
+    def _from_parts(cls, dim, indices, values):
+        vec = cls.__new__(cls)
+        vec._dim = dim
+        vec._indices = indices
+        vec._values = values
+        return vec
+
+    @classmethod
+    def _to_db(cls, value, dim=None):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        if dim is not None and value.dimensions() != dim:
+            raise ValueError('expected %d dimensions, not %d' % (dim, value.dimensions()))
+
+        return value.to_text()
+
+    @classmethod
+    def _to_db_binary(cls, value):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        return value.to_binary()
+
+    @classmethod
+    def _from_db(cls, value):
+        if value is None or isinstance(value, cls):
+            return value
+
+        return cls.from_text(value)
+
+    @classmethod
+    def _from_db_binary(cls, value):
+        if value is None or isinstance(value, cls):
+            return value
+
+        return cls.from_binary(value)
diff --git a/.venv/lib/python3.12/site-packages/pgvector/utils/vector.py b/.venv/lib/python3.12/site-packages/pgvector/utils/vector.py
new file mode 100644
index 00000000..3fa2f356
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pgvector/utils/vector.py
@@ -0,0 +1,78 @@
+import numpy as np
+from struct import pack, unpack_from
+
+
+class Vector:
+    def __init__(self, value):
+        # asarray still copies if same dtype
+        if not isinstance(value, np.ndarray) or value.dtype != '>f4':
+            value = np.asarray(value, dtype='>f4')
+
+        if value.ndim != 1:
+            raise ValueError('expected ndim to be 1')
+
+        self._value = value
+
+    def __repr__(self):
+        return f'Vector({self.to_list()})'
+
+    def dimensions(self):
+        return len(self._value)
+
+    def to_list(self):
+        return self._value.tolist()
+
+    def to_numpy(self):
+        return self._value
+
+    def to_text(self):
+        return '[' + ','.join([str(float(v)) for v in self._value]) + ']'
+
+    def to_binary(self):
+        return pack('>HH', self.dimensions(), 0) + self._value.tobytes()
+
+    @classmethod
+    def from_text(cls, value):
+        return cls([float(v) for v in value[1:-1].split(',')])
+
+    @classmethod
+    def from_binary(cls, value):
+        dim, unused = unpack_from('>HH', value)
+        return cls(np.frombuffer(value, dtype='>f4', count=dim, offset=4))
+
+    @classmethod
+    def _to_db(cls, value, dim=None):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        if dim is not None and value.dimensions() != dim:
+            raise ValueError('expected %d dimensions, not %d' % (dim, value.dimensions()))
+
+        return value.to_text()
+
+    @classmethod
+    def _to_db_binary(cls, value):
+        if value is None:
+            return value
+
+        if not isinstance(value, cls):
+            value = cls(value)
+
+        return value.to_binary()
+
+    @classmethod
+    def _from_db(cls, value):
+        if value is None or isinstance(value, np.ndarray):
+            return value
+
+        return cls.from_text(value).to_numpy().astype(np.float32)
+
+    @classmethod
+    def _from_db_binary(cls, value):
+        if value is None or isinstance(value, np.ndarray):
+            return value
+
+        return cls.from_binary(value).to_numpy().astype(np.float32)