bitfab 0.22.1 → 0.23.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 +4 -4
- data/lib/bitfab/client.rb +74 -10
- data/lib/bitfab/http_client.rb +4 -1
- data/lib/bitfab/replay.rb +16 -5
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +5 -6
- 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: 3827f24f9f67b8264b588233b5e1eb3ffe6581dca039951a391253a87cf7c10b
|
|
4
|
+
data.tar.gz: 744d87d7188cb454c03242b95ff0ba7e1a0c0d53f131df9aff7449920ec69414
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a376efa4cba26a1aff6728acea5deded7982f12fcc60fe586dbc84850653a2649565bdf88d3ac49eb2ddaac78c4e257533a22aebf5fbe8b559f4dd0699e572f
|
|
7
|
+
data.tar.gz: 726b98d0e1516d621bed5bda08b26fc62a47fbdcc3b3bfd17777208513b3c5979213761aa1175cb7ec18d4351fe729f49562fcbea3ddf73d5d1744f25f308c25
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -20,21 +20,40 @@ module Bitfab
|
|
|
20
20
|
# outputs (which may themselves be nil or false).
|
|
21
21
|
MOCK_REPLAY_MISS = Object.new.freeze
|
|
22
22
|
|
|
23
|
-
attr_reader :
|
|
23
|
+
attr_reader :service_url
|
|
24
24
|
|
|
25
|
-
def initialize(api_key
|
|
26
|
-
@
|
|
25
|
+
def initialize(api_key: nil, service_url: nil, enabled: true, strict: false)
|
|
26
|
+
@api_key_config = api_key
|
|
27
27
|
@service_url = service_url || DEFAULT_SERVICE_URL
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@
|
|
28
|
+
# The user's on/off intent; effective enabled also requires a resolved key.
|
|
29
|
+
@explicitly_enabled = enabled
|
|
30
|
+
@strict = strict
|
|
31
|
+
# Cached only once a non-empty key is found, so an early resolve (before
|
|
32
|
+
# env loaded) can't poison a later one.
|
|
33
|
+
@resolved_api_key = nil
|
|
34
|
+
@api_key_warned = false
|
|
35
|
+
# The key is NOT read here. HttpClient gets a proc so the key is resolved
|
|
36
|
+
# at send time, after any env loading has run.
|
|
37
|
+
@http_client = HttpClient.new(api_key: -> { resolve_api_key }, service_url: @service_url)
|
|
34
38
|
@pending_span_threads = {}
|
|
35
39
|
@pending_span_mutex = Mutex.new
|
|
36
40
|
end
|
|
37
41
|
|
|
42
|
+
# The configured API key (a proc is resolved on read). Reflects what was
|
|
43
|
+
# passed to the client; the ENV["BITFAB_API_KEY"] fallback applied during
|
|
44
|
+
# actual tracing is not surfaced here, and reading this never warns.
|
|
45
|
+
def api_key
|
|
46
|
+
@api_key_config.respond_to?(:call) ? @api_key_config.call : @api_key_config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Effective tracing state, evaluated lazily: enabled only when not
|
|
50
|
+
# explicitly disabled AND a key resolves. Reading this resolves the key
|
|
51
|
+
# (and may emit the one-time empty-key warning), exactly as the first traced
|
|
52
|
+
# call would.
|
|
53
|
+
def enabled
|
|
54
|
+
tracing_enabled?
|
|
55
|
+
end
|
|
56
|
+
|
|
38
57
|
# Replay historical traces through a method and create a test run.
|
|
39
58
|
#
|
|
40
59
|
# @param receiver [Object, Class] an instance for instance methods, or a Class for class methods
|
|
@@ -100,7 +119,10 @@ module Bitfab
|
|
|
100
119
|
# Called by Traceable, not intended for direct use.
|
|
101
120
|
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:,
|
|
102
121
|
mock_on_replay: false)
|
|
103
|
-
|
|
122
|
+
# Decide at CALL time, not construction. The key may be set after the
|
|
123
|
+
# client is built (env loaded later), so re-checking per call lets a
|
|
124
|
+
# late-resolved key take effect.
|
|
125
|
+
return yield unless tracing_enabled?
|
|
104
126
|
|
|
105
127
|
# Span setup runs before the user's block. Tracing is a side-channel, so
|
|
106
128
|
# if anything here raises (id generation, trace-state bookkeeping, a
|
|
@@ -321,6 +343,48 @@ module Bitfab
|
|
|
321
343
|
|
|
322
344
|
private
|
|
323
345
|
|
|
346
|
+
# Resolve the API key lazily, the first time a span actually needs it.
|
|
347
|
+
#
|
|
348
|
+
# The key is intentionally NOT read at construction: a client built when a
|
|
349
|
+
# file is required can run before the app loads its env, so the key would be
|
|
350
|
+
# empty at construction even though it is set moments later. Resolution
|
|
351
|
+
# order: the configured value (a proc is called while still unresolved),
|
|
352
|
+
# then a fallback read of ENV["BITFAB_API_KEY"]. Once a non-empty key is
|
|
353
|
+
# found it is cached.
|
|
354
|
+
def resolve_api_key
|
|
355
|
+
return @resolved_api_key unless @resolved_api_key.nil?
|
|
356
|
+
|
|
357
|
+
from_config = @api_key_config.respond_to?(:call) ? @api_key_config.call : @api_key_config
|
|
358
|
+
candidate = if from_config && !from_config.to_s.strip.empty?
|
|
359
|
+
from_config
|
|
360
|
+
else
|
|
361
|
+
ENV["BITFAB_API_KEY"]
|
|
362
|
+
end
|
|
363
|
+
key = (candidate && !candidate.to_s.strip.empty?) ? candidate : nil
|
|
364
|
+
if key
|
|
365
|
+
@resolved_api_key = key
|
|
366
|
+
return key
|
|
367
|
+
end
|
|
368
|
+
if @strict
|
|
369
|
+
raise "Bitfab: no API key resolved. Set BITFAB_API_KEY or pass api_key: to " \
|
|
370
|
+
"Bitfab.configure. If a script loads env later, load it before the first " \
|
|
371
|
+
"traced call, or pass api_key: -> { ENV[\"BITFAB_API_KEY\"] }."
|
|
372
|
+
end
|
|
373
|
+
if @explicitly_enabled && !@api_key_warned
|
|
374
|
+
@api_key_warned = true
|
|
375
|
+
warn "Bitfab: api_key is empty: tracing is disabled. Provide a valid API key to enable tracing."
|
|
376
|
+
end
|
|
377
|
+
nil
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Whether tracing should run, decided lazily at call time. An explicit
|
|
381
|
+
# `enabled: false` short-circuits without ever touching the key.
|
|
382
|
+
def tracing_enabled?
|
|
383
|
+
return false unless @explicitly_enabled
|
|
384
|
+
|
|
385
|
+
!resolve_api_key.nil?
|
|
386
|
+
end
|
|
387
|
+
|
|
324
388
|
# Build an Enumerator that drives `source`, restoring `[trace_id, span_id]`
|
|
325
389
|
# on the iterating fiber so nested `bitfab_span` calls inside lazy / `each`
|
|
326
390
|
# callbacks nest under the parent span. Yielded values are collected as the
|
data/lib/bitfab/http_client.rb
CHANGED
|
@@ -226,9 +226,12 @@ module Bitfab
|
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
def headers
|
|
229
|
+
# Resolve the key at request time, invoking the proc form if one was
|
|
230
|
+
# supplied (never read at construction).
|
|
231
|
+
key = @api_key.respond_to?(:call) ? @api_key.call : @api_key
|
|
229
232
|
{
|
|
230
233
|
"Content-Type" => "application/json",
|
|
231
|
-
"Authorization" => "Bearer #{
|
|
234
|
+
"Authorization" => "Bearer #{key}"
|
|
232
235
|
}
|
|
233
236
|
end
|
|
234
237
|
end
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -266,7 +266,7 @@ module Bitfab
|
|
|
266
266
|
# SOURCE (historical) trace that was replayed, taken from the server
|
|
267
267
|
# item: the result's own :trace_id at this stage is the new replay
|
|
268
268
|
# trace id (assigned in run() after complete_replay), not the source.
|
|
269
|
-
report = lambda do |result, source_trace_id|
|
|
269
|
+
report = lambda do |result, source_trace_id, test_run_id|
|
|
270
270
|
return unless on_progress
|
|
271
271
|
|
|
272
272
|
progress_mutex.synchronize do
|
|
@@ -275,8 +275,19 @@ module Bitfab
|
|
|
275
275
|
error.nil? ? (succeeded += 1) : (errored += 1)
|
|
276
276
|
begin
|
|
277
277
|
on_progress.call({
|
|
278
|
-
completed:, total:, succeeded:, errored:,
|
|
279
|
-
item: {
|
|
278
|
+
test_run_id:, completed:, total:, succeeded:, errored:,
|
|
279
|
+
item: {
|
|
280
|
+
trace_id: source_trace_id,
|
|
281
|
+
replay_trace_id: result[:trace_id],
|
|
282
|
+
input: result[:input],
|
|
283
|
+
result: result[:result],
|
|
284
|
+
original_output: result[:original_output],
|
|
285
|
+
error:,
|
|
286
|
+
duration_ms: result[:duration_ms],
|
|
287
|
+
tokens: result[:tokens],
|
|
288
|
+
model: result[:model],
|
|
289
|
+
db_snapshot_ref: result[:db_snapshot_ref]
|
|
290
|
+
}
|
|
280
291
|
})
|
|
281
292
|
rescue => e
|
|
282
293
|
warn "Bitfab: replay on_progress callback raised: #{e.message}"
|
|
@@ -288,7 +299,7 @@ module Bitfab
|
|
|
288
299
|
server_items.map do |item|
|
|
289
300
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
290
301
|
adapt_inputs, include_db_branch_lease)
|
|
291
|
-
report.call(result, item["traceId"])
|
|
302
|
+
report.call(result, item["traceId"], test_run_id)
|
|
292
303
|
result
|
|
293
304
|
end
|
|
294
305
|
else
|
|
@@ -306,7 +317,7 @@ module Bitfab
|
|
|
306
317
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
307
318
|
adapt_inputs, include_db_branch_lease)
|
|
308
319
|
results_mutex.synchronize { results[idx] = result }
|
|
309
|
-
report.call(result, item["traceId"])
|
|
320
|
+
report.call(result, item["traceId"], test_run_id)
|
|
310
321
|
end
|
|
311
322
|
end
|
|
312
323
|
end
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -59,8 +59,8 @@ module Bitfab
|
|
|
59
59
|
# @example
|
|
60
60
|
# Bitfab.configure(api_key: ENV["BITFAB_API_KEY"])
|
|
61
61
|
#
|
|
62
|
-
def configure(api_key
|
|
63
|
-
@client = Client.new(api_key:, service_url:, enabled:)
|
|
62
|
+
def configure(api_key: nil, service_url: nil, enabled: true, strict: false)
|
|
63
|
+
@client = Client.new(api_key:, service_url:, enabled:, strict:)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Returns the global client, raising if not configured.
|
|
@@ -110,10 +110,9 @@ module Bitfab
|
|
|
110
110
|
# line per trace to stderr, which the Bitfab plugin polls to report live
|
|
111
111
|
# progress while the replay runs in the background, so replay scripts never
|
|
112
112
|
# hand-format the protocol. stdout stays the ReplayResult JSON. Never raises
|
|
113
|
-
# (progress must not crash a run). The progress hash's item
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# settled.
|
|
113
|
+
# (progress must not crash a run). The progress hash's item is forwarded
|
|
114
|
+
# verbatim through to_json, so the plugin sees the source trace id, local
|
|
115
|
+
# replay trace id, outputs, error, and duration of the item that just settled.
|
|
117
116
|
#
|
|
118
117
|
# @example
|
|
119
118
|
# Bitfab.client.replay(..., on_progress: Bitfab.method(:report_replay_progress))
|