langfuse-ruby 0.1.1 → 0.1.3

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.
@@ -9,7 +9,7 @@ client = Langfuse.new(
9
9
  host: ENV['LANGFUSE_HOST'] || 'https://cloud.langfuse.com'
10
10
  )
11
11
 
12
- puts "🚀 Starting prompt management example..."
12
+ puts '🚀 Starting prompt management example...'
13
13
 
14
14
  # Example 1: Create and use text prompts
15
15
  puts "\n📝 Example 1: Create and use text prompts"
@@ -17,9 +17,9 @@ puts "\n📝 Example 1: Create and use text prompts"
17
17
  begin
18
18
  # Create a text prompt
19
19
  text_prompt = client.create_prompt(
20
- name: "greeting-prompt",
21
- prompt: "Hello {{user_name}}! Welcome to {{service_name}}. How can I help you with {{topic}} today?",
22
- labels: ["greeting", "customer-service"],
20
+ name: 'greeting-prompt',
21
+ prompt: 'Hello {{user_name}}! Welcome to {{service_name}}. How can I help you with {{topic}} today?',
22
+ labels: %w[greeting customer-service],
23
23
  config: {
24
24
  temperature: 0.7,
25
25
  max_tokens: 100
@@ -27,24 +27,22 @@ begin
27
27
  )
28
28
 
29
29
  puts "Created text prompt: #{text_prompt.name} (Version: #{text_prompt.version})"
30
-
31
30
  rescue Langfuse::APIError => e
32
31
  puts "Note: Prompt might already exist - #{e.message}"
33
32
  end
34
33
 
35
34
  # Get and use the prompt
36
35
  begin
37
- prompt = client.get_prompt("greeting-prompt")
36
+ prompt = client.get_prompt('greeting-prompt')
38
37
 
39
38
  # Compile prompt with variables
40
39
  compiled_text = prompt.compile(
41
- user_name: "Alice",
42
- service_name: "AI Assistant",
43
- topic: "machine learning"
40
+ user_name: 'Alice',
41
+ service_name: 'AI Assistant',
42
+ topic: 'machine learning'
44
43
  )
45
44
 
46
45
  puts "Compiled prompt: #{compiled_text}"
47
-
48
46
  rescue Langfuse::APIError => e
49
47
  puts "Could not retrieve prompt: #{e.message}"
50
48
  end
@@ -55,18 +53,18 @@ puts "\n💬 Example 2: Create and use chat prompts"
55
53
  begin
56
54
  # Create a chat prompt
57
55
  chat_prompt = client.create_prompt(
58
- name: "ai-assistant-chat",
56
+ name: 'ai-assistant-chat',
59
57
  prompt: [
60
58
  {
61
- role: "system",
62
- content: "You are a helpful AI assistant specialized in {{domain}}. Always be {{tone}} and provide {{detail_level}} answers."
59
+ role: 'system',
60
+ content: 'You are a helpful AI assistant specialized in {{domain}}. Always be {{tone}} and provide {{detail_level}} answers.'
63
61
  },
64
62
  {
65
- role: "user",
66
- content: "{{user_message}}"
63
+ role: 'user',
64
+ content: '{{user_message}}'
67
65
  }
68
66
  ],
69
- labels: ["chat", "assistant", "ai"],
67
+ labels: %w[chat assistant ai],
70
68
  config: {
71
69
  temperature: 0.8,
72
70
  max_tokens: 200
@@ -74,28 +72,26 @@ begin
74
72
  )
75
73
 
76
74
  puts "Created chat prompt: #{chat_prompt.name}"
77
-
78
75
  rescue Langfuse::APIError => e
79
76
  puts "Note: Chat prompt might already exist - #{e.message}"
80
77
  end
81
78
 
82
79
  # Get and use the chat prompt
83
80
  begin
84
- chat_prompt = client.get_prompt("ai-assistant-chat")
81
+ chat_prompt = client.get_prompt('ai-assistant-chat')
85
82
 
86
83
  # Compile chat prompt with variables
87
84
  compiled_messages = chat_prompt.compile(
88
- domain: "software development",
89
- tone: "friendly and professional",
90
- detail_level: "detailed",
91
- user_message: "How do I implement a REST API in Ruby?"
85
+ domain: 'software development',
86
+ tone: 'friendly and professional',
87
+ detail_level: 'detailed',
88
+ user_message: 'How do I implement a REST API in Ruby?'
92
89
  )
93
90
 
94
- puts "Compiled chat messages:"
91
+ puts 'Compiled chat messages:'
95
92
  compiled_messages.each_with_index do |message, index|
96
93
  puts " #{index + 1}. #{message[:role]}: #{message[:content]}"
97
94
  end
98
-
99
95
  rescue Langfuse::APIError => e
100
96
  puts "Could not retrieve chat prompt: #{e.message}"
101
97
  end
@@ -112,34 +108,34 @@ puts "Template variables: #{translation_template.input_variables}"
112
108
 
113
109
  # Use the template
114
110
  translated_prompt = translation_template.format(
115
- source_language: "English",
116
- target_language: "Spanish",
117
- text: "Hello, how are you today?"
111
+ source_language: 'English',
112
+ target_language: 'Spanish',
113
+ text: 'Hello, how are you today?'
118
114
  )
119
115
 
120
116
  puts "Translation prompt: #{translated_prompt}"
121
117
 
122
118
  # Create a reusable chat template
123
119
  coding_template = Langfuse::ChatPromptTemplate.from_messages([
124
- {
125
- role: "system",
126
- content: "You are an expert {{language}} developer. Provide clean, well-commented code examples."
127
- },
128
- {
129
- role: "user",
130
- content: "{{request}}"
131
- }
132
- ])
120
+ {
121
+ role: 'system',
122
+ content: 'You are an expert {{language}} developer. Provide clean, well-commented code examples.'
123
+ },
124
+ {
125
+ role: 'user',
126
+ content: '{{request}}'
127
+ }
128
+ ])
133
129
 
