Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gemfiles/anthropic.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/anthropic_1_11.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/anthropic_1_12.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/anthropic_uninstalled.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/contrib.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/openai.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/openai_0_33.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/openai_0_34.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/openai_ruby_openai.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/openai_uninstalled.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/opentelemetry_latest.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/opentelemetry_min.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_server.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_llm.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_llm_1_8.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_llm_1_9.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_llm_uninstalled.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_openai.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_openai_7_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_openai_8_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/ruby_openai_uninstalled.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
1 change: 1 addition & 0 deletions gemfiles/server.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

source "https://rubygems.org"

gem "openssl", "4.0.0"
gem "appraisal", "~> 2.5"
gem "climate_control", "~> 1.2"
gem "kramdown", "~> 2.0"
Expand Down
4 changes: 3 additions & 1 deletion lib/braintrust.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ class Error < StandardError; end
# @param filter_ai_spans [Boolean, nil] Enable AI span filtering (overrides BRAINTRUST_OTEL_FILTER_AI_SPANS env var)
# @param span_filter_funcs [Array<Proc>, nil] Custom span filter functions
# @param exporter [Exporter, nil] Optional exporter override (for testing)
# @param environment [Hash, nil] Span-origin environment override, e.g. { type: "ci", name: "github_actions" }
# @param auto_instrument [Boolean, Hash, nil] Auto-instrumentation config:
# - nil (default): use BRAINTRUST_AUTO_INSTRUMENT env var, default true if not set
# - true: explicitly enable
# - false: explicitly disable
# - Hash with :only or :except keys for filtering
# @return [State] the created state
def self.init(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil, set_global: true, blocking_login: false, enable_tracing: true, tracer_provider: nil, filter_ai_spans: nil, span_filter_funcs: nil, exporter: nil, auto_instrument: nil)
def self.init(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil, set_global: true, blocking_login: false, enable_tracing: true, tracer_provider: nil, filter_ai_spans: nil, span_filter_funcs: nil, exporter: nil, auto_instrument: nil, environment: nil)
state = State.from_env(
api_key: api_key,
org_name: org_name,
Expand All @@ -61,6 +62,7 @@ def self.init(api_key: nil, org_name: nil, default_project: nil, app_url: nil, a
tracer_provider: tracer_provider,
filter_ai_spans: filter_ai_spans,
span_filter_funcs: span_filter_funcs,
environment: environment,
exporter: exporter
)

Expand Down
115 changes: 111 additions & 4 deletions lib/braintrust/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ module Braintrust
# and allows overriding with explicit options
class Config
attr_reader :api_key, :org_name, :default_project, :app_url, :api_url,
:filter_ai_spans, :span_filter_funcs
:filter_ai_spans, :span_filter_funcs, :environment

def initialize(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil,
filter_ai_spans: nil, span_filter_funcs: nil)
filter_ai_spans: nil, span_filter_funcs: nil, environment: nil)
@api_key = api_key
@org_name = org_name
@default_project = default_project
@app_url = app_url
@api_url = api_url
@filter_ai_spans = filter_ai_spans
@span_filter_funcs = span_filter_funcs || []
@environment = environment
end

# Create a Config from environment variables, with option overrides
Expand All @@ -29,9 +30,10 @@ def initialize(api_key: nil, org_name: nil, default_project: nil, app_url: nil,
# @param api_url [String, nil] API URL (overrides BRAINTRUST_API_URL env var)
# @param filter_ai_spans [Boolean, nil] Enable AI span filtering (overrides BRAINTRUST_OTEL_FILTER_AI_SPANS env var)
# @param span_filter_funcs [Array<Proc>, nil] Custom span filter functions
# @param environment [Hash, nil] Span-origin environment override, e.g. { type: "ci", name: "github_actions" }
# @return [Config] the created config
def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil,
filter_ai_spans: nil, span_filter_funcs: nil)
filter_ai_spans: nil, span_filter_funcs: nil, environment: nil)
# Parse filter_ai_spans from ENV if not explicitly provided
env_filter_ai_spans = ENV["BRAINTRUST_OTEL_FILTER_AI_SPANS"]
filter_ai_spans_value = if filter_ai_spans.nil?
Expand All @@ -47,8 +49,113 @@ def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: ni
app_url: app_url || ENV["BRAINTRUST_APP_URL"] || "https://www.braintrust.dev",
api_url: api_url || ENV["BRAINTRUST_API_URL"] || "https://api.braintrust.dev",
filter_ai_spans: filter_ai_spans_value,
span_filter_funcs: span_filter_funcs
span_filter_funcs: span_filter_funcs,
environment: detect_environment(environment)
)
end

def self.detect_environment(explicit = nil)
return normalize_environment(explicit) if explicit

