commitgpt 0.3.1 → 0.3.3
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/bin/aicm +1 -1
- data/commitgpt.gemspec +19 -18
- data/lib/commitgpt/cli.rb +28 -23
- data/lib/commitgpt/commit_ai.rb +126 -127
- data/lib/commitgpt/config_manager.rb +34 -34
- data/lib/commitgpt/provider_presets.rb +12 -12
- data/lib/commitgpt/setup_wizard.rb +87 -84
- data/lib/commitgpt/version.rb +1 -1
- data/lib/commitgpt.rb +2 -2
- metadata +6 -5
data/lib/commitgpt/commit_ai.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
3
|
+
require 'httparty'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'io/console'
|
|
8
|
+
require 'tty-prompt'
|
|
9
|
+
require_relative 'string'
|
|
10
|
+
require_relative 'config_manager'
|
|
11
11
|
|
|
12
12
|
# CommitGpt based on GPT-3
|
|
13
13
|
module CommitGpt
|
|
@@ -17,17 +17,17 @@ module CommitGpt
|
|
|
17
17
|
|
|
18
18
|
def initialize
|
|
19
19
|
provider_config = ConfigManager.get_active_provider_config
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
if provider_config
|
|
22
|
-
@api_key = provider_config[
|
|
23
|
-
@base_url = provider_config[
|
|
24
|
-
@model = provider_config[
|
|
25
|
-
@diff_len = provider_config[
|
|
22
|
+
@api_key = provider_config['api_key']
|
|
23
|
+
@base_url = provider_config['base_url']
|
|
24
|
+
@model = provider_config['model']
|
|
25
|
+
@diff_len = provider_config['diff_len'] || 32_768
|
|
26
26
|
else
|
|
27
27
|
@api_key = nil
|
|
28
28
|
@base_url = nil
|
|
29
29
|
@model = nil
|
|
30
|
-
@diff_len =
|
|
30
|
+
@diff_len = 32_768
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -47,7 +47,7 @@ module CommitGpt
|
|
|
47
47
|
case action
|
|
48
48
|
when :commit
|
|
49
49
|
commit_command = "git commit -m \"#{ai_commit_message}\""
|
|
50
|
-
puts "\n▲ Executing: #{commit_command}".
|
|
50
|
+
puts "\n▲ Executing: #{commit_command}".yellow
|
|
51
51
|
system(commit_command)
|
|
52
52
|
puts "\n\n"
|
|
53
53
|
puts `git log -1`
|
|
@@ -57,18 +57,18 @@ module CommitGpt
|
|
|
57
57
|
next
|
|
58
58
|
when :edit
|
|
59
59
|
prompt = TTY::Prompt.new
|
|
60
|
-
new_message = prompt.ask(
|
|
60
|
+
new_message = prompt.ask('Enter your commit message:')
|
|
61
61
|
if new_message && !new_message.strip.empty?
|
|
62
62
|
commit_command = "git commit -m \"#{new_message}\""
|
|
63
63
|
system(commit_command)
|
|
64
64
|
puts "\n"
|
|
65
65
|
puts `git log -1`
|
|
66
66
|
else
|
|
67
|
-
puts
|
|
67
|
+
puts '▲ Commit aborted (empty message).'.red
|
|
68
68
|
end
|
|
69
69
|
break
|
|
70
70
|
when :exit
|
|
71
|
-
puts
|
|
71
|
+
puts '▲ Exit without commit.'.yellow
|
|
72
72
|
break
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -76,15 +76,15 @@ module CommitGpt
|
|
|
76
76
|
|
|
77
77
|
def list_models
|
|
78
78
|
headers = {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
'Content-Type' => 'application/json',
|
|
80
|
+
'User-Agent' => "Ruby/#{RUBY_VERSION}"
|
|
81
81
|
}
|
|
82
|
-
headers[
|
|
82
|
+
headers['Authorization'] = "Bearer #{@api_key}" if @api_key
|
|
83
83
|
|
|
84
84
|
begin
|
|
85
85
|
response = HTTParty.get("#{@base_url}/models", headers: headers)
|
|
86
|
-
models = response[
|
|
87
|
-
models.each { |m| puts m[
|
|
86
|
+
models = response['data'] || []
|
|
87
|
+
models.each { |m| puts m['id'] }
|
|
88
88
|
rescue StandardError => e
|
|
89
89
|
puts "▲ Failed to list models: #{e.message}".red
|
|
90
90
|
end
|
|
@@ -92,15 +92,15 @@ module CommitGpt
|
|
|
92
92
|
|
|
93
93
|
def list_models
|
|
94
94
|
headers = {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
'Content-Type' => 'application/json',
|
|
96
|
+
'User-Agent' => "Ruby/#{RUBY_VERSION}"
|
|
97
97
|
}
|
|
98
|
-
headers[
|
|
98
|
+
headers['Authorization'] = "Bearer #{AICM_KEY}" if AICM_KEY
|
|
99
99
|
|
|
100
100
|
begin
|
|
101
101
|
response = HTTParty.get("#{AICM_LINK}/models", headers: headers)
|
|
102
|
-
models = response[
|
|
103
|
-
models.each { |m| puts m[
|
|
102
|
+
models = response['data'] || []
|
|
103
|
+
models.each { |m| puts m['id'] }
|
|
104
104
|
rescue StandardError => e
|
|
105
105
|
puts "▲ Failed to list models: #{e.message}".red
|
|
106
106
|
end
|
|
@@ -108,15 +108,15 @@ module CommitGpt
|
|
|
108
108
|
|
|
109
109
|
private
|
|
110
110
|
|
|
111
|
-
def confirm_commit(
|
|
111
|
+
def confirm_commit(_message)
|
|
112
112
|
prompt = TTY::Prompt.new
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
begin
|
|
115
|
-
prompt.select(
|
|
116
|
-
menu.choice
|
|
117
|
-
menu.choice
|
|
118
|
-
menu.choice
|
|
119
|
-
menu.choice
|
|
115
|
+
prompt.select('Action:') do |menu|
|
|
116
|
+
menu.choice 'Commit', :commit
|
|
117
|
+
menu.choice 'Regenerate', :regenerate
|
|
118
|
+
menu.choice 'Edit', :edit
|
|
119
|
+
menu.choice 'Exit without commit', :exit
|
|
120
120
|
end
|
|
121
121
|
rescue TTY::Reader::InputInterrupt, Interrupt
|
|
122
122
|
:exit
|
|
@@ -133,47 +133,47 @@ module CommitGpt
|
|
|
133
133
|
diff_unstaged = `git diff . #{exclusions}`.chomp
|
|
134
134
|
|
|
135
135
|
if !diff_unstaged.empty?
|
|
136
|
-
if
|
|
136
|
+
if diff_cached.empty?
|
|
137
|
+
# Scenario: Only unstaged changes
|
|
138
|
+
choice = prompt_no_staged_changes
|
|
139
|
+
case choice
|
|
140
|
+
when :add_all
|
|
141
|
+
puts '▲ Running git add .'.yellow
|
|
142
|
+
system('git add .')
|
|
143
|
+
diff_cached = `git diff --cached . #{exclusions}`.chomp
|
|
144
|
+
if diff_cached.empty?
|
|
145
|
+
puts '▲ Still no changes to commit.'.red
|
|
146
|
+
return nil
|
|
147
|
+
end
|
|
148
|
+
when :exit
|
|
149
|
+
return nil
|
|
150
|
+
end
|
|
151
|
+
else
|
|
137
152
|
# Scenario: Mixed state (some staged, some not)
|
|
138
|
-
puts
|
|
139
|
-
|
|
153
|
+
puts '▲ You have both staged and unstaged changes:'.yellow
|
|
154
|
+
|
|
140
155
|
staged_files = `git diff --cached --name-status . #{exclusions}`.chomp
|
|
141
156
|
unstaged_files = `git diff --name-status . #{exclusions}`.chomp
|
|
142
157
|
|
|
143
158
|
puts "\n #{'Staged changes:'.green}"
|
|
144
|
-
puts staged_files.gsub(/^/,
|
|
159
|
+
puts staged_files.gsub(/^/, ' ')
|
|
145
160
|
|
|
146
161
|
puts "\n #{'Unstaged changes:'.red}"
|
|
147
|
-
puts unstaged_files.gsub(/^/,
|
|
148
|
-
puts
|
|
162
|
+
puts unstaged_files.gsub(/^/, ' ')
|
|
163
|
+
puts ''
|
|
149
164
|
|
|
150
165
|
prompt = TTY::Prompt.new
|
|
151
|
-
choice = prompt.select(
|
|
152
|
-
menu.choice
|
|
153
|
-
menu.choice
|
|
154
|
-
menu.choice
|
|
166
|
+
choice = prompt.select('How to proceed?') do |menu|
|
|
167
|
+
menu.choice 'Include unstaged changes (git add .)', :add_all
|
|
168
|
+
menu.choice 'Use staged changes only', :staged_only
|
|
169
|
+
menu.choice 'Exit', :exit
|
|
155
170
|
end
|
|
156
171
|
|
|
157
172
|
case choice
|
|
158
173
|
when :add_all
|
|
159
|
-
puts
|
|
160
|
-
system(
|
|
161
|
-
diff_cached = `git diff --cached . #{exclusions}`.chomp
|
|
162
|
-
when :exit
|
|
163
|
-
return nil
|
|
164
|
-
end
|
|
165
|
-
else
|
|
166
|
-
# Scenario: Only unstaged changes
|
|
167
|
-
choice = prompt_no_staged_changes
|
|
168
|
-
case choice
|
|
169
|
-
when :add_all
|
|
170
|
-
puts "▲ Running git add .".yellow
|
|
171
|
-
system("git add .")
|
|
174
|
+
puts '▲ Running git add .'.yellow
|
|
175
|
+
system('git add .')
|
|
172
176
|
diff_cached = `git diff --cached . #{exclusions}`.chomp
|
|
173
|
-
if diff_cached.empty?
|
|
174
|
-
puts "▲ Still no changes to commit.".red
|
|
175
|
-
return nil
|
|
176
|
-
end
|
|
177
177
|
when :exit
|
|
178
178
|
return nil
|
|
179
179
|
end
|
|
@@ -184,7 +184,7 @@ module CommitGpt
|
|
|
184
184
|
# git status --porcelain includes untracked files
|
|
185
185
|
git_status = `git status --porcelain`.chomp
|
|
186
186
|
if git_status.empty?
|
|
187
|
-
puts
|
|
187
|
+
puts '▲ No changes to commit. Working tree clean.'.yellow
|
|
188
188
|
return nil
|
|
189
189
|
else
|
|
190
190
|
# Only untracked files? Or ignored files?
|
|
@@ -193,11 +193,11 @@ module CommitGpt
|
|
|
193
193
|
choice = prompt_no_staged_changes
|
|
194
194
|
case choice
|
|
195
195
|
when :add_all
|
|
196
|
-
puts
|
|
197
|
-
system(
|
|
196
|
+
puts '▲ Running git add .'.yellow
|
|
197
|
+
system('git add .')
|
|
198
198
|
diff_cached = `git diff --cached . #{exclusions}`.chomp
|
|
199
199
|
when :exit
|
|
200
|
-
|
|
200
|
+
return nil
|
|
201
201
|
end
|
|
202
202
|
end
|
|
203
203
|
end
|
|
@@ -221,12 +221,12 @@ module CommitGpt
|
|
|
221
221
|
end
|
|
222
222
|
|
|
223
223
|
def prompt_no_staged_changes
|
|
224
|
-
puts
|
|
224
|
+
puts '▲ No staged changes found (but unstaged/untracked files exist).'.yellow
|
|
225
225
|
prompt = TTY::Prompt.new
|
|
226
226
|
begin
|
|
227
|
-
prompt.select(
|
|
227
|
+
prompt.select('Choose an option:') do |menu|
|
|
228
228
|
menu.choice "Run 'git add .' to stage all changes", :add_all
|
|
229
|
-
menu.choice
|
|
229
|
+
menu.choice 'Exit (stage files manually)', :exit
|
|
230
230
|
end
|
|
231
231
|
rescue TTY::Reader::InputInterrupt, Interrupt
|
|
232
232
|
:exit
|
|
@@ -237,10 +237,10 @@ module CommitGpt
|
|
|
237
237
|
puts "▲ The diff is too large (#{current_len} chars, max #{max_len}).".yellow
|
|
238
238
|
prompt = TTY::Prompt.new
|
|
239
239
|
begin
|
|
240
|
-
prompt.select(
|
|
240
|
+
prompt.select('Choose an option:') do |menu|
|
|
241
241
|
menu.choice "Use first #{max_len} characters to generate commit message", :truncate
|
|
242
|
-
menu.choice
|
|
243
|
-
menu.choice
|
|
242
|
+
menu.choice 'Use unlimited characters (may fail or be slow)', :unlimited
|
|
243
|
+
menu.choice 'Exit', :exit
|
|
244
244
|
end
|
|
245
245
|
rescue TTY::Reader::InputInterrupt, Interrupt
|
|
246
246
|
:exit
|
|
@@ -252,7 +252,7 @@ module CommitGpt
|
|
|
252
252
|
|
|
253
253
|
# Check if config exists
|
|
254
254
|
unless ConfigManager.config_exists?
|
|
255
|
-
puts
|
|
255
|
+
puts '▲ Configuration not found. Generating default config...'.yellow
|
|
256
256
|
ConfigManager.generate_default_configs
|
|
257
257
|
puts "▲ Please run 'aicm setup' to configure your provider.".red
|
|
258
258
|
return false
|
|
@@ -272,36 +272,36 @@ module CommitGpt
|
|
|
272
272
|
begin
|
|
273
273
|
`git rev-parse --is-inside-work-tree`
|
|
274
274
|
rescue StandardError
|
|
275
|
-
puts
|
|
275
|
+
puts '▲ This is not a git repository'.red
|
|
276
276
|
return false
|
|
277
277
|
end
|
|
278
278
|
|
|
279
279
|
true
|
|
280
280
|
end
|
|
281
281
|
|
|
282
|
-
def generate_commit(diff =
|
|
282
|
+
def generate_commit(diff = '')
|
|
283
283
|
messages = [
|
|
284
284
|
{
|
|
285
|
-
role:
|
|
286
|
-
content:
|
|
285
|
+
role: 'system',
|
|
286
|
+
content: 'Generate a concise git commit message title in present tense that precisely describes the key changes in the following code diff. Focus on what was changed, not just file names. Provide only the title, no description or body. ' \
|
|
287
287
|
"Message language: English. Rules:\n" \
|
|
288
288
|
"- Commit message must be a maximum of 100 characters.\n" \
|
|
289
289
|
"- Exclude anything unnecessary such as translation. Your entire response will be passed directly into git commit.\n" \
|
|
290
290
|
"- IMPORTANT: Do not include any explanations, introductions, or additional text. Do not wrap the commit message in quotes or any other formatting. The commit message must not exceed 100 characters. Respond with ONLY the commit message text. \n" \
|
|
291
291
|
"- Be specific: include concrete details (package names, versions, functionality) rather than generic statements. \n" \
|
|
292
|
-
|
|
292
|
+
'- Return ONLY the commit message, nothing else.'
|
|
293
293
|
},
|
|
294
294
|
{
|
|
295
|
-
role:
|
|
295
|
+
role: 'user',
|
|
296
296
|
content: "Generate a commit message for the following git diff:\n\n#{diff}"
|
|
297
297
|
}
|
|
298
298
|
]
|
|
299
299
|
|
|
300
300
|
# Check config for disable_reasoning support (default true if not set)
|
|
301
301
|
provider_config = ConfigManager.get_active_provider_config
|
|
302
|
-
can_disable_reasoning = provider_config.key?(
|
|
302
|
+
can_disable_reasoning = provider_config.key?('can_disable_reasoning') ? provider_config['can_disable_reasoning'] : true
|
|
303
303
|
# Get configured max_tokens or default to 2000
|
|
304
|
-
configured_max_tokens = provider_config[
|
|
304
|
+
configured_max_tokens = provider_config['max_tokens'] || 2000
|
|
305
305
|
|
|
306
306
|
payload = {
|
|
307
307
|
model: @model,
|
|
@@ -318,33 +318,37 @@ module CommitGpt
|
|
|
318
318
|
end
|
|
319
319
|
|
|
320
320
|
# Initial UI feedback (only on first try)
|
|
321
|
-
puts
|
|
321
|
+
puts '....... Generating your AI commit message ......'.gray unless defined?(@is_retrying) && @is_retrying
|
|
322
322
|
|
|
323
|
-
full_content =
|
|
324
|
-
full_reasoning =
|
|
323
|
+
full_content = ''
|
|
324
|
+
full_reasoning = ''
|
|
325
325
|
printed_reasoning = false
|
|
326
326
|
printed_content_prefix = false
|
|
327
327
|
stop_stream = false
|
|
328
|
-
|
|
328
|
+
|
|
329
329
|
uri = URI("#{@base_url}/chat/completions")
|
|
330
330
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
331
|
-
http.use_ssl = (uri.scheme ==
|
|
331
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
332
332
|
http.read_timeout = 120
|
|
333
333
|
|
|
334
334
|
request = Net::HTTP::Post.new(uri)
|
|
335
|
-
request[
|
|
336
|
-
request[
|
|
335
|
+
request['Content-Type'] = 'application/json'
|
|
336
|
+
request['Authorization'] = "Bearer #{@api_key}" if @api_key
|
|
337
337
|
request.body = payload.to_json
|
|
338
338
|
|
|
339
339
|
begin
|
|
340
340
|
http.request(request) do |response|
|
|
341
|
-
if response.code !=
|
|
341
|
+
if response.code != '200'
|
|
342
342
|
# Parse error body
|
|
343
|
-
error_body = response.read_body
|
|
344
|
-
result =
|
|
345
|
-
|
|
343
|
+
error_body = response.read_body
|
|
344
|
+
result = begin
|
|
345
|
+
JSON.parse(error_body)
|
|
346
|
+
rescue StandardError
|
|
347
|
+
nil
|
|
348
|
+
end
|
|
349
|
+
|
|
346
350
|
error_msg = if result
|
|
347
|
-
result.dig(
|
|
351
|
+
result.dig('error', 'message') || result['error'] || result['message']
|
|
348
352
|
else
|
|
349
353
|
error_body
|
|
350
354
|
end
|
|
@@ -353,10 +357,10 @@ module CommitGpt
|
|
|
353
357
|
error_msg = "HTTP #{response.code}"
|
|
354
358
|
error_msg += " Raw: #{error_body}" unless error_body.to_s.strip.empty?
|
|
355
359
|
end
|
|
356
|
-
|
|
357
|
-
if can_disable_reasoning && (error_msg =~ /parameter|reasoning|unsupported/i || response.code ==
|
|
360
|
+
|
|
361
|
+
if can_disable_reasoning && (error_msg =~ /parameter|reasoning|unsupported/i || response.code == '400')
|
|
358
362
|
puts "▲ Provider does not support 'disable_reasoning'. Updating config and retrying...".yellow
|
|
359
|
-
ConfigManager.update_provider(provider_config[
|
|
363
|
+
ConfigManager.update_provider(provider_config['name'], { 'can_disable_reasoning' => false })
|
|
360
364
|
@is_retrying = true
|
|
361
365
|
return generate_commit(diff)
|
|
362
366
|
else
|
|
@@ -364,9 +368,9 @@ module CommitGpt
|
|
|
364
368
|
return nil
|
|
365
369
|
end
|
|
366
370
|
end
|
|
367
|
-
|
|
371
|
+
|
|
368
372
|
# Process Streaming Response
|
|
369
|
-
buffer =
|
|
373
|
+
buffer = ''
|
|
370
374
|
response.read_body do |chunk|
|
|
371
375
|
break if stop_stream
|
|
372
376
|
|
|
@@ -374,22 +378,20 @@ module CommitGpt
|
|
|
374
378
|
while (line_end = buffer.index("\n"))
|
|
375
379
|
line = buffer.slice!(0, line_end + 1).strip
|
|
376
380
|
next if line.empty?
|
|
377
|
-
next unless line.start_with?(
|
|
381
|
+
next unless line.start_with?('data: ')
|
|
378
382
|
|
|
379
|
-
data_str = line[6
|
|
380
|
-
next if data_str ==
|
|
383
|
+
data_str = line[6..]
|
|
384
|
+
next if data_str == '[DONE]'
|
|
381
385
|
|
|
382
386
|
begin
|
|
383
387
|
data = JSON.parse(data_str)
|
|
384
|
-
delta = data.dig(
|
|
388
|
+
delta = data.dig('choices', 0, 'delta')
|
|
385
389
|
next unless delta
|
|
386
390
|
|
|
387
391
|
# Handle Reasoning
|
|
388
|
-
reasoning_chunk = delta[
|
|
392
|
+
reasoning_chunk = delta['reasoning_content'] || delta['reasoning']
|
|
389
393
|
if reasoning_chunk && !reasoning_chunk.empty?
|
|
390
|
-
unless printed_reasoning
|
|
391
|
-
puts "\nThinking...".gray
|
|
392
|
-
end
|
|
394
|
+
puts "\nThinking...".gray unless printed_reasoning
|
|
393
395
|
print reasoning_chunk.gray
|
|
394
396
|
full_reasoning += reasoning_chunk
|
|
395
397
|
printed_reasoning = true
|
|
@@ -397,17 +399,17 @@ module CommitGpt
|
|
|
397
399
|
end
|
|
398
400
|
|
|
399
401
|
# Handle Content
|
|
400
|
-
content_chunk = delta[
|
|
402
|
+
content_chunk = delta['content']
|
|
401
403
|
if content_chunk && !content_chunk.empty?
|
|
402
404
|
if printed_reasoning && !printed_content_prefix
|
|
403
|
-
puts
|
|
405
|
+
puts '' # Newline after reasoning block
|
|
404
406
|
end
|
|
405
|
-
|
|
407
|
+
|
|
406
408
|
unless printed_content_prefix
|
|
407
409
|
print "\n▲ Commit message: git commit -am \"".green
|
|
408
410
|
printed_content_prefix = true
|
|
409
411
|
end
|
|
410
|
-
|
|
412
|
+
|
|
411
413
|
# Prevent infinite loops/repetitive garbage
|
|
412
414
|
if full_content.length + content_chunk.length > 300
|
|
413
415
|
stop_stream = true
|
|
@@ -418,12 +420,9 @@ module CommitGpt
|
|
|
418
420
|
full_content += content_chunk
|
|
419
421
|
$stdout.flush
|
|
420
422
|
end
|
|
421
|
-
|
|
422
|
-
# Handle Usage (some providers send usage at the end)
|
|
423
|
-
if data["usage"]
|
|
424
|
-
@last_usage = data["usage"]
|
|
425
|
-
end
|
|
426
423
|
|
|
424
|
+
# Handle Usage (some providers send usage at the end)
|
|
425
|
+
@last_usage = data['usage'] if data['usage']
|
|
427
426
|
rescue JSON::ParserError
|
|
428
427
|
# Partial JSON, wait for more data
|
|
429
428
|
end
|
|
@@ -434,38 +433,38 @@ module CommitGpt
|
|
|
434
433
|
puts "▲ Error: #{e.message}".red
|
|
435
434
|
return nil
|
|
436
435
|
end
|
|
437
|
-
|
|
436
|
+
|
|
438
437
|
# Close the quote
|
|
439
|
-
puts "
|
|
438
|
+
puts '"'.green if printed_content_prefix
|
|
440
439
|
|
|
441
440
|
# Post-processing Logic (Retry if empty content)
|
|
442
441
|
if (full_content.nil? || full_content.strip.empty?) && (full_reasoning && !full_reasoning.strip.empty?)
|
|
443
442
|
if can_disable_reasoning
|
|
444
443
|
puts "\n▲ Model returned reasoning despite 'disable_reasoning: true'. Updating config and retrying...".yellow
|
|
445
|
-
ConfigManager.update_provider(provider_config[
|
|
444
|
+
ConfigManager.update_provider(provider_config['name'], { 'can_disable_reasoning' => false })
|
|
446
445
|
@is_retrying = true
|
|
447
446
|
return generate_commit(diff)
|
|
448
447
|
else
|
|
449
448
|
puts "\n▲ Model output truncated (Reasoning consumed all #{configured_max_tokens} tokens).".red
|
|
450
449
|
prompt = TTY::Prompt.new
|
|
451
|
-
choice = prompt.select(
|
|
450
|
+
choice = prompt.select('Choose an action:') do |menu|
|
|
452
451
|
menu.choice "Double max_tokens to #{configured_max_tokens * 2}", :double
|
|
453
|
-
menu.choice
|
|
454
|
-
menu.choice
|
|
452
|
+
menu.choice 'Set custom max_tokens...', :custom
|
|
453
|
+
menu.choice 'Abort', :abort
|
|
455
454
|
end
|
|
456
455
|
|
|
457
456
|
new_max = case choice
|
|
458
457
|
when :double
|
|
459
458
|
configured_max_tokens * 2
|
|
460
459
|
when :custom
|
|
461
|
-
prompt.ask(
|
|
460
|
+
prompt.ask('Enter new max_tokens:', convert: :int)
|
|
462
461
|
when :abort
|
|
463
462
|
return nil
|
|
464
463
|
end
|
|
465
|
-
|
|
464
|
+
|
|
466
465
|
if new_max
|
|
467
466
|
puts "▲ Updating max_tokens to #{new_max} and retrying...".yellow
|
|
468
|
-
ConfigManager.update_provider(provider_config[
|
|
467
|
+
ConfigManager.update_provider(provider_config['name'], { 'max_tokens' => new_max })
|
|
469
468
|
@is_retrying = true
|
|
470
469
|
return generate_commit(diff)
|
|
471
470
|
end
|
|
@@ -474,10 +473,10 @@ module CommitGpt
|
|
|
474
473
|
end
|
|
475
474
|
|
|
476
475
|
if full_content.empty? && full_reasoning.empty?
|
|
477
|
-
puts
|
|
476
|
+
puts '▲ No response from AI.'.red
|
|
478
477
|
return nil
|
|
479
478
|
end
|
|
480
|
-
|
|
479
|
+
|
|
481
480
|
# Print usage info if available (saved from stream or approximated)
|
|
482
481
|
if defined?(@last_usage) && @last_usage
|
|
483
482
|
puts "\n...... Tokens: #{@last_usage['total_tokens']} (Prompt: #{@last_usage['prompt_tokens']}, Completion: #{@last_usage['completion_tokens']})\n\n".gray
|
|
@@ -489,7 +488,7 @@ module CommitGpt
|
|
|
489
488
|
|
|
490
489
|
# Take only the first non-empty line to avoid repetition or multi-line garbage
|
|
491
490
|
first_line = full_content.split("\n").map(&:strip).reject(&:empty?).first
|
|
492
|
-
first_line&.gsub(/\A["']|["']\z/,
|
|
491
|
+
first_line&.gsub(/\A["']|["']\z/, '') || ''
|
|
493
492
|
end
|
|
494
493
|
end
|
|
495
494
|
end
|