bitfab 0.43.1 → 0.51.1

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: d1073eb267b5c91b7a845d3abac599939b8838741bfa252b688f0dbe803276c5
4
- data.tar.gz: 118f34e8a91a8a0cec3e4071098744fada2879b948577d8bebae275ed1771402
3
+ metadata.gz: 3dd6fa16ae3ff5f26d94f6b86f9fa702129cfb502e3f94b293e383d107d1c0de
4
+ data.tar.gz: 220cdb686e1a5522fdcd9579e897f60b0896360583b49615006f0489723c6d21
5
5
  SHA512:
6
- metadata.gz: 34f05d59438374486058802390023be43c61d0a103b30bec88ee431623dc8201253398d08e0a80600c6d0bf8d7f8861b6078748bfe1bdf8b5f4e0b92d2bb9960
7
- data.tar.gz: 389a6a04ecbf88da123f48a806854f00e51b9f2cf8dc45ea25b698050f288829bfb81bf7e262e82fdaad80a0b0c2ac583f8c42e5d43e244d3259f544bd03e708
6
+ metadata.gz: cf75d4072ac82e17312f40f86c1a0e54c3a60a019818695507a5d53cfc1bfb15db6e705ad6a971c6e77abd67b4b752e180457d483b5f42becc52167323784dd9
7
+ data.tar.gz: 8857e35745377b128479948ec4622ef2223f3876015f03e161ac80e43f61ec1c1556cb4d4839dd174c35afba9b3e1fbfcbd0c4da69766b80cd593cd8caf96c07
data/lib/bitfab/client.rb CHANGED
@@ -93,11 +93,14 @@ module Bitfab
93
93
  # @param method_name [Symbol] the method to replay
94
94
  # @param trace_function_key [String] the trace function key for this method
95
95
  # @param limit [Integer, nil] maximum number of traces to replay (default: 5;
96
- # maximum: 5,000). Ignored when trace_ids or dataset_id is passed because
96
+ # maximum: 5,000). Ignored when trace_ids or a dataset is passed because
97
97
  # either source already determines how many traces replay. Supplying
98
- # trace_ids also emits a warning.
99
- # @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100),
100
- # mutually exclusive with dataset_id
98
+ # trace_ids also emits a warning. To replay part of a dataset selection,
99
+ # name the members to run in trace_ids rather than bounding it here.
100
+ # @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100).
101
+ # Passed alongside dataset_id or dataset_ids it pins which members of that
102
+ # selection replay, and the server rejects any ID none of those datasets
103
+ # contains
101
104
  # @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
102
105
  # @param code_change_description [String, nil] optional rationale for the
103
106
  # code change being tested in this replay (stored on the experiment).
@@ -110,9 +113,14 @@ module Bitfab
110
113
  # Omit to capture automatically, or pass nil to suppress file capture.
111
114
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
112
115
  # replay runs into a single experiment batch
116
+ # @param dataset_ids [Array<String>, nil] optional UUIDs of the datasets this
117
+ # replay runs against, for benchmarking one method against several corpora
118
+ # in a single run; mutually exclusive with dataset_id. Passing trace_ids
119
+ # alongside it narrows the run to those members
113
120
  # @param dataset_id [String, nil] optional UUID of the dataset this replay
114
- # runs against, mutually exclusive with trace_ids and stored on the
115
- # resulting experiment for durable attribution
121
+ # runs against, mutually exclusive with dataset_ids and stored on the
122
+ # resulting experiment for durable attribution. Alone it replays the
123
+ # dataset's full membership, and with trace_ids only those members
116
124
  # @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
117
125
  # directly to this experiment, graded as the union with the dataset's
118
126
  # runnable graders at completion; each must be an active/live grader in the
@@ -158,7 +166,7 @@ module Bitfab
158
166
  # queued work from in-flight work. A raising callback never crashes the run.
159
167
  # @return [Hash] with :items, :test_run_id, :test_run_url
160
168
  def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
