activerecord-turbopuffer-adapter 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- data/lib/active_record/connection_adapters/turbopuffer_adapter.rb +6 -1
- data/lib/turbopuffer/active_record/schema/attribute.rb +1 -1
- data/lib/turbopuffer/active_record/schema/model.rb +1 -0
- data/lib/turbopuffer/active_record/type/bytes.rb +21 -0
- data/lib/turbopuffer/active_record/type/sparse_vector.rb +21 -0
- data/lib/turbopuffer/active_record/type/vector.rb +16 -2
- data/lib/turbopuffer/active_record/type.rb +2 -0
- data/lib/turbopuffer/active_record/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bb295502835167a50a215f96707b208574ac3e6641b3cd9d02c50646b2335b6
|
|
4
|
+
data.tar.gz: '09c4fcdaf392f394619a7a1b786a0be832e248edd7449a5d49e804908fd4d94d'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d822446b93294cbca0db8d9cb13a373588e160805d6dc1f1d8e9e88b3d6695ebff58e3cb94fae1715bf0e42850cafb6b88b807b369cc48d174dbeab893e7c35a
|
|
7
|
+
data.tar.gz: a22568481e63de3ca8a83a7367a59ac9b1e57853ed2315873c2948cd17a0bf4d4a348e77188e8d2f9deefcc25484b8e128e028948dbe11c4064adbc1909e1ffe
|
|
@@ -32,12 +32,17 @@ module ActiveRecord
|
|
|
32
32
|
m.register_type "float", Type::Float.new
|
|
33
33
|
m.register_type "bool", Type::Boolean.new
|
|
34
34
|
m.register_type "datetime", ::Turbopuffer::ActiveRecord::Type::DateTime.new
|
|
35
|
+
m.register_type "bytes", ::Turbopuffer::ActiveRecord::Type::Bytes.new
|
|
36
|
+
m.register_type "{}f16", ::Turbopuffer::ActiveRecord::Type::SparseVector.new
|
|
35
37
|
m.register_type(%r{\A\[\].+\z}) do |type|
|
|
36
38
|
::Turbopuffer::ActiveRecord::Type::Array.new(m.lookup(type.delete_prefix("[]")))
|
|
37
39
|
end
|
|
38
40
|
m.register_type(%r{\A\[\d+\]f(?:16|32)\z}) do |type|
|
|
39
41
|
::Turbopuffer::ActiveRecord::Type::Vector.new(type[/\d+/].to_i)
|
|
40
42
|
end
|
|
43
|
+
m.register_type(%r{\A\[\d+\]i8\z}) do |type|
|
|
44
|
+
::Turbopuffer::ActiveRecord::Type::Vector.new(type[/\d+/].to_i, integer: true)
|
|
45
|
+
end
|
|
41
46
|
end
|
|
42
47
|
end
|
|
43
48
|
|
|
@@ -96,7 +101,7 @@ module ActiveRecord
|
|
|
96
101
|
|
|
97
102
|
def type_cast(value)
|
|
98
103
|
case value
|
|
99
|
-
when Array
|
|
104
|
+
when Array, Hash
|
|
100
105
|
value
|
|
101
106
|
else
|
|
102
107
|
super
|
|
@@ -2,7 +2,7 @@ module Turbopuffer
|
|
|
2
2
|
module ActiveRecord
|
|
3
3
|
module Schema
|
|
4
4
|
class Attribute
|
|
5
|
-
TYPES = %r{\A(?:\[\])?(?:string|uuid|int|uint|float|bool|datetime)\z|\A\[\d+\]
|
|
5
|
+
TYPES = %r{\A(?:\[\])?(?:string|uuid|int|uint|float|bool|datetime)\z|\Abytes\z|\A\[\d+\](?:f16|f32|i8)\z|\A\{\}f16\z}
|
|
6
6
|
|
|
7
7
|
attr_reader :name, :type, :filterable, :full_text_search, :ann, :glob, :regex, :notnull
|
|
8
8
|
|
|
@@ -115,6 +115,7 @@ module Turbopuffer
|
|
|
115
115
|
attrs[:ann] = true if attribute.ann
|
|
116
116
|
attrs[:glob] = true if attribute.glob
|
|
117
117
|
attrs[:regex] = true if attribute.regex
|
|
118
|
+
attrs[:sparse_knn] = { distance_metric: "dot_product" } if attribute.type == "{}f16"
|
|
118
119
|
|
|
119
120
|
schema[attribute.name] = attrs
|
|
120
121
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
|
|
3
|
+
module Turbopuffer
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module Type
|
|
6
|
+
class Bytes < ::ActiveModel::Type::Value
|
|
7
|
+
def serialize(value)
|
|
8
|
+
return if value.nil?
|
|
9
|
+
|
|
10
|
+
Base64.strict_encode64(value)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def deserialize(value)
|
|
14
|
+
return if value.nil?
|
|
15
|
+
|
|
16
|
+
Base64.decode64(value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Turbopuffer
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module Type
|
|
4
|
+
class SparseVector < ::ActiveModel::Type::Value
|
|
5
|
+
include ::ActiveModel::Type::Helpers::Mutable
|
|
6
|
+
|
|
7
|
+
def serialize(value)
|
|
8
|
+
return if value.nil?
|
|
9
|
+
|
|
10
|
+
value.to_h { |key, weight| [ key.to_s, Float(weight) ] }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def deserialize(value)
|
|
14
|
+
return if value.nil?
|
|
15
|
+
|
|
16
|
+
value.to_h.transform_keys(&:to_s)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -4,15 +4,16 @@ module Turbopuffer
|
|
|
4
4
|
class Vector < ::ActiveModel::Type::Value
|
|
5
5
|
include ::ActiveModel::Type::Helpers::Mutable
|
|
6
6
|
|
|
7
|
-
def initialize(dimensions)
|
|
7
|
+
def initialize(dimensions, integer: false)
|
|
8
8
|
@dimensions = dimensions
|
|
9
|
+
@integer = integer
|
|
9
10
|
super()
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def serialize(value)
|
|
13
14
|
return if value.nil?
|
|
14
15
|
|
|
15
|
-
vector = ::Array.wrap(value).map { |component|
|
|
16
|
+
vector = ::Array.wrap(value).map { |component| component(component) }
|
|
16
17
|
|
|
17
18
|
unless vector.size == @dimensions
|
|
18
19
|
raise ArgumentError, "expected a vector of #{@dimensions} dimensions, got #{vector.size}"
|
|
@@ -24,6 +25,19 @@ module Turbopuffer
|
|
|
24
25
|
def deserialize(value)
|
|
25
26
|
value
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def component(value)
|
|
32
|
+
number = Float(value)
|
|
33
|
+
return number unless @integer
|
|
34
|
+
|
|
35
|
+
unless number == number.to_i
|
|
36
|
+
raise ArgumentError, "expected an integer vector component, got #{value.inspect}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
number.to_i
|
|
40
|
+
end
|
|
27
41
|
end
|
|
28
42
|
end
|
|
29
43
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
require "turbopuffer/active_record/type/date_time"
|
|
2
2
|
require "turbopuffer/active_record/type/unsigned_integer"
|
|
3
|
+
require "turbopuffer/active_record/type/bytes"
|
|
3
4
|
require "turbopuffer/active_record/type/array"
|
|
4
5
|
require "turbopuffer/active_record/type/vector"
|
|
6
|
+
require "turbopuffer/active_record/type/sparse_vector"
|
|
5
7
|
|
|
6
8
|
module Turbopuffer
|
|
7
9
|
module ActiveRecord
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-turbopuffer-adapter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Monette
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activerecord
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '8.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: base64
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: railties
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -81,7 +95,9 @@ files:
|
|
|
81
95
|
- lib/turbopuffer/active_record/schema/model.rb
|
|
82
96
|
- lib/turbopuffer/active_record/type.rb
|
|
83
97
|
- lib/turbopuffer/active_record/type/array.rb
|
|
98
|
+
- lib/turbopuffer/active_record/type/bytes.rb
|
|
84
99
|
- lib/turbopuffer/active_record/type/date_time.rb
|
|
100
|
+
- lib/turbopuffer/active_record/type/sparse_vector.rb
|
|
85
101
|
- lib/turbopuffer/active_record/type/unsigned_integer.rb
|
|
86
102
|
- lib/turbopuffer/active_record/type/vector.rb
|
|
87
103
|
- lib/turbopuffer/active_record/version.rb
|