langfuse-ruby 0.1.0 → 0.1.2
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 +4 -4
- data/CHANGELOG.md +29 -34
- data/Gemfile +5 -5
- data/Gemfile.lock +1 -1
- data/README.md +3 -3
- data/{PUBLISH_GUIDE.md → docs/PUBLISH_GUIDE.md} +7 -7
- data/docs/README.md +29 -0
- data/examples/connection_config_demo.rb +127 -0
- data/examples/prompt_management.rb +62 -71
- data/langfuse-ruby.gemspec +12 -7
- data/lib/langfuse/client.rb +57 -18
- data/lib/langfuse/evaluation.rb +9 -8
- data/lib/langfuse/prompt.rb +2 -2
- data/lib/langfuse/version.rb +1 -1
- data/lib/langfuse.rb +14 -10
- data/scripts/release.sh +2 -2
- data/scripts/verify_release.rb +2 -2
- metadata +14 -12
- data/PROJECT_SUMMARY.md +0 -263
- data/test_basic.rb +0 -183
- /data/{FINAL_SUMMARY.md → docs/FINAL_SUMMARY.md} +0 -0
- /data/{RELEASE_CHECKLIST.md → docs/RELEASE_CHECKLIST.md} +0 -0
data/langfuse-ruby.gemspec
CHANGED
@@ -1,20 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/langfuse/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |spec|
|
4
6
|
spec.name = 'langfuse-ruby'
|
5
7
|
spec.version = Langfuse::VERSION
|
6
|
-
spec.authors = ['
|
7
|
-
spec.email = ['
|
8
|
+
spec.authors = ['Richard Sun']
|
9
|
+
spec.email = ['richard.sun@ai-firstly.com']
|
8
10
|
spec.summary = 'Ruby SDK for Langfuse - Open source LLM engineering platform'
|
9
|
-
spec.description = 'Ruby client library for Langfuse, providing tracing, prompt management,
|
10
|
-
|
11
|
+
spec.description = 'Ruby client library for Langfuse, providing tracing, prompt management, ' \
|
12
|
+
'and evaluation capabilities for LLM applications'
|
13
|
+
spec.homepage = 'https://langfuse.com'
|
11
14
|
spec.license = 'MIT'
|
12
15
|
spec.required_ruby_version = '>= 2.7.0'
|
13
16
|
|
14
17
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
15
|
-
spec.metadata['homepage_uri'] =
|
16
|
-
spec.metadata['source_code_uri'] = 'https://github.com/
|
17
|
-
spec.metadata['changelog_uri'] = 'https://github.com/
|
18
|
+
spec.metadata['homepage_uri'] = 'https://langfuse.com/docs/sdk/ruby'
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/ai-firstly/langfuse-ruby'
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/ai-firstly/langfuse-ruby/blob/main/CHANGELOG.md'
|
21
|
+
spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/langfuse-ruby'
|
22
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/ai-firstly/langfuse-ruby/issues'
|
18
23
|
|
19
24
|
# Specify which files should be added to the gem when it is released.
|
20
25
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
data/lib/langfuse/client.rb
CHANGED
@@ -16,8 +16,8 @@ module Langfuse
|
|
16
16
|
@timeout = timeout || Langfuse.configuration.timeout
|
17
17
|
@retries = retries || Langfuse.configuration.retries
|
18
18
|
|
19
|
-
raise AuthenticationError,
|
20
|
-
raise AuthenticationError,
|
19
|
+
raise AuthenticationError, 'Public key is required' unless @public_key
|
20
|
+
raise AuthenticationError, 'Secret key is required' unless @secret_key
|
21
21
|
|
22
22
|
@connection = build_connection
|
23
23
|
@event_queue = Concurrent::Array.new
|
@@ -95,16 +95,31 @@ module Langfuse
|
|
95
95
|
def get_prompt(name, version: nil, label: nil, cache_ttl_seconds: 60)
|
96
96
|
cache_key = "prompt:#{name}:#{version}:#{label}"
|
97
97
|
|
98
|
-
if cached_prompt = @prompt_cache&.dig(cache_key)
|
99
|
-
return cached_prompt[:prompt]
|
98
|
+
if (cached_prompt = @prompt_cache&.dig(cache_key)) && (Time.now - cached_prompt[:cached_at] < cache_ttl_seconds)
|
99
|
+
return cached_prompt[:prompt]
|
100
100
|
end
|
101
101
|
|
102
|
-
path = "/api/public/prompts/#{name}"
|
102
|
+
path = "/api/public/v2/prompts/#{name}"
|
103
103
|
params = {}
|
104
104
|
params[:version] = version if version
|
105
105
|
params[:label] = label if label
|
106
106
|
|
107
|
+
puts "Making request to: #{@host}#{path} with params: #{params}" if @debug
|
108
|
+
|
107
109
|
response = get(path, params)
|
110
|
+
|
111
|
+
puts "Response status: #{response.status}" if @debug
|
112
|
+
puts "Response headers: #{response.headers}" if @debug
|
113
|
+
puts "Response body type: #{response.body.class}" if @debug
|
114
|
+
|
115
|
+
# Check if response body is a string (HTML) instead of parsed JSON
|
116
|
+
if response.body.is_a?(String) && response.body.include?('<!DOCTYPE html>')
|
117
|
+
puts 'Received HTML response instead of JSON:' if @debug
|
118
|
+
puts response.body[0..200] if @debug
|
119
|
+
raise APIError,
|
120
|
+
'Received HTML response instead of JSON. This usually indicates a 404 error or incorrect API endpoint.'
|
121
|
+
end
|
122
|
+
|
108
123
|
prompt = Prompt.new(response.body)
|
109
124
|
|
110
125
|
# Cache the prompt
|
@@ -123,12 +138,12 @@ module Langfuse
|
|
123
138
|
**kwargs
|
124
139
|
}
|
125
140
|
|
126
|
-
response = post(
|
141
|
+
response = post('/api/public/v2/prompts', data)
|
127
142
|
Prompt.new(response.body)
|
128
143
|
end
|
129
144
|
|
130
145
|
# Score/Evaluation operations
|
131
|
-
def score(trace_id: nil, observation_id: nil,
|
146
|
+
def score(name:, value:, trace_id: nil, observation_id: nil, data_type: nil, comment: nil, **kwargs)
|
132
147
|
data = {
|
133
148
|
name: name,
|
134
149
|
value: value,
|
@@ -187,15 +202,15 @@ module Langfuse
|
|
187
202
|
batch: events,
|
188
203
|
metadata: {
|
189
204
|
batch_size: events.length,
|
190
|
-
sdk_name:
|
205
|
+
sdk_name: 'langfuse-ruby',
|
191
206
|
sdk_version: Langfuse::VERSION
|
192
207
|
}
|
193
208
|
}
|
194
209
|
|
195
210
|
begin
|
196
|
-
response = post(
|
211
|
+
response = post('/api/public/ingestion', batch_data)
|
197
212
|
puts "Flushed #{events.length} events" if @debug
|
198
|
-
rescue => e
|
213
|
+
rescue StandardError => e
|
199
214
|
puts "Failed to flush events: #{e.message}" if @debug
|
200
215
|
# Re-queue events on failure
|
201
216
|
events.each { |event| @event_queue << event }
|
@@ -212,11 +227,24 @@ module Langfuse
|
|
212
227
|
|
213
228
|
def build_connection
|
214
229
|
Faraday.new(url: @host) do |conn|
|
215
|
-
|
230
|
+
# 配置请求和响应处理
|
216
231
|
conn.request :json
|
217
232
|
conn.response :json, content_type: /\bjson$/
|
218
|
-
|
233
|
+
|
234
|
+
# 设置 User-Agent 头部
|
235
|
+
conn.headers['User-Agent'] = "langfuse-ruby/#{Langfuse::VERSION}"
|
236
|
+
# 根据 Langfuse 文档配置 Basic Auth
|
237
|
+
# username: Langfuse Public Key, password: Langfuse Secret Key
|
238
|
+
conn.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{@public_key}:#{@secret_key}")}"
|
239
|
+
|
240
|
+
# 设置超时
|
219
241
|
conn.options.timeout = @timeout
|
242
|
+
|
243
|
+
# 添加调试日志
|
244
|
+
conn.response :logger if @debug
|
245
|
+
|
246
|
+
# 使用默认适配器
|
247
|
+
conn.adapter Faraday.default_adapter
|
220
248
|
end
|
221
249
|
end
|
222
250
|
|
@@ -236,29 +264,40 @@ module Langfuse
|
|
236
264
|
rescue Faraday::ConnectionFailed => e
|
237
265
|
if retries_left > 0
|
238
266
|
retries_left -= 1
|
239
|
-
sleep(2
|
267
|
+
sleep(2**(@retries - retries_left))
|
240
268
|
retry
|
241
269
|
end
|
242
270
|
raise NetworkError, "Connection failed: #{e.message}"
|
243
|
-
rescue => e
|
271
|
+
rescue StandardError => e
|
244
272
|
raise APIError, "Request failed: #{e.message}"
|
245
273
|
end
|
246
274
|
end
|
247
275
|
|
248
276
|
def handle_response(response)
|
277
|
+
puts "Handling response with status: #{response.status}" if @debug
|
278
|
+
|
249
279
|
case response.status
|
250
280
|
when 200..299
|
251
281
|
response
|
252
282
|
when 401
|
253
283
|
raise AuthenticationError, "Authentication failed: #{response.body}"
|
284
|
+
when 404
|
285
|
+
# 404 错误通常返回 HTML 页面
|
286
|
+
error_message = 'Resource not found (404)'
|
287
|
+
if response.body.is_a?(String) && response.body.include?('<!DOCTYPE html>')
|
288
|
+
error_message += '. Server returned HTML page instead of JSON API response. This usually means the requested resource does not exist.'
|
289
|
+
else
|
290
|
+
error_message += ": #{response.body}"
|
291
|
+
end
|
292
|
+
raise ValidationError, error_message
|
254
293
|
when 429
|
255
294
|
raise RateLimitError, "Rate limit exceeded: #{response.body}"
|
256
295
|
when 400..499
|
257
|
-
raise ValidationError, "Client error: #{response.body}"
|
296
|
+
raise ValidationError, "Client error (#{response.status}): #{response.body}"
|
258
297
|
when 500..599
|
259
|
-
raise APIError, "Server error: #{response.body}"
|
298
|
+
raise APIError, "Server error (#{response.status}): #{response.body}"
|
260
299
|
else
|
261
|
-
raise APIError, "Unexpected response
|
300
|
+
raise APIError, "Unexpected response (#{response.status}): #{response.body}"
|
262
301
|
end
|
263
302
|
end
|
264
303
|
|
@@ -268,7 +307,7 @@ module Langfuse
|
|
268
307
|
sleep(5) # Flush every 5 seconds
|
269
308
|
begin
|
270
309
|
flush unless @event_queue.empty?
|
271
|
-
rescue => e
|
310
|
+
rescue StandardError => e
|
272
311
|
puts "Error in flush thread: #{e.message}" if @debug
|
273
312
|
end
|
274
313
|
end
|
data/lib/langfuse/evaluation.rb
CHANGED
@@ -67,7 +67,7 @@ module Langfuse
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def evaluate(input, output, expected: nil, context: nil)
|
70
|
-
raise NotImplementedError,
|
70
|
+
raise NotImplementedError, 'Subclasses must implement evaluate method'
|
71
71
|
end
|
72
72
|
|
73
73
|
protected
|
@@ -129,7 +129,7 @@ module Langfuse
|
|
129
129
|
length = output.to_s.length
|
130
130
|
|
131
131
|
if @min_length && @max_length
|
132
|
-
score =
|
132
|
+
score = length >= @min_length && length <= @max_length ? 1 : 0
|
133
133
|
comment = score == 1 ? "Length #{length} within range" : "Length #{length} outside range #{@min_length}-#{@max_length}"
|
134
134
|
elsif @min_length
|
135
135
|
score = length >= @min_length ? 1 : 0
|
@@ -151,7 +151,7 @@ module Langfuse
|
|
151
151
|
end
|
152
152
|
|
153
153
|
class RegexEvaluator < BaseEvaluator
|
154
|
-
def initialize(name: 'regex', description: 'Regex evaluator'
|
154
|
+
def initialize(pattern:, name: 'regex', description: 'Regex evaluator')
|
155
155
|
super(name: name, description: description)
|
156
156
|
@pattern = pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern)
|
157
157
|
end
|
@@ -205,11 +205,11 @@ module Langfuse
|
|
205
205
|
|
206
206
|
(1..str1.length).each do |i|
|
207
207
|
(1..str2.length).each do |j|
|
208
|
-
cost = str1[i-1] == str2[j-1] ? 0 : 1
|
208
|
+
cost = str1[i - 1] == str2[j - 1] ? 0 : 1
|
209
209
|
matrix[i][j] = [
|
210
|
-
matrix[i-1][j] + 1, # deletion
|
211
|
-
matrix[i][j-1] + 1, # insertion
|
212
|
-
matrix[i-1][j-1] + cost # substitution
|
210
|
+
matrix[i - 1][j] + 1, # deletion
|
211
|
+
matrix[i][j - 1] + 1, # insertion
|
212
|
+
matrix[i - 1][j - 1] + cost # substitution
|
213
213
|
].min
|
214
214
|
end
|
215
215
|
end
|
@@ -219,7 +219,8 @@ module Langfuse
|
|
219
219
|
end
|
220
220
|
|
221
221
|
class LLMEvaluator < BaseEvaluator
|
222
|
-
def initialize(name: 'llm_evaluator', description: 'LLM-based evaluator',
|
222
|
+
def initialize(client:, name: 'llm_evaluator', description: 'LLM-based evaluator', model: 'gpt-3.5-turbo',
|
223
|
+
prompt_template: nil)
|
223
224
|
super(name: name, description: description)
|
224
225
|
@client = client
|
225
226
|
@model = model
|
data/lib/langfuse/prompt.rb
CHANGED
@@ -61,7 +61,7 @@ module Langfuse
|
|
61
61
|
def text_to_langchain_prompt
|
62
62
|
# Convert text prompt to LangChain PromptTemplate format
|
63
63
|
{
|
64
|
-
_type:
|
64
|
+
_type: 'prompt',
|
65
65
|
input_variables: extract_variables(@prompt),
|
66
66
|
template: @prompt
|
67
67
|
}
|
@@ -78,7 +78,7 @@ module Langfuse
|
|
78
78
|
end
|
79
79
|
|
80
80
|
{
|
81
|
-
_type:
|
81
|
+
_type: 'chat',
|
82
82
|
messages: messages,
|
83
83
|
input_variables: messages.flat_map { |m| m[:input_variables] }.uniq
|
84
84
|
}
|
data/lib/langfuse/version.rb
CHANGED
data/lib/langfuse.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
-
|
2
|
-
require_relative "langfuse/client"
|
3
|
-
require_relative "langfuse/trace"
|
4
|
-
require_relative "langfuse/span"
|
5
|
-
require_relative "langfuse/generation"
|
6
|
-
require_relative "langfuse/prompt"
|
7
|
-
require_relative "langfuse/evaluation"
|
8
|
-
require_relative "langfuse/errors"
|
9
|
-
require_relative "langfuse/utils"
|
1
|
+
# frozen_string_literal: true
|
10
2
|
|
3
|
+
require_relative 'langfuse/version'
|
4
|
+
require_relative 'langfuse/client'
|
5
|
+
require_relative 'langfuse/trace'
|
6
|
+
require_relative 'langfuse/span'
|
7
|
+
require_relative 'langfuse/generation'
|
8
|
+
require_relative 'langfuse/prompt'
|
9
|
+
require_relative 'langfuse/evaluation'
|
10
|
+
require_relative 'langfuse/errors'
|
11
|
+
require_relative 'langfuse/utils'
|
12
|
+
|
13
|
+
# Ruby SDK for Langfuse - Open source LLM engineering platform
|
11
14
|
module Langfuse
|
12
15
|
class << self
|
13
16
|
# Configure the Langfuse client with default settings
|
@@ -25,13 +28,14 @@ module Langfuse
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
# Configuration class for Langfuse client settings
|
28
32
|
class Configuration
|
29
33
|
attr_accessor :public_key, :secret_key, :host, :debug, :timeout, :retries
|
30
34
|
|
31
35
|
def initialize
|
32
36
|
@public_key = nil
|
33
37
|
@secret_key = nil
|
34
|
-
@host =
|
38
|
+
@host = 'https://us.cloud.langfuse.com'
|
35
39
|
@debug = false
|
36
40
|
@timeout = 30
|
37
41
|
@retries = 3
|
data/scripts/release.sh
CHANGED
@@ -116,5 +116,5 @@ echo " 3. Announce the release"
|
|
116
116
|
echo
|
117
117
|
echo "🔗 Useful links:"
|
118
118
|
echo " - RubyGems: https://rubygems.org/gems/langfuse"
|
119
|
-
echo " - GitHub: https://github.com/
|
120
|
-
echo " - Documentation: https://github.com/
|
119
|
+
echo " - GitHub: https://github.com/ai-firstly/langfuse-ruby"
|
120
|
+
echo " - Documentation: https://github.com/ai-firstly/langfuse-ruby#readme"
|
data/scripts/verify_release.rb
CHANGED
@@ -135,5 +135,5 @@ end
|
|
135
135
|
|
136
136
|
puts "\n🔗 Useful links:"
|
137
137
|
puts ' - RubyGems page: https://rubygems.org/gems/langfuse'
|
138
|
-
puts ' - Documentation: https://github.com/
|
139
|
-
puts ' - Issues: https://github.com/
|
138
|
+
puts ' - Documentation: https://github.com/ai-firstly/langfuse-ruby'
|
139
|
+
puts ' - Issues: https://github.com/ai-firstly/langfuse-ruby/issues'
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langfuse-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Richard Sun
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
@@ -167,7 +167,7 @@ dependencies:
|
|
167
167
|
description: Ruby client library for Langfuse, providing tracing, prompt management,
|
168
168
|
and evaluation capabilities for LLM applications
|
169
169
|
email:
|
170
|
-
-
|
170
|
+
- richard.sun@ai-firstly.com
|
171
171
|
executables: []
|
172
172
|
extensions: []
|
173
173
|
extra_rdoc_files: []
|
@@ -176,16 +176,17 @@ files:
|
|
176
176
|
- ".github/workflows/release.yml"
|
177
177
|
- ".gitignore"
|
178
178
|
- CHANGELOG.md
|
179
|
-
- FINAL_SUMMARY.md
|
180
179
|
- Gemfile
|
181
180
|
- Gemfile.lock
|
182
181
|
- LICENSE
|
183
|
-
- PROJECT_SUMMARY.md
|
184
|
-
- PUBLISH_GUIDE.md
|
185
182
|
- README.md
|
186
|
-
- RELEASE_CHECKLIST.md
|
187
183
|
- Rakefile
|
184
|
+
- docs/FINAL_SUMMARY.md
|
185
|
+
- docs/PUBLISH_GUIDE.md
|
186
|
+
- docs/README.md
|
187
|
+
- docs/RELEASE_CHECKLIST.md
|
188
188
|
- examples/basic_tracing.rb
|
189
|
+
- examples/connection_config_demo.rb
|
189
190
|
- examples/prompt_management.rb
|
190
191
|
- langfuse-ruby.gemspec
|
191
192
|
- lib/langfuse.rb
|
@@ -200,16 +201,17 @@ files:
|
|
200
201
|
- lib/langfuse/version.rb
|
201
202
|
- scripts/release.sh
|
202
203
|
- scripts/verify_release.rb
|
203
|
-
- test_basic.rb
|
204
204
|
- test_offline.rb
|
205
|
-
homepage: https://
|
205
|
+
homepage: https://langfuse.com
|
206
206
|
licenses:
|
207
207
|
- MIT
|
208
208
|
metadata:
|
209
209
|
allowed_push_host: https://rubygems.org
|
210
|
-
homepage_uri: https://
|
211
|
-
source_code_uri: https://github.com/
|
212
|
-
changelog_uri: https://github.com/
|
210
|
+
homepage_uri: https://langfuse.com/docs/sdk/ruby
|
211
|
+
source_code_uri: https://github.com/ai-firstly/langfuse-ruby
|
212
|
+
changelog_uri: https://github.com/ai-firstly/langfuse-ruby/blob/main/CHANGELOG.md
|
213
|
+
documentation_uri: https://rubydoc.info/gems/langfuse-ruby
|
214
|
+
bug_tracker_uri: https://github.com/ai-firstly/langfuse-ruby/issues
|
213
215
|
post_install_message:
|
214
216
|
rdoc_options: []
|
215
217
|
require_paths:
|
data/PROJECT_SUMMARY.md
DELETED
@@ -1,263 +0,0 @@
|
|
1
|
-
# Langfuse Ruby SDK - 项目总结
|
2
|
-
|
3
|
-
## 🎉 项目完成状态
|
4
|
-
|
5
|
-
✅ **项目已完成** - 所有核心功能已实现并通过测试
|
6
|
-
|
7
|
-
## 📋 功能清单
|
8
|
-
|
9
|
-
### ✅ 已实现的功能
|
10
|
-
|
11
|
-
1. **核心架构**
|
12
|
-
- [x] 模块化设计
|
13
|
-
- [x] 完整的错误处理系统
|
14
|
-
- [x] 配置管理
|
15
|
-
- [x] 工具类和辅助方法
|
16
|
-
|
17
|
-
2. **客户端管理**
|
18
|
-
- [x] HTTP 客户端(基于 Faraday)
|
19
|
-
- [x] 认证系统(Basic Auth)
|
20
|
-
- [x] 自动重试机制
|
21
|
-
- [x] 超时处理
|
22
|
-
- [x] 错误分类和处理
|
23
|
-
|
24
|
-
3. **追踪功能**
|
25
|
-
- [x] Trace 创建和管理
|
26
|
-
- [x] Span 创建和嵌套
|
27
|
-
- [x] Generation 追踪(LLM 调用)
|
28
|
-
- [x] 事件队列和批处理
|
29
|
-
- [x] 后台线程自动刷新
|
30
|
-
|
31
|
-
4. **提示管理**
|
32
|
-
- [x] 提示创建和获取
|
33
|
-
- [x] 版本控制
|
34
|
-
- [x] 缓存机制
|
35
|
-
- [x] 变量编译({{variable}} 格式)
|
36
|
-
- [x] 文本和聊天提示支持
|
37
|
-
- [x] 提示模板类
|
38
|
-
|
39
|
-
5. **评估系统**
|
40
|
-
- [x] 基础评估器框架
|
41
|
-
- [x] 精确匹配评估器
|
42
|
-
- [x] 相似度评估器(Levenshtein 距离)
|
43
|
-
- [x] 长度评估器
|
44
|
-
- [x] 包含评估器
|
45
|
-
- [x] 正则表达式评估器
|
46
|
-
- [x] 自定义评分系统
|
47
|
-
|
48
|
-
6. **测试和文档**
|
49
|
-
- [x] 完整的测试套件
|
50
|
-
- [x] 详细的 README 文档
|
51
|
-
- [x] 使用示例
|
52
|
-
- [x] API 文档
|
53
|
-
- [x] 变更日志
|
54
|
-
|
55
|
-
## 🏗️ 项目结构
|
56
|
-
|
57
|
-
```
|
58
|
-
langfuse/
|
59
|
-
├── lib/
|
60
|
-
│ └── langfuse/
|
61
|
-
│ ├── client.rb # 核心客户端
|
62
|
-
│ ├── trace.rb # 追踪功能
|
63
|
-
│ ├── span.rb # Span 管理
|
64
|
-
│ ├── generation.rb # Generation 管理
|
65
|
-
│ ├── prompt.rb # 提示管理
|
66
|
-
│ ├── evaluation.rb # 评估系统
|
67
|
-
│ ├── errors.rb # 错误定义
|
68
|
-
│ ├── utils.rb # 工具类
|
69
|
-
│ └── version.rb # 版本信息
|
70
|
-
├── spec/ # 测试文件
|
71
|
-
├── examples/ # 使用示例
|
72
|
-
├── langfuse.gemspec # Gem 规范
|
73
|
-
├── README.md # 项目文档
|
74
|
-
├── CHANGELOG.md # 变更日志
|
75
|
-
└── LICENSE # 许可证
|
76
|
-
```
|
77
|
-
|
78
|
-
## 🔧 技术栈
|
79
|
-
|
80
|
-
- **Ruby**: >= 2.7.0
|
81
|
-
- **HTTP 客户端**: Faraday 2.0+
|
82
|
-
- **并发处理**: concurrent-ruby 1.0+
|
83
|
-
- **JSON 处理**: json 2.0+
|
84
|
-
- **测试框架**: RSpec 3.0+
|
85
|
-
- **代码质量**: RuboCop 1.0+
|
86
|
-
|
87
|
-
## 🚀 核心特性
|
88
|
-
|
89
|
-
### 1. 追踪系统
|
90
|
-
- 支持嵌套的 traces、spans 和 generations
|
91
|
-
- 自动 ID 生成和时间戳
|
92
|
-
- 元数据和标签支持
|
93
|
-
- 异步事件处理
|
94
|
-
|
95
|
-
### 2. 提示管理
|
96
|
-
- 版本控制和缓存
|
97
|
-
- 变量替换系统
|
98
|
-
- 多种提示格式支持
|
99
|
-
- LangChain 兼容性
|
100
|
-
|
101
|
-
### 3. 评估框架
|
102
|
-
- 多种内置评估器
|
103
|
-
- 可扩展的评估系统
|
104
|
-
- 自定义评分支持
|
105
|
-
- 详细的评估结果
|
106
|
-
|
107
|
-
### 4. 企业级特性
|
108
|
-
- 完整的错误处理
|
109
|
-
- 配置管理
|
110
|
-
- 环境变量支持
|
111
|
-
- 框架集成示例
|
112
|
-
|
113
|
-
## 📊 测试结果
|
114
|
-
|
115
|
-
```
|
116
|
-
🚀 Testing Langfuse Ruby SDK (Offline Mode)...
|
117
|
-
|
118
|
-
✅ Configuration successful
|
119
|
-
✅ Client initialization successful
|
120
|
-
✅ Trace creation successful
|
121
|
-
✅ Generation creation successful
|
122
|
-
✅ Prompt template successful
|
123
|
-
✅ Chat prompt template successful
|
124
|
-
✅ Evaluators successful (5/5)
|
125
|
-
✅ Utils successful
|
126
|
-
✅ Event queue successful
|
127
|
-
✅ Complex workflow successful
|
128
|
-
✅ Error handling successful
|
129
|
-
|
130
|
-
🎉 All offline tests completed successfully!
|
131
|
-
```
|
132
|
-
|
133
|
-
## 📚 使用示例
|
134
|
-
|
135
|
-
### 基本用法
|
136
|
-
```ruby
|
137
|
-
require 'langfuse'
|
138
|
-
|
139
|
-
client = Langfuse.new(
|
140
|
-
public_key: "pk-lf-...",
|
141
|
-
secret_key: "sk-lf-..."
|
142
|
-
)
|
143
|
-
|
144
|
-
trace = client.trace(
|
145
|
-
name: "chat-completion",
|
146
|
-
user_id: "user123",
|
147
|
-
input: { message: "Hello!" }
|
148
|
-
)
|
149
|
-
|
150
|
-
generation = trace.generation(
|
151
|
-
name: "openai-chat",
|
152
|
-
model: "gpt-3.5-turbo",
|
153
|
-
input: [{ role: "user", content: "Hello!" }],
|
154
|
-
output: { content: "Hi there!" }
|
155
|
-
)
|
156
|
-
|
157
|
-
client.flush
|
158
|
-
```
|
159
|
-
|
160
|
-
### 提示管理
|
161
|
-
```ruby
|
162
|
-
# 获取提示
|
163
|
-
prompt = client.get_prompt("greeting-prompt")
|
164
|
-
|
165
|
-
# 编译提示
|
166
|
-
compiled = prompt.compile(
|
167
|
-
user_name: "Alice",
|
168
|
-
topic: "AI"
|
169
|
-
)
|
170
|
-
```
|
171
|
-
|
172
|
-
### 评估系统
|
173
|
-
```ruby
|
174
|
-
evaluator = Langfuse::Evaluators::ExactMatchEvaluator.new
|
175
|
-
result = evaluator.evaluate(
|
176
|
-
"What is 2+2?",
|
177
|
-
"4",
|
178
|
-
expected: "4"
|
179
|
-
)
|
180
|
-
```
|
181
|
-
|
182
|
-
## 🔄 与官方 SDK 对比
|
183
|
-
|
184
|
-
| 功能 | Python SDK | JS SDK | Ruby SDK |
|
185
|
-
|------|------------|--------|----------|
|
186
|
-
| 基础追踪 | ✅ | ✅ | ✅ |
|
187
|
-
| 提示管理 | ✅ | ✅ | ✅ |
|
188
|
-
| 评估系统 | ✅ | ✅ | ✅ |
|
189
|
-
| 异步处理 | ✅ | ✅ | ✅ |
|
190
|
-
| 错误处理 | ✅ | ✅ | ✅ |
|
191
|
-
| 框架集成 | ✅ | ✅ | ✅ |
|
192
|
-
|
193
|
-
## 📦 发布准备
|
194
|
-
|
195
|
-
### Gem 规范
|
196
|
-
- 名称: `langfuse`
|
197
|
-
- 版本: `0.1.0`
|
198
|
-
- 许可证: MIT
|
199
|
-
- Ruby 版本: >= 2.7.0
|
200
|
-
|
201
|
-
### 依赖项
|
202
|
-
```ruby
|
203
|
-
spec.add_dependency "faraday", "~> 2.0"
|
204
|
-
spec.add_dependency "faraday-net_http", "~> 3.0"
|
205
|
-
spec.add_dependency "json", "~> 2.0"
|
206
|
-
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
207
|
-
```
|
208
|
-
|
209
|
-
## 🛠️ 开发指南
|
210
|
-
|
211
|
-
### 安装依赖
|
212
|
-
```bash
|
213
|
-
bundle install
|
214
|
-
```
|
215
|
-
|
216
|
-
### 运行测试
|
217
|
-
```bash
|
218
|
-
bundle exec rspec
|
219
|
-
```
|
220
|
-
|
221
|
-
### 离线测试
|
222
|
-
```bash
|
223
|
-
ruby test_offline.rb
|
224
|
-
```
|
225
|
-
|
226
|
-
## 🔮 未来扩展
|
227
|
-
|
228
|
-
### 可能的改进
|
229
|
-
1. **性能优化**
|
230
|
-
- 连接池管理
|
231
|
-
- 更高效的批处理
|
232
|
-
- 内存优化
|
233
|
-
|
234
|
-
2. **功能扩展**
|
235
|
-
- 更多评估器
|
236
|
-
- 数据集管理
|
237
|
-
- 实验功能
|
238
|
-
|
239
|
-
3. **集成支持**
|
240
|
-
- Rails 集成 gem
|
241
|
-
- Sidekiq 中间件
|
242
|
-
- 更多框架支持
|
243
|
-
|
244
|
-
## 📄 许可证
|
245
|
-
|
246
|
-
MIT License - 允许商业和开源使用
|
247
|
-
|
248
|
-
## 🤝 贡献指南
|
249
|
-
|
250
|
-
1. Fork 项目
|
251
|
-
2. 创建功能分支
|
252
|
-
3. 提交更改
|
253
|
-
4. 创建 Pull Request
|
254
|
-
|
255
|
-
## 📞 支持
|
256
|
-
|
257
|
-
- GitHub Issues: 报告 bug 和功能请求
|
258
|
-
- 文档: 详细的使用指南
|
259
|
-
- 示例: 完整的使用示例
|
260
|
-
|
261
|
-
---
|
262
|
-
|
263
|
-
**总结**: 这个 Langfuse Ruby SDK 是一个功能完整、测试充分的生产级 SDK,完全兼容 Langfuse API,可以立即用于生产环境。它提供了与官方 Python 和 JavaScript SDK 相同的功能,并且遵循 Ruby 社区的最佳实践。
|