161
- name: nil, code_change_description: Replay::CODE_CHANGE_UNSET, code_change_files: Replay::CODE_CHANGE_UNSET, experiment_group_id: nil, dataset_id: nil, grader_ids: nil, mock: "marked",
169
+ name: nil, code_change_description: Replay::CODE_CHANGE_UNSET, code_change_files: Replay::CODE_CHANGE_UNSET, experiment_group_id: nil, dataset_id: nil, dataset_ids: nil, grader_ids: nil, mock: "marked",
162
170
  adapt_inputs: nil, mock_override: nil, db_branch: nil, on_item_start: nil, on_item_finish: nil, on_progress: nil)
163
171
  Replay.run(
164
172
  self,
@@ -173,6 +181,7 @@ module Bitfab
173
181
  code_change_files:,
174
182
  experiment_group_id:,
175
183
  dataset_id:,
184
+ dataset_ids:,
176
185
  grader_ids:,
177
186
  mock:,
178
187
  adapt_inputs:,
@@ -173,14 +173,17 @@ module Bitfab
173
173
  # replay runs into a single experiment batch
174
174
  # @param name [String, nil] optional display name for the resulting
175
175
  # experiment/test run
176
- # @param dataset_id [String, nil] optional UUID of the dataset this replay
177
- # runs against, stored on the resulting experiment for durable attribution
176
+ # @param dataset_ids [Array<String>, nil] optional UUIDs of the datasets this
177
+ # replay runs against. The run replays the union of their traces, graded by
178
+ # the union of their graders, and is attributed to every one of them. A
179
+ # single id travels as datasetId so a new SDK keeps working against a
180
+ # server that predates datasetIds.
178
181
  # @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
179
182
  # directly to this experiment, graded as the union with the dataset's
180
183
  # runnable graders at completion; each must be an active/live grader in the
181
184
  # same org and trace function or the server rejects the replay
182
185
  def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil,
183
- code_change_files: nil, experiment_group_id: nil, name: nil, include_db_branch_lease: false, dataset_id: nil,
186
+ code_change_files: nil, experiment_group_id: nil, name: nil, include_db_branch_lease: false, dataset_ids: nil,
184
187
  grader_ids: nil, db_branch_settings: nil)
185
188
  payload = {
186
189
  "traceFunctionKey" => trace_function_key
@@ -195,7 +198,13 @@ module Bitfab
195
198
  payload["experimentGroupId"] = experiment_group_id unless experiment_group_id.nil?
196
199
  payload["includeDbBranchLease"] = true if include_db_branch_lease
197
200
  payload["lazyDbBranchLease"] = true if include_db_branch_lease
198
- payload["datasetId"] = dataset_id unless dataset_id.nil?
201
+ unless dataset_ids.nil?
202
+ if dataset_ids.length == 1
203
+ payload["datasetId"] = dataset_ids.first
204
+ else
205
+ payload["datasetIds"] = dataset_ids
206
+ end
207
+ end
199
208
  payload["graderIds"] = grader_ids unless grader_ids.nil?
200
209
  payload["dbBranchSettings"] = db_branch_settings unless db_branch_settings.nil?
201
210
 
data/lib/bitfab/replay.rb CHANGED
@@ -187,7 +187,7 @@ module Bitfab
187
187
  # @param method_name [Symbol] the method to replay
188
188
  # @param trace_function_key [String] the trace function key for this method
189
189
  # @param limit [Integer, nil] maximum number of traces to replay (default: 5).
190
- # Ignored when trace_ids or dataset_id is passed because either source
190
+ # Ignored when trace_ids or a dataset is passed because either source
191
191
  # already determines how many traces replay. Supplying trace_ids also
192
192
  # emits a warning.
193
193
  # @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100)
@@ -205,7 +205,15 @@ module Bitfab
205
205
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
206
206
  # replay runs into a single experiment batch
207
207
  # @param dataset_id [String, nil] optional UUID of the dataset this replay
208
- # runs against, stored on the resulting experiment for durable attribution
208
+ # runs against, stored on the resulting experiment for durable attribution;
209
+ # mutually exclusive with dataset_ids. Alone it replays the dataset's full
210
+ # membership, and with trace_ids it replays only those members
211
+ # @param dataset_ids [Array<String>, nil] optional UUIDs of the datasets this
212
+ # replay runs against, for benchmarking one method against several corpora
213
+ # in a single run; mutually exclusive with dataset_id. The run replays the
214
+ # union of their traces, graded by the union of their graders, and is
215
+ # attributed to every one of them. Passing trace_ids alongside it narrows
216
+ # the run to those members
209
217
  # @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
210
218
  # directly to this experiment, graded as the union with the dataset's
211
219
  # runnable graders at completion; each must be an active/live grader in the
@@ -248,7 +256,7 @@ module Bitfab
248
256
  # @return [Hash] with :items, :test_run_id, :test_run_url
249
257
  def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
250
258
  max_concurrency: 10, code_change_description: CODE_CHANGE_UNSET, code_change_files: CODE_CHANGE_UNSET, experiment_group_id: nil,
251
- dataset_id: nil, grader_ids: nil, mock: "marked",
259
+ dataset_id: nil, dataset_ids: nil, grader_ids: nil, mock: "marked",
252
260
  adapt_inputs: nil, mock_override: nil, db_branch: nil, on_item_start: nil, on_item_finish: nil, on_progress: nil)
