Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[BUG] Streaming transcription spans omit the stream flag

Open Beginner friendly
#1,271 0 comments 1 reaction 0 assignees View on GitHub

Maintainers usually reply within 1 day

@yorzi is already working on this.

Since Oct 23, 2026.

  • #1274 by @yorzi — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
ruby
Domain
observability

Research direction

Start with the transcription payload construction used by RubyLLM.transcribe and compare it with the existing streaming payload paths for speech and chat. Run CI=1 SKIP_COVERAGE=1 bundle exec rspec /tmp/transcription_stream_span_spec.rb; the streamed cases should include gen_ai.request.stream: true, while the buffered case should omit it and preserve span context and error behavior.

Written by the indexing model from the issue text.

Description

bug
Basic checks
  • I searched existing issues - this hasn't been reported
  • I can reproduce this consistently
  • This is a RubyLLM bug, not my application code
What's broken?

RubyLLM.transcribe streams correctly when given a block, but its OpenTelemetry span omits gen_ai.request.stream. This happens on a successful transcription and when the callback raises. The transcription payload never sets the existing streaming flag, unlike speech and chat.

Applications filtering spans by streaming mode cannot identify these transcription requests.

How to reproduce

From the repository root, save this as /tmp/transcription_stream_span_spec.rb. Run CI=1 SKIP_COVERAGE=1 bundle exec rspec /tmp/transcription_stream_span_spec.rb. The HTTP response is stubbed; no provider credentials or external request are needed.

# frozen_string_literal: true

require 'spec_helper'
require File.expand_path('spec/support/open_telemetry', Dir.pwd)

RSpec.describe 'Transcription streaming span reproduction' do
  include_context 'with configured RubyLLM'
  include_context 'with OpenTelemetry tracing'

  describe 'transcription spans' do
    let(:audio) { File.expand_path('spec/fixtures/ruby.wav', Dir.pwd) }

    def stub_transcription_stream
      events = [
        { type: 'transcript.text.delta', delta: 'private transcript' },
        { type: 'transcript.text.done', text: 'private transcript', usage: { input_tokens: 3, output_tokens: 1 } }
      ].map { |event| "data: #{event.to_json}\n\n" }.join
      stub_request(:post, 'https://api.openai.com/v1/audio/transcriptions')
        .to_return(body: events, headers: { 'Content-Type' => 'text/event-stream' })
    end

    it 'marks a streamed transcription and keeps the span active through its callbacks' do
      stub_transcription_stream
      chunks = []
      result = RubyLLM.transcribe(audio, model: model_for(:openai, :streaming_transcription), provider: :openai) do |chunk|
        chunks << chunk
        expect(spans).to be_empty
        expect(OpenTelemetry::Trace.current_span.recording?).to be(true)
      end

      expect(result.text).to eq('private transcript')
      expect(chunks.last).to be_done
      expect(spans.sole.attributes).to include('gen_ai.operation.name' => 'transcription', 'gen_ai.request.stream' => true)
      expect(spans.sole.attributes.to_s).not_to include('private')
    end

    it 'keeps the streaming flag when the transcription callback raises' do
      stub_transcription_stream
      previous = OpenTelemetry::Context.current

      expect do
        RubyLLM.transcribe(audio, model: model_for(:openai, :streaming_transcription), provider: :openai) do
          raise 'private callback error'
        end
      end.to raise_error(RuntimeError, 'private callback error')

      expect(OpenTelemetry::Context.current).to equal(previous)
      expect(spans.sole.status.code).to eq(OpenTelemetry::Trace::Status::ERROR)
      expect(spans.sole.attributes).to include('gen_ai.request.stream' => true, 'error.type' => 'RuntimeError')
      expect(spans.sole.attributes.to_s).not_to include('private')
    end

    it 'omits the streaming flag from a buffered transcription' do
      stub_request(:post, 'https://api.openai.com/v1/audio/transcriptions')
        .to_return_json(body: { text: 'private transcript', usage: { input_tokens: 3, output_tokens: 1 } })

      result = RubyLLM.transcribe(audio, model: model_for(:openai, :streaming_transcription), provider: :openai)

      expect(result.text).to eq('private transcript')
      expect(spans.sole.attributes).not_to have_key('gen_ai.request.stream')
    end
  end

end
Expected behavior

Streamed transcription spans include gen_ai.request.stream: true, including when a callback raises. A buffered transcription keeps the attribute omitted. Return values, callback errors, and span context remain unchanged.

What actually happened

On upstream main, the two streaming examples fail because the attribute is missing. The buffered guard already passes. The operation still delivers the transcript, and the raising callback still produces an error span.

I have a one-line local payload correction with regression specs for all three paths.

Environment
  • Ruby: 3.3.5
  • RubyLLM: upstream main a32aa84ba9bc387580598c555ab068c6ccd0f56c
  • Provider: OpenAI transcription, using stubbed SSE replies
  • OS: macOS
  • OpenTelemetry: repository development SDK and in-memory exporter
Dominant language
Ruby
Stars
4.5k
Forks
523
Avg merge
9h 26m
Merged PRs (30d)
213

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from crmne/ruby_llm

All issues in crmne/ruby_llm

Similar issues

More Ruby issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.