134
130
  puts "Chat template variables: #{coding_template.input_variables}"
135
131
 
136
132
  # Use the chat template
137
133
  coding_messages = coding_template.format(
138
- language: "Ruby",
139
- request: "Show me how to create a simple HTTP server"
134
+ language: 'Ruby',
135
+ request: 'Show me how to create a simple HTTP server'
140
136
  )
141
137
 
142
- puts "Coding chat messages:"
138
+ puts 'Coding chat messages:'
143
139
  coding_messages.each_with_index do |message, index|
144
140
  puts " #{index + 1}. #{message[:role]}: #{message[:content]}"
145
141
  end
@@ -149,17 +145,16 @@ puts "\n🔄 Example 4: Prompt versioning and caching"
149
145
 
150
146
  # Get specific version of a prompt
151
147
  begin
152
- versioned_prompt = client.get_prompt("greeting-prompt", version: 1)
148
+ versioned_prompt = client.get_prompt('greeting-prompt', version: 1)
153
149
  puts "Retrieved prompt version: #{versioned_prompt.version}"
154
150
 
155
151
  # Get latest version (cached)
156
- latest_prompt = client.get_prompt("greeting-prompt")
152
+ latest_prompt = client.get_prompt('greeting-prompt')
157
153
  puts "Latest prompt version: #{latest_prompt.version}"
158
154
 
159
155
  # Get with label
160
- labeled_prompt = client.get_prompt("greeting-prompt", label: "production")
156
+ labeled_prompt = client.get_prompt('greeting-prompt', label: 'production')
161
157
  puts "Labeled prompt: #{labeled_prompt.labels}"
162
-
163
158
  rescue Langfuse::APIError => e
164
159
  puts "Could not retrieve versioned prompt: #{e.message}"
165
160
  end
@@ -169,27 +164,27 @@ puts "\n🔗 Example 5: Using prompts in tracing"
169
164
 
