langfuse-ruby 0.1.4 → 0.1.6
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/.github/workflows/ci.yml +11 -11
- data/.github/workflows/release.yml +8 -9
- data/.rubocop.yml +66 -0
- data/CHANGELOG.md +34 -1
- data/CLAUDE.md +100 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +7 -3
- data/Makefile +73 -0
- data/README.md +92 -9
- data/Rakefile +4 -2
- data/docs/FINAL_SUMMARY.md +11 -10
- data/docs/PUBLISH_GUIDE.md +2 -2
- data/docs/README.md +3 -3
- data/docs/RELEASE_CHECKLIST.md +44 -13
- data/examples/auto_flush_control.rb +3 -2
- data/examples/basic_tracing.rb +6 -5
- data/examples/connection_config_demo.rb +1 -0
- data/examples/event_usage.rb +3 -2
- data/examples/prompt_management.rb +3 -2
- data/examples/simplified_usage.rb +126 -0
- data/examples/url_encoding_demo.rb +57 -0
- data/langfuse-ruby.gemspec +6 -2
- data/lib/langfuse/client.rb +160 -3
- data/lib/langfuse/errors.rb +2 -0
- data/lib/langfuse/evaluation.rb +14 -12
- data/lib/langfuse/event.rb +21 -5
- data/lib/langfuse/generation.rb +155 -5
- data/lib/langfuse/null_objects.rb +74 -0
- data/lib/langfuse/observation_types.rb +61 -0
- data/lib/langfuse/prompt.rb +2 -2
- data/lib/langfuse/span.rb +162 -7
- data/lib/langfuse/trace.rb +152 -1
- data/lib/langfuse/utils.rb +7 -0
- data/lib/langfuse/version.rb +3 -1
- data/lib/langfuse.rb +128 -2
- data/scripts/release.sh +1 -1
- data/{test_offline.rb → scripts/test_offline.rb} +5 -4
- data/scripts/verify_release.rb +5 -4
- metadata +27 -9
- data/docs/TYPE_VALIDATION_TROUBLESHOOTING.md +0 -202
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e15fd7f7f4e2e8b1a8018f27de35f42a3124973c8a455b50781a0d08924aa3d
|
|
4
|
+
data.tar.gz: 91253f7addf89cef33fcf047376f8667fae8e2e9d75f096b4096d4c876d06b20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62d8773683844ecc44a1d258765f0522f77e83b02cbb7b7441d892e3e6a9252348f8ccfd331e185186b46aa9fb30203146e7beffd29a1330b5ab14ff75862a47
|
|
7
|
+
data.tar.gz: 23e33a0b7d7b8b25c07e9663cf320771a6db8160fb18302179acc8aeaaf99c1740add31c84448106df46efe22e89b2c36ce7bae6b66e600e525226854a2003d7
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -2,33 +2,33 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [
|
|
5
|
+
branches: [ master ]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: [
|
|
7
|
+
branches: [ master ]
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
test:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
strategy:
|
|
14
14
|
matrix:
|
|
15
|
-
ruby-version: ['
|
|
16
|
-
|
|
15
|
+
ruby-version: ['3.1', '3.2', '3.3', '3.4']
|
|
16
|
+
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v4
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
21
21
|
uses: ruby/setup-ruby@v1
|
|
22
22
|
with:
|
|
23
23
|
ruby-version: ${{ matrix.ruby-version }}
|
|
24
24
|
bundler-cache: true
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
- name: Run tests
|
|
27
27
|
run: bundle exec rspec
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
- name: Run offline tests
|
|
30
|
-
run: ruby test_offline.rb
|
|
31
|
-
|
|
30
|
+
run: bundle exec ruby scripts/test_offline.rb
|
|
31
|
+
|
|
32
32
|
- name: Run RuboCop
|
|
33
33
|
run: bundle exec rubocop
|
|
34
34
|
continue-on-error: true
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
- name: Set up Ruby
|
|
44
44
|
uses: ruby/setup-ruby@v1
|
|
45
45
|
with:
|
|
46
|
-
ruby-version: '3.
|
|
46
|
+
ruby-version: '3.4'
|
|
47
47
|
bundler-cache: true
|
|
48
48
|
|
|
49
49
|
- name: Build gem
|
|
@@ -16,17 +16,17 @@ jobs:
|
|
|
16
16
|
- name: Set up Ruby
|
|
17
17
|
uses: ruby/setup-ruby@v1
|
|
18
18
|
with:
|
|
19
|
-
ruby-version: '3.
|
|
19
|
+
ruby-version: '3.4'
|
|
20
20
|
bundler-cache: true
|
|
21
21
|
|
|
22
22
|
- name: Run tests
|
|
23
23
|
run: bundle exec rspec
|
|
24
24
|
|
|
25
25
|
- name: Run offline tests
|
|
26
|
-
run: ruby test_offline.rb
|
|
26
|
+
run: bundle exec ruby scripts/test_offline.rb
|
|
27
27
|
|
|
28
28
|
- name: Build gem
|
|
29
|
-
run: gem build langfuse.gemspec
|
|
29
|
+
run: gem build langfuse-ruby.gemspec
|
|
30
30
|
|
|
31
31
|
- name: Publish to RubyGems
|
|
32
32
|
run: |
|
|
@@ -38,14 +38,13 @@ jobs:
|
|
|
38
38
|
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
39
39
|
|
|
40
40
|
- name: Create GitHub Release
|
|
41
|
-
uses:
|
|
42
|
-
env:
|
|
43
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
41
|
+
uses: softprops/action-gh-release@v2
|
|
44
42
|
with:
|
|
45
|
-
|
|
46
|
-
release_name: Release ${{ github.ref }}
|
|
43
|
+
files: "*.gem"
|
|
47
44
|
body: |
|
|
48
45
|
Changes in this Release
|
|
49
46
|
- Check CHANGELOG.md for details
|
|
50
47
|
draft: false
|
|
51
|
-
prerelease: false
|
|
48
|
+
prerelease: false
|
|
49
|
+
env:
|
|
50
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.1
|
|
3
|
+
NewCops: enable
|
|
4
|
+
Exclude:
|
|
5
|
+
- 'bin/*'
|
|
6
|
+
- 'vendor/**/*'
|
|
7
|
+
- 'spec/fixtures/**/*'
|
|
8
|
+
- 'scripts/**/*'
|
|
9
|
+
- 'examples/**/*'
|
|
10
|
+
- 'langfuse-ruby.gemspec'
|
|
11
|
+
- 'Gemfile'
|
|
12
|
+
- 'Rakefile'
|
|
13
|
+
SuggestExtensions: false
|
|
14
|
+
|
|
15
|
+
Layout/LineLength:
|
|
16
|
+
Max: 160
|
|
17
|
+
|
|
18
|
+
Style/Documentation:
|
|
19
|
+
Enabled: false
|
|
20
|
+
|
|
21
|
+
Style/FrozenStringLiteralComment:
|
|
22
|
+
Enabled: true
|
|
23
|
+
EnforcedStyle: always
|
|
24
|
+
|
|
25
|
+
Style/ConditionalAssignment:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Style/IfUnlessModifier:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Metrics/BlockLength:
|
|
32
|
+
Exclude:
|
|
33
|
+
- 'spec/**/*'
|
|
34
|
+
- 'langfuse-ruby.gemspec'
|
|
35
|
+
|
|
36
|
+
Metrics/MethodLength:
|
|
37
|
+
Max: 50
|
|
38
|
+
Exclude:
|
|
39
|
+
- 'scripts/**/*'
|
|
40
|
+
|
|
41
|
+
Metrics/AbcSize:
|
|
42
|
+
Max: 50
|
|
43
|
+
Exclude:
|
|
44
|
+
- 'scripts/**/*'
|
|
45
|
+
|
|
46
|
+
Metrics/CyclomaticComplexity:
|
|
47
|
+
Max: 20
|
|
48
|
+
|
|
49
|
+
Metrics/PerceivedComplexity:
|
|
50
|
+
Max: 20
|
|
51
|
+
|
|
52
|
+
Metrics/ParameterLists:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
Naming/AccessorMethodName:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
Style/AsciiComments:
|
|
59
|
+
Enabled: false
|
|
60
|
+
|
|
61
|
+
Lint/UnusedMethodArgument:
|
|
62
|
+
Exclude:
|
|
63
|
+
- 'lib/langfuse/evaluation.rb'
|
|
64
|
+
|
|
65
|
+
Metrics/ClassLength:
|
|
66
|
+
Max: 600
|
data/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- **Simplified API**: New class-level convenience methods for easier usage
|
|
12
|
+
- `Langfuse.trace(name, **options, &block)` - Block-based tracing with automatic flush
|
|
13
|
+
- `Langfuse.get_prompt(name, variables:, retries:)` - Get and compile prompts with retry support
|
|
14
|
+
- `Langfuse.client` - Thread-safe singleton client access
|
|
15
|
+
- `Langfuse.flush` / `Langfuse.shutdown` / `Langfuse.reset!` - Client lifecycle management
|
|
16
|
+
- **Graceful Degradation**: Null object pattern for fault tolerance
|
|
17
|
+
- `NullTrace`, `NullGeneration`, `NullSpan`, `NullEvent` - No-op objects when Langfuse is unavailable
|
|
18
|
+
- **Retry Support**: `get_prompt` now supports configurable retries with exponential backoff (default: 2 retries)
|
|
19
|
+
- New example file `examples/simplified_usage.rb` demonstrating the simplified API
|
|
20
|
+
- Comprehensive test coverage for convenience methods (28 new tests)
|
|
21
|
+
|
|
22
|
+
## [0.1.5] - 2025-12-26
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Support for all enhanced observation types: `agent`, `tool`, `chain`, `retriever`, `embedding`, `evaluator`, `guardrail`
|
|
26
|
+
- New `ObservationType` module with constants for all observation types
|
|
27
|
+
- Convenience methods on `Client` and `Trace` for creating enhanced observations
|
|
28
|
+
- New `as_type` parameter on `span()` method for specifying observation type
|
|
29
|
+
- Comprehensive test coverage for enhanced observation types
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- Fixed URL encoding for prompt names containing special characters (/, spaces, etc.) in `get_prompt` method
|
|
33
|
+
- Prompt names are now automatically URL-encoded before being interpolated into API paths
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Updated Ruby version requirement to >= 3.1.0
|
|
37
|
+
- Environment variables moved from metadata to top-level trace attributes
|
|
38
|
+
|
|
39
|
+
### Internal
|
|
40
|
+
- Added `Utils.url_encode` helper method for consistent URL encoding across the SDK
|
|
41
|
+
- CI improvements for offline test execution
|
|
42
|
+
|
|
10
43
|
## [0.1.4] - 2025-07-29
|
|
11
44
|
|
|
12
45
|
### Added
|
|
13
|
-
- Added support for `trace
|
|
46
|
+
- Added support for `trace` event type in Langfuse ingestion API
|
|
14
47
|
- Added support for `event-create` event type in Langfuse ingestion API
|
|
15
48
|
- New `Event` class for creating generic events within traces, spans, and generations
|
|
16
49
|
- Added `event()` method to `Client`, `Trace`, `Span`, and `Generation` classes
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is the official Ruby SDK for [Langfuse](https://langfuse.com) - an open-source LLM engineering platform. The SDK provides tracing, prompt management, and evaluation capabilities for LLM applications.
|
|
8
|
+
|
|
9
|
+
## Common Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install dependencies
|
|
13
|
+
bundle install
|
|
14
|
+
|
|
15
|
+
# Run all RSpec tests
|
|
16
|
+
bundle exec rake spec
|
|
17
|
+
|
|
18
|
+
# Run a single test file
|
|
19
|
+
bundle exec rspec spec/langfuse/client_spec.rb
|
|
20
|
+
|
|
21
|
+
# Run offline tests (no network required)
|
|
22
|
+
bundle exec rake test_offline
|
|
23
|
+
|
|
24
|
+
# Run all tests (spec + offline)
|
|
25
|
+
bundle exec rake test_all
|
|
26
|
+
|
|
27
|
+
# Lint code
|
|
28
|
+
bundle exec rubocop
|
|
29
|
+
|
|
30
|
+
# Build the gem
|
|
31
|
+
bundle exec rake build
|
|
32
|
+
|
|
33
|
+
# Release to RubyGems
|
|
34
|
+
bundle exec rake release_gem
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Architecture
|
|
38
|
+
|
|
39
|
+
### Core Classes
|
|
40
|
+
|
|
41
|
+
- **`Langfuse`** ([lib/langfuse.rb](lib/langfuse.rb)) - Module with class-level convenience methods (`trace`, `get_prompt`, `client`, `flush`, `shutdown`, `reset!`)
|
|
42
|
+
- **`Langfuse::Client`** ([lib/langfuse/client.rb](lib/langfuse/client.rb)) - Main entry point. Handles API authentication, HTTP connections (via Faraday), event queuing, and background flush thread for auto-batching events.
|
|
43
|
+
- **`Langfuse::Trace`** ([lib/langfuse/trace.rb](lib/langfuse/trace.rb)) - Top-level container for a request/session.
|
|
44
|
+
- **`Langfuse::Span`** ([lib/langfuse/span.rb](lib/langfuse/span.rb)) - Timed operation with enhanced type support.
|
|
45
|
+
- **`Langfuse::Generation`** ([lib/langfuse/generation.rb](lib/langfuse/generation.rb)) - LLM call tracking.
|
|
46
|
+
- **`Langfuse::Event`** ([lib/langfuse/event.rb](lib/langfuse/event.rb)) - Point-in-time events.
|
|
47
|
+
- **`Langfuse::Prompt`** ([lib/langfuse/prompt.rb](lib/langfuse/prompt.rb)) - Prompt templates with caching.
|
|
48
|
+
- **`Langfuse::NullTrace/NullGeneration/NullSpan`** ([lib/langfuse/null_objects.rb](lib/langfuse/null_objects.rb)) - Null objects for graceful degradation.
|
|
49
|
+
|
|
50
|
+
### Simplified API (Recommended)
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# Block-based tracing with automatic flush
|
|
54
|
+
Langfuse.trace("my-trace", user_id: "user-1") do |trace|
|
|
55
|
+
gen = trace.generation(name: "openai", model: "gpt-4", input: messages)
|
|
56
|
+
response = call_llm(...)
|
|
57
|
+
gen.end(output: response, usage: usage)
|
|
58
|
+
end # Auto flush!
|
|
59
|
+
|
|
60
|
+
# Get prompt with variables and retry
|
|
61
|
+
Langfuse.get_prompt("my-prompt", variables: { name: "Alice" }, retries: 3)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Event Flow
|
|
65
|
+
|
|
66
|
+
1. Observations (traces, spans, generations, events) are created via Client methods
|
|
67
|
+
2. Events are queued in `@event_queue` (thread-safe `Concurrent::Array`)
|
|
68
|
+
3. Background flush thread sends batched events to `/api/public/ingestion` endpoint
|
|
69
|
+
4. Manual flush available via `client.flush`; graceful shutdown via `client.shutdown`
|
|
70
|
+
|
|
71
|
+
### Observation Types
|
|
72
|
+
|
|
73
|
+
The SDK supports enhanced observation types defined in `ObservationType` module:
|
|
74
|
+
- Core: `span`, `generation`, `event`
|
|
75
|
+
- Enhanced: `agent`, `tool`, `chain`, `retriever`, `embedding`, `evaluator`, `guardrail`
|
|
76
|
+
|
|
77
|
+
Enhanced types are implemented as spans with `as_type` metadata sent to the API.
|
|
78
|
+
|
|
79
|
+
### Configuration
|
|
80
|
+
|
|
81
|
+
Client accepts config via:
|
|
82
|
+
1. Constructor parameters
|
|
83
|
+
2. `Langfuse.configure` block
|
|
84
|
+
3. Environment variables: `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`, `LANGFUSE_FLUSH_INTERVAL`, `LANGFUSE_AUTO_FLUSH`
|
|
85
|
+
|
|
86
|
+
### Error Handling
|
|
87
|
+
|
|
88
|
+
Custom exceptions in [lib/langfuse/errors.rb](lib/langfuse/errors.rb):
|
|
89
|
+
- `AuthenticationError`, `APIError`, `NetworkError`, `ValidationError`, `RateLimitError`, `TimeoutError`
|
|
90
|
+
|
|
91
|
+
Graceful degradation: When Langfuse is unavailable, `Langfuse.trace` yields a `NullTrace` that silently no-ops all operations.
|
|
92
|
+
|
|
93
|
+
## Key Implementation Details
|
|
94
|
+
|
|
95
|
+
- Uses Faraday for HTTP with Basic Auth (public_key:secret_key)
|
|
96
|
+
- Prompt names with special characters are auto-URL-encoded via `Utils.url_encode`
|
|
97
|
+
- `trace-update` events merge into existing `trace-create` in queue (deduplication)
|
|
98
|
+
- All keys are converted to camelCase before API submission via `Utils.deep_camelize_keys`
|
|
99
|
+
- Thread-safe singleton client via `Thread.current[:langfuse_client]`
|
|
100
|
+
- `get_prompt` supports configurable retries with exponential backoff
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
langfuse-ruby (0.1.
|
|
4
|
+
langfuse-ruby (0.1.6)
|
|
5
5
|
concurrent-ruby (~> 1.0)
|
|
6
6
|
faraday (>= 1.8, < 3.0)
|
|
7
|
+
faraday-multipart (~> 1.0)
|
|
7
8
|
faraday-net_http (>= 1.0, < 4.0)
|
|
8
9
|
json (~> 2.0)
|
|
9
10
|
|
|
@@ -24,6 +25,8 @@ GEM
|
|
|
24
25
|
faraday-net_http (>= 2.0, < 3.5)
|
|
25
26
|
json
|
|
26
27
|
logger
|
|
28
|
+
faraday-multipart (1.1.1)
|
|
29
|
+
multipart-post (~> 2.0)
|
|
27
30
|
faraday-net_http (3.4.1)
|
|
28
31
|
net-http (>= 0.5.0)
|
|
29
32
|
hashdiff (1.2.0)
|
|
@@ -31,6 +34,7 @@ GEM
|
|
|
31
34
|
language_server-protocol (3.17.0.5)
|
|
32
35
|
lint_roller (1.1.0)
|
|
33
36
|
logger (1.7.0)
|
|
37
|
+
multipart-post (2.4.1)
|
|
34
38
|
net-http (0.6.0)
|
|
35
39
|
uri
|
|
36
40
|
parallel (1.27.0)
|
|
@@ -85,7 +89,7 @@ GEM
|
|
|
85
89
|
yard (0.9.37)
|
|
86
90
|
|
|
87
91
|
PLATFORMS
|
|
88
|
-
arm64-darwin
|
|
92
|
+
arm64-darwin
|
|
89
93
|
ruby
|
|
90
94
|
|
|
91
95
|
DEPENDENCIES
|
|
@@ -93,7 +97,7 @@ DEPENDENCIES
|
|
|
93
97
|
langfuse-ruby!
|
|
94
98
|
rake (~> 13.0)
|
|
95
99
|
rspec (~> 3.0)
|
|
96
|
-
rubocop (~> 1.
|
|
100
|
+
rubocop (~> 1.0)
|
|
97
101
|
vcr (~> 6.0)
|
|
98
102
|
webmock (~> 3.0)
|
|
99
103
|
yard (~> 0.9)
|
data/Makefile
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.PHONY: install test spec test-offline test-all lint lint-fix build release clean console help tag
|
|
2
|
+
|
|
3
|
+
help: ## Show this help
|
|
4
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
|
5
|
+
|
|
6
|
+
install: ## Install dependencies
|
|
7
|
+
bundle install
|
|
8
|
+
|
|
9
|
+
spec: ## Run RSpec tests
|
|
10
|
+
bundle exec rake spec
|
|
11
|
+
|
|
12
|
+
test-offline: ## Run offline tests (no network required)
|
|
13
|
+
bundle exec rake test_offline
|
|
14
|
+
|
|
15
|
+
test-all: ## Run all tests (spec + offline)
|
|
16
|
+
bundle exec rake test_all
|
|
17
|
+
|
|
18
|
+
test: spec ## Alias for spec
|
|
19
|
+
|
|
20
|
+
lint: ## Run RuboCop linter
|
|
21
|
+
bundle exec rubocop
|
|
22
|
+
|
|
23
|
+
lint-fix: ## Run RuboCop with auto-correct
|
|
24
|
+
bundle exec rubocop -A
|
|
25
|
+
|
|
26
|
+
build: ## Build the gem
|
|
27
|
+
bundle exec rake build
|
|
28
|
+
|
|
29
|
+
release: tag build ## Release: tag + build + push to RubyGems
|
|
30
|
+
bundle exec rake release_gem
|
|
31
|
+
|
|
32
|
+
clean: ## Remove built gem files
|
|
33
|
+
rm -f langfuse-ruby-*.gem
|
|
34
|
+
rm -rf pkg/
|
|
35
|
+
|
|
36
|
+
console: ## Start an IRB console with the gem loaded
|
|
37
|
+
bundle exec irb -r langfuse
|
|
38
|
+
|
|
39
|
+
tag: ## Create and push a version tag. Usage: make tag [VERSION=x.y.z]
|
|
40
|
+
@git fetch --tags; \
|
|
41
|
+
if [ -z "$(VERSION)" ]; then \
|
|
42
|
+
LATEST=$$(git tag -l 'v*' --sort=-v:refname | head -n1); \
|
|
43
|
+
if [ -z "$$LATEST" ]; then \
|
|
44
|
+
NEW_TAG="v0.0.1"; \
|
|
45
|
+
else \
|
|
46
|
+
MAJOR=$$(echo $$LATEST | sed 's/^v//' | cut -d. -f1); \
|
|
47
|
+
MINOR=$$(echo $$LATEST | sed 's/^v//' | cut -d. -f2); \
|
|
48
|
+
PATCH=$$(echo $$LATEST | sed 's/^v//' | cut -d. -f3); \
|
|
49
|
+
PATCH=$$((PATCH + 1)); \
|
|
50
|
+
NEW_TAG="v$$MAJOR.$$MINOR.$$PATCH"; \
|
|
51
|
+
fi; \
|
|
52
|
+
else \
|
|
53
|
+
NEW_TAG="v$(VERSION)"; \
|
|
54
|
+
LATEST=$$(git tag -l 'v*' --sort=-v:refname | head -n1); \
|
|
55
|
+
if [ "$$LATEST" = "$$NEW_TAG" ]; then \
|
|
56
|
+
echo "Tag $$NEW_TAG already exists on remote, deleting and re-pushing..."; \
|
|
57
|
+
git tag -d "$$NEW_TAG" 2>/dev/null || true; \
|
|
58
|
+
git push origin --delete "$$NEW_TAG" 2>/dev/null || true; \
|
|
59
|
+
elif git tag -l "$$NEW_TAG" | grep -q "$$NEW_TAG"; then \
|
|
60
|
+
echo "Error: Tag $$NEW_TAG exists but is not the latest tag (latest: $$LATEST). Aborting."; \
|
|
61
|
+
exit 1; \
|
|
62
|
+
fi; \
|
|
63
|
+
fi; \
|
|
64
|
+
NEW_VERSION=$$(echo $$NEW_TAG | sed 's/^v//'); \
|
|
65
|
+
echo "Updating version to $$NEW_VERSION ..."; \
|
|
66
|
+
sed -i '' "s/VERSION = '.*'/VERSION = '$$NEW_VERSION'/" lib/langfuse/version.rb; \
|
|
67
|
+
git add lib/langfuse/version.rb; \
|
|
68
|
+
git commit -m "Release $$NEW_TAG" --allow-empty; \
|
|
69
|
+
git tag "$$NEW_TAG"; \
|
|
70
|
+
echo "Pushing tag $$NEW_TAG ..."; \
|
|
71
|
+
git push origin HEAD; \
|
|
72
|
+
git push origin "$$NEW_TAG"; \
|
|
73
|
+
echo "Done! Tagged and pushed $$NEW_TAG"
|
data/README.md
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# Langfuse Ruby SDK
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/langfuse-ruby)
|
|
4
|
-
[](https://github.com/ai-firstly/langfuse-ruby/actions/workflows/ci.yml)
|
|
5
|
-
[](https://www.ruby-lang.org/)
|
|
6
|
-
[](LICENSE)
|
|
3
|
+
[](https://badge.fury.io/rb/langfuse-ruby) [](https://github.com/ai-firstly/langfuse-ruby/actions/workflows/ci.yml) [](https://www.ruby-lang.org/) [](LICENSE)
|
|
7
4
|
|
|
8
5
|
Ruby SDK for [Langfuse](https://langfuse.com) - the open-source LLM engineering platform. This SDK provides comprehensive tracing, prompt management, and evaluation capabilities for LLM applications.
|
|
9
6
|
|
|
@@ -69,7 +66,7 @@ trace = client.trace(
|
|
|
69
66
|
name: "chat-completion",
|
|
70
67
|
user_id: "user123",
|
|
71
68
|
session_id: "session456",
|
|
72
|
-
|
|
69
|
+
environment: "production"
|
|
73
70
|
)
|
|
74
71
|
|
|
75
72
|
# Add a generation (LLM call)
|
|
@@ -88,6 +85,80 @@ trace.update(output: 'Hello! How can I help you today?')
|
|
|
88
85
|
client.flush
|
|
89
86
|
```
|
|
90
87
|
|
|
88
|
+
## Simplified Usage (Recommended)
|
|
89
|
+
|
|
90
|
+
For most use cases, you can use the simplified class-level API with automatic flush:
|
|
91
|
+
|
|
92
|
+
### Block-based Tracing
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
require 'langfuse'
|
|
96
|
+
|
|
97
|
+
# Configure once
|
|
98
|
+
Langfuse.configure do |config|
|
|
99
|
+
config.public_key = ENV['LANGFUSE_PUBLIC_KEY']
|
|
100
|
+
config.secret_key = ENV['LANGFUSE_SECRET_KEY']
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Use block-based tracing - flush happens automatically!
|
|
104
|
+
Langfuse.trace("my-trace", user_id: "user-1", input: { message: "Hello" }) do |trace|
|
|
105
|
+
generation = trace.generation(
|
|
106
|
+
name: "openai-chat",
|
|
107
|
+
model: "gpt-4",
|
|
108
|
+
input: [{ role: "user", content: "Hello" }],
|
|
109
|
+
model_parameters: { temperature: 0.7 }
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Call your LLM
|
|
113
|
+
response = call_openai(...)
|
|
114
|
+
|
|
115
|
+
# Record the response
|
|
116
|
+
generation.end(output: response.content, usage: response.usage)
|
|
117
|
+
trace.update(output: response.content)
|
|
118
|
+
end # Automatic flush here!
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Get Prompts with Variables
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
# Get and compile a prompt in one call
|
|
125
|
+
prompt = Langfuse.get_prompt("greeting-prompt", variables: { name: "Alice" })
|
|
126
|
+
# => "Hello Alice! How can I help you today?"
|
|
127
|
+
|
|
128
|
+
# Get prompt without compilation
|
|
129
|
+
prompt_obj = Langfuse.get_prompt("greeting-prompt")
|
|
130
|
+
compiled = prompt_obj.compile(name: "Bob")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Graceful Degradation
|
|
134
|
+
|
|
135
|
+
The simplified API includes null objects that ensure your code continues working even if Langfuse is unavailable:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# If Langfuse fails, a NullTrace is used - your code still runs
|
|
139
|
+
Langfuse.trace("my-trace") do |trace|
|
|
140
|
+
# This works even if Langfuse is down
|
|
141
|
+
gen = trace.generation(name: "test", model: "gpt-4", input: "hello")
|
|
142
|
+
gen.end(output: "world")
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Other Class Methods
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
# Get the thread-safe singleton client
|
|
150
|
+
client = Langfuse.client
|
|
151
|
+
|
|
152
|
+
# Manual flush (when not using block-based tracing)
|
|
153
|
+
Langfuse.flush
|
|
154
|
+
|
|
155
|
+
# Shutdown the client
|
|
156
|
+
Langfuse.shutdown
|
|
157
|
+
|
|
158
|
+
# Reset the singleton (useful for testing)
|
|
159
|
+
Langfuse.reset!
|
|
160
|
+
```
|
|
161
|
+
|
|
91
162
|
### 3. Nested Spans
|
|
92
163
|
|
|
93
164
|
```ruby
|
|
@@ -173,6 +244,9 @@ event = client.event(
|
|
|
173
244
|
# Get a prompt
|
|
174
245
|
prompt = client.get_prompt("chat-prompt", version: 1)
|
|
175
246
|
|
|
247
|
+
# Prompt names with special characters are automatically URL-encoded
|
|
248
|
+
prompt = client.get_prompt("EXEMPLE/my-prompt") # Works correctly!
|
|
249
|
+
|
|
176
250
|
# Compile prompt with variables
|
|
177
251
|
compiled = prompt.compile(
|
|
178
252
|
user_name: "Alice",
|
|
@@ -183,6 +257,8 @@ puts compiled
|
|
|
183
257
|
# Output: "Hello Alice! Let's discuss machine learning today."
|
|
184
258
|
```
|
|
185
259
|
|
|
260
|
+
> **Note**: Prompt names containing special characters (like `/`, spaces, `?`, etc.) are automatically URL-encoded. You don't need to manually encode them.
|
|
261
|
+
|
|
186
262
|
### Create Prompts
|
|
187
263
|
|
|
188
264
|
```ruby
|
|
@@ -493,6 +569,14 @@ Check out the `examples/` directory for more comprehensive examples:
|
|
|
493
569
|
- [Evaluation Pipeline](examples/evaluation_pipeline.rb)
|
|
494
570
|
- [Rails Integration](examples/rails_integration.rb)
|
|
495
571
|
|
|
572
|
+
## Documentation
|
|
573
|
+
|
|
574
|
+
For more detailed information, please refer to the [documentation](docs/README.md).
|
|
575
|
+
|
|
576
|
+
- [Publishing Guide](docs/PUBLISH_GUIDE.md)
|
|
577
|
+
- [Release Checklist](docs/RELEASE_CHECKLIST.md)
|
|
578
|
+
- [Examples](examples/)
|
|
579
|
+
|
|
496
580
|
## Development
|
|
497
581
|
|
|
498
582
|
After checking out the repo, run:
|
|
@@ -525,7 +609,6 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
525
609
|
|
|
526
610
|
## Links
|
|
527
611
|
|
|
528
|
-
- [Langfuse Documentation](https://
|
|
529
|
-
- [
|
|
530
|
-
- [
|
|
531
|
-
- [Ruby SDK Documentation](https://rubydoc.info/gems/langfuse-ruby)
|
|
612
|
+
- [Langfuse Ruby SDK Documentation](https://rubydoc.info/gems/langfuse-ruby)
|
|
613
|
+
- [RubyGems](https://rubygems.org/gems/langfuse-ruby)
|
|
614
|
+
- [Langfuse Documentation](https://langfuse.com/docs)
|
data/Rakefile
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'bundler/gem_tasks'
|
|
2
4
|
require 'rspec/core/rake_task'
|
|
3
5
|
require_relative 'lib/langfuse/version'
|
|
@@ -9,13 +11,13 @@ task default: :spec
|
|
|
9
11
|
# Custom release task
|
|
10
12
|
desc 'Release gem to RubyGems'
|
|
11
13
|
task release_gem: [:build] do
|
|
12
|
-
sh "gem push
|
|
14
|
+
sh "gem push langfuse-ruby-#{Langfuse::VERSION}.gem"
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
# Offline test task
|
|
16
18
|
desc 'Run offline tests'
|
|
17
19
|
task :test_offline do
|
|
18
|
-
sh 'ruby test_offline.rb'
|
|
20
|
+
sh 'ruby scripts/test_offline.rb'
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# Complete test suite
|
data/docs/FINAL_SUMMARY.md
CHANGED
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
### 核心功能
|
|
10
10
|
- ✅ **完整的追踪系统** - traces, spans, generations
|
|
11
|
-
- ✅
|
|
11
|
+
- ✅ **简化 API** - 类级别方法,block-based tracing,自动 flush
|
|
12
|
+
- ✅ **提示管理** - 版本控制、缓存、变量编译、重试支持
|
|
12
13
|
- ✅ **评估系统** - 6种内置评估器 + 自定义评分
|
|
14
|
+
- ✅ **优雅降级** - NullTrace/NullGeneration 空对象模式
|
|
13
15
|
- ✅ **客户端管理** - HTTP客户端、认证、重试机制
|
|
14
16
|
- ✅ **异步处理** - 事件队列、后台线程
|
|
15
17
|
- ✅ **错误处理** - 完整的错误分类和处理
|
|
16
|
-
- ✅ **工具类** - ID生成、时间戳、数据转换
|
|
17
18
|
|
|
18
19
|
### 技术规格
|
|
19
|
-
- **Ruby版本**: >=
|
|
20
|
+
- **Ruby版本**: >= 3.1.0
|
|
20
21
|
- **依赖项**: Faraday, concurrent-ruby, json
|
|
21
22
|
- **测试**: RSpec + 离线测试 (23个测试全部通过)
|
|
22
23
|
- **文档**: 完整的README、API文档、示例代码
|
|
@@ -83,7 +84,7 @@ vim langfuse.gemspec
|
|
|
83
84
|
# 初始化 Git 仓库
|
|
84
85
|
git init
|
|
85
86
|
git add .
|
|
86
|
-
git commit -m "Initial commit: Langfuse Ruby SDK
|
|
87
|
+
git commit -m "Initial commit: Langfuse Ruby SDK v#{Langfuse::VERSION}"
|
|
87
88
|
|
|
88
89
|
# 创建 GitHub 仓库并推送
|
|
89
90
|
git remote add origin https://github.com/您的用户名/langfuse-ruby.git
|
|
@@ -97,16 +98,16 @@ git push -u origin main
|
|
|
97
98
|
|
|
98
99
|
# 方法 2: 手动发布
|
|
99
100
|
gem build langfuse.gemspec
|
|
100
|
-
gem push langfuse-
|
|
101
|
+
gem push langfuse-ruby-#{Langfuse::VERSION}.gem
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
### 3. 验证发布
|
|
104
105
|
```bash
|
|
105
106
|
# 运行验证脚本
|
|
106
|
-
ruby scripts/
|
|
107
|
+
ruby scripts/test_offline.rb
|
|
107
108
|
|
|
108
109
|
# 手动验证
|
|
109
|
-
gem install langfuse
|
|
110
|
+
gem install langfuse-ruby
|
|
110
111
|
ruby -e "require 'langfuse'; puts Langfuse::VERSION"
|
|
111
112
|
```
|
|
112
113
|
|
|
@@ -120,9 +121,9 @@ ruby -e "require 'langfuse'; puts Langfuse::VERSION"
|
|
|
120
121
|
- **文档文件**: 5个
|
|
121
122
|
|
|
122
123
|
### 测试覆盖率
|
|
123
|
-
- **RSpec 测试**:
|
|
124
|
-
-
|
|
125
|
-
- **总测试数**:
|
|
124
|
+
- **RSpec 测试**: 84个 (100% 通过)
|
|
125
|
+
- **新增测试**: 28个 (简化 API + 空对象)
|
|
126
|
+
- **总测试数**: 84个 (100% 通过)
|
|
126
127
|
|
|
127
128
|
### 功能完整性
|
|
128
129
|
- **与 Python SDK 对比**: 100% 功能对等
|