langfuse-ruby 0.1.3 → 0.1.5
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 +53 -0
- data/CHANGELOG.md +45 -4
- data/Gemfile.lock +7 -3
- data/README.md +11 -2
- data/docs/URL_ENCODING_FIX.md +164 -0
- data/examples/basic_tracing.rb +1 -1
- data/examples/url_encoding_demo.rb +57 -0
- data/langfuse-ruby.gemspec +7 -4
- data/lib/langfuse/client.rb +184 -4
- data/lib/langfuse/event.rb +21 -5
- data/lib/langfuse/generation.rb +155 -5
- data/lib/langfuse/observation_types.rb +61 -0
- data/lib/langfuse/span.rb +162 -7
- data/lib/langfuse/trace.rb +185 -1
- data/lib/langfuse/utils.rb +5 -0
- data/lib/langfuse/version.rb +1 -1
- data/lib/langfuse.rb +1 -0
- data/test_offline.rb +33 -7
- metadata +39 -9
data/lib/langfuse/span.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module Langfuse
|
|
2
2
|
class Span
|
|
3
3
|
attr_reader :id, :trace_id, :name, :start_time, :end_time, :input, :output,
|
|
4
|
-
:metadata, :level, :status_message, :parent_observation_id, :version,
|
|
4
|
+
:metadata, :level, :status_message, :parent_observation_id, :version,
|
|
5
|
+
:as_type, :client
|
|
5
6
|
|
|
6
7
|
def initialize(client:, trace_id:, id: nil, name: nil, start_time: nil, end_time: nil,
|
|
7
8
|
input: nil, output: nil, metadata: nil, level: nil, status_message: nil,
|
|
8
|
-
parent_observation_id: nil, version: nil, **kwargs)
|
|
9
|
+
parent_observation_id: nil, version: nil, as_type: nil, **kwargs)
|
|
9
10
|
@client = client
|
|
10
11
|
@id = id || Utils.generate_id
|
|
11
12
|
@trace_id = trace_id
|
|
@@ -19,6 +20,7 @@ module Langfuse
|
|
|
19
20
|
@status_message = status_message
|
|
20
21
|
@parent_observation_id = parent_observation_id
|
|
21
22
|
@version = version
|
|
23
|
+
@as_type = validate_as_type(as_type)
|
|
22
24
|
@kwargs = kwargs
|
|
23
25
|
|
|
24
26
|
# Create the span
|
|
@@ -50,8 +52,9 @@ module Langfuse
|
|
|
50
52
|
self
|
|
51
53
|
end
|
|
52
54
|
|
|
55
|
+
# Create a child span
|
|
53
56
|
def span(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
54
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
57
|
+
metadata: nil, level: nil, status_message: nil, version: nil, as_type: nil, **kwargs)
|
|
55
58
|
@client.span(
|
|
56
59
|
trace_id: @trace_id,
|
|
57
60
|
name: name,
|
|
@@ -64,10 +67,12 @@ module Langfuse
|
|
|
64
67
|
status_message: status_message,
|
|
65
68
|
parent_observation_id: @id,
|
|
66
69
|
version: version,
|
|
70
|
+
as_type: as_type,
|
|
67
71
|
**kwargs
|
|
68
72
|
)
|
|
69
73
|
end
|
|
70
74
|
|
|
75
|
+
# Create a child generation
|
|
71
76
|
def generation(name: nil, start_time: nil, end_time: nil, completion_start_time: nil,
|
|
72
77
|
model: nil, model_parameters: nil, input: nil, output: nil, usage: nil,
|
|
73
78
|
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
@@ -91,6 +96,7 @@ module Langfuse
|
|
|
91
96
|
)
|
|
92
97
|
end
|
|
93
98
|
|
|
99
|
+
# Create a child event
|
|
94
100
|
def event(name:, start_time: nil, input: nil, output: nil, metadata: nil,
|
|
95
101
|
level: nil, status_message: nil, version: nil, **kwargs)
|
|
96
102
|
@client.event(
|
|
@@ -108,6 +114,138 @@ module Langfuse
|
|
|
108
114
|
)
|
|
109
115
|
end
|
|
110
116
|
|
|
117
|
+
# Convenience methods for enhanced observation types
|
|
118
|
+
|
|
119
|
+
# Create a child agent observation
|
|
120
|
+
def agent(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
121
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
122
|
+
span(
|
|
123
|
+
name: name,
|
|
124
|
+
start_time: start_time,
|
|
125
|
+
end_time: end_time,
|
|
126
|
+
input: input,
|
|
127
|
+
output: output,
|
|
128
|
+
metadata: metadata,
|
|
129
|
+
level: level,
|
|
130
|
+
status_message: status_message,
|
|
131
|
+
version: version,
|
|
132
|
+
as_type: ObservationType::AGENT,
|
|
133
|
+
**kwargs
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Create a child tool observation
|
|
138
|
+
def tool(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
139
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
140
|
+
span(
|
|
141
|
+
name: name,
|
|
142
|
+
start_time: start_time,
|
|
143
|
+
end_time: end_time,
|
|
144
|
+
input: input,
|
|
145
|
+
output: output,
|
|
146
|
+
metadata: metadata,
|
|
147
|
+
level: level,
|
|
148
|
+
status_message: status_message,
|
|
149
|
+
version: version,
|
|
150
|
+
as_type: ObservationType::TOOL,
|
|
151
|
+
**kwargs
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Create a child chain observation
|
|
156
|
+
def chain(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
157
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
158
|
+
span(
|
|
159
|
+
name: name,
|
|
160
|
+
start_time: start_time,
|
|
161
|
+
end_time: end_time,
|
|
162
|
+
input: input,
|
|
163
|
+
output: output,
|
|
164
|
+
metadata: metadata,
|
|
165
|
+
level: level,
|
|
166
|
+
status_message: status_message,
|
|
167
|
+
version: version,
|
|
168
|
+
as_type: ObservationType::CHAIN,
|
|
169
|
+
**kwargs
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Create a child retriever observation
|
|
174
|
+
def retriever(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
175
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
176
|
+
span(
|
|
177
|
+
name: name,
|
|
178
|
+
start_time: start_time,
|
|
179
|
+
end_time: end_time,
|
|
180
|
+
input: input,
|
|
181
|
+
output: output,
|
|
182
|
+
metadata: metadata,
|
|
183
|
+
level: level,
|
|
184
|
+
status_message: status_message,
|
|
185
|
+
version: version,
|
|
186
|
+
as_type: ObservationType::RETRIEVER,
|
|
187
|
+
**kwargs
|
|
188
|
+
)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Create a child embedding observation
|
|
192
|
+
def embedding(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
193
|
+
model: nil, usage: nil, metadata: nil, level: nil, status_message: nil,
|
|
194
|
+
version: nil, **kwargs)
|
|
195
|
+
merged_metadata = (metadata || {}).merge(
|
|
196
|
+
{ model: model, usage: usage }.compact
|
|
197
|
+
)
|
|
198
|
+
span(
|
|
199
|
+
name: name,
|
|
200
|
+
start_time: start_time,
|
|
201
|
+
end_time: end_time,
|
|
202
|
+
input: input,
|
|
203
|
+
output: output,
|
|
204
|
+
metadata: merged_metadata.empty? ? nil : merged_metadata,
|
|
205
|
+
level: level,
|
|
206
|
+
status_message: status_message,
|
|
207
|
+
version: version,
|
|
208
|
+
as_type: ObservationType::EMBEDDING,
|
|
209
|
+
**kwargs
|
|
210
|
+
)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Create a child evaluator observation
|
|
214
|
+
def evaluator(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
215
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
216
|
+
span(
|
|
217
|
+
name: name,
|
|
218
|
+
start_time: start_time,
|
|
219
|
+
end_time: end_time,
|
|
220
|
+
input: input,
|
|
221
|
+
output: output,
|
|
222
|
+
metadata: metadata,
|
|
223
|
+
level: level,
|
|
224
|
+
status_message: status_message,
|
|
225
|
+
version: version,
|
|
226
|
+
as_type: ObservationType::EVALUATOR,
|
|
227
|
+
**kwargs
|
|
228
|
+
)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Create a child guardrail observation
|
|
232
|
+
def guardrail(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
233
|
+
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
234
|
+
span(
|
|
235
|
+
name: name,
|
|
236
|
+
start_time: start_time,
|
|
237
|
+
end_time: end_time,
|
|
238
|
+
input: input,
|
|
239
|
+
output: output,
|
|
240
|
+
metadata: metadata,
|
|
241
|
+
level: level,
|
|
242
|
+
status_message: status_message,
|
|
243
|
+
version: version,
|
|
244
|
+
as_type: ObservationType::GUARDRAIL,
|
|
245
|
+
**kwargs
|
|
246
|
+
)
|
|
247
|
+
end
|
|
248
|
+
|
|
111
249
|
def score(name:, value:, data_type: nil, comment: nil, **kwargs)
|
|
112
250
|
@client.score(
|
|
113
251
|
observation_id: @id,
|
|
@@ -124,7 +262,7 @@ module Langfuse
|
|
|
124
262
|
end
|
|
125
263
|
|
|
126
264
|
def to_dict
|
|
127
|
-
{
|
|
265
|
+
data = {
|
|
128
266
|
id: @id,
|
|
129
267
|
trace_id: @trace_id,
|
|
130
268
|
name: @name,
|
|
@@ -137,11 +275,24 @@ module Langfuse
|
|
|
137
275
|
status_message: @status_message,
|
|
138
276
|
parent_observation_id: @parent_observation_id,
|
|
139
277
|
version: @version
|
|
140
|
-
}
|
|
278
|
+
}
|
|
279
|
+
data[:type] = @as_type if @as_type
|
|
280
|
+
data.merge(@kwargs).compact
|
|
141
281
|
end
|
|
142
282
|
|
|
143
283
|
private
|
|
144
284
|
|
|
285
|
+
def validate_as_type(type)
|
|
286
|
+
return nil if type.nil?
|
|
287
|
+
|
|
288
|
+
type_str = type.to_s
|
|
289
|
+
unless ObservationType.valid?(type_str)
|
|
290
|
+
raise ValidationError, "Invalid observation type: #{type}. Valid types are: #{ObservationType::ALL.join(', ')}"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
type_str
|
|
294
|
+
end
|
|
295
|
+
|
|
145
296
|
def create_span
|
|
146
297
|
data = {
|
|
147
298
|
id: @id,
|
|
@@ -156,7 +307,9 @@ module Langfuse
|
|
|
156
307
|
status_message: @status_message,
|
|
157
308
|
parent_observation_id: @parent_observation_id,
|
|
158
309
|
version: @version
|
|
159
|
-
}
|
|
310
|
+
}
|
|
311
|
+
data[:type] = @as_type if @as_type
|
|
312
|
+
data = data.merge(@kwargs).compact
|
|
160
313
|
|
|
161
314
|
@client.enqueue_event('span-create', data)
|
|
162
315
|
end
|
|
@@ -175,7 +328,9 @@ module Langfuse
|
|
|
175
328
|
status_message: @status_message,
|
|
176
329
|
parent_observation_id: @parent_observation_id,
|
|
177
330
|
version: @version
|
|
178
|
-
}
|
|
331
|
+
}
|
|
332
|
+
data[:type] = @as_type if @as_type
|
|
333
|
+
data = data.merge(@kwargs).compact
|
|
179
334
|
|
|
180
335
|
@client.enqueue_event('span-update', data)
|
|
181
336
|
end
|
data/lib/langfuse/trace.rb
CHANGED
|
@@ -24,9 +24,10 @@ module Langfuse
|
|
|
24
24
|
create_trace
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Create a child span with optional type
|
|
27
28
|
def span(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
28
29
|
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
29
|
-
version: nil, **kwargs)
|
|
30
|
+
version: nil, as_type: nil, **kwargs)
|
|
30
31
|
@client.span(
|
|
31
32
|
trace_id: @id,
|
|
32
33
|
name: name,
|
|
@@ -39,10 +40,12 @@ module Langfuse
|
|
|
39
40
|
status_message: status_message,
|
|
40
41
|
parent_observation_id: parent_observation_id,
|
|
41
42
|
version: version,
|
|
43
|
+
as_type: as_type,
|
|
42
44
|
**kwargs
|
|
43
45
|
)
|
|
44
46
|
end
|
|
45
47
|
|
|
48
|
+
# Create a child generation
|
|
46
49
|
def generation(name: nil, start_time: nil, end_time: nil, completion_start_time: nil,
|
|
47
50
|
model: nil, model_parameters: nil, input: nil, output: nil, usage: nil,
|
|
48
51
|
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
@@ -67,6 +70,7 @@ module Langfuse
|
|
|
67
70
|
)
|
|
68
71
|
end
|
|
69
72
|
|
|
73
|
+
# Create a child event
|
|
70
74
|
def event(name:, start_time: nil, input: nil, output: nil, metadata: nil,
|
|
71
75
|
level: nil, status_message: nil, parent_observation_id: nil, version: nil, **kwargs)
|
|
72
76
|
@client.event(
|
|
@@ -84,6 +88,151 @@ module Langfuse
|
|
|
84
88
|
)
|
|
85
89
|
end
|
|
86
90
|
|
|
91
|
+
# Convenience methods for enhanced observation types
|
|
92
|
+
|
|
93
|
+
# Create a child agent observation
|
|
94
|
+
def agent(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
95
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
96
|
+
version: nil, **kwargs)
|
|
97
|
+
span(
|
|
98
|
+
name: name,
|
|
99
|
+
start_time: start_time,
|
|
100
|
+
end_time: end_time,
|
|
101
|
+
input: input,
|
|
102
|
+
output: output,
|
|
103
|
+
metadata: metadata,
|
|
104
|
+
level: level,
|
|
105
|
+
status_message: status_message,
|
|
106
|
+
parent_observation_id: parent_observation_id,
|
|
107
|
+
version: version,
|
|
108
|
+
as_type: ObservationType::AGENT,
|
|
109
|
+
**kwargs
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Create a child tool observation
|
|
114
|
+
def tool(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
115
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
116
|
+
version: nil, **kwargs)
|
|
117
|
+
span(
|
|
118
|
+
name: name,
|
|
119
|
+
start_time: start_time,
|
|
120
|
+
end_time: end_time,
|
|
121
|
+
input: input,
|
|
122
|
+
output: output,
|
|
123
|
+
metadata: metadata,
|
|
124
|
+
level: level,
|
|
125
|
+
status_message: status_message,
|
|
126
|
+
parent_observation_id: parent_observation_id,
|
|
127
|
+
version: version,
|
|
128
|
+
as_type: ObservationType::TOOL,
|
|
129
|
+
**kwargs
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Create a child chain observation
|
|
134
|
+
def chain(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
135
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
136
|
+
version: nil, **kwargs)
|
|
137
|
+
span(
|
|
138
|
+
name: name,
|
|
139
|
+
start_time: start_time,
|
|
140
|
+
end_time: end_time,
|
|
141
|
+
input: input,
|
|
142
|
+
output: output,
|
|
143
|
+
metadata: metadata,
|
|
144
|
+
level: level,
|
|
145
|
+
status_message: status_message,
|
|
146
|
+
parent_observation_id: parent_observation_id,
|
|
147
|
+
version: version,
|
|
148
|
+
as_type: ObservationType::CHAIN,
|
|
149
|
+
**kwargs
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Create a child retriever observation
|
|
154
|
+
def retriever(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
155
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
156
|
+
version: nil, **kwargs)
|
|
157
|
+
span(
|
|
158
|
+
name: name,
|
|
159
|
+
start_time: start_time,
|
|
160
|
+
end_time: end_time,
|
|
161
|
+
input: input,
|
|
162
|
+
output: output,
|
|
163
|
+
metadata: metadata,
|
|
164
|
+
level: level,
|
|
165
|
+
status_message: status_message,
|
|
166
|
+
parent_observation_id: parent_observation_id,
|
|
167
|
+
version: version,
|
|
168
|
+
as_type: ObservationType::RETRIEVER,
|
|
169
|
+
**kwargs
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Create a child embedding observation
|
|
174
|
+
def embedding(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
175
|
+
model: nil, usage: nil, metadata: nil, level: nil, status_message: nil,
|
|
176
|
+
parent_observation_id: nil, version: nil, **kwargs)
|
|
177
|
+
merged_metadata = (metadata || {}).merge(
|
|
178
|
+
{ model: model, usage: usage }.compact
|
|
179
|
+
)
|
|
180
|
+
span(
|
|
181
|
+
name: name,
|
|
182
|
+
start_time: start_time,
|
|
183
|
+
end_time: end_time,
|
|
184
|
+
input: input,
|
|
185
|
+
output: output,
|
|
186
|
+
metadata: merged_metadata.empty? ? nil : merged_metadata,
|
|
187
|
+
level: level,
|
|
188
|
+
status_message: status_message,
|
|
189
|
+
parent_observation_id: parent_observation_id,
|
|
190
|
+
version: version,
|
|
191
|
+
as_type: ObservationType::EMBEDDING,
|
|
192
|
+
**kwargs
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Create a child evaluator observation
|
|
197
|
+
def evaluator(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
198
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
199
|
+
version: nil, **kwargs)
|
|
200
|
+
span(
|
|
201
|
+
name: name,
|
|
202
|
+
start_time: start_time,
|
|
203
|
+
end_time: end_time,
|
|
204
|
+
input: input,
|
|
205
|
+
output: output,
|
|
206
|
+
metadata: metadata,
|
|
207
|
+
level: level,
|
|
208
|
+
status_message: status_message,
|
|
209
|
+
parent_observation_id: parent_observation_id,
|
|
210
|
+
version: version,
|
|
211
|
+
as_type: ObservationType::EVALUATOR,
|
|
212
|
+
**kwargs
|
|
213
|
+
)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Create a child guardrail observation
|
|
217
|
+
def guardrail(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
218
|
+
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
219
|
+
version: nil, **kwargs)
|
|
220
|
+
span(
|
|
221
|
+
name: name,
|
|
222
|
+
start_time: start_time,
|
|
223
|
+
end_time: end_time,
|
|
224
|
+
input: input,
|
|
225
|
+
output: output,
|
|
226
|
+
metadata: metadata,
|
|
227
|
+
level: level,
|
|
228
|
+
status_message: status_message,
|
|
229
|
+
parent_observation_id: parent_observation_id,
|
|
230
|
+
version: version,
|
|
231
|
+
as_type: ObservationType::GUARDRAIL,
|
|
232
|
+
**kwargs
|
|
233
|
+
)
|
|
234
|
+
end
|
|
235
|
+
|
|
87
236
|
def score(name:, value:, data_type: nil, comment: nil, **kwargs)
|
|
88
237
|
@client.score(
|
|
89
238
|
trace_id: @id,
|
|
@@ -95,6 +244,23 @@ module Langfuse
|
|
|
95
244
|
)
|
|
96
245
|
end
|
|
97
246
|
|
|
247
|
+
def update(name: nil, user_id: nil, session_id: nil, version: nil,
|
|
248
|
+
release: nil, input: nil, output: nil, metadata: nil, tags: nil, **kwargs)
|
|
249
|
+
# 更新实例变量
|
|
250
|
+
@name = name if name
|
|
251
|
+
@user_id = user_id if user_id
|
|
252
|
+
@session_id = session_id if session_id
|
|
253
|
+
@version = version if version
|
|
254
|
+
@release = release if release
|
|
255
|
+
@input = input if input
|
|
256
|
+
@output = output if output
|
|
257
|
+
@metadata = metadata if metadata
|
|
258
|
+
@tags = tags if tags
|
|
259
|
+
@kwargs.merge!(kwargs) if kwargs.any?
|
|
260
|
+
# 触发 trace-update 事件
|
|
261
|
+
update_trace
|
|
262
|
+
end
|
|
263
|
+
|
|
98
264
|
def get_url
|
|
99
265
|
"#{@client.host}/trace/#{@id}"
|
|
100
266
|
end
|
|
@@ -134,5 +300,23 @@ module Langfuse
|
|
|
134
300
|
|
|
135
301
|
@client.enqueue_event('trace-create', data)
|
|
136
302
|
end
|
|
303
|
+
|
|
304
|
+
def update_trace
|
|
305
|
+
data = {
|
|
306
|
+
id: @id,
|
|
307
|
+
name: @name,
|
|
308
|
+
user_id: @user_id,
|
|
309
|
+
session_id: @session_id,
|
|
310
|
+
version: @version,
|
|
311
|
+
release: @release,
|
|
312
|
+
input: @input,
|
|
313
|
+
output: @output,
|
|
314
|
+
metadata: @metadata,
|
|
315
|
+
tags: @tags,
|
|
316
|
+
timestamp: @timestamp
|
|
317
|
+
}.merge(@kwargs).compact
|
|
318
|
+
|
|
319
|
+
@client.enqueue_event('trace-update', data)
|
|
320
|
+
end
|
|
137
321
|
end
|
|
138
322
|
end
|
data/lib/langfuse/utils.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'securerandom'
|
|
2
2
|
require 'time'
|
|
3
|
+
require 'erb'
|
|
3
4
|
|
|
4
5
|
module Langfuse
|
|
5
6
|
module Utils
|
|
@@ -12,6 +13,10 @@ module Langfuse
|
|
|
12
13
|
Time.now.utc.iso8601(3)
|
|
13
14
|
end
|
|
14
15
|
|
|
16
|
+
def url_encode(string)
|
|
17
|
+
ERB::Util.url_encode(string.to_s)
|
|
18
|
+
end
|
|
19
|
+
|
|
15
20
|
def deep_symbolize_keys(hash)
|
|
16
21
|
return hash unless hash.is_a?(Hash)
|
|
17
22
|
|
data/lib/langfuse/version.rb
CHANGED
data/lib/langfuse.rb
CHANGED
data/test_offline.rb
CHANGED
|
@@ -209,8 +209,8 @@ begin
|
|
|
209
209
|
user_id: 'user-456',
|
|
210
210
|
session_id: 'session-789',
|
|
211
211
|
input: { query: 'Explain quantum computing' },
|
|
212
|
+
environment: 'test',
|
|
212
213
|
metadata: {
|
|
213
|
-
environment: 'test',
|
|
214
214
|
version: '1.0.0',
|
|
215
215
|
tags: %w[physics computing]
|
|
216
216
|
}
|
|
@@ -288,19 +288,45 @@ end
|
|
|
288
288
|
# Test 11: Error handling
|
|
289
289
|
puts "\n11. Testing error handling..."
|
|
290
290
|
begin
|
|
291
|
-
#
|
|
291
|
+
# Save current configuration
|
|
292
|
+
original_public_key = Langfuse.configuration.public_key
|
|
293
|
+
original_secret_key = Langfuse.configuration.secret_key
|
|
294
|
+
|
|
295
|
+
# Test authentication error - missing public key
|
|
292
296
|
begin
|
|
293
|
-
Langfuse
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
Langfuse.configuration.public_key = nil
|
|
298
|
+
ENV.delete('LANGFUSE_PUBLIC_KEY')
|
|
299
|
+
Langfuse.new(public_key: nil, secret_key: 'secret')
|
|
300
|
+
puts '❌ Expected AuthenticationError but none was raised'
|
|
301
|
+
rescue Langfuse::AuthenticationError => e
|
|
302
|
+
puts "✅ Authentication error caught: #{e.message}"
|
|
303
|
+
ensure
|
|
304
|
+
Langfuse.configuration.public_key = original_public_key
|
|
296
305
|
end
|
|
297
306
|
|
|
298
|
-
# Test authentication error
|
|
307
|
+
# Test authentication error - missing secret key
|
|
299
308
|
begin
|
|
300
|
-
Langfuse.
|
|
309
|
+
Langfuse.configuration.secret_key = nil
|
|
310
|
+
ENV.delete('LANGFUSE_SECRET_KEY')
|
|
311
|
+
Langfuse.new(public_key: 'public', secret_key: nil)
|
|
312
|
+
puts '❌ Expected AuthenticationError but none was raised'
|
|
301
313
|
rescue Langfuse::AuthenticationError => e
|
|
302
314
|
puts "✅ Authentication error caught: #{e.message}"
|
|
315
|
+
ensure
|
|
316
|
+
Langfuse.configuration.secret_key = original_secret_key
|
|
303
317
|
end
|
|
318
|
+
|
|
319
|
+
# Test validation error through prompt template
|
|
320
|
+
begin
|
|
321
|
+
template = Langfuse::PromptTemplate.from_template('Hello {{name}}!')
|
|
322
|
+
# This should work fine
|
|
323
|
+
result = template.format(name: 'World')
|
|
324
|
+
puts "✅ Prompt template validation passed: #{result}"
|
|
325
|
+
rescue Langfuse::ValidationError => e
|
|
326
|
+
puts "✅ Validation error caught: #{e.message}"
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
puts '✅ Error handling tests completed'
|
|
304
330
|
rescue StandardError => e
|
|
305
331
|
puts "❌ Error handling failed: #{e.message}"
|
|
306
332
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Sun
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -28,30 +28,56 @@ dependencies:
|
|
|
28
28
|
name: faraday
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '1.8'
|
|
34
|
+
- - "<"
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '3.0'
|
|
34
37
|
type: :runtime
|
|
35
38
|
prerelease: false
|
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
40
|
requirements:
|
|
38
|
-
- - "
|
|
41
|
+
- - ">="
|
|
39
42
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
43
|
+
version: '1.8'
|
|
44
|
+
- - "<"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
41
47
|
- !ruby/object:Gem::Dependency
|
|
42
48
|
name: faraday-net_http
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.0'
|
|
54
|
+
- - "<"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '4.0'
|
|
57
|
+
type: :runtime
|
|
58
|
+
prerelease: false
|
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '1.0'
|
|
64
|
+
- - "<"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '4.0'
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: faraday-multipart
|
|
43
69
|
requirement: !ruby/object:Gem::Requirement
|
|
44
70
|
requirements:
|
|
45
71
|
- - "~>"
|
|
46
72
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
73
|
+
version: '1.0'
|
|
48
74
|
type: :runtime
|
|
49
75
|
prerelease: false
|
|
50
76
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
77
|
requirements:
|
|
52
78
|
- - "~>"
|
|
53
79
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
80
|
+
version: '1.0'
|
|
55
81
|
- !ruby/object:Gem::Dependency
|
|
56
82
|
name: json
|
|
57
83
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -172,6 +198,7 @@ executables: []
|
|
|
172
198
|
extensions: []
|
|
173
199
|
extra_rdoc_files: []
|
|
174
200
|
files:
|
|
201
|
+
- ".github/workflows/ci.yml"
|
|
175
202
|
- ".github/workflows/release.yml"
|
|
176
203
|
- ".gitignore"
|
|
177
204
|
- CHANGELOG.md
|
|
@@ -185,11 +212,13 @@ files:
|
|
|
185
212
|
- docs/README.md
|
|
186
213
|
- docs/RELEASE_CHECKLIST.md
|
|
187
214
|
- docs/TYPE_VALIDATION_TROUBLESHOOTING.md
|
|
215
|
+
- docs/URL_ENCODING_FIX.md
|
|
188
216
|
- examples/auto_flush_control.rb
|
|
189
217
|
- examples/basic_tracing.rb
|
|
190
218
|
- examples/connection_config_demo.rb
|
|
191
219
|
- examples/event_usage.rb
|
|
192
220
|
- examples/prompt_management.rb
|
|
221
|
+
- examples/url_encoding_demo.rb
|
|
193
222
|
- langfuse-ruby.gemspec
|
|
194
223
|
- lib/langfuse.rb
|
|
195
224
|
- lib/langfuse/client.rb
|
|
@@ -197,6 +226,7 @@ files:
|
|
|
197
226
|
- lib/langfuse/evaluation.rb
|
|
198
227
|
- lib/langfuse/event.rb
|
|
199
228
|
- lib/langfuse/generation.rb
|
|
229
|
+
- lib/langfuse/observation_types.rb
|
|
200
230
|
- lib/langfuse/prompt.rb
|
|
201
231
|
- lib/langfuse/span.rb
|
|
202
232
|
- lib/langfuse/trace.rb
|
|
@@ -223,7 +253,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
223
253
|
requirements:
|
|
224
254
|
- - ">="
|
|
225
255
|
- !ruby/object:Gem::Version
|
|
226
|
-
version:
|
|
256
|
+
version: 3.1.0
|
|
227
257
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
258
|
requirements:
|
|
229
259
|
- - ">="
|