Class: DSPy::LM
- Inherits:
-
Object
- Object
- DSPy::LM
- 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/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/gemini/schema_converter.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/gemini_structured_output_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, ConfigurationError, Error, GeminiAdapter, GeminiResponseMetadata, IncompatibleImageFeatureError, Message, MessageBuilder, MissingAPIKeyError, OllamaAdapter, OpenAIAdapter, OpenAIResponseMetadata, OpenAIUsage, Response, ResponseMetadata, RetryHandler, StrategySelector, StructuredOutputStrategy, UnsupportedProviderError, Usage
Instance Attribute Summary collapse
-
#adapter ⇒ Object
readonly
Returns the value of attribute adapter.
-
#api_key ⇒ Object
readonly
Returns the value of attribute api_key.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#model_id ⇒ Object
readonly
Returns the value of attribute model_id.
-
#provider ⇒ Object
readonly
Returns the value of attribute provider.
Instance Method Summary collapse
- #chat(inference_module, input_values, &block) ⇒ Object
- #execute_raw_chat(messages, &streaming_block) ⇒ Object
-
#initialize(model_id, api_key: nil, **options) ⇒ LM
constructor
A new instance of LM.
-
#messages_to_hash_array(messages) ⇒ Object
Convert Message objects to hash array for adapters.
-
#normalize_messages(messages) ⇒ Object
Convert messages to normalized Message objects.
- #raw_chat(messages = nil, &block) ⇒ Object
- #validate_messages!(messages) ⇒ Object
Constructor Details
#initialize(model_id, api_key: nil, **options) ⇒ LM
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/dspy/lm.rb', line 34 def initialize(model_id, api_key: nil, **) @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, **) end |
Instance Attribute Details
#adapter ⇒ Object (readonly)
Returns the value of attribute adapter.
32 33 34 |
# File 'lib/dspy/lm.rb', line 32 def adapter @adapter end |
#api_key ⇒ Object (readonly)
Returns the value of attribute api_key.
32 33 34 |
# File 'lib/dspy/lm.rb', line 32 def api_key @api_key end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
32 33 34 |
# File 'lib/dspy/lm.rb', line 32 def model @model end |
#model_id ⇒ Object (readonly)
Returns the value of attribute model_id.
32 33 34 |
# File 'lib/dspy/lm.rb', line 32 def model_id @model_id end |
#provider ⇒ Object (readonly)
Returns the value of attribute provider.
32 33 34 |
# File 'lib/dspy/lm.rb', line 32 def provider @provider end |
Instance Method Details
#chat(inference_module, input_values, &block) ⇒ Object
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 |
# File 'lib/dspy/lm.rb', line 45 def chat(inference_module, input_values, &block) # Capture the current DSPy context before entering Sync block parent_context = DSPy::Context.current.dup Sync do # Properly restore the context in the new fiber created by Sync # We need to set both thread and fiber storage for the new context system thread_key = :"dspy_context_#{Thread.current.object_id}" Thread.current[thread_key] = parent_context Thread.current[:dspy_context] = parent_context # Keep for backward compatibility Fiber[:dspy_context] = parent_context signature_class = inference_module.signature_class # Build messages from inference module = (inference_module, input_values) # Execute with instrumentation response = instrument_lm_request(, signature_class.name) do chat_with_strategy(, 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
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 394 395 396 |
# File 'lib/dspy/lm.rb', line 369 def execute_raw_chat(, &streaming_block) # Generate unique request ID for tracking request_id = SecureRandom.hex(8) start_time = Time.now # Store request context for correlation Thread.current[:dspy_request_id] = request_id Thread.current[:dspy_request_start_time] = start_time begin response = instrument_lm_request(, 'RawPrompt') do # Convert messages to hash format for adapter = () # Direct adapter call, no strategies or JSON parsing adapter.chat(messages: , signature: nil, &streaming_block) end # Emit the standard lm.tokens event (consistent with other LM calls) emit_token_usage(response, 'RawPrompt') # Return raw response content, not parsed JSON response.content ensure # Clean up thread-local storage Thread.current[:dspy_request_id] = nil Thread.current[:dspy_request_start_time] = nil end end |
#messages_to_hash_array(messages) ⇒ Object
Convert Message objects to hash array for adapters
439 440 441 442 443 444 445 446 447 |
# File 'lib/dspy/lm.rb', line 439 def () .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
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/dspy/lm.rb', line 399 def () # Validate array format first unless .is_a?(Array) raise ArgumentError, "messages must be an array" end return if .all? { |m| m.is_a?(Message) } # Convert hash messages to Message objects normalized = [] .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 = MessageFactory.create(msg) if .nil? raise ArgumentError, "Failed to create Message from hash at index #{index}" end normalized << 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
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/dspy/lm.rb', line 74 def raw_chat( = nil, &block) # Support both array format and builder DSL if block_given? && .nil? # DSL mode - block is for building messages builder = MessageBuilder.new yield builder = builder. streaming_block = nil else # Array mode - block is for streaming ||= [] streaming_block = block end # Normalize and validate messages = () # Execute with instrumentation execute_raw_chat(, &streaming_block) end |
#validate_messages!(messages) ⇒ Object
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/dspy/lm.rb', line 347 def () unless .is_a?(Array) raise ArgumentError, "messages must be an array" end .each_with_index do |, index| # Accept both Message objects and hash format for backward compatibility if .is_a?(Message) # Already validated by type system next elsif .is_a?(Hash) && .key?(:role) && .key?(:content) # Legacy hash format - validate role valid_roles = %w[system user assistant] unless valid_roles.include?([: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 |