bitfab 0.74.3 → 0.75.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 +9 -5
- data/lib/bitfab/span_context.rb +45 -3
- data/lib/bitfab/verdicts.rb +17 -2
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +8 -0
- 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: fad2341a7e82e22c18faf4809a1807407660eed238b04886b2687e4dc15989b2
|
|
4
|
+
data.tar.gz: 7a46b9c4935290898decf6f835257d5b8551281eb1ddebb1680128fcd5ac8c74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82ef99ecba72308fc1e865257f260756cab829bae0167404f50f8317dd0d921446b02f4379f5c254e98f8deb3fb68104afa73075c4081cc9d5a7b7345243611f
|
|
7
|
+
data.tar.gz: 696fb090a73f51ab3bb9ca07277e89a0529dd3031a8c96bfa581f7c54235ba3205f804df4790e5d83163d635dfa5228f9d0be05e4e37d4c1805fb7271a920732
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -722,7 +722,8 @@ module Bitfab
|
|
|
722
722
|
explicit_span_receiver:,
|
|
723
723
|
explicit_span_method_name: explicit_span_method_name&.to_sym,
|
|
724
724
|
surface:, recording_override:, content_withheld:,
|
|
725
|
-
sends_on_completion: sends_on_completion
|
|
725
|
+
sends_on_completion: sends_on_completion,
|
|
726
|
+
trace_function_key:
|
|
726
727
|
) do
|
|
727
728
|
invoke = -> { around_body ? around_body.call { yield } : yield }
|
|
728
729
|
result = if explicit_span_receiver
|
|
@@ -769,7 +770,8 @@ module Bitfab
|
|
|
769
770
|
surface:,
|
|
770
771
|
around_body:,
|
|
771
772
|
explicit_span_receiver:,
|
|
772
|
-
explicit_span_method_name
|
|
773
|
+
explicit_span_method_name:,
|
|
774
|
+
trace_function_key:
|
|
773
775
|
)
|
|
774
776
|
end
|
|
775
777
|
|
|
@@ -812,7 +814,7 @@ module Bitfab
|
|
|
812
814
|
end
|
|
813
815
|
|
|
814
816
|
def root_trace_function_key(trace_id, trace_function_key)
|
|
815
|
-
TraceState.
|
|
817
|
+
TraceState.trace_function_key(trace_id) || trace_function_key
|
|
816
818
|
end
|
|
817
819
|
|
|
818
820
|
def wait_for_simulation_plan
|
|
@@ -902,8 +904,10 @@ module Bitfab
|
|
|
902
904
|
# span output. The span is sent exactly once: when iteration finishes,
|
|
903
905
|
# raises, or the wrapper is `.close`d.
|
|
904
906
|
def wrap_enumerator(source, trace_id:, span_id:, complete:, explicit_span_receiver: nil,
|
|
905
|
-
explicit_span_method_name: nil, surface: nil, around_body: nil)
|
|
907
|
+
explicit_span_method_name: nil, surface: nil, around_body: nil, trace_function_key: nil)
|
|
906
908
|
span_entry = {trace_id:, span_id:, surface:}
|
|
909
|
+
SpanContext.record_trace_function_keys(span_entry, trace_function_key)
|
|
910
|
+
trace_function_keys = span_entry[:trace_function_keys]
|
|
907
911
|
if explicit_span_receiver
|
|
908
912
|
span_entry[:explicit_span_receiver] = explicit_span_receiver
|
|
909
913
|
span_entry[:explicit_span_method_name] = explicit_span_method_name&.to_sym
|
|
@@ -916,7 +920,7 @@ module Bitfab
|
|
|
916
920
|
# the right parent. The fiber bridge carries the same parent into
|
|
917
921
|
# Enumerator.new / enum_for source fibers on this thread.
|
|
918
922
|
ReplayContext.with_span(trace_id:, span_id:) do
|
|
919
|
-
SpanContext.with_fiber_bridge(trace_id:, span_id:, surface:) do
|
|
923
|
+
SpanContext.with_fiber_bridge(trace_id:, span_id:, surface:, trace_function_key:, trace_function_keys:) do
|
|
920
924
|
SpanContext.stack.push(span_entry)
|
|
921
925
|
begin
|
|
922
926
|
iterate = lambda do
|
data/lib/bitfab/span_context.rb
CHANGED
|
@@ -19,6 +19,16 @@ module Bitfab
|
|
|
19
19
|
@span_state[:trace_id]
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# The keys of every trace function running at this point, outermost first,
|
|
23
|
+
# each listed once. A trace function called inside another one comes after
|
|
24
|
+
# it, so a cost recorded here can be counted toward each of them. Empty
|
|
25
|
+
# outside a trace.
|
|
26
|
+
def trace_function_keys
|
|
27
|
+
span_keys = @span_state[:trace_function_keys] || []
|
|
28
|
+
root_keys = Subtree.active_sessions.reverse.map(&:trace_function_key).uniq
|
|
29
|
+
(root_keys - span_keys) + span_keys
|
|
30
|
+
end
|
|
31
|
+
|
|
22
32
|
# Add a context entry to this span.
|
|
23
33
|
# The entire hash is pushed as a single entry in the contexts array.
|
|
24
34
|
# Context entries are accumulated - multiple calls add to the list.
|
|
@@ -53,6 +63,15 @@ module Bitfab
|
|
|
53
63
|
@trace_id = trace_id
|
|
54
64
|
end
|
|
55
65
|
|
|
66
|
+
# The key of the trace function the current trace is stored under (the one
|
|
67
|
+
# that started it). A bitfab_span or get_function call inside another trace
|
|
68
|
+
# function joins that trace, so it reports the outer key. A bitfab_trace
|
|
69
|
+
# root inside another root starts its own trace, so it reports its own key.
|
|
70
|
+
# Empty string outside a trace.
|
|
71
|
+
def trace_function_key
|
|
72
|
+
TraceState.trace_function_key(@trace_id) || ""
|
|
73
|
+
end
|
|
74
|
+
|
|
56
75
|
# Set the session ID for this trace.
|
|
57
76
|
# Session ID is used to group traces from the same user session.
|
|
58
77
|
# This is stored as a database column.
|
|
@@ -121,7 +140,8 @@ module Bitfab
|
|
|
121
140
|
end
|
|
122
141
|
|
|
123
142
|
# Thread-local span stack for tracking nested spans.
|
|
124
|
-
# Each entry is a Hash with :trace_id and :span_id keys
|
|
143
|
+
# Each entry is a Hash with :trace_id and :span_id keys, plus the span's own
|
|
144
|
+
# :trace_function_key and the :trace_function_keys running at it.
|
|
125
145
|
module SpanContext
|
|
126
146
|
STACK_KEY = :__bitfab_span_stack
|
|
127
147
|
FIBER_BRIDGE_STACK_KEY = :__bitfab_span_fiber_bridge_stack
|
|
@@ -139,8 +159,9 @@ module Bitfab
|
|
|
139
159
|
# Enumerator.new and enum_for run their source body in another Fiber on
|
|
140
160
|
# the same thread. Bridge only the active Enumerator parent across that
|
|
141
161
|
# boundary; ordinary fiber-local span stacks remain isolated.
|
|
142
|
-
def with_fiber_bridge(trace_id:, span_id:, surface: nil)
|
|
162
|
+
def with_fiber_bridge(trace_id:, span_id:, surface: nil, trace_function_key: nil, trace_function_keys: nil)
|
|
143
163
|
entry = {trace_id:, span_id:}
|
|
164
|
+
record_trace_function_keys(entry, trace_function_key, trace_function_keys)
|
|
144
165
|
entry[:surface] = surface if surface
|
|
145
166
|
fiber_bridge_stack.push(entry)
|
|
146
167
|
yield
|
|
@@ -150,8 +171,9 @@ module Bitfab
|
|
|
150
171
|
|
|
151
172
|
# Execute a block with a new span pushed onto the stack.
|
|
152
173
|
# 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, sends_on_completion: nil)
|
|
174
|
+
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, trace_function_key: nil)
|
|
154
175
|
entry = {trace_id:, span_id:}
|
|
176
|
+
record_trace_function_keys(entry, trace_function_key)
|
|
155
177
|
entry[:sends_on_completion] = sends_on_completion if sends_on_completion
|
|
156
178
|
entry[:surface] = surface if surface
|
|
157
179
|
entry[:recording_override] = recording_override if recording_override
|
|
@@ -166,6 +188,22 @@ module Bitfab
|
|
|
166
188
|
stack.pop
|
|
167
189
|
end
|
|
168
190
|
|
|
191
|
+
# The trace function keys running at this point with the given key added
|
|
192
|
+
# at the end, each listed once. A nil key returns the running keys as is.
|
|
193
|
+
def trace_function_keys_with(trace_function_key)
|
|
194
|
+
running = current&.dig(:trace_function_keys) || []
|
|
195
|
+
trace_function_key ? running | [trace_function_key] : running
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Store a span's own trace function key and the keys running at that span
|
|
199
|
+
# on its stack entry, computing the running keys from the current span
|
|
200
|
+
# when they are not given. Nothing is stored when no key is running.
|
|
201
|
+
def record_trace_function_keys(entry, trace_function_key, trace_function_keys = nil)
|
|
202
|
+
entry[:trace_function_key] = trace_function_key if trace_function_key
|
|
203
|
+
keys = trace_function_keys || trace_function_keys_with(trace_function_key)
|
|
204
|
+
entry[:trace_function_keys] = keys unless keys.empty?
|
|
205
|
+
end
|
|
206
|
+
|
|
169
207
|
def fiber_bridge_stack
|
|
170
208
|
current_thread = Thread.current
|
|
171
209
|
current_thread.thread_variable_get(FIBER_BRIDGE_STACK_KEY) || begin
|
|
@@ -208,6 +246,10 @@ module Bitfab
|
|
|
208
246
|
end
|
|
209
247
|
end
|
|
210
248
|
|
|
249
|
+
def trace_function_key(trace_id)
|
|
250
|
+
get(trace_id)&.dig(:trace_function_key)
|
|
251
|
+
end
|
|
252
|
+
|
|
211
253
|
def delete(trace_id)
|
|
212
254
|
@states_mutex.synchronize { @states.delete(trace_id) }
|
|
213
255
|
end
|
data/lib/bitfab/verdicts.rb
CHANGED
|
@@ -194,12 +194,27 @@ module Bitfab
|
|
|
194
194
|
end
|
|
195
195
|
end
|
|
196
196
|
|
|
197
|
-
# Reads one trace's verdicts, or nil when the trace has none.
|
|
197
|
+
# Reads one trace's verdicts, or nil when the trace has none. See {#get_all} for the shape of each
|
|
198
|
+
# "evidence" item.
|
|
198
199
|
def get(trace_id)
|
|
199
200
|
get_all([trace_id]).first
|
|
200
201
|
end
|
|
201
202
|
|
|
202
|
-
# Reads the verdicts of several traces.
|
|
203
|
+
# Reads the verdicts of several traces. Each trace and each entry of its "assertions" carries
|
|
204
|
+
# "evidence": nil, or an array of hashes, one per span cited, each with "spanId" and "text".
|
|
205
|
+
#
|
|
206
|
+
# Evidence written by Bitfab's assertion judge also carries "field", "ranges", and "spanChanged",
|
|
207
|
+
# which point at the exact passages the judge read. "field" is the span field the judge read
|
|
208
|
+
# ("summary", "input", "output", "reasoning", "content", "metadata", "errors", "contexts", or
|
|
209
|
+
# "llm_usage"). "ranges" are [start, end) character ranges into that field's text as the judge saw
|
|
210
|
+
# it: a string field as is, any other value as JSON indented by two spaces. Indexes are JavaScript
|
|
211
|
+
# string indexes (UTF-16 code units), not Ruby character indexes, so text outside the Basic
|
|
212
|
+
# Multilingual Plane counts two per character. Use them to highlight only when "spanChanged" is false.
|
|
213
|
+
#
|
|
214
|
+
# "spanChanged" is true when the span no longer matches what was judged: "text" is then only a short
|
|
215
|
+
# preview saved with the verdict, and "ranges" must not be used to highlight anything. When it is
|
|
216
|
+
# false, "text" is the full text of "ranges" read from the span, joined by a newline, an ellipsis,
|
|
217
|
+
# and a newline. Unknown keys are passed through as the server sent them.
|
|
203
218
|
def get_all(trace_ids)
|
|
204
219
|
return [] if trace_ids.empty?
|
|
205
220
|
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -49,6 +49,10 @@ module Bitfab
|
|
|
49
49
|
""
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def trace_function_keys
|
|
53
|
+
[]
|
|
54
|
+
end
|
|
55
|
+
|
|
52
56
|
def add_context(_context)
|
|
53
57
|
# No-op
|
|
54
58
|
end
|
|
@@ -61,6 +65,10 @@ module Bitfab
|
|
|
61
65
|
# No-op trace handle returned when outside a span context.
|
|
62
66
|
# All methods do nothing, preventing crashes when called outside traced code.
|
|
63
67
|
class NoOpCurrentTrace
|
|
68
|
+
def trace_function_key
|
|
69
|
+
""
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
def set_session_id(_session_id)
|
|
65
73
|
# No-op
|
|
66
74
|
end
|