langfuse-rb 0.12.0 → 0.12.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/CHANGELOG.md +7 -1
- data/lib/langfuse/dataset_item_client.rb +1 -0
- data/lib/langfuse/experiment_attributes.rb +67 -0
- data/lib/langfuse/experiment_result.rb +5 -2
- data/lib/langfuse/experiment_runner.rb +78 -15
- data/lib/langfuse/otel_attributes.rb +11 -0
- data/lib/langfuse/propagation.rb +24 -1
- data/lib/langfuse/traced_execution.rb +7 -5
- data/lib/langfuse/version.rb +1 -1
- data/lib/langfuse.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 20d0cd9db76be223db4867cbc9f54f59ee5accc0058e29bab14561c457d8fecc
|
|
4
|
+
data.tar.gz: b43bd144dca940b375ac916feca399f9e10da54db201fff9f4d140acdd301ec5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66175d8c844c314ac8f62180e98d3bc95f8fb3934ea71880d7bd7e21fc5bbbd06f472a5b992cb4529929aab27cc153a7641f8f2a0f73ffb2ed869df127056980
|
|
7
|
+
data.tar.gz: 0e7a1e8e7a34aeaa964d8b1c0295c6448a3fa58e075c1bdda6e6aafe98b00ee9d48a7e904c3724b1b2ab42d166119bf80b6aa7be21eb750b072768d6588363d3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.1] - 2026-09-03
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Emit Langfuse v4 experiment attributes for local and dataset-backed experiment observations (#118).
|
|
14
|
+
|
|
10
15
|
## [0.12.0] - 2026-08-20
|
|
11
16
|
|
|
12
17
|
### Changed
|
|
@@ -173,7 +178,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
173
178
|
- Migrated from legacy ingestion API to OTLP endpoint
|
|
174
179
|
- Removed `tracing_enabled` configuration flag (#2)
|
|
175
180
|
|
|
176
|
-
[Unreleased]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.
|
|
181
|
+
[Unreleased]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.1...HEAD
|
|
182
|
+
[0.12.1]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.0...v0.12.1
|
|
177
183
|
[0.12.0]: https://github.com/simplepractice/langfuse-rb/compare/v0.11.0...v0.12.0
|
|
178
184
|
[0.11.0]: https://github.com/simplepractice/langfuse-rb/compare/v0.10.1...v0.11.0
|
|
179
185
|
[0.10.1]: https://github.com/simplepractice/langfuse-rb/compare/v0.10.0...v0.10.1
|
|
@@ -103,6 +103,7 @@ module Langfuse
|
|
|
103
103
|
#
|
|
104
104
|
# Executes the block inside an observed span, flushes the trace, then
|
|
105
105
|
# creates a dataset run item linking this item to the resulting trace.
|
|
106
|
+
# Use {Client#run_experiment} when Langfuse v4 experiment attribution is required.
|
|
106
107
|
#
|
|
107
108
|
# @param run_name [String] run name for grouping
|
|
108
109
|
# @param run_description [String, nil] optional run description
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
|
|
6
|
+
module Langfuse
|
|
7
|
+
# Builds the OpenTelemetry attributes that identify v4 experiment observations.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module ExperimentAttributes
|
|
11
|
+
ENVIRONMENT = "sdk-experiment"
|
|
12
|
+
|
|
13
|
+
def self.generate_experiment_id
|
|
14
|
+
SecureRandom.hex(8)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.generate_item_id(input)
|
|
18
|
+
serialized_input = OtelAttributes.serialize(input, preserve_strings: true) || input.to_s
|
|
19
|
+
Digest::SHA256.hexdigest(serialized_input)[0, 16]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# rubocop:disable Metrics/ParameterLists
|
|
23
|
+
def self.propagated(experiment_id:, run_name:, item_id:, root_observation_id:,
|
|
24
|
+
dataset_id: nil, experiment_metadata: nil, item_metadata: nil, mask: nil)
|
|
25
|
+
attributes = {
|
|
26
|
+
OtelAttributes::EXPERIMENT_ID => experiment_id,
|
|
27
|
+
OtelAttributes::EXPERIMENT_NAME => run_name,
|
|
28
|
+
OtelAttributes::EXPERIMENT_DATASET_ID => dataset_id,
|
|
29
|
+
OtelAttributes::EXPERIMENT_ITEM_ID => item_id,
|
|
30
|
+
OtelAttributes::EXPERIMENT_ITEM_ROOT_OBSERVATION_ID => root_observation_id,
|
|
31
|
+
OtelAttributes::ENVIRONMENT => ENVIRONMENT
|
|
32
|
+
}.compact
|
|
33
|
+
attributes.merge!(masked_metadata(experiment_metadata, OtelAttributes::EXPERIMENT_METADATA, mask))
|
|
34
|
+
attributes.merge!(masked_metadata(item_metadata, OtelAttributes::EXPERIMENT_ITEM_METADATA, mask))
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Metrics/ParameterLists
|
|
37
|
+
|
|
38
|
+
def self.root(description:, expected_output:, mask: nil)
|
|
39
|
+
masked_output = Masking.apply(expected_output, mask: mask)
|
|
40
|
+
{
|
|
41
|
+
OtelAttributes::EXPERIMENT_DESCRIPTION => description,
|
|
42
|
+
OtelAttributes::EXPERIMENT_ITEM_EXPECTED_OUTPUT =>
|
|
43
|
+
OtelAttributes.serialize(masked_output, preserve_strings: true)
|
|
44
|
+
}.compact
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.observation_metadata(name:, run_name:, experiment_metadata:, item_metadata:,
|
|
48
|
+
dataset_id: nil, dataset_item_id: nil)
|
|
49
|
+
metadata = hash_metadata(item_metadata).merge(hash_metadata(experiment_metadata))
|
|
50
|
+
metadata[:experiment_name] = name
|
|
51
|
+
metadata[:experiment_run_name] = run_name
|
|
52
|
+
metadata[:dataset_id] = dataset_id if dataset_id
|
|
53
|
+
metadata[:dataset_item_id] = dataset_item_id if dataset_item_id
|
|
54
|
+
metadata
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.masked_metadata(metadata, prefix, mask)
|
|
58
|
+
OtelAttributes.flatten_metadata(Masking.apply(metadata, mask: mask), prefix)
|
|
59
|
+
end
|
|
60
|
+
private_class_method :masked_metadata
|
|
61
|
+
|
|
62
|
+
def self.hash_metadata(metadata)
|
|
63
|
+
metadata.is_a?(Hash) ? metadata : {}
|
|
64
|
+
end
|
|
65
|
+
private_class_method :hash_metadata
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -18,26 +18,29 @@ module Langfuse
|
|
|
18
18
|
# @return [String, nil] run description
|
|
19
19
|
# @return [Array<ItemResult>] per-item results (all items, including failures)
|
|
20
20
|
# @return [Array<Evaluation>] run-level evaluation results
|
|
21
|
+
# @return [String, nil] resolved experiment ID shared by the run
|
|
21
22
|
# @return [String, nil] dataset run ID from the server
|
|
22
23
|
# @return [String, nil] URL to the dataset run in Langfuse UI
|
|
23
24
|
attr_reader :name, :run_name, :description, :item_results, :run_evaluations,
|
|
24
|
-
:dataset_run_id, :dataset_run_url
|
|
25
|
+
:experiment_id, :dataset_run_id, :dataset_run_url
|
|
25
26
|
|
|
26
27
|
# @param name [String] experiment/run name
|
|
27
28
|
# @param item_results [Array<ItemResult>] per-item results
|
|
28
29
|
# @param run_evaluations [Array<Evaluation>] run-level evaluations
|
|
29
30
|
# @param run_name [String, nil] auto-generated run name
|
|
30
31
|
# @param description [String, nil] run description
|
|
32
|
+
# @param experiment_id [String, nil] resolved experiment ID shared by the run
|
|
31
33
|
# @param dataset_run_id [String, nil] dataset run ID from the server
|
|
32
34
|
# @param dataset_run_url [String, nil] URL to the dataset run in Langfuse UI
|
|
33
35
|
# rubocop:disable Metrics/ParameterLists
|
|
34
36
|
def initialize(name:, item_results:, run_evaluations: [], run_name: nil, description: nil,
|
|
35
|
-
dataset_run_id: nil, dataset_run_url: nil)
|
|
37
|
+
experiment_id: nil, dataset_run_id: nil, dataset_run_url: nil)
|
|
36
38
|
@name = name
|
|
37
39
|
@item_results = item_results
|
|
38
40
|
@run_evaluations = run_evaluations
|
|
39
41
|
@run_name = run_name
|
|
40
42
|
@description = description
|
|
43
|
+
@experiment_id = experiment_id
|
|
41
44
|
@dataset_run_id = dataset_run_id
|
|
42
45
|
@dataset_run_url = dataset_run_url
|
|
43
46
|
end
|
|
@@ -33,6 +33,8 @@ module Langfuse
|
|
|
33
33
|
@description = description
|
|
34
34
|
@run_name = run_name || "#{name} - #{Time.now.utc.iso8601}"
|
|
35
35
|
@logger = Langfuse.configuration.logger
|
|
36
|
+
@fallback_experiment_id = ExperimentAttributes.generate_experiment_id
|
|
37
|
+
@resolved_experiment_id = nil
|
|
36
38
|
@dataset_run_id = nil
|
|
37
39
|
@dataset_id = nil
|
|
38
40
|
end
|
|
@@ -53,6 +55,7 @@ module Langfuse
|
|
|
53
55
|
run_evaluations: run_evals,
|
|
54
56
|
run_name: @run_name,
|
|
55
57
|
description: @description,
|
|
58
|
+
experiment_id: resolved_experiment_id,
|
|
56
59
|
dataset_run_id: @dataset_run_id,
|
|
57
60
|
dataset_run_url: build_dataset_run_url
|
|
58
61
|
)
|
|
@@ -68,7 +71,7 @@ module Langfuse
|
|
|
68
71
|
return ItemResult.new(item: item, trace_id: trace_id, observation_id: observation_id, error: task_error)
|
|
69
72
|
end
|
|
70
73
|
|
|
71
|
-
evaluations = execute_evaluators(item, output, trace_id)
|
|
74
|
+
evaluations = execute_evaluators(item, output, trace_id, observation_id)
|
|
72
75
|
ItemResult.new(item: item, output: output, trace_id: trace_id,
|
|
73
76
|
observation_id: observation_id, evaluations: evaluations)
|
|
74
77
|
end
|
|
@@ -77,21 +80,19 @@ module Langfuse
|
|
|
77
80
|
TracedExecution.call(
|
|
78
81
|
trace_name: "experiment-#{@name}",
|
|
79
82
|
input: item.input,
|
|
80
|
-
metadata:
|
|
81
|
-
task: ->(_span) { @task.call(item) }
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
link_to_dataset_run(item, trace_id, span.id) if item.is_a?(DatasetItemClient)
|
|
85
|
-
end
|
|
83
|
+
metadata: observation_metadata(item),
|
|
84
|
+
task: ->(_span) { @task.call(item) },
|
|
85
|
+
prepare_context: ->(span, trace_id) { prepare_experiment_context(item, span, trace_id) }
|
|
86
|
+
)
|
|
86
87
|
end
|
|
87
88
|
|
|
88
|
-
def execute_evaluators(item, output, trace_id)
|
|
89
|
+
def execute_evaluators(item, output, trace_id, observation_id)
|
|
89
90
|
evaluations = @evaluators.flat_map do |evaluator|
|
|
90
91
|
raw_result = call_evaluator(evaluator, item, output)
|
|
91
92
|
normalize_evaluator_result(raw_result, source: "Evaluator")
|
|
92
93
|
end
|
|
93
94
|
|
|
94
|
-
evaluations.each { |evaluation| persist_score(evaluation, trace_id) }
|
|
95
|
+
evaluations.each { |evaluation| persist_score(evaluation, trace_id, observation_id) }
|
|
95
96
|
evaluations
|
|
96
97
|
end
|
|
97
98
|
|
|
@@ -104,10 +105,11 @@ module Langfuse
|
|
|
104
105
|
nil
|
|
105
106
|
end
|
|
106
107
|
|
|
107
|
-
def persist_score(evaluation, trace_id)
|
|
108
|
+
def persist_score(evaluation, trace_id, observation_id)
|
|
108
109
|
@client.create_score(
|
|
109
110
|
name: evaluation.name, value: evaluation.value,
|
|
110
|
-
trace_id: trace_id,
|
|
111
|
+
trace_id: trace_id, observation_id: observation_id,
|
|
112
|
+
comment: evaluation.comment, data_type: evaluation.data_type,
|
|
111
113
|
config_id: evaluation.config_id, metadata: evaluation.metadata
|
|
112
114
|
)
|
|
113
115
|
rescue StandardError => e
|
|
@@ -145,13 +147,74 @@ module Langfuse
|
|
|
145
147
|
trace_id: trace_id, observation_id: observation_id,
|
|
146
148
|
metadata: @metadata, run_description: @description
|
|
147
149
|
)
|
|
148
|
-
|
|
149
|
-
@dataset_run_id = response&.dig("datasetRunId")
|
|
150
|
-
@dataset_id = item.dataset_id if @dataset_run_id
|
|
151
|
-
end
|
|
150
|
+
capture_dataset_run_identity(item, response)
|
|
152
151
|
response
|
|
153
152
|
rescue StandardError => e
|
|
154
153
|
@logger.warn("Dataset run item linking failed: #{e.message}")
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Do not expose a server run ID that differs from the identity already attached to observations.
|
|
158
|
+
def capture_dataset_run_identity(item, response)
|
|
159
|
+
candidate_id = response&.dig("datasetRunId")
|
|
160
|
+
return unless candidate_id
|
|
161
|
+
return if @resolved_experiment_id && @resolved_experiment_id != candidate_id
|
|
162
|
+
|
|
163
|
+
@dataset_run_id = candidate_id if @dataset_run_id.nil?
|
|
164
|
+
@dataset_id = item.dataset_id if @dataset_id.nil?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def prepare_experiment_context(item, span, trace_id)
|
|
168
|
+
response = link_to_dataset_run(item, trace_id, span.id) if item.is_a?(DatasetItemClient)
|
|
169
|
+
root_experiment_attributes(item).each do |key, value|
|
|
170
|
+
span.otel_span.set_attribute(key, value)
|
|
171
|
+
end
|
|
172
|
+
propagated_experiment_attributes(item, span.id, response)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def root_experiment_attributes(item)
|
|
176
|
+
ExperimentAttributes.root(
|
|
177
|
+
description: @description,
|
|
178
|
+
expected_output: item.expected_output,
|
|
179
|
+
mask: Langfuse.configuration.mask
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def propagated_experiment_attributes(item, observation_id, response)
|
|
184
|
+
ExperimentAttributes.propagated(
|
|
185
|
+
experiment_id: resolved_experiment_id(response),
|
|
186
|
+
run_name: @run_name,
|
|
187
|
+
dataset_id: item_dataset_id(item),
|
|
188
|
+
item_id: item_id(item),
|
|
189
|
+
root_observation_id: observation_id,
|
|
190
|
+
experiment_metadata: @metadata,
|
|
191
|
+
item_metadata: item_metadata(item),
|
|
192
|
+
mask: Langfuse.configuration.mask
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# A run keeps its first resolved identity even if later dataset links have a different result.
|
|
197
|
+
def resolved_experiment_id(response = nil)
|
|
198
|
+
@resolved_experiment_id ||= response&.dig("datasetRunId") || @fallback_experiment_id
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def observation_metadata(item)
|
|
202
|
+
ExperimentAttributes.observation_metadata(
|
|
203
|
+
name: @name,
|
|
204
|
+
run_name: @run_name,
|
|
205
|
+
experiment_metadata: @metadata,
|
|
206
|
+
item_metadata: item_metadata(item),
|
|
207
|
+
dataset_id: item_dataset_id(item),
|
|
208
|
+
dataset_item_id: item.is_a?(DatasetItemClient) ? item.id : nil
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def item_id(item)
|
|
213
|
+
item.is_a?(DatasetItemClient) ? item.id : ExperimentAttributes.generate_item_id(item.input)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def item_dataset_id(item)
|
|
217
|
+
item.dataset_id if item.is_a?(DatasetItemClient)
|
|
155
218
|
end
|
|
156
219
|
|
|
157
220
|
def flush_all
|
|
@@ -55,6 +55,17 @@ module Langfuse
|
|
|
55
55
|
OBSERVATION_COMPLETION_START_TIME = "langfuse.observation.completion_start_time"
|
|
56
56
|
IS_APP_ROOT = "langfuse.internal.is_app_root"
|
|
57
57
|
|
|
58
|
+
# Experiment attributes
|
|
59
|
+
EXPERIMENT_ID = "langfuse.experiment.id"
|
|
60
|
+
EXPERIMENT_NAME = "langfuse.experiment.name"
|
|
61
|
+
EXPERIMENT_DESCRIPTION = "langfuse.experiment.description"
|
|
62
|
+
EXPERIMENT_METADATA = "langfuse.experiment.metadata"
|
|
63
|
+
EXPERIMENT_DATASET_ID = "langfuse.experiment.dataset.id"
|
|
64
|
+
EXPERIMENT_ITEM_ID = "langfuse.experiment.item.id"
|
|
65
|
+
EXPERIMENT_ITEM_EXPECTED_OUTPUT = "langfuse.experiment.item.expected_output"
|
|
66
|
+
EXPERIMENT_ITEM_METADATA = "langfuse.experiment.item.metadata"
|
|
67
|
+
EXPERIMENT_ITEM_ROOT_OBSERVATION_ID = "langfuse.experiment.item.root_observation_id"
|
|
68
|
+
|
|
58
69
|
# Common attributes
|
|
59
70
|
VERSION = "langfuse.version"
|
|
60
71
|
RELEASE = "langfuse.release"
|
data/lib/langfuse/propagation.rb
CHANGED
|
@@ -30,6 +30,11 @@ module Langfuse
|
|
|
30
30
|
ENVIRONMENT_VALUE_PATTERN = /\A(?!langfuse)[a-z0-9_-]+\z/
|
|
31
31
|
private_constant :ENVIRONMENT_VALUE_PATTERN
|
|
32
32
|
|
|
33
|
+
EXPERIMENT_ATTRIBUTES_CONTEXT_KEY = OpenTelemetry::Context.create_key(
|
|
34
|
+
"#{BAGGAGE_PREFIX}experiment_attributes"
|
|
35
|
+
)
|
|
36
|
+
private_constant :EXPERIMENT_ATTRIBUTES_CONTEXT_KEY
|
|
37
|
+
|
|
33
38
|
# Map of propagated attribute keys to span attribute keys
|
|
34
39
|
SPAN_KEY_MAP = {
|
|
35
40
|
"user_id" => OtelAttributes::TRACE_USER_ID,
|
|
@@ -186,7 +191,25 @@ module Langfuse
|
|
|
186
191
|
end
|
|
187
192
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
188
193
|
|
|
189
|
-
|
|
194
|
+
experiment_attributes = context.value(EXPERIMENT_ATTRIBUTES_CONTEXT_KEY) || {}
|
|
195
|
+
propagated_attributes.merge(experiment_attributes)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Apply SDK-owned experiment attributes to the current span and future children.
|
|
199
|
+
#
|
|
200
|
+
# @param attributes [Hash<String, Object>] serialized OpenTelemetry experiment attributes
|
|
201
|
+
# @yield Block within which experiment attributes propagate
|
|
202
|
+
# @return [Object] the block result
|
|
203
|
+
# @api private
|
|
204
|
+
def self._with_experiment_attributes(attributes, &)
|
|
205
|
+
return yield if attributes.nil? || attributes.empty?
|
|
206
|
+
|
|
207
|
+
frozen_attributes = attributes.dup.freeze
|
|
208
|
+
current_span = OpenTelemetry::Trace.current_span
|
|
209
|
+
frozen_attributes.each { |key, value| current_span.set_attribute(key, value) } if current_span.recording?
|
|
210
|
+
|
|
211
|
+
context = OpenTelemetry::Context.current.set_value(EXPERIMENT_ATTRIBUTES_CONTEXT_KEY, frozen_attributes)
|
|
212
|
+
OpenTelemetry::Context.with_current(context, &)
|
|
190
213
|
end
|
|
191
214
|
|
|
192
215
|
# Merge metadata with existing context value
|
|
@@ -15,9 +15,9 @@ module Langfuse
|
|
|
15
15
|
# @param input [Object] input set on the root observation
|
|
16
16
|
# @param metadata [Hash] metadata set on the root observation and trace
|
|
17
17
|
# @param task [Proc] the callable to execute — receives the span
|
|
18
|
-
# @
|
|
18
|
+
# @param prepare_context [#call, nil] optional callable returning SDK-owned propagated attributes
|
|
19
19
|
# @return [Array<(Object, String, String, StandardError | nil)>] output, trace_id, observation_id, error
|
|
20
|
-
def self.call(trace_name:, input:, task:, metadata: {})
|
|
20
|
+
def self.call(trace_name:, input:, task:, metadata: {}, prepare_context: nil)
|
|
21
21
|
trace_id = nil
|
|
22
22
|
observation_id = nil
|
|
23
23
|
output = nil
|
|
@@ -26,9 +26,11 @@ module Langfuse
|
|
|
26
26
|
Langfuse.observe(trace_name, input: input, metadata: metadata) do |span|
|
|
27
27
|
trace_id = span.trace_id
|
|
28
28
|
observation_id = span.id
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
experiment_attributes = prepare_context&.call(span, trace_id) || {}
|
|
30
|
+
Propagation._with_experiment_attributes(experiment_attributes) do
|
|
31
|
+
Langfuse.propagate_attributes(trace_name: trace_name, metadata: metadata) do
|
|
32
|
+
output, task_error = execute_task(span, task)
|
|
33
|
+
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
|
data/lib/langfuse/version.rb
CHANGED
data/lib/langfuse.rb
CHANGED
|
@@ -74,6 +74,7 @@ require_relative "langfuse/otel_setup"
|
|
|
74
74
|
require_relative "langfuse/masking"
|
|
75
75
|
require_relative "langfuse/masking_exporter"
|
|
76
76
|
require_relative "langfuse/otel_attributes"
|
|
77
|
+
require_relative "langfuse/experiment_attributes"
|
|
77
78
|
require_relative "langfuse/propagation"
|
|
78
79
|
require_relative "langfuse/app_root_tracking"
|
|
79
80
|
require_relative "langfuse/span_processor"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: langfuse-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.12.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SimplePractice
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -197,6 +197,7 @@ files:
|
|
|
197
197
|
- lib/langfuse/dataset_item_client.rb
|
|
198
198
|
- lib/langfuse/evaluation.rb
|
|
199
199
|
- lib/langfuse/exit_hook.rb
|
|
200
|
+
- lib/langfuse/experiment_attributes.rb
|
|
200
201
|
- lib/langfuse/experiment_item.rb
|
|
201
202
|
- lib/langfuse/experiment_result.rb
|
|
202
203
|
- lib/langfuse/experiment_runner.rb
|