bitfab 0.33.2 → 0.33.4

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: f3dd02ac43d3ed65f11a6dcf52aa0d0d3a331fa428fb568a74147f5007fc9e22
4
- data.tar.gz: 94b0cc1645fc42ce929a4fd1363de7ededc908f20dd411d3e7f8fd68d3bed0f8
3
+ metadata.gz: 12cae09488f9200115e768e31661e55be850acc2dbcf2b53eece9c4ab4d59ca2
4
+ data.tar.gz: 3242cb27cbd71d15094bc4024cb914a494eefa2eeee317e1d45c8ba77c77f4a0
5
5
  SHA512:
6
- metadata.gz: b3a07fecf4731278963e8db381a0bfc6b4677d17bd2464f36f1d5ccfd455a8d3f066273502674158c3ac3b99a180f3017b0c5185f8e6ff3795c87b7fd2c612cb
7
- data.tar.gz: 38e33c8a2dc6ab7e2e6a4c416bea15828a81724eb8ffe4b0fb440088e3b05fea53abaded2fa46bf2e852f7fd307cd5216d149b296b321a5399861861b08bd8d1
6
+ metadata.gz: 11f4111de6c94527b2f4b14757b50ccaddea211d3366bf1a5241e8e7e0ffa065138a84fa7f6db84baaec926cfeb9754fe7024a090b297ecb35e7916a1fc517f1
7
+ data.tar.gz: 880b440e2610064045ff1e753d65167c46be2a7220897aa0631b739e24affec2e99cd05de12acf2d39f162474d8fbec6ff13b614f60c6390529395ad2478e4ab
data/lib/bitfab/client.rb CHANGED
@@ -76,9 +76,14 @@ module Bitfab
76
76
  # @param trace_ids [Array<String>, nil] optional list of trace IDs to replay (max 100)
77
77
  # @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
78
78
  # @param code_change_description [String, nil] optional rationale for the
79
- # code change being tested in this replay (stored on the experiment)
79
+ # code change being tested in this replay (stored on the experiment).
80
+ # When supplied without code_change_files, this text is preserved while
81
+ # the SDK automatically captures the files.
82
+ # Pass nil when the replay should have no description; use
83
+ # code_change_files: nil to suppress automatic file capture.
80
84
  # @param code_change_files [Array<Hash>, nil] optional list of edited files,
81
- # each as { path:, before:, after: } (use "" for new/deleted files)
85
+ # each as { path:, before:, after: } (use "" for new/deleted files).
86
+ # Omit to capture automatically, or pass nil to suppress file capture.
82
87
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
83
88
  # replay runs into a single experiment batch
84
89
  # @param dataset_id [String, nil] optional UUID of the dataset this replay
@@ -118,7 +123,7 @@ module Bitfab
118
123
  # A raising callback never crashes the run.
119
124
  # @return [Hash] with :items, :test_run_id, :test_run_url
120
125
  def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
121
- name: nil, code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, grader_ids: nil, mock: "marked",
126
+ 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",
122
127
  adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
123
128
  Replay.run(
124
129
  self, receiver, method_name,
@@ -238,12 +243,23 @@ module Bitfab
238
243
  # Execute a block inside a span context, sending trace data on completion.
239
244
  # Called by Traceable, not intended for direct use.
240
245
  def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:,
241
- mock_on_replay: false)
246
+ capture_when: "always", mock_on_replay: false)
242
247
  # Decide at CALL time, not construction. The key may be set after the
243
248
  # client is built (env loaded later), so re-checking per call lets a
244
249
  # late-resolved key take effect.
245
250
  return yield unless tracing_enabled?
246
251
 
252
+ resolved_capture_when = capture_when.to_s
253
+ unless %w[always nested].include?(resolved_capture_when)
254
+ Bitfab.warn_once(
255
+ "invalid-capture-when:#{trace_function_key}",
256
+ "unknown capture_when value #{capture_when.inspect}; defaulting to \"always\". " \
257
+ "Valid values: \"always\", \"nested\"."
258
+ )
259
+ resolved_capture_when = "always"
260
+ end
261
+ return yield if resolved_capture_when == "nested" && SpanContext.current.nil?
262
+
247
263
  # Span setup runs before the user's block. Tracing is a side-channel, so
248
264
  # if anything here raises (id generation, trace-state bookkeeping, a
249
265
  # malformed replay mock tree) the user's method must still run. On failure
@@ -887,12 +903,15 @@ module Bitfab
887
903
  # @param method_name [Symbol] the method to wrap
