langfuse-ruby 0.1.7 → 0.2.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.
@@ -3,12 +3,14 @@
3
3
  module Langfuse
4
4
  class Generation
5
5
  attr_reader :id, :trace_id, :name, :start_time, :end_time, :completion_start_time,
6
- :model, :model_parameters, :input, :output, :usage, :metadata, :level,
6
+ :model, :model_parameters, :input, :output, :usage, :usage_details, :cost_details,
7
+ :prompt_name, :prompt_version, :metadata, :level,
7
8
  :status_message, :parent_observation_id, :version, :as_type, :client
8
9
 
9
10
  def initialize(client:, trace_id:, id: nil, name: nil, start_time: nil, end_time: nil,
10
11
  completion_start_time: nil, model: nil, model_parameters: nil, input: nil,
11
- output: nil, usage: nil, metadata: nil, level: nil, status_message: nil,
12
+ output: nil, usage: nil, usage_details: nil, cost_details: nil, prompt: nil,
13
+ metadata: nil, level: nil, status_message: nil,
12
14
  parent_observation_id: nil, version: nil, as_type: nil, **kwargs)
13
15
  @client = client
14
16
  @id = id || Utils.generate_id
@@ -22,6 +24,9 @@ module Langfuse
22
24
  @input = input
23
25
  @output = output
24
26
  @usage = usage || {}
27
+ @usage_details = usage_details || {}
28
+ @cost_details = cost_details || {}
29
+ @prompt_name, @prompt_version = extract_prompt_info(prompt)
25
30
  @metadata = metadata || {}
26
31
  @level = level
27
32
  @status_message = status_message
@@ -35,7 +40,8 @@ module Langfuse
35
40
  end
36
41
 
37
42
  def update(name: nil, end_time: nil, completion_start_time: nil, model: nil,
38
- model_parameters: nil, input: nil, output: nil, usage: nil, metadata: nil,
43
+ model_parameters: nil, input: nil, output: nil, usage: nil,
44
+ usage_details: nil, cost_details: nil, prompt: nil, metadata: nil,
39
45
  level: nil, status_message: nil, version: nil, **kwargs)
40
46
  @name = name if name
41
47
  @end_time = end_time if end_time
@@ -45,6 +51,9 @@ module Langfuse
45
51
  @input = input if input
46
52
  @output = output if output
47
53
  @usage.merge!(usage) if usage
54
+ @usage_details.merge!(usage_details) if usage_details
55
+ @cost_details.merge!(cost_details) if cost_details
56
+ @prompt_name, @prompt_version = extract_prompt_info(prompt) if prompt
48
57
  @metadata.merge!(metadata) if metadata
49
58
  @level = level if level
50
59
  @status_message = status_message if status_message
@@ -55,10 +64,12 @@ module Langfuse
55
64
  self
56
65
  end
57
66
 
58
- def end(output: nil, end_time: nil, usage: nil, **kwargs)
67
+ def end(output: nil, end_time: nil, usage: nil, usage_details: nil, cost_details: nil, **kwargs)
59
68
  @end_time = end_time || Utils.current_timestamp
60
69
  @output = output if output
61
70
  @usage.merge!(usage) if usage
71
+ @usage_details.merge!(usage_details) if usage_details
72
+ @cost_details.merge!(cost_details) if cost_details
62
73
  @kwargs.merge!(kwargs)
63
74
 
64
75
  update_generation
@@ -261,6 +272,7 @@ module Langfuse
261
272
 
262
273
  def score(name:, value:, data_type: nil, comment: nil, **kwargs)
263
274
  @client.score(
275
+ trace_id: @trace_id,
264
276
  observation_id: @id,
265
277
  name: name,
266
278
  value: value,
@@ -293,12 +305,32 @@ module Langfuse
293
305
  parent_observation_id: @parent_observation_id,
294
306
  version: @version
295
307
  }
308
+ data[:usage_details] = @usage_details unless @usage_details.nil? || @usage_details.empty?
309
+ data[:cost_details] = @cost_details unless @cost_details.nil? || @cost_details.empty?
310
+ data[:prompt_name] = @prompt_name if @prompt_name
311
+ data[:prompt_version] = @prompt_version if @prompt_version
296
312
  data[:type] = @as_type if @as_type
