Class: DSPy::LM

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dspy/lm.rb,
lib/dspy/lm/usage.rb,
lib/dspy/lm/errors.rb,
lib/dspy/lm/adapter.rb,
lib/dspy/lm/message.rb,
lib/dspy/lm/response.rb,
lib/dspy/lm/cache_manager.rb,
lib/dspy/lm/retry_handler.rb,
lib/dspy/lm/vision_models.rb,
lib/dspy/lm/adapter_factory.rb,
lib/dspy/lm/message_builder.rb,
lib/dspy/lm/strategy_selector.rb,
lib/dspy/lm/adapters/gemini_adapter.rb,
lib/dspy/lm/adapters/ollama_adapter.rb,
lib/dspy/lm/adapters/openai_adapter.rb,
lib/dspy/lm/strategies/base_strategy.rb,
lib/dspy/lm/adapters/anthropic_adapter.rb,
lib/dspy/lm/structured_output_strategy.rb,
lib/dspy/lm/adapters/openai/schema_converter.rb,
lib/dspy/lm/strategies/anthropic_tool_use_strategy.rb,
lib/dspy/lm/strategies/enhanced_prompting_strategy.rb,
lib/dspy/lm/strategies/anthropic_extraction_strategy.rb,
lib/dspy/lm/strategies/openai_structured_output_strategy.rb

Defined Under Namespace

Modules: Adapters, MessageFactory, ResponseMetadataFactory, Strategies, UsageFactory, VisionModels Classes: Adapter, AdapterError, AdapterFactory, AnthropicAdapter, AnthropicResponseMetadata, CacheManager, ConfigurationError, Error, GeminiAdapter, GeminiResponseMetadata, IncompatibleImageFeatureError, Message, MessageBuilder, MissingAPIKeyError, OllamaAdapter, OpenAIAdapter, OpenAIResponseMetadata, OpenAIUsage, Response, ResponseMetadata, RetryHandler, StrategySelector, StructuredOutputStrategy, UnsupportedProviderError, Usage

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model_id, api_key: nil, **options) ⇒ LM

Returns a new instance of LM.



33
34
35
36
37
38
39
40
41
42
# File 'lib/dspy/lm.rb', line 33

def initialize(model_id, api_key: nil, **options)
  @model_id = model_id
  @api_key = api_key
  
  # Parse provider and model from model_id
  @provider, @model = parse_model_id(model_id)
  
  # Create appropriate adapter with options
  @adapter = AdapterFactory.create(model_id, api_key: api_key, **options)
end

Instance Attribute Details

#adapterObject (readonly)

Returns the value of attribute adapter.



31
32
33
# File 'lib/dspy/lm.rb', line 31

def adapter
  @adapter
end

#api_keyObject (readonly)

Returns the value of attribute api_key.



31
32
33
# File 'lib/dspy/lm.rb', line 31

def api_key
  @api_key
end

#modelObject (readonly)

Returns the value of attribute model.



31
32
33
# File 'lib/dspy/lm.rb', line 31

def model
  @model
end

#model_idObject (readonly)

Returns the value of attribute model_id.



31
32
33
# File 'lib/dspy/lm.rb', line 31

def model_id
  @model_id
end

#providerObject (readonly)

Returns the value of attribute provider.



31
32
33
# File 'lib/dspy/lm.rb', line 31

def provider
  @provider
end

Class Method Details

.cache_managerObject



146
147
148
# File 'lib/dspy/lm/cache_manager.rb', line 146

def cache_manager
  @cache_manager ||= CacheManager.new
end

Instance Method Details

#chat(inference_module, input_values, &block) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/dspy/lm.rb', line 44

def chat(inference_module, input_values, &block)
  Sync do
    signature_class = inference_module.signature_class
    
    # Build messages from inference module
    messages = build_messages(inference_module, input_values)
    
    # Execute with instrumentation
    response = instrument_lm_request(messages, signature_class.name) do
      chat_with_strategy(messages, signature_class, &block)
    end
    
    # Parse response (no longer needs separate instrumentation)
    parsed_result = parse_response(response, input_values, signature_class)
    
    parsed_result
  end
