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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.8.0] - 2026-10-19

### Changed

- **Default model**: Changed from `gpt-5.6-terra` to `gpt-6-luna`, the most efficient tier of OpenAI's GPT-6 family. It costs far less ($0.10/$0.50 vs $2/$12 per 1M input/output tokens) and answered a sample prompt about twice as fast in our testing. `generate_schema!` uses the same model.

- **Default `reasoning_effort` is now `"low"` instead of `"none"`**: `gpt-6.1-sol` and `gpt-6-astra` reject `"none"` with a 400 error, so switching `model` to one of them used to fail unless you also changed `reasoning_effort`. `"low"` is accepted by all of OpenAI's current flagship models. Since the effort is no longer `"none"`, a reasoning summary (`summary: "auto"`) is now requested by default too. Set `reasoning_effort = "none"` to get the previous behavior on models that support it.

## [0.7.0] - 2026-08-24

### Changed
Expand Down
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.7.0)
ai-chat (0.8.0)
amazing_print (~> 2.0)
base64 (~> 0.1, > 0.1.1)
json (~> 2.0)
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ap response
# => {
# :role => "assistant",
# :content => "Matz is nice and so we are nice.",
# :response => { id: "resp_abc...", model: "gpt-5.6-terra", ... }
# :response => { id: "resp_abc...", model: "gpt-6-luna", ... }
# }

ap chat.messages
Expand All @@ -68,7 +68,7 @@ ap chat.messages
# {
# :role => "assistant",
# :content => "Matz is nice and so we are nice.",
# :response => { id: "resp_abc...", model: "gpt-5.6-terra", ... }
# :response => { id: "resp_abc...", model: "gpt-6-luna", ... }
# }
# ]
```
Expand Down Expand Up @@ -131,11 +131,11 @@ chat.add("Here's what I think...", role: "assistant")

### Model

The gem defaults to `gpt-5.6-terra`. You can change it:
The gem defaults to `gpt-6-luna`. You can change it:

```ruby
chat = AI::Chat.new
chat.model = "gpt-5.6-luna"
chat.model = "gpt-6.1-sol"
```

### API Key
Expand Down Expand Up @@ -409,9 +409,9 @@ chat.user("Explain the tradeoffs between microservices and monoliths.")
chat.generate!
```

By default, `reasoning_effort` is `"none"`, so the model answers directly without spending reasoning tokens. This matters because GPT-5.5 and newer models (including the default `gpt-5.6-terra`) fall back to `"medium"` reasoning when the parameter is omitted, which is slower and more expensive.
By default, `reasoning_effort` is `"low"`, so the model does a little reasoning before it answers. `"low"` is accepted by all of OpenAI's current flagship models (`gpt-6-luna`, `gpt-6.1-sol`, and `gpt-6-astra`), while some of them reject `"none"`, so you can switch models without also changing the effort. The gem always sends an effort because GPT-5.5 and newer models fall back to `"medium"` when the parameter is omitted, which is slower and more expensive.

Set `reasoning_effort = nil` to omit the reasoning parameter entirely — you will need this if you switch to a model that does not support reasoning (e.g., `gpt-4o`) or one that predates the `"none"` option.
Set `reasoning_effort = "none"` for the fastest replies on models that support it (such as `gpt-6-luna`). Set `reasoning_effort = nil` to omit the reasoning parameter entirely — you will need this if you switch to a model that does not support reasoning (e.g., `gpt-4o`).

### Verbosity

Expand Down Expand Up @@ -476,7 +476,7 @@ chat.generate!

