langfuse-ruby 0.2.0 → 0.2.1
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/.rubocop.yml +1 -1
- data/CHANGELOG.md +44 -0
- data/CLAUDE.md +40 -14
- data/Gemfile.lock +1 -1
- data/Makefile +1 -4
- data/README.md +216 -99
- data/Rakefile +0 -6
- data/docs/FINAL_SUMMARY.md +12 -185
- data/docs/PUBLISH_GUIDE.md +36 -272
- data/docs/README.md +12 -22
- data/docs/RELEASE_CHECKLIST.md +20 -145
- data/docs/V4.md +159 -0
- data/examples/basic_tracing.rb +11 -10
- data/examples/simplified_usage.rb +7 -6
- data/examples/v4_otel_tracing.rb +70 -0
- data/lib/langfuse/client.rb +457 -247
- data/lib/langfuse/event.rb +1 -17
- data/lib/langfuse/generation.rb +35 -120
- data/lib/langfuse/null_objects.rb +4 -0
- data/lib/langfuse/otel_exporter.rb +101 -11
- data/lib/langfuse/partial_updates.rb +30 -0
- data/lib/langfuse/prompt.rb +9 -83
- data/lib/langfuse/prompt_cache.rb +65 -0
- data/lib/langfuse/span.rb +28 -153
- data/lib/langfuse/span_wrappers.rb +32 -0
- data/lib/langfuse/template_compiler.rb +56 -0
- data/lib/langfuse/trace.rb +26 -162
- data/lib/langfuse/utils.rb +28 -26
- data/lib/langfuse/version.rb +1 -1
- data/lib/langfuse.rb +49 -52
- data/scripts/release.sh +12 -12
- metadata +8 -2
data/lib/langfuse/span.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Langfuse
|
|
4
4
|
class Span
|
|
5
|
+
include PartialUpdates
|
|
6
|
+
|
|
5
7
|
attr_reader :id, :trace_id, :name, :start_time, :end_time, :input, :output,
|
|
6
8
|
:metadata, :level, :status_message, :parent_observation_id, :version,
|
|
7
9
|
:as_type, :client
|
|
@@ -31,25 +33,32 @@ module Langfuse
|
|
|
31
33
|
|
|
32
34
|
def update(name: nil, end_time: nil, input: nil, output: nil, metadata: nil,
|
|
33
35
|
level: nil, status_message: nil, version: nil, **kwargs)
|
|
34
|
-
@name = name
|
|
35
|
-
@end_time = end_time
|
|
36
|
-
@input = input
|
|
37
|
-
@output = output
|
|
36
|
+
@name = name unless name.nil?
|
|
37
|
+
@end_time = end_time unless end_time.nil?
|
|
38
|
+
@input = input unless input.nil?
|
|
39
|
+
@output = output unless output.nil?
|
|
38
40
|
@metadata.merge!(metadata) if metadata
|
|
39
|
-
@level = level
|
|
40
|
-
@status_message = status_message
|
|
41
|
-
@version = version
|
|
41
|
+
@level = level unless level.nil?
|
|
42
|
+
@status_message = status_message unless status_message.nil?
|
|
43
|
+
@version = version unless version.nil?
|
|
42
44
|
@kwargs.merge!(kwargs)
|
|
43
45
|
|
|
46
|
+
track_changes(
|
|
47
|
+
{ name: name, end_time: end_time, input: input, output: output,
|
|
48
|
+
metadata: metadata, level: level, status_message: status_message,
|
|
49
|
+
version: version },
|
|
50
|
+
kwargs.keys
|
|
51
|
+
)
|
|
44
52
|
update_span
|
|
45
53
|
self
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
def end(output: nil, end_time: nil, **kwargs)
|
|
49
57
|
@end_time = end_time || Utils.current_timestamp
|
|
50
|
-
@output = output
|
|
58
|
+
@output = output unless output.nil?
|
|
51
59
|
@kwargs.merge!(kwargs)
|
|
52
60
|
|
|
61
|
+
track_changes({ end_time: @end_time, output: output }, kwargs.keys)
|
|
53
62
|
update_span
|
|
54
63
|
self
|
|
55
64
|
end
|
|
@@ -77,6 +86,7 @@ module Langfuse
|
|
|
77
86
|
# Create a child generation
|
|
78
87
|
def generation(name: nil, start_time: nil, end_time: nil, completion_start_time: nil,
|
|
79
88
|
model: nil, model_parameters: nil, input: nil, output: nil, usage: nil,
|
|
89
|
+
usage_details: nil, cost_details: nil, prompt: nil,
|
|
80
90
|
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
81
91
|
@client.generation(
|
|
82
92
|
trace_id: @trace_id,
|
|
@@ -89,6 +99,9 @@ module Langfuse
|
|
|
89
99
|
input: input,
|
|
90
100
|
output: output,
|
|
91
101
|
usage: usage,
|
|
102
|
+
usage_details: usage_details,
|
|
103
|
+
cost_details: cost_details,
|
|
104
|
+
prompt: prompt,
|
|
92
105
|
metadata: metadata,
|
|
93
106
|
level: level,
|
|
94
107
|
status_message: status_message,
|
|
@@ -116,79 +129,11 @@ module Langfuse
|
|
|
116
129
|
)
|
|
117
130
|
end
|
|
118
131
|
|
|
119
|
-
# Convenience methods for enhanced observation types
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
span(
|
|
125
|
-
name: name,
|
|
126
|
-
start_time: start_time,
|
|
127
|
-
end_time: end_time,
|
|
128
|
-
input: input,
|
|
129
|
-
output: output,
|
|
130
|
-
metadata: metadata,
|
|
131
|
-
level: level,
|
|
132
|
-
status_message: status_message,
|
|
133
|
-
version: version,
|
|
134
|
-
as_type: ObservationType::AGENT,
|
|
135
|
-
**kwargs
|
|
136
|
-
)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Create a child tool observation
|
|
140
|
-
def tool(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
141
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
142
|
-
span(
|
|
143
|
-
name: name,
|
|
144
|
-
start_time: start_time,
|
|
145
|
-
end_time: end_time,
|
|
146
|
-
input: input,
|
|
147
|
-
output: output,
|
|
148
|
-
metadata: metadata,
|
|
149
|
-
level: level,
|
|
150
|
-
status_message: status_message,
|
|
151
|
-
version: version,
|
|
152
|
-
as_type: ObservationType::TOOL,
|
|
153
|
-
**kwargs
|
|
154
|
-
)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# Create a child chain observation
|
|
158
|
-
def chain(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
159
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
160
|
-
span(
|
|
161
|
-
name: name,
|
|
162
|
-
start_time: start_time,
|
|
163
|
-
end_time: end_time,
|
|
164
|
-
input: input,
|
|
165
|
-
output: output,
|
|
166
|
-
metadata: metadata,
|
|
167
|
-
level: level,
|
|
168
|
-
status_message: status_message,
|
|
169
|
-
version: version,
|
|
170
|
-
as_type: ObservationType::CHAIN,
|
|
171
|
-
**kwargs
|
|
172
|
-
)
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# Create a child retriever observation
|
|
176
|
-
def retriever(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
177
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
178
|
-
span(
|
|
179
|
-
name: name,
|
|
180
|
-
start_time: start_time,
|
|
181
|
-
end_time: end_time,
|
|
182
|
-
input: input,
|
|
183
|
-
output: output,
|
|
184
|
-
metadata: metadata,
|
|
185
|
-
level: level,
|
|
186
|
-
status_message: status_message,
|
|
187
|
-
version: version,
|
|
188
|
-
as_type: ObservationType::RETRIEVER,
|
|
189
|
-
**kwargs
|
|
190
|
-
)
|
|
191
|
-
end
|
|
132
|
+
# Convenience methods for enhanced observation types: each is a child span
|
|
133
|
+
# with a fixed as_type. (embedding keeps its own definition because it folds
|
|
134
|
+
# model/usage into metadata first.)
|
|
135
|
+
extend SpanWrappers
|
|
136
|
+
define_span_wrappers
|
|
192
137
|
|
|
193
138
|
# Create a child embedding observation
|
|
194
139
|
def embedding(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
@@ -212,42 +157,6 @@ module Langfuse
|
|
|
212
157
|
)
|
|
213
158
|
end
|
|
214
159
|
|
|
215
|
-
# Create a child evaluator observation
|
|
216
|
-
def evaluator(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
217
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
218
|
-
span(
|
|
219
|
-
name: name,
|
|
220
|
-
start_time: start_time,
|
|
221
|
-
end_time: end_time,
|
|
222
|
-
input: input,
|
|
223
|
-
output: output,
|
|
224
|
-
metadata: metadata,
|
|
225
|
-
level: level,
|
|
226
|
-
status_message: status_message,
|
|
227
|
-
version: version,
|
|
228
|
-
as_type: ObservationType::EVALUATOR,
|
|
229
|
-
**kwargs
|
|
230
|
-
)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Create a child guardrail observation
|
|
234
|
-
def guardrail(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
235
|
-
metadata: nil, level: nil, status_message: nil, version: nil, **kwargs)
|
|
236
|
-
span(
|
|
237
|
-
name: name,
|
|
238
|
-
start_time: start_time,
|
|
239
|
-
end_time: end_time,
|
|
240
|
-
input: input,
|
|
241
|
-
output: output,
|
|
242
|
-
metadata: metadata,
|
|
243
|
-
level: level,
|
|
244
|
-
status_message: status_message,
|
|
245
|
-
version: version,
|
|
246
|
-
as_type: ObservationType::GUARDRAIL,
|
|
247
|
-
**kwargs
|
|
248
|
-
)
|
|
249
|
-
end
|
|
250
|
-
|
|
251
160
|
def score(name:, value:, data_type: nil, comment: nil, **kwargs)
|
|
252
161
|
@client.score(
|
|
253
162
|
trace_id: @trace_id,
|
|
@@ -295,45 +204,11 @@ module Langfuse
|
|
|
295
204
|
end
|
|
296
205
|
|
|
297
206
|
def create_span
|
|
298
|
-
|
|
299
|
-
id: @id,
|
|
300
|
-
trace_id: @trace_id,
|
|
301
|
-
name: @name,
|
|
302
|
-
start_time: @start_time,
|
|
303
|
-
end_time: @end_time,
|
|
304
|
-
input: @input,
|
|
305
|
-
output: @output,
|
|
306
|
-
metadata: @metadata,
|
|
307
|
-
level: @level,
|
|
308
|
-
status_message: @status_message,
|
|
309
|
-
parent_observation_id: @parent_observation_id,
|
|
310
|
-
version: @version
|
|
311
|
-
}
|
|
312
|
-
data[:type] = @as_type if @as_type
|
|
313
|
-
data = data.merge(@kwargs).compact
|
|
314
|
-
|
|
315
|
-
@client.enqueue_event('span-create', data)
|
|
207
|
+
@client.enqueue_event('span-create', to_dict)
|
|
316
208
|
end
|
|
317
209
|
|
|
318
210
|
def update_span
|
|
319
|
-
|
|
320
|
-
id: @id,
|
|
321
|
-
trace_id: @trace_id,
|
|
322
|
-
name: @name,
|
|
323
|
-
start_time: @start_time,
|
|
324
|
-
end_time: @end_time,
|
|
325
|
-
input: @input,
|
|
326
|
-
output: @output,
|
|
327
|
-
metadata: @metadata,
|
|
328
|
-
level: @level,
|
|
329
|
-
status_message: @status_message,
|
|
330
|
-
parent_observation_id: @parent_observation_id,
|
|
331
|
-
version: @version
|
|
332
|
-
}
|
|
333
|
-
data[:type] = @as_type if @as_type
|
|
334
|
-
data = data.merge(@kwargs).compact
|
|
335
|
-
|
|
336
|
-
@client.enqueue_event('span-update', data)
|
|
211
|
+
@client.enqueue_event('span-update', update_body)
|
|
337
212
|
end
|
|
338
213
|
end
|
|
339
214
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Langfuse
|
|
4
|
+
# Defines the enhanced observation helpers (agent, tool, chain, retriever,
|
|
5
|
+
# evaluator, guardrail) as thin wrappers around the extending class's #span,
|
|
6
|
+
# each pinning a fixed as_type.
|
|
7
|
+
#
|
|
8
|
+
# `embedding` is intentionally not generated here: every wrapper folds
|
|
9
|
+
# model/usage into metadata before delegating, so it stays hand-written.
|
|
10
|
+
module SpanWrappers
|
|
11
|
+
TYPES = {
|
|
12
|
+
agent: ObservationType::AGENT,
|
|
13
|
+
tool: ObservationType::TOOL,
|
|
14
|
+
chain: ObservationType::CHAIN,
|
|
15
|
+
retriever: ObservationType::RETRIEVER,
|
|
16
|
+
evaluator: ObservationType::EVALUATOR,
|
|
17
|
+
guardrail: ObservationType::GUARDRAIL
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# `evaluator_name` exists because Client exposes the helper as
|
|
21
|
+
# `evaluator_obs`, keeping it distinct from the Evaluators API.
|
|
22
|
+
def define_span_wrappers(evaluator_name: :evaluator)
|
|
23
|
+
TYPES.each do |name, as_type|
|
|
24
|
+
method_name = name == :evaluator ? evaluator_name : name
|
|
25
|
+
|
|
26
|
+
define_method(method_name) do |**kwargs|
|
|
27
|
+
span(**kwargs, as_type: as_type)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Langfuse
|
|
4
|
+
# Placeholder handling shared by Prompt, PromptTemplate and ChatPromptTemplate.
|
|
5
|
+
# Both `{{var}}` and `{var}` are supported.
|
|
6
|
+
module TemplateCompiler
|
|
7
|
+
VARIABLE_PATTERN = /\{\{([a-zA-Z0-9_.]+)\}\}|\{([a-zA-Z0-9_.]+)\}/
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
# Substitutes every provided variable in a single pass, so a value that
|
|
11
|
+
# itself contains a placeholder is never expanded again (a value like
|
|
12
|
+
# "{{admin_prompt}}" stays literal). Placeholders without a matching
|
|
13
|
+
# variable are left untouched.
|
|
14
|
+
def compile(template, variables = {})
|
|
15
|
+
text = template.to_s
|
|
16
|
+
return text if variables.nil? || variables.empty?
|
|
17
|
+
|
|
18
|
+
values = variables.to_h { |key, value| [key.to_s, value.to_s] }
|
|
19
|
+
|
|
20
|
+
text.gsub(placeholder_pattern(values.keys)) do
|
|
21
|
+
values[Regexp.last_match(1) || Regexp.last_match(2)]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def compile_messages(messages, variables = {})
|
|
26
|
+
messages.map do |message|
|
|
27
|
+
role = message[:role] || message['role']
|
|
28
|
+
content = compile(message[:content] || message['content'], variables)
|
|
29
|
+
message.merge(role: role, content: content)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def extract_variables(text)
|
|
34
|
+
text.to_s.scan(VARIABLE_PATTERN).map { |double, single| double || single }.uniq
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def extract_message_variables(messages)
|
|
38
|
+
messages.flat_map do |message|
|
|
39
|
+
content = message[:content] || message['content']
|
|
40
|
+
extract_variables(content)
|
|
41
|
+
end.uniq
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Longest name first so a `user` variable cannot shadow `{{user_name}}`.
|
|
47
|
+
def placeholder_pattern(names)
|
|
48
|
+
alternatives = names.sort_by { |name| -name.length }
|
|
49
|
+
.map { |name| Regexp.escape(name) }
|
|
50
|
+
.join('|')
|
|
51
|
+
|
|
52
|
+
/\{\{(#{alternatives})\}\}|\{(#{alternatives})\}/
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/langfuse/trace.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Langfuse
|
|
4
4
|
class Trace
|
|
5
|
+
include PartialUpdates
|
|
6
|
+
|
|
5
7
|
attr_reader :id, :name, :user_id, :session_id, :version, :release, :input, :output,
|
|
6
8
|
:metadata, :tags, :timestamp, :public, :client
|
|
7
9
|
|
|
@@ -95,87 +97,11 @@ module Langfuse
|
|
|
95
97
|
)
|
|
96
98
|
end
|
|
97
99
|
|
|
98
|
-
# Convenience methods for enhanced observation types
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
version: nil, **kwargs)
|
|
104
|
-
span(
|
|
105
|
-
name: name,
|
|
106
|
-
start_time: start_time,
|
|
107
|
-
end_time: end_time,
|
|
108
|
-
input: input,
|
|
109
|
-
output: output,
|
|
110
|
-
metadata: metadata,
|
|
111
|
-
level: level,
|
|
112
|
-
status_message: status_message,
|
|
113
|
-
parent_observation_id: parent_observation_id,
|
|
114
|
-
version: version,
|
|
115
|
-
as_type: ObservationType::AGENT,
|
|
116
|
-
**kwargs
|
|
117
|
-
)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Create a child tool observation
|
|
121
|
-
def tool(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
122
|
-
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
123
|
-
version: nil, **kwargs)
|
|
124
|
-
span(
|
|
125
|
-
name: name,
|
|
126
|
-
start_time: start_time,
|
|
127
|
-
end_time: end_time,
|
|
128
|
-
input: input,
|
|
129
|
-
output: output,
|
|
130
|
-
metadata: metadata,
|
|
131
|
-
level: level,
|
|
132
|
-
status_message: status_message,
|
|
133
|
-
parent_observation_id: parent_observation_id,
|
|
134
|
-
version: version,
|
|
135
|
-
as_type: ObservationType::TOOL,
|
|
136
|
-
**kwargs
|
|
137
|
-
)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Create a child chain observation
|
|
141
|
-
def chain(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
142
|
-
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
143
|
-
version: nil, **kwargs)
|
|
144
|
-
span(
|
|
145
|
-
name: name,
|
|
146
|
-
start_time: start_time,
|
|
147
|
-
end_time: end_time,
|
|
148
|
-
input: input,
|
|
149
|
-
output: output,
|
|
150
|
-
metadata: metadata,
|
|
151
|
-
level: level,
|
|
152
|
-
status_message: status_message,
|
|
153
|
-
parent_observation_id: parent_observation_id,
|
|
154
|
-
version: version,
|
|
155
|
-
as_type: ObservationType::CHAIN,
|
|
156
|
-
**kwargs
|
|
157
|
-
)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Create a child retriever observation
|
|
161
|
-
def retriever(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
162
|
-
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
163
|
-
version: nil, **kwargs)
|
|
164
|
-
span(
|
|
165
|
-
name: name,
|
|
166
|
-
start_time: start_time,
|
|
167
|
-
end_time: end_time,
|
|
168
|
-
input: input,
|
|
169
|
-
output: output,
|
|
170
|
-
metadata: metadata,
|
|
171
|
-
level: level,
|
|
172
|
-
status_message: status_message,
|
|
173
|
-
parent_observation_id: parent_observation_id,
|
|
174
|
-
version: version,
|
|
175
|
-
as_type: ObservationType::RETRIEVER,
|
|
176
|
-
**kwargs
|
|
177
|
-
)
|
|
178
|
-
end
|
|
100
|
+
# Convenience methods for enhanced observation types: each is a child span
|
|
101
|
+
# with a fixed as_type. (embedding keeps its own definition because it folds
|
|
102
|
+
# model/usage into metadata first.)
|
|
103
|
+
extend SpanWrappers
|
|
104
|
+
define_span_wrappers
|
|
179
105
|
|
|
180
106
|
# Create a child embedding observation
|
|
181
107
|
def embedding(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
@@ -200,46 +126,6 @@ module Langfuse
|
|
|
200
126
|
)
|
|
201
127
|
end
|
|
202
128
|
|
|
203
|
-
# Create a child evaluator observation
|
|
204
|
-
def evaluator(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
205
|
-
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
206
|
-
version: nil, **kwargs)
|
|
207
|
-
span(
|
|
208
|
-
name: name,
|
|
209
|
-
start_time: start_time,
|
|
210
|
-
end_time: end_time,
|
|
211
|
-
input: input,
|
|
212
|
-
output: output,
|
|
213
|
-
metadata: metadata,
|
|
214
|
-
level: level,
|
|
215
|
-
status_message: status_message,
|
|
216
|
-
parent_observation_id: parent_observation_id,
|
|
217
|
-
version: version,
|
|
218
|
-
as_type: ObservationType::EVALUATOR,
|
|
219
|
-
**kwargs
|
|
220
|
-
)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# Create a child guardrail observation
|
|
224
|
-
def guardrail(name: nil, start_time: nil, end_time: nil, input: nil, output: nil,
|
|
225
|
-
metadata: nil, level: nil, status_message: nil, parent_observation_id: nil,
|
|
226
|
-
version: nil, **kwargs)
|
|
227
|
-
span(
|
|
228
|
-
name: name,
|
|
229
|
-
start_time: start_time,
|
|
230
|
-
end_time: end_time,
|
|
231
|
-
input: input,
|
|
232
|
-
output: output,
|
|
233
|
-
metadata: metadata,
|
|
234
|
-
level: level,
|
|
235
|
-
status_message: status_message,
|
|
236
|
-
parent_observation_id: parent_observation_id,
|
|
237
|
-
version: version,
|
|
238
|
-
as_type: ObservationType::GUARDRAIL,
|
|
239
|
-
**kwargs
|
|
240
|
-
)
|
|
241
|
-
end
|
|
242
|
-
|
|
243
129
|
def score(name:, value:, data_type: nil, comment: nil, **kwargs)
|
|
244
130
|
@client.score(
|
|
245
131
|
trace_id: @id,
|
|
@@ -255,19 +141,27 @@ module Langfuse
|
|
|
255
141
|
release: nil, input: nil, output: nil, metadata: nil, tags: nil,
|
|
256
142
|
public: nil, **kwargs)
|
|
257
143
|
# 更新实例变量
|
|
258
|
-
@name = name
|
|
259
|
-
@user_id = user_id
|
|
260
|
-
@session_id = session_id
|
|
261
|
-
@version = version
|
|
262
|
-
@release = release
|
|
263
|
-
@input = input
|
|
264
|
-
@output = output
|
|
265
|
-
@metadata
|
|
266
|
-
@tags = tags
|
|
144
|
+
@name = name unless name.nil?
|
|
145
|
+
@user_id = user_id unless user_id.nil?
|
|
146
|
+
@session_id = session_id unless session_id.nil?
|
|
147
|
+
@version = version unless version.nil?
|
|
148
|
+
@release = release unless release.nil?
|
|
149
|
+
@input = input unless input.nil?
|
|
150
|
+
@output = output unless output.nil?
|
|
151
|
+
@metadata.merge!(metadata) if metadata
|
|
152
|
+
@tags = tags unless tags.nil?
|
|
267
153
|
@public = public unless public.nil?
|
|
268
154
|
@kwargs.merge!(kwargs) if kwargs.any?
|
|
155
|
+
|
|
156
|
+
track_changes(
|
|
157
|
+
{ name: name, user_id: user_id, session_id: session_id, version: version,
|
|
158
|
+
release: release, input: input, output: output, metadata: metadata,
|
|
159
|
+
tags: tags, public: public },
|
|
160
|
+
kwargs.keys
|
|
161
|
+
)
|
|
269
162
|
# 触发 trace-update 事件
|
|
270
163
|
update_trace
|
|
164
|
+
self
|
|
271
165
|
end
|
|
272
166
|
|
|
273
167
|
def get_url
|
|
@@ -294,41 +188,11 @@ module Langfuse
|
|
|
294
188
|
private
|
|
295
189
|
|
|
296
190
|
def create_trace
|
|
297
|
-
|
|
298
|
-
id: @id,
|
|
299
|
-
name: @name,
|
|
300
|
-
user_id: @user_id,
|
|
301
|
-
session_id: @session_id,
|
|
302
|
-
version: @version,
|
|
303
|
-
release: @release,
|
|
304
|
-
input: @input,
|
|
305
|
-
output: @output,
|
|
306
|
-
metadata: @metadata,
|
|
307
|
-
tags: @tags,
|
|
308
|
-
timestamp: @timestamp,
|
|
309
|
-
public: @public
|
|
310
|
-
}.merge(@kwargs).compact
|
|
311
|
-
|
|
312
|
-
@client.enqueue_event('trace-create', data)
|
|
191
|
+
@client.enqueue_event('trace-create', to_dict)
|
|
313
192
|
end
|
|
314
193
|
|
|
315
194
|
def update_trace
|
|
316
|
-
|
|
317
|
-
id: @id,
|
|
318
|
-
name: @name,
|
|
319
|
-
user_id: @user_id,
|
|
320
|
-
session_id: @session_id,
|
|
321
|
-
version: @version,
|
|
322
|
-
release: @release,
|
|
323
|
-
input: @input,
|
|
324
|
-
output: @output,
|
|
325
|
-
metadata: @metadata,
|
|
326
|
-
tags: @tags,
|
|
327
|
-
timestamp: @timestamp,
|
|
328
|
-
public: @public
|
|
329
|
-
}.merge(@kwargs).compact
|
|
330
|
-
|
|
331
|
-
@client.enqueue_event('trace-update', data)
|
|
195
|
+
@client.enqueue_event('trace-update', update_body, trace_ref: self)
|
|
332
196
|
end
|
|
333
197
|
end
|
|
334
198
|
end
|
data/lib/langfuse/utils.rb
CHANGED
|
@@ -33,26 +33,39 @@ module Langfuse
|
|
|
33
33
|
ERB::Util.url_encode(string.to_s)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def deep_symbolize_keys(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
def deep_symbolize_keys(object)
|
|
37
|
+
case object
|
|
38
|
+
when Hash
|
|
39
|
+
object.each_with_object({}) do |(key, value), result|
|
|
40
|
+
new_key = key.is_a?(String) ? key.to_sym : key
|
|
41
|
+
result[new_key] = deep_symbolize_keys(value)
|
|
42
|
+
end
|
|
43
|
+
when Array
|
|
44
|
+
object.map { |item| deep_symbolize_keys(item) }
|
|
45
|
+
else
|
|
46
|
+
object
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
# 将哈希的键名转换为小驼峰格式
|
|
51
|
+
def deep_camelize_keys(object)
|
|
52
|
+
case object
|
|
53
|
+
when Hash
|
|
54
|
+
object.each_with_object({}) do |(key, value), result|
|
|
55
|
+
new_key = camelize_key(key.to_s)
|
|
56
|
+
result[new_key] = deep_camelize_keys(value)
|
|
57
|
+
end
|
|
58
|
+
when Array
|
|
59
|
+
object.map { |item| deep_camelize_keys(item) }
|
|
60
|
+
else
|
|
61
|
+
object
|
|
53
62
|
end
|
|
54
63
|
end
|
|
55
64
|
|
|
65
|
+
# Kept for backwards compatibility: it has always camelized keys rather
|
|
66
|
+
# than only stringifying them.
|
|
67
|
+
alias deep_stringify_keys deep_camelize_keys
|
|
68
|
+
|
|
56
69
|
# Prepare an event body for the ingestion API:
|
|
57
70
|
# - top-level keys are camelized (snake_case -> camelCase)
|
|
58
71
|
# - user data values (input/output/metadata/usageDetails/costDetails/modelParameters)
|
|
@@ -64,24 +77,13 @@ module Langfuse
|
|
|
64
77
|
body.each_with_object({}) do |(key, value), result|
|
|
65
78
|
new_key = camelize_key(key.to_s)
|
|
66
79
|
result[new_key] = if !VERBATIM_BODY_KEYS.include?(new_key) && value.is_a?(Hash)
|
|
67
|
-
|
|
80
|
+
deep_camelize_keys(value)
|
|
68
81
|
else
|
|
69
82
|
value
|
|
70
83
|
end
|
|
71
84
|
end
|
|
72
85
|
end
|
|
73
86
|
|
|
74
|
-
# 将哈希的键名转换为小驼峰格式
|
|
75
|
-
def deep_camelize_keys(hash)
|
|
76
|
-
return hash unless hash.is_a?(Hash)
|
|
77
|
-
|
|
78
|
-
hash.each_with_object({}) do |(key, value), result|
|
|
79
|
-
new_key = camelize_key(key.to_s)
|
|
80
|
-
new_value = value.is_a?(Hash) ? deep_camelize_keys(value) : value
|
|
81
|
-
result[new_key] = new_value
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
87
|
private
|
|
86
88
|
|
|
87
89
|
# 将蛇形命名转换为小驼峰命名
|
data/lib/langfuse/version.rb
CHANGED