Class: DSPy::LM::OpenAIAdapter
- Defined in:
- lib/dspy/lm/adapters/openai_adapter.rb
Direct Known Subclasses
Instance Attribute Summary
Attributes inherited from Adapter
Instance Method Summary collapse
- #chat(messages:, signature: nil, response_format: nil, &block) ⇒ Object
-
#initialize(model:, api_key:, structured_outputs: false) ⇒ OpenAIAdapter
constructor
A new instance of OpenAIAdapter.
Constructor Details
#initialize(model:, api_key:, structured_outputs: false) ⇒ OpenAIAdapter
Returns a new instance of OpenAIAdapter.
10 11 12 13 14 15 |
# File 'lib/dspy/lm/adapters/openai_adapter.rb', line 10 def initialize(model:, api_key:, structured_outputs: false) super(model: model, api_key: api_key) validate_api_key!(api_key, 'openai') @client = OpenAI::Client.new(api_key: api_key) @structured_outputs_enabled = structured_outputs end |
Instance Method Details
#chat(messages:, signature: nil, response_format: nil, &block) ⇒ Object
17 18 19 20 21 22 23 24 25 26 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/dspy/lm/adapters/openai_adapter.rb', line 17 def chat(messages:, signature: nil, response_format: nil, &block) = () # Validate vision support if images are present if contains_images?() VisionModels.validate_vision_support!('openai', model) # Convert messages to OpenAI format with proper image handling = () end # Set temperature based on model capabilities temperature = case model when /^gpt-5/, /^gpt-4o/ 1.0 # GPT-5 and GPT-4o models only support default temperature of 1.0 else 0.0 # Near-deterministic for other models (0.0 no longer universally supported) end request_params = { model: model, messages: , temperature: temperature } # Add response format if provided by strategy if response_format request_params[:response_format] = response_format elsif @structured_outputs_enabled && signature && supports_structured_outputs? # Legacy behavior for backward compatibility response_format = DSPy::LM::Adapters::OpenAI::SchemaConverter.to_openai_format(signature) request_params[:response_format] = response_format end # Add streaming if block provided if block_given? request_params[:stream] = proc do |chunk, _bytesize| block.call(chunk) if chunk.dig("choices", 0, "delta", "content") end end begin response = @client.chat.completions.create(**request_params) if response.respond_to?(:error) && response.error raise AdapterError, "OpenAI API error: #{response.error}" end choice = response.choices.first = choice. content = .content usage = response.usage # Handle structured output refusals if .respond_to?(:refusal) && .refusal raise AdapterError, "OpenAI refused to generate output: #{.refusal}" end # Convert usage data to typed struct usage_struct = UsageFactory.create('openai', usage) # Create typed metadata = ResponseMetadataFactory.create('openai', { model: model, response_id: response.id, created: response.created, structured_output: @structured_outputs_enabled && signature && supports_structured_outputs?, system_fingerprint: response.system_fingerprint, finish_reason: choice.finish_reason }) Response.new( content: content, usage: usage_struct, metadata: ) rescue => e # Check for specific error types and messages error_msg = e..to_s # Try to parse error body if it looks like JSON error_body = if error_msg.start_with?('{') JSON.parse(error_msg) rescue nil elsif e.respond_to?(:response) && e.response e.response[:body] rescue nil end # Check for specific image-related errors if error_msg.include?('image_parse_error') || error_msg.include?('unsupported image') raise AdapterError, "Image processing failed: #{error_msg}. Ensure your image is a valid PNG, JPEG, GIF, or WebP format and under 5MB." elsif error_msg.include?('rate') && error_msg.include?('limit') raise AdapterError, "OpenAI rate limit exceeded: #{error_msg}. Please wait and try again." elsif error_msg.include?('authentication') || error_msg.include?('API key') || error_msg.include?('Unauthorized') raise AdapterError, "OpenAI authentication failed: #{error_msg}. Check your API key." elsif error_body && error_body.dig('error', 'message') raise AdapterError, "OpenAI API error: #{error_body.dig('error', 'message')}" else # Generic error handling raise AdapterError, "OpenAI adapter error: #{e.}" end end end |