langfuse-rb 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -51
- data/README.md +33 -20
- data/lib/langfuse/api_client.rb +142 -24
- data/lib/langfuse/client.rb +169 -10
- data/lib/langfuse/config.rb +69 -0
- data/lib/langfuse/prompt_cache.rb +119 -8
- data/lib/langfuse/propagation.rb +5 -16
- data/lib/langfuse/rails_cache_adapter.rb +115 -55
- data/lib/langfuse/stale_while_revalidate.rb +262 -0
- data/lib/langfuse/version.rb +1 -1
- metadata +37 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60270020fc35460c5e29381351bbca35f1cdfe8b7dfe05eca43a0fb20dc06b7b
|
|
4
|
+
data.tar.gz: 4951c9b1546de4c9d00bb3b3be4c5325c8edf4d5bf6f5ecab7d9520ab052451b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84fa1fc6ea91bda9ddcaa32dd43cb457439e0e4cdee06e3f3df88aae9c54c5b10abd16cb120f234da28733ba6497c466d0ece7888410d7b2711815e13f3956ef
|
|
7
|
+
data.tar.gz: 785bd5801a8c6b0ecd7c94f43083bdd6f12463bb918fa5f4a6faee32924a87a5058cf9700b20984fa9e8964cb3070975235771ddfddabca115cb33538020b065
|
data/CHANGELOG.md
CHANGED
|
@@ -7,54 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [
|
|
11
|
-
|
|
12
|
-
###
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
#### Developer Experience
|
|
48
|
-
- Comprehensive error handling with specific error classes
|
|
49
|
-
- HTTP client with automatic retry logic and exponential backoff
|
|
50
|
-
- Circuit breaker pattern for resilience (via Stoplight)
|
|
51
|
-
- 99.7% test coverage with 339 comprehensive test cases
|
|
52
|
-
- Extensive documentation with guides for Rails, tracing, and migration
|
|
53
|
-
|
|
54
|
-
#### Dependencies
|
|
55
|
-
- Ruby >= 3.2.0
|
|
56
|
-
- No Rails dependency (works with any Ruby project)
|
|
57
|
-
- Minimal runtime dependencies (Faraday, Mustache, OpenTelemetry)
|
|
58
|
-
|
|
59
|
-
[Unreleased]: https://github.com/langfuse/langfuse-ruby/compare/v1.0.0...HEAD
|
|
60
|
-
[1.0.0]: https://github.com/langfuse/langfuse-ruby/releases/tag/v1.0.0
|
|
10
|
+
## [0.3.0] - 2026-01-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Stale-while-revalidate (SWR) cache strategy for improved performance (#35)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- OpenTelemetry Baggage API method signatures for context propagation (#39)
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Relaxed Faraday version constraint for better compatibility with older projects (#37)
|
|
20
|
+
|
|
21
|
+
## [0.2.0] - 2025-12-19
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Prompt creation and update methods (`create_prompt`, `update_prompt`) (#36)
|
|
25
|
+
|
|
26
|
+
## [0.1.0] - 2025-12-01
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- Observe API with context propagation and scoring (#31)
|
|
30
|
+
- W3C TraceContext propagator for distributed tracing (#1)
|
|
31
|
+
- Ruby 3.4 support (#3)
|
|
32
|
+
- OpenTelemetry-based tracing with OTLP export
|
|
33
|
+
- Distributed caching with Rails.cache backend and stampede protection
|
|
34
|
+
- Prompt management (text and chat) with Mustache templating
|
|
35
|
+
- In-memory caching with TTL and LRU eviction
|
|
36
|
+
- Fallback prompt support
|
|
37
|
+
- Global configuration pattern with `Langfuse.configure`
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- Migrated from legacy ingestion API to OTLP endpoint
|
|
41
|
+
- Removed `tracing_enabled` configuration flag (#2)
|
|
42
|
+
|
|
43
|
+
[Unreleased]: https://github.com/simplepractice/langfuse-rb/compare/v0.3.0...HEAD
|
|
44
|
+
[0.3.0]: https://github.com/simplepractice/langfuse-rb/compare/v0.2.0...v0.3.0
|
|
45
|
+
[0.2.0]: https://github.com/simplepractice/langfuse-rb/compare/v0.1.0...v0.2.0
|
|
46
|
+
[0.1.0]: https://github.com/simplepractice/langfuse-rb/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -2,49 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
# Langfuse Ruby SDK
|
|
4
4
|
|
|
5
|
-
[](https://badge.fury.io/rb/langfuse)
|
|
5
|
+
[](https://badge.fury.io/rb/langfuse-rb)
|
|
6
6
|
[](https://www.ruby-lang.org/en/)
|
|
7
7
|
[](coverage)
|
|
8
8
|
|
|
9
9
|
> Ruby SDK for [Langfuse](https://langfuse.com) - Open-source LLM observability and prompt management.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<br>
|
|
12
|
+
|
|
13
|
+
### Features
|
|
12
14
|
|
|
13
15
|
- 🎯 **Prompt Management** - Centralized prompt versioning with Mustache templating
|
|
14
16
|
- 📊 **LLM Tracing** - Zero-boilerplate observability built on OpenTelemetry
|
|
15
|
-
- ⚡ **Performance** - In-memory or Redis-backed caching with stampede protection
|
|
17
|
+
- ⚡ **Performance** - In-memory or Redis-backed caching with stampede protection, both supporting stale-while-revalidate cache strategy
|
|
16
18
|
- 💬 **Chat & Text Prompts** - First-class support for both formats
|
|
17
19
|
- 🔄 **Automatic Retries** - Built-in exponential backoff for resilient API calls
|
|
18
20
|
- 🛡️ **Fallback Support** - Graceful degradation when API unavailable
|
|
19
21
|
- 🚀 **Rails-Friendly** - Global configuration pattern, works with any Ruby project
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
<br>
|
|
24
|
+
|
|
25
|
+
### Installation
|
|
22
26
|
|
|
23
27
|
```ruby
|
|
24
|
-
# Gemfile
|
|
28
|
+
# Add to Gemfile & bundle install
|
|
25
29
|
gem 'langfuse-rb'
|
|
26
30
|
```
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
bundle install
|
|
30
|
-
```
|
|
32
|
+
<br>
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
### Quick Start
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
> Configure once at startup
|
|
35
37
|
|
|
36
38
|
```ruby
|
|
37
39
|
# config/initializers/langfuse.rb (Rails)
|
|
38
|
-
#
|
|
40
|
+
# Or at the top of your script
|
|
39
41
|
Langfuse.configure do |config|
|
|
40
42
|
config.public_key = ENV['LANGFUSE_PUBLIC_KEY']
|
|
41
43
|
config.secret_key = ENV['LANGFUSE_SECRET_KEY']
|
|
42
44
|
# Optional: for self-hosted instances
|
|
43
45
|
config.base_url = ENV.fetch('LANGFUSE_BASE_URL', 'https://cloud.langfuse.com')
|
|
46
|
+
|
|
47
|
+
# Optional: Enable stale-while-revalidate for best performance
|
|
48
|
+
config.cache_backend = :rails # or :memory
|
|
49
|
+
config.cache_stale_while_revalidate = true
|
|
44
50
|
end
|
|
45
51
|
```
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
> Fetch and use a prompt
|
|
48
54
|
|
|
49
55
|
```ruby
|
|
50
56
|
prompt = Langfuse.client.get_prompt("greeting")
|
|
@@ -52,7 +58,7 @@ message = prompt.compile(name: "Alice")
|
|
|
52
58
|
# => "Hello Alice!"
|
|
53
59
|
```
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
> Trace an LLM call
|
|
56
62
|
|
|
57
63
|
```ruby
|
|
58
64
|
Langfuse.observe("chat-completion", as_type: :generation) do |gen|
|
|
@@ -74,28 +80,35 @@ Langfuse.observe("chat-completion", as_type: :generation) do |gen|
|
|
|
74
80
|
end
|
|
75
81
|
```
|
|
76
82
|
|
|
77
|
-
> [!IMPORTANT]
|
|
83
|
+
> [!IMPORTANT]
|
|
78
84
|
> For complete reference see [docs](./docs/) section.
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
<br>
|
|
87
|
+
|
|
88
|
+
### Requirements
|
|
81
89
|
|
|
82
90
|
- Ruby >= 3.2.0
|
|
83
91
|
- No Rails dependency (works with any Ruby project)
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
<br>
|
|
94
|
+
|
|
95
|
+
### Contributing
|
|
86
96
|
|
|
87
97
|
We welcome contributions! Please:
|
|
88
98
|
|
|
89
|
-
1. Check existing [issues](https://github.com/simplepractice/langfuse-rb/issues)
|
|
99
|
+
1. Check existing [issues](https://github.com/simplepractice/langfuse-rb/issues)
|
|
90
100
|
2. Open an issue to discuss your idea
|
|
91
101
|
3. Fork the repo and create a feature branch
|
|
92
102
|
4. Write tests (maintain >95% coverage)
|
|
93
103
|
5. Ensure `bundle exec rspec` and `bundle exec rubocop` pass
|
|
94
104
|
6. Submit a pull request
|
|
95
105
|
|
|
96
|
-
|
|
106
|
+
> [!TIP]
|
|
107
|
+
> See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
|
|
108
|
+
|
|
109
|
+
<br>
|
|
97
110
|
|
|
98
|
-
|
|
111
|
+
### Support
|
|
99
112
|
|
|
100
113
|
- **[GitHub Issues](https://github.com/simplepractice/langfuse-rb/issues)** - Bug reports and feature requests
|
|
101
114
|
- **[Langfuse Documentation](https://langfuse.com/docs)** - Platform documentation
|
|
@@ -103,4 +116,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
|
|
|
103
116
|
|
|
104
117
|
## License
|
|
105
118
|
|
|
106
|
-
[MIT](LICENSE)
|
|
119
|
+
[MIT](LICENSE)
|
data/lib/langfuse/api_client.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "faraday"
|
|
|
4
4
|
require "faraday/retry"
|
|
5
5
|
require "base64"
|
|
6
6
|
require "json"
|
|
7
|
+
require "uri"
|
|
7
8
|
|
|
8
9
|
module Langfuse
|
|
9
10
|
# HTTP client for Langfuse API
|
|
@@ -20,8 +21,7 @@ module Langfuse
|
|
|
20
21
|
# logger: Logger.new($stdout)
|
|
21
22
|
# )
|
|
22
23
|
#
|
|
23
|
-
# rubocop:disable Metrics/ClassLength
|
|
24
|
-
class ApiClient
|
|
24
|
+
class ApiClient # rubocop:disable Metrics/ClassLength
|
|
25
25
|
attr_reader :public_key, :secret_key, :base_url, :timeout, :logger, :cache
|
|
26
26
|
|
|
27
27
|
# Initialize a new API client
|
|
@@ -106,26 +106,88 @@ module Langfuse
|
|
|
106
106
|
# @raise [ApiError] for other API errors
|
|
107
107
|
def get_prompt(name, version: nil, label: nil)
|
|
108
108
|
raise ArgumentError, "Cannot specify both version and label" if version && label
|
|
109
|
+
return fetch_prompt_from_api(name, version: version, label: label) if cache.nil?
|
|
109
110
|
|
|
110
111
|
cache_key = PromptCache.build_key(name, version: version, label: label)
|
|
112
|
+
fetch_with_appropriate_caching_strategy(cache_key, name, version, label)
|
|
113
|
+
end
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
# Create a new prompt (or new version if prompt with same name exists)
|
|
116
|
+
#
|
|
117
|
+
# @param name [String] The prompt name
|
|
118
|
+
# @param prompt [String, Array<Hash>] The prompt content
|
|
119
|
+
# @param type [String] Prompt type ("text" or "chat")
|
|
120
|
+
# @param config [Hash] Optional configuration (model params, etc.)
|
|
121
|
+
# @param labels [Array<String>] Optional labels (e.g., ["production"])
|
|
122
|
+
# @param tags [Array<String>] Optional tags
|
|
123
|
+
# @param commit_message [String, nil] Optional commit message
|
|
124
|
+
# @return [Hash] The created prompt data
|
|
125
|
+
# @raise [UnauthorizedError] if authentication fails
|
|
126
|
+
# @raise [ApiError] for other API errors
|
|
127
|
+
#
|
|
128
|
+
# @example Create a text prompt
|
|
129
|
+
# api_client.create_prompt(
|
|
130
|
+
# name: "greeting",
|
|
131
|
+
# prompt: "Hello {{name}}!",
|
|
132
|
+
# type: "text",
|
|
133
|
+
# labels: ["production"]
|
|
134
|
+
# )
|
|
135
|
+
#
|
|
136
|
+
# rubocop:disable Metrics/ParameterLists
|
|
137
|
+
def create_prompt(name:, prompt:, type:, config: {}, labels: [], tags: [], commit_message: nil)
|
|
138
|
+
path = "/api/public/v2/prompts"
|
|
139
|
+
payload = {
|
|
140
|
+
name: name,
|
|
141
|
+
prompt: prompt,
|
|
142
|
+
type: type,
|
|
143
|
+
config: config,
|
|
144
|
+
labels: labels,
|
|
145
|
+
tags: tags
|
|
146
|
+
}
|
|
147
|
+
payload[:commitMessage] = commit_message if commit_message
|
|
148
|
+
|
|
149
|
+
response = connection.post(path, payload)
|
|
150
|
+
handle_response(response)
|
|
151
|
+
rescue Faraday::RetriableResponse => e
|
|
152
|
+
logger.error("Faraday error: Retries exhausted - #{e.response.status}")
|
|
153
|
+
handle_response(e.response)
|
|
154
|
+
rescue Faraday::Error => e
|
|
155
|
+
logger.error("Faraday error: #{e.message}")
|
|
156
|
+
raise ApiError, "HTTP request failed: #{e.message}"
|
|
157
|
+
end
|
|
158
|
+
# rubocop:enable Metrics/ParameterLists
|
|
159
|
+
|
|
160
|
+
# Update labels for an existing prompt version
|
|
161
|
+
#
|
|
162
|
+
# @param name [String] The prompt name
|
|
163
|
+
# @param version [Integer] The version number to update
|
|
164
|
+
# @param labels [Array<String>] New labels (replaces existing). Required.
|
|
165
|
+
# @return [Hash] The updated prompt data
|
|
166
|
+
# @raise [ArgumentError] if labels is not an array
|
|
167
|
+
# @raise [NotFoundError] if the prompt is not found
|
|
168
|
+
# @raise [UnauthorizedError] if authentication fails
|
|
169
|
+
# @raise [ApiError] for other API errors
|
|
170
|
+
#
|
|
171
|
+
# @example Promote a prompt to production
|
|
172
|
+
# api_client.update_prompt(
|
|
173
|
+
# name: "greeting",
|
|
174
|
+
# version: 2,
|
|
175
|
+
# labels: ["production"]
|
|
176
|
+
# )
|
|
177
|
+
def update_prompt(name:, version:, labels:)
|
|
178
|
+
raise ArgumentError, "labels must be an array" unless labels.is_a?(Array)
|
|
179
|
+
|
|
180
|
+
path = "/api/public/v2/prompts/#{URI.encode_uri_component(name)}/versions/#{version}"
|
|
181
|
+
payload = { newLabels: labels }
|
|
182
|
+
|
|
183
|
+
response = connection.patch(path, payload)
|
|
184
|
+
handle_response(response)
|
|
185
|
+
rescue Faraday::RetriableResponse => e
|
|
186
|
+
logger.error("Faraday error: Retries exhausted - #{e.response.status}")
|
|
187
|
+
handle_response(e.response)
|
|
188
|
+
rescue Faraday::Error => e
|
|
189
|
+
logger.error("Faraday error: #{e.message}")
|
|
190
|
+
raise ApiError, "HTTP request failed: #{e.message}"
|
|
129
191
|
end
|
|
130
192
|
|
|
131
193
|
# Send a batch of events to the Langfuse ingestion API
|
|
@@ -167,8 +229,63 @@ module Langfuse
|
|
|
167
229
|
raise ApiError, "Batch send failed: #{e.message}"
|
|
168
230
|
end
|
|
169
231
|
|
|
232
|
+
def shutdown
|
|
233
|
+
cache.shutdown if cache.respond_to?(:shutdown)
|
|
234
|
+
end
|
|
235
|
+
|
|
170
236
|
private
|
|
171
237
|
|
|
238
|
+
# Fetch prompt using the most appropriate caching strategy available
|
|
239
|
+
#
|
|
240
|
+
# @param cache_key [String] The cache key for this prompt
|
|
241
|
+
# @param name [String] The name of the prompt
|
|
242
|
+
# @param version [Integer, nil] Optional specific version number
|
|
243
|
+
# @param label [String, nil] Optional label
|
|
244
|
+
# @return [Hash] The prompt data
|
|
245
|
+
def fetch_with_appropriate_caching_strategy(cache_key, name, version, label)
|
|
246
|
+
if swr_cache_available?
|
|
247
|
+
fetch_with_swr_cache(cache_key, name, version, label)
|
|
248
|
+
elsif distributed_cache_available?
|
|
249
|
+
fetch_with_distributed_cache(cache_key, name, version, label)
|
|
250
|
+
else
|
|
251
|
+
fetch_with_simple_cache(cache_key, name, version, label)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Check if SWR cache is available
|
|
256
|
+
def swr_cache_available?
|
|
257
|
+
cache.respond_to?(:swr_enabled?) && cache.swr_enabled?
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Check if distributed cache is available
|
|
261
|
+
def distributed_cache_available?
|
|
262
|
+
cache.respond_to?(:fetch_with_lock)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Fetch with SWR cache
|
|
266
|
+
def fetch_with_swr_cache(cache_key, name, version, label)
|
|
267
|
+
cache.fetch_with_stale_while_revalidate(cache_key) do
|
|
268
|
+
fetch_prompt_from_api(name, version: version, label: label)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Fetch with distributed cache (Rails.cache with stampede protection)
|
|
273
|
+
def fetch_with_distributed_cache(cache_key, name, version, label)
|
|
274
|
+
cache.fetch_with_lock(cache_key) do
|
|
275
|
+
fetch_prompt_from_api(name, version: version, label: label)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Fetch with simple cache (in-memory cache)
|
|
280
|
+
def fetch_with_simple_cache(cache_key, name, version, label)
|
|
281
|
+
cached_data = cache.get(cache_key)
|
|
282
|
+
return cached_data if cached_data
|
|
283
|
+
|
|
284
|
+
prompt_data = fetch_prompt_from_api(name, version: version, label: label)
|
|
285
|
+
cache.set(cache_key, prompt_data)
|
|
286
|
+
prompt_data
|
|
287
|
+
end
|
|
288
|
+
|
|
172
289
|
# Fetch a prompt from the API (without caching)
|
|
173
290
|
#
|
|
174
291
|
# @param name [String] The name of the prompt
|
|
@@ -180,7 +297,7 @@ module Langfuse
|
|
|
180
297
|
# @raise [ApiError] for other API errors
|
|
181
298
|
def fetch_prompt_from_api(name, version: nil, label: nil)
|
|
182
299
|
params = build_prompt_params(version: version, label: label)
|
|
183
|
-
path = "/api/public/v2/prompts/#{name}"
|
|
300
|
+
path = "/api/public/v2/prompts/#{URI.encode_uri_component(name)}"
|
|
184
301
|
|
|
185
302
|
response = connection.get(path, params)
|
|
186
303
|
handle_response(response)
|
|
@@ -215,7 +332,9 @@ module Langfuse
|
|
|
215
332
|
# Retries transient errors with exponential backoff:
|
|
216
333
|
# - Max 2 retries (3 total attempts)
|
|
217
334
|
# - Exponential backoff (0.05s * 2^retry_count)
|
|
218
|
-
# - Retries GET
|
|
335
|
+
# - Retries GET and PATCH requests (idempotent operations)
|
|
336
|
+
# - Retries POST requests to batch endpoint (idempotent due to event UUIDs)
|
|
337
|
+
# - Note: POST to create_prompt is NOT idempotent; retries may create duplicate versions
|
|
219
338
|
# - Retries on: 429 (rate limit), 503 (service unavailable), 504 (gateway timeout)
|
|
220
339
|
# - Does NOT retry on: 4xx errors (except 429), 5xx errors (except 503, 504)
|
|
221
340
|
#
|
|
@@ -225,7 +344,7 @@ module Langfuse
|
|
|
225
344
|
max: 2,
|
|
226
345
|
interval: 0.05,
|
|
227
346
|
backoff_factor: 2,
|
|
228
|
-
methods: %i[get post],
|
|
347
|
+
methods: %i[get post patch],
|
|
229
348
|
retry_statuses: [429, 503, 504],
|
|
230
349
|
exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed]
|
|
231
350
|
}
|
|
@@ -278,7 +397,7 @@ module Langfuse
|
|
|
278
397
|
# @raise [ApiError] for other error statuses
|
|
279
398
|
def handle_response(response)
|
|
280
399
|
case response.status
|
|
281
|
-
when 200
|
|
400
|
+
when 200, 201
|
|
282
401
|
response.body
|
|
283
402
|
when 401
|
|
284
403
|
raise UnauthorizedError, "Authentication failed. Check your API keys."
|
|
@@ -327,4 +446,3 @@ module Langfuse
|
|
|
327
446
|
end
|
|
328
447
|
end
|
|
329
448
|
end
|
|
330
|
-
# rubocop:enable Metrics/ClassLength
|