Class: RainbowLLM::ChatBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/rainbow_llm/chat_builder.rb

Overview

Fluent API builder for chat requests with automatic failover across models

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(models) ⇒ ChatBuilder

Returns a new instance of ChatBuilder.



8
9
10
11
12
13
# File 'lib/rainbow_llm/chat_builder.rb', line 8

def initialize(models)
  @models = models
  @temperature = nil
  @schema = nil
  @timeout = nil
end

Instance Attribute Details

#modelsObject (readonly)

Returns the value of attribute models.



6
7
8
# File 'lib/rainbow_llm/chat_builder.rb', line 6

def models
  @models
end

#schemaObject (readonly)

Returns the value of attribute schema.



6
7
8
# File 'lib/rainbow_llm/chat_builder.rb', line 6

def schema
  @schema
end

#temperatureObject (readonly)

Returns the value of attribute temperature.



6
7
8
# File 'lib/rainbow_llm/chat_builder.rb', line 6

def temperature
  @temperature
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



6
7
8
# File 'lib/rainbow_llm/chat_builder.rb', line 6

def timeout
  @timeout
end

Instance Method Details

#ask(prompt) ⇒ Object

Raises:



27
28
29
30
31
32
33
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
# File 'lib/rainbow_llm/chat_builder.rb', line 27

def ask(prompt)
  raise InvalidArgument, "prompt cannot be nil or empty" if prompt.nil? || prompt.to_s.strip.empty?

  details = {}

  models.each do |model_string|
    # Split model string into provider and model parts
    provider, model = model_string.split("/", 2)

    if model.nil? || model.strip.empty?
      details[model_string] = {
        status: :error,
        error: "Invalid model format: '#{model_string}'. Expected format: 'provider/model_name'"
      }
      next
    end

    context =
      if RainbowLLM.configuration.providers.key?(provider.to_sym)
        RubyLLM.context do |c|
          c.request_timeout = @timeout if @timeout
          c.openai_api_base = RainbowLLM.configuration.providers.dig(provider.to_sym, :uri_base)
          c.openai_api_key = RainbowLLM.configuration.providers.dig(provider.to_sym, :access_token)
        end
      elsif @timeout
        RubyLLM.context do |c|
          c.request_timeout = @timeout if @timeout
        end
      end

    provider_type = RainbowLLM.configuration.providers.dig(provider.to_sym, :provider) || provider
    assume_model_exists = RainbowLLM.configuration.providers.dig(provider.to_sym, :assume_model_exists)

    opts = {model:, provider: provider_type, assume_model_exists:, context:}.compact

    result =
      begin
        chat = RubyLLM.chat(**opts)
        chat = chat.with_temperature(temperature) if temperature
        chat = chat.with_schema(schema) if schema
        response = chat.ask(prompt)
        content = check_content(extract_content(response))

        if content
          details[model_string] = {status: :success}
          {content:, model: model_string, status: :success}
        else
          details[model_string] = {status: :empty}
          nil
        end
      rescue Faraday::TimeoutError => e
        warn "Provider #{provider} failed with timeout error: #{e.message}"
        details[model_string] = {status: :timeout, error: e.message}
        nil
      rescue RubyLLM::Error => e
        # Log the error for debugging purposes
        warn "Provider #{provider} failed with error: #{e.message}"
        details[model_string] = {status: :error, error: e.message}
        nil
      end

    if result && result[:status] == :success
      return Response.new(content: result[:content], model: result[:model], details:)
    end
  end

  # If we get here, all providers failed
  Response.new(content: nil, model: nil, details:)
end

#with_schema(value) ⇒ Object



19
20
21
# File 'lib/rainbow_llm/chat_builder.rb', line 19

def with_schema(value)
  dup.tap { |builder| builder.instance_variable_set(:@schema, value) }
end

#with_temperature(value) ⇒ Object



15
16
17
# File 'lib/rainbow_llm/chat_builder.rb', line 15

def with_temperature(value)
  dup.tap { |builder| builder.instance_variable_set(:@temperature, value) }
end

#with_timeout(seconds) ⇒ Object



23
24
25
# File 'lib/rainbow_llm/chat_builder.rb', line 23

def with_timeout(seconds)
  dup.tap { |builder| builder.instance_variable_set(:@timeout, seconds) }
end