ai-chat 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/ai_chat.gemspec +3 -2
- data/lib/ai/chat/version.rb +1 -1
- data/lib/ai/chat.rb +66 -30
- data/test_program/test_ai_chat.rb +25 -6
- metadata +18 -5
- data/.rspec_status +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4875edb9f6b70da87fbdabfc11fa25be2e76d51496a0ba369e2cf360511abd5
|
4
|
+
data.tar.gz: a062324e299eea25b68696d1d292f2f0b9fe217b3adaa7068020b6db7d198e53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd196bf575fdb10389c93d52e29596bfe68c5a48c49e81b2f5efe377ab91c55b4a2533bb0d09912d06c2400bee688f98f76605161dca9fcf4cab34ebad3067e5
|
7
|
+
data.tar.gz: d973e4d78ef6fe30333f494d60cd590d523727bf1a175c6fb6ce1832c26dd03dd52bbff8681ad15aec4a19afda2b5616ac9fdda21499ec86e635a106381af9d1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.0.5] - 2025-04-25
|
4
|
+
|
5
|
+
- Updated to use OpenAI's Responses API instead of Chat Completions API
|
6
|
+
- Fixed image processing to work with the new API format
|
7
|
+
- Improved error handling and response parsing
|
8
|
+
|
3
9
|
## [0.0.1] - 2025-02-27
|
4
10
|
|
5
11
|
- Initial release
|
data/ai_chat.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Raghu Betina", "Jelani Woods"]
|
9
9
|
spec.email = ["raghu@firstdraft.com", "jelani@firstdraft.com"]
|
10
10
|
|
11
|
-
spec.summary = "This gem provides a class called `AI::Chat` that is intended to make it as easy as possible to use OpenAI's
|
12
|
-
spec.description = "This gem provides a class called `AI::Chat` that is intended to make it as easy as possible to use OpenAI's
|
11
|
+
spec.summary = "This gem provides a class called `AI::Chat` that is intended to make it as easy as possible to use OpenAI's Responses API."
|
12
|
+
spec.description = "This gem provides a class called `AI::Chat` that is intended to make it as easy as possible to use OpenAI's Responses API. Supports Structured Output and Image Processing."
|
13
13
|
spec.homepage = "https://github.com/firstdraft/ai-chat"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = ">= 2.0.0"
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
|
34
34
|
# Register dependencies of the gem
|
35
35
|
spec.add_runtime_dependency "mime-types", "~> 3.0"
|
36
|
+
spec.add_runtime_dependency "base64" # Works for all Ruby versions
|
36
37
|
|
37
38
|
# Development dependencies
|
38
39
|
spec.add_development_dependency "rake", "~> 13.0"
|
data/lib/ai/chat/version.rb
CHANGED
data/lib/ai/chat.rb
CHANGED
@@ -22,15 +22,13 @@ module AI
|
|
22
22
|
if item.key?("image") || item.key?(:image)
|
23
23
|
image_value = item.fetch("image") { item.fetch(:image) }
|
24
24
|
{
|
25
|
-
type: "
|
26
|
-
image_url:
|
27
|
-
url: process_image(image_value)
|
28
|
-
}
|
25
|
+
type: "input_image",
|
26
|
+
image_url: process_image(image_value)
|
29
27
|
}
|
30
28
|
elsif item.key?("text") || item.key?(:text)
|
31
29
|
text_value = item.fetch("text") { item.fetch(:text) }
|
32
30
|
{
|
33
|
-
type: "
|
31
|
+
type: "input_text",
|
34
32
|
text: text_value
|
35
33
|
}
|
36
34
|
else
|
@@ -54,7 +52,7 @@ module AI
|
|
54
52
|
else
|
55
53
|
text_and_images_array = [
|
56
54
|
{
|
57
|
-
type: "
|
55
|
+
type: "input_text",
|
58
56
|
text: content
|
59
57
|
}
|
60
58
|
]
|
@@ -62,10 +60,8 @@ module AI
|
|
62
60
|
if images && !images.empty?
|
63
61
|
images_array = images.map do |image|
|
64
62
|
{
|
65
|
-
type: "
|
66
|
-
image_url:
|
67
|
-
url: process_image(image)
|
68
|
-
}
|
63
|
+
type: "input_image",
|
64
|
+
image_url: process_image(image)
|
69
65
|
}
|
70
66
|
end
|
71
67
|
|
@@ -73,10 +69,8 @@ module AI
|
|
73
69
|
else
|
74
70
|
text_and_images_array.push(
|
75
71
|
{
|
76
|
-
type: "
|
77
|
-
image_url:
|
78
|
-
url: process_image(image)
|
79
|
-
}
|
72
|
+
type: "input_image",
|
73
|
+
image_url: process_image(image)
|
80
74
|
}
|
81
75
|
)
|
82
76
|
end
|
@@ -100,35 +94,77 @@ module AI
|
|
100
94
|
"content-type" => "application/json"
|
101
95
|
}
|
102
96
|
|
103
|
-
response_format = if schema.nil?
|
104
|
-
{
|
105
|
-
"type" => "text"
|
106
|
-
}
|
107
|
-
else
|
108
|
-
{
|
109
|
-
"type" => "json_schema",
|
110
|
-
"json_schema" => JSON.parse(schema)
|
111
|
-
}
|
112
|
-
end
|
113
|
-
|
114
97
|
request_body_hash = {
|
115
98
|
"model" => model,
|
116
|
-
"
|
117
|
-
"messages" => messages
|
99
|
+
"input" => messages
|
118
100
|
}
|
119
101
|
|
102
|
+
# Handle structured output (JSON schema)
|
103
|
+
if !schema.nil?
|
104
|
+
# Parse the schema and use it with Structured Output (json_schema)
|
105
|
+
schema_obj = JSON.parse(schema)
|
106
|
+
|
107
|
+
# Extract schema name from the parsed schema, or use a default
|
108
|
+
schema_name = schema_obj["name"] || "output_object"
|
109
|
+
|
110
|
+
# Responses API uses proper Structured Output with schema
|
111
|
+
request_body_hash["text"] = {
|
112
|
+
"format" => {
|
113
|
+
"type" => "json_schema",
|
114
|
+
"schema" => schema_obj["schema"] || schema_obj,
|
115
|
+
"name" => schema_name,
|
116
|
+
"strict" => true
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
120
121
|
request_body_json = JSON.generate(request_body_hash)
|
121
122
|
|
122
|
-
uri = URI("https://api.openai.com/v1/
|
123
|
+
uri = URI("https://api.openai.com/v1/responses")
|
123
124
|
raw_response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
124
125
|
request = Net::HTTP::Post.new(uri, request_headers_hash)
|
125
126
|
request.body = request_body_json
|
126
127
|
http.request(request)
|
127
128
|
end
|
128
129
|
|
129
|
-
|
130
|
+
# Handle empty responses or HTTP errors
|
131
|
+
if raw_response.code.to_i >= 400
|
132
|
+
raise "HTTP Error #{raw_response.code}: #{raw_response.message}\n#{raw_response.body}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if raw_response.body.nil? || raw_response.body.empty?
|
136
|
+
raise "Empty response received from OpenAI API"
|
137
|
+
end
|
130
138
|
|
131
|
-
|
139
|
+
parsed_response = JSON.parse(raw_response.body)
|
140
|
+
|
141
|
+
# Check for API errors
|
142
|
+
if parsed_response.key?("error") && parsed_response["error"].is_a?(Hash)
|
143
|
+
error_message = parsed_response["error"]["message"] || parsed_response["error"].inspect
|
144
|
+
raise "OpenAI API Error: #{error_message}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Extract the text content from the response
|
148
|
+
content = ""
|
149
|
+
|
150
|
+
# Parse response according to the documented structure
|
151
|
+
if parsed_response.key?("output") && parsed_response["output"].is_a?(Array) && !parsed_response["output"].empty?
|
152
|
+
output_item = parsed_response["output"][0]
|
153
|
+
|
154
|
+
if output_item["type"] == "message" && output_item.key?("content")
|
155
|
+
content_items = output_item["content"]
|
156
|
+
output_text_item = content_items.find { |item| item["type"] == "output_text" }
|
157
|
+
|
158
|
+
if output_text_item && output_text_item.key?("text")
|
159
|
+
content = output_text_item["text"]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# If no content is found, throw an error
|
165
|
+
if content.empty?
|
166
|
+
raise "Failed to extract content from response: #{parsed_response.inspect}"
|
167
|
+
end
|
132
168
|
|
133
169
|
messages.push({role: "assistant", content: content})
|
134
170
|
|
@@ -56,11 +56,11 @@ z.user("1 slice of pizza")
|
|
56
56
|
response = z.assistant!
|
57
57
|
puts "Nutrition values: #{response.inspect}"
|
58
58
|
|
59
|
-
# Test with
|
60
|
-
puts "\
|
61
|
-
puts "
|
59
|
+
# Test with a single image
|
60
|
+
puts "\nSingle Image Support Test:"
|
61
|
+
puts "------------------------"
|
62
62
|
begin
|
63
|
-
img_path = File.expand_path("../../spec/fixtures/
|
63
|
+
img_path = File.expand_path("../../spec/fixtures/test1.jpg", __FILE__)
|
64
64
|
if File.exist?(img_path)
|
65
65
|
i = AI::Chat.new
|
66
66
|
i.user("What's in this image?", image: img_path)
|
@@ -70,8 +70,27 @@ begin
|
|
70
70
|
puts "Test image not found at #{img_path}"
|
71
71
|
end
|
72
72
|
rescue => e
|
73
|
-
puts "
|
74
|
-
|
73
|
+
puts "Single image test error: #{e.message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Test with multiple images
|
77
|
+
puts "\nMultiple Images Support Test:"
|
78
|
+
puts "--------------------------"
|
79
|
+
begin
|
80
|
+
img_path1 = File.expand_path("../../spec/fixtures/test1.jpg", __FILE__)
|
81
|
+
img_path2 = File.expand_path("../../spec/fixtures/test2.jpg", __FILE__)
|
82
|
+
img_path3 = File.expand_path("../../spec/fixtures/test3.jpg", __FILE__)
|
83
|
+
|
84
|
+
if File.exist?(img_path1) && File.exist?(img_path2) && File.exist?(img_path3)
|
85
|
+
i = AI::Chat.new
|
86
|
+
i.user("Compare these images and tell me what you see.", images: [img_path1, img_path2, img_path3])
|
87
|
+
response = i.assistant!
|
88
|
+
puts "Multiple images description: #{response}"
|
89
|
+
else
|
90
|
+
puts "One or more test images not found"
|
91
|
+
end
|
92
|
+
rescue => e
|
93
|
+
puts "Multiple images test error: #{e.message}"
|
75
94
|
end
|
76
95
|
|
77
96
|
# Get messages example
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ai-chat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Raghu Betina
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: base64
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,8 +123,8 @@ dependencies:
|
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '1.32'
|
111
125
|
description: This gem provides a class called `AI::Chat` that is intended to make
|
112
|
-
it as easy as possible to use OpenAI's
|
113
|
-
|
126
|
+
it as easy as possible to use OpenAI's Responses API. Supports Structured Output
|
127
|
+
and Image Processing.
|
114
128
|
email:
|
115
129
|
- raghu@firstdraft.com
|
116
130
|
- jelani@firstdraft.com
|
@@ -120,7 +134,6 @@ extra_rdoc_files: []
|
|
120
134
|
files:
|
121
135
|
- ".config/rubocop/config.yml"
|
122
136
|
- ".reek.yml"
|
123
|
-
- ".rspec_status"
|
124
137
|
- ".ruby-version"
|
125
138
|
- CHANGELOG.md
|
126
139
|
- CODE_OF_CONDUCT.md
|
@@ -158,5 +171,5 @@ requirements: []
|
|
158
171
|
rubygems_version: 3.6.5
|
159
172
|
specification_version: 4
|
160
173
|
summary: This gem provides a class called `AI::Chat` that is intended to make it as
|
161
|
-
easy as possible to use OpenAI's
|
174
|
+
easy as possible to use OpenAI's Responses API.
|
162
175
|
test_files: []
|
data/.rspec_status
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
example_id | status | run_time |
|
2
|
-
--------------------------------------------- | ------ | --------------- |
|
3
|
-
./spec/ai/chat/image_support_spec.rb[1:1:1:1] | passed | 0.00007 seconds |
|
4
|
-
./spec/ai/chat/image_support_spec.rb[1:1:2:1] | passed | 0.0001 seconds |
|
5
|
-
./spec/ai/chat/image_support_spec.rb[1:1:3:1] | passed | 0.00012 seconds |
|
6
|
-
./spec/ai/chat/image_support_spec.rb[1:1:3:2] | passed | 0.00018 seconds |
|
7
|
-
./spec/ai/chat/image_support_spec.rb[1:2:1:1] | passed | 0.0016 seconds |
|
8
|
-
./spec/ai/chat/image_support_spec.rb[1:2:1:2] | passed | 0.0001 seconds |
|
9
|
-
./spec/ai/chat/image_support_spec.rb[1:2:2:1] | passed | 0.00011 seconds |
|
10
|
-
./spec/ai/chat/image_support_spec.rb[1:2:3:1] | passed | 0.00007 seconds |
|
11
|
-
./spec/ai/chat/image_support_spec.rb[1:2:3:2] | passed | 0.00013 seconds |
|
12
|
-
./spec/ai/chat/image_support_spec.rb[1:3:1] | passed | 0.00005 seconds |
|
13
|
-
./spec/ai/chat/image_support_spec.rb[1:3:2] | passed | 0.00006 seconds |
|
14
|
-
./spec/ai/chat/image_support_spec.rb[1:3:3] | passed | 0.00008 seconds |
|
15
|
-
./spec/ai/chat/image_support_spec.rb[1:3:4] | passed | 0.00017 seconds |
|
16
|
-
./spec/ai/chat/image_support_spec.rb[1:3:5] | passed | 0.00074 seconds |
|