297
313
  data.merge(@kwargs).compact
298
314
  end
299
315
 
300
316
  private
301
317
 
318
+ # Accepts a Langfuse::Prompt, a Hash with :name/:version, or nil.
319
+ # Returns [prompt_name, prompt_version] used to link the generation to a prompt version.
320
+ def extract_prompt_info(prompt)
321
+ return [nil, nil] if prompt.nil?
322
+
323
+ if prompt.respond_to?(:name) && prompt.respond_to?(:version)
324
+ [prompt.name, prompt.version]
325
+ elsif prompt.is_a?(Hash)
326
+ name = prompt[:name] || prompt['name']
327
+ version = prompt[:version] || prompt['version']
328
+ [name, version]
329
+ else
330
+ [nil, nil]
331
+ end
332
+ end
333
+
302
334
  def validate_as_type(type)
303
335
  return nil if type.nil?
304
336
 
@@ -9,21 +9,45 @@ module Langfuse
9
9
  class OtelExporter
10
10
  OTEL_ENDPOINT = '/api/public/otel/v1/traces'
11
11
 
12
+ class << self
13
+ # Convert an ID (UUID or hex string) to an OTEL 32-char hex trace ID.
14
+ # OTEL trace IDs are 16 bytes (32 hex chars). Native hex IDs pass through unchanged.
15
+ def to_otel_trace_id(id_str)
16
+ return '0' * 32 unless id_str
17
+
18
+ hex = id_str.to_s.delete('-')
19
+ hex.ljust(32, '0')[0, 32]
20
+ end
21
+
22
+ # Convert an ID (UUID or hex string) to an OTEL 16-char hex span ID.
23
+ # OTEL span IDs are 8 bytes (16 hex chars). Native hex IDs pass through unchanged.
24
+ def to_otel_span_id(id_str)
25
+ return '0' * 16 unless id_str
26
+
27
+ hex = id_str.to_s.delete('-')
28
+ hex[0, 16]
29
+ end
30
+ end
31
+
12
32
  # @param connection [Faraday::Connection] HTTP connection to Langfuse host
13
33
  # @param debug [Boolean] whether to print debug output
14
- def initialize(connection:, debug: false)
34
+ # @param logger [Logger, nil] logger for debug output
35
+ def initialize(connection:, debug: false, logger: nil)
15
36
  @connection = connection
16
37
  @debug = debug
38
+ @logger = logger
17
39
  end
18
40
 
19
41
  # Export a batch of Langfuse events as OTLP spans.
42
+ # Note: score-create events are not part of the OTLP mapping and are
43
+ # handled separately by the client via the ingestion API.
20
44
  # @param events [Array<Hash>] array of event hashes from the event queue
21
45
  # @return [Faraday::Response]
22
46
  def export(events)
23
47
  resource_spans = build_resource_spans(events)
24
48
  payload = { resourceSpans: resource_spans }
25
49
 
26
- puts "OTEL export payload: #{JSON.pretty_generate(payload)}" if @debug
50
+ log_debug { "OTEL export payload: #{JSON.pretty_generate(payload)}" }
27
51
 
28
52
  @connection.post(OTEL_ENDPOINT) do |req|
29
53
  req.headers['Content-Type'] = 'application/json'
@@ -33,6 +57,16 @@ module Langfuse
33
57
 
34
58
  private
35
59
 
60
+ def log_debug(&block)
61
+ return unless @debug
62
+
63
+ if @logger
64
+ @logger.debug(block.call)
65
+ else
66
+ puts block.call
67
+ end
68
+ end
69
+
36
70
  # Build the top-level resourceSpans array from events.
37
71
  # Groups events by trace_id, producing one scopeSpan per trace.
38
72
  def build_resource_spans(events)
@@ -86,8 +120,6 @@ module Langfuse
86
120
  build_observation_span(body, 'generation')
87
121
  when 'event-create'
88
122
  build_event_span(body)
89
- when 'score-create'
90
- build_score_span(body)
91
123
  end