253
261
  unless MOCK_STRATEGIES.include?(mock.to_s)
254
262
  raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
@@ -260,10 +268,7 @@ module Bitfab
260
268
  raise ArgumentError, "trace_ids supports at most 100 trace IDs per replay (got #{trace_ids.length})."
261
269
  end
262
270
  end
263
- if trace_ids && dataset_id
264
- raise ArgumentError,
265
- "trace_ids and dataset_id select different replay sources and cannot be used together."
266
- end
271
+ resolved_dataset_ids = resolve_dataset_ids(dataset_id, dataset_ids)
267
272
  if limit && trace_ids
268
273
  warn "Bitfab: limit is ignored when trace_ids is passed: the explicit trace ID list already " \
269
274
  "determines how many traces replay."
@@ -321,7 +326,7 @@ module Bitfab
321
326
  code_change_files:,
322
327
  experiment_group_id:,
323
328
  include_db_branch_lease:,
324
- dataset_id:,
329
+ dataset_ids: resolved_dataset_ids,
325
330
  grader_ids:,
326
331
  db_branch_settings: resolved_db_branch_settings
327
332
  )
@@ -472,6 +477,19 @@ module Bitfab
472
477
  CC_MAX_FILE_BYTES = 500_000
473
478
  CC_MAX_TOTAL_BYTES = 2_000_000
474
479
 
480
+ def resolve_dataset_ids(dataset_id, dataset_ids)
481
+ if dataset_id && dataset_ids
482
+ raise ArgumentError,
483
+ "dataset_id and dataset_ids are the same selector: pass one dataset through dataset_id, " \
484
+ "or several through dataset_ids."
485
+ end
486
+ return [dataset_id] if dataset_id
487
+ return nil if dataset_ids.nil?
488
+ raise ArgumentError, "dataset_ids must contain at least one dataset ID." if dataset_ids.empty?
489
+
490
+ dataset_ids.uniq
491
+ end
492
+
475
493
  # Whether the caller asked for database branching. +true+ and a Hash both
476
494
  # turn it on; +false+ and +nil+ leave it off. Kept separate from
477
495
  # +db_branch_settings+ because +db_branch: true+ enables branching while
@@ -20,7 +20,7 @@ module Bitfab
20
20
  class ReplayRegistry
21
21
  OPTION_NAMES = %i[
22
22
  limit trace_ids name max_concurrency code_change_description
23
- code_change_files experiment_group_id dataset_id grader_ids mock
23
+ code_change_files experiment_group_id dataset_id dataset_ids grader_ids mock
24
24
  mock_override adapt_inputs db_branch
25
25
  ].freeze