end

#execute_raw_chat(messages, &streaming_block) ⇒ Object



343
344
345
346
347
348
349
350
351
352
353
# File 'lib/dspy/lm.rb', line 343

def execute_raw_chat(messages, &streaming_block)
  response = instrument_lm_request(messages, 'RawPrompt') do
    # Convert messages to hash format for adapter
    hash_messages = messages_to_hash_array(messages)
    # Direct adapter call, no strategies or JSON parsing
    adapter.chat(messages: hash_messages, signature: nil, &streaming_block)
  end
  
  # Return raw response content, not parsed JSON
  response.content
end

#messages_to_hash_array(messages) ⇒ Object

Convert Message objects to hash array for adapters



396
397
398
399
400
401
402
403
404
# File 'lib/dspy/lm.rb', line 396

def messages_to_hash_array(messages)
  messages.map do |msg|
    if msg.is_a?(Message)
      msg.to_h
    else
      msg
    end
  end
end

#normalize_messages(messages) ⇒ Object

Convert messages to normalized Message objects



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/dspy/lm.rb', line 356

def normalize_messages(messages)
  # Validate array format first
  unless messages.is_a?(Array)
    raise ArgumentError, "messages must be an array"
  end
  
  return messages if messages.all? { |m| m.is_a?(Message) }
  
  # Convert hash messages to Message objects
  normalized = []
  messages.each_with_index do |msg, index|
    if msg.is_a?(Message)
      normalized << msg
    elsif msg.is_a?(Hash)
      # Validate hash has required fields
      unless msg.key?(:role) && msg.key?(:content)
        raise ArgumentError, "Message at index #{index} must have :role and :content"
      end
      
      # Validate role
      valid_roles = %w[system user assistant]
      unless valid_roles.include?(msg[:role])
        raise ArgumentError, "Invalid role at index #{index}: #{msg[:role]}. Must be one of: #{valid_roles.join(', ')}"
      end
      
      # Create Message object
      message = MessageFactory.create(msg)
      if message.nil?
        raise ArgumentError, "Failed to create Message from hash at index #{index}"
      end
      normalized << message
    else
      raise ArgumentError, "Message at index #{index} must be a Message object or hash with :role and :content"
    end
  end
  
  normalized
end

#raw_chat(messages = nil, &block) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/dspy/lm.rb', line 63

def raw_chat(messages = nil, &block)
  # Support both array format and builder DSL
  if block_given? && messages.nil?
    # DSL mode - block is for building messages
    builder = MessageBuilder.new
    yield builder
    messages = builder.messages
    streaming_block = nil
  else
    # Array mode - block is for streaming
    messages ||= []
    streaming_block = block
  end
  
  # Normalize and validate messages
  messages = normalize_messages(messages)
  
  # Execute with instrumentation
  execute_raw_chat(messages, &streaming_block)
end

#validate_messages!(messages) ⇒ Object



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/dspy/lm.rb', line 321

def validate_messages!(messages)
  unless messages.is_a?(Array)
    raise ArgumentError, "messages must be an array"
  end
  
  messages.each_with_index do |message, index|
    # Accept both Message objects and hash format for backward compatibility
    if message.is_a?(Message)
      # Already validated by type system
      next
    elsif message.is_a?(Hash) && message.key?(:role) && message.key?(:content)
      # Legacy hash format - validate role
      valid_roles = %w[system user assistant]
      unless valid_roles.include?(message[:role])
        raise ArgumentError, "Invalid role at index #{index}: #{message[:role]}. Must be one of: #{valid_roles.join(', ')}"
      end
    else
      raise ArgumentError, "Message at index #{index} must be a Message object or hash with :role and :content"
    end
  end
end