888
904
  # @param name [String, nil] explicit span name (defaults to method name)
889
905
  # @param type [String] span type
906
+ # @param capture_when [String, Symbol] "always" to allow a root trace, or
907
+ # "nested" to capture only when another Bitfab span is active. Unknown
908
+ # values warn once and default to "always"
890
909
  # @param mock_on_replay [Boolean] mark this span for the default "marked" mock strategy
891
- def wrap(klass, method_name, name: nil, type: "custom", mock_on_replay: false)
910
+ def wrap(klass, method_name, name: nil, type: "custom", capture_when: "always", mock_on_replay: false)
892
911
  Bitfab::Traceable.wrap(
893
912
  klass, method_name,
894
913
  trace_function_key: @trace_function_key,
895
- name:, type:, mock_on_replay:,
914
+ name:, type:, capture_when:, mock_on_replay:,
896
915
  client: @client
897
916
  )
898
917
  end
data/lib/bitfab/replay.rb CHANGED
@@ -81,6 +81,8 @@ module Bitfab
81
81
 
82
82
  # Replay historical traces through a traced method and create a test run.
83
83
  module Replay
84
+ CODE_CHANGE_UNSET = Object.new.freeze
85
+
84
86
  module_function
85
87
 
86
88
  # Replay historical traces through a method and create a test run.
@@ -99,9 +101,14 @@ module Bitfab
99
101
  # @param name [String, nil] optional display name for the resulting experiment/test run
100
102
  # @param max_concurrency [Integer, nil] max threads for parallel replay (default: 10)
101
103
  # @param code_change_description [String, nil] optional rationale for the
102
- # code change being tested in this replay (stored on the experiment)
104
+ # code change being tested in this replay (stored on the experiment).
105
+ # When supplied without code_change_files, this text is preserved while
106
+ # the SDK automatically captures the files.
107
+ # Pass nil when the replay should have no description; use
108
+ # code_change_files: nil to suppress automatic file capture.
103
109
  # @param code_change_files [Array<Hash>, nil] optional list of edited files,
104
- # each as { path:, before:, after: } (empty string for new/deleted files)
110
+ # each as { path:, before:, after: } (empty string for new/deleted files).
111
+ # Omit to capture automatically, or pass nil to suppress file capture.
105
112
  # @param experiment_group_id [String, nil] optional UUID grouping multiple
106
113
  # replay runs into a single experiment batch
107
114
  # @param dataset_id [String, nil] optional UUID of the dataset this replay
@@ -140,7 +147,7 @@ module Bitfab
140
147
  # crashes the run.
141
148
  # @return [Hash] with :items, :test_run_id, :test_run_url
142
149
  def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
143
- max_concurrency: 10, code_change_description: nil, code_change_files: nil, experiment_group_id: nil,
150
+ max_concurrency: 10, code_change_description: CODE_CHANGE_UNSET, code_change_files: CODE_CHANGE_UNSET, experiment_group_id: nil,
144
151
  dataset_id: nil, grader_ids: nil, mock: "marked",
145
152
  adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
146
153
  unless MOCK_STRATEGIES.include?(mock.to_s)
@@ -187,18 +194,18 @@ module Bitfab
187
194
 
188
195
  include_db_branch_lease = !db_branch.nil?
189
196
 
190
- # An explicit code change always wins; only when the caller passed neither
191
- # field do we fall back to the payload the replay wrapper captured from git
192
- # and handed over via BITFAB_CODE_CHANGE_PATH. This is what makes an
193
- # experiment show its diff even when the run happened outside the
194
- # assistant's make-change rails.
195
- if code_change_description.nil? && code_change_files.nil?
197
+ # code_change_files controls capture: omitted auto-captures, an array
198
+ # wins, and explicit nil opts out. Preserve a caller-supplied description
199
+ # while filling only the omitted files.
200
+ if code_change_files.equal?(CODE_CHANGE_UNSET)
196
201
  captured = resolve_auto_code_change(name)
197
202
  unless captured.nil?
198
- code_change_description = captured[0]
199
203
  code_change_files = captured[1]
204
+ code_change_description = captured[0] if code_change_description.equal?(CODE_CHANGE_UNSET)
200
205
  end
201
206
  end
207
+ code_change_description = nil if code_change_description.equal?(CODE_CHANGE_UNSET)
208
+ code_change_files = nil if code_change_files.equal?(CODE_CHANGE_UNSET)
202
209
 
203
210
  resolved_db_branch_settings = db_branch_settings(db_branch)