26
26
 
@@ -81,8 +81,26 @@ module Bitfab
81
81
 
82
82
  # Argument parsing and output for the SDK-owned replay command.
83
83
  module ReplayCli
84
+ MAX_PINNED_TRACE_IDS = 100
85
+
84
86
  module_function
85
87
 
88
+ def pinned_dataset_members(client, dataset_ids, limit)
89
+ members = dataset_ids.flat_map { |dataset_id|
90
+ client.datasets.list_traces(dataset_id).fetch("traceIds")
91
+ }.uniq.sort
92
+ return nil if limit >= members.length
93
+
94
+ if limit > MAX_PINNED_TRACE_IDS
95
+ raise ArgumentError,
96
+ "Bounding a dataset replay to #{limit} names one trace ID per replayed item, and " \
97
+ "a replay accepts at most #{MAX_PINNED_TRACE_IDS}. Drop the limit to replay " \
98
+ "every selected dataset in full."
99
+ end
100
+
101
+ members.first(limit)
102
+ end
103
+
86
104
  def run(registry, argv: ARGV, stdout: $stdout, stderr: $stderr)
87
105
  args = parse(registry, argv.dup)
88
106
  registration = registry.fetch(args.fetch(:pipeline))
@@ -97,29 +115,39 @@ module Bitfab
97
115
  options.merge!(dynamic_options)
98
116
  end
99
117
 
100
- if options[:trace_ids] && options[:dataset_id]
118
+ selected_dataset_ids = registry_dataset_ids(options)
119
+ options.delete(:dataset_id)
120
+ options.delete(:dataset_ids)
121
+ options[:dataset_ids] = selected_dataset_ids if selected_dataset_ids
122
+
123
+ if options[:trace_ids] && selected_dataset_ids
101
124
  raise ArgumentError,
102
- "Replay registry options trace_ids and dataset_id select different sources and cannot be used together."
125
+ "Replay registry options trace_ids and dataset_ids select different sources and cannot be used together."
103
126
  end
104
127
 
105
128
  if args[:trace_ids]
106
- options.delete(:limit)
107
- options.delete(:dataset_id)
129
+ options.delete(:dataset_ids)
108
130
  options[:trace_ids] = args[:trace_ids]
109
- elsif args[:dataset_id]
110
- options.delete(:trace_ids)
111
- options.delete(:limit)
112
- elsif args[:limit]
131
+ elsif args[:dataset_ids]
113
132
  options.delete(:trace_ids)
114
- options.delete(:dataset_id)
115
- options[:limit] = args[:limit]
116
- elsif options[:trace_ids] || options[:dataset_id]
117
- options.delete(:limit)
133
+ end
134
+
135
+ bound = args[:limit] || options[:limit]
136
+ options.delete(:limit)
137
+ bounded_dataset_ids = args[:dataset_ids] || options[:dataset_ids]
138
+
139
+ if options[:trace_ids]
140
+ options[:trace_ids] = options[:trace_ids].first(bound) if bound
141
+ elsif bounded_dataset_ids
142
+ if bound
143
+ pinned = pinned_dataset_members(registration.client, bounded_dataset_ids, bound)
144
+ options[:trace_ids] = pinned if pinned
145
+ end
118
146
  else
119
- options[:limit] = options.fetch(:limit, 10)
147
+ options[:limit] = bound || 10
120
148
  end
121
149
 
122
- %i[name max_concurrency experiment_group_id dataset_id grader_ids mock].each do |key|
150
+ %i[name max_concurrency experiment_group_id dataset_ids grader_ids mock].each do |key|
123
151
  options[key] = args[key] unless args[key].nil?
124
152
  end
125
153
 
@@ -145,8 +173,7 @@ module Bitfab
145
173
  options[:on_item_start] = reporter
146
174
  options[:on_item_finish] = reporter
147
175
 
