vibe-sort 0.2.0

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.
@@ -0,0 +1,377 @@
1
+ # VibeSort v0.2.0 Update Summary
2
+
3
+ ## Overview
4
+
5
+ VibeSort has been updated to **version 0.2.0** with support for sorting arrays containing **integers, floats, and strings**. This expands the gem's capabilities while maintaining the same clean API and architecture.
6
+
7
+ ---
8
+
9
+ ## 🎯 What Changed
10
+
11
+ ### Core Functionality
12
+
13
+ #### Before (v0.1.0)
14
+ - ✅ Sort arrays of numbers (integers and floats)
15
+ - ❌ Strings not supported
16
+ - ❌ Mixed-type arrays rejected
17
+
18
+ #### After (v0.2.0)
19
+ - ✅ Sort arrays of numbers (integers and floats)
20
+ - ✅ Sort arrays of strings (case-sensitive)
21
+ - ✅ Sort mixed-type arrays (numbers + strings)
22
+
23
+ ---
24
+
25
+ ## 📝 Technical Changes
26
+
27
+ ### 1. Input Validation (`VibeSort::Client`)
28
+
29
+ **File:** `lib/vibe_sort/client.rb`
30
+
31
+ **Before:**
32
+ ```ruby
33
+ def valid_input?(array)
34
+ return false unless array.is_a?(Array)
35
+ return false if array.empty?
36
+ array.all? { |item| item.is_a?(Numeric) }
37
+ end
38
+ ```
39
+
40
+ **After:**
41
+ ```ruby
42
+ def valid_input?(array)
43
+ return false unless array.is_a?(Array)
44
+ return false if array.empty?
45
+ array.all? { |item| item.is_a?(Numeric) || item.is_a?(String) }
46
+ end
47
+ ```
48
+
49
+ **Error Message Updated:**
50
+ - Before: `"Input must be an array of numbers"`
51
+ - After: `"Input must be an array of numbers or strings"`
52
+
53
+ ---
54
+
55
+ ### 2. AI Prompting (`VibeSort::Sorter`)
56
+
57
+ **File:** `lib/vibe_sort/sorter.rb`
58
+
59
+ **Before:**
60
+ ```ruby
61
+ {
62
+ role: "system",
63
+ content: "You are an assistant that only sorts number arrays.
64
+ Return a JSON object with a single key 'sorted_array'
65
+ containing the numbers sorted in ascending order."
66
+ }
67
+ ```
68
+
69
+ **After:**
70
+ ```ruby
71
+ {
72
+ role: "system",
73
+ content: "You are an assistant that only sorts arrays.
74
+ The array may contain numbers and strings.
75
+ Sort the array in ascending order.
76
+ Follow standard sorting rules: numbers should come before strings,
77
+ and string sorting should be case-sensitive.
78
+ Return a JSON object with a single key 'sorted_array'
79
+ containing the sorted elements."
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ### 3. Output Validation (`VibeSort::Sorter`)
86
+
87
+ **File:** `lib/vibe_sort/sorter.rb`
88
+
89
+ **Before:**
90
+ ```ruby
91
+ def validate_sorted_array!(sorted_array)
92
+ unless sorted_array.is_a?(Array)
93
+ raise ApiError.new("Response does not contain a valid 'sorted_array'")
94
+ end
95
+
96
+ unless sorted_array.all? { |item| item.is_a?(Numeric) }
97
+ raise ApiError.new("Sorted array contains non-numeric values")
98
+ end
99
+ end
100
+ ```
101
+
102
+ **After:**
103
+ ```ruby
104
+ def validate_sorted_array!(sorted_array)
105
+ unless sorted_array.is_a?(Array)
106
+ raise ApiError.new("Response does not contain a valid 'sorted_array'")
107
+ end
108
+
109
+ unless sorted_array.all? { |item| item.is_a?(Numeric) || item.is_a?(String) }
110
+ raise ApiError.new("Sorted array contains invalid values (must be numbers or strings)")
111
+ end
112
+ end
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 🚀 New Usage Examples
118
+
119
+ ### Sorting Strings
120
+
121
+ ```ruby
122
+ client = VibeSort::Client.new(api_key: ENV['OPENAI_API_KEY'])
123
+
124
+ words = ["banana", "Apple", "cherry", "date"]
125
+ result = client.sort(words)
126
+
127
+ puts result[:sorted_array]
128
+ # => ["Apple", "banana", "cherry", "date"]
129
+ ```
130
+
131
+ ### Sorting Mixed Types
132
+
133
+ ```ruby
134
+ mixed_items = [42, "hello", 8, "world", 15.5, "Apple"]
135
+ result = client.sort(mixed_items)
136
+
137
+ puts result[:sorted_array]
138
+ # => [8, 15.5, 42, "Apple", "hello", "world"]
139
+ # Note: Numbers come before strings
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 📋 Sorting Rules
145
+
146
+ The AI follows these standard sorting conventions:
147
+
148
+ 1. **Numbers Only**: Ascending numerical order
149
+ - `[5, 2, 8, 1]` → `[1, 2, 5, 8]`
150
+
151
+ 2. **Strings Only**: Ascending alphabetical order (case-sensitive)
152
+ - `["banana", "Apple", "cherry"]` → `["Apple", "banana", "cherry"]`
153
+ - Capital letters come before lowercase in ASCII ordering
154
+
155
+ 3. **Mixed Types**: Numbers first, then strings
156
+ - `[42, "hello", 8, "world"]` → `[8, 42, "hello", "world"]`
157
+ - Each group is sorted within itself
158
+
159
+ ---
160
+
161
+ ## 📚 Documentation Updates
162
+
163
+ ### Updated Files
164
+
165
+ 1. **README.md**
166
+ - Added string sorting examples
167
+ - Added mixed-type sorting examples
168
+ - Updated features list
169
+ - Added sorting behavior section
170
+ - Updated error message examples
171
+
172
+ 2. **docs/api_reference.md**
173
+ - Updated method signatures
174
+ - Added sorting rules documentation
175
+ - Updated examples with strings and mixed types
176
+ - Updated error messages
177
+
178
+ 3. **CHANGELOG.md**
179
+ - Added v0.2.0 release notes
180
+ - Documented all changes
181
+ - Listed technical details
182
+
183
+ 4. **lib/vibe_sort/version.rb**
184
+ - Updated version to `0.2.0`
185
+
186
+ ---
187
+
188
+ ## ✅ Backward Compatibility
189
+
190
+ **Good News:** This update is **fully backward compatible**!
191
+
192
+ - All v0.1.0 code continues to work without changes
193
+ - Number-only arrays still work exactly the same
194
+ - No breaking changes to the API
195
+ - Existing applications can upgrade without modification
196
+
197
+ ```ruby
198
+ # This still works exactly as before
199
+ client = VibeSort::Client.new(api_key: ENV['OPENAI_API_KEY'])
200
+ result = client.sort([5, 2, 8, 1, 9])
201
+ # => { success: true, sorted_array: [1, 2, 5, 8, 9] }
202
+ ```
203
+
204
+ ---
205
+
206
+ ## 🧪 Testing Recommendations
207
+
208
+ When upgrading to v0.2.0, test these scenarios:
209
+
210
+ ### 1. String Arrays
211
+ ```ruby
212
+ client.sort(["zebra", "Apple", "banana", "cherry"])
213
+ ```
214
+
215
+ ### 2. Mixed Arrays
216
+ ```ruby
217
+ client.sort([100, "test", 5, "alpha", 3.14])
218
+ ```
219
+
220
+ ### 3. Edge Cases
221
+ ```ruby
222
+ # Empty strings
223
+ client.sort(["", "hello", "world"])
224
+
225
+ # Special characters
226
+ client.sort(["@symbol", "123", "abc"])
227
+
228
+ # Unicode
229
+ client.sort(["café", "apple", "über"])
230
+ ```
231
+
232
+ ### 4. Invalid Input (should fail gracefully)
233
+ ```ruby
234
+ client.sort([1, :symbol, "text"]) # Contains symbol
235
+ client.sort([1, { key: "value" }]) # Contains hash
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 🔍 Implementation Notes
241
+
242
+ ### Why These Changes?
243
+
244
+ 1. **Input Validation**: Extended to accept strings, maintaining type safety
245
+ 2. **AI Prompt**: Generalized to handle multiple data types while maintaining clear sorting rules
246
+ 3. **Output Validation**: Ensures API returns only expected types
247
+
248
+ ### AI Model Behavior
249
+
250
+ The OpenAI GPT model (gpt-3.5-turbo-1106) handles mixed-type sorting intelligently:
251
+
252
+ - Understands standard sorting conventions
253
+ - Consistently places numbers before strings
254
+ - Handles case-sensitive string sorting
255
+ - Maintains JSON output format
256
+
257
+ ### Performance Considerations
258
+
259
+ - No performance difference from v0.1.0
260
+ - Same API latency (~1-3 seconds)
261
+ - Same cost per request (~$0.001-0.002)
262
+ - Same token usage (~100-200 tokens)
263
+
264
+ ---
265
+
266
+ ## 🎨 Architecture Remains Clean
267
+
268
+ The core architecture is unchanged:
269
+
270
+ ```
271
+ User Application
272
+
273
+ VibeSort::Client (validates input)
274
+
275
+ VibeSort::Configuration (stores settings)
276
+
277
+ VibeSort::Sorter (communicates with OpenAI)
278
+
279
+ OpenAI API (processes request)
280
+
281
+ Sorted Array (returned to user)
282
+ ```
283
+
284
+ ---
285
+
286
+ ## 📦 Upgrade Instructions
287
+
288
+ ### Using Bundler
289
+
290
+ Update your `Gemfile`:
291
+
292
+ ```ruby
293
+ gem 'vibe-sort', '~> 0.2.0'
294
+ ```
295
+
296
+ Then run:
297
+
298
+ ```bash
299
+ bundle update vibe-sort
300
+ ```
301
+
302
+ ### Manual Installation
303
+
304
+ ```bash
305
+ gem install vibe-sort -v 0.2.0
306
+ ```
307
+
308
+ ---
309
+
310
+ ## 🐛 Known Limitations
311
+
312
+ Same limitations as v0.1.0:
313
+
314
+ - Requires internet connection
315
+ - Incurs API costs
316
+ - Not suitable for production use
317
+ - 1-3 second latency per request
318
+ - Limited by OpenAI rate limits
319
+ - Ascending order only (no descending option)
320
+
321
+ **New considerations:**
322
+
323
+ - String sorting is case-sensitive (ASCII order)
324
+ - Unicode characters may sort unexpectedly
325
+ - Very long strings may increase token usage
326
+
327
+ ---
328
+
329
+ ## 🔮 Future Enhancements
330
+
331
+ Potential features for future versions:
332
+
333
+ 1. **Custom Sort Orders**
334
+ - Descending order option
335
+ - Case-insensitive string sorting
336
+ - Custom comparator functions
337
+
338
+ 2. **More Data Types**
339
+ - Date/time sorting
340
+ - Boolean values
341
+ - Custom object sorting
342
+
343
+ 3. **Advanced Features**
344
+ - Batch sorting
345
+ - Caching
346
+ - Retry logic
347
+ - Streaming responses
348
+
349
+ ---
350
+
351
+ ## 📞 Support
352
+
353
+ - 🐛 [Report Issues](https://github.com/chayut/vibe-sort/issues)
354
+ - 💡 [Feature Requests](https://github.com/chayut/vibe-sort/issues/new?labels=enhancement)
355
+ - 📖 [Documentation](https://github.com/chayut/vibe-sort/tree/main/docs)
356
+
357
+ ---
358
+
359
+ ## ✨ Summary
360
+
361
+ Version 0.2.0 expands VibeSort's capabilities while maintaining:
362
+
363
+ - ✅ Same clean API
364
+ - ✅ Same architecture
365
+ - ✅ Full backward compatibility
366
+ - ✅ Same performance characteristics
367
+ - ✅ Comprehensive documentation
368
+
369
+ The gem now handles a wider variety of data types, making it more versatile for educational purposes and demonstrations.
370
+
371
+ **Happy Sorting!** 🌀
372
+
373
+ ---
374
+
375
+ **Version:** 0.2.0
376
+ **Release Date:** October 16, 2025
377
+ **Status:** Stable
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vibe
4
+ module Sort
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
data/lib/vibe/sort.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sort/version"
4
+
5
+ module Vibe
6
+ module Sort
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VibeSort
4
+ # Client is the main public interface for the VibeSort gem
5
+ class Client
6
+ attr_reader :config
7
+
8
+ # Initialize a new VibeSort client
9
+ #
10
+ # @param api_key [String] OpenAI API key
11
+ # @param temperature [Float] Temperature for the model (default: 0.0)
12
+ # @raise [ArgumentError] if api_key is invalid
13
+ #
14
+ # @example
15
+ # client = VibeSort::Client.new(api_key: ENV['OPENAI_API_KEY'])
16
+ def initialize(api_key:, temperature: 0.0)
17
+ @config = Configuration.new(api_key: api_key, temperature: temperature)
18
+ end
19
+
20
+ # Sort an array of numbers and/or strings using OpenAI API
21
+ #
22
+ # @param array [Array] Array of numbers and/or strings to sort
23
+ # @return [Hash] Result hash with keys:
24
+ # - :success [Boolean] whether the operation succeeded
25
+ # - :sorted_array [Array] the sorted array (empty on failure)
26
+ # - :error [String] error message (only present on failure)
27
+ #
28
+ # @example Successful sort with numbers
29
+ # result = client.sort([5, 2, 8, 1, 9])
30
+ # #=> { success: true, sorted_array: [1, 2, 5, 8, 9] }
31
+ #
32
+ # @example Successful sort with strings
33
+ # result = client.sort(["banana", "Apple", "cherry"])
34
+ # #=> { success: true, sorted_array: ["Apple", "banana", "cherry"] }
35
+ #
36
+ # @example Successful sort with mixed types
37
+ # result = client.sort([42, "hello", 8, "world"])
38
+ # #=> { success: true, sorted_array: [8, 42, "hello", "world"] }
39
+ #
40
+ # @example Invalid input
41
+ # result = client.sort([1, :symbol, 3])
42
+ # #=> { success: false, sorted_array: [], error: "Input must be an array of numbers or strings" }
43
+ #
44
+ # @example API error
45
+ # result = client.sort([1, 2, 3]) # with invalid API key
46
+ # #=> { success: false, sorted_array: [], error: "OpenAI API error: Invalid API key" }
47
+ def sort(array)
48
+ # Validate input
49
+ unless valid_input?(array)
50
+ return {
51
+ success: false,
52
+ sorted_array: [],
53
+ error: "Input must be an array of numbers or strings"
54
+ }
55
+ end
56
+
57
+ # Perform the sort via API
58
+ sorter = Sorter.new(config)
59
+ sorter.perform(array)
60
+ rescue ApiError => e
61
+ {
62
+ success: false,
63
+ sorted_array: [],
64
+ error: e.message
65
+ }
66
+ rescue StandardError => e
67
+ {
68
+ success: false,
69
+ sorted_array: [],
70
+ error: "Unexpected error: #{e.message}"
71
+ }
72
+ end
73
+
74
+ private
75
+
76
+ # Validate that input is an array of numbers and/or strings
77
+ #
78
+ # @param array [Object] Input to validate
79
+ # @return [Boolean] true if valid, false otherwise
80
+ def valid_input?(array)
81
+ return false unless array.is_a?(Array)
82
+ return false if array.empty?
83
+
84
+ array.all? { |item| item.is_a?(Numeric) || item.is_a?(String) }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VibeSort
4
+ # Configuration class for VibeSort
5
+ # Holds API key and temperature settings
6
+ class Configuration
7
+ attr_reader :api_key, :temperature
8
+
9
+ # Initialize a new Configuration
10
+ #
11
+ # @param api_key [String] OpenAI API key
12
+ # @param temperature [Float] Temperature for the model (0.0 to 2.0)
13
+ # @raise [ArgumentError] if api_key is nil or empty
14
+ def initialize(api_key:, temperature: 0.0)
15
+ raise ArgumentError, "API key cannot be nil or empty" if api_key.nil? || api_key.empty?
16
+
17
+ @api_key = api_key
18
+ @temperature = temperature
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VibeSort
4
+ # Custom error class for API-related errors
5
+ class ApiError < StandardError
6
+ attr_reader :response
7
+
8
+ # Initialize a new ApiError
9
+ #
10
+ # @param message [String] Error message
11
+ # @param response [Faraday::Response, nil] HTTP response object
12
+ def initialize(message, response = nil)
13
+ super(message)
14
+ @response = response
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VibeSort
4
+ # Sorter class handles the API call to OpenAI
5
+ class Sorter
6
+ OPENAI_API_URL = "https://api.openai.com/v1/chat/completions"
7
+ DEFAULT_MODEL = "gpt-3.5-turbo-1106"
8
+
9
+ attr_reader :config
10
+
11
+ # Initialize a new Sorter
12
+ #
13
+ # @param config [VibeSort::Configuration] Configuration object
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ # Perform the sorting operation via OpenAI API
19
+ #
20
+ # @param array [Array] Array of numbers and/or strings to sort
21
+ # @return [Hash] Result hash with :success, :sorted_array, and optional :error keys
22
+ # @raise [VibeSort::ApiError] if the API request fails
23
+ def perform(array)
24
+ response = connection.post do |req|
25
+ req.body = build_payload(array)
26
+ end
27
+
28
+ handle_response(response)
29
+ end
30
+
31
+ private
32
+
33
+ # Build the connection to OpenAI API
34
+ #
35
+ # @return [Faraday::Connection] Faraday connection object
36
+ def connection
37
+ @connection ||= Faraday.new(url: OPENAI_API_URL) do |f|
38
+ f.request :json # Encodes request body as JSON
39
+ f.response :json # Decodes response body as JSON
40
+ f.headers["Authorization"] = "Bearer #{config.api_key}"
41
+ f.headers["Content-Type"] = "application/json"
42
+ f.adapter Faraday.default_adapter
43
+ end
44
+ end
45
+
46
+ # Build the request payload for OpenAI API
47
+ #
48
+ # @param array [Array] Array to sort (numbers and/or strings)
49
+ # @return [Hash] Request payload
50
+ def build_payload(array)
51
+ {
52
+ model: DEFAULT_MODEL,
53
+ temperature: config.temperature,
54
+ response_format: { type: "json_object" },
55
+ messages: [
56
+ {
57
+ role: "system",
58
+ content: "You are an assistant that only sorts arrays. The array may contain numbers and strings. Sort the array in ascending order. Follow standard sorting rules: numbers should come before strings, and string sorting should be case-sensitive. Return a JSON object with a single key 'sorted_array' containing the sorted elements."
59
+ },
60
+ {
61
+ role: "user",
62
+ content: "Please sort this array: #{array.to_json}"
63
+ }
64
+ ]
65
+ }
66
+ end
67
+
68
+ # Handle the API response
69
+ #
70
+ # @param response [Faraday::Response] HTTP response
71
+ # @return [Hash] Result hash
72
+ # @raise [VibeSort::ApiError] if response is invalid or parsing fails
73
+ def handle_response(response)
74
+ unless response.success?
75
+ error_message = extract_error_message(response)
76
+ raise ApiError.new("OpenAI API error: #{error_message}", response)
77
+ end
78
+
79
+ parse_sorted_array(response)
80
+ end
81
+
82
+ # Extract error message from failed response
83
+ #
84
+ # @param response [Faraday::Response] HTTP response
85
+ # @return [String] Error message
86
+ def extract_error_message(response)
87
+ return "Unknown error" unless response.body.is_a?(Hash)
88
+
89
+ response.body.dig("error", "message") || "HTTP #{response.status}"
90
+ rescue StandardError
91
+ "HTTP #{response.status}"
92
+ end
93
+
94
+ # Parse the sorted array from the API response
95
+ #
96
+ # @param response [Faraday::Response] HTTP response
97
+ # @return [Hash] Success result with sorted array
98
+ # @raise [VibeSort::ApiError] if parsing fails or validation fails
99
+ def parse_sorted_array(response)
100
+ content = response.body.dig("choices", 0, "message", "content")
101
+ raise ApiError.new("Invalid response structure", response) if content.nil?
102
+
103
+ parsed_content = JSON.parse(content)
104
+ sorted_array = parsed_content["sorted_array"]
105
+
106
+ validate_sorted_array!(sorted_array)
107
+
108
+ { success: true, sorted_array: sorted_array }
109
+ rescue JSON::ParserError => e
110
+ raise ApiError.new("Failed to parse JSON response: #{e.message}", response)
111
+ end
112
+
113
+ # Validate that the sorted array is valid
114
+ #
115
+ # @param sorted_array [Object] Parsed sorted array
116
+ # @raise [VibeSort::ApiError] if validation fails
117
+ def validate_sorted_array!(sorted_array)
118
+ raise ApiError, "Response does not contain a valid 'sorted_array'" unless sorted_array.is_a?(Array)
119
+
120
+ return if sorted_array.all? { |item| item.is_a?(Numeric) || item.is_a?(String) }
121
+
122
+ raise ApiError, "Sorted array contains invalid values (must be numbers or strings)"
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VibeSort
4
+ VERSION = "0.2.0"
5
+ end
data/lib/vibe_sort.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ require_relative "vibe_sort/version"
7
+ require_relative "vibe_sort/error"
8
+ require_relative "vibe_sort/configuration"
9
+ require_relative "vibe_sort/sorter"
10
+ require_relative "vibe_sort/client"
11
+
12
+ module VibeSort
13
+ class Error < StandardError; end
14
+ end
data/sig/vibe/sort.rbs ADDED
@@ -0,0 +1,6 @@
1
+ module Vibe
2
+ module Sort
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end