response = chat.last[:response]
response[:id] # => "resp_abc123..."
response[:model] # => "gpt-5.6-terra"
response[:model] # => "gpt-6-luna"
response[:usage] # => { input_tokens: 5, output_tokens: 7, total_tokens: 12 }
```

Expand Down
2 changes: 1 addition & 1 deletion ai-chat.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Gem::Specification.new do |spec|
spec.name = "ai-chat"
spec.version = "0.7.0"
spec.version = "0.8.0"
spec.authors = ["Raghu Betina", "Jelani Woods"]
spec.email = ["raghu@firstdraft.com", "jelani@firstdraft.com"]
spec.homepage = "https://github.com/firstdraft/ai-chat"
Expand Down
10 changes: 5 additions & 5 deletions examples/17_verbosity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
puts "-" * 30
chat4 = AI::Chat.new
chat4.user("How high do planes typically fly?")
begin
chat4.verbosity = :extreme
begin
chat4.verbosity = :extreme
puts "✗ Failed to raise ArgumentError for invalid verbosity: #{chat4.verbosity}"
rescue ArgumentError => e
puts "✓ Raises Argument error correctly: #{e.message}"
Expand All @@ -66,9 +66,9 @@
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
puts
begin
response = chat5.generate![:content]
chat5.generate!
puts "✓ Generates structured output response with verbosity"
rescue => e
puts "✗ Failed to generate structured output response with verbosity: #{e.message}"
Expand Down Expand Up @@ -96,7 +96,7 @@
chat7.verbosity = :low
chat7.user("How high do planes typically fly?")
begin
response = chat7.generate![:content]
chat7.generate!
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}"
Expand Down
10 changes: 5 additions & 5 deletions lib/ai/chat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def initialize(api_key: nil, api_key_env_var: nil, proxy: nil)
@proxy = proxy.nil? ? ENV[PROXY_ENV]&.downcase == "true" : !!proxy
@api_key = resolve_api_key
@messages = []
@reasoning_effort = "none"
@model = "gpt-5.6-terra"
@reasoning_effort = "low"
@model = "gpt-6-luna"
client_options = {api_key: @api_key}
client_options[:base_url] = BASE_PROXY_URL if @proxy
@client = OpenAI::Client.new(**client_options)
Expand All @@ -46,7 +46,7 @@ def initialize(api_key: nil, api_key_env_var: nil, proxy: nil)

def self.generate_schema!(description, location: "schema.json", api_key: nil, api_key_env_var: nil, proxy: nil)
proxy = proxy.nil? ? ENV[PROXY_ENV]&.downcase == "true" : !!proxy
api_key = api_key || ENV.fetch(api_key_env_var || (proxy ? PROXY_KEY_ENV : OPENAI_KEY_ENV))
api_key ||= ENV.fetch(api_key_env_var || (proxy ? PROXY_KEY_ENV : OPENAI_KEY_ENV))
prompt_path = File.expand_path("../prompts/schema_generator.md", __dir__)
system_prompt = File.read(prompt_path)

Expand All @@ -57,7 +57,7 @@ def self.generate_schema!(description, location: "schema.json", api_key: nil, ap

client = OpenAI::Client.new(**options)
response = client.responses.create(
model: "gpt-5.6-terra",
model: "gpt-6-luna",
input: [
{role: :system, content: system_prompt},
{role: :user, content: description}
Expand Down Expand Up @@ -175,7 +175,7 @@ def proxy=(value)
@api_key = new_key
@api_key_validated = false
@client = new_client
rescue => error
rescue
@proxy = previous_proxy
raise
end
Expand Down
21 changes: 15 additions & 6 deletions spec/unit/chat_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -769,19 +769,28 @@ def schema_client_double
allow(responses).to receive(:create).and_return(double("response"))
end

it "defaults to gpt-5.6-terra" do
expect(chat.model).to eq("gpt-5.6-terra")
it "defaults to gpt-6-luna" do
expect(chat.model).to eq("gpt-6-luna")
end

it "defaults reasoning_effort to \"none\"" do
expect(chat.reasoning_effort).to eq("none")
it "defaults reasoning_effort to \"low\"" do
expect(chat.reasoning_effort).to eq("low")
end

it "sends reasoning effort none without a summary by default" do
it "sends reasoning effort low with a summary by default" do
chat.send(:create_response)

expect(responses).to have_received(:create).with(
hash_including(model: "gpt-5.6-terra", reasoning: {effort: "none"})
hash_including(model: "gpt-6-luna", reasoning: {effort: "low", summary: "auto"})
)
end

it "sends reasoning effort none without a summary" do
chat.reasoning_effort = "none"
chat.send(:create_response)

expect(responses).to have_received(:create).with(
hash_including(reasoning: {effort: "none"})
)
end

Expand Down
Loading