langfuse-rb 0.12.3 → 0.12.4
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 +8 -1
- data/lib/langfuse/otel_attributes.rb +1 -0
- data/lib/langfuse/otel_setup.rb +2 -1
- data/lib/langfuse/root_span_id_generator.rb +38 -0
- data/lib/langfuse/version.rb +1 -1
- data/lib/langfuse.rb +35 -12
- 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: 48ce9deac30bb5cbbb9df10203bf6a7ea930fc43fa14d29170bdf41f672c9213
|
|
4
|
+
data.tar.gz: 7fec96c181ce22befedbafefd937ab37114677abe82adf1fc71fa5903b0b5b02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4acb19d60809504ec0e9f037de4633d43758fe8ce894d4924d0fead4f6e5a37a792d77d6c3cc432776713dd2124a9ee33748bf5d811aacf1b5a731f5e92b9445
|
|
7
|
+
data.tar.gz: f639e18b59bd80cea44f99b0db5458a833f05c42eef252dba7018b7537b752966995656b47b322c7aadcfc5e152e95f20612693c897ac8d0015c17fdfc2feacd
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.12.4] - 2026-10-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Mark explicit-ID observations as roots for legacy trace input/output (#129).
|
|
14
|
+
- Create parentless roots for explicit `trace_id:` values and apply root sampling (#128).
|
|
15
|
+
|
|
10
16
|
## [0.12.3] - 2026-09-15
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
@@ -188,7 +194,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
188
194
|
- Migrated from legacy ingestion API to OTLP endpoint
|
|
189
195
|
- Removed `tracing_enabled` configuration flag (#2)
|
|
190
196
|
|
|
191
|
-
[Unreleased]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.
|
|
197
|
+
[Unreleased]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.4...HEAD
|
|
198
|
+
[0.12.4]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.3...v0.12.4
|
|
192
199
|
[0.12.3]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.2...v0.12.3
|
|
193
200
|
[0.12.2]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.1...v0.12.2
|
|
194
201
|
[0.12.1]: https://github.com/simplepractice/langfuse-rb/compare/v0.12.0...v0.12.1
|
|
@@ -53,6 +53,7 @@ module Langfuse
|
|
|
53
53
|
OBSERVATION_PROMPT_NAME = "langfuse.observation.prompt.name"
|
|
54
54
|
OBSERVATION_PROMPT_VERSION = "langfuse.observation.prompt.version"
|
|
55
55
|
OBSERVATION_COMPLETION_START_TIME = "langfuse.observation.completion_start_time"
|
|
56
|
+
AS_ROOT = "langfuse.internal.as_root"
|
|
56
57
|
IS_APP_ROOT = "langfuse.internal.is_app_root"
|
|
57
58
|
|
|
58
59
|
# Experiment attributes
|
data/lib/langfuse/otel_setup.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "base64"
|
|
|
6
6
|
require_relative "masking_exporter"
|
|
7
7
|
require_relative "trace_export_guard"
|
|
8
8
|
require_relative "resilient_metrics_reporter"
|
|
9
|
+
require_relative "root_span_id_generator"
|
|
9
10
|
|
|
10
11
|
module Langfuse
|
|
11
12
|
# OpenTelemetry initialization and setup for Langfuse tracing.
|
|
@@ -122,7 +123,7 @@ module Langfuse
|
|
|
122
123
|
)
|
|
123
124
|
|
|
124
125
|
OpenTelemetry::SDK::Trace::TracerProvider
|
|
125
|
-
.new(sampler: build_sampler(config.sample_rate))
|
|
126
|
+
.new(sampler: build_sampler(config.sample_rate), id_generator: RootSpanIdGenerator.new)
|
|
126
127
|
.tap { |provider| provider.add_span_processor(processor) }
|
|
127
128
|
end
|
|
128
129
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Langfuse
|
|
4
|
+
# Supplies deterministic IDs only during explicit root creation on one provider.
|
|
5
|
+
#
|
|
6
|
+
# @api private
|
|
7
|
+
class RootSpanIdGenerator
|
|
8
|
+
def initialize
|
|
9
|
+
@trace_id_key = OpenTelemetry::Context.create_key("langfuse-root-trace-id")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate_trace_id
|
|
13
|
+
OpenTelemetry::Context.value(@trace_id_key) || OpenTelemetry::Trace.generate_trace_id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generate_span_id
|
|
17
|
+
OpenTelemetry::Trace.generate_span_id
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# A trace ID identifies a trace, not a missing parent. Preserve propagated
|
|
21
|
+
# attributes while clearing the ambient span and its internal root claim.
|
|
22
|
+
# The provider-local key and Context scope isolate concurrent fibers/threads
|
|
23
|
+
# and restore the caller's context even when span creation raises.
|
|
24
|
+
#
|
|
25
|
+
# @param trace_id [String] Validated binary W3C trace ID
|
|
26
|
+
# @yieldparam context [OpenTelemetry::Context] Parentless root context
|
|
27
|
+
# @return [Object] The block result
|
|
28
|
+
# @api private
|
|
29
|
+
def with_trace_id(trace_id)
|
|
30
|
+
context = OpenTelemetry::Trace.context_with_span(OpenTelemetry::Trace::Span::INVALID)
|
|
31
|
+
if Propagation.baggage_available?
|
|
32
|
+
context = OpenTelemetry::Baggage.remove_value(Propagation::LANGFUSE_TRACE_ID_BAGGAGE_KEY, context: context)
|
|
33
|
+
end
|
|
34
|
+
context = context.set_value(@trace_id_key, trace_id)
|
|
35
|
+
OpenTelemetry::Context.with_current(context) { yield context }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/langfuse/version.rb
CHANGED
data/lib/langfuse.rb
CHANGED
|
@@ -463,14 +463,15 @@ module Langfuse
|
|
|
463
463
|
# Creates a new observation (root or child)
|
|
464
464
|
#
|
|
465
465
|
# This is the module-level factory method that creates observations of any type.
|
|
466
|
-
#
|
|
467
|
-
#
|
|
466
|
+
# An explicit trace_id creates a parentless root. Otherwise it inherits the
|
|
467
|
+
# active span, or the supplied parent_span_context for child observations.
|
|
468
468
|
#
|
|
469
469
|
# @param name [String] Descriptive name for the observation
|
|
470
470
|
# @param attrs [Hash, Types::SpanAttributes, Types::GenerationAttributes, nil] Observation attributes
|
|
471
471
|
# @param as_type [Symbol, String] Observation type (:span, :generation, :event, etc.)
|
|
472
|
-
# @param trace_id [String, nil] Optional 32-char lowercase hex trace ID
|
|
472
|
+
# @param trace_id [String, nil] Optional 32-char lowercase hex trace ID for a new parentless root.
|
|
473
473
|
# Mutually exclusive with `parent_span_context`. Use {Langfuse.create_trace_id} to generate one.
|
|
474
|
+
# Also marks the observation as a root for legacy trace summaries.
|
|
474
475
|
# @param parent_span_context [OpenTelemetry::Trace::SpanContext, nil] Parent span context for child observations
|
|
475
476
|
# @param start_time [Time, Integer, nil] Optional start time (Time object or Unix timestamp in nanoseconds)
|
|
476
477
|
# @param skip_validation [Boolean] Skip validation (for internal use). Defaults to false.
|
|
@@ -496,13 +497,17 @@ module Langfuse
|
|
|
496
497
|
type_str = as_type.to_s
|
|
497
498
|
validate_observation_type!(as_type, type_str) unless skip_validation
|
|
498
499
|
|
|
499
|
-
otel_tracer =
|
|
500
|
+
otel_tracer, id_generator = otel_tracer_and_id_generator
|
|
500
501
|
otel_span = create_otel_span(
|
|
501
502
|
name: name,
|
|
502
503
|
start_time: start_time,
|
|
503
504
|
parent_span_context: parent_span_context,
|
|
504
|
-
otel_tracer: otel_tracer
|
|
505
|
+
otel_tracer: otel_tracer,
|
|
506
|
+
trace_id: trace_id && parent_span_context.trace_id,
|
|
507
|
+
id_generator: id_generator
|
|
505
508
|
)
|
|
509
|
+
# Match Python's explicit trace-context root mark for legacy trace input/output.
|
|
510
|
+
otel_span.set_attribute(OtelAttributes::AS_ROOT, true) if trace_id
|
|
506
511
|
apply_observation_attributes(otel_span, type_str, attrs)
|
|
507
512
|
|
|
508
513
|
observation = wrap_otel_span(otel_span, type_str, otel_tracer)
|
|
@@ -517,7 +522,7 @@ module Langfuse
|
|
|
517
522
|
# @param name [String] Descriptive name for the observation
|
|
518
523
|
# @param attrs [Hash] Observation attributes (optional positional or keyword)
|
|
519
524
|
# @param as_type [Symbol, String] Observation type (:span, :generation, :event, etc.)
|
|
520
|
-
# @param trace_id [String, nil] Optional 32-char lowercase hex trace ID
|
|
525
|
+
# @param trace_id [String, nil] Optional 32-char lowercase hex trace ID for a new parentless root.
|
|
521
526
|
# Use {Langfuse.create_trace_id} to generate one. Forwarded to {.start_observation}.
|
|
522
527
|
# @param kwargs [Hash] Additional keyword arguments merged into observation attributes (e.g., input:, output:, metadata:)
|
|
523
528
|
# @yield [observation] Optional block that receives the observation object
|
|
@@ -605,13 +610,19 @@ module Langfuse
|
|
|
605
610
|
false
|
|
606
611
|
end
|
|
607
612
|
|
|
608
|
-
# Gets the OpenTelemetry tracer for Langfuse
|
|
613
|
+
# Gets the OpenTelemetry tracer and its root ID generator for Langfuse
|
|
609
614
|
#
|
|
610
|
-
#
|
|
611
|
-
|
|
612
|
-
|
|
615
|
+
# Capture the ID generator from the same provider as the returned tracer,
|
|
616
|
+
# so concurrent shutdown or reconfiguration cannot switch its root context key.
|
|
617
|
+
# @return [Array] The OTel tracer and optional root ID generator
|
|
618
|
+
# @api private
|
|
619
|
+
def otel_tracer_and_id_generator
|
|
620
|
+
return [noop_tracer, nil] unless ensure_tracing_started
|
|
613
621
|
|
|
614
|
-
|
|
622
|
+
provider = tracer_provider
|
|
623
|
+
id_generator = provider.id_generator if provider.respond_to?(:id_generator)
|
|
624
|
+
id_generator = nil unless id_generator.is_a?(RootSpanIdGenerator)
|
|
625
|
+
[provider.tracer(LANGFUSE_TRACER_NAME, Langfuse::VERSION), id_generator]
|
|
615
626
|
end
|
|
616
627
|
|
|
617
628
|
# Creates an OpenTelemetry span (root or child)
|
|
@@ -620,8 +631,20 @@ module Langfuse
|
|
|
620
631
|
# @param start_time [Time, Integer, nil] Optional start time
|
|
621
632
|
# @param parent_span_context [OpenTelemetry::Trace::SpanContext, nil] Parent span context
|
|
622
633
|
# @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer
|
|
634
|
+
# @param trace_id [String, nil] Validated binary trace ID for an explicit root
|
|
635
|
+
# @param id_generator [RootSpanIdGenerator, nil] ID generator captured with the tracer
|
|
623
636
|
# @return [OpenTelemetry::SDK::Trace::Span] The created span
|
|
624
|
-
|
|
637
|
+
# @api private
|
|
638
|
+
def create_otel_span(name:, otel_tracer:, start_time: nil, parent_span_context: nil, trace_id: nil,
|
|
639
|
+
id_generator: nil)
|
|
640
|
+
if trace_id && id_generator
|
|
641
|
+
# Start an actual root through the internal provider's ID generator, so
|
|
642
|
+
# native root sampling sees an invalid parent rather than a sampled placeholder.
|
|
643
|
+
return id_generator.with_trace_id(trace_id) do |context|
|
|
644
|
+
otel_tracer.start_span(name, with_parent: context, start_timestamp: start_time)
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
|
|
625
648
|
if parent_span_context
|
|
626
649
|
# Create child span with parent context
|
|
627
650
|
# Create a non-recording span from the parent context to set in context
|
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.4
|
|
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-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -222,6 +222,7 @@ files:
|
|
|
222
222
|
- lib/langfuse/rails_cache_adapter.rb
|
|
223
223
|
- lib/langfuse/read_api.rb
|
|
224
224
|
- lib/langfuse/resilient_metrics_reporter.rb
|
|
225
|
+
- lib/langfuse/root_span_id_generator.rb
|
|
225
226
|
- lib/langfuse/sampling.rb
|
|
226
227
|
- lib/langfuse/score_client.rb
|
|
227
228
|
- lib/langfuse/score_value.rb
|