Restore 0.1.5 version from stash

This commit is contained in:
liaibo
2025-12-08 19:56:24 +08:00
parent de189e938d
commit 8db3f4e32d
8578 changed files with 2703426 additions and 217 deletions
@@ -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'
]
@@ -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)
@@ -0,0 +1,6 @@
from django.contrib.postgres.operations import CreateExtension
class VectorExtension(CreateExtension):
def __init__(self):
self.name = 'vector'
@@ -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 = ' <%%> '
@@ -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)
@@ -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
@@ -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)
@@ -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)