bitfab 0.9.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 +7 -0
- data/README.md +342 -0
- data/lib/bitfab/client.rb +226 -0
- data/lib/bitfab/constants.rb +6 -0
- data/lib/bitfab/http_client.rb +175 -0
- data/lib/bitfab/replay.rb +149 -0
- data/lib/bitfab/serialize.rb +103 -0
- data/lib/bitfab/span_context.rb +151 -0
- data/lib/bitfab/traceable.rb +141 -0
- data/lib/bitfab/version.rb +5 -0
- data/lib/bitfab.rb +97 -0
- metadata +152 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bitfab
|
|
4
|
+
# Mixin for declarative span tracing on instance methods.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Bitfab.configure(api_key: "...")
|
|
8
|
+
#
|
|
9
|
+
# class OrderService
|
|
10
|
+
# include Bitfab::Traceable
|
|
11
|
+
# bitfab_function "order-processing"
|
|
12
|
+
#
|
|
13
|
+
# bitfab_span :process_order, type: "function"
|
|
14
|
+
# def process_order(order_id)
|
|
15
|
+
# { total: 100 }
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# bitfab_span :validate_order, name: "Validate", type: "guardrail"
|
|
19
|
+
# def validate_order(order_id)
|
|
20
|
+
# { valid: true }
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
module Traceable
|
|
25
|
+
def self.included(base)
|
|
26
|
+
base.extend(ClassMethods)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Wrap an existing method on an external class with span tracing.
|
|
30
|
+
# Use this to trace third-party library calls without modifying their source.
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# Bitfab::Traceable.wrap(OpenAI::Client, :chat,
|
|
34
|
+
# trace_function_key: "openai", name: "Chat", type: "llm")
|
|
35
|
+
#
|
|
36
|
+
# @param klass [Class, Module] the class to wrap
|
|
37
|
+
# @param method_name [Symbol] the method to wrap
|
|
38
|
+
# @param trace_function_key [String] the trace function key
|
|
39
|
+
# @param name [String, nil] explicit span name (defaults to method name)
|
|
40
|
+
# @param type [String] span type: llm, agent, function, guardrail, handoff, custom
|
|
41
|
+
def self.wrap(klass, method_name, trace_function_key:, name: nil, type: "custom")
|
|
42
|
+
span_name = name || method_name.to_s
|
|
43
|
+
method_name_str = method_name.to_s
|
|
44
|
+
|
|
45
|
+
wrapper = Module.new do
|
|
46
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
47
|
+
Bitfab.client.send(:execute_span,
|
|
48
|
+
trace_function_key:,
|
|
49
|
+
span_name:,
|
|
50
|
+
span_type: type,
|
|
51
|
+
function_name: method_name_str,
|
|
52
|
+
args:,
|
|
53
|
+
kwargs:) do
|
|
54
|
+
super(*args, **kwargs, &block)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
klass.prepend(wrapper)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module ClassMethods
|
|
63
|
+
# Set the trace function key for this class.
|
|
64
|
+
# All spans declared in this class will be grouped under this key.
|
|
65
|
+
#
|
|
66
|
+
# @param key [String] the trace function key
|
|
67
|
+
def bitfab_function(key)
|
|
68
|
+
@bitfab_function_key = key
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Declare that a method should be wrapped with span tracing.
|
|
72
|
+
#
|
|
73
|
+
# Supports three styles:
|
|
74
|
+
# bitfab_span :foo, type: "function" # before def foo (uses method_added hook)
|
|
75
|
+
# def foo; end
|
|
76
|
+
#
|
|
77
|
+
# bitfab_span def foo # inline (def returns :foo, method already exists)
|
|
78
|
+
# ...
|
|
79
|
+
# end, type: "function"
|
|
80
|
+
#
|
|
81
|
+
# def foo; end # after def foo (method already exists)
|
|
82
|
+
# bitfab_span :foo, type: "function"
|
|
83
|
+
#
|
|
84
|
+
# @param method_name [Symbol] the method to wrap
|
|
85
|
+
# @param trace_function_key [String, nil] trace function key (overrides class-level bitfab_function)
|
|
86
|
+
# @param name [String, nil] explicit span name (defaults to method name)
|
|
87
|
+
# @param type [String] span type: llm, agent, function, guardrail, handoff, custom
|
|
88
|
+
def bitfab_span(method_name, trace_function_key: nil, name: nil, type: "custom")
|
|
89
|
+
trace_function_key ||= @bitfab_function_key
|
|
90
|
+
unless trace_function_key
|
|
91
|
+
raise "No trace function key provided. Pass `trace_function_key:` to `bitfab_span` " \
|
|
92
|
+
"or call `bitfab_function 'my-key'` before using `bitfab_span`."
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# If the method already exists (inline or after-method style), wrap it immediately
|
|
96
|
+
if method_defined?(method_name) || private_method_defined?(method_name)
|
|
97
|
+
_bitfab_wrap_method(method_name, trace_function_key:, name:, type:)
|
|
98
|
+
else
|
|
99
|
+
# Method doesn't exist yet (before-method style) — register for method_added hook
|
|
100
|
+
@_bitfab_pending_spans ||= {}
|
|
101
|
+
@_bitfab_pending_spans[method_name] = {
|
|
102
|
+
trace_function_key:,
|
|
103
|
+
name:,
|
|
104
|
+
type:
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def method_added(method_name)
|
|
112
|
+
super
|
|
113
|
+
return unless defined?(@_bitfab_pending_spans) && @_bitfab_pending_spans&.key?(method_name)
|
|
114
|
+
|
|
115
|
+
config = @_bitfab_pending_spans.delete(method_name)
|
|
116
|
+
_bitfab_wrap_method(method_name, **config)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def _bitfab_wrap_method(method_name, trace_function_key:, name: nil, type: "custom")
|
|
120
|
+
span_name = name || method_name.to_s
|
|
121
|
+
method_name_str = method_name.to_s
|
|
122
|
+
|
|
123
|
+
wrapper = Module.new do
|
|
124
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
125
|
+
Bitfab.client.send(:execute_span,
|
|
126
|
+
trace_function_key:,
|
|
127
|
+
span_name:,
|
|
128
|
+
span_type: type,
|
|
129
|
+
function_name: method_name_str,
|
|
130
|
+
args:,
|
|
131
|
+
kwargs:) do
|
|
132
|
+
super(*args, **kwargs, &block)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
prepend(wrapper)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/bitfab.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "bitfab/version"
|
|
4
|
+
require_relative "bitfab/constants"
|
|
5
|
+
require_relative "bitfab/serialize"
|
|
6
|
+
require_relative "bitfab/span_context"
|
|
7
|
+
require_relative "bitfab/http_client"
|
|
8
|
+
require_relative "bitfab/replay"
|
|
9
|
+
require_relative "bitfab/client"
|
|
10
|
+
require_relative "bitfab/traceable"
|
|
11
|
+
|
|
12
|
+
module Bitfab
|
|
13
|
+
# No-op span handle returned when outside a span context.
|
|
14
|
+
# All methods do nothing, preventing crashes when called outside traced code.
|
|
15
|
+
class NoOpCurrentSpan
|
|
16
|
+
def trace_id
|
|
17
|
+
""
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_context(_context)
|
|
21
|
+
# No-op
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def set_prompt(_prompt)
|
|
25
|
+
# No-op
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# No-op trace handle returned when outside a span context.
|
|
30
|
+
# All methods do nothing, preventing crashes when called outside traced code.
|
|
31
|
+
class NoOpCurrentTrace
|
|
32
|
+
def set_session_id(_session_id)
|
|
33
|
+
# No-op
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def set_metadata(_metadata)
|
|
37
|
+
# No-op
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_context(_context)
|
|
41
|
+
# No-op
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
NO_OP_SPAN = NoOpCurrentSpan.new.freeze
|
|
46
|
+
NO_OP_TRACE = NoOpCurrentTrace.new.freeze
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
# Configure the global Bitfab client.
|
|
50
|
+
#
|
|
51
|
+
# @param api_key [String] API key for authentication
|
|
52
|
+
# @param service_url [String, nil] base URL (default: https://bitfab.ai)
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# Bitfab.configure(api_key: ENV["BITFAB_API_KEY"])
|
|
56
|
+
#
|
|
57
|
+
def configure(api_key:, service_url: nil, enabled: true)
|
|
58
|
+
@client = Client.new(api_key:, service_url:, enabled:)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the global client, raising if not configured.
|
|
62
|
+
def client
|
|
63
|
+
@client or raise "Bitfab not configured. Call Bitfab.configure(api_key: '...') first."
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Reset the global client (primarily for testing).
|
|
67
|
+
def reset!
|
|
68
|
+
@client = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Get a handle to the current active span.
|
|
72
|
+
#
|
|
73
|
+
# Call this from inside a traced method to get a span handle that allows
|
|
74
|
+
# setting metadata at runtime.
|
|
75
|
+
#
|
|
76
|
+
# @return [CurrentSpan, NoOpCurrentSpan] the current span, or a no-op if outside a span context
|
|
77
|
+
def current_span
|
|
78
|
+
entry = SpanContext.current
|
|
79
|
+
return NO_OP_SPAN unless entry
|
|
80
|
+
|
|
81
|
+
CurrentSpan.new(entry)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get a handle to the current active trace.
|
|
85
|
+
#
|
|
86
|
+
# Call this from inside a traced method to get a trace handle that allows
|
|
87
|
+
# setting trace-level context at runtime.
|
|
88
|
+
#
|
|
89
|
+
# @return [CurrentTrace, NoOpCurrentTrace] the current trace, or a no-op if outside a span context
|
|
90
|
+
def current_trace
|
|
91
|
+
entry = SpanContext.current
|
|
92
|
+
return NO_OP_TRACE unless entry
|
|
93
|
+
|
|
94
|
+
CurrentTrace.new(entry[:trace_id])
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bitfab
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.9.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Harvest Team
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.13'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.13'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop-standard
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: standard
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1.0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1.0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: webmock
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '3.0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '3.0'
|
|
110
|
+
description: Client library for sending function execution spans to the Bitfab API.
|
|
111
|
+
Provides automatic tracing with nested span support.
|
|
112
|
+
email:
|
|
113
|
+
- team@goharvest.ai
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- README.md
|
|
119
|
+
- lib/bitfab.rb
|
|
120
|
+
- lib/bitfab/client.rb
|
|
121
|
+
- lib/bitfab/constants.rb
|
|
122
|
+
- lib/bitfab/http_client.rb
|
|
123
|
+
- lib/bitfab/replay.rb
|
|
124
|
+
- lib/bitfab/serialize.rb
|
|
125
|
+
- lib/bitfab/span_context.rb
|
|
126
|
+
- lib/bitfab/traceable.rb
|
|
127
|
+
- lib/bitfab/version.rb
|
|
128
|
+
homepage: https://bitfab.ai
|
|
129
|
+
licenses:
|
|
130
|
+
- MIT
|
|
131
|
+
metadata:
|
|
132
|
+
homepage_uri: https://bitfab.ai
|
|
133
|
+
source_code_uri: https://bitfab.ai
|
|
134
|
+
rubygems_mfa_required: 'true'
|
|
135
|
+
rdoc_options: []
|
|
136
|
+
require_paths:
|
|
137
|
+
- lib
|
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '3.4'
|
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
|
+
requirements:
|
|
145
|
+
- - ">="
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
version: '0'
|
|
148
|
+
requirements: []
|
|
149
|
+
rubygems_version: 3.6.9
|
|
150
|
+
specification_version: 4
|
|
151
|
+
summary: Bitfab Ruby SDK for function tracing and span management
|
|
152
|
+
test_files: []
|