bitfab 0.58.3 → 0.59.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 959d7652dd7c6e3fdfea18c79e759300b2ebf826401f0e0601ea3642eae84034
4
- data.tar.gz: bc7820f643314689a9440247edf0e81d4c234208ce1c903db7a9cb904da685fe
3
+ metadata.gz: 0f71ead77c070efe05df2af12495ef411048fd0164655e0f1fbe11a5457b518c
4
+ data.tar.gz: 4e9a222f559e2977366ecf411f97429db22f89d1aca6b8b6de767b982594f8ce
5
5
  SHA512:
6
- metadata.gz: a92544b809ea07e087e55b031d82130baf820f636cf41b4d90f4d1a0d1d3513024d733c6cdbe57e74dd7832552c0029816424f25c0fe65e1e585168b8908221e
7
- data.tar.gz: 691217d535f95027773b65454c40470901a393aef97906283fe9f33ea38a05bd21b70105dbc8d0344c9fffaa1fb6839ecc99fc5b7480c879761189ca343e0b0d
6
+ metadata.gz: 406d7de1521d252c335432fa44c04fd7f3e11f0992281388d8939140ab05b5b055cb3cd5a4dc6bf30cfba15a40795f7c1f21782d989bbbebd1abeac62e5ff7fc
7
+ data.tar.gz: 9e80632bfc746766ffd6011ceb45848a2875590e29b2f9c983b4c4ed344b722c310a15a760b6d4bd99045357f9def49b08904bb53618ad0dedbf5b33c2c4cffe
data/lib/bitfab/client.rb CHANGED
@@ -425,6 +425,13 @@ module Bitfab
425
425
  span_id = SecureRandom.uuid
426
426
  parent_span_id = parent&.dig(:span_id)
427
427
  is_root_span = parent_span_id.nil?
428
+ ancestor_span_id = nearest_captured_ancestor_span_id
429
+ open_instrumentation = (surface == :trace) ? "trace" : "span"
430
+ sends_on_completion = lambda do
431
+ is_root_span || declared_node || !content_off_by_simulation_plan?(
432
+ trace_id:, trace_function_key:, span_name:, instrumentation: open_instrumentation
433
+ )
434
+ end
428
435
  started_at = Bitfab.now_iso_timestamp
429
436
  resolved_test_run_id = replay_ctx ? replay_ctx[:test_run_id] : test_run_id
430
437
  resolved_input_source_span_id = replay_ctx&.dig(:input_source_span_id)
@@ -550,6 +557,7 @@ module Bitfab
550
557
  span_data: content_off ? span_data.merge("content_off_by_simulation_plan" => true) : span_data,
551
558
  instrumentation: recorded_instrumentation,
552
559
  declared_node:,
560
+ nearest_captured_ancestor_span_id: ancestor_span_id,
553
561
  trace_function_key:,
554
562
  trace_id:,
555
563
  span_id:,
@@ -618,7 +626,8 @@ module Bitfab
618
626
  span_id:,
619
627
  explicit_span_receiver:,
620
628
  explicit_span_method_name: explicit_span_method_name&.to_sym,
621
- surface:, recording_override:, content_withheld:
629
+ surface:, recording_override:, content_withheld:,
630
+ sends_on_completion: sends_on_completion
622
631
  ) do
623
632
  invoke = -> { around_body ? around_body.call { yield } : yield }
624
633
  result = if explicit_span_receiver
@@ -683,6 +692,14 @@ module Bitfab
683
692
  raise ArgumentError, "id must be a valid Bitfab span ID" unless id.is_a?(String) && UUID_PATTERN.match?(id)
684
693
  end
685
694
 
695
+ def nearest_captured_ancestor_span_id
696
+ SpanContext.stack.reverse_each do |entry|
697
+ sends = entry[:sends_on_completion]
698
+ return entry[:span_id] if sends.nil? || sends.call
699
+ end
700
+ nil
701
+ end
702
+
686
703
  def content_off_by_simulation_plan?(trace_id:, trace_function_key:, span_name:, instrumentation: "span")
687
704
  return false if SimulationPlan::FRAMEWORKS.include?(instrumentation)
688
705
 
@@ -927,7 +944,8 @@ module Bitfab
927
944
  def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
928
945
  span_name:, span_type:, function_name:, contexts:, prompt:, args:, kwargs:, result:, error:,
929
946
  started_at:, ended_at:, test_run_id: nil, input_source_span_id: nil, mocked: false,
930
- mock_target: nil, mock_source: nil, span_data: {}, instrumentation: "span", declared_node: false)
947
+ mock_target: nil, mock_source: nil, span_data: {}, instrumentation: "span", declared_node: false,
948
+ nearest_captured_ancestor_span_id: nil)
931
949
  extra_span_data = span_data
932
950
  # If drop() was called on this trace, suppress the span PAYLOAD upload for
933
951
  # every span that completes after the flag was set. The trace completion
@@ -983,6 +1001,9 @@ module Bitfab
983
1001
  "span_origin" => Bitfab.make_span_origin(instrumentation)
984
1002
  }
985
1003
  raw_span["parent_id"] = parent_span_id if parent_span_id
1004
+ if nearest_captured_ancestor_span_id && nearest_captured_ancestor_span_id != parent_span_id
1005
+ raw_span["nearest_captured_ancestor_id"] = nearest_captured_ancestor_span_id
1006
+ end
986
1007
  raw_span["input_source_span_id"] = input_source_span_id if input_source_span_id
