Module: RubyLLM::Protocols::ChatCompletions::Transcription
- Defined in:
- lib/ruby_llm/protocols/chat_completions/transcription.rb
Overview
Audio transcription methods for the OpenAI API integration
Class Method Summary collapse
- .build_streamed_transcription(chunks, model:) ⇒ Object
- .build_transcription_chunk(data) ⇒ Object
-
.default_response_format(model) ⇒ Object
Diarization models return plain text with no segments unless the response format asks for them.
- .encode_speaker_references(references) ⇒ Object
- .parse_transcription_response(response, model:) ⇒ Object
- .render_transcription_options(timestamps:, format:, streaming:) ⇒ Object
- .render_transcription_payload(file_part, model:, language:, format: nil, speaker_names: nil, speaker_references: nil, provider_options: {}, prompt: nil, temperature: nil) ⇒ Object
- .reported_cost(_usage) ⇒ Object
-
.stream_transcription(payload, model:, &block) ⇒ Object
OpenAI streams transcriptions as server-sent events carrying text deltas, completed segments on diarization models, and a final event with the whole transcript and its usage.
-
.streamed_transcript_text(chunks) ⇒ Object
Diarization models stream segments instead of deltas, so the transcript is rebuilt from whichever the provider sent.
- .streamed_transcription_segments(chunks, data) ⇒ Object
- .transcription_duration(usage) ⇒ Object
- .transcription_tokens(usage) ⇒ Object
- .transcription_url ⇒ Object
Class Method Details
.build_streamed_transcription(chunks, model:) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 88 def build_streamed_transcription(chunks, model:) final = chunks.reverse.find(&:done?) data = final&.raw || {} usage = data['usage'] || {} RubyLLM::Transcription.new( text: final&.text || streamed_transcript_text(chunks), model: model, language: data['language'], duration: transcription_duration(usage), segments: streamed_transcription_segments(chunks, data), reported_cost: reported_cost(usage), **transcription_tokens(usage) ) end |
.build_transcription_chunk(data) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 76 def build_transcription_chunk(data) type = data['type'] RubyLLM::TranscriptionChunk.new( type: type, delta: data['delta'], text: (data['text'] if type == RubyLLM::TranscriptionChunk::DONE), segment: (data.except('type') if type == RubyLLM::TranscriptionChunk::SEGMENT), raw: data ) end |
.default_response_format(model) ⇒ Object
Diarization models return plain text with no segments unless the response format asks for them.
57 58 59 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 57 def default_response_format(model) 'diarized_json' if model.include?('diarize') end |
.encode_speaker_references(references) ⇒ Object
43 44 45 46 47 48 49 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 43 def encode_speaker_references(references) return nil unless references references.map do |ref| Attachment.new(ref, config: @config).for_llm end end |
.parse_transcription_response(response, model:) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 118 def parse_transcription_response(response, model:) data = response.body return RubyLLM::Transcription.new(text: data, model: model) if data.is_a?(String) usage = data['usage'] || {} RubyLLM::Transcription.new( text: data['text'], model: model, language: data['language'], duration: data['duration'] || transcription_duration(usage), segments: data['segments'], words: data['words'], reported_cost: reported_cost(usage), **transcription_tokens(usage) ) end |
.render_transcription_options(timestamps:, format:, streaming:) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 14 def (timestamps:, format:, streaming:) return {} if .nil? values = Array().map(&:to_s) unless values.any? && (values - %w[word segment]).empty? raise ArgumentError, 'Transcription timestamps must be word or segment' end if streaming || (format && format != 'verbose_json') raise ArgumentError, 'Transcription timestamps require a non-streaming verbose_json response' end { response_format: 'verbose_json', timestamp_granularities: values } end |
.render_transcription_payload(file_part, model:, language:, format: nil, speaker_names: nil, speaker_references: nil, provider_options: {}, prompt: nil, temperature: nil) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 28 def render_transcription_payload(file_part, model:, language:, format: nil, speaker_names: nil, speaker_references: nil, provider_options: {}, prompt: nil, temperature: nil) { model: model, file: file_part, language: language, response_format: format || default_response_format(model), prompt: prompt, temperature: temperature, known_speaker_names: speaker_names, known_speaker_references: encode_speaker_references(speaker_references) }.compact.merge() end |
.reported_cost(_usage) ⇒ Object
51 52 53 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 51 def reported_cost(_usage) nil end |
.stream_transcription(payload, model:, &block) ⇒ Object
OpenAI streams transcriptions as server-sent events carrying text deltas, completed segments on diarization models, and a final event with the whole transcript and its usage.
64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 64 def stream_transcription(payload, model:, &block) chunks = [] stream_events(transcription_url, payload.merge(stream: 'true')) do |data| chunk = build_transcription_chunk(data) chunks << chunk block.call chunk end build_streamed_transcription(chunks, model: model) end |
.streamed_transcript_text(chunks) ⇒ Object
Diarization models stream segments instead of deltas, so the transcript is rebuilt from whichever the provider sent.
106 107 108 109 110 111 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 106 def streamed_transcript_text(chunks) deltas = chunks.filter_map(&:delta) return deltas.join if deltas.any? chunks.filter_map { |chunk| chunk.segment&.fetch('text', nil) }.join(' ') end |
.streamed_transcription_segments(chunks, data) ⇒ Object
113 114 115 116 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 113 def streamed_transcription_segments(chunks, data) segments = data['segments'] || chunks.filter_map(&:segment) segments.empty? ? nil : segments end |
.transcription_duration(usage) ⇒ Object
144 145 146 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 144 def transcription_duration(usage) usage['seconds'] end |
.transcription_tokens(usage) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 137 def transcription_tokens(usage) { input_tokens: usage['input_tokens'] || usage['prompt_tokens'], output_tokens: usage['output_tokens'] || usage['completion_tokens'] } end |
.transcription_url ⇒ Object
10 11 12 |
# File 'lib/ruby_llm/protocols/chat_completions/transcription.rb', line 10 def transcription_url 'audio/transcriptions' end |