@@ -31,6 +31,55 @@ def initialize(model: nil, provider: nil, assume_model_exists: false, context: n
3131 }
3232 end
3333
34+ ##
35+ # This method lets you ensure the responses follow a schema you define like this:
36+ #
37+ # chat.with_response_format(:integer).ask("What is 2 + 2?").to_i
38+ # # => 4
39+ # chat.with_response_format(:string).ask("Say 'Hello World' and nothing else.").content
40+ # # => "Hello World"
41+ # chat.with_response_format(:array, items: { type: :string })
42+ # chat.ask('What are the 2 largest countries? Only respond with country names.').content
43+ # # => ["Russia", "Canada"]
44+ # chat.with_response_format(:object, properties: { age: { type: :integer } })
45+ # chat.ask('Provide sample customer age between 10 and 100.').content
46+ # # => { "age" => 42 }
47+ # chat.with_response_format(
48+ # :object,
49+ # properties: { hobbies: { type: :array, items: { type: :string, enum: %w[Soccer Golf Hockey] } } }
50+ # )
51+ # chat.ask('Provide at least 1 hobby.').content
52+ # # => { "hobbies" => ["Soccer"] }
53+ #
54+ # You can also provide the JSON schema you want directly to the method like this:
55+ # chat.with_response_format(type: :object, properties: { age: { type: :integer } })
56+ # # => { "age" => 31 }
57+ #
58+ # In this example the code is automatically switching to OpenAI's json_mode since no object
59+ # properties are requested:
60+ # chat.with_response_format(:json) # Don't care about structure, just give me JSON
61+ # chat.ask('Provide a sample customer data object with name and email keys.').content
62+ # # => { "name" => "Tobias", "email" => "[email protected] " } 63+ # chat.ask('Provide a sample customer data object with name and email keys.').content
64+ # # => { "first_name" => "Michael", "email_address" => "[email protected] " } 65+ #
66+ # @param type [Symbol] (optional) This can be anything supported by the API JSON schema types (integer, object, etc)
67+ # @param schema [Hash] The schema for the response format. It can be a JSON schema or a simple hash.
68+ # @return [Chat] (self)
69+ def with_response_format ( type = nil , **schema )
70+ schema_hash = if type . is_a? ( Symbol ) || type . is_a? ( String )
71+ { type : type == :json ? :object : type }
72+ elsif type . is_a? ( Hash )
73+ type
74+ else
75+ { }
76+ end . merge ( schema )
77+
78+ @response_schema = Schema . new ( schema_hash )
79+
80+ self
81+ end
82+
3483 def ask ( message = nil , with : { } , &block )
3584 add_message role : :user , content : Content . new ( message , with )
3685 complete ( &block )
@@ -96,14 +145,16 @@ def each(&)
96145
97146 def complete ( &) # rubocop:disable Metrics/MethodLength
98147 @on [ :new_message ] &.call
99- response = @provider . complete (
100- messages ,
101- tools : @tools ,
102- temperature : @temperature ,
103- model : @model . id ,
104- connection : @connection ,
105- &
106- )
148+ response = @provider . with_response_schema ( @response_schema ) do
149+ @provider . complete (
150+ messages ,
151+ tools : @tools ,
152+ temperature : @temperature ,
153+ model : @model . id ,
154+ connection : @connection ,
155+ &
156+ )
157+ end
107158 @on [ :end_message ] &.call ( response )
108159
109160 add_message response
0 commit comments