completion-kit 0.12.0 → 0.12.1
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/app/assets/javascripts/completion_kit/application.js +32 -0
- data/app/controllers/completion_kit/provider_credentials_controller.rb +7 -2
- data/app/jobs/completion_kit/model_discovery_job.rb +2 -2
- data/app/models/completion_kit/provider_credential.rb +22 -12
- data/app/services/completion_kit/model_discovery_service.rb +16 -3
- data/app/views/completion_kit/provider_credentials/_models_card.html.erb +1 -1
- data/app/views/completion_kit/provider_credentials/statuses.turbo_stream.erb +4 -0
- data/config/routes.rb +1 -0
- data/lib/completion_kit/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a39761d576cee622378682d2b920ed0540bd4cbefbda033236c2108c1676d835
|
|
4
|
+
data.tar.gz: 7ad068f5c6a9a96513d47e689f5b0926e101aec4058e033076c9e6d87b31d003
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 80e6c44594f02b408911390576ea7ed9c377ef4ab4d7f068341b5177277a8baa6776e09986a4afedd430d723f84e700ca85962a504f949eb17a860d53cfeba82
|
|
7
|
+
data.tar.gz: 749da38fb1236140bc6f6cf45419d979500c671a1a8e92471c35aa79abe95fbb32a5d898286932cf4b82af0628e533b93a8ad6fc0b458c4322321efa98317c4d
|
|
@@ -5,6 +5,7 @@ document.addEventListener("turbo:load", function() {
|
|
|
5
5
|
});
|
|
6
6
|
ckTickRelativeTimes();
|
|
7
7
|
ckAutoFocusFirstError();
|
|
8
|
+
if (ckDiscoveringInDom()) ckStartDiscoveryPolling(0);
|
|
8
9
|
});
|
|
9
10
|
|
|
10
11
|
document.addEventListener("input", function(e) {
|
|
@@ -120,6 +121,37 @@ function ckRefreshModels() {
|
|
|
120
121
|
method: "POST",
|
|
121
122
|
headers: { "X-CSRF-Token": csrfToken }
|
|
122
123
|
});
|
|
124
|
+
ckStartDiscoveryPolling(8000);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var ckDiscoveryPollTimer = null;
|
|
128
|
+
var ckDiscoveryPollUntil = 0;
|
|
129
|
+
|
|
130
|
+
function ckDiscoveringInDom() {
|
|
131
|
+
return !!document.querySelector('[id^="discovery_status_"] .ck-discovery-bar:not(.ck-discovery-bar--failed):not(.ck-discovery-bar--completed)');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function ckPollDiscoveryOnce() {
|
|
135
|
+
fetch("/completion_kit/provider_credentials/statuses", { headers: { "Accept": "text/vnd.turbo-stream.html" } })
|
|
136
|
+
.then(function(r) { return r.ok ? r.text() : null; })
|
|
137
|
+
.then(function(html) {
|
|
138
|
+
if (html && window.Turbo && window.Turbo.renderStreamMessage) window.Turbo.renderStreamMessage(html);
|
|
139
|
+
})
|
|
140
|
+
.catch(function() {});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function ckStartDiscoveryPolling(graceMs) {
|
|
144
|
+
if (!document.querySelector('[id^="discovery_status_"]')) return;
|
|
145
|
+
ckDiscoveryPollUntil = Date.now() + (graceMs || 0);
|
|
146
|
+
if (ckDiscoveryPollTimer) return;
|
|
147
|
+
ckPollDiscoveryOnce();
|
|
148
|
+
ckDiscoveryPollTimer = setInterval(function() {
|
|
149
|
+
ckPollDiscoveryOnce();
|
|
150
|
+
if (!ckDiscoveringInDom() && Date.now() > ckDiscoveryPollUntil) {
|
|
151
|
+
clearInterval(ckDiscoveryPollTimer);
|
|
152
|
+
ckDiscoveryPollTimer = null;
|
|
153
|
+
}
|
|
154
|
+
}, 1500);
|
|
123
155
|
}
|
|
124
156
|
|
|
125
157
|
function ckUpdateRefreshProgress() {
|
|
@@ -6,6 +6,11 @@ module CompletionKit
|
|
|
6
6
|
@provider_credentials = ProviderCredential.order(:provider)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def statuses
|
|
10
|
+
@provider_credentials = ProviderCredential.order(:provider)
|
|
11
|
+
respond_to { |format| format.turbo_stream }
|
|
12
|
+
end
|
|
13
|
+
|
|
9
14
|
def new
|
|
10
15
|
@provider_credential = ProviderCredential.new(provider: params[:provider])
|
|
11
16
|
end
|
|
@@ -35,7 +40,7 @@ module CompletionKit
|
|
|
35
40
|
@provider_credential.update_columns(discovery_status: "discovering", discovery_current: 0, discovery_total: 0)
|
|
36
41
|
@provider_credential.reload
|
|
37
42
|
@provider_credential.broadcast_discovery_progress
|
|
38
|
-
ModelDiscoveryJob.perform_later(@provider_credential.id)
|
|
43
|
+
ModelDiscoveryJob.perform_later(@provider_credential.id, force: true)
|
|
39
44
|
head :ok
|
|
40
45
|
end
|
|
41
46
|
|
|
@@ -44,7 +49,7 @@ module CompletionKit
|
|
|
44
49
|
cred.update_columns(discovery_status: "discovering", discovery_current: 0, discovery_total: 0)
|
|
45
50
|
cred.reload
|
|
46
51
|
cred.broadcast_discovery_progress
|
|
47
|
-
ModelDiscoveryJob.perform_later(cred.id)
|
|
52
|
+
ModelDiscoveryJob.perform_later(cred.id, force: true)
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
head :ok
|
|
@@ -24,7 +24,7 @@ module CompletionKit
|
|
|
24
24
|
credential.broadcast_discovery_progress
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def perform(provider_credential_id)
|
|
27
|
+
def perform(provider_credential_id, force: false)
|
|
28
28
|
credential = ProviderCredential.find_by(id: provider_credential_id)
|
|
29
29
|
return unless credential
|
|
30
30
|
|
|
@@ -38,7 +38,7 @@ module CompletionKit
|
|
|
38
38
|
credential.broadcast_discovery_progress
|
|
39
39
|
|
|
40
40
|
service = ModelDiscoveryService.new(config: credential.config_hash)
|
|
41
|
-
service.refresh! do |current, total|
|
|
41
|
+
service.refresh!(force: force) do |current, total|
|
|
42
42
|
credential.update_columns(discovery_current: current, discovery_total: total)
|
|
43
43
|
credential.reload
|
|
44
44
|
credential.broadcast_discovery_progress
|
|
@@ -79,11 +79,13 @@ module CompletionKit
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def broadcast_discovery_progress
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
safely_broadcast do
|
|
83
|
+
broadcast_replace_to(
|
|
84
|
+
"completion_kit_provider_#{id}",
|
|
85
|
+
target: "discovery_status_#{id}",
|
|
86
|
+
html: render_partial("completion_kit/provider_credentials/discovery_status", provider_credential: self)
|
|
87
|
+
)
|
|
88
|
+
end
|
|
87
89
|
broadcast_provider_models
|
|
88
90
|
end
|
|
89
91
|
|
|
@@ -93,13 +95,15 @@ module CompletionKit
|
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
def broadcast_provider_models
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
safely_broadcast do
|
|
99
|
+
Turbo::StreamsChannel.broadcast_action_to(
|
|
100
|
+
"completion_kit_provider_#{id}",
|
|
101
|
+
action: "replace",
|
|
102
|
+
target: "provider_models_#{id}",
|
|
103
|
+
method: "morph",
|
|
104
|
+
html: render_partial("completion_kit/provider_credentials/models_card", provider_credential: self)
|
|
105
|
+
)
|
|
106
|
+
end
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
private
|
|
@@ -133,6 +137,12 @@ module CompletionKit
|
|
|
133
137
|
CompletionKit::ApplicationController.render(partial: partial, locals: locals)
|
|
134
138
|
end
|
|
135
139
|
|
|
140
|
+
def safely_broadcast
|
|
141
|
+
yield
|
|
142
|
+
rescue StandardError => e
|
|
143
|
+
Rails.logger.error("[CompletionKit] discovery broadcast render failed: #{e.class}: #{e.message}")
|
|
144
|
+
end
|
|
145
|
+
|
|
136
146
|
def api_endpoint_not_internal
|
|
137
147
|
return if api_endpoint.blank?
|
|
138
148
|
|
|
@@ -12,7 +12,7 @@ module CompletionKit
|
|
|
12
12
|
@api_endpoint = config[:api_endpoint]
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def refresh!(&on_progress)
|
|
15
|
+
def refresh!(force: false, &on_progress)
|
|
16
16
|
discovered = fetch_models
|
|
17
17
|
reconcile(discovered)
|
|
18
18
|
# OpenRouter publishes capability metadata (output modalities, etc.), so we
|
|
@@ -20,6 +20,7 @@ module CompletionKit
|
|
|
20
20
|
# Judging stays unknown ("?") until a real run proves it.
|
|
21
21
|
return if @provider == "openrouter"
|
|
22
22
|
|
|
23
|
+
reset_failed_generation if force
|
|
23
24
|
probe_new_models(&on_progress)
|
|
24
25
|
end
|
|
25
26
|
|
|
@@ -181,6 +182,11 @@ module CompletionKit
|
|
|
181
182
|
end
|
|
182
183
|
end
|
|
183
184
|
|
|
185
|
+
def reset_failed_generation
|
|
186
|
+
Model.where(provider: @provider, status: %w[active failed], supports_generation: false)
|
|
187
|
+
.update_all(supports_generation: nil, generation_error: nil)
|
|
188
|
+
end
|
|
189
|
+
|
|
184
190
|
def probe_new_models(&on_progress)
|
|
185
191
|
candidates = Model.where(provider: @provider, status: %w[active failed])
|
|
186
192
|
.where("supports_generation IS NULL OR supports_judging IS NULL OR (generation_error IS NOT NULL AND #{retryable_error_sql('generation_error')}) OR (judging_error IS NOT NULL AND #{retryable_error_sql('judging_error')})")
|
|
@@ -220,7 +226,7 @@ module CompletionKit
|
|
|
220
226
|
|
|
221
227
|
def probe_generation(model)
|
|
222
228
|
probe_input = "Reply with exactly this token and nothing else: PING-OK"
|
|
223
|
-
response = send_probe(model.model_id, probe_input,
|
|
229
|
+
response = send_probe(model.model_id, probe_input, probe_max_output_tokens)
|
|
224
230
|
if response.success?
|
|
225
231
|
text = extract_text(response).to_s
|
|
226
232
|
if text.blank?
|
|
@@ -251,7 +257,7 @@ module CompletionKit
|
|
|
251
257
|
AI output to evaluate: The sky is blue.
|
|
252
258
|
PROMPT
|
|
253
259
|
|
|
254
|
-
response = send_probe(model.model_id, judge_input,
|
|
260
|
+
response = send_probe(model.model_id, judge_input, probe_max_output_tokens)
|
|
255
261
|
if response.success?
|
|
256
262
|
text = extract_text(response).to_s
|
|
257
263
|
if text.match?(/Score:\s*\d/i)
|
|
@@ -269,6 +275,13 @@ module CompletionKit
|
|
|
269
275
|
model.judging_error = e.message
|
|
270
276
|
end
|
|
271
277
|
|
|
278
|
+
OPENAI_REASONING_PROBE_BUDGET = 65_536
|
|
279
|
+
CHAT_PROBE_BUDGET = 1_024
|
|
280
|
+
|
|
281
|
+
def probe_max_output_tokens
|
|
282
|
+
@provider == "openai" ? OPENAI_REASONING_PROBE_BUDGET : CHAT_PROBE_BUDGET
|
|
283
|
+
end
|
|
284
|
+
|
|
272
285
|
def send_probe(model_id, input, max_tokens)
|
|
273
286
|
case @provider
|
|
274
287
|
when "openai" then openai_probe(model_id, input, max_tokens)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<% if provider_credential.discovery_status == "completed" %>
|
|
18
18
|
<span class="ck-model-list__summary-stamp">updated <time data-relative-time datetime="<%= provider_credential.updated_at.utc.iso8601 %>"><%= time_ago_in_words(provider_credential.updated_at) %> ago</time></span>
|
|
19
19
|
<% end %>
|
|
20
|
-
<button type="button" class="ck-icon-btn ck-model-list__refresh<%= ' ck-icon-btn--spinning' if discovering %>" title="Refresh models" aria-label="Refresh available models" <%= 'disabled' if discovering %> onclick="event.preventDefault();event.stopPropagation();fetch('<%= refresh_provider_credential_path(provider_credential) %>', {method:'POST',headers:{'X-CSRF-Token':document.querySelector('meta[name=csrf-token]').content}})">
|
|
20
|
+
<button type="button" class="ck-icon-btn ck-model-list__refresh<%= ' ck-icon-btn--spinning' if discovering %>" title="Refresh models" aria-label="Refresh available models" <%= 'disabled' if discovering %> onclick="event.preventDefault();event.stopPropagation();fetch('<%= refresh_provider_credential_path(provider_credential) %>', {method:'POST',headers:{'X-CSRF-Token':document.querySelector('meta[name=csrf-token]').content}});if(window.ckStartDiscoveryPolling)ckStartDiscoveryPolling(8000)">
|
|
21
21
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="13" height="13" aria-hidden="true"><path fill-rule="evenodd" d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.681.75.75 0 0 1-1.264-.808 6 6 0 0 1 9.44-.908l.84.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44.908l-.84-.84v1.68a.75.75 0 0 1-1.5 0V9.567a.75.75 0 0 1 .75-.75h3.182a.75.75 0 0 1 0 1.5h-1.37l.84.841a4.5 4.5 0 0 0 7.08-.681.75.75 0 0 1 1.024-.274Z" clip-rule="evenodd"/></svg>
|
|
22
22
|
</button>
|
|
23
23
|
</span>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<% @provider_credentials.each do |pc| %>
|
|
2
|
+
<%= turbo_stream.replace "discovery_status_#{pc.id}" do %><%= render "discovery_status", provider_credential: pc %><% end %>
|
|
3
|
+
<%= turbo_stream.replace "provider_models_#{pc.id}" do %><%= render "models_card", provider_credential: pc %><% end %>
|
|
4
|
+
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -53,6 +53,7 @@ CompletionKit::Engine.routes.draw do
|
|
|
53
53
|
|
|
54
54
|
resources :provider_credentials, only: [:index, :new, :create, :edit, :update] do
|
|
55
55
|
post :refresh, on: :member
|
|
56
|
+
get :statuses, on: :collection
|
|
56
57
|
end
|
|
57
58
|
post "refresh_models", to: "provider_credentials#refresh_all", as: :refresh_models
|
|
58
59
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: completion-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.12.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -379,6 +379,7 @@ files:
|
|
|
379
379
|
- app/views/completion_kit/provider_credentials/edit.html.erb
|
|
380
380
|
- app/views/completion_kit/provider_credentials/index.html.erb
|
|
381
381
|
- app/views/completion_kit/provider_credentials/new.html.erb
|
|
382
|
+
- app/views/completion_kit/provider_credentials/statuses.turbo_stream.erb
|
|
382
383
|
- app/views/completion_kit/responses/show.html.erb
|
|
383
384
|
- app/views/completion_kit/runs/_actions.html.erb
|
|
384
385
|
- app/views/completion_kit/runs/_form.html.erb
|