Module: Instructor::OpenAI::Patch

Defined in:
lib/instructor/openai/patch.rb

Overview

The ‘Patch` module provides methods for patching and modifying the OpenAI client behavior.

Instance Method Summary collapse

Instance Method Details

#apply_validation_context(parameters, validation_context) ⇒ Hash

Applies the validation context to the parameters.

Parameters:

  • parameters (Hash)

    The original parameters.

  • validation_context (Hash)

    The validation context.

Returns:

  • (Hash)

    The parameters with applied validation context.



124
125
126
127
128
129
130
131
132
# File 'lib/instructor/openai/patch.rb', line 124

def apply_validation_context(parameters, validation_context)
  return parameters unless validation_context.is_a?(Hash)

  Array[validation_context].each_with_index do |message, index|
    parameters[:messages][index][:content] = parameters[:messages][index][:content] % message
  end

  parameters
end

#build_function(model) ⇒ Hash

Builds the function details for the API request.

Parameters:

  • model (Class)

    The response model class.

Returns:

  • (Hash)

    The function details.



138
139
140
141
142
143
144
145
146
147
# File 'lib/instructor/openai/patch.rb', line 138

def build_function(model)
  {
    type: 'function',
    function: {
      name: generate_function_name(model),
      description: generate_description(model),
      parameters: model.json_schema
    }
  }
end

#chat(parameters:, response_model: nil, max_retries: 0, validation_context: nil) ⇒ Object

Sends a chat request to the API and processes the response.

Parameters:

  • parameters (Hash)

    The parameters for the chat request as expected by the OpenAI client.

  • response_model (Class) (defaults to: nil)

    The response model class.

  • max_retries (Integer) (defaults to: 0)

    The maximum number of retries. Default is 0.

  • validation_context (Hash) (defaults to: nil)

    The validation context for the parameters. Optional.

Returns:

  • (Object)

    The processed response.



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/instructor/openai/patch.rb', line 31

def chat(parameters:, response_model: nil, max_retries: 0, validation_context: nil)
  return json_post(path: '/chat/completions', parameters:) if response_model.nil?

  with_retries(max_retries, [JSON::ParserError, Instructor::ValidationError, Faraday::ParsingError]) do
    model = determine_model(response_model)
    function = build_function(model)
    parameters = prepare_parameters(parameters, validation_context, function)
    response = json_post(path: '/chat/completions', parameters:)
    process_response(response, model)
  end
end

#determine_model(response_model) ⇒ Class

Determines the response model based on the provided value.

Parameters:

  • response_model (Class)

    The response model class or typed array.

Returns:

  • (Class)

    The response model.



109
110
111
112
113
114
115
116
117
# File 'lib/instructor/openai/patch.rb', line 109

def determine_model(response_model)
  if response_model.is_a?(T::Types::TypedArray)
    @iterable = true
    response_model.type.raw_type
  else
    @iterable = false
    response_model
  end
end

#generate_description(model) ⇒ String

Generates the description for the function.

Parameters:

  • model (Class)

    The response model class.

Returns:

  • (String)

    The generated description.



157
158
159
160
161
162
163
164
165
# File 'lib/instructor/openai/patch.rb', line 157

def generate_description(model)
  if model.respond_to?(:instructions)
    raise Instructor::Error, 'The instructions must be a string' unless model.instructions.is_a?(String)

    model.instructions
  else
    "Correctly extracted `#{model.name}` with all the required parameters with correct types"
  end
end

#generate_function_name(model) ⇒ Object



149
150
151
# File 'lib/instructor/openai/patch.rb', line 149

def generate_function_name(model)
  model.schema.fetch(:title, model.name)
end

#iterable?(response) ⇒ Boolean

Checks if the response is iterable.

Returns:

  • (Boolean)

    ‘true` if the response is iterable, `false` otherwise.



170
171
172
# File 'lib/instructor/openai/patch.rb', line 170

def iterable?(response)
  @iterable && response.is_a?(Array)
end

#prepare_parameters(parameters, validation_context, function) ⇒ Hash

Prepares the parameters for the chat request.

Parameters:

  • parameters (Hash)

    The original parameters.

  • validation_context (Hash)

    The validation context for the parameters.

  • function (Hash)

    The function details.

Returns:

  • (Hash)

    The prepared parameters.



49
50
51
52
53
54
# File 'lib/instructor/openai/patch.rb', line 49

def prepare_parameters(parameters, validation_context, function)
  parameters = apply_validation_context(parameters, validation_context)
  parameters.merge!(tools: [function])
  tool_choice = resolve_tool_choice(function)
  parameters.merge!(tool_choice:)
end

#process_multiple_responses(parsed_response, model) ⇒ Array<Object>

Processes multiple responses from the API.

Parameters:

  • parsed_response (Array<Hash>)

    The parsed API responses.

  • model (Class)

    The response model class.

Returns:

  • (Array<Object>)

    The processed responses.



88
89
90
91
92
93
# File 'lib/instructor/openai/patch.rb', line 88

def process_multiple_responses(parsed_response, model)
  parsed_response.map do |response|
    instance = model.new(response)
    instance.valid? ? instance : raise(Instructor::ValidationError)
  end
end

#process_response(response, model) ⇒ Object

Processes the API response.

Parameters:

  • response (Hash)

    The API response.

  • model (Class)

    The response model class.

Returns:

  • (Object)

    The processed response.



74
75
76
77
78
79
80
81
# File 'lib/instructor/openai/patch.rb', line 74

def process_response(response, model)
  parsed_response = Response.new(response).parse
  if iterable?(parsed_response)
    process_multiple_responses(parsed_response, model)
  else
    process_single_response(parsed_response, model)
  end
end

#process_single_response(parsed_response, model) ⇒ Object

Processes a single response from the API.

Parameters:

  • parsed_response (Hash)

    The parsed API response.

  • model (Class)

    The response model class.

Returns:

  • (Object)

    The processed response.



100
101
102
103
# File 'lib/instructor/openai/patch.rb', line 100

def process_single_response(parsed_response, model)
  instance = model.new(parsed_response)
  instance.valid? ? instance : raise(Instructor::ValidationError)
end

#resolve_tool_choice(function) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/instructor/openai/patch.rb', line 56

def resolve_tool_choice(function)
  case Instructor.mode
  when Instructor::Mode::TOOLS.function
    { type: 'function', function: { name: function[:function][:name] } }
  when Instructor::Mode::TOOLS.auto
    'auto'
  when Instructor::Mode::TOOLS.required
    'required'
  when Instructor::Mode::TOOLS.none
    'none'
  end
end

#with_retries(max_retries, exceptions) { ... } ⇒ Object

Executes a block of code with retries in case of specific exceptions.

Parameters:

  • max_retries (Integer)

    The maximum number of retries.

  • exceptions (Array<Class>)

    The exceptions to catch and retry.

Yields:

  • The block of code to execute.



13
14
15
16
17
18
19
20
21
22
# File 'lib/instructor/openai/patch.rb', line 13

def with_retries(max_retries, exceptions, &block)
  attempts = 0
  begin
    block.call
  rescue *exceptions
    attempts += 1
    retry if attempts < max_retries
    raise
  end
end