braintrust 0.1.3 → 0.1.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 +4 -4
- data/lib/braintrust/api/datasets.rb +3 -3
- data/lib/braintrust/api/functions.rb +2 -3
- data/lib/braintrust/api/internal/auth.rb +2 -6
- data/lib/braintrust/api/internal/experiments.rb +6 -4
- data/lib/braintrust/api/internal/projects.rb +2 -3
- data/lib/braintrust/eval/functions.rb +12 -3
- data/lib/braintrust/eval.rb +21 -5
- data/lib/braintrust/internal/http.rb +97 -0
- data/lib/braintrust/trace/attachment.rb +3 -1
- data/lib/braintrust/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1a5c8840f707c7b4da95e4ccc8abea32591606d667a309432f2955d5df26eca
|
|
4
|
+
data.tar.gz: a45e62f34a1d59dd11e1cc46ff8d128a495a45a80fa1ce2026c76b648b58de89
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75b71465a80ed2cfd3c6600113dd62357d01e0bd672f2043045f56d7d0223882cc2c5fd9f8927973ae546f99b68971bbe66fd34d66ec6fd62fafd65ca52abcd7
|
|
7
|
+
data.tar.gz: 06eb21fec07c05755aacd0a214cd16594f47a7033e723a2798df26692a0c15ccd9d5ed614588cc805620d303bb0f1a1c577c66b25c92c6dbbaa40283023cd662
|
|
@@ -4,6 +4,7 @@ require "net/http"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require "uri"
|
|
6
6
|
require_relative "../logger"
|
|
7
|
+
require_relative "../internal/http"
|
|
7
8
|
|
|
8
9
|
module Braintrust
|
|
9
10
|
class API
|
|
@@ -111,6 +112,7 @@ module Braintrust
|
|
|
111
112
|
payload[:version] = version if version
|
|
112
113
|
|
|
113
114
|
response = http_post_json_raw("/btql", payload)
|
|
115
|
+
Braintrust::Internal::Http.decompress_response!(response)
|
|
114
116
|
|
|
115
117
|
# Parse JSONL response
|
|
116
118
|
records = response.body.lines
|
|
@@ -158,9 +160,7 @@ module Braintrust
|
|
|
158
160
|
start_time = Time.now
|
|
159
161
|
Log.debug("[API] #{method.upcase} #{uri}")
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
http.use_ssl = (uri.scheme == "https")
|
|
163
|
-
response = http.request(request)
|
|
163
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
164
164
|
|
|
165
165
|
duration_ms = ((Time.now - start_time) * 1000).round(2)
|
|
166
166
|
Log.debug("[API] #{method.upcase} #{uri} -> #{response.code} (#{duration_ms}ms, #{response.body.bytesize} bytes)")
|
|
@@ -4,6 +4,7 @@ require "net/http"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require "uri"
|
|
6
6
|
require_relative "../logger"
|
|
7
|
+
require_relative "../internal/http"
|
|
7
8
|
|
|
8
9
|
module Braintrust
|
|
9
10
|
class API
|
|
@@ -242,9 +243,7 @@ module Braintrust
|
|
|
242
243
|
start_time = Time.now
|
|
243
244
|
Log.debug("[API] #{method.upcase} #{uri}")
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
http.use_ssl = (uri.scheme == "https")
|
|
247
|
-
response = http.request(request)
|
|
246
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
248
247
|
|
|
249
248
|
duration_ms = ((Time.now - start_time) * 1000).round(2)
|
|
250
249
|
Log.debug("[API] #{method.upcase} #{uri} -> #{response.code} (#{duration_ms}ms, #{response.body.bytesize} bytes)")
|
|
@@ -4,6 +4,7 @@ require "net/http"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require "uri"
|
|
6
6
|
require_relative "../../logger"
|
|
7
|
+
require_relative "../../internal/http"
|
|
7
8
|
|
|
8
9
|
module Braintrust
|
|
9
10
|
class API
|
|
@@ -44,12 +45,7 @@ module Braintrust
|
|
|
44
45
|
request = Net::HTTP::Post.new(uri)
|
|
45
46
|
request["Authorization"] = "Bearer #{api_key}"
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
http.use_ssl = true if uri.scheme == "https"
|
|
49
|
-
|
|
50
|
-
response = http.start do |http_session|
|
|
51
|
-
http_session.request(request)
|
|
52
|
-
end
|
|
48
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
53
49
|
|
|
54
50
|
Log.debug("Login: received response [#{response.code}]")
|
|
55
51
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "net/http"
|
|
4
4
|
require "json"
|
|
5
5
|
require "uri"
|
|
6
|
+
require_relative "../../internal/http"
|
|
6
7
|
|
|
7
8
|
module Braintrust
|
|
8
9
|
class API
|
|
@@ -22,7 +23,8 @@ module Braintrust
|
|
|
22
23
|
# @param tags [Array<String>, nil] Optional tags
|
|
23
24
|
# @param metadata [Hash, nil] Optional metadata
|
|
24
25
|
# @return [Hash] Experiment data with "id", "name", "project_id", etc.
|
|
25
|
-
def create(name:, project_id:, ensure_new: true, tags: nil, metadata: nil
|
|
26
|
+
def create(name:, project_id:, ensure_new: true, tags: nil, metadata: nil,
|
|
27
|
+
dataset_id: nil, dataset_version: nil)
|
|
26
28
|
uri = URI("#{@state.api_url}/v1/experiment")
|
|
27
29
|
|
|
28
30
|
payload = {
|
|
@@ -32,15 +34,15 @@ module Braintrust
|
|
|
32
34
|
}
|
|
33
35
|
payload[:tags] = tags if tags
|
|
34
36
|
payload[:metadata] = metadata if metadata
|
|
37
|
+
payload[:dataset_id] = dataset_id if dataset_id
|
|
38
|
+
payload[:dataset_version] = dataset_version if dataset_version
|
|
35
39
|
|
|
36
40
|
request = Net::HTTP::Post.new(uri)
|
|
37
41
|
request["Content-Type"] = "application/json"
|
|
38
42
|
request["Authorization"] = "Bearer #{@state.api_key}"
|
|
39
43
|
request.body = JSON.dump(payload)
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
http.use_ssl = (uri.scheme == "https")
|
|
43
|
-
response = http.request(request)
|
|
45
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
44
46
|
|
|
45
47
|
unless response.is_a?(Net::HTTPSuccess)
|
|
46
48
|
raise Error, "HTTP #{response.code} for POST #{uri}: #{response.body}"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "net/http"
|
|
4
4
|
require "json"
|
|
5
5
|
require "uri"
|
|
6
|
+
require_relative "../../internal/http"
|
|
6
7
|
|
|
7
8
|
module Braintrust
|
|
8
9
|
class API
|
|
@@ -26,9 +27,7 @@ module Braintrust
|
|
|
26
27
|
request["Authorization"] = "Bearer #{@state.api_key}"
|
|
27
28
|
request.body = JSON.dump({name: name})
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
http.use_ssl = (uri.scheme == "https")
|
|
31
|
-
response = http.request(request)
|
|
30
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
32
31
|
|
|
33
32
|
unless response.is_a?(Net::HTTPSuccess)
|
|
34
33
|
raise Error, "HTTP #{response.code} for POST #{uri}: #{response.body}"
|
|
@@ -98,9 +98,18 @@ module Braintrust
|
|
|
98
98
|
# The remote scorer receives all scorer arguments
|
|
99
99
|
result = api.functions.invoke(id: function_id, input: scorer_input)
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
score = case result
|
|
102
|
+
when Hash
|
|
103
|
+
if result.key?("score")
|
|
104
|
+
result["score"].to_f
|
|
105
|
+
else
|
|
106
|
+
raise Error, "Hash result must contain 'score' key"
|
|
107
|
+
end
|
|
108
|
+
when String
|
|
109
|
+
result.to_f
|
|
110
|
+
else
|
|
111
|
+
raise Error, "Unsupported result type: #{result.class}"
|
|
112
|
+
end
|
|
104
113
|
|
|
105
114
|
span.set_attribute("braintrust.output_json", JSON.dump(score))
|
|
106
115
|
score
|
data/lib/braintrust/eval.rb
CHANGED
|
@@ -220,8 +220,14 @@ module Braintrust
|
|
|
220
220
|
api.login
|
|
221
221
|
|
|
222
222
|
# Resolve dataset to cases if dataset parameter provided
|
|
223
|
+
dataset_id = nil
|
|
224
|
+
dataset_version = nil
|
|
225
|
+
|
|
223
226
|
if dataset
|
|
224
|
-
|
|
227
|
+
resolved = resolve_dataset(dataset, project, api)
|
|
228
|
+
cases = resolved[:cases]
|
|
229
|
+
dataset_id = resolved[:dataset_id]
|
|
230
|
+
dataset_version = resolved[:dataset_version]
|
|
225
231
|
end
|
|
226
232
|
|
|
227
233
|
# Register project and experiment via internal API
|
|
@@ -234,7 +240,9 @@ module Braintrust
|
|
|
234
240
|
project_id: project_result["id"],
|
|
235
241
|
ensure_new: !update,
|
|
236
242
|
tags: tags,
|
|
237
|
-
metadata: metadata
|
|
243
|
+
metadata: metadata,
|
|
244
|
+
dataset_id: dataset_id,
|
|
245
|
+
dataset_version: dataset_version
|
|
238
246
|
)
|
|
239
247
|
|
|
240
248
|
experiment_id = experiment_result["id"]
|
|
@@ -292,11 +300,11 @@ module Braintrust
|
|
|
292
300
|
end
|
|
293
301
|
end
|
|
294
302
|
|
|
295
|
-
# Resolve dataset parameter to
|
|
303
|
+
# Resolve dataset parameter to cases with metadata for experiment linking
|
|
296
304
|
# @param dataset [String, Hash, Dataset] Dataset specifier or instance
|
|
297
305
|
# @param project [String] Project name (used as default if not specified)
|
|
298
306
|
# @param api [API] Braintrust API client
|
|
299
|
-
# @return [
|
|
307
|
+
# @return [Hash] Hash with :cases, :dataset_id, and :dataset_version
|
|
300
308
|
def resolve_dataset(dataset, project, api)
|
|
301
309
|
limit = nil
|
|
302
310
|
|
|
@@ -315,7 +323,15 @@ module Braintrust
|
|
|
315
323
|
raise ArgumentError, "dataset must be String, Hash, or Dataset, got #{dataset.class}"
|
|
316
324
|
end
|
|
317
325
|
|
|
318
|
-
dataset_obj.fetch_all(limit: limit)
|
|
326
|
+
cases = dataset_obj.fetch_all(limit: limit)
|
|
327
|
+
|
|
328
|
+
# Use pinned version if available, otherwise compute from max(_xact_id)
|
|
329
|
+
version = dataset_obj.version
|
|
330
|
+
version ||= cases
|
|
331
|
+
.filter_map { |c| c[:origin] && JSON.parse(c[:origin])["_xact_id"] }
|
|
332
|
+
.max
|
|
333
|
+
|
|
334
|
+
{cases: cases, dataset_id: dataset_obj.id, dataset_version: version}
|
|
319
335
|
end
|
|
320
336
|
end
|
|
321
337
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "zlib"
|
|
6
|
+
require "stringio"
|
|
7
|
+
require_relative "../logger"
|
|
8
|
+
|
|
9
|
+
module Braintrust
|
|
10
|
+
module Internal
|
|
11
|
+
# HTTP utilities for redirect following and response decompression.
|
|
12
|
+
# Drop-in enhancement for raw Net::HTTP request calls throughout the SDK.
|
|
13
|
+
module Http
|
|
14
|
+
DEFAULT_MAX_REDIRECTS = 5
|
|
15
|
+
|
|
16
|
+
# Execute an HTTP request, following redirects as needed.
|
|
17
|
+
#
|
|
18
|
+
# @param uri [URI] The request URI
|
|
19
|
+
# @param request [Net::HTTPRequest] The prepared request object
|
|
20
|
+
# @param max_redirects [Integer] Maximum number of redirects to follow
|
|
21
|
+
# @return [Net::HTTPResponse] The final response
|
|
22
|
+
# @raise [Braintrust::Error] On too many redirects or missing Location header
|
|
23
|
+
def self.with_redirects(uri, request, max_redirects: DEFAULT_MAX_REDIRECTS)
|
|
24
|
+
response = perform_request(uri, request)
|
|
25
|
+
|
|
26
|
+
redirects = 0
|
|
27
|
+
original_request = request
|
|
28
|
+
|
|
29
|
+
while response.is_a?(Net::HTTPRedirection)
|
|
30
|
+
redirects += 1
|
|
31
|
+
if redirects > max_redirects
|
|
32
|
+
raise Error, "Too many redirects (max #{max_redirects})"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
location = response["location"]
|
|
36
|
+
unless location
|
|
37
|
+
raise Error, "Redirect response #{response.code} without Location header"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
redirect_uri = URI(location)
|
|
41
|
+
redirect_uri = uri + redirect_uri unless redirect_uri.host
|
|
42
|
+
|
|
43
|
+
Log.debug("[HTTP] Following #{response.code} redirect to #{redirect_uri}")
|
|
44
|
+
|
|
45
|
+
request = build_redirect_request(response, redirect_uri, original_request, uri)
|
|
46
|
+
uri = redirect_uri
|
|
47
|
+
response = perform_request(uri, request)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
response
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Decompress an HTTP response body in place based on Content-Encoding.
|
|
54
|
+
# No-op if the response has no recognized encoding.
|
|
55
|
+
#
|
|
56
|
+
# @param response [Net::HTTPResponse] The response to decompress
|
|
57
|
+
# @return [void]
|
|
58
|
+
def self.decompress_response!(response)
|
|
59
|
+
encoding = response["content-encoding"]&.downcase
|
|
60
|
+
case encoding
|
|
61
|
+
when "gzip", "x-gzip"
|
|
62
|
+
gz = Zlib::GzipReader.new(StringIO.new(response.body))
|
|
63
|
+
response.body.replace(gz.read)
|
|
64
|
+
gz.close
|
|
65
|
+
response.delete("content-encoding")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.perform_request(uri, request)
|
|
70
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
71
|
+
http.use_ssl = (uri.scheme == "https")
|
|
72
|
+
http.request(request)
|
|
73
|
+
end
|
|
74
|
+
private_class_method :perform_request
|
|
75
|
+
|
|
76
|
+
def self.build_redirect_request(response, redirect_uri, original_request, original_uri)
|
|
77
|
+
if response.code == "307" || response.code == "308"
|
|
78
|
+
request = original_request.class.new(redirect_uri)
|
|
79
|
+
request.body = original_request.body
|
|
80
|
+
request["Content-Type"] = original_request["Content-Type"] if original_request["Content-Type"]
|
|
81
|
+
else
|
|
82
|
+
# 301, 302, 303: follow with GET, no body
|
|
83
|
+
request = Net::HTTP::Get.new(redirect_uri)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Strip Authorization when redirecting to a different host (e.g. S3)
|
|
87
|
+
if original_uri.host == redirect_uri.host
|
|
88
|
+
auth = original_request["Authorization"]
|
|
89
|
+
request["Authorization"] = auth if auth
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
request
|
|
93
|
+
end
|
|
94
|
+
private_class_method :build_redirect_request
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "net/http"
|
|
4
4
|
require_relative "../internal/encoding"
|
|
5
|
+
require_relative "../internal/http"
|
|
5
6
|
require "uri"
|
|
6
7
|
|
|
7
8
|
module Braintrust
|
|
@@ -91,7 +92,8 @@ module Braintrust
|
|
|
91
92
|
# att = Braintrust::Trace::Attachment.from_url("https://example.com/image.png")
|
|
92
93
|
def self.from_url(url)
|
|
93
94
|
uri = URI.parse(url)
|
|
94
|
-
|
|
95
|
+
request = Net::HTTP::Get.new(uri)
|
|
96
|
+
response = Braintrust::Internal::Http.with_redirects(uri, request)
|
|
95
97
|
|
|
96
98
|
unless response.is_a?(Net::HTTPSuccess)
|
|
97
99
|
raise StandardError, "Failed to fetch URL: #{response.code} #{response.message}"
|
data/lib/braintrust/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: braintrust
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Braintrust
|
|
@@ -242,6 +242,7 @@ files:
|
|
|
242
242
|
- lib/braintrust/eval/summary.rb
|
|
243
243
|
- lib/braintrust/internal/encoding.rb
|
|
244
244
|
- lib/braintrust/internal/env.rb
|
|
245
|
+
- lib/braintrust/internal/http.rb
|
|
245
246
|
- lib/braintrust/internal/origin.rb
|
|
246
247
|
- lib/braintrust/internal/template.rb
|
|
247
248
|
- lib/braintrust/internal/thread_pool.rb
|