148
- count = options[:trace_ids]&.length || options[:limit] || "dataset"
149
- stderr.puts "[replay] Replaying #{count} traces from \"#{registration.trace_function_key}\"..."
176
+ stderr.puts "[replay] Replaying #{describe_selection(options)} from \"#{registration.trace_function_key}\"..."
150
177
 
151
178
  result = begin
152
179
  registration.client.replay(
@@ -175,7 +202,10 @@ module Bitfab
175
202
  args = {}
176
203
  parser = OptionParser.new do |options|
177
204
  options.banner = "Usage: bitfab-replay <#{registry.names.join("|")}> [options]"
178
- options.on("--limit N", Integer) { |value| args[:limit] = positive_integer("--limit", value) }
205
+ options.on("--limit N", Integer,
206
+ "How many traces to replay. It bounds a trace ID or dataset selection instead of replacing it") do |value|
207
+ args[:limit] = positive_integer("--limit", value)
208
+ end
179
209
  options.on("--trace-ids IDS") { |value| args[:trace_ids] = comma_separated("--trace-ids", value) }
180
210
  options.on("--name NAME") { |value| args[:name] = value }
181
211
  options.on("--concurrency N", Integer) do |value|
@@ -186,7 +216,7 @@ module Bitfab
186
216
  end
187
217
  options.on("--code-change PATH") { |value| args[:code_change] = value }
188
218
  options.on("--experiment-group-id UUID") { |value| args[:experiment_group_id] = value }
189
- options.on("--dataset-id UUID") { |value| args[:dataset_id] = value }
219
+ options.on("--dataset-ids IDS", "--dataset-id IDS") { |value| args[:dataset_ids] = comma_separated("--dataset-ids", value) }
190
220
  options.on("--grader-ids IDS") { |value| args[:grader_ids] = comma_separated("--grader-ids", value) }
191
221
  options.on("--mock STRATEGY", %w[none all marked]) { |value| args[:mock] = value }
192
222
  options.on("--db-branch") { args[:db_branch] = true }
@@ -196,9 +226,9 @@ module Bitfab
196
226
  options.on("--param NAME=VALUE") { |value| (args[:param] ||= []) << value }
197
227
  end
198
228
  parser.parse!(argv)
199
- if args[:trace_ids] && args[:dataset_id]
229
+ if args[:trace_ids] && args[:dataset_ids]
200
230
  raise OptionParser::InvalidArgument,
201
- "--trace-ids and --dataset-id select different replay sources and cannot be used together"
231
+ "--trace-ids and --dataset-ids select different replay sources and cannot be used together"
202
232
  end
203
233
  if args[:code_change] && args[:no_code_change]
204
234
  raise OptionParser::InvalidArgument, "--code-change and --no-code-change cannot be used together"
@@ -210,6 +240,26 @@ module Bitfab
210
240
  args.merge(pipeline:)
211
241
  end
212
242
 
243
+ def registry_dataset_ids(options)
244
+ dataset_id = options[:dataset_id]
245
+ dataset_ids = options[:dataset_ids]
246
+ if dataset_id && dataset_ids
247
+ raise ArgumentError,
248
+ "Replay registry options dataset_id and dataset_ids are the same selector: set one of them."
249
+ end
250
+ return [dataset_id] if dataset_id
251
+
252
+ dataset_ids
253
+ end
254
+
255
+ def describe_selection(options)
256
+ return "#{options[:trace_ids].length} traces" if options[:trace_ids]
257
+ return "#{options[:limit]} traces" if options[:limit]
258
+
259
+ dataset_count = options[:dataset_ids]&.length || 1
260
+ (dataset_count > 1) ? "#{dataset_count} datasets' traces" : "dataset traces"
261
+ end
262
+
213
263
  def positive_integer(flag, value)
214
264
  raise OptionParser::InvalidArgument, "#{flag} must be a positive integer" if value < 1
215
265
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.43.1"
4
+ VERSION = "0.51.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitfab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.43.1
4
+ version: 0.51.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team