170
165
  begin
171
166
  # Get a prompt for use in generation
172
- system_prompt = client.get_prompt("ai-assistant-chat")
167
+ system_prompt = client.get_prompt('ai-assistant-chat')
173
168
 
174
169
  # Create a trace
175
170
  trace = client.trace(
176
- name: "prompt-based-chat",
177
- user_id: "user-789",
178
- input: { message: "Explain Ruby blocks" }
171
+ name: 'prompt-based-chat',
172
+ user_id: 'user-789',
173
+ input: { message: 'Explain Ruby blocks' }
179
174
  )
180
175
 
181
176
  # Compile the prompt
182
177
  messages = system_prompt.compile(
183
- domain: "Ruby programming",
184
- tone: "educational and clear",
185
- detail_level: "beginner-friendly",
186
- user_message: "Explain Ruby blocks"
178
+ domain: 'Ruby programming',
179
+ tone: 'educational and clear',
180
+ detail_level: 'beginner-friendly',
181
+ user_message: 'Explain Ruby blocks'
187
182
  )
188
183
 
189
184
  # Create generation with prompt
190
185
  generation = trace.generation(
191
- name: "openai-chat-with-prompt",
192
- model: "gpt-3.5-turbo",
186
+ name: 'openai-chat-with-prompt',
187
+ model: 'gpt-3.5-turbo',
193
188
  input: messages,
194
189
  output: {
195
190
  content: "Ruby blocks are pieces of code that can be passed to methods. They're defined using either do...end or curly braces {}. Blocks are commonly used with iterators like .each, .map, and .select."
@@ -207,7 +202,6 @@ begin
207
202
 
208
203
  puts "Created generation with prompt: #{generation.id}"
209
204
  puts "Trace URL: #{trace.get_url}"
210
-
211
205
  rescue Langfuse::APIError => e
212
206
  puts "Could not use prompt in tracing: #{e.message}"
213
207
  end
@@ -218,12 +212,12 @@ puts "\n🎯 Example 6: Advanced prompt features"
218
212
  # Create a prompt with complex templating
219
213
  begin
220
214
  complex_prompt = client.create_prompt(
221
- name: "code-review-prompt",
215
+ name: 'code-review-prompt',
222
216
  prompt: {
223
- system: "You are a senior {{language}} developer reviewing code. Focus on {{review_aspects}}.",
217
+ system: 'You are a senior {{language}} developer reviewing code. Focus on {{review_aspects}}.',
224
218
  user: "Please review this {{language}} code:\n\n```{{language}}\n{{code}}\n```\n\nProvide feedback on: {{specific_feedback}}"
225
219
  },
226
- labels: ["code-review", "development"],
220
+ labels: %w[code-review development],
227
221
  config: {
228
222
  temperature: 0.3,
229
223
  max_tokens: 500
@@ -231,7 +225,6 @@ begin
231
225
  )
232
226
 
233
227
  puts "Created complex prompt: #{complex_prompt.name}"
234
-
235
228
  rescue Langfuse::APIError => e
236
229
  puts "Note: Complex prompt might already exist - #{e.message}"
237
230
  end
@@ -239,16 +232,14 @@ end
239
232
  # Create a prompt with conditional logic (using Ruby)
240
233
  class ConditionalPrompt
241
234
  def self.generate(user_level:, topic:, include_examples: true)
242
- base_prompt = "Explain {{topic}} for a {{user_level}} audience."
235
+ base_prompt = 'Explain {{topic}} for a {{user_level}} audience.'
243
236
 
244
- if include_examples
245
- base_prompt += " Include practical examples."
246
- end
237
+ base_prompt += ' Include practical examples.' if include_examples
247
238
 
248
- if user_level == "beginner"
249
- base_prompt += " Use simple language and avoid jargon."
250
- elsif user_level == "advanced"
251
- base_prompt += " Feel free to use technical terminology."
239
+ if user_level == 'beginner'
240
+ base_prompt += ' Use simple language and avoid jargon.'
241
+ elsif user_level == 'advanced'
242
+ base_prompt += ' Feel free to use technical terminology.'
252
243
  end
253
244
 
254
245
  base_prompt
@@ -256,8 +247,8 @@ class ConditionalPrompt
256
247
  end
257
248
 
258
249
  conditional_prompt_text = ConditionalPrompt.generate(
259
- user_level: "beginner",
260
- topic: "machine learning",
250
+ user_level: 'beginner',
251
+ topic: 'machine learning',
261
252
  include_examples: true
262
253
  )
263
254
 
@@ -266,8 +257,8 @@ puts "Conditional prompt: #{conditional_prompt_text}"
266
257
  # Use with template
267
258
  conditional_template = Langfuse::PromptTemplate.from_template(conditional_prompt_text)
268
259
  formatted_prompt = conditional_template.format(
269
- topic: "neural networks",
270
- user_level: "beginner"
260
+ topic: 'neural networks',
261
+ user_level: 'beginner'
271
262
  )
272
263
 
273
264
  puts "Formatted conditional prompt: #{formatted_prompt}"
@@ -277,7 +268,7 @@ puts "\n🔄 Flushing events..."
277
268
  client.flush
278
269
 
279
270
  puts "\n✅ Prompt management example completed!"
280
- puts "Check your Langfuse dashboard to see the prompts and traces."
271
+ puts 'Check your Langfuse dashboard to see the prompts and traces.'
281
272
 
282
273
  # Shutdown client
283
274
  client.shutdown
@@ -6,22 +6,29 @@ require 'concurrent'
6
6
 
7
7
  module Langfuse
8
8
  class Client
9
- attr_reader :public_key, :secret_key, :host, :debug, :timeout, :retries
9
+ attr_reader :public_key, :secret_key, :host, :debug, :timeout, :retries, :flush_interval, :auto_flush
10
10
 
11
- def initialize(public_key: nil, secret_key: nil, host: nil, debug: false, timeout: 30, retries: 3)
11
+ def initialize(public_key: nil, secret_key: nil, host: nil, debug: false, timeout: 30, retries: 3,
12
+ flush_interval: nil, auto_flush: nil)
12
13
  @public_key = public_key || ENV['LANGFUSE_PUBLIC_KEY'] || Langfuse.configuration.public_key
13
14
  @secret_key = secret_key || ENV['LANGFUSE_SECRET_KEY'] || Langfuse.configuration.secret_key
14
15
  @host = host || ENV['LANGFUSE_HOST'] || Langfuse.configuration.host
15
16
  @debug = debug || Langfuse.configuration.debug
16
17
  @timeout = timeout || Langfuse.configuration.timeout
17
18
  @retries = retries || Langfuse.configuration.retries
19
+ @flush_interval = flush_interval || ENV['LANGFUSE_FLUSH_INTERVAL']&.to_i || Langfuse.configuration.flush_interval
20
+ @auto_flush = if auto_flush.nil?
21
+ ENV['LANGFUSE_AUTO_FLUSH'] == 'false' ? false : Langfuse.configuration.auto_flush
22
+ else
23
+ auto_flush
24
+ end
18
25
 
19
26
  raise AuthenticationError, 'Public key is required' unless @public_key
20
27
  raise AuthenticationError, 'Secret key is required' unless @secret_key
21
28
 
22
29
  @connection = build_connection
23
30
  @event_queue = Concurrent::Array.new
24
- @flush_thread = start_flush_thread
31
+ @flush_thread = start_flush_thread if @auto_flush
25
32
  end
26
33
 
27
34
  # Trace operations
@@ -91,6 +98,25 @@ module Langfuse
91
98
  )
92
99
  end
93
100
 
101
+ # Event operations
102
+ def event(trace_id:, name:, start_time: nil, input: nil, output: nil, metadata: nil,
103
+ level: nil, status_message: nil, parent_observation_id: nil, version: nil, **kwargs)
104
+ Event.new(
105
+ client: self,
106
+ trace_id: trace_id,
107
+ name: name,
108
+ start_time: start_time,
109
+ input: input,
110
+ output: output,
111
+ metadata: metadata,
112
+ level: level,
113
+ status_message: status_message,
114
+ parent_observation_id: parent_observation_id,
115
+ version: version,
116
+ **kwargs
117
+ )
118
+ end
119
+
94
120
  # Prompt operations
95
121
  def get_prompt(name, version: nil, label: nil, cache_ttl_seconds: 60)
96
122
  cache_key = "prompt:#{name}:#{version}:#{label}"
@@ -99,7 +125,7 @@ module Langfuse
99
125
  return cached_prompt[:prompt]
100
126
  end
101
127
 
102
- path = "/api/public/prompts/#{name}"
128
+ path = "/api/public/v2/prompts/#{name}"
103
129
  params = {}
104
130
  params[:version] = version if version
105
131
  params[:label] = label if label
@@ -138,7 +164,7 @@ module Langfuse
138
164
  **kwargs
139
165
  }
140
166
 
141
- response = post('/api/public/prompts', data)
167
+ response = post('/api/public/v2/prompts', data)
142
168
  Prompt.new(response.body)
143
169
  end
144
170
 
@@ -158,29 +184,22 @@ module Langfuse
158
184
  enqueue_event('score-create', data)
159
185
  end
160
186
 
161
- # HTTP methods
162
- def get(path, params = {})
163
- request(:get, path, params: params)
164
- end
165
-
166
- def post(path, data = {})
167
- request(:post, path, json: data)
168
- end
169
-
170
- def put(path, data = {})
171
- request(:put, path, json: data)
172
- end
173
-
174
- def delete(path, params = {})
175
- request(:delete, path, params: params)
176
- end
177
-
178
- def patch(path, data = {})
179
- request(:patch, path, json: data)
180
- end
181
-
182
187
  # Event queue management
183
188
  def enqueue_event(type, body)
189
+ # 验证事件类型是否有效
190
+ valid_types = %w[
191
+ trace-create
192
+ generation-create generation-update
193
+ span-create span-update
194
+ event-create
195
+ score-create
196
+ ]
197
+
198
+ unless valid_types.include?(type)
199
+ puts "Warning: Invalid event type '#{type}'. Skipping event." if @debug
200
+ return
201
+ end
202
+
184
203
  event = {
185
204
  id: Utils.generate_id,
186
205
  type: type,
@@ -198,47 +217,144 @@ module Langfuse
198
217
  events = @event_queue.shift(@event_queue.length)
199
218
  return if events.empty?
200
219
 
201
- batch_data = {
202
- batch: events,
203
- metadata: {
204
- batch_size: events.length,
205
- sdk_name: 'langfuse-ruby',
206
- sdk_version: Langfuse::VERSION
207
- }
208
- }
220
+ send_batch(events)
221
+ end
222
+
223
+ def shutdown
224
+ @flush_thread&.kill if @auto_flush
225
+ flush unless @event_queue.empty?
226
+ end
227
+
228
+ private
229
+
230
+ def debug_event_data(events)
231
+ return unless @debug
232
+
233
+ puts "\n=== Event Data Debug Information ==="
234
+ events.each_with_index do |event, index|
235
+ puts "Event #{index + 1}:"
236
+ puts " ID: #{event[:id]}"
237
+ puts " Type: #{event[:type]}"
238
+ puts " Timestamp: #{event[:timestamp]}"
239
+ puts " Body keys: #{event[:body]&.keys || 'nil'}"
240
+
241
+ # 检查常见的问题
242
+ puts ' ⚠️ WARNING: Empty or nil type!' if event[:type].nil? || event[:type].to_s.empty?
243
+
244
+ puts ' ⚠️ WARNING: Empty body!' if event[:body].nil?
245
+
246
+ puts ' ---'
247
+ end
248
+ puts "=== End Debug Information ===\n"
249
+ end
250
+
251
+ def send_batch(events)
252
+ # 调试事件数据
253
+ debug_event_data(events)
254
+
255
+ # 验证事件数据
256
+ valid_events = events.select do |event|
257
+ if event[:type].nil? || event[:type].to_s.empty?
258
+ puts "Warning: Event with empty type detected, skipping: #{event[:id]}" if @debug
259
+ false
260
+ elsif event[:body].nil?
261
+ puts "Warning: Event with empty body detected, skipping: #{event[:id]}" if @debug
262
+ false
263
+ else
264
+ true
265
+ end
266
+ end
267
+
268
+ if valid_events.empty?
269
+ puts 'No valid events to send' if @debug
270
+ return
271
+ end
272
+
273
+ batch_data = build_batch_data(valid_events)
274
+ puts "Sending batch data: #{batch_data}" if @debug
209
275
 
210
276
  begin
211
277
  response = post('/api/public/ingestion', batch_data)
212
- puts "Flushed #{events.length} events" if @debug
278
+ puts "Flushed #{valid_events.length} events" if @debug
279
+ response
213
280
  rescue StandardError => e
214
281
  puts "Failed to flush events: #{e.message}" if @debug
215
282
  # Re-queue events on failure
216
- events.each { |event| @event_queue << event }
283
+ valid_events.each { |event| @event_queue << event }
217
284
  raise
218
285
  end
219
286
  end
220
287
 
221
- def shutdown
222
- @flush_thread&.kill
223
- flush unless @event_queue.empty?
288
+ def build_batch_data(events)
289
+ {
290
+ batch: events,
291
+ metadata: Utils.deep_camelize_keys({
292
+ batch_size: events.length,
293
+ sdk_name: 'langfuse-ruby',
294
+ sdk_version: Langfuse::VERSION
295
+ })
296
+ }
224
297
  end
225
298
 
226
- private
299
+ def start_flush_thread
300
+ return unless @auto_flush
301
+
302
+ Thread.new do
303
+ loop do
304
+ sleep(@flush_interval) # Configurable flush interval
305
+ begin
306
+ flush unless @event_queue.empty?
307
+ rescue StandardError => e
308
+ puts "Error in flush thread: #{e.message}" if @debug
309
+ end
310
+ end
311
+ end
312
+ end
227
313
 
228
314
  def build_connection
229
315
  Faraday.new(url: @host) do |conn|
230
- conn.request :authorization, :basic, @public_key, @secret_key
316
+ # 配置请求和响应处理
231
317
  conn.request :json
232
- # 更宽松的 JSON 响应处理,支持更多 content-type
233
318
  conn.response :json, content_type: /\bjson$/
234
- conn.adapter Faraday.default_adapter
319
+
320
+ # 设置 User-Agent 头部
321
+ conn.headers['User-Agent'] = "langfuse-ruby/#{Langfuse::VERSION}"
322
+ # 根据 Langfuse 文档配置 Basic Auth
323
+ # username: Langfuse Public Key, password: Langfuse Secret Key
324
+ conn.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{@public_key}:#{@secret_key}")}"
325
+
326
+ # 设置超时
235
327
  conn.options.timeout = @timeout
236
328
 
237
- # 添加响应中间件来处理错误响应
329
+ # 添加调试日志
238
330
  conn.response :logger if @debug
331
+
332
+ # 使用默认适配器
333
+ conn.adapter Faraday.default_adapter
239
334
  end
240
335
  end
241
336
 
337
+ # HTTP methods
338
+ def get(path, params = {})
339
+ request(:get, path, params: params)
340
+ end
341
+
342
+ def post(path, data = {})
343
+ request(:post, path, json: data)
344
+ end
345
+
346
+ def put(path, data = {})
347
+ request(:put, path, json: data)
348
+ end
349
+
350
+ def delete(path, params = {})
351
+ request(:delete, path, params: params)
352
+ end
353
+
354
+ def patch(path, data = {})
355
+ request(:patch, path, json: data)
356
+ end
357
+
242
358
  def request(method, path, params: {}, json: nil)
243
359
  retries_left = @retries
244
360
 
@@ -284,25 +400,27 @@ module Langfuse
284
400
  when 429
285
401
  raise RateLimitError, "Rate limit exceeded: #{response.body}"
286
402
  when 400..499
287
- raise ValidationError, "Client error (#{response.status}): #{response.body}"
403
+ # 400 错误提供更详细的错误信息
404
+ error_details = ''
405
+ if response.body.is_a?(Hash) && response.body['error']
406
+ error_details = "\nError details: #{response.body['error']}"
407
+ elsif response.body.is_a?(String)
408
+ error_details = "\nError details: #{response.body}"
409
+ end
410
+
411
+ # 特别处理类型验证错误
412
+ unless response.body.to_s.include?('invalid_union') || response.body.to_s.include?('discriminator')
413
+ raise ValidationError, "Client error (#{response.status}): #{response.body}#{error_details}"
414
+ end
415
+
416
+ raise ValidationError,
417
+ "Event type validation failed (#{response.status}): The event type or structure is invalid. Please check the event format.#{error_details}"
418
+
288
419
  when 500..599
289
420
  raise APIError, "Server error (#{response.status}): #{response.body}"
290
421
  else
291
422
  raise APIError, "Unexpected response (#{response.status}): #{response.body}"
292
423
  end
293
424
  end
294
-
295
- def start_flush_thread
296
- Thread.new do
297
- loop do
298
- sleep(5) # Flush every 5 seconds
299
- begin
300
- flush unless @event_queue.empty?
301
- rescue StandardError => e
302
- puts "Error in flush thread: #{e.message}" if @debug
303
- end
304
- end
305
- end
306
- end
307
425
  end
308
426
  end
@@ -0,0 +1,63 @@
1
+ module Langfuse
2
+ class Event
3
+ attr_reader :id, :trace_id, :name, :start_time, :input, :output, :metadata,
4
+ :level, :status_message, :parent_observation_id, :version, :client
5
+
6
+ def initialize(client:, trace_id:, name:, id: nil, start_time: nil, input: nil,
7
+ output: nil, metadata: nil, level: nil, status_message: nil,
8
+ parent_observation_id: nil, version: nil, **kwargs)
9
+ @client = client
10
+ @id = id || Utils.generate_id
11
+ @trace_id = trace_id
12
+ @name = name
13
+ @start_time = start_time || Utils.current_timestamp
14
+ @input = input
15
+ @output = output
16
+ @metadata = metadata || {}
17
+ @level = level
18
+ @status_message = status_message
19
+ @parent_observation_id = parent_observation_id
20
+ @version = version
21
+ @kwargs = kwargs
22
+
23
+ # Create the event
24
+ create_event
25
+ end
26
+
27
+ def to_dict
28
+ {
29
+ id: @id,
30
+ trace_id: @trace_id,
31
+ name: @name,
32
+ start_time: @start_time,
33
+ input: @input,
34
+ output: @output,
35
+ metadata: @metadata,
36
+ level: @level,
37
+ status_message: @status_message,
38
+ parent_observation_id: @parent_observation_id,
39
+ version: @version
40
+ }.merge(@kwargs).compact
41
+ end
42
+
43
+ private
44
+
45
+ def create_event
46
+ data = {
47
+ id: @id,
48
+ trace_id: @trace_id,
49
+ name: @name,
50
+ start_time: @start_time,
51
+ input: @input,
52
+ output: @output,
53
+ metadata: @metadata,
54
+ level: @level,
55
+ status_message: @status_message,
56
+ parent_observation_id: @parent_observation_id,
57
+ version: @version
58
+ }.merge(@kwargs).compact
59
+
60
+ @client.enqueue_event('event-create', data)
61
+ end
62
+ end
63
+ end