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.



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

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.



13
14
15
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 13

def provider
  @provider
end

Instance Method Details

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



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

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

  validate_document_support!(normalized_messages)

  # Validate vision support if images are present
  if contains_images?(normalized_messages)
    validate_vision_support!
    normalized_messages = format_multimodal_messages(normalized_messages, provider)
  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



42
43
44
# File 'lib/dspy/ruby_llm/lm/adapters/ruby_llm_adapter.rb', line 42

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