env_type = env_value("BRAINTRUST_ENVIRONMENT_TYPE")
if env_type && !env_type.empty?
env_name = env_value("BRAINTRUST_ENVIRONMENT_NAME")
return {type: env_type, name: env_name}.compact
end

{
"GITHUB_ACTIONS" => "github_actions",
"GITLAB_CI" => "gitlab_ci",
"CIRCLECI" => "circleci",
"BUILDKITE" => "buildkite",
"JENKINS_URL" => "jenkins",
"JENKINS_HOME" => "jenkins",
"TF_BUILD" => "azure_pipelines",
"TEAMCITY_VERSION" => "teamcity",
"TRAVIS" => "travis",
"BITBUCKET_BUILD_NUMBER" => "bitbucket"
}.each do |key, name|
return {type: "ci", name: name} if process_env_value(key)
end
return {type: "ci", name: "ci"} if process_env_value("CI")

{
"VERCEL" => "vercel",
"NETLIFY" => "netlify",
"AWS_LAMBDA_FUNCTION_NAME" => "aws_lambda",
"AWS_EXECUTION_ENV" => "aws_lambda",
"K_SERVICE" => "cloud_run",
"FUNCTION_TARGET" => "gcp_functions",
"KUBERNETES_SERVICE_HOST" => "kubernetes",
"ECS_CONTAINER_METADATA_URI" => "ecs",
"ECS_CONTAINER_METADATA_URI_V4" => "ecs",
"DYNO" => "heroku",
"FLY_APP_NAME" => "fly",
"RAILWAY_ENVIRONMENT" => "railway",
"RENDER_SERVICE_NAME" => "render"
}.each do |key, name|
return {type: "server", name: name} if process_env_value(key)
end

deployment_mode_environment(process_env_value("RAILS_ENV")) ||
deployment_mode_environment(process_env_value("RACK_ENV"))
end

def self.normalize_environment(environment)
type = environment[:type] || environment["type"]
name = environment[:name] || environment["name"]
{type: type, name: name}.compact
end

def self.deployment_mode_environment(value)
return nil if value.nil? || value.empty?

normalized = value.downcase
return {type: "server", name: normalized} if ["production", "staging"].include?(normalized)
return {type: "local", name: normalized} if ["development", "local"].include?(normalized)

nil
end

def self.env_value(key)
value = ENV[key]
value = read_braintrust_env_file_value(key) if value.nil? || value.strip.empty?
value&.strip
end

def self.process_env_value(key)
value = ENV[key]
value&.strip unless value.nil? || value.strip.empty?
end

def self.read_braintrust_env_file_value(key)
dir = Dir.pwd
65.times do
path = File.join(dir, ".env.braintrust")
if File.file?(path)
File.foreach(path) do |line|
stripped = line.strip
next if stripped.empty? || stripped.start_with?("#")

name, value = stripped.split("=", 2)
next unless name&.strip == key

return value&.strip&.delete_prefix('"')&.delete_suffix('"')&.delete_prefix("'")&.delete_suffix("'")
end
return nil
end

parent = File.dirname(dir)
return nil if parent == dir

dir = parent
end
nil
rescue
nil
end

private_class_method :detect_environment, :normalize_environment, :deployment_mode_environment,
:env_value, :process_env_value, :read_braintrust_env_file_value
end
end
6 changes: 4 additions & 2 deletions lib/braintrust/state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class MissingAPIKeyError < ArgumentError; end
# @param filter_ai_spans [Boolean, nil] Enable AI span filtering
# @param span_filter_funcs [Array<Proc>, nil] Custom span filter functions
# @param exporter [Exporter, nil] Optional exporter override (for testing)
# @param environment [Hash, nil] Span-origin environment override
# @return [State] the created state
def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil, blocking_login: false, enable_tracing: true, tracer_provider: nil, filter_ai_spans: nil, span_filter_funcs: nil, exporter: nil)
def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil, blocking_login: false, enable_tracing: true, tracer_provider: nil, filter_ai_spans: nil, span_filter_funcs: nil, exporter: nil, environment: nil)
require_relative "config"
config = Config.from_env(
api_key: api_key,
Expand All @@ -35,7 +36,8 @@ def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: ni
app_url: app_url,
api_url: api_url,
filter_ai_spans: filter_ai_spans,
span_filter_funcs: span_filter_funcs
span_filter_funcs: span_filter_funcs,
environment: environment
)
new(
api_key: config.api_key,
Expand Down
5 changes: 4 additions & 1 deletion lib/braintrust/trace/span_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ module SpanFilter
SYSTEM_ATTRIBUTES = [
"braintrust.parent",
"braintrust.org",
"braintrust.app_url"
"braintrust.app_url",
"braintrust.context_json",
"braintrust.environment.type",
"braintrust.environment.name"
].freeze

# Prefixes that indicate an AI-related span
Expand Down
Loading