92
124
  end
93
125
 
@@ -102,6 +134,9 @@ module Langfuse
102
134
  add_attr(attributes, 'langfuse.session.id', body['sessionId'])
103
135
  add_attr(attributes, 'langfuse.release', body['release'])
104
136
  add_attr(attributes, 'langfuse.version', body['version'])
137
+ add_attr(attributes, 'langfuse.environment', body['environment'])
138
+ add_attr(attributes, 'langfuse.trace.public', body['public']) unless body['public'].nil?
139
+ add_attr(attributes, 'langfuse.internal.as_root', true)
105
140
  add_json_attr(attributes, 'langfuse.trace.input', body['input'])
106
141
  add_json_attr(attributes, 'langfuse.trace.output', body['output'])
107
142
  add_json_attr(attributes, 'langfuse.trace.metadata', body['metadata'])
@@ -186,44 +221,6 @@ module Langfuse
186
221
  span
187
222
  end
188
223
 
189
- # Build a minimal OTEL span for a score event.
190
- def build_score_span(body)
191
- trace_id_raw = body['traceId']
192
- return nil unless trace_id_raw
193
-
194
- trace_id = to_otel_trace_id(trace_id_raw)
195
- span_id = to_otel_span_id(body['id'] || SecureRandom.uuid)
196
- timestamp = to_unix_nano(body['timestamp'] || Time.now.utc.iso8601(3))
197
-
198
- attributes = []
199
- add_attr(attributes, 'langfuse.score.name', body['name'])
200
- add_attr(attributes, 'langfuse.score.value', body['value'])
201
- add_attr(attributes, 'langfuse.score.data_type', body['dataType'])
202
- add_attr(attributes, 'langfuse.score.comment', body['comment'])
203
- add_attr(attributes, 'langfuse.observation.type', 'score')
204
-
205
- if body['observationId']
206
- add_attr(attributes, 'langfuse.score.observation_id', body['observationId'])
207
- end
208
-
209
- span = {
210
- traceId: trace_id,
211
- spanId: span_id,
212
- name: "score-#{body['name']}",
213
- kind: 1,
214
- startTimeUnixNano: timestamp,
215
- endTimeUnixNano: timestamp,
216
- attributes: attributes,
217
- status: { code: 1 }
218
- }
219
-
220
- # Parent is either the observation or the trace
221
- parent_raw = body['observationId'] || trace_id_raw
222
- span[:parentSpanId] = to_otel_span_id(parent_raw) if parent_raw
223
-
224
- span
225
- end
226
-
227
224
  # Build OTEL attributes for a span/generation observation.
228
225
  def build_observation_attributes(body, obs_type)
229
226
  attributes = []
@@ -234,6 +231,7 @@ module Langfuse
234
231
  add_json_attr(attributes, 'langfuse.observation.metadata', body['metadata'])
235
232
  add_attr(attributes, 'langfuse.observation.level', body['level'])
236
233
  add_attr(attributes, 'langfuse.observation.status_message', body['statusMessage'])
234
+ add_attr(attributes, 'langfuse.environment', body['environment'])
237
235
 
238
236
  if obs_type == 'generation'
239
237
  add_generation_attributes(attributes, body)
@@ -261,25 +259,19 @@ module Langfuse
261
259
  add_attr(attributes, 'gen_ai.usage.total_tokens', total) if total
262
260
  end
263
261
 
262
+ add_json_attr(attributes, 'langfuse.observation.usage_details', body['usageDetails'])
263
+ add_json_attr(attributes, 'langfuse.observation.cost_details', body['costDetails'])
264
+ add_attr(attributes, 'langfuse.observation.prompt.name', body['promptName'])
265
+ add_attr(attributes, 'langfuse.observation.prompt.version', body['promptVersion'])
264
266
  add_attr(attributes, 'langfuse.observation.completion_start_time', body['completionStartTime'])
265
267
  end
266
268
 
