bitfab 0.20.1 → 0.21.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 +4 -4
- data/lib/bitfab/client.rb +9 -2
- data/lib/bitfab/replay.rb +64 -19
- data/lib/bitfab/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7536f774b491c131b04a135a41c5f753c7c507615e54aa675c314cff785b08df
|
|
4
|
+
data.tar.gz: 47e7804161bc40563f5aca99147ef43d2af1af6d73d22f6ad3ae3402ea4b51f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4aed7d077e6aa0ca89e8130b526578276591e60b41ae10670dc14db4eaca6221c30481dcad84acb8d7dfd7e549b2b92aa2c5a750efa36c4df85224b01360fe19
|
|
7
|
+
data.tar.gz: d73a3933eceba9c705fdab9f8b38f9f46766e026e59cdf61ee0ca2ea49a01ec4e5029fcbc87161a0a16602a2365ef3eeddba1acff27e659bf8fcf69b08d85138
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -59,14 +59,21 @@ module Bitfab
|
|
|
59
59
|
# onto the method's current signature when its shape changed after the
|
|
60
60
|
# traces were captured. Receives (args, kwargs, ctx) where ctx is
|
|
61
61
|
# { trace_id:, source_span_id: }, and returns [new_args, new_kwargs].
|
|
62
|
+
# @param on_progress [#call, nil] optional callback invoked once per item as
|
|
63
|
+
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
64
|
+
# errored: }. Use it to render replay progress (e.g. a terminal progress
|
|
65
|
+
# bar). Replay does not know pass/fail yet, so the totals only distinguish
|
|
66
|
+
# items whose method ran (:succeeded) from items that raised (:errored).
|
|
67
|
+
# A raising callback never crashes the run.
|
|
62
68
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
63
69
|
def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
64
70
|
code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
|
|
65
|
-
adapt_inputs: nil, environment: nil)
|
|
71
|
+
adapt_inputs: nil, environment: nil, on_progress: nil)
|
|
66
72
|
Replay.run(
|
|
67
73
|
self, receiver, method_name,
|
|
68
74
|
trace_function_key:, limit:, trace_ids:, max_concurrency:,
|
|
69
|
-
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, mock:, adapt_inputs:, environment
|
|
75
|
+
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, mock:, adapt_inputs:, environment:,
|
|
76
|
+
on_progress:
|
|
70
77
|
)
|
|
71
78
|
end
|
|
72
79
|
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -100,10 +100,14 @@ module Bitfab
|
|
|
100
100
|
# { trace_id:, source_span_id: }, and returns [new_args, new_kwargs]. Runs
|
|
101
101
|
# per item inside the same rescue as the method, so a raising adapter sets
|
|
102
102
|
# that item's :error rather than crashing the run.
|
|
103
|
+
# @param on_progress [#call, nil] optional callback invoked once per item as
|
|
104
|
+
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
105
|
+
# errored: }. Use it to render replay progress (e.g. a terminal progress
|
|
106
|
+
# bar). A raising callback never crashes the run.
|
|
103
107
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
104
108
|
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
105
109
|
code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "none",
|
|
106
|
-
adapt_inputs: nil, environment: nil)
|
|
110
|
+
adapt_inputs: nil, environment: nil, on_progress: nil)
|
|
107
111
|
unless MOCK_STRATEGIES.include?(mock.to_s)
|
|
108
112
|
raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
|
|
109
113
|
end
|
|
@@ -157,7 +161,7 @@ module Bitfab
|
|
|
157
161
|
|
|
158
162
|
result_items = if server_items.any?
|
|
159
163
|
process_items(http_client, server_items, receiver, method_name, test_run_id, max_concurrency, mock.to_s,
|
|
160
|
-
adapt_inputs, include_db_branch_lease)
|
|
164
|
+
adapt_inputs, include_db_branch_lease, on_progress:)
|
|
161
165
|
else
|
|
162
166
|
[]
|
|
163
167
|
end
|
|
@@ -170,6 +174,10 @@ module Bitfab
|
|
|
170
174
|
# loudly.
|
|
171
175
|
complete_response = http_client.complete_replay(test_run_id)
|
|
172
176
|
trace_id_map = complete_response&.dig("traceIds")
|
|
177
|
+
# Per-replay-trace token usage keyed by server trace id: the REPLAYED
|
|
178
|
+
# run's tokens (span-aggregated server-side), used below to fill each
|
|
179
|
+
# item's :tokens.
|
|
180
|
+
replay_tokens = complete_response&.dig("tokens") || {}
|
|
173
181
|
|
|
174
182
|
if trace_id_map.nil?
|
|
175
183
|
# Older servers don't return the mapping. Preserve the legacy
|
|
@@ -202,6 +210,9 @@ module Bitfab
|
|
|
202
210
|
completed_count += 1
|
|
203
211
|
missing << item[:trace_id] if mapped.nil?
|
|
204
212
|
end
|
|
213
|
+
# Pull this item's replayed-run tokens by its server trace id, before
|
|
214
|
+
# :trace_id is overwritten with that id below.
|
|
215
|
+
item[:tokens] = normalize_tokens(replay_tokens[mapped]) if mapped
|
|
205
216
|
item[:trace_id] = mapped
|
|
206
217
|
end
|
|
207
218
|
if missing.any?
|
|
@@ -230,13 +241,39 @@ module Bitfab
|
|
|
230
241
|
|
|
231
242
|
# Process all replay items, optionally in parallel using threads.
|
|
232
243
|
def process_items(http_client, server_items, receiver, method_name, test_run_id, max_concurrency, mock_strategy,
|
|
233
|
-
adapt_inputs = nil, include_db_branch_lease = false)
|
|
244
|
+
adapt_inputs = nil, include_db_branch_lease = false, on_progress: nil)
|
|
234
245
|
concurrency = max_concurrency || server_items.length
|
|
235
246
|
|
|
247
|
+
# Reports running totals once per item as it settles. In the parallel
|
|
248
|
+
# path it runs from worker threads, so the mutex both makes the counter
|
|
249
|
+
# updates safe and serializes the user's callback (never called
|
|
250
|
+
# concurrently). A raising callback is swallowed: progress UI must never
|
|
251
|
+
# crash the run.
|
|
252
|
+
total = server_items.length
|
|
253
|
+
progress_mutex = Mutex.new
|
|
254
|
+
completed = 0
|
|
255
|
+
succeeded = 0
|
|
256
|
+
errored = 0
|
|
257
|
+
report = lambda do |result|
|
|
258
|
+
return unless on_progress
|
|
259
|
+
|
|
260
|
+
progress_mutex.synchronize do
|
|
261
|
+
completed += 1
|
|
262
|
+
result[:error].nil? ? (succeeded += 1) : (errored += 1)
|
|
263
|
+
begin
|
|
264
|
+
on_progress.call({completed:, total:, succeeded:, errored:})
|
|
265
|
+
rescue => e
|
|
266
|
+
warn "Bitfab: replay on_progress callback raised: #{e.message}"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
236
271
|
if concurrency <= 1
|
|
237
272
|
server_items.map do |item|
|
|
238
|
-
process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
239
|
-
include_db_branch_lease)
|
|
273
|
+
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
274
|
+
adapt_inputs, include_db_branch_lease)
|
|
275
|
+
report.call(result)
|
|
276
|
+
result
|
|
240
277
|
end
|
|
241
278
|
else
|
|
242
279
|
results_mutex = Mutex.new
|
|
@@ -253,6 +290,7 @@ module Bitfab
|
|
|
253
290
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
254
291
|
adapt_inputs, include_db_branch_lease)
|
|
255
292
|
results_mutex.synchronize { results[idx] = result }
|
|
293
|
+
report.call(result)
|
|
256
294
|
end
|
|
257
295
|
end
|
|
258
296
|
end
|
|
@@ -386,27 +424,34 @@ module Bitfab
|
|
|
386
424
|
}
|
|
387
425
|
end
|
|
388
426
|
|
|
389
|
-
# Pull durationMs /
|
|
390
|
-
#
|
|
391
|
-
#
|
|
427
|
+
# Pull durationMs / model from the start-replay server item. Nil-safe
|
|
428
|
+
# defaults so older servers without these fields still produce a consistent
|
|
429
|
+
# shape. Tokens are intentionally NOT read from the start item (it carries
|
|
430
|
+
# the ORIGINAL trace's tokens); the replayed run's tokens are filled in by
|
|
431
|
+
# run() from the complete-replay response once spans are aggregated
|
|
432
|
+
# server-side, and stay nil here and on older servers.
|
|
392
433
|
def extract_server_item_metrics(server_item)
|
|
393
|
-
raw_tokens = server_item["tokens"]
|
|
394
|
-
tokens = if raw_tokens.is_a?(Hash)
|
|
395
|
-
{
|
|
396
|
-
input: raw_tokens["input"],
|
|
397
|
-
output: raw_tokens["output"],
|
|
398
|
-
cached: raw_tokens["cached"],
|
|
399
|
-
total: raw_tokens["total"]
|
|
400
|
-
}
|
|
401
|
-
end
|
|
402
|
-
|
|
403
434
|
{
|
|
404
435
|
duration_ms: server_item["durationMs"],
|
|
405
|
-
tokens
|
|
436
|
+
tokens: nil,
|
|
406
437
|
model: server_item["model"]
|
|
407
438
|
}
|
|
408
439
|
end
|
|
409
440
|
|
|
441
|
+
# Normalize a complete-replay tokens hash (string-keyed JSON) into the
|
|
442
|
+
# symbol-keyed shape the replay item exposes. Nil when the server reported
|
|
443
|
+
# no token data for this trace.
|
|
444
|
+
def normalize_tokens(raw_tokens)
|
|
445
|
+
return nil unless raw_tokens.is_a?(Hash)
|
|
446
|
+
|
|
447
|
+
{
|
|
448
|
+
input: raw_tokens["input"],
|
|
449
|
+
output: raw_tokens["output"],
|
|
450
|
+
cached: raw_tokens["cached"],
|
|
451
|
+
total: raw_tokens["total"]
|
|
452
|
+
}
|
|
453
|
+
end
|
|
454
|
+
|
|
410
455
|
# Execute a single replay item: deserialize inputs, call method with replay context.
|
|
411
456
|
def execute_item(item, receiver, method_name, test_run_id, input_source_span_id = nil, metrics = {},
|
|
412
457
|
input_source_trace_id: nil, mock_strategy: "none", mock_tree: nil, adapt_inputs: nil, adapt_ctx: nil,
|
data/lib/bitfab/version.rb
CHANGED