bitfab 0.64.25 → 0.65.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/assertions.rb +53 -2
- data/lib/bitfab/{assertion_categories.rb → categories.rb} +6 -2
- data/lib/bitfab/client.rb +8 -2
- data/lib/bitfab/scenarios.rb +8 -2
- data/lib/bitfab/subtree.rb +9 -0
- data/lib/bitfab/traceable.rb +58 -11
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ed49d0b493c83442f21b1fc0ec1598cc675ce438601c357d9e3b16173c9e06d
|
|
4
|
+
data.tar.gz: fde85c743c635b053536c67ede3cced56eb560f92e13ed4014851399ef0f9fa1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed71773a0f5eb1b56f7245e6b77add9c61119ed38dabb2271f322e07b7cbda014d22f9167d52631ebdb99452f64916c1713ec0808042c43db8c1a37184ad5a34
|
|
7
|
+
data.tar.gz: 4539a52a91412534ef12b06ac326b14280ed09b241f203783c731c9ab5b8c4879f623b977ab71124ac2d1c90b4e8e29e27dbbdf7e6ba94ef3a14afe0d9599327
|
data/lib/bitfab/assertions.rb
CHANGED
|
@@ -4,22 +4,39 @@ require "uri"
|
|
|
4
4
|
|
|
5
5
|
module Bitfab
|
|
6
6
|
class Traces
|
|
7
|
+
CATEGORY_ID_KEYS = ["category_id", :category_id].freeze
|
|
8
|
+
DEPRECATED_CATEGORY_ID_KEYS = ["category_assertion_id", :category_assertion_id].freeze
|
|
9
|
+
private_constant :CATEGORY_ID_KEYS, :DEPRECATED_CATEGORY_ID_KEYS
|
|
10
|
+
|
|
7
11
|
def initialize(http_client)
|
|
8
12
|
@http_client = http_client
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
# Each returned assertion carries "category_id" and, with the same value,
|
|
16
|
+
# the deprecated "category_assertion_id".
|
|
11
17
|
def get_assertions(trace_id)
|
|
12
|
-
@http_client.get(assertions_path(trace_id))
|
|
18
|
+
response = @http_client.get(assertions_path(trace_id))
|
|
19
|
+
return response unless response.is_a?(Hash) && response["assertions"].is_a?(Array)
|
|
20
|
+
|
|
21
|
+
response.merge("assertions" => response["assertions"].map { |assertion| with_both_category_keys(assertion) })
|
|
13
22
|
end
|
|
14
23
|
|
|
24
|
+
# Assertion hashes take "category_id": omit it to keep the assignment, or
|
|
25
|
+
# pass nil to clear it. The deprecated "category_assertion_id" is still
|
|
26
|
+
# accepted and sent as "category_id"; "category_id" wins when both are set.
|
|
15
27
|
def save_assertions(trace_id, assertions, source: "agent")
|
|
16
28
|
save_assertions_all([{"traceId" => trace_id, "assertions" => assertions}], source:)
|
|
17
29
|
end
|
|
18
30
|
|
|
31
|
+
# Same assertion hash keys as {#save_assertions}, for several traces at once.
|
|
19
32
|
def save_assertions_all(updates, source: "agent")
|
|
20
33
|
return [] if updates.empty?
|
|
21
34
|
|
|
22
|
-
@http_client.request(
|
|
35
|
+
saved = @http_client.request(
|
|
36
|
+
"/api/sdk/traces/assertions",
|
|
37
|
+
{"updates" => updates.map { |update| with_new_category_key(update) }, "source" => source}
|
|
38
|
+
)["assertions"]
|
|
39
|
+
saved.is_a?(Array) ? saved.map { |assertion| with_both_category_keys(assertion) } : saved
|
|
23
40
|
end
|
|
24
41
|
|
|
25
42
|
def archive_assertions(trace_id, assertion_ids)
|
|
@@ -31,5 +48,39 @@ module Bitfab
|
|
|
31
48
|
def assertions_path(trace_id, suffix = "")
|
|
32
49
|
"/api/sdk/traces/#{URI.encode_www_form_component(trace_id).gsub("+", "%20")}/assertions#{suffix}"
|
|
33
50
|
end
|
|
51
|
+
|
|
52
|
+
def with_new_category_key(update)
|
|
53
|
+
return update unless update.is_a?(Hash)
|
|
54
|
+
|
|
55
|
+
key = ["assertions", :assertions].find { |candidate| update[candidate].is_a?(Array) }
|
|
56
|
+
return update if key.nil?
|
|
57
|
+
|
|
58
|
+
update.merge(key => update[key].map { |assertion| rename_category_key(assertion) })
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def rename_category_key(assertion)
|
|
62
|
+
return assertion unless assertion.is_a?(Hash)
|
|
63
|
+
|
|
64
|
+
old_key = DEPRECATED_CATEGORY_ID_KEYS.find { |candidate| assertion.key?(candidate) }
|
|
65
|
+
return assertion if old_key.nil?
|
|
66
|
+
|
|
67
|
+
Bitfab.warn_once("category-assertion-id-deprecated",
|
|
68
|
+
"category_assertion_id is deprecated; use category_id instead.")
|
|
69
|
+
renamed = assertion.except(*DEPRECATED_CATEGORY_ID_KEYS)
|
|
70
|
+
renamed["category_id"] = assertion[old_key] unless CATEGORY_ID_KEYS.any? { |candidate| assertion.key?(candidate) }
|
|
71
|
+
renamed
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def with_both_category_keys(assertion)
|
|
75
|
+
return assertion unless assertion.is_a?(Hash)
|
|
76
|
+
|
|
77
|
+
if assertion.key?("category_id")
|
|
78
|
+
assertion.key?("category_assertion_id") ? assertion : assertion.merge("category_assertion_id" => assertion["category_id"])
|
|
79
|
+
elsif assertion.key?("category_assertion_id")
|
|
80
|
+
assertion.merge("category_id" => assertion["category_assertion_id"])
|
|
81
|
+
else
|
|
82
|
+
assertion
|
|
83
|
+
end
|
|
84
|
+
end
|
|
34
85
|
end
|
|
35
86
|
end
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
require "uri"
|
|
4
4
|
|
|
5
5
|
module Bitfab
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# The organization's categories, which group assertions and scenarios.
|
|
7
|
+
class Categories
|
|
8
|
+
PATH = "/api/sdk/categories"
|
|
8
9
|
private_constant :PATH
|
|
9
10
|
|
|
10
11
|
def initialize(http_client)
|
|
@@ -36,4 +37,7 @@ module Bitfab
|
|
|
36
37
|
URI.encode_www_form_component(value).gsub("+", "%20")
|
|
37
38
|
end
|
|
38
39
|
end
|
|
40
|
+
|
|
41
|
+
# @deprecated Use {Categories}.
|
|
42
|
+
AssertionCategories = Categories
|
|
39
43
|
end
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -37,7 +37,13 @@ module Bitfab
|
|
|
37
37
|
|
|
38
38
|
attr_reader :service_url
|
|
39
39
|
|
|
40
|
-
attr_reader :datasets, :
|
|
40
|
+
attr_reader :datasets, :categories, :organization_members, :traces, :spans, :labels, :graders, :experiments
|
|
41
|
+
|
|
42
|
+
# @deprecated Use {#categories}. Returns the same object.
|
|
43
|
+
def assertion_categories
|
|
44
|
+
Bitfab.warn_once("assertion-categories-deprecated", "assertion_categories is deprecated; use categories instead.")
|
|
45
|
+
@categories
|
|
46
|
+
end
|
|
41
47
|
|
|
42
48
|
def initialize(api_key: nil, service_url: nil, capture_enabled: nil, enabled: nil, strict: false, simulation_plan: true, timeout: 120, trace_across_threads: nil, env_vars: nil, baml_client: nil, db_snapshot: nil)
|
|
43
49
|
@db_snapshot = DbSnapshot.normalize_config(db_snapshot)
|
|
@@ -54,7 +60,7 @@ module Bitfab
|
|
|
54
60
|
@http_client = HttpClient.new(api_key: -> { resolve_api_key }, service_url: @service_url, timeout:)
|
|
55
61
|
@http_client.configure_simulation_plan(enabled: simulation_plan, start: !api_key_resolved_lazily?)
|
|
56
62
|
@datasets = Datasets.new(@http_client)
|
|
57
|
-
@
|
|
63
|
+
@categories = Categories.new(@http_client)
|
|
58
64
|
@organization_members = OrganizationMembers.new(@http_client)
|
|
59
65
|
@traces = Traces.new(@http_client)
|
|
60
66
|
@spans = Spans.new(@http_client)
|
data/lib/bitfab/scenarios.rb
CHANGED
|
@@ -21,8 +21,13 @@ module Bitfab
|
|
|
21
21
|
# {"summary" => String, "actions" => [{"name", "details", "result", "failed", "at"}], "source" => Hash or nil}
|
|
22
22
|
# and replaces the saved value whole. Passing nil clears it. Leaving it out keeps it.
|
|
23
23
|
#
|
|
24
|
+
# category_ids: files the scenario under those categories and replaces its whole set of categories.
|
|
25
|
+
# Leave it out to keep the current set, and pass an empty array to clear it. The returned hash lists the
|
|
26
|
+
# scenario's categories under "categories", each {"id", "title", "description"}, sorted by title.
|
|
27
|
+
#
|
|
24
28
|
# Raises ArgumentError before any request when nothing is passed, or when data: and append_data: are both passed.
|
|
25
|
-
def save_scenario(trace_id, name: nil, description: nil, trigger: nil, data: nil, append_data: nil, actual_outcome: UNSET
|
|
29
|
+
def save_scenario(trace_id, name: nil, description: nil, trigger: nil, data: nil, append_data: nil, actual_outcome: UNSET,
|
|
30
|
+
category_ids: nil)
|
|
26
31
|
if !data.nil? && !append_data.nil?
|
|
27
32
|
raise ArgumentError, "Pass data or append_data, not both: data replaces the saved data whole."
|
|
28
33
|
end
|
|
@@ -34,8 +39,9 @@ module Bitfab
|
|
|
34
39
|
payload["data"] = data unless data.nil?
|
|
35
40
|
payload["appendData"] = append_data unless append_data.nil?
|
|
36
41
|
payload["actualOutcome"] = actual_outcome unless actual_outcome.equal?(UNSET)
|
|
42
|
+
payload["categoryIds"] = category_ids.to_a unless category_ids.nil?
|
|
37
43
|
if payload.empty?
|
|
38
|
-
raise ArgumentError, "Pass at least one of name, description, trigger, data, append_data, or
|
|
44
|
+
raise ArgumentError, "Pass at least one of name, description, trigger, data, append_data, actual_outcome, or category_ids: there is nothing to save otherwise."
|
|
39
45
|
end
|
|
40
46
|
|
|
41
47
|
@http_client.request(scenario_path(trace_id), payload)["scenario"]
|
data/lib/bitfab/subtree.rb
CHANGED
|
@@ -796,6 +796,8 @@ module Bitfab
|
|
|
796
796
|
end
|
|
797
797
|
|
|
798
798
|
def skip_boundary_call?(event)
|
|
799
|
+
return false if sdk_frame?(event.path)
|
|
800
|
+
|
|
799
801
|
boundary = @stacks[Fiber.current.object_id]&.last
|
|
800
802
|
if boundary.is_a?(Boundary) && boundary.skip_next_call &&
|
|
801
803
|
boundary.receiver.equal?(event.self) && boundary.method_name == called_method_id(event)
|
|
@@ -819,6 +821,13 @@ module Bitfab
|
|
|
819
821
|
true
|
|
820
822
|
end
|
|
821
823
|
|
|
824
|
+
def sdk_frame?(path)
|
|
825
|
+
@sdk_frames ||= {}
|
|
826
|
+
@sdk_frames.fetch(path) do
|
|
827
|
+
@sdk_frames[path] = Subtree.realpath(path)&.start_with?(SDK_PREFIX) || false
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
822
831
|
def passthrough_wrapper?(parameters)
|
|
823
832
|
kinds = parameters.map(&:first)
|
|
824
833
|
kinds.any? { |kind| kind == :rest || kind == :keyrest } &&
|
data/lib/bitfab/traceable.rb
CHANGED
|
@@ -108,7 +108,7 @@ module Bitfab
|
|
|
108
108
|
client: target_client, trace_function_key:, config: capture_config,
|
|
109
109
|
max_depth:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
|
|
110
110
|
root_receiver: self, root_method_name: method_name
|
|
111
|
-
) { super(*args, **kwargs, &block) }
|
|
111
|
+
) { Traceable.send(:as_root_call, self, method_name) { super(*args, **kwargs, &block) } }
|
|
112
112
|
end
|
|
113
113
|
call = Subtree::ConfiguredCall.new(receiver: self, method_name:, name: span_name,
|
|
114
114
|
type:, args:, kwargs:, nested_trace_key: trace_function_key)
|
|
@@ -122,12 +122,14 @@ module Bitfab
|
|
|
122
122
|
client: target_client, trace_function_key:, config: capture_config,
|
|
123
123
|
max_depth:, max_captured_subtree_spans:, include_wrappers:, mock_on_replay_default:,
|
|
124
124
|
root_receiver: self, root_method_name: method_name
|
|
125
|
-
) { super(*args, **kwargs, &block) }
|
|
125
|
+
) { Traceable.send(:as_root_call, self, method_name) { super(*args, **kwargs, &block) } }
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
wrapper.instance_variable_set(:@bitfab_span_method, method_name_str)
|
|
130
130
|
wrapper.instance_variable_set(:@bitfab_span_key, trace_function_key)
|
|
131
|
+
wrapper.instance_variable_set(:@bitfab_trace_method, method_name_str)
|
|
132
|
+
wrapper.instance_variable_set(:@bitfab_trace_client, bound_client)
|
|
131
133
|
preserve_visibility(klass, wrapper, method_name)
|
|
132
134
|
klass.prepend(wrapper)
|
|
133
135
|
end
|
|
@@ -142,9 +144,12 @@ module Bitfab
|
|
|
142
144
|
bound_client = client
|
|
143
145
|
wrapper = Module.new do
|
|
144
146
|
define_method(method_name) do |*args, **kwargs, &block|
|
|
147
|
+
next super(*args, **kwargs, &block) if Traceable.send(:own_span_call?, self, method_name)
|
|
148
|
+
|
|
145
149
|
Subtree.assert_surface!(:trace)
|
|
146
150
|
session = Subtree.current_session
|
|
147
|
-
if !session || (bound_client && !session.client.equal?(bound_client))
|
|
151
|
+
if !session || (bound_client && !session.client.equal?(bound_client)) ||
|
|
152
|
+
Traceable.send(:root_handles_call?, self, method_name, wrapper)
|
|
148
153
|
next super(*args, **kwargs, &block)
|
|
149
154
|
end
|
|
150
155
|
unless capture
|
|
@@ -176,6 +181,49 @@ module Bitfab
|
|
|
176
181
|
klass.prepend(wrapper)
|
|
177
182
|
end
|
|
178
183
|
|
|
184
|
+
ROOT_CALL_KEY = :bitfab_root_call
|
|
185
|
+
|
|
186
|
+
def self.as_root_call(receiver, method_name)
|
|
187
|
+
previous = Thread.current[ROOT_CALL_KEY]
|
|
188
|
+
Thread.current[ROOT_CALL_KEY] = [receiver, method_name.to_sym]
|
|
189
|
+
yield
|
|
190
|
+
ensure
|
|
191
|
+
Thread.current[ROOT_CALL_KEY] = previous
|
|
192
|
+
end
|
|
193
|
+
private_class_method :as_root_call
|
|
194
|
+
|
|
195
|
+
def self.root_handles_call?(receiver, method_name, node_wrapper)
|
|
196
|
+
marker = Thread.current[ROOT_CALL_KEY]
|
|
197
|
+
if marker && marker[0].equal?(receiver) && marker[1] == method_name.to_sym
|
|
198
|
+
Thread.current[ROOT_CALL_KEY] = nil
|
|
199
|
+
return true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
trace_wrapper = inner_trace_wrapper(receiver, method_name, node_wrapper)
|
|
203
|
+
return false unless trace_wrapper
|
|
204
|
+
|
|
205
|
+
client = trace_wrapper.instance_variable_get(:@bitfab_trace_client) || Bitfab.client_or_nil
|
|
206
|
+
(client&.send(:should_record?) && !Subtree.nested_root_suppressed?) ? true : false
|
|
207
|
+
end
|
|
208
|
+
private_class_method :root_handles_call?
|
|
209
|
+
|
|
210
|
+
def self.own_span_call?(receiver, method_name)
|
|
211
|
+
span = SpanContext.current
|
|
212
|
+
return false unless span && span[:surface] == :span
|
|
213
|
+
|
|
214
|
+
span[:explicit_span_receiver].equal?(receiver) && span[:explicit_span_method_name] == method_name
|
|
215
|
+
end
|
|
216
|
+
private_class_method :own_span_call?
|
|
217
|
+
|
|
218
|
+
def self.inner_trace_wrapper(receiver, method_name, node_wrapper)
|
|
219
|
+
ancestors = receiver.is_a?(Module) ? receiver.singleton_class.ancestors : receiver.class.ancestors
|
|
220
|
+
name = method_name.to_s
|
|
221
|
+
ancestors.drop_while { |mod| !mod.equal?(node_wrapper) }.find do |mod|
|
|
222
|
+
mod.instance_variable_defined?(:@bitfab_trace_method) && mod.instance_variable_get(:@bitfab_trace_method) == name
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
private_class_method :inner_trace_wrapper
|
|
226
|
+
|
|
179
227
|
def self.source_of(klass, method_name)
|
|
180
228
|
path, line = klass.instance_method(method_name).source_location || [nil, nil]
|
|
181
229
|
[path, line, method_name]
|
|
@@ -345,15 +393,14 @@ module Bitfab
|
|
|
345
393
|
|
|
346
394
|
def method_added(method_name)
|
|
347
395
|
super
|
|
396
|
+
if defined?(@_bitfab_pending_nodes) && @_bitfab_pending_nodes&.key?(method_name)
|
|
397
|
+
_bitfab_node_method(method_name, **@_bitfab_pending_nodes.delete(method_name))
|
|
398
|
+
end
|
|
348
399
|
if defined?(@_bitfab_pending_spans) && @_bitfab_pending_spans&.key?(method_name)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
_bitfab_trace_method(method_name, **config)
|
|
354
|
-
elsif defined?(@_bitfab_pending_nodes) && @_bitfab_pending_nodes&.key?(method_name)
|
|
355
|
-
config = @_bitfab_pending_nodes.delete(method_name)
|
|
356
|
-
_bitfab_node_method(method_name, **config)
|
|
400
|
+
_bitfab_wrap_method(method_name, **@_bitfab_pending_spans.delete(method_name))
|
|
401
|
+
end
|
|
402
|
+
if defined?(@_bitfab_pending_traces) && @_bitfab_pending_traces&.key?(method_name)
|
|
403
|
+
_bitfab_trace_method(method_name, **@_bitfab_pending_traces.delete(method_name))
|
|
357
404
|
end
|
|
358
405
|
end
|
|
359
406
|
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -15,7 +15,7 @@ require_relative "bitfab/git_state"
|
|
|
15
15
|
require_relative "bitfab/span_context"
|
|
16
16
|
require_relative "bitfab/http_client"
|
|
17
17
|
require_relative "bitfab/datasets"
|
|
18
|
-
require_relative "bitfab/
|
|
18
|
+
require_relative "bitfab/categories"
|
|
19
19
|
require_relative "bitfab/organization_members"
|
|
20
20
|
require_relative "bitfab/traces"
|
|
21
21
|
require_relative "bitfab/spans"
|
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.
|
|
4
|
+
version: 0.65.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harvest Team
|
|
@@ -155,9 +155,9 @@ files:
|
|
|
155
155
|
- exe/bitfab-replay
|
|
156
156
|
- exe/bitfab-seed
|
|
157
157
|
- lib/bitfab.rb
|
|
158
|
-
- lib/bitfab/assertion_categories.rb
|
|
159
158
|
- lib/bitfab/assertions.rb
|
|
160
159
|
- lib/bitfab/baml.rb
|
|
160
|
+
- lib/bitfab/categories.rb
|
|
161
161
|
- lib/bitfab/client.rb
|
|
162
162
|
- lib/bitfab/cloudReplay.py
|
|
163
163
|
- lib/bitfab/cloud_replay_cli.rb
|