267
- # Convert a UUID string to OTEL 32-char hex trace ID.
268
- # OTEL trace IDs are 16 bytes (32 hex chars).
269
- def to_otel_trace_id(uuid_str)
270
- return '0' * 32 unless uuid_str
271
-
272
- hex = uuid_str.to_s.delete('-')
273
- hex.ljust(32, '0')[0, 32]
269
+ def to_otel_trace_id(id_str)
270
+ self.class.to_otel_trace_id(id_str)
274
271
  end
275
272
 
276
- # Convert a UUID string to OTEL 16-char hex span ID.
277
- # OTEL span IDs are 8 bytes (16 hex chars).
278
- def to_otel_span_id(uuid_str)
279
- return '0' * 16 unless uuid_str
280
-
281
- hex = uuid_str.to_s.delete('-')
282
- hex[0, 16]
273
+ def to_otel_span_id(id_str)
274
+ self.class.to_otel_span_id(id_str)
283
275
  end
284
276
 
285
277
  # Convert an ISO8601 timestamp string to nanoseconds since epoch.
@@ -287,7 +279,7 @@ module Langfuse
287
279
  return '0' unless timestamp_str
288
280
 
289
281
  time = Time.parse(timestamp_str.to_s)
290
- ((time.to_f * 1_000_000_000).to_i).to_s
282
+ (time.to_f * 1_000_000_000).to_i.to_s
291
283
  rescue ArgumentError
292
284
  '0'
293
285
  end
data/lib/langfuse/span.rb CHANGED
@@ -250,6 +250,7 @@ module Langfuse
250
250
 
251
251
  def score(name:, value:, data_type: nil, comment: nil, **kwargs)
252
252
  @client.score(
253
+ trace_id: @trace_id,
253
254
  observation_id: @id,
254
255
  name: name,
255
256
  value: value,
@@ -3,11 +3,11 @@
3
3
  module Langfuse
4
4
  class Trace
5
5
  attr_reader :id, :name, :user_id, :session_id, :version, :release, :input, :output,
6
- :metadata, :tags, :timestamp, :client
6
+ :metadata, :tags, :timestamp, :public, :client
7
7
 
8
8
  def initialize(client:, id:, name: nil, user_id: nil, session_id: nil, version: nil,
9
9
  release: nil, input: nil, output: nil, metadata: nil, tags: nil,
10
- timestamp: nil, **kwargs)
10
+ timestamp: nil, public: nil, **kwargs)
11
11
  @client = client
12
12
  @id = id
13
13
  @name = name
@@ -20,6 +20,7 @@ module Langfuse
20
20
  @metadata = metadata || {}
21
21
  @tags = tags || []
22
22
  @timestamp = timestamp
23
+ @public = public
23
24
  @kwargs = kwargs
24
25
 
25
26
  # Create the trace
@@ -50,6 +51,7 @@ module Langfuse
50
51
  # Create a child generation
51
52
  def generation(name: nil, start_time: nil, end_time: nil, completion_start_time: nil,
52
53
  model: nil, model_parameters: nil, input: nil, output: nil, usage: nil,
54
+ usage_details: nil, cost_details: nil, prompt: nil,
53
55
  metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
54
56
  version: nil, **kwargs)
55
57
  @client.generation(
@@ -63,6 +65,9 @@ module Langfuse
63
65
  input: input,
64
66
  output: output,
65
67
  usage: usage,
68
+ usage_details: usage_details,
69
+ cost_details: cost_details,
70
+ prompt: prompt,
66
71
  metadata: metadata,
67
72
  level: level,
68
73
  status_message: status_message,
@@ -247,7 +252,8 @@ module Langfuse
247
252
  end
248
253
 
249
254
  def update(name: nil, user_id: nil, session_id: nil, version: nil,
250
- release: nil, input: nil, output: nil, metadata: nil, tags: nil, **kwargs)
255
+ release: nil, input: nil, output: nil, metadata: nil, tags: nil,
256
+ public: nil, **kwargs)
251
257
  # 更新实例变量
252
258
  @name = name if name
253
259
  @user_id = user_id if user_id
@@ -258,6 +264,7 @@ module Langfuse
258
264
  @output = output if output
259
265
  @metadata = metadata if metadata
260
266
  @tags = tags if tags
