Class: RubyLLM::Speech

Inherits:
Object
  • Object
show all
Includes:
Accounting::Usage::Result, RubyLLM::Support::Inspectable
Defined in:
lib/ruby_llm/speech.rb

Overview

A Speech is audio generated from text. RubyLLM.speak returns one. It holds the raw audio bytes along with the model, voice, and format used.

speech = RubyLLM.speak "Hello, welcome to RubyLLM!"
speech.save "welcome.mp3"

Constant Summary collapse

MIME_TYPES =

Maps audio format names to their MIME types.

{
  'aac' => 'audio/aac',
  'flac' => 'audio/flac',
  'mp3' => 'audio/mpeg',
  'opus' => 'audio/opus',
  'pcm' => 'audio/pcm',
  'wav' => 'audio/wav'
}.freeze

Constants included from RubyLLM::Support::Inspectable

RubyLLM::Support::Inspectable::TRUNCATE_AT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Accounting::Usage::Result

#ruby_llm_usage_entries, #ruby_llm_usage_entries=

Methods included from RubyLLM::Support::Inspectable

#full_inspect, #inspect, #pretty_print

Constructor Details

#initialize(data:, model:, voice: nil, format: 'mp3', mime_type: nil, input_tokens: nil, output_tokens: nil) ⇒ Speech

:nodoc:



40
41
42
43
44
45
46
47
48
49
# File 'lib/ruby_llm/speech.rb', line 40

def initialize(data:, model:, voice: nil, format: 'mp3', mime_type: nil, # :nodoc:
               input_tokens: nil, output_tokens: nil)
  @data = data
  @model = model
  @voice = voice
  @format = (format || 'mp3').to_s
  @mime_type = mime_type || MIME_TYPES.fetch(@format, "audio/#{@format}")
  @input_tokens = input_tokens
  @output_tokens = output_tokens
end

Instance Attribute Details

#dataObject (readonly)

The raw audio bytes returned by the provider.



25
26
27
# File 'lib/ruby_llm/speech.rb', line 25

def data
  @data
end

#formatObject (readonly)

The audio format name, such as "mp3" or "pcm".



35
36
37
# File 'lib/ruby_llm/speech.rb', line 35

def format
  @format
end

#mime_typeObject (readonly)

The MIME type of the audio, such as "audio/mpeg".



38
39
40
# File 'lib/ruby_llm/speech.rb', line 38

def mime_type
  @mime_type
end

#modelObject (readonly)

The id of the model that generated the audio.



28
29
30
# File 'lib/ruby_llm/speech.rb', line 28

def model
  @model
end

#voiceObject (readonly)

The voice used for synthesis. When no voice: was given, this is the provider default.



32
33
34
# File 'lib/ruby_llm/speech.rb', line 32

def voice
  @voice
end

Class Method Details

.speak(input, model: nil, provider: nil, assume_model_exists: false, voice: nil, format: nil, context: nil, provider_options: {}, metadata: nil, &block) ⇒ Object

Generates speech for input and returns a Speech holding the audio. Uses config.default_speech_model unless model: is given. Pass provider: and assume_model_exists: true to use a model that is not in the registry. provider_options: takes options in the provider's request vocabulary, such as instructions: and speed: for OpenAI, and merges them into the request as-is.

speech = RubyLLM.speak "Hello, welcome to RubyLLM!"
speech.save "welcome.mp3"

RubyLLM.speak "Welcome back.", voice: "nova"
RubyLLM.speak "Save this as a WAV file.", format: "wav"
RubyLLM.speak "Say cheerfully: Have a wonderful day!",
            model: "gemini-3.1-flash-tts-preview", provider: :gemini

Given a block, yields SpeechChunk objects as audio arrives and still returns the complete Speech. Chunks contain consecutive bytes of the recording and are not separate audio files.

File.open("welcome.mp3", "wb") do |file|
RubyLLM.speak("Welcome back.") { |chunk| file.write(chunk.data) }
end

Raises RubyLLM::Error when the selected protocol cannot stream speech, or RubyLLM::ModelNotFoundError if model: is not in the registry.



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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ruby_llm/speech.rb', line 76

def self.speak(input,
               model: nil,
               provider: nil,
               assume_model_exists: false,
               voice: nil,
               format: nil,
               context: nil,
               provider_options: {},
               metadata: nil,
               &block)
  config = context&.config || RubyLLM.config
  model ||= config.default_speech_model
  model, provider_instance = Models.resolve(model, provider: provider, assume_model_exists: assume_model_exists,
                                                   config: config)
  empty_tokens = Tokens.new

  payload = {
    provider: provider_instance.slug,
    provider_class: provider_instance.name,
    model: model.id,
    model_info: model,
    input: input,
    voice: voice,
    format: format,
    provider_options: provider_options,
    metadata: ,
    streaming: !block.nil?,
    tokens: empty_tokens,
    cost: Cost.new(tokens: empty_tokens, model:, category: :audio_tokens)
  }

  RubyLLM.instrument('speech.ruby_llm', payload, config: config) do |event|
    result = provider_instance.speak(input, model:, voice:, format:, provider_options:, &block)
    event[:result] = result
    event[:response_model] = result.model
    event[:voice] = result.voice
    event[:format] = result.format
    event[:audio_bytes] = result.to_blob.bytesize
    event[:tokens] = result.tokens
    event[:cost] = result.cost
    result
  end
end

Instance Method Details

#costObject

Returns the speech cost across every provider attempt.



134
135
136
137
138
# File 'lib/ruby_llm/speech.rb', line 134

def cost
  return ruby_llm_usage_cost unless ruby_llm_usage_entries.empty?

  Cost.new(tokens:, model: model_info, category: :audio_tokens)
end

#inspect_attributesObject

:nodoc:



155
156
157
# File 'lib/ruby_llm/speech.rb', line 155

def inspect_attributes # :nodoc:
  { model: model, voice: voice, format: format, data: data && "#{data.bytesize} bytes" }
end

#model_infoObject

:nodoc:



140
141
142
143
144
# File 'lib/ruby_llm/speech.rb', line 140

def model_info # :nodoc:
  @model_info ||= RubyLLM.models.find(model)
rescue ModelNotFoundError
  nil
end

#save(path) ⇒ Object

Writes the audio to path in binary mode and returns path.

speech.save "welcome.mp3"


150
151
152
153
# File 'lib/ruby_llm/speech.rb', line 150

def save(path)
  File.binwrite(File.expand_path(path), to_blob)
  path
end

#to_blobObject

Returns the raw audio bytes. Alias for #data, mirroring Image#to_blob.



121
122
123
# File 'lib/ruby_llm/speech.rb', line 121

def to_blob
  data
end

#tokensObject

Returns provider-reported usage across every attempt. Its fields are nil when the provider did not report any.



127
128
129
130
131
# File 'lib/ruby_llm/speech.rb', line 127

def tokens
  return ruby_llm_usage_tokens unless ruby_llm_usage_entries.empty?

  Tokens.new(input: @input_tokens, output: @output_tokens)
end