Class: DSPy::Context
- Inherits:
-
Object
- Object
- DSPy::Context
- Defined in:
- lib/dspy/context.rb
Class Method Summary collapse
Class Method Details
.clear! ⇒ Object
104 105 106 107 |
# File 'lib/dspy/context.rb', line 104 def clear! Thread.current[:dspy_context] = nil Fiber[:dspy_context] = nil end |
.current ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/dspy/context.rb', line 8 def current # Check if we're in an async context (fiber created by async gem) if in_async_context? # Use Fiber storage for async contexts to enable inheritance # Inherit from Thread.current if Fiber storage is not set Fiber[:dspy_context] ||= Thread.current[:dspy_context] || { trace_id: SecureRandom.uuid, span_stack: [] } # Return Fiber storage in async contexts Fiber[:dspy_context] else # Use Thread.current for regular synchronous contexts Thread.current[:dspy_context] ||= { trace_id: SecureRandom.uuid, span_stack: [] } # Also sync to Fiber storage so async contexts can inherit it Fiber[:dspy_context] = Thread.current[:dspy_context] Thread.current[:dspy_context] end end |
.with_span(operation:, **attributes) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/dspy/context.rb', line 34 def with_span(operation:, **attributes) span_id = SecureRandom.uuid parent_span_id = current[:span_stack].last start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) # Prepare attributes with context information span_attributes = { trace_id: current[:trace_id], span_id: span_id, parent_span_id: parent_span_id, operation: operation, **attributes } # Log span start with proper hierarchy (internal logging only) DSPy.log('span.start', **span_attributes) if DSPy::Observability.enabled? # Push to stack for child spans tracking current[:span_stack].push(span_id) begin # Use OpenTelemetry's proper context management for nesting if DSPy::Observability.enabled? && DSPy::Observability.tracer # Prepare attributes and add trace name for root spans span_attributes = attributes.transform_keys(&:to_s).reject { |k, v| v.nil? } # Set trace name if this is likely a root span (no parent in our stack) if current[:span_stack].length == 1 # This will be the first span span_attributes['langfuse.trace.name'] = operation end # Record start time for explicit duration tracking otel_start_time = Time.now DSPy::Observability.tracer.in_span( operation, attributes: span_attributes, kind: :internal ) do |span| result = yield(span) # Add explicit timing information to help Langfuse if span duration_ms = ((Time.now - otel_start_time) * 1000).round(3) span.set_attribute('duration.ms', duration_ms) span.set_attribute('langfuse.observation.startTime', otel_start_time.iso8601(3)) span.set_attribute('langfuse.observation.endTime', Time.now.iso8601(3)) end result end else yield(nil) end ensure # Pop from stack current[:span_stack].pop # Log span end with duration (internal logging only) if DSPy::Observability.enabled? duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2) DSPy.log('span.end', trace_id: current[:trace_id], span_id: span_id, duration_ms: duration_ms ) end end end |