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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69922615a71c6eefbe25e734208f52099110685db3ad28073dd79e284ec3381b
4
- data.tar.gz: 47e713a1a9336e51dcb6e446e23d3364f2bcf8f23ff944a0b52e2a7d4e49acbf
3
+ metadata.gz: 8bb295502835167a50a215f96707b208574ac3e6641b3cd9d02c50646b2335b6
4
+ data.tar.gz: '09c4fcdaf392f394619a7a1b786a0be832e248edd7449a5d49e804908fd4d94d'
5
5
  SHA512:
6
- metadata.gz: 7b366354338fd3d15e689340dbddf279ebf4b4d3823412ae1ef95d21eca48d1a06b492738b1aa21c9abb87e77906e2d3f07b67e71f80b61241e1f3143126946d
7
- data.tar.gz: fe83fd43d546c5e07e19fcc38a14760b2810955f5e3398ce4b4895c19280fa3c77dc88874a6bb771ff5572fd90a6a9ebd13bb0563c2a69fa40de5375b6fa34fa
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+\]f(?:16|32)\z}
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| Float(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
@@ -1,5 +1,5 @@
1
1
  module Turbopuffer
2
2
  module ActiveRecord
3
- VERSION = "0.1.4"
3
+ VERSION = "0.1.5"
4
4
  end
5
5
  end
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
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-22 00:00:00.000000000 Z
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