Class: DSPy::RubyLLM::LM::Adapters::RubyLLMAdapter

Inherits:
LM::Adapter
  • Object
show all
Defined in:
lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb

Constant Summary collapse

SCOPED_OPTIONS =

Options that require a scoped context instead of global RubyLLM config

%i[base_url timeout max_retries].freeze

Instance Attribute Summary collapse

Attributes inherited from LM::Adapter

#api_key, #model

Instance Method Summary collapse

Constructor Details

#initialize(model:, api_key: nil, **options) ⇒ RubyLLMAdapter

Returns a new instance of RubyLLMAdapter.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 21

def initialize(model:, api_key: nil, **options)
  @api_key = api_key
  @options = options
  @structured_outputs_enabled = options.fetch(:structured_outputs, true)
  @provider_override = options[:provider] # Optional provider override

  # Detect provider eagerly (matches OpenAI/Anthropic/Gemini adapters)
  @provider = detect_provider(model)

  # Determine if we should use global RubyLLM config or create scoped context
  @use_global_config = should_use_global_config?(api_key, options)

  super(model: model, api_key: api_key)

  # Only validate API key if not using global config
  unless @use_global_config
    validate_api_key_for_provider!(api_key)
  end

  # Validate base_url if provided
  validate_base_url!(@options[:base_url])
end

Instance Attribute Details

#providerObject (readonly)

Returns the value of attribute provider.



16
17
18
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 16

def provider
  @provider
end

Instance Method Details

#chat(messages:, signature: nil, &block) ⇒ Object



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
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 49

def chat(messages:, signature: nil, &block)
  normalized_messages = normalize_messages(messages)

  # Validate vision support if images are present
  if contains_images?(normalized_messages)
    validate_vision_support!
    normalized_messages = format_multimodal_messages(normalized_messages)
  end

  chat_instance = create_chat_instance

  if block_given?
    stream_response(chat_instance, normalized_messages, signature, &block)
  else
    standard_response(chat_instance, normalized_messages, signature)
  end
rescue ::RubyLLM::UnauthorizedError => e
  raise DSPy::LM::MissingAPIKeyError.new(provider)
rescue ::RubyLLM::RateLimitError => e
  raise DSPy::LM::AdapterError, "Rate limit exceeded for #{provider}: #{e.message}"
rescue ::RubyLLM::ModelNotFoundError => e
  raise DSPy::LM::AdapterError, "Model not found: #{e.message}. Check available models with RubyLLM.models.all"
rescue ::RubyLLM::BadRequestError => e
  raise DSPy::LM::AdapterError, "Invalid request to #{provider}: #{e.message}"
rescue ::RubyLLM::ConfigurationError => e
  raise DSPy::LM::ConfigurationError, "RubyLLM configuration error: #{e.message}"
rescue ::RubyLLM::Error => e
  raise DSPy::LM::AdapterError, "RubyLLM error (#{provider}): #{e.message}"
end

#contextObject

Returns the context - either scoped or global



45
46
47
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 45

def context
  @context ||= @use_global_config ? nil : create_context(@api_key)
end