bitfab 0.21.1 → 0.21.2
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 +18 -3
- data/lib/bitfab/serialize.rb +55 -16
- data/lib/bitfab/version.rb +1 -1
- 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: 910352d2c5434b3fae7c3c56ccfea74dc0a22ce35d0cb4d2cb9d463e4ba3660b
|
|
4
|
+
data.tar.gz: 2e4e07a7447b75404e753f9a2d45350edfbee088ba100aff06031cf477cbc59c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d197beb80ef3dcd1e57a8aebbe3aafd39ed000cadb0e1098ca71369d69ba53d4e805626f632b3bfdfb5f0cbe36d12e34d589f0b3def09be7e397bb1363e8b5e
|
|
7
|
+
data.tar.gz: 2f35b7eeba42eb86005e8cb9af3f46654313a84bc375e3c0edbd04e716cd8c511d0d302c2626d5f679b663f3482a24d8ec9b91d9b653fb2da6412aea0da2280b
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -425,9 +425,12 @@ module Bitfab
|
|
|
425
425
|
def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
|
|
426
426
|
span_name:, span_type:, function_name:, contexts:, prompt:, args:, kwargs:, result:, error:,
|
|
427
427
|
started_at:, ended_at:, test_run_id: nil, input_source_span_id: nil)
|
|
428
|
-
# Human-readable JSON (input/output fields)
|
|
429
|
-
|
|
430
|
-
|
|
428
|
+
# Human-readable JSON (input/output fields). The *_with_report variants
|
|
429
|
+
# also report what could not be faithfully captured so a lossy span is
|
|
430
|
+
# marked non-replayable below instead of shipping as if it round-trips.
|
|
431
|
+
human_inputs, input_dropped = Serialize.serialize_inputs_with_report(args, kwargs)
|
|
432
|
+
human_output, output_dropped = Serialize.serialize_value_with_report(result)
|
|
433
|
+
dropped = input_dropped + output_dropped
|
|
431
434
|
|
|
432
435
|
# Marshal + Base64 for full Ruby-to-Ruby object reconstruction
|
|
433
436
|
raw_input = (args.length == 1 && kwargs.empty?) ? args[0] : {args:, kwargs:}
|
|
@@ -469,6 +472,18 @@ module Bitfab
|
|
|
469
472
|
}
|
|
470
473
|
payload["testRunId"] = test_run_id if test_run_id
|
|
471
474
|
|
|
475
|
+
# A value that could only be captured as a placeholder makes the span
|
|
476
|
+
# non-replayable; record it on the payload's errors (matching the Python
|
|
477
|
+
# and TypeScript SDKs) instead of shipping the lossy capture silently.
|
|
478
|
+
unless dropped.empty?
|
|
479
|
+
names = dropped.uniq.sort.join(", ")
|
|
480
|
+
payload["errors"] = [{
|
|
481
|
+
"source" => "sdk",
|
|
482
|
+
"step" => "serialization_degraded",
|
|
483
|
+
"error" => "non-replayable: could not faithfully capture #{names}"
|
|
484
|
+
}]
|
|
485
|
+
end
|
|
486
|
+
|
|
472
487
|
@http_client.send_external_span(payload) # Returns the background thread
|
|
473
488
|
end
|
|
474
489
|
|
data/lib/bitfab/serialize.rb
CHANGED
|
@@ -29,28 +29,44 @@ module Bitfab
|
|
|
29
29
|
# Without this the wire-side JSON.dump in the http client can produce a
|
|
30
30
|
# request that times out or gets rejected, leaving a trace with zero spans.
|
|
31
31
|
def serialize_value(value)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
serialize_value_with_report(value).first
|
|
33
|
+
end
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
# Like serialize_value, but also reports what could not be faithfully
|
|
36
|
+
# captured. Returns [result, dropped] where dropped lists the class name (or
|
|
37
|
+
# "max_depth"/"too_large") behind every placeholder the walk had to emit. A
|
|
38
|
+
# non-empty dropped means the capture is lossy, so the send boundary marks
|
|
39
|
+
# the span serialization_degraded (non-replayable) instead of shipping it as
|
|
40
|
+
# if it round-trips. Mirrors the Python/TS SDKs' report serializers.
|
|
41
|
+
def serialize_value_with_report(value)
|
|
42
|
+
dropped = []
|
|
43
|
+
result = serialize_value_inner(value, 0, dropped)
|
|
44
|
+
if oversized?(result)
|
|
45
|
+
dropped << "too_large"
|
|
46
|
+
return [unserializable_stub(value, "too_large"), dropped]
|
|
47
|
+
end
|
|
48
|
+
[result, dropped]
|
|
36
49
|
rescue StandardError, SystemStackError
|
|
37
|
-
unserializable_stub(value, "unexpected_error")
|
|
50
|
+
[unserializable_stub(value, "unexpected_error"), [class_name(value)]]
|
|
38
51
|
end
|
|
39
52
|
|
|
40
|
-
def serialize_value_inner(value, depth)
|
|
41
|
-
|
|
53
|
+
def serialize_value_inner(value, depth, dropped = [])
|
|
54
|
+
if depth > MAX_SERIALIZE_DEPTH
|
|
55
|
+
dropped << "max_depth"
|
|
56
|
+
return "<unserializable: max_depth>"
|
|
57
|
+
end
|
|
42
58
|
|
|
43
59
|
case value
|
|
44
60
|
when nil, true, false, Integer, Float, String
|
|
45
61
|
value
|
|
46
62
|
when Hash
|
|
47
63
|
value.each_with_object({}) do |(k, v), acc|
|
|
48
|
-
acc[safe_to_s(k)] = serialize_value_inner(v, depth + 1)
|
|
64
|
+
acc[safe_to_s(k)] = serialize_value_inner(v, depth + 1, dropped)
|
|
49
65
|
end
|
|
50
66
|
when Array
|
|
51
|
-
value.map { |v| serialize_value_inner(v, depth + 1) }
|
|
67
|
+
value.map { |v| serialize_value_inner(v, depth + 1, dropped) }
|
|
52
68
|
when Set
|
|
53
|
-
value.map { |v| serialize_value_inner(v, depth + 1) }
|
|
69
|
+
value.map { |v| serialize_value_inner(v, depth + 1, dropped) }
|
|
54
70
|
when Time, DateTime
|
|
55
71
|
safely_call(value, "iso8601", 3) { safe_to_s(value) }
|
|
56
72
|
when Date
|
|
@@ -59,16 +75,26 @@ module Bitfab
|
|
|
59
75
|
value.to_s
|
|
60
76
|
else
|
|
61
77
|
if value.respond_to?(:to_h)
|
|
62
|
-
h = safely_call(value, "to_h")
|
|
63
|
-
|
|
78
|
+
h = safely_call(value, "to_h") do
|
|
79
|
+
dropped << class_name(value)
|
|
80
|
+
return unserializable_stub(value, "to_h_raised")
|
|
81
|
+
end
|
|
82
|
+
serialize_value_inner(h, depth + 1, dropped)
|
|
64
83
|
elsif value.respond_to?(:to_a)
|
|
65
|
-
a = safely_call(value, "to_a")
|
|
66
|
-
|
|
84
|
+
a = safely_call(value, "to_a") do
|
|
85
|
+
dropped << class_name(value)
|
|
86
|
+
return unserializable_stub(value, "to_a_raised")
|
|
87
|
+
end
|
|
88
|
+
serialize_value_inner(a, depth + 1, dropped)
|
|
67
89
|
else
|
|
90
|
+
# An arbitrary object with no structured conversion: stringifying it is
|
|
91
|
+
# a lossy capture (a repr, not its data), so report it as dropped.
|
|
92
|
+
dropped << class_name(value)
|
|
68
93
|
safe_to_s(value)
|
|
69
94
|
end
|
|
70
95
|
end
|
|
71
96
|
rescue StandardError, SystemStackError
|
|
97
|
+
dropped << class_name(value)
|
|
72
98
|
unserializable_stub(value, "inner_error")
|
|
73
99
|
end
|
|
74
100
|
|
|
@@ -108,14 +134,27 @@ module Bitfab
|
|
|
108
134
|
|
|
109
135
|
# Serialize function inputs (args + kwargs) for span data (human-readable).
|
|
110
136
|
def serialize_inputs(args, kwargs = {})
|
|
111
|
-
|
|
137
|
+
serialize_inputs_with_report(args, kwargs).first
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Like serialize_inputs, but also returns the accumulated dropped list so the
|
|
141
|
+
# send boundary can mark a lossy capture non-replayable.
|
|
142
|
+
def serialize_inputs_with_report(args, kwargs = {})
|
|
143
|
+
dropped = []
|
|
144
|
+
serialized = args.map do |arg|
|
|
145
|
+
result, arg_dropped = serialize_value_with_report(arg)
|
|
146
|
+
dropped.concat(arg_dropped)
|
|
147
|
+
result
|
|
148
|
+
end
|
|
112
149
|
unless kwargs.empty?
|
|
113
150
|
kw = kwargs.each_with_object({}) do |(k, v), acc|
|
|
114
|
-
|
|
151
|
+
result, v_dropped = serialize_value_with_report(v)
|
|
152
|
+
dropped.concat(v_dropped)
|
|
153
|
+
acc[safe_to_s(k)] = result
|
|
115
154
|
end
|
|
116
155
|
serialized << kw
|
|
117
156
|
end
|
|
118
|
-
serialized
|
|
157
|
+
[serialized, dropped]
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
# Marshal a value to a Base64-encoded string for Ruby-to-Ruby reconstruction.
|
data/lib/bitfab/version.rb
CHANGED