267
+ @public = public unless public.nil?
261
268
  @kwargs.merge!(kwargs) if kwargs.any?
262
269
  # 触发 trace-update 事件
263
270
  update_trace
@@ -279,7 +286,8 @@ module Langfuse
279
286
  output: @output,
280
287
  metadata: @metadata,
281
288
  tags: @tags,
282
- timestamp: @timestamp
289
+ timestamp: @timestamp,
290
+ public: @public
283
291
  }.merge(@kwargs).compact
284
292
  end
285
293
 
@@ -297,7 +305,8 @@ module Langfuse
297
305
  output: @output,
298
306
  metadata: @metadata,
299
307
  tags: @tags,
300
- timestamp: @timestamp
308
+ timestamp: @timestamp,
309
+ public: @public
301
310
  }.merge(@kwargs).compact
302
311
 
303
312
  @client.enqueue_event('trace-create', data)
@@ -315,7 +324,8 @@ module Langfuse
315
324
  output: @output,
316
325
  metadata: @metadata,
317
326
  tags: @tags,
318
- timestamp: @timestamp
327
+ timestamp: @timestamp,
328
+ public: @public
319
329
  }.merge(@kwargs).compact
320
330
 
321
331
  @client.enqueue_event('trace-update', data)
@@ -6,11 +6,25 @@ require 'erb'
6
6
 
7
7
  module Langfuse
8
8
  module Utils
9
+ # Body keys whose values must be passed through verbatim (user data),
10
+ # everything else nested under them keeps its original key format.
11
+ VERBATIM_BODY_KEYS = %w[input output metadata usageDetails costDetails modelParameters].freeze
12
+
9
13
  class << self
10
14
  def generate_id
11
15
  SecureRandom.uuid
12
16
  end
13
17
 
18
+ # W3C-compatible 32-char hex trace ID (16 bytes), used for OTel ingestion
19
+ def generate_hex_trace_id
20
+ SecureRandom.hex(16)
21
+ end
22
+
23
+ # W3C-compatible 16-char hex span/observation ID (8 bytes), used for OTel ingestion
24
+ def generate_hex_span_id
25
+ SecureRandom.hex(8)
26
+ end
27
+
14
28
  def current_timestamp
15
29
  Time.now.utc.iso8601(3)
16
30
  end
@@ -39,6 +53,24 @@ module Langfuse
39
53
  end
40
54
  end
41
55
 
56
+ # Prepare an event body for the ingestion API:
57
+ # - top-level keys are camelized (snake_case -> camelCase)
58
+ # - user data values (input/output/metadata/usageDetails/costDetails/modelParameters)
59
+ # are passed through verbatim so user-provided keys are not mangled
60
+ # - the legacy `usage` object keys are camelized (prompt_tokens -> promptTokens)
61
+ def prepare_event_body(body)
62
+ return body unless body.is_a?(Hash)
63
+
64
+ body.each_with_object({}) do |(key, value), result|
65
+ new_key = camelize_key(key.to_s)
66
+ result[new_key] = if !VERBATIM_BODY_KEYS.include?(new_key) && value.is_a?(Hash)
67
+ deep_stringify_keys(value)
68
+ else
69
+ value
70
+ end
71
+ end
72
+ end
73
+
42
74
  # 将哈希的键名转换为小驼峰格式
43
75
  def deep_camelize_keys(hash)
44
76
  return hash unless hash.is_a?(Hash)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Langfuse
4
- VERSION = '0.1.7'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/langfuse.rb CHANGED
@@ -16,6 +16,8 @@ require_relative 'langfuse/otel_exporter'
16
16
 
17
17
  # Ruby SDK for Langfuse - Open source LLM engineering platform
18
18
  module Langfuse
19
+ CLIENT_MUTEX = Mutex.new
20
+
19
21
  class << self
20
22
  # Configure the Langfuse client with default settings
21
23
  def configure
@@ -31,10 +33,10 @@ module Langfuse
31
33
  Client.new(**kwargs)
32
34
  end
33
35
 
34
- # Get a thread-safe singleton client instance
36
+ # Get a process-wide, thread-safe singleton client instance
35
37
  # @return [Client] Langfuse client
