Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ai-chat (0.5.1)
ai-chat (0.5.4)
amazing_print (~> 2.0)
base64 (~> 0.1, > 0.1.1)
json (~> 2.0)
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,15 @@ The `reasoning_effort` parameter guides the model on how many reasoning tokens t

By default, `reasoning_effort` is `nil`, which means no reasoning parameter is sent to the API. For `gpt-5.2` (the default model), this is equivalent to `"none"` reasoning.

## Verbosity

Verbosity determines how many output tokens are generated. Lowering the number of tokens reduces overall latency. While the model's reasoning approach stays mostly the same, the model finds ways to answer more concisely—which can either improve or diminish answer quality, depending on your use case. Here are some scenarios for both ends of the verbosity spectrum:

- High verbosity: Use when you need the model to provide thorough explanations of documents or perform extensive code refactoring.
- Low verbosity: Best for situations where you want concise answers or simple code generation, such as SQL queries.

The supported values are `:high`, `:medium`, or `:low`. The default value is `:medium` for `gpt-5.2`. **Older models (like `gpt-4.1-nano`) only support `:medium`**.

## Advanced: Response Details

When you call `generate!` (or later call `get_response` in background mode), the gem stores additional information about the API response:
Expand Down
102 changes: 102 additions & 0 deletions examples/17_verbosity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env ruby

require_relative "../lib/ai-chat"
require "dotenv"
Dotenv.load(File.expand_path("../.env", __dir__))
require "amazing_print"

puts "\n=== AI::Chat Verbosity Tests ==="
puts

puts "1. Supports verbosity = :low"
puts "-" * 30
chat1 = AI::Chat.new
chat1.verbosity = :low
chat1.user("How high do planes typically fly?")
response = chat1.generate![:content]
puts response
puts
puts "Total characters: #{response.length}"
puts

puts "2. Supports verbosity = :medium"
puts "-" * 30
chat2 = AI::Chat.new
chat2.verbosity = :medium
chat2.user("How high do planes typically fly?")
response = chat2.generate![:content]
puts response
puts
puts "Total characters: #{response.length}"
puts

puts "3. Supports verbosity = :high"
puts "-" * 30
chat3 = AI::Chat.new
chat3.verbosity = :medium
chat3.user("How high do planes typically fly?")
response = chat3.generate![:content]
puts response
puts
puts "Total characters: #{response.length}"
puts

puts "4. Raises error for unsupported verbosity values"
puts "-" * 30
chat4 = AI::Chat.new
chat4.user("How high do planes typically fly?")
begin
chat4.verbosity = :extreme
puts "✗ Failed to raise ArgumentError for invalid verbosity: #{chat4.verbosity}"
rescue ArgumentError => e
puts "✓ Raises Argument error correctly: #{e.message}"
end
puts

puts
puts "5. Supports verbosity with Structured Outputs"
puts "-" * 30
path = "my_schemas/test.json"
File.delete(path) if File.exist?(path)
AI::Chat.generate_schema!("A user with full name (required), first_name (required), last_name (required), coolness_level (score between 0-10 representing how cool the user sounds), coolness_reasoning (the explanation of the coolness_leve)", location: path, api_key: ENV["OPENAI_API_KEY"])

chat5 = AI::Chat.new
chat5.verbosity = :low
chat5.schema_file = path
chat5.system("Extract the profile information from the user message. Describe it like a 90's robot.")
chat5.user("My name is Bryan. I like to skateboard and be cool. I'm seventeen and a quarter. You can reach me at bryan@example.com.")
puts chat5.last[:content]
puts
begin
response = chat5.generate![:content]
puts "✓ Generates structured output response with verbosity"
rescue => e
puts "✗ Failed to generate structured output response with verbosity: #{e.message}"
end
puts

puts "6. Supports verbosity = :medium for gpt-4.1-nano"
puts "-" * 30
chat6 = AI::Chat.new
chat6.model = "gpt-4.1-nano"
chat6.verbosity = :medium
chat6.user("How high do planes typically fly?")
response = chat6.generate![:content]
puts response
puts
puts "Total characters: #{response.length}"
puts

puts "7. Fails verbosity = :low for gpt-4.1-nano"
puts "-" * 30
chat7 = AI::Chat.new
chat7.model = "gpt-4.1-nano"
chat7.verbosity = :low
chat7.user("How high do planes typically fly?")
begin
response = chat7.generate![:content]
puts "✗ Failed to raise error when setting :low verbosity on unsupported model."
rescue OpenAI::Errors::BadRequestError => e
puts "✓ Successfully raises error on for unsupported verbosity for older model: #{e.message}"
end
puts
6 changes: 6 additions & 0 deletions examples/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,9 @@

# Proxy
require_relative "15_proxy"

# Get items
require_relative "16_get_items"

# Verbosity
require_relative "17_verbosity"
13 changes: 12 additions & 1 deletion lib/ai/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module AI
class Chat
# :reek:Attribute
attr_accessor :background, :code_interpreter, :conversation_id, :image_generation, :image_folder, :messages, :model, :reasoning_effort, :web_search
attr_reader :client, :last_response_id, :proxy, :schema, :schema_file
attr_reader :client, :last_response_id, :proxy, :schema, :schema_file, :verbosity

BASE_PROXY_URL = "https://prepend.me/api.openai.com/v1"

Expand All @@ -34,6 +34,7 @@ def initialize(api_key: nil, api_key_env_var: "OPENAI_API_KEY")
@image_generation = false
@image_folder = "./images"
@api_key_validated = false
@verbosity = :medium
end

def self.generate_schema!(description, location: "schema.json", api_key: nil, api_key_env_var: "OPENAI_API_KEY", proxy: false)
Expand Down Expand Up @@ -183,6 +184,14 @@ def schema_file=(path)
self.schema = content
end

def verbosity=(value)
if ["low", "medium", "high"].include?(value.to_s)
@verbosity = value.to_sym
else
raise ArgumentError, "Invalid verbosity value:'#{value}'. Must be one of :low, :medium, :high."
end
end

def last
messages.last
end
Expand Down Expand Up @@ -221,6 +230,7 @@ def inspectable_attributes
attrs << [:@web_search, @web_search] if @web_search
attrs << [:@schema, @schema] if @schema
attrs << [:@schema_file, @schema_file] if @schema_file
attrs << [:@verbosity, verbosity] if verbosity

attrs
end
Expand Down Expand Up @@ -274,6 +284,7 @@ def create_response

create_conversation unless conversation_id
parameters[:conversation] = conversation_id
parameters[:text] = (parameters[:text] || {}).merge(verbosity: verbosity) if verbosity

messages_to_send = prepare_messages_for_api
parameters[:input] = strip_responses(messages_to_send) unless messages_to_send.empty?
Expand Down