turbopuffer 2.9.0 → 2.10.0

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: 9787f889c45e5fc5892d8a76e508413d80f5af373c6c04ed8a91a87ad5abe0c0
4
- data.tar.gz: 400686f51741d20a8dd15c0f58ad8054d6d1b5ed41afe06e8db1849ade2ab5c9
3
+ metadata.gz: b2a36dd806fc7e21775b4a0edc355e2d22a0414bbf8e9069324514177f082031
4
+ data.tar.gz: '099d6f2b73a34fe9485a2b36501cc26b4c9a74f4369f69d61b193b423b82e9d2'
5
5
  SHA512:
6
- metadata.gz: 2e063904070bd100a6bc86eb9dfc0cba61e9b6ae9d5c148390e358f3cd25d2d33be0937910b6da7428c7e8e5cd7aea32de85d92a98827064ef74fa363b7e5e6e
7
- data.tar.gz: d8931336b1455bcde03bf1fcbafe73409c5e13a537e71e8f5397b4703b5299757f60d266da9a4806f6889c68aacee47f9da9a2bfc33d1484f419cbbc4ff50df7
6
+ metadata.gz: 3a2e126a3c4ae82652aebeaa7a58ecf06207780366faa8e913a598bcc49d8c6c8be90d47bfdb9059206ba03be5b8ed2425cca5004673b0914201d8e87ce9c317
7
+ data.tar.gz: 584446333353eced6da9ebc066231a892d6ef5a40b58d5e5c4bb260b3b4da73a6e57fe33610af6d34bcee40365024f645df5fb1a6d5caaf9f33777b6bd006742
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.10.0](https://github.com/turbopuffer/turbopuffer-ruby/compare/v2.9.0...v2.10.0) (2026-10-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * openapi: add encryption_context to customer-managed encryption ([1df756b](https://github.com/turbopuffer/turbopuffer-ruby/commit/1df756b4a97d526ced288201f8d7a5193bfd5c8b))
9
+ * Send client-generated request IDs ([#77](https://github.com/turbopuffer/turbopuffer-ruby/issues/77)) ([3812a2f](https://github.com/turbopuffer/turbopuffer-ruby/commit/3812a2fc924254f1c5c8b67a6f8dd6892b02e7aa))
10
+ * spec: split query consistency into strong and eventual variants ([2e12e6c](https://github.com/turbopuffer/turbopuffer-ruby/commit/2e12e6c36b4bb9ba8dcba9ecddfb243d4bda1dd7))
11
+ * tpuf-engine: add max_staleness_seconds to eventual consistency ([e77b67d](https://github.com/turbopuffer/turbopuffer-ruby/commit/e77b67dc350327860e1f2f75702b162d06cb222e))
12
+
3
13
  ## [2.9.0](https://github.com/turbopuffer/turbopuffer-ruby/compare/v2.8.0...v2.9.0) (2026-10-20)
4
14
 
5
15
 
data/README.md CHANGED
@@ -17,7 +17,7 @@ To use this gem, install via Bundler by adding the following to your application
17
17
  <!-- x-release-please-start-version -->
18
18
 
19
19
  ```ruby
20
- gem "turbopuffer", "~> 2.9.0"
20
+ gem "turbopuffer", "~> 2.10.0"
21
21
  ```
22
22
 
23
23
  <!-- x-release-please-end -->
@@ -9,16 +9,30 @@ module Turbopuffer
9
9
  # header. The server controls how many requests are recorded by advertising a
10
10
  # sample rate in the `X-turbopuffer-Client-Telemetry-Sample-Rate` response header.
11
11
  #
12
+ # Each recorded request is sent with an `X-Request-Id` that its record repeats, so the
13
+ # server can show the client's timings in the same trace as its own.
14
+ #
12
15
  # @api private
13
16
  module Telemetry
14
17
  ENV_VAR = "TURBOPUFFER_TELEMETRY"
15
18
  HEADER = "x-turbopuffer-client-telemetry"
16
19
  SAMPLE_RATE_HEADER = "x-turbopuffer-client-telemetry-sample-rate"
20
+ # Sorted by ASCII value, so that encoded IDs sort like their bytes.
21
+ REQUEST_ID_ALPHABET = "234567abcdefghijklmnopqrstuvwxyz"
22
+ # The bytes in a request ID before it's encoded. The first `REQUEST_ID_RANDOM_BYTES` are
23
+ # random, and the rest hold the time it was generated.
24
+ REQUEST_ID_BYTES = 18
25
+ # The request header that carries the IDs of recorded requests.
26
+ REQUEST_ID_HEADER = "x-request-id"
27
+ # The prefix of generated request IDs.
28
+ REQUEST_ID_PREFIX = "tpreq_"
29
+ # The random bytes at the start of a request ID.
30
+ REQUEST_ID_RANDOM_BYTES = 12
17
31
 
18
32
  ENVELOPE_VERSION = 1
19
33
 
20
34
  # Keeps the header well under the 8 KB per-line limit of common proxies.
21
- MAX_RECORDS_PER_HEADER = 20
35
+ MAX_RECORDS_PER_HEADER = 16
22
36
  MAX_BUFFERED_RECORDS = 1000
23
37
 
24
38
  TRUE_VALUES = %w[1 true yes on].freeze
@@ -124,6 +138,37 @@ module Turbopuffer
124
138
  # @return [Float]
125
139
  def random = Kernel.rand
126
140
 
141
+ # @api private
142
+ #
143
+ # Encodes `bytes` in base32 with `REQUEST_ID_ALPHABET`, in RFC 4648's bit order without
144
+ # padding.
145
+ #
146
+ # @param bytes [String]
147
+ #
148
+ # @return [String]
149
+ def encode_base32(bytes)
150
+ bytes.unpack1("B*").scan(/.{1,5}/).map { REQUEST_ID_ALPHABET[_1.ljust(5, "0").to_i(2)] }.join
151
+ end
152
+
153
+ # @api private
154
+ #
155
+ # Generates the ID of a request that will be recorded. Its format is private to turbopuffer,
156
+ # which only links IDs in the format it expects: 12 random bytes, then the 48-bit
157
+ # millisecond time they were generated XORed with the first 6 random bytes, in base32
158
+ # after a prefix.
159
+ #
160
+ # @param now [Time]
161
+ # @param random [String] overridden by tests
162
+ #
163
+ # @return [String]
164
+ def new_request_id(now, random: SecureRandom.random_bytes(REQUEST_ID_RANDOM_BYTES))
165
+ millis = (now.to_r * 1000).floor
166
+ # The low bytes of the big-endian time.
167
+ time = [millis].pack("Q>").bytes.last(REQUEST_ID_BYTES - REQUEST_ID_RANDOM_BYTES)
168
+ masked = time.zip(random.bytes).map { _1 ^ _2 }.pack("C*")
169
+ REQUEST_ID_PREFIX + encode_base32(random + masked)
170
+ end
171
+
127
172
  # @api private
128
173
  #
129
174
  # Returns the buffer shared by clients with the same API key and base URL, or nil if
@@ -234,33 +279,48 @@ module Turbopuffer
234
279
 
235
280
  # @api private
236
281
  #
237
- # Samples and buffers a record for one request.
282
+ # Decides whether to record a request that's starting, returning the sample rate it was
283
+ # recorded at.
284
+ #
285
+ # @return [Float, nil]
286
+ def sample
287
+ rate = @sample_rate
288
+ return nil if rate <= 0.0 || (rate < 1.0 && Telemetry.random >= rate)
289
+ rate
290
+ end
291
+
292
+ # @api private
293
+ #
294
+ # Buffers the record of a request sampled at `sample_rate`.
238
295
  #
239
296
  # @param method [Symbol, String]
240
297
  # @param url [URI::Generic, String]
241
298
  # @param status [Integer]
242
299
  # @param retries [Integer]
243
300
  # @param durations [Hash{Symbol=>Integer}] in nanoseconds
301
+ # @param sample_rate [Float]
244
302
  # @param server_total_ms [Numeric, nil]
245
303
  # @param error [String, nil]
246
304
  # @param reused [Boolean, nil]
305
+ # @param request_id [String, nil] the `X-Request-Id` the request was sent with, when the
306
+ # client generated it
247
307
  def record(
248
308
  method:,
249
309
  url:,
250
310
  status:,
251
311
  retries:,
252
312
  durations:,
313
+ sample_rate:,
253
314
  server_total_ms: nil,
254
315
  error: nil,
255
- reused: nil
316
+ reused: nil,
317
+ request_id: nil
256
318
  )
257
- rate = @sample_rate
258
- return if rate <= 0.0 || (rate < 1.0 && Telemetry.random >= rate)
259
-
260
319
  rec = {route: Telemetry.route_template(method, url), status: status}
261
320
  rec[:error] = error unless error.nil?
262
321
  rec[:retries] = retries unless retries.zero?
263
- rec[:sample_rate] = rate if rate < 1.0
322
+ rec[:request_id] = request_id unless request_id.nil?
323
+ rec[:sample_rate] = sample_rate if sample_rate < 1.0
264
324
  durations.each do |name, duration|
265
325
  # A zero compress duration means the body wasn't compressed.
266
326
  next if name == :compress && duration.zero?
@@ -353,6 +413,11 @@ module Turbopuffer
353
413
  @info = info
354
414
  # Kept for later attempts until one of them may have reached the server.
355
415
  @batch = nil
416
+ # The rate the request was recorded at, or nil if it isn't recorded. Sampled up front, so
417
+ # that only requests whose records the server will get are sent with request IDs.
418
+ @sample_rate = buffer.sample
419
+ # Sent with every attempt, so retries share the request's trace.
420
+ @request_id = @sample_rate && Telemetry.new_request_id(Time.now)
356
421
  end
357
422
 
358
423
  # @api private
@@ -360,9 +425,15 @@ module Turbopuffer
360
425
  # @param attempt [Integer]
361
426
  # @param request [Hash{Symbol=>Object}]
362
427
  def attempt_begin(attempt, request)
428
+ headers = request.fetch(:headers)
363
429
  # Taken only once a request is about to be sent, so one that fails before then can't lose it.
364
- @batch = @buffer.take if attempt.zero?
365
- request.fetch(:headers)[HEADER] = @batch.header if @batch
430
+ if attempt.zero?
431
+ @batch = @buffer.take
432
+ # An ID set by the caller is theirs to use, so the request keeps it and isn't linked.
433
+ @request_id = nil if headers.key?(REQUEST_ID_HEADER)
434
+ end
435
+ headers[HEADER] = @batch.header if @batch
436
+ headers[REQUEST_ID_HEADER] = @request_id if @request_id
366
437
  end
367
438
 
368
439
  # @api private
@@ -388,6 +459,9 @@ module Turbopuffer
388
459
  @buffer.restore(@batch)
389
460
  @batch = nil
390
461
  end
462
+ return if @sample_rate.nil?
463
+ # A rate of zero asks clients to stop recording, even requests that were already sampled.
464
+ return if @buffer.sample_rate <= 0.0
391
465
 
392
466
  @buffer.record(
393
467
  method: @info.method,
@@ -395,9 +469,11 @@ module Turbopuffer
395
469
  status: status(result),
396
470
  retries: result.retries,
397
471
  durations: durations,
472
+ sample_rate: @sample_rate,
398
473
  server_total_ms: server_total_ms(result.parsed),
399
474
  error: error_name(result.error_kind),
400
- reused: timings.reused
475
+ reused: timings.reused,
476
+ request_id: @request_id
401
477
  )
402
478
  end
403
479
 
@@ -25,7 +25,15 @@ module Turbopuffer
25
25
  # @return [Symbol, :"customer-managed"]
26
26
  required :mode, const: :"customer-managed"
27
27
 
28
- # @!method initialize(key_name:, mode: :"customer-managed")
28
+ # @!attribute encryption_context
29
+ # The AWS KMS encryption context to send with every request that uses the key.
30
+ # Only supported on AWS, and must be enabled for your organization. Keys may not
31
+ # start with `tpuf:` or `aws:`.
32
+ #
33
+ # @return [Hash{Symbol=>String}, nil]
34
+ optional :encryption_context, Turbopuffer::Internal::Type::HashOf[String]
35
+
36
+ # @!method initialize(key_name:, encryption_context: nil, mode: :"customer-managed")
29
37
  # Some parameter documentations has been truncated, see
30
38
  # {Turbopuffer::Models::Encryption::CustomerManaged} for more details.
31
39
  #
@@ -33,6 +41,8 @@ module Turbopuffer
33
41
  #
34
42
  # @param key_name [String] The identifier of the CMEK key to use for encryption. For GCP, the fully-qualifi
35
43
  #
44
+ # @param encryption_context [Hash{Symbol=>String}] The AWS KMS encryption context to send with every request that uses the key. Onl
45
+ #
36
46
  # @param mode [Symbol, :"customer-managed"]
37
47
  end
38
48
 
@@ -30,8 +30,8 @@ module Turbopuffer
30
30
  # @!attribute consistency
31
31
  # The consistency level for a query.
32
32
  #
33
- # @return [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency, nil]
34
- optional :consistency, -> { Turbopuffer::NamespaceExplainQueryParams::Consistency }
33
+ # @return [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Eventual, nil]
34
+ optional :consistency, union: -> { Turbopuffer::NamespaceExplainQueryParams::Consistency }
35
35
 
36
36
  # @!attribute distance_metric
37
37
  # A function used to calculate vector similarity.
@@ -107,7 +107,7 @@ module Turbopuffer
107
107
  #
108
108
  # @param compute_attributes [Hash{Symbol=>Object}] Computes additional values on documents returned by a query. Each key is the nam
109
109
  #
110
- # @param consistency [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency] The consistency level for a query.
110
+ # @param consistency [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Eventual] The consistency level for a query.
111
111
  #
112
112
  # @param distance_metric [Symbol, Turbopuffer::Models::DistanceMetric] A function used to calculate vector similarity.
113
113
  #
@@ -131,33 +131,59 @@ module Turbopuffer
131
131
  #
132
132
  # @param request_options [Turbopuffer::RequestOptions, Hash{Symbol=>Object}]
133
133
 
134
- class Consistency < Turbopuffer::Internal::Type::BaseModel
135
- # @!attribute level
136
- # The query's consistency level.
137
- #
138
- # @return [Symbol, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Level, nil]
139
- optional :level, enum: -> { Turbopuffer::NamespaceExplainQueryParams::Consistency::Level }
134
+ # The consistency level for a query.
135
+ module Consistency
136
+ extend Turbopuffer::Internal::Type::Union
137
+
138
+ discriminator :level
140
139
 
141
- # @!method initialize(level: nil)
142
- # The consistency level for a query.
143
- #
144
- # @param level [Symbol, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Level] The query's consistency level.
140
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
141
+ variant :strong, -> { Turbopuffer::NamespaceExplainQueryParams::Consistency::Strong }
145
142
 
146
- # The query's consistency level.
147
- #
148
- # @see Turbopuffer::Models::NamespaceExplainQueryParams::Consistency#level
149
- module Level
150
- extend Turbopuffer::Internal::Type::Enum
143
+ # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
144
+ variant :eventual, -> { Turbopuffer::NamespaceExplainQueryParams::Consistency::Eventual }
151
145
 
152
- # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
153
- STRONG = :strong
146
+ class Strong < Turbopuffer::Internal::Type::BaseModel
147
+ # @!attribute level
148
+ #
149
+ # @return [Symbol, :strong]
150
+ required :level, const: :strong
154
151
 
155
- # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
156
- EVENTUAL = :eventual
152
+ # @!method initialize(level: :strong)
153
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest
154
+ # writes.
155
+ #
156
+ # @param level [Symbol, :strong]
157
+ end
157
158
 
158
- # @!method self.values
159
- # @return [Array<Symbol>]
159
+ class Eventual < Turbopuffer::Internal::Type::BaseModel
160
+ # @!attribute level
161
+ #
162
+ # @return [Symbol, :eventual]
163
+ required :level, const: :eventual
164
+
165
+ # @!attribute max_staleness_seconds
166
+ # How long the query may reuse the node's cached view of the namespace before
167
+ # refreshing it from object storage. Defaults to 60.
168
+ #
169
+ # @return [Integer, nil]
170
+ optional :max_staleness_seconds, Integer
171
+
172
+ # @!method initialize(max_staleness_seconds: nil, level: :eventual)
173
+ # Some parameter documentations has been truncated, see
174
+ # {Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Eventual} for
175
+ # more details.
176
+ #
177
+ # Eventual consistency. Does not require a round-trip to object storage, but may
178
+ # not see the latest writes.
179
+ #
180
+ # @param max_staleness_seconds [Integer] How long the query may reuse the node's cached view of the namespace before refr
181
+ #
182
+ # @param level [Symbol, :eventual]
160
183
  end
184
+
185
+ # @!method self.variants
186
+ # @return [Array(Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Eventual)]
161
187
  end
162
188
 
163
189
  # Limits the documents returned by a query.
@@ -21,8 +21,8 @@ module Turbopuffer
21
21
  # @!attribute consistency
22
22
  # The consistency level for a query.
23
23
  #
24
- # @return [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency, nil]
25
- optional :consistency, -> { Turbopuffer::NamespaceMultiQueryParams::Consistency }
24
+ # @return [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Eventual, nil]
25
+ optional :consistency, union: -> { Turbopuffer::NamespaceMultiQueryParams::Consistency }
26
26
 
27
27
  # @!attribute limit
28
28
  # Limits the total number of reranked documents returned.
@@ -57,7 +57,7 @@ module Turbopuffer
57
57
  #
58
58
  # @param namespace [String]
59
59
  #
60
- # @param consistency [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency] The consistency level for a query.
60
+ # @param consistency [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Eventual] The consistency level for a query.
61
61
  #
62
62
  # @param limit [Integer, Turbopuffer::Models::RerankLimit] Limits the total number of reranked documents returned.
63
63
  #
@@ -188,33 +188,59 @@ module Turbopuffer
188
188
  end
189
189
  end
190
190
 
191
- class Consistency < Turbopuffer::Internal::Type::BaseModel
192
- # @!attribute level
193
- # The query's consistency level.
194
- #
195
- # @return [Symbol, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Level, nil]
196
- optional :level, enum: -> { Turbopuffer::NamespaceMultiQueryParams::Consistency::Level }
191
+ # The consistency level for a query.
192
+ module Consistency
193
+ extend Turbopuffer::Internal::Type::Union
197
194
 
198
- # @!method initialize(level: nil)
199
- # The consistency level for a query.
200
- #
201
- # @param level [Symbol, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Level] The query's consistency level.
195
+ discriminator :level
202
196
 
203
- # The query's consistency level.
204
- #
205
- # @see Turbopuffer::Models::NamespaceMultiQueryParams::Consistency#level
206
- module Level
207
- extend Turbopuffer::Internal::Type::Enum
197
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
198
+ variant :strong, -> { Turbopuffer::NamespaceMultiQueryParams::Consistency::Strong }
208
199
 
209
- # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
210
- STRONG = :strong
200
+ # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
201
+ variant :eventual, -> { Turbopuffer::NamespaceMultiQueryParams::Consistency::Eventual }
211
202
 
212
- # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
213
- EVENTUAL = :eventual
203
+ class Strong < Turbopuffer::Internal::Type::BaseModel
204
+ # @!attribute level
205
+ #
206
+ # @return [Symbol, :strong]
207
+ required :level, const: :strong
214
208
 
215
- # @!method self.values
216
- # @return [Array<Symbol>]
209
+ # @!method initialize(level: :strong)
210
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest
211
+ # writes.
212
+ #
213
+ # @param level [Symbol, :strong]
217
214
  end
215
+
216
+ class Eventual < Turbopuffer::Internal::Type::BaseModel
217
+ # @!attribute level
218
+ #
219
+ # @return [Symbol, :eventual]
220
+ required :level, const: :eventual
221
+
222
+ # @!attribute max_staleness_seconds
223
+ # How long the query may reuse the node's cached view of the namespace before
224
+ # refreshing it from object storage. Defaults to 60.
225
+ #
226
+ # @return [Integer, nil]
227
+ optional :max_staleness_seconds, Integer
228
+
229
+ # @!method initialize(max_staleness_seconds: nil, level: :eventual)
230
+ # Some parameter documentations has been truncated, see
231
+ # {Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Eventual} for more
232
+ # details.
233
+ #
234
+ # Eventual consistency. Does not require a round-trip to object storage, but may
235
+ # not see the latest writes.
236
+ #
237
+ # @param max_staleness_seconds [Integer] How long the query may reuse the node's cached view of the namespace before refr
238
+ #
239
+ # @param level [Symbol, :eventual]
240
+ end
241
+
242
+ # @!method self.variants
243
+ # @return [Array(Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Eventual)]
218
244
  end
219
245
 
220
246
  # Limits the total number of reranked documents returned.
@@ -30,8 +30,8 @@ module Turbopuffer
30
30
  # @!attribute consistency
31
31
  # The consistency level for a query.
32
32
  #
33
- # @return [Turbopuffer::Models::NamespaceQueryParams::Consistency, nil]
34
- optional :consistency, -> { Turbopuffer::NamespaceQueryParams::Consistency }
33
+ # @return [Turbopuffer::Models::NamespaceQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceQueryParams::Consistency::Eventual, nil]
34
+ optional :consistency, union: -> { Turbopuffer::NamespaceQueryParams::Consistency }
35
35
 
36
36
  # @!attribute distance_metric
37
37
  # A function used to calculate vector similarity.
@@ -113,7 +113,7 @@ module Turbopuffer
113
113
  #
114
114
  # @param compute_attributes [Hash{Symbol=>Object}] Computes additional values on documents returned by a query. Each key is the nam
115
115
  #
116
- # @param consistency [Turbopuffer::Models::NamespaceQueryParams::Consistency] The consistency level for a query.
116
+ # @param consistency [Turbopuffer::Models::NamespaceQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceQueryParams::Consistency::Eventual] The consistency level for a query.
117
117
  #
118
118
  # @param distance_metric [Symbol, Turbopuffer::Models::DistanceMetric] A function used to calculate vector similarity.
119
119
  #
@@ -139,33 +139,59 @@ module Turbopuffer
139
139
  #
140
140
  # @param request_options [Turbopuffer::RequestOptions, Hash{Symbol=>Object}]
141
141
 
142
- class Consistency < Turbopuffer::Internal::Type::BaseModel
143
- # @!attribute level
144
- # The query's consistency level.
145
- #
146
- # @return [Symbol, Turbopuffer::Models::NamespaceQueryParams::Consistency::Level, nil]
147
- optional :level, enum: -> { Turbopuffer::NamespaceQueryParams::Consistency::Level }
142
+ # The consistency level for a query.
143
+ module Consistency
144
+ extend Turbopuffer::Internal::Type::Union
145
+
146
+ discriminator :level
148
147
 
149
- # @!method initialize(level: nil)
150
- # The consistency level for a query.
151
- #
152
- # @param level [Symbol, Turbopuffer::Models::NamespaceQueryParams::Consistency::Level] The query's consistency level.
148
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
149
+ variant :strong, -> { Turbopuffer::NamespaceQueryParams::Consistency::Strong }
153
150
 
154
- # The query's consistency level.
155
- #
156
- # @see Turbopuffer::Models::NamespaceQueryParams::Consistency#level
157
- module Level
158
- extend Turbopuffer::Internal::Type::Enum
151
+ # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
152
+ variant :eventual, -> { Turbopuffer::NamespaceQueryParams::Consistency::Eventual }
159
153
 
160
- # Strong consistency. Requires a round-trip to object storage to fetch the latest writes.
161
- STRONG = :strong
154
+ class Strong < Turbopuffer::Internal::Type::BaseModel
155
+ # @!attribute level
156
+ #
157
+ # @return [Symbol, :strong]
158
+ required :level, const: :strong
162
159
 
163
- # Eventual consistency. Does not require a round-trip to object storage, but may not see the latest writes.
164
- EVENTUAL = :eventual
160
+ # @!method initialize(level: :strong)
161
+ # Strong consistency. Requires a round-trip to object storage to fetch the latest
162
+ # writes.
163
+ #
164
+ # @param level [Symbol, :strong]
165
+ end
165
166
 
166
- # @!method self.values
167
- # @return [Array<Symbol>]
167
+ class Eventual < Turbopuffer::Internal::Type::BaseModel
168
+ # @!attribute level
169
+ #
170
+ # @return [Symbol, :eventual]
171
+ required :level, const: :eventual
172
+
173
+ # @!attribute max_staleness_seconds
174
+ # How long the query may reuse the node's cached view of the namespace before
175
+ # refreshing it from object storage. Defaults to 60.
176
+ #
177
+ # @return [Integer, nil]
178
+ optional :max_staleness_seconds, Integer
179
+
180
+ # @!method initialize(max_staleness_seconds: nil, level: :eventual)
181
+ # Some parameter documentations has been truncated, see
182
+ # {Turbopuffer::Models::NamespaceQueryParams::Consistency::Eventual} for more
183
+ # details.
184
+ #
185
+ # Eventual consistency. Does not require a round-trip to object storage, but may
186
+ # not see the latest writes.
187
+ #
188
+ # @param max_staleness_seconds [Integer] How long the query may reuse the node's cached view of the namespace before refr
189
+ #
190
+ # @param level [Symbol, :eventual]
168
191
  end
192
+
193
+ # @!method self.variants
194
+ # @return [Array(Turbopuffer::Models::NamespaceQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceQueryParams::Consistency::Eventual)]
169
195
  end
170
196
 
171
197
  # Limits the documents returned by a query.
@@ -106,7 +106,7 @@ module Turbopuffer
106
106
  #
107
107
  # @param compute_attributes [Hash{Symbol=>Object}] Body param: Computes additional values on documents returned by a query. Each ke
108
108
  #
109
- # @param consistency [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency] Body param: The consistency level for a query.
109
+ # @param consistency [Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceExplainQueryParams::Consistency::Eventual] Body param: The consistency level for a query.
110
110
  #
111
111
  # @param distance_metric [Symbol, Turbopuffer::Models::DistanceMetric] Body param: A function used to calculate vector similarity.
112
112
  #
@@ -209,7 +209,7 @@ module Turbopuffer
209
209
  #
210
210
  # @param namespace [String] Path param: The name of the namespace.
211
211
  #
212
- # @param consistency [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency] Body param: The consistency level for a query.
212
+ # @param consistency [Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceMultiQueryParams::Consistency::Eventual] Body param: The consistency level for a query.
213
213
  #
214
214
  # @param limit [Integer, Turbopuffer::Models::RerankLimit] Body param: Limits the total number of reranked documents returned.
215
215
  #
@@ -279,7 +279,7 @@ module Turbopuffer
279
279
  #
280
280
  # @param compute_attributes [Hash{Symbol=>Object}] Body param: Computes additional values on documents returned by a query. Each ke
281
281
  #
282
- # @param consistency [Turbopuffer::Models::NamespaceQueryParams::Consistency] Body param: The consistency level for a query.
282
+ # @param consistency [Turbopuffer::Models::NamespaceQueryParams::Consistency::Strong, Turbopuffer::Models::NamespaceQueryParams::Consistency::Eventual] Body param: The consistency level for a query.
283
283
  #
284
284
  # @param distance_metric [Symbol, Turbopuffer::Models::DistanceMetric] Body param: A function used to calculate vector similarity.
285
285
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Turbopuffer
4
- VERSION = "2.9.0"
4
+ VERSION = "2.10.0"
5
5
  end
@@ -9,6 +9,16 @@ module Turbopuffer
9
9
  ENV_VAR = T.let(T.unsafe(nil), String)
10
10
  HEADER = T.let(T.unsafe(nil), String)
11
11
  SAMPLE_RATE_HEADER = T.let(T.unsafe(nil), String)
12
+ # Sorted by ASCII value, so that encoded IDs sort like their bytes.
13
+ REQUEST_ID_ALPHABET = T.let(T.unsafe(nil), String)
14
+ # The bytes in a request ID before it's encoded.
15
+ REQUEST_ID_BYTES = T.let(T.unsafe(nil), Integer)
16
+ # The request header that carries the IDs of recorded requests.
17
+ REQUEST_ID_HEADER = T.let(T.unsafe(nil), String)
18
+ # The prefix of generated request IDs.
19
+ REQUEST_ID_PREFIX = T.let(T.unsafe(nil), String)
20
+ # The random bytes at the start of a request ID.
21
+ REQUEST_ID_RANDOM_BYTES = T.let(T.unsafe(nil), Integer)
12
22
 
13
23
  ENVELOPE_VERSION = T.let(T.unsafe(nil), Integer)
14
24
 
@@ -60,6 +70,24 @@ module Turbopuffer
60
70
  def random
61
71
  end
62
72
 
73
+ # @api private
74
+ #
75
+ # Encodes `bytes` in base32 with `REQUEST_ID_ALPHABET`, in RFC 4648's bit order without
76
+ # padding.
77
+ sig { params(bytes: String).returns(String) }
78
+ def encode_base32(bytes)
79
+ end
80
+
81
+ # @api private
82
+ #
83
+ # Generates the ID of a request that will be recorded. Its format is private to turbopuffer.
84
+ sig { params(now: Time, random: String).returns(String) }
85
+ def new_request_id(
86
+ now,
87
+ random: SecureRandom.random_bytes(REQUEST_ID_RANDOM_BYTES)
88
+ )
89
+ end
90
+
63
91
  # @api private
64
92
  #
65
93
  # Returns the buffer shared by clients with the same API key and base URL, or nil if
@@ -129,7 +157,15 @@ module Turbopuffer
129
157
 
130
158
  # @api private
131
159
  #
132
- # Samples and buffers a record for one request.
160
+ # Decides whether to record a request that's starting, returning the sample rate it was
161
+ # recorded at.
162
+ sig { returns(T.nilable(Float)) }
163
+ def sample
164
+ end
165
+
166
+ # @api private
167
+ #
168
+ # Buffers the record of a request sampled at `sample_rate`.
133
169
  sig do
134
170
  params(
135
171
  method: T.any(Symbol, String),
@@ -137,9 +173,11 @@ module Turbopuffer
137
173
  status: Integer,
138
174
  retries: Integer,
139
175
  durations: T::Hash[Symbol, Integer],
176
+ sample_rate: Float,
140
177
  server_total_ms: T.nilable(Numeric),
141
178
  error: T.nilable(String),
142
- reused: T.nilable(T::Boolean)
179
+ reused: T.nilable(T::Boolean),
180
+ request_id: T.nilable(String)
143
181
  ).void
144
182
  end
145
183
  def record(
@@ -148,9 +186,11 @@ module Turbopuffer
148
186
  status:,
149
187
  retries:,
150
188
  durations:,
189
+ sample_rate:,
151
190
  server_total_ms: nil,
152
191
  error: nil,
153
- reused: nil
192
+ reused: nil,
193
+ request_id: nil
154
194
  )
155
195
  end
156
196