langfuse-ruby 0.1.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.
@@ -0,0 +1,41 @@
1
+ require 'securerandom'
2
+ require 'time'
3
+
4
+ module Langfuse
5
+ module Utils
6
+ class << self
7
+ def generate_id
8
+ SecureRandom.uuid
9
+ end
10
+
11
+ def current_timestamp
12
+ Time.now.utc.iso8601(3)
13
+ end
14
+
15
+ def deep_symbolize_keys(hash)
16
+ return hash unless hash.is_a?(Hash)
17
+
18
+ hash.each_with_object({}) do |(key, value), result|
19
+ new_key = key.is_a?(String) ? key.to_sym : key
20
+ new_value = value.is_a?(Hash) ? deep_symbolize_keys(value) : value
21
+ result[new_key] = new_value
22
+ end
23
+ end
24
+
25
+ def deep_stringify_keys(hash)
26
+ return hash unless hash.is_a?(Hash)
27
+
28
+ hash.each_with_object({}) do |(key, value), result|
29
+ new_key = key.to_s
30
+ new_value = value.is_a?(Hash) ? deep_stringify_keys(value) : value
31
+ result[new_key] = new_value
32
+ end
33
+ end
34
+
35
+ def validate_required_fields(data, required_fields)
36
+ missing_fields = required_fields.select { |field| data[field].nil? || data[field].to_s.empty? }
37
+ raise ValidationError, "Missing required fields: #{missing_fields.join(', ')}" unless missing_fields.empty?
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Langfuse
2
+ VERSION = "0.1.0"
3
+ end
data/lib/langfuse.rb ADDED
@@ -0,0 +1,40 @@
1
+ require_relative "langfuse/version"
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"
10
+
11
+ module Langfuse
12
+ class << self
13
+ # Configure the Langfuse client with default settings
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ # Create a new Langfuse client instance
23
+ def new(**options)
24
+ Client.new(**options)
25
+ end
26
+ end
27
+
28
+ class Configuration
29
+ attr_accessor :public_key, :secret_key, :host, :debug, :timeout, :retries
30
+
31
+ def initialize
32
+ @public_key = nil
33
+ @secret_key = nil
34
+ @host = "https://cloud.langfuse.com"
35
+ @debug = false
36
+ @timeout = 30
37
+ @retries = 3
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,120 @@
1
+ #!/bin/bash
2
+
3
+ # Langfuse Ruby SDK Release Script
4
+ set -e
5
+
6
+ echo "🚀 Starting Langfuse Ruby SDK release process..."
7
+
8
+ # Colors for output
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ NC='\033[0m' # No Color
13
+
14
+ # Function to print colored output
15
+ print_status() {
16
+ echo -e "${GREEN}✅ $1${NC}"
17
+ }
18
+
19
+ print_warning() {
20
+ echo -e "${YELLOW}⚠️ $1${NC}"
21
+ }
22
+
23
+ print_error() {
24
+ echo -e "${RED}❌ $1${NC}"
25
+ }
26
+
27
+ # Check if we're in a git repository
28
+ if [ ! -d ".git" ]; then
29
+ print_error "This is not a git repository. Please run 'git init' first."
30
+ exit 1
31
+ fi
32
+
33
+ # Get current version
34
+ CURRENT_VERSION=$(ruby -r ./lib/langfuse/version.rb -e "puts Langfuse::VERSION")
35
+ print_status "Current version: $CURRENT_VERSION"
36
+
37
+ # Check if there are uncommitted changes
38
+ if [ -n "$(git status --porcelain)" ]; then
39
+ print_warning "You have uncommitted changes. Please commit or stash them first."
40
+ git status --short
41
+ exit 1
42
+ fi
43
+
44
+ # Run tests
45
+ print_status "Running tests..."
46
+ if ! bundle exec rspec; then
47
+ print_error "Tests failed. Please fix them before releasing."
48
+ exit 1
49
+ fi
50
+
51
+ print_status "Running offline tests..."
52
+ if ! ruby test_offline.rb; then
53
+ print_error "Offline tests failed. Please fix them before releasing."
54
+ exit 1
55
+ fi
56
+
57
+ # Build gem
58
+ print_status "Building gem..."
59
+ if ! gem build langfuse.gemspec; then
60
+ print_error "Gem build failed."
61
+ exit 1
62
+ fi
63
+
64
+ # Check if gem was built successfully
65
+ if [ ! -f "langfuse-${CURRENT_VERSION}.gem" ]; then
66
+ print_error "Gem file not found: langfuse-${CURRENT_VERSION}.gem"
67
+ exit 1
68
+ fi
69
+
70
+ print_status "Gem built successfully: langfuse-${CURRENT_VERSION}.gem"
71
+
72
+ # Ask for confirmation
73
+ echo
74
+ echo "📋 Release Summary:"
75
+ echo " Version: $CURRENT_VERSION"
76
+ echo " Gem file: langfuse-${CURRENT_VERSION}.gem"
77
+ echo " Tests: ✅ Passed"
78
+ echo
79
+
80
+ read -p "Do you want to proceed with the release? (y/N): " -n 1 -r
81
+ echo
82
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
83
+ print_warning "Release cancelled."
84
+ rm -f "langfuse-${CURRENT_VERSION}.gem"
85
+ exit 0
86
+ fi
87
+
88
+ # Create git tag
89
+ print_status "Creating git tag v${CURRENT_VERSION}..."
90
+ git tag "v${CURRENT_VERSION}"
91
+
92
+ # Push to git
93
+ print_status "Pushing to git..."
94
+ git push origin main
95
+ git push origin "v${CURRENT_VERSION}"
96
+
97
+ # Publish to RubyGems
98
+ print_status "Publishing to RubyGems..."
99
+ if gem push "langfuse-${CURRENT_VERSION}.gem"; then
100
+ print_status "Successfully published to RubyGems!"
101
+ else
102
+ print_error "Failed to publish to RubyGems."
103
+ print_warning "You may need to run 'gem signin' first."
104
+ exit 1
105
+ fi
106
+
107
+ # Clean up
108
+ rm -f "langfuse-${CURRENT_VERSION}.gem"
109
+
110
+ print_status "Release completed successfully! 🎉"
111
+ echo
112
+ echo "📝 Next steps:"
113
+ echo " 1. Check https://rubygems.org/gems/langfuse"
114
+ echo " 2. Update documentation if needed"
115
+ echo " 3. Announce the release"
116
+ echo
117
+ echo "🔗 Useful links:"
118
+ echo " - RubyGems: https://rubygems.org/gems/langfuse"
119
+ echo " - GitHub: https://github.com/your-username/langfuse-ruby"
120
+ echo " - Documentation: https://github.com/your-username/langfuse-ruby#readme"
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Langfuse Ruby SDK Release Verification Script
4
+ require 'net/http'
5
+ require 'json'
6
+ require 'uri'
7
+
8
+ puts '🔍 Verifying Langfuse Ruby SDK release...'
9
+
10
+ # Get current version
11
+ require_relative '../lib/langfuse/version'
12
+ current_version = Langfuse::VERSION
13
+
14
+ puts "📦 Current version: #{current_version}"
15
+
16
+ # Check if gem is available on RubyGems
17
+ def check_rubygems(gem_name, version)
18
+ uri = URI("https://rubygems.org/api/v1/gems/#{gem_name}.json")
19
+
20
+ begin
21
+ response = Net::HTTP.get_response(uri)
22
+
23
+ if response.code == '200'
24
+ data = JSON.parse(response.body)
25
+ puts '✅ Gem found on RubyGems:'
26
+ puts " Name: #{data['name']}"
27
+ puts " Version: #{data['version']}"
28
+ puts " Downloads: #{data['downloads']}"
29
+ puts " Authors: #{data['authors']}"
30
+ puts " Homepage: #{data['homepage_uri']}"
31
+ puts " Source: #{data['source_code_uri']}"
32
+
33
+ if data['version'] == version
34
+ puts '✅ Version matches current version'
35
+ else
36
+ puts "⚠️ Version mismatch: Expected #{version}, found #{data['version']}"
37
+ end
38
+
39
+ true
40
+ else
41
+ puts "❌ Gem not found on RubyGems (HTTP #{response.code})"
42
+ false
43
+ end
44
+ rescue StandardError => e
45
+ puts "❌ Error checking RubyGems: #{e.message}"
46
+ false
47
+ end
48
+ end
49
+
50
+ # Check if specific version is available
51
+ def check_version_availability(gem_name, version)
52
+ uri = URI("https://rubygems.org/api/v1/versions/#{gem_name}.json")
53
+
54
+ begin
55
+ response = Net::HTTP.get_response(uri)
56
+
57
+ if response.code == '200'
58
+ versions = JSON.parse(response.body)
59
+ version_found = versions.any? { |v| v['number'] == version }
60
+
61
+ if version_found
62
+ puts "✅ Version #{version} is available on RubyGems"
63
+ true
64
+ else
65
+ puts "❌ Version #{version} not found on RubyGems"
66
+ puts " Available versions: #{versions.map { |v| v['number'] }.join(', ')}"
67
+ false
68
+ end
69
+ else
70
+ puts "❌ Could not fetch version list (HTTP #{response.code})"
71
+ false
72
+ end
73
+ rescue StandardError => e
74
+ puts "❌ Error checking version availability: #{e.message}"
75
+ false
76
+ end
77
+ end
78
+
79
+ # Run checks
80
+ puts "\n🔍 Checking RubyGems availability..."
81
+ gem_available = check_rubygems('langfuse', current_version)
82
+
83
+ puts "\n🔍 Checking version availability..."
84
+ version_available = check_version_availability('langfuse', current_version)
85
+
86
+ # Test local installation
87
+ puts "\n🔍 Testing local gem functionality..."
88
+ begin
89
+ require_relative '../lib/langfuse'
90
+
91
+ # Test basic functionality
92
+ puts '✅ Langfuse module loaded successfully'
93
+ puts " Version: #{Langfuse::VERSION}"
94
+
95
+ # Test client creation (without real credentials)
96
+ begin
97
+ client = Langfuse.new(public_key: 'test', secret_key: 'test')
98
+ puts '✅ Client creation successful'
99
+ rescue Langfuse::AuthenticationError
100
+ puts '✅ Authentication error expected (no real credentials)'
101
+ rescue StandardError => e
102
+ puts "❌ Unexpected error creating client: #{e.message}"
103
+ end
104
+
105
+ # Test configuration
106
+ Langfuse.configure do |config|
107
+ config.public_key = 'test'
108
+ config.secret_key = 'test'
109
+ end
110
+ puts '✅ Configuration successful'
111
+
112
+ # Test utilities
113
+ id = Langfuse::Utils.generate_id
114
+ timestamp = Langfuse::Utils.current_timestamp
115
+ puts "✅ Utilities working (ID: #{id[0..7]}..., Timestamp: #{timestamp})"
116
+ rescue StandardError => e
117
+ puts "❌ Error testing local functionality: #{e.message}"
118
+ end
119
+
120
+ # Summary
121
+ puts "\n📊 Verification Summary:"
122
+ puts " Gem available on RubyGems: #{gem_available ? '✅' : '❌'}"
123
+ puts " Version available: #{version_available ? '✅' : '❌'}"
124
+ puts ' Local functionality: ✅'
125
+
126
+ if gem_available && version_available
127
+ puts "\n🎉 Release verification successful!"
128
+ puts ' Your gem is ready for use!'
129
+ puts "\n📝 Installation command:"
130
+ puts ' gem install langfuse-ruby'
131
+ else
132
+ puts "\n⚠️ Release verification incomplete"
133
+ puts ' Please check the issues above'
134
+ end
135
+
136
+ puts "\n🔗 Useful links:"
137
+ puts ' - RubyGems page: https://rubygems.org/gems/langfuse'
138
+ puts ' - Documentation: https://github.com/your-username/langfuse-ruby'
139
+ puts ' - Issues: https://github.com/your-username/langfuse-ruby/issues'
data/test_basic.rb ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'lib/langfuse'
4
+
5
+ puts "🚀 Testing Langfuse Ruby SDK..."
6
+
7
+ # Test 1: Basic configuration
8
+ puts "\n1. Testing configuration..."
9
+ Langfuse.configure do |config|
10
+ config.public_key = "test_key"
11
+ config.secret_key = "test_secret"
12
+ config.host = "https://test.langfuse.com"
13
+ config.debug = true
14
+ end
15
+
16
+ puts "✅ Configuration successful"
17
+ puts " Public key: #{Langfuse.configuration.public_key}"
18
+ puts " Host: #{Langfuse.configuration.host}"
19
+
20
+ # Test 2: Client initialization
21
+ puts "\n2. Testing client initialization..."
22
+ begin
23
+ client = Langfuse.new(
24
+ public_key: "test_key",
25
+ secret_key: "test_secret",
26
+ host: "https://test.langfuse.com"
27
+ )
28
+ puts "✅ Client initialization successful"
29
+ puts " Client class: #{client.class}"
30
+ puts " Public key: #{client.public_key}"
31
+ rescue => e
32
+ puts "❌ Client initialization failed: #{e.message}"
33
+ end
34
+
35
+ # Test 3: Trace creation
36
+ puts "\n3. Testing trace creation..."
37
+ begin
38
+ trace = client.trace(
39
+ name: "test-trace",
40
+ user_id: "test-user",
41
+ input: { message: "Hello, world!" }
42
+ )
43
+ puts "✅ Trace creation successful"
44
+ puts " Trace ID: #{trace.id}"
45
+ puts " Trace name: #{trace.name}"
46
+ rescue => e
47
+ puts "❌ Trace creation failed: #{e.message}"
48
+ end
49
+
50
+ # Test 4: Generation creation
51
+ puts "\n4. Testing generation creation..."
52
+ begin
53
+ generation = trace.generation(
54
+ name: "test-generation",
55
+ model: "gpt-3.5-turbo",
56
+ input: [{ role: "user", content: "Hello!" }],
57
+ output: { content: "Hi there!" }
58
+ )
59
+ puts "✅ Generation creation successful"
60
+ puts " Generation ID: #{generation.id}"
61
+ puts " Model: #{generation.model}"
62
+ rescue => e
63
+ puts "❌ Generation creation failed: #{e.message}"
64
+ end
65
+
66
+ # Test 5: Span creation
67
+ puts "\n5. Testing span creation..."
68
+ begin
69
+ span = trace.span(
70
+ name: "test-span",
71
+ input: { query: "test query" }
72
+ )
73
+ puts "✅ Span creation successful"
74
+ puts " Span ID: #{span.id}"
75
+ puts " Span name: #{span.name}"
76
+ rescue => e
77
+ puts "❌ Span creation failed: #{e.message}"
78
+ end
79
+
80
+ # Test 6: Prompt template
81
+ puts "\n6. Testing prompt template..."
82
+ begin
83
+ template = Langfuse::PromptTemplate.from_template(
84
+ "Hello {{name}}! How are you feeling {{mood}} today?"
85
+ )
86
+
87
+ formatted = template.format(
88
+ name: "Alice",
89
+ mood: "happy"
90
+ )
91
+
92
+ puts "✅ Prompt template successful"
93
+ puts " Template variables: #{template.input_variables}"
94
+ puts " Formatted: #{formatted}"
95
+ rescue => e
96
+ puts "❌ Prompt template failed: #{e.message}"
97
+ end
98
+
99
+ # Test 7: Chat prompt template
100
+ puts "\n7. Testing chat prompt template..."
101
+ begin
102
+ chat_template = Langfuse::ChatPromptTemplate.from_messages([
103
+ { role: "system", content: "You are a helpful {{role}} assistant." },
104
+ { role: "user", content: "{{user_input}}" }
105
+ ])
106
+
107
+ messages = chat_template.format(
108
+ role: "coding",
109
+ user_input: "Help me with Ruby"
110
+ )
111
+
112
+ puts "✅ Chat prompt template successful"
113
+ puts " Template variables: #{chat_template.input_variables}"
114
+ puts " Messages count: #{messages.length}"
115
+ rescue => e
116
+ puts "❌ Chat prompt template failed: #{e.message}"
117
+ end
118
+
119
+ # Test 8: Evaluators
120
+ puts "\n8. Testing evaluators..."
121
+ begin
122
+ # Exact match evaluator
123
+ exact_match = Langfuse::Evaluators::ExactMatchEvaluator.new
124
+ result = exact_match.evaluate(
125
+ input: "What is 2+2?",
126
+ output: "4",
127
+ expected: "4"
128
+ )
129
+ puts "✅ Exact match evaluator successful"
130
+ puts " Result: #{result}"
131
+
132
+ # Similarity evaluator
133
+ similarity = Langfuse::Evaluators::SimilarityEvaluator.new
134
+ result = similarity.evaluate(
135
+ input: "What is AI?",
136
+ output: "Artificial Intelligence",
137
+ expected: "AI is artificial intelligence"
138
+ )
139
+ puts "✅ Similarity evaluator successful"
140
+ puts " Result: #{result}"
141
+ rescue => e
142
+ puts "❌ Evaluator failed: #{e.message}"
143
+ end
144
+
145
+ # Test 9: Utils
146
+ puts "\n9. Testing utilities..."
147
+ begin
148
+ id = Langfuse::Utils.generate_id
149
+ timestamp = Langfuse::Utils.current_timestamp
150
+
151
+ puts "✅ Utils successful"
152
+ puts " Generated ID: #{id}"
153
+ puts " Timestamp: #{timestamp}"
154
+ rescue => e
155
+ puts "❌ Utils failed: #{e.message}"
156
+ end
157
+
158
+ # Test 10: Event queue
159
+ puts "\n10. Testing event queue..."
160
+ begin
161
+ queue_size_before = client.instance_variable_get(:@event_queue).length
162
+
163
+ client.score(
164
+ trace_id: trace.id,
165
+ name: "test-score",
166
+ value: 0.9
167
+ )
168
+
169
+ queue_size_after = client.instance_variable_get(:@event_queue).length
170
+
171
+ puts "✅ Event queue successful"
172
+ puts " Queue size before: #{queue_size_before}"
173
+ puts " Queue size after: #{queue_size_after}"
174
+ rescue => e
175
+ puts "❌ Event queue failed: #{e.message}"
176
+ end
177
+
178
+ puts "\n🎉 All tests completed!"
179
+ puts " This SDK is ready for use with Langfuse!"
180
+ puts " Remember to set your real API keys when using in production."
181
+
182
+ # Clean shutdown
183
+ client.shutdown