commitgpt 0.3.9 → 0.3.10
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/README.md +8 -4
- data/lib/commitgpt/provider_presets.rb +10 -1
- data/lib/commitgpt/setup_wizard.rb +82 -31
- data/lib/commitgpt/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc50a5ed7cfb5cc59703014b3b6f58370028dfe2c1bc5c7186e3c6d43a755dd7
|
|
4
|
+
data.tar.gz: e9dd5c01f7c07062fc803710c79a31ce40ab9a9625918c2753bf59d25b4f9d4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2005518993b77ff65ab764075016d6c1a8bb1259efff645017ab3f0a23a83f03e1b18fc7df257f497a8ecf5a705d4a0c91c29a5e044d00e9896437faed7cf675
|
|
7
|
+
data.tar.gz: d290dac27335967df2b7dfdc8309509f7f228ede0d50c7e0b86e99c28cb16b1551d2bb5004ce3d846a183f1fb85beda193d5fec2873ad9e397211fa6e93ceb26
|
data/README.md
CHANGED
|
@@ -261,13 +261,17 @@ Get an API key at [https://developer.amd.com.cn/radeon/tokenfactory](https://dev
|
|
|
261
261
|
|
|
262
262
|
> **Note**: Registration requires **both an email address and a Chinese (+86) mobile phone number**. Without a Chinese phone number you cannot complete sign-up, so this provider is only usable if you have one.
|
|
263
263
|
|
|
264
|
+
> **Note**: The gateway in front of this endpoint always answers `GET /models` with 403, so models cannot be discovered automatically. CommitGPT ships the list below and offers it directly during setup — no detection request is made for this provider.
|
|
265
|
+
|
|
264
266
|
```
|
|
265
|
-
DeepSeek-V4-Flash
|
|
266
|
-
Qwen3.6-35B-A3B
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
DeepSeek-V4-Flash # Max Context Length: 1,000,000 tokens
|
|
268
|
+
Qwen3.6-35B-A3B # Max Context Length: 262,144 tokens
|
|
269
|
+
MiniCPM-V46 # Max Context Length: 262,144 tokens
|
|
270
|
+
MiniCPM5-1B # Max Context Length: 131,072 tokens
|
|
269
271
|
```
|
|
270
272
|
|
|
273
|
+
Setup reports the selected model's context window and suggests a matching `diff_len`, so you don't have to work the number out yourself.
|
|
274
|
+
|
|
271
275
|
## How It Works
|
|
272
276
|
This CLI tool runs a `git diff` command to grab all staged changes, sends this to OpenAI's GPT API (or compatible endpoint), and returns an AI-generated commit message. The tool uses the `/v1/chat/completions` endpoint with optimized prompts/system instructions for generating conventional commit messages.
|
|
273
277
|
|
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
module CommitGpt
|
|
4
4
|
# Provider presets for common AI providers
|
|
5
5
|
PROVIDER_PRESETS = [
|
|
6
|
-
|
|
6
|
+
# The Azure gateway in front of this endpoint always answers /models with 403,
|
|
7
|
+
# so the model list is built in and no request is made during setup.
|
|
8
|
+
# Each value is that model's context window in tokens.
|
|
9
|
+
{ label: 'AMD Radeon (China)', value: 'amd', base_url: 'https://developer.amd.com.cn/radeon/api/v1',
|
|
10
|
+
models: {
|
|
11
|
+
'DeepSeek-V4-Flash' => 1_000_000,
|
|
12
|
+
'MiniCPM-V46' => 262_144,
|
|
13
|
+
'MiniCPM5-1B' => 131_072,
|
|
14
|
+
'Qwen3.6-35B-A3B' => 262_144
|
|
15
|
+
} },
|
|
7
16
|
{ label: 'Anthropic Claude', value: 'anthropic', base_url: 'https://api.anthropic.com/v1' },
|
|
8
17
|
{ label: 'Cerebras', value: 'cerebras', base_url: 'https://api.cerebras.ai/v1' },
|
|
9
18
|
{ label: 'DeepSeek', value: 'deepseek', base_url: 'https://api.deepseek.com' },
|
|
@@ -10,6 +10,18 @@ require_relative 'string'
|
|
|
10
10
|
module CommitGpt
|
|
11
11
|
# Interactive setup wizard for configuring AI providers
|
|
12
12
|
class SetupWizard
|
|
13
|
+
# Diff bytes per context token. Code and CJK pack more bytes into a token
|
|
14
|
+
# than English prose, so this stays deliberately conservative.
|
|
15
|
+
DIFF_BYTES_PER_TOKEN = 2
|
|
16
|
+
|
|
17
|
+
# Ceiling for the suggested diff length. Past this a single request gets
|
|
18
|
+
# slow and one commit message stops describing the change usefully, so
|
|
19
|
+
# chunked mode is the better answer than a bigger window.
|
|
20
|
+
MAX_SUGGESTED_DIFF_LEN = 131_072
|
|
21
|
+
|
|
22
|
+
# Used when the model's context window is unknown
|
|
23
|
+
FALLBACK_DIFF_LEN = 32_768
|
|
24
|
+
|
|
13
25
|
def initialize
|
|
14
26
|
@prompt = TTY::Prompt.new
|
|
15
27
|
end
|
|
@@ -45,13 +57,13 @@ module CommitGpt
|
|
|
45
57
|
provider = config['providers'].find { |p| p['name'] == selected }
|
|
46
58
|
|
|
47
59
|
# Fetch models and let user select
|
|
48
|
-
models = fetch_models_with_timeout(provider['base_url'], provider['api_key'])
|
|
60
|
+
models = fetch_models_with_timeout(provider['base_url'], provider['api_key'], preset_models(selected))
|
|
49
61
|
return if models.nil?
|
|
50
62
|
|
|
51
63
|
model = select_model(models, provider['model'])
|
|
52
64
|
|
|
53
65
|
# Prompt for diff length
|
|
54
|
-
diff_len = prompt_diff_len(provider['diff_len']
|
|
66
|
+
diff_len = prompt_diff_len(default_diff_len(selected, model, provider['diff_len']))
|
|
55
67
|
|
|
56
68
|
# Update config
|
|
57
69
|
ConfigManager.update_provider(selected, { 'model' => model, 'diff_len' => diff_len })
|
|
@@ -75,7 +87,8 @@ module CommitGpt
|
|
|
75
87
|
end
|
|
76
88
|
|
|
77
89
|
# Fetch models and let user select
|
|
78
|
-
models = fetch_models_with_timeout(provider_config['base_url'], provider_config['api_key']
|
|
90
|
+
models = fetch_models_with_timeout(provider_config['base_url'], provider_config['api_key'],
|
|
91
|
+
preset_models(provider_config['name']))
|
|
79
92
|
return if models.nil?
|
|
80
93
|
|
|
81
94
|
model = select_model(models, provider_config['model'])
|
|
@@ -148,14 +161,14 @@ module CommitGpt
|
|
|
148
161
|
return if api_key.nil? # User cancelled
|
|
149
162
|
|
|
150
163
|
# Fetch models with timeout
|
|
151
|
-
models = fetch_models_with_timeout(base_url, api_key)
|
|
164
|
+
models = fetch_models_with_timeout(base_url, api_key, preset_models(provider_name))
|
|
152
165
|
return if models.nil?
|
|
153
166
|
|
|
154
167
|
# Let user select model
|
|
155
168
|
model = select_model(models, existing_provider&.dig('model'))
|
|
156
169
|
|
|
157
170
|
# Prompt for diff length
|
|
158
|
-
diff_len = prompt_diff_len(existing_provider&.dig('diff_len')
|
|
171
|
+
diff_len = prompt_diff_len(default_diff_len(provider_name, model, existing_provider&.dig('diff_len')))
|
|
159
172
|
|
|
160
173
|
# Save configuration
|
|
161
174
|
ConfigManager.update_provider(
|
|
@@ -181,14 +194,14 @@ module CommitGpt
|
|
|
181
194
|
q.default 'http://localhost:8080/v1'
|
|
182
195
|
end
|
|
183
196
|
|
|
184
|
-
api_key = @prompt.mask('Enter your API key (optional):') { |q| q.echo false }
|
|
197
|
+
api_key = @prompt.mask('Enter your API key (optional):') { |q| q.echo false }.to_s
|
|
185
198
|
|
|
186
199
|
# Fetch models
|
|
187
200
|
models = fetch_models_with_timeout(base_url, api_key)
|
|
188
201
|
return if models.nil?
|
|
189
202
|
|
|
190
203
|
model = select_model(models)
|
|
191
|
-
diff_len = prompt_diff_len(
|
|
204
|
+
diff_len = prompt_diff_len(FALLBACK_DIFF_LEN)
|
|
192
205
|
|
|
193
206
|
# Add to presets dynamically (just for this session)
|
|
194
207
|
# Save to config
|
|
@@ -230,20 +243,19 @@ module CommitGpt
|
|
|
230
243
|
|
|
231
244
|
# Prompt for API key
|
|
232
245
|
def prompt_api_key(_provider_name, existing_key)
|
|
233
|
-
|
|
246
|
+
has_existing = existing_key && !existing_key.empty?
|
|
247
|
+
|
|
248
|
+
message = if has_existing
|
|
234
249
|
'Enter your API key (press Enter to keep existing):'
|
|
235
250
|
else
|
|
236
251
|
'Enter your API key:'
|
|
237
252
|
end
|
|
238
253
|
|
|
239
|
-
|
|
254
|
+
# mask returns nil, not '', when the input is empty
|
|
255
|
+
key = @prompt.mask(message) { |q| q.echo false }.to_s
|
|
240
256
|
|
|
241
257
|
# If user pressed Enter and there's an existing key, use it
|
|
242
|
-
|
|
243
|
-
existing_key
|
|
244
|
-
else
|
|
245
|
-
key
|
|
246
|
-
end
|
|
258
|
+
key.empty? && has_existing ? existing_key : key
|
|
247
259
|
end
|
|
248
260
|
|
|
249
261
|
# Ask how long to wait when fetching the model list, and persist it
|
|
@@ -262,12 +274,56 @@ module CommitGpt
|
|
|
262
274
|
seconds
|
|
263
275
|
end
|
|
264
276
|
|
|
265
|
-
#
|
|
266
|
-
def
|
|
277
|
+
# Built-in models a preset declares, as { model => context tokens }
|
|
278
|
+
def preset_model_table(provider_name)
|
|
279
|
+
models = PROVIDER_PRESETS.find { |p| p[:value] == provider_name }&.dig(:models)
|
|
280
|
+
models unless models.nil? || models.empty?
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Built-in model list for a preset, if it declares one
|
|
284
|
+
def preset_models(provider_name)
|
|
285
|
+
preset_model_table(provider_name)&.keys
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Context window of a built-in model, in tokens
|
|
289
|
+
def model_context_tokens(provider_name, model)
|
|
290
|
+
preset_model_table(provider_name)&.dig(model)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Default diff length for a model, derived from its context window when
|
|
294
|
+
# known, and reported so the number is not a mystery. Numbers are printed
|
|
295
|
+
# undelimited because they get typed straight into the next prompt.
|
|
296
|
+
def default_diff_len(provider_name, model, existing)
|
|
297
|
+
context_tokens = model_context_tokens(provider_name, model)
|
|
298
|
+
return existing || FALLBACK_DIFF_LEN if context_tokens.nil?
|
|
299
|
+
|
|
300
|
+
fits = context_tokens * DIFF_BYTES_PER_TOKEN
|
|
301
|
+
suggested = [fits, MAX_SUGGESTED_DIFF_LEN].min
|
|
302
|
+
capped = ' (capped; larger diffs use chunked mode)' if suggested < fits
|
|
303
|
+
|
|
304
|
+
puts "\n#{model} context window: #{context_tokens} tokens (~#{fits} bytes of diff)".gray
|
|
305
|
+
puts " Suggested max diff length: #{suggested} bytes#{capped}".gray
|
|
306
|
+
|
|
307
|
+
# Only a value the user actually chose outranks the suggestion
|
|
308
|
+
customized = existing && existing != FALLBACK_DIFF_LEN
|
|
309
|
+
customized ? existing : suggested
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Fetch models from provider with timeout.
|
|
313
|
+
# A preset that declares :models has no usable /models endpoint, so its
|
|
314
|
+
# built-in list is offered directly and no request is made.
|
|
315
|
+
def fetch_models_with_timeout(base_url, api_key, builtin_models = nil)
|
|
316
|
+
if builtin_models
|
|
317
|
+
puts 'Using the built-in model list (this provider does not expose /models).'.gray
|
|
318
|
+
return builtin_models
|
|
319
|
+
end
|
|
320
|
+
|
|
267
321
|
timeout = ConfigManager.get_model_fetch_timeout
|
|
268
322
|
puts 'Fetching available models...'.gray
|
|
269
323
|
|
|
270
324
|
models = nil
|
|
325
|
+
failure = nil
|
|
326
|
+
|
|
271
327
|
begin
|
|
272
328
|
Timeout.timeout(timeout) do
|
|
273
329
|
headers = {
|
|
@@ -279,28 +335,23 @@ module CommitGpt
|
|
|
279
335
|
response = HTTParty.get("#{base_url}/models", headers: headers)
|
|
280
336
|
|
|
281
337
|
if response.code == 200
|
|
282
|
-
models = response['data'] || []
|
|
283
|
-
models = models.map { |m| m['id'] }.compact.sort
|
|
338
|
+
models = (response['data'] || []).map { |m| m['id'] }.compact.sort
|
|
284
339
|
else
|
|
285
|
-
|
|
286
|
-
return nil
|
|
340
|
+
failure = "Failed to fetch models: HTTP #{response.code}"
|
|
287
341
|
end
|
|
288
342
|
end
|
|
289
343
|
rescue Timeout::Error
|
|
290
|
-
|
|
291
|
-
puts " Run 'aicm setup' again to raise the timeout.".gray
|
|
292
|
-
exit(0)
|
|
344
|
+
failure = "Connection timeout (#{timeout}s). Please check your network, base_url, and api_key."
|
|
293
345
|
rescue StandardError => e
|
|
294
|
-
|
|
295
|
-
exit(0)
|
|
346
|
+
failure = "Error fetching models: #{e.message}"
|
|
296
347
|
end
|
|
297
348
|
|
|
298
|
-
if models.nil? || models.empty?
|
|
299
|
-
|
|
300
|
-
exit(0)
|
|
301
|
-
end
|
|
349
|
+
failure ||= 'No models found. Please check your configuration.' if models.nil? || models.empty?
|
|
350
|
+
return models if failure.nil?
|
|
302
351
|
|
|
303
|
-
|
|
352
|
+
puts "✖ #{failure}".red
|
|
353
|
+
puts " Run 'aicm setup' again to raise the timeout.".gray if failure.start_with?('Connection timeout')
|
|
354
|
+
exit(0)
|
|
304
355
|
end
|
|
305
356
|
|
|
306
357
|
# Let user select a model
|
|
@@ -328,7 +379,7 @@ module CommitGpt
|
|
|
328
379
|
end
|
|
329
380
|
|
|
330
381
|
# Prompt for diff length
|
|
331
|
-
def prompt_diff_len(default =
|
|
382
|
+
def prompt_diff_len(default = FALLBACK_DIFF_LEN)
|
|
332
383
|
answer = @prompt.ask('Set the maximum diff length (Bytes) for generating commit message:') do |q|
|
|
333
384
|
q.default default.to_s
|
|
334
385
|
q.convert :int
|
data/lib/commitgpt/version.rb
CHANGED