204
211
  replay_data = http_client.start_replay(
@@ -38,6 +38,9 @@ module Bitfab
38
38
  # @param trace_function_key [String] the trace function key
39
39
  # @param name [String, nil] explicit span name (defaults to method name)
40
40
  # @param type [String] span type: llm, agent, function, guardrail, handoff, custom
41
+ # @param capture_when [String, Symbol] "always" to allow a root trace, or
42
+ # "nested" to capture only when another Bitfab span is active. Unknown
43
+ # values warn once and default to "always"
41
44
  # @param mock_on_replay [Boolean] mark this span for the default "marked" mock strategy.
42
45
  # When true, `client.replay(...)` returns this span's
43
46
  # historical output instead of executing the wrapped method.
@@ -48,7 +51,7 @@ module Bitfab
48
51
  # preserve the bound client through the fluent wrapper, matching Python's
49
52
  # `BitfabFunction.span()` and TypeScript's `BitfabFunction.withSpan()`.
50
53
  def self.wrap(klass, method_name, trace_function_key:, name: nil, type: "custom",
51
- mock_on_replay: false, client: nil)
54
+ capture_when: "always", mock_on_replay: false, client: nil)
52
55
  span_name = name || method_name.to_s
53
56
  method_name_str = method_name.to_s
54
57
  bound_client = client
@@ -63,6 +66,7 @@ module Bitfab
63
66
  function_name: method_name_str,
64
67
  args:,
65
68
  kwargs:,
69
+ capture_when:,
66
70
  mock_on_replay:) do
67
71
  super(*args, **kwargs, &block)
68
72
  end
@@ -132,10 +136,14 @@ module Bitfab
132
136
  # @param trace_function_key [String, nil] trace function key (overrides class-level bitfab_function)
133
137
  # @param name [String, nil] explicit span name (defaults to method name)
134
138
  # @param type [String] span type: llm, agent, function, guardrail, handoff, custom
139
+ # @param capture_when [String, Symbol] "always" to allow a root trace, or
140
+ # "nested" to capture only when another Bitfab span is active. Unknown
141
+ # values warn once and default to "always"
135
142
  # @param mock_on_replay [Boolean] mark this span for the default "marked" mock strategy.
136
143
  # When true, `client.replay(...)` returns this span's
137
144
  # historical output instead of executing the wrapped method.
138
- def bitfab_span(method_name, trace_function_key: nil, name: nil, type: "custom", mock_on_replay: false)
145
+ def bitfab_span(method_name, trace_function_key: nil, name: nil, type: "custom",
146
+ capture_when: "always", mock_on_replay: false)
139
147
  trace_function_key ||= @bitfab_function_key
140
148
  unless trace_function_key
141
149
  raise "No trace function key provided. Pass `trace_function_key:` to `bitfab_span` " \
@@ -144,7 +152,9 @@ module Bitfab
144
152
 
145
153
  # If the method already exists (inline or after-method style), wrap it immediately
146
154
  if method_defined?(method_name) || private_method_defined?(method_name)
147
- _bitfab_wrap_method(method_name, trace_function_key:, name:, type:, mock_on_replay:)
155
+ _bitfab_wrap_method(
156
+ method_name, trace_function_key:, name:, type:, capture_when:, mock_on_replay:
157
+ )
148
158
  else
149
159
  # Method doesn't exist yet (before-method style) - register for method_added hook
150
160
  @_bitfab_pending_spans ||= {}
@@ -152,6 +162,7 @@ module Bitfab
152
162
  trace_function_key:,
153
163
  name:,
154
164
  type:,
165
+ capture_when:,
155
166
  mock_on_replay:
156
167
  }
157
168
  end
@@ -167,7 +178,8 @@ module Bitfab
167
178
  _bitfab_wrap_method(method_name, **config)
168
179
  end
169
180
 
170
- def _bitfab_wrap_method(method_name, trace_function_key:, name: nil, type: "custom", mock_on_replay: false)
181
+ def _bitfab_wrap_method(method_name, trace_function_key:, name: nil, type: "custom",
182
+ capture_when: "always", mock_on_replay: false)
171
183
  span_name = name || method_name.to_s
172
184
  method_name_str = method_name.to_s
173
185
 
@@ -185,6 +197,7 @@ module Bitfab
185
197
  function_name: method_name_str,
186
198
  args:,
187
199
  kwargs:,
200
+ capture_when:,
188
201
  mock_on_replay:) do
189
202
  super(*args, **kwargs, &block)
190
203
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.33.2"
4
+ VERSION = "0.33.4"
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.33.2
4
+ version: 0.33.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team