987
1008
 
988
1009
  payload = {
@@ -1,6 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
+ class GraderRerunError < StandardError
5
+ attr_reader :run
6
+
7
+ def initialize(message, run:)
8
+ super(message)
9
+ @run = run
10
+ end
11
+ end
12
+
13
+ class GraderRerunTimeoutError < GraderRerunError; end
14
+
4
15
  # Dataset operations for the authenticated organization, reached as
5
16
  # `client.datasets`. A dataset is a named bucket of traces scoped to one
6
17
  # trace function. Experiments replay against it and its graders score its
@@ -8,7 +19,8 @@ module Bitfab
8
19
  # string keys matching the HTTP API ("traceCount", "addedTraceIds", ...).
9
20
  class Datasets
10
21
  DEFAULT_RERUN_TIMEOUT_SECONDS = 90
11
- DEFAULT_RERUN_POLL_INTERVAL_SECONDS = 1
22
+ RERUN_POLL_INTERVAL_SECONDS = 1.0
23
+ private_constant :RERUN_POLL_INTERVAL_SECONDS
12
24
  TERMINAL_RERUN_STATUSES = ["completed", "errored"].freeze
13
25
 
14
26
  def initialize(http_client)
@@ -86,22 +98,21 @@ module Bitfab
86
98
  end
87
99
 
88
100
  # Re-run graders over every trace in the dataset. Defaults to every
89
- # assigned grader, and an unassigned id is rejected. Waits for the run to
90
- # finish (up to `timeout` seconds) unless `wait:` is false, and returns the
91
- # last run state seen either way. A request matching an in-flight run
92
- # joins it.
93
- def rerun_graders(dataset_id, grader_ids: nil, wait: true,
94
- timeout: DEFAULT_RERUN_TIMEOUT_SECONDS, poll_interval: DEFAULT_RERUN_POLL_INTERVAL_SECONDS)
101
+ # assigned grader, and an unassigned id is rejected. Blocks until the run
102
+ # completes and returns it. Raises GraderRerunError when the run errors and
103
+ # GraderRerunTimeoutError when it is still going after `timeout` seconds.
104
+ # The run keeps going on Bitfab after a timeout. A request matching an
105
+ # in-flight run joins it.
106
+ def rerun_graders(dataset_id, grader_ids: nil, timeout: DEFAULT_RERUN_TIMEOUT_SECONDS)
95
107
  payload = grader_ids.nil? ? {} : {"graderIds" => grader_ids}
96
108
  started = @http_client.request(dataset_path(dataset_id, "/rerunGraders"), payload)
97
- return started unless wait
98
-
99
109
  deadline = monotonic_now + timeout
100
110
  run = started["run"]
101
111
  while !TERMINAL_RERUN_STATUSES.include?(run["status"]) && monotonic_now < deadline
102
- sleep(poll_interval)
112
+ sleep(RERUN_POLL_INTERVAL_SECONDS)
103
113
  run = get_grader_rerun(dataset_id, run_id: run["id"]) || run
104
114
  end
115
+ settle_grader_rerun(dataset_id, run, timeout)
105
116
  {"run" => run, "joinedExisting" => started["joinedExisting"]}
106
117
  end
107
118
 
@@ -114,6 +125,24 @@ module Bitfab
114
125
 
115
126
  private
116
127
 
128
+ def settle_grader_rerun(dataset_id, run, timeout)
129
+ case run["status"]
130
+ when "completed"
131
+ nil
132
+ when "errored"
133
+ raise GraderRerunError.new(
134
+ "Grader re-run on dataset #{dataset_id} errored: #{run["error"] || "no reason recorded"}",
135
+ run:
136
+ )
137
+ else
138
+ raise GraderRerunTimeoutError.new(
139
+ "Grader re-run #{run["id"]} on dataset #{dataset_id} is still #{run["status"]} after #{timeout} seconds. " \
140
+ "It keeps going on Bitfab. Read it later with get_grader_rerun(#{dataset_id.inspect}, run_id: #{run["id"].inspect}).",
141
+ run:
142
+ )
143
+ end
144
+ end
145
+
117
146
  def dataset_path(dataset_id, suffix = "")
118
147
  "/api/sdk/datasets/#{URI.encode_www_form_component(dataset_id)}#{suffix}"
119
148
  end
@@ -150,8 +150,9 @@ module Bitfab
150
150
 
151
151
  # Execute a block with a new span pushed onto the stack.
152
152
  # The span is automatically popped when the block completes.
153
- def with_span(trace_id:, span_id:, explicit_span_receiver: nil, explicit_span_method_name: nil, surface: nil, recording_override: nil, content_withheld: nil)
153
+ def with_span(trace_id:, span_id:, explicit_span_receiver: nil, explicit_span_method_name: nil, surface: nil, recording_override: nil, content_withheld: nil, sends_on_completion: nil)
154
154
  entry = {trace_id:, span_id:}
155
+ entry[:sends_on_completion] = sends_on_completion if sends_on_completion
155
156
  entry[:surface] = surface if surface
156
157
  entry[:recording_override] = recording_override if recording_override
157
158
  entry[:content_withheld] = content_withheld if content_withheld
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.58.3"
4
+ VERSION = "0.59.0"
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.58.3
4
+ version: 0.59.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team