simforge 0.5.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/lib/simforge/client.rb +114 -0
- data/lib/simforge/constants.rb +5 -0
- data/lib/simforge/http_client.rb +108 -0
- data/lib/simforge/serialize.rb +69 -0
- data/lib/simforge/span_context.rb +29 -0
- data/lib/simforge/traceable.rb +108 -0
- data/lib/simforge/version.rb +5 -0
- data/lib/simforge.rb +35 -0
- metadata +108 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 662f1ce8942083e856907a38568a40b252c4d6360f043fc25da58900a46e36e7
|
|
4
|
+
data.tar.gz: 9d74cf518171472d22b49c6b8fb3544e258607e5284acebe0f6b46e71213e0cd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 556e17aa0f010ecb394f7a5937c3c02a43ef0d76bffe00082899074a19f26c555bec5c4d47dfe1afa98c34f7093beae5dd4b4a4d2d9d00a68d0155aadd979aea
|
|
7
|
+
data.tar.gz: 14cd3fbc8c4ea4096036ce312e6eac67ab7b57a634c10faba0962fa3dd10368928ebc3c25c1236b5ff6019a8db36094b0a4e4a8eeb737da2c76fc42e5525cd62
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
require_relative "constants"
|
|
7
|
+
require_relative "http_client"
|
|
8
|
+
require_relative "span_context"
|
|
9
|
+
require_relative "serialize"
|
|
10
|
+
|
|
11
|
+
module Simforge
|
|
12
|
+
class Client
|
|
13
|
+
SPAN_TYPES = %w[llm agent function guardrail handoff custom].freeze
|
|
14
|
+
|
|
15
|
+
attr_reader :api_key, :service_url
|
|
16
|
+
|
|
17
|
+
def initialize(api_key:, service_url: nil)
|
|
18
|
+
@api_key = api_key
|
|
19
|
+
@service_url = service_url || DEFAULT_SERVICE_URL
|
|
20
|
+
@http_client = HttpClient.new(api_key:, service_url: @service_url)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Execute a block inside a span context, sending trace data on completion.
|
|
24
|
+
# Called by Traceable — not intended for direct use.
|
|
25
|
+
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:)
|
|
26
|
+
validate_span_type!(span_type)
|
|
27
|
+
|
|
28
|
+
parent = SpanContext.current
|
|
29
|
+
trace_id = parent ? parent[:trace_id] : SecureRandom.uuid
|
|
30
|
+
span_id = SecureRandom.uuid
|
|
31
|
+
parent_span_id = parent&.dig(:span_id)
|
|
32
|
+
started_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
33
|
+
|
|
34
|
+
result = nil
|
|
35
|
+
error = nil
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
result = SpanContext.with_span(trace_id:, span_id:) { yield }
|
|
39
|
+
rescue => e
|
|
40
|
+
error = e.message
|
|
41
|
+
raise
|
|
42
|
+
ensure
|
|
43
|
+
ended_at = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
|
|
44
|
+
|
|
45
|
+
send_span(
|
|
46
|
+
trace_function_key:,
|
|
47
|
+
trace_id:,
|
|
48
|
+
span_id:,
|
|
49
|
+
parent_span_id:,
|
|
50
|
+
span_name:,
|
|
51
|
+
span_type:,
|
|
52
|
+
function_name:,
|
|
53
|
+
args:,
|
|
54
|
+
kwargs:,
|
|
55
|
+
result:,
|
|
56
|
+
error:,
|
|
57
|
+
started_at:,
|
|
58
|
+
ended_at:
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def validate_span_type!(type)
|
|
68
|
+
return if SPAN_TYPES.include?(type.to_s)
|
|
69
|
+
|
|
70
|
+
raise ArgumentError, "Invalid span type '#{type}'. Must be one of: #{SPAN_TYPES.join(", ")}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def send_span(trace_function_key:, trace_id:, span_id:, parent_span_id:,
|
|
74
|
+
span_name:, span_type:, function_name:, args:, kwargs:, result:, error:,
|
|
75
|
+
started_at:, ended_at:)
|
|
76
|
+
# Human-readable JSON (input/output fields)
|
|
77
|
+
human_inputs = Serialize.serialize_inputs(args, kwargs)
|
|
78
|
+
human_output = Serialize.serialize_value(result)
|
|
79
|
+
|
|
80
|
+
# Marshal + Base64 for full Ruby-to-Ruby object reconstruction
|
|
81
|
+
raw_input = (args.length == 1 && kwargs.empty?) ? args[0] : {args:, kwargs:}
|
|
82
|
+
marshalled_input = Serialize.marshal_value(raw_input)
|
|
83
|
+
marshalled_output = Serialize.marshal_value(result)
|
|
84
|
+
|
|
85
|
+
span_data = {
|
|
86
|
+
"name" => span_name,
|
|
87
|
+
"type" => span_type,
|
|
88
|
+
"input" => human_inputs,
|
|
89
|
+
"output" => human_output,
|
|
90
|
+
"function_name" => function_name
|
|
91
|
+
}
|
|
92
|
+
span_data["input_serialized"] = marshalled_input if marshalled_input
|
|
93
|
+
span_data["output_serialized"] = marshalled_output if marshalled_output
|
|
94
|
+
span_data["error"] = error if error
|
|
95
|
+
|
|
96
|
+
raw_span = {
|
|
97
|
+
"id" => span_id,
|
|
98
|
+
"trace_id" => trace_id,
|
|
99
|
+
"started_at" => started_at,
|
|
100
|
+
"ended_at" => ended_at,
|
|
101
|
+
"span_data" => span_data
|
|
102
|
+
}
|
|
103
|
+
raw_span["parent_id"] = parent_span_id if parent_span_id
|
|
104
|
+
|
|
105
|
+
@http_client.send_external_span(
|
|
106
|
+
"type" => "sdk-function",
|
|
107
|
+
"source" => "ruby-sdk-function",
|
|
108
|
+
"sourceTraceId" => trace_id,
|
|
109
|
+
"traceFunctionKey" => trace_function_key,
|
|
110
|
+
"rawSpan" => raw_span
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
require_relative "constants"
|
|
8
|
+
require_relative "version"
|
|
9
|
+
|
|
10
|
+
module Simforge
|
|
11
|
+
class HttpClient
|
|
12
|
+
attr_reader :service_url
|
|
13
|
+
|
|
14
|
+
def initialize(api_key:, service_url: nil, timeout: 120)
|
|
15
|
+
@api_key = api_key
|
|
16
|
+
@service_url = (service_url || DEFAULT_SERVICE_URL).chomp("/")
|
|
17
|
+
@timeout = timeout
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Make a POST request to the Simforge API.
|
|
21
|
+
# Returns parsed JSON response hash.
|
|
22
|
+
def request(endpoint, payload, timeout: nil, max_retries: 1, retry_delay: 0.1)
|
|
23
|
+
uri = URI("#{@service_url}#{endpoint}")
|
|
24
|
+
request_timeout = timeout || @timeout
|
|
25
|
+
|
|
26
|
+
last_error = nil
|
|
27
|
+
|
|
28
|
+
max_retries.times do |attempt|
|
|
29
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
30
|
+
http.use_ssl = uri.scheme == "https"
|
|
31
|
+
http.open_timeout = request_timeout
|
|
32
|
+
http.read_timeout = request_timeout
|
|
33
|
+
|
|
34
|
+
req = Net::HTTP::Post.new(uri.path, headers)
|
|
35
|
+
req.body = JSON.generate(payload)
|
|
36
|
+
|
|
37
|
+
response = http.request(req)
|
|
38
|
+
|
|
39
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
40
|
+
raise Net::HTTPError.new("HTTP #{response.code}: #{response.body}", response)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
result = JSON.parse(response.body)
|
|
44
|
+
|
|
45
|
+
if result["error"]
|
|
46
|
+
msg = result["error"]
|
|
47
|
+
msg = "#{msg} Configure it at: #{@service_url}#{result["url"]}" if result["url"]
|
|
48
|
+
raise StandardError, msg
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
rescue => e
|
|
53
|
+
last_error = e
|
|
54
|
+
sleep(retry_delay) if attempt < max_retries - 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
raise last_error
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Send an external span (fire-and-forget in background thread).
|
|
61
|
+
def send_external_span(payload)
|
|
62
|
+
merged = payload.merge("sdkVersion" => VERSION)
|
|
63
|
+
|
|
64
|
+
Simforge._run_in_background do
|
|
65
|
+
request("/api/sdk/externalSpans", merged, timeout: 30)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def headers
|
|
72
|
+
{
|
|
73
|
+
"Content-Type" => "application/json",
|
|
74
|
+
"Authorization" => "Bearer #{@api_key}"
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# --- Background thread management ---
|
|
80
|
+
|
|
81
|
+
@pending_threads_mutex = Mutex.new
|
|
82
|
+
@pending_threads = []
|
|
83
|
+
|
|
84
|
+
class << self
|
|
85
|
+
# Run a block in a background thread with tracking.
|
|
86
|
+
def _run_in_background(&block)
|
|
87
|
+
thread = Thread.new do
|
|
88
|
+
block.call
|
|
89
|
+
rescue
|
|
90
|
+
# Silently ignore failures in background spans
|
|
91
|
+
ensure
|
|
92
|
+
@pending_threads_mutex.synchronize { @pending_threads.delete(Thread.current) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@pending_threads_mutex.synchronize { @pending_threads << thread }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Wait for all pending background threads to complete.
|
|
99
|
+
def flush_traces(timeout: 30)
|
|
100
|
+
threads = @pending_threads_mutex.synchronize { @pending_threads.dup }
|
|
101
|
+
threads.each { |t| t.join(timeout) }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
at_exit do
|
|
106
|
+
Simforge.flush_traces(timeout: 2)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require "json"
|
|
5
|
+
require "time"
|
|
6
|
+
|
|
7
|
+
module Simforge
|
|
8
|
+
module Serialize
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Serialize a value for JSON storage (human-readable).
|
|
12
|
+
# Handles primitives, hashes, arrays, and objects with common conversion methods.
|
|
13
|
+
# Note: We intentionally avoid as_json here because it requires ActiveSupport,
|
|
14
|
+
# and we want to keep the SDK dependency-free (stdlib only).
|
|
15
|
+
def serialize_value(value)
|
|
16
|
+
case value
|
|
17
|
+
when nil, true, false, Integer, Float, String
|
|
18
|
+
value
|
|
19
|
+
when Hash
|
|
20
|
+
value.transform_keys(&:to_s).transform_values { |v| serialize_value(v) }
|
|
21
|
+
when Array
|
|
22
|
+
value.map { |v| serialize_value(v) }
|
|
23
|
+
when Set
|
|
24
|
+
value.map { |v| serialize_value(v) }
|
|
25
|
+
when Time, DateTime
|
|
26
|
+
value.iso8601(3)
|
|
27
|
+
when Date
|
|
28
|
+
value.to_s
|
|
29
|
+
when Symbol
|
|
30
|
+
value.to_s
|
|
31
|
+
else
|
|
32
|
+
if value.respond_to?(:to_h)
|
|
33
|
+
serialize_value(value.to_h)
|
|
34
|
+
elsif value.respond_to?(:to_a)
|
|
35
|
+
serialize_value(value.to_a)
|
|
36
|
+
else
|
|
37
|
+
value.to_s
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Serialize function inputs (args + kwargs) for span data (human-readable).
|
|
43
|
+
def serialize_inputs(args, kwargs = {})
|
|
44
|
+
serialized = args.map { |arg| serialize_value(arg) }
|
|
45
|
+
serialized << kwargs.transform_keys(&:to_s).transform_values { |v| serialize_value(v) } unless kwargs.empty?
|
|
46
|
+
serialized
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Marshal a value to a Base64-encoded string for Ruby-to-Ruby reconstruction.
|
|
50
|
+
# Handles arbitrary Ruby objects including custom classes.
|
|
51
|
+
#
|
|
52
|
+
# @param value [Object] any Ruby value
|
|
53
|
+
# @return [String, nil] Base64-encoded Marshal dump, or nil if marshalling fails
|
|
54
|
+
def marshal_value(value)
|
|
55
|
+
Base64.strict_encode64(Marshal.dump(value))
|
|
56
|
+
rescue TypeError, ArgumentError
|
|
57
|
+
# Some objects (Proc, IO, etc.) can't be marshalled
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Unmarshal a Base64-encoded string back into a Ruby object.
|
|
62
|
+
#
|
|
63
|
+
# @param encoded [String] Base64-encoded Marshal dump
|
|
64
|
+
# @return [Object] the reconstructed Ruby object
|
|
65
|
+
def unmarshal_value(encoded)
|
|
66
|
+
Marshal.load(Base64.strict_decode64(encoded)) # rubocop:disable Security/MarshalLoad
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Simforge
|
|
4
|
+
# Thread-local span stack for tracking nested spans.
|
|
5
|
+
# Each entry is a Hash with :trace_id and :span_id keys.
|
|
6
|
+
module SpanContext
|
|
7
|
+
STACK_KEY = :simforge_span_stack
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def stack
|
|
12
|
+
Thread.current[STACK_KEY] ||= []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def current
|
|
16
|
+
stack.last
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Execute a block with a new span pushed onto the stack.
|
|
20
|
+
# The span is automatically popped when the block completes.
|
|
21
|
+
def with_span(trace_id:, span_id:)
|
|
22
|
+
entry = {trace_id:, span_id:}
|
|
23
|
+
stack.push(entry)
|
|
24
|
+
yield
|
|
25
|
+
ensure
|
|
26
|
+
stack.pop
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Simforge
|
|
4
|
+
# Mixin for declarative span tracing on instance methods.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# Simforge.configure(api_key: "...")
|
|
8
|
+
#
|
|
9
|
+
# class OrderService
|
|
10
|
+
# include Simforge::Traceable
|
|
11
|
+
# simforge_function "order-processing"
|
|
12
|
+
#
|
|
13
|
+
# simforge_span :process_order, type: "function"
|
|
14
|
+
# def process_order(order_id)
|
|
15
|
+
# { total: 100 }
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# simforge_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
|
+
module ClassMethods
|
|
30
|
+
# Set the trace function key for this class.
|
|
31
|
+
# All spans declared in this class will be grouped under this key.
|
|
32
|
+
#
|
|
33
|
+
# @param key [String] the trace function key
|
|
34
|
+
def simforge_function(key)
|
|
35
|
+
@simforge_function_key = key
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Declare that a method should be wrapped with span tracing.
|
|
39
|
+
#
|
|
40
|
+
# Supports three styles:
|
|
41
|
+
# simforge_span :foo, type: "function" # before def foo (uses method_added hook)
|
|
42
|
+
# def foo; end
|
|
43
|
+
#
|
|
44
|
+
# simforge_span def foo # inline (def returns :foo, method already exists)
|
|
45
|
+
# ...
|
|
46
|
+
# end, type: "function"
|
|
47
|
+
#
|
|
48
|
+
# def foo; end # after def foo (method already exists)
|
|
49
|
+
# simforge_span :foo, type: "function"
|
|
50
|
+
#
|
|
51
|
+
# @param method_name [Symbol] the method to wrap
|
|
52
|
+
# @param trace_function_key [String, nil] trace function key (overrides class-level simforge_function)
|
|
53
|
+
# @param name [String, nil] explicit span name (defaults to method name)
|
|
54
|
+
# @param type [String] span type: llm, agent, function, guardrail, handoff, custom
|
|
55
|
+
def simforge_span(method_name, trace_function_key: nil, name: nil, type: "custom")
|
|
56
|
+
trace_function_key ||= @simforge_function_key
|
|
57
|
+
unless trace_function_key
|
|
58
|
+
raise "No trace function key provided. Pass `trace_function_key:` to `simforge_span` " \
|
|
59
|
+
"or call `simforge_function 'my-key'` before using `simforge_span`."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# If the method already exists (inline or after-method style), wrap it immediately
|
|
63
|
+
if method_defined?(method_name) || private_method_defined?(method_name)
|
|
64
|
+
_simforge_wrap_method(method_name, trace_function_key:, name:, type:)
|
|
65
|
+
else
|
|
66
|
+
# Method doesn't exist yet (before-method style) — register for method_added hook
|
|
67
|
+
@_simforge_pending_spans ||= {}
|
|
68
|
+
@_simforge_pending_spans[method_name] = {
|
|
69
|
+
trace_function_key:,
|
|
70
|
+
name:,
|
|
71
|
+
type:
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def method_added(method_name)
|
|
79
|
+
super
|
|
80
|
+
return unless defined?(@_simforge_pending_spans) && @_simforge_pending_spans&.key?(method_name)
|
|
81
|
+
|
|
82
|
+
config = @_simforge_pending_spans.delete(method_name)
|
|
83
|
+
_simforge_wrap_method(method_name, **config)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def _simforge_wrap_method(method_name, trace_function_key:, name: nil, type: "custom")
|
|
87
|
+
span_name = name || method_name.to_s
|
|
88
|
+
method_name_str = method_name.to_s
|
|
89
|
+
|
|
90
|
+
wrapper = Module.new do
|
|
91
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
92
|
+
Simforge.client.send(:execute_span,
|
|
93
|
+
trace_function_key:,
|
|
94
|
+
span_name:,
|
|
95
|
+
span_type: type,
|
|
96
|
+
function_name: method_name_str,
|
|
97
|
+
args:,
|
|
98
|
+
kwargs:) do
|
|
99
|
+
super(*args, **kwargs, &block)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
prepend(wrapper)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/simforge.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "simforge/version"
|
|
4
|
+
require_relative "simforge/constants"
|
|
5
|
+
require_relative "simforge/serialize"
|
|
6
|
+
require_relative "simforge/span_context"
|
|
7
|
+
require_relative "simforge/http_client"
|
|
8
|
+
require_relative "simforge/client"
|
|
9
|
+
require_relative "simforge/traceable"
|
|
10
|
+
|
|
11
|
+
module Simforge
|
|
12
|
+
class << self
|
|
13
|
+
# Configure the global Simforge client.
|
|
14
|
+
#
|
|
15
|
+
# @param api_key [String] API key for authentication
|
|
16
|
+
# @param service_url [String, nil] base URL (default: https://simforge.goharvest.ai)
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# Simforge.configure(api_key: ENV["SIMFORGE_API_KEY"])
|
|
20
|
+
#
|
|
21
|
+
def configure(api_key:, service_url: nil)
|
|
22
|
+
@client = Client.new(api_key:, service_url:)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the global client, raising if not configured.
|
|
26
|
+
def client
|
|
27
|
+
@client or raise "Simforge not configured. Call Simforge.configure(api_key: '...') first."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Reset the global client (primarily for testing).
|
|
31
|
+
def reset!
|
|
32
|
+
@client = nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: simforge
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.5.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Harvest Team
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-01-29 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '13.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '13.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: standard
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: webmock
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.0'
|
|
68
|
+
description: Client library for sending function execution spans to the Simforge API.
|
|
69
|
+
Provides automatic tracing with nested span support.
|
|
70
|
+
email:
|
|
71
|
+
- team@goharvest.ai
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- lib/simforge.rb
|
|
77
|
+
- lib/simforge/client.rb
|
|
78
|
+
- lib/simforge/constants.rb
|
|
79
|
+
- lib/simforge/http_client.rb
|
|
80
|
+
- lib/simforge/serialize.rb
|
|
81
|
+
- lib/simforge/span_context.rb
|
|
82
|
+
- lib/simforge/traceable.rb
|
|
83
|
+
- lib/simforge/version.rb
|
|
84
|
+
homepage: https://simforge.goharvest.ai
|
|
85
|
+
licenses:
|
|
86
|
+
- MIT
|
|
87
|
+
metadata:
|
|
88
|
+
homepage_uri: https://simforge.goharvest.ai
|
|
89
|
+
source_code_uri: https://simforge.goharvest.ai
|
|
90
|
+
rubygems_mfa_required: 'true'
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '3.1'
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubygems_version: 3.6.6
|
|
106
|
+
specification_version: 4
|
|
107
|
+
summary: Simforge Ruby SDK for function tracing and span management
|
|
108
|
+
test_files: []
|