36
38
  def client
37
- Thread.current[:langfuse_client] ||= Client.new
39
+ @client || CLIENT_MUTEX.synchronize { @client ||= Client.new }
38
40
  end
39
41
 
40
42
  # Get a prompt and optionally compile it with variables
@@ -59,7 +61,7 @@ module Langfuse
59
61
  end
60
62
  rescue StandardError => e
61
63
  if attempts <= retries
62
- sleep_time = 2**(attempts - 1) * 0.1 # Exponential backoff: 0.1s, 0.2s, 0.4s...
64
+ sleep_time = (2**(attempts - 1)) * 0.1 # Exponential backoff: 0.1s, 0.2s, 0.4s...
63
65
  warn "Langfuse prompt fetch failed (#{prompt_name}), retrying in #{sleep_time}s... (attempt #{attempts}/#{retries + 1})" if configuration.debug
64
66
  sleep(sleep_time)
65
67
  retry
@@ -100,7 +102,7 @@ module Langfuse
100
102
  # Langfuse.flush
101
103
  #
102
104
  def trace(name = nil, user_id: nil, session_id: nil, input: nil, output: nil,
103
- metadata: nil, tags: nil, version: nil, release: nil, **kwargs, &block)
105
+ metadata: nil, tags: nil, version: nil, release: nil, **kwargs)
104
106
  trace = client.trace(
105
107
  name: name,
106
108
  user_id: user_id,
@@ -152,14 +154,14 @@ module Langfuse
152
154
 
153
155
  # Reset the singleton client (mainly for testing)
154
156
  def reset!
155
- Thread.current[:langfuse_client] = nil
157
+ CLIENT_MUTEX.synchronize { @client = nil }
156
158
  end
157
159
  end
158
160
 
159
161
  # Configuration class for Langfuse client settings
160
162
  class Configuration
161
163
  attr_accessor :public_key, :secret_key, :host, :debug, :timeout, :retries, :flush_interval, :auto_flush,
162
- :ingestion_mode
164
+ :ingestion_mode, :environment, :sample_rate, :mask, :flush_at, :logger, :shutdown_on_exit
163
165
 
164
166
  def initialize
165
167
  @public_key = nil
@@ -171,6 +173,12 @@ module Langfuse
171
173
  @flush_interval = 5
172
174
  @auto_flush = true
173
175
  @ingestion_mode = :legacy # :legacy or :otel
176
+ @environment = nil # default tracing environment for all events
177
+ @sample_rate = nil # 0.0..1.0, nil disables sampling
178
+ @mask = nil # callable applied to input/output/metadata before sending
179
+ @flush_at = 15 # flush as soon as this many events are queued
180
+ @logger = nil # custom Logger instance
181
+ @shutdown_on_exit = true # register an at_exit hook that flushes pending events
174
182
  end
175
183
  end
176
184
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: langfuse-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Sun
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
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.1.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.1.0
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: concurrent-ruby
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +161,20 @@ dependencies:
147
161
  - - "~>"
148
162
  - !ruby/object:Gem::Version
149
163
  version: '1.0'
164
+ - !ruby/object:Gem::Dependency
165
+ name: tsort
166
+ requirement: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: 0.1.0
171
+ type: :development
172
+ prerelease: false
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: 0.1.0
150
178
  - !ruby/object:Gem::Dependency
151
179
  name: vcr
152
180
  requirement: !ruby/object:Gem::Requirement
@@ -200,6 +228,7 @@ files:
200
228
  - ".github/workflows/ci.yml"
201
229
  - ".github/workflows/release.yml"
202
230
  - ".gitignore"
231
+ - ".mise.toml"
203
232
  - ".rubocop.yml"
204
233
  - CHANGELOG.md
205
234
  - CLAUDE.md
@@ -263,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
263
292
  - !ruby/object:Gem::Version
264
293
  version: '0'
265
294
  requirements: []
266
- rubygems_version: 3.6.9
295
+ rubygems_version: 4.0.16
267
296
  specification_version: 4
268
297
  summary: Ruby SDK for Langfuse - Open source LLM engineering platform
269
298
  test_files: []