completion-kit 0.28.45 → 0.28.46
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/stylesheets/completion_kit/application.css +14 -3
- data/app/controllers/completion_kit/onboarding_controller.rb +1 -1
- data/app/controllers/completion_kit/runs_controller.rb +14 -5
- data/app/helpers/completion_kit/application_helper.rb +11 -7
- data/app/models/completion_kit/prompt.rb +1 -1
- data/app/models/completion_kit/run.rb +17 -10
- data/app/services/completion_kit/mcp_tools/metric_versions.rb +1 -1
- data/app/services/completion_kit/mcp_tools/prompts.rb +1 -1
- data/app/services/completion_kit/prompt_improvement_service.rb +1 -1
- data/app/views/completion_kit/agreements/_buttons.html.erb +1 -1
- data/app/views/completion_kit/api_reference/_body.html.erb +2 -2
- data/app/views/completion_kit/metric_groups/_form.html.erb +13 -2
- data/app/views/completion_kit/metric_groups/edit.html.erb +1 -1
- data/app/views/completion_kit/onboarding/show.html.erb +2 -2
- data/app/views/completion_kit/prompts/_form.html.erb +2 -1
- data/app/views/completion_kit/provider_credentials/_models_card.html.erb +2 -2
- data/app/views/completion_kit/runs/_form.html.erb +14 -10
- data/app/views/completion_kit/runs/_response_row.html.erb +2 -1
- data/app/views/completion_kit/runs/edit.html.erb +1 -1
- data/app/views/completion_kit/runs/new.html.erb +1 -1
- data/app/views/layouts/completion_kit/application.html.erb +1 -1
- data/db/migrate/20261013000001_drop_completion_kit_mcp_sessions.rb +16 -0
- data/lib/completion_kit/errors.rb +2 -2
- 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: f620811d2032fa4aad51754dcee5aa92657cc193c890587845cb0c185060ef18
|
|
4
|
+
data.tar.gz: ea6426877ef769569438a48f3f2cc7848569e02c395db53e629898cda4c01545
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c6f1a8afbf36d89f1dbd42919fc870732e6f5aeb14164303e62d68644489837f845fc50b19d5ea9735e55b5e1876e743f67ced221e9d3d720f08726d24b4f4f8
|
|
7
|
+
data.tar.gz: 4cf7b53081569d22ddb471e6b40418baeba32e3fcd11e284df14cd582810603f7e132165603e169cb19f3655fefc7b5f69a9fe437ee687be181d7dc4ced95447
|
|
@@ -2311,6 +2311,15 @@ label.ck-checkbox[hidden] {
|
|
|
2311
2311
|
flex: 1;
|
|
2312
2312
|
}
|
|
2313
2313
|
|
|
2314
|
+
.ck-select-with-action > .field_with_errors {
|
|
2315
|
+
flex: 1;
|
|
2316
|
+
min-width: 0;
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
.ck-select-with-action > .field_with_errors > .ck-input {
|
|
2320
|
+
width: 100%;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2314
2323
|
.ck-icon-btn:hover {
|
|
2315
2324
|
background: rgba(255, 255, 255, 0.05);
|
|
2316
2325
|
border-color: var(--ck-line-strong);
|
|
@@ -5175,11 +5184,13 @@ a.tag-mark {
|
|
|
5175
5184
|
}
|
|
5176
5185
|
|
|
5177
5186
|
|
|
5178
|
-
.ck-input--error
|
|
5187
|
+
.ck-input--error,
|
|
5188
|
+
.ck-input[aria-invalid="true"] {
|
|
5179
5189
|
border-color: var(--ck-danger);
|
|
5180
|
-
background: rgba(248, 113, 113, 0.05);
|
|
5190
|
+
background-color: rgba(248, 113, 113, 0.05);
|
|
5181
5191
|
}
|
|
5182
|
-
.ck-input--error:focus
|
|
5192
|
+
.ck-input--error:focus,
|
|
5193
|
+
.ck-input[aria-invalid="true"]:focus {
|
|
5183
5194
|
box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.15);
|
|
5184
5195
|
border-color: var(--ck-danger);
|
|
5185
5196
|
}
|
|
@@ -21,7 +21,7 @@ module CompletionKit
|
|
|
21
21
|
|
|
22
22
|
def sample_data
|
|
23
23
|
Onboarding::SampleData.install!
|
|
24
|
-
redirect_to onboarding_path, notice: "Loaded a sample dataset and prompt
|
|
24
|
+
redirect_to onboarding_path, notice: "Loaded a sample dataset and prompt. Edit or delete them whenever."
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -40,6 +40,7 @@ module CompletionKit
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def create
|
|
43
|
+
@judge_only = judge_only_chosen?
|
|
43
44
|
@run = Run.new(run_attributes)
|
|
44
45
|
if @run.save
|
|
45
46
|
redirect_to run_path(@run), notice: "Run was successfully created."
|
|
@@ -50,10 +51,11 @@ module CompletionKit
|
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def update
|
|
54
|
+
@judge_only = judge_only_chosen?
|
|
53
55
|
if @run.responses.any? && run_generation_changed?
|
|
54
56
|
attrs = run_attributes.to_h
|
|
55
57
|
attrs.delete("name") if attrs["name"].to_s == @run.name.to_s
|
|
56
|
-
new_run = Run.new(attrs.merge(status: "pending"))
|
|
58
|
+
new_run = Run.new(attrs.merge(status: "pending", cloned_from_judge_model: @run.judge_model))
|
|
57
59
|
if new_run.save
|
|
58
60
|
redirect_to run_path(new_run), notice: "Saved as a new run. The previous run and its results are preserved."
|
|
59
61
|
else
|
|
@@ -224,11 +226,16 @@ module CompletionKit
|
|
|
224
226
|
end
|
|
225
227
|
|
|
226
228
|
def run_params
|
|
227
|
-
params.require(:run).permit(:name, :prompt_id, :dataset_id, :judge_model, :temperature, :max_tokens, :judge_temperature, :output_column, :expected_column, metric_ids: [], tag_names: [])
|
|
229
|
+
params.require(:run).permit(:name, :prompt_id, :dataset_id, :judge_model, :temperature, :max_tokens, :judge_temperature, :output_column, :expected_column, :judge_only, metric_ids: [], tag_names: [])
|
|
228
230
|
end
|
|
229
231
|
|
|
230
232
|
def run_attributes
|
|
231
|
-
run_params.except(:metric_ids).merge(selected_metric_ids: run_params[:metric_ids])
|
|
233
|
+
attributes = run_params.except(:metric_ids, :judge_only).merge(selected_metric_ids: run_params[:metric_ids])
|
|
234
|
+
judge_only_chosen? ? attributes.merge(prompt_id: nil) : attributes
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def judge_only_chosen?
|
|
238
|
+
run_params[:judge_only] == "1"
|
|
232
239
|
end
|
|
233
240
|
|
|
234
241
|
# Editing a run that already has results forks a new run — but only when a
|
|
@@ -237,9 +244,10 @@ module CompletionKit
|
|
|
237
244
|
GENERATION_RUN_FIELDS = %i[prompt_id dataset_id judge_model temperature max_tokens judge_temperature output_column expected_column].freeze
|
|
238
245
|
|
|
239
246
|
def run_generation_changed?
|
|
247
|
+
attributes = run_attributes
|
|
240
248
|
GENERATION_RUN_FIELDS.each do |field|
|
|
241
|
-
next unless
|
|
242
|
-
return true if normalize_run_field(field,
|
|
249
|
+
next unless attributes.key?(field)
|
|
250
|
+
return true if normalize_run_field(field, attributes[field]) != normalize_run_field(field, @run.public_send(field))
|
|
243
251
|
end
|
|
244
252
|
return false unless params[:run].key?(:metric_ids)
|
|
245
253
|
Array(params[:run][:metric_ids]).map(&:to_i).reject(&:zero?).sort != @run.metric_ids.sort
|
|
@@ -247,6 +255,7 @@ module CompletionKit
|
|
|
247
255
|
|
|
248
256
|
def normalize_run_field(field, value)
|
|
249
257
|
s = value.to_s.strip
|
|
258
|
+
return s.presence || "actual_output" if field == :output_column
|
|
250
259
|
return nil if s.empty?
|
|
251
260
|
case field
|
|
252
261
|
when :temperature, :judge_temperature then s.to_f
|
|
@@ -151,18 +151,22 @@ module CompletionKit
|
|
|
151
151
|
model[:name]
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
def ck_grouped_models(models, selected = nil)
|
|
154
|
+
def ck_grouped_models(models, selected = nil, rejected: false)
|
|
155
|
+
kept = nil
|
|
155
156
|
if selected.present? && models.none? { |m| m[:id] == selected }
|
|
156
|
-
retired = CompletionKit::Model.find_by(model_id: selected)
|
|
157
|
+
retired = CompletionKit::Model.find_by(model_id: selected) unless rejected
|
|
157
158
|
if retired
|
|
158
159
|
models = models + [{ id: retired.model_id, name: "#{retired.display_name || retired.model_id} (retired)", provider: retired.provider }]
|
|
160
|
+
else
|
|
161
|
+
kept = [["#{selected} (unavailable)", selected]]
|
|
159
162
|
end
|
|
160
163
|
end
|
|
161
164
|
|
|
162
165
|
groups = models.group_by { |m| ck_model_optgroup_label(m) }
|
|
163
166
|
ordered_keys = groups.keys.sort_by { |label| ck_model_optgroup_sort_key(label) }
|
|
164
167
|
grouped = ordered_keys.map { |label| [label, groups[label].map { |m| [ck_model_option_label(m), m[:id]] }] }
|
|
165
|
-
grouped_options_for_select(grouped, selected)
|
|
168
|
+
options = grouped_options_for_select(grouped, selected)
|
|
169
|
+
kept ? options_for_select(kept, selected) + options : options
|
|
166
170
|
end
|
|
167
171
|
|
|
168
172
|
# Optgroup label for the model select — mirrors the provider models table:
|
|
@@ -170,15 +174,15 @@ module CompletionKit
|
|
|
170
174
|
# else is a single group.
|
|
171
175
|
def ck_model_optgroup_label(model)
|
|
172
176
|
case model[:provider]
|
|
173
|
-
when "openrouter" then "OpenRouter
|
|
174
|
-
when "openai" then "OpenAI
|
|
177
|
+
when "openrouter" then "OpenRouter · #{model[:id].to_s.split("/", 2).first.delete_prefix("~")}"
|
|
178
|
+
when "openai" then "OpenAI · #{ck_openai_model_family(model[:id])}"
|
|
175
179
|
else ck_provider_label(model[:provider])
|
|
176
180
|
end
|
|
177
181
|
end
|
|
178
182
|
|
|
179
183
|
def ck_model_optgroup_sort_key(label)
|
|
180
|
-
if label.start_with?("OpenAI
|
|
181
|
-
[0, OPENAI_MODEL_FAMILY_ORDER.index(label.delete_prefix("OpenAI
|
|
184
|
+
if label.start_with?("OpenAI · ")
|
|
185
|
+
[0, OPENAI_MODEL_FAMILY_ORDER.index(label.delete_prefix("OpenAI · ")), label]
|
|
182
186
|
elsif label.start_with?("OpenRouter")
|
|
183
187
|
[2, 0, label]
|
|
184
188
|
else
|
|
@@ -333,13 +333,10 @@ module CompletionKit
|
|
|
333
333
|
unless judge_only_input_data_checks? || (dataset && dataset.headers.include?(column))
|
|
334
334
|
return fail_with_summary!("Dataset has no \"#{column}\" column")
|
|
335
335
|
end
|
|
336
|
-
else
|
|
337
|
-
generation_error = generation_client_error
|
|
338
|
-
return fail_with_summary!(generation_error) if generation_error
|
|
339
336
|
end
|
|
340
337
|
|
|
341
|
-
|
|
342
|
-
return fail_with_summary!(
|
|
338
|
+
model_error = model_start_error
|
|
339
|
+
return fail_with_summary!(model_error) if model_error
|
|
343
340
|
|
|
344
341
|
begin
|
|
345
342
|
transaction do
|
|
@@ -490,7 +487,8 @@ module CompletionKit
|
|
|
490
487
|
end
|
|
491
488
|
|
|
492
489
|
def rerun!
|
|
493
|
-
|
|
490
|
+
ensure_models_ready!
|
|
491
|
+
|
|
494
492
|
Run.create!(
|
|
495
493
|
prompt_id: prompt_id,
|
|
496
494
|
dataset_id: dataset_id,
|
|
@@ -712,11 +710,20 @@ module CompletionKit
|
|
|
712
710
|
%(No configured provider serves judge model "#{judge_model}")
|
|
713
711
|
end
|
|
714
712
|
|
|
715
|
-
def
|
|
716
|
-
|
|
713
|
+
def model_start_error
|
|
714
|
+
(generation_client_error unless judge_only?) || judge_model_error
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def ensure_models_ready!
|
|
718
|
+
unless judge_only?
|
|
719
|
+
raise UnknownModelError, prompt.llm_model unless ApiConfig.provider_for_model(prompt.llm_model)
|
|
720
|
+
|
|
721
|
+
client_error = generation_client_error
|
|
722
|
+
raise ConfigurationError, client_error if client_error
|
|
723
|
+
end
|
|
717
724
|
|
|
718
725
|
judge_error = judge_model_error
|
|
719
|
-
raise
|
|
726
|
+
raise UnknownModelError.new(judge_model, judge_error) if judge_error
|
|
720
727
|
end
|
|
721
728
|
|
|
722
729
|
def judge_model_newly_selected?
|
|
@@ -775,7 +782,7 @@ module CompletionKit
|
|
|
775
782
|
|
|
776
783
|
if prompt.present?
|
|
777
784
|
count = Run.where(prompt_id: prompt_id).count + 1
|
|
778
|
-
self.name = "#{prompt.name}
|
|
785
|
+
self.name = "#{prompt.name} · v#{prompt.version_number} ##{count}"
|
|
779
786
|
elsif dataset.present?
|
|
780
787
|
count = Run.where(prompt_id: nil, dataset_id: dataset.id).count + 1
|
|
781
788
|
self.name = "#{dataset.name} scoring ##{count}"
|
|
@@ -27,7 +27,7 @@ module CompletionKit
|
|
|
27
27
|
handler: :publish
|
|
28
28
|
},
|
|
29
29
|
"metric_versions_dismiss" => {
|
|
30
|
-
description: "Destroy a draft MetricVersion (use for either source: 'edit' or source: 'suggestion'). Published versions are refused
|
|
30
|
+
description: "Destroy a draft MetricVersion (use for either source: 'edit' or source: 'suggestion'). Published versions are refused. To demote a published version, publish a different one as current instead.",
|
|
31
31
|
inputSchema: {
|
|
32
32
|
type: "object",
|
|
33
33
|
properties: {
|
|
@@ -28,7 +28,7 @@ module CompletionKit
|
|
|
28
28
|
handler: :create
|
|
29
29
|
},
|
|
30
30
|
"prompts_update" => {
|
|
31
|
-
description: "Update a prompt. If the prompt already has runs, this creates a new DRAFT version (current=false) rather than editing in place or publishing
|
|
31
|
+
description: "Update a prompt. If the prompt already has runs, this creates a new DRAFT version (current=false) rather than editing in place or publishing. Promote it with prompts_publish, so an agent's edits don't go live without a gate. If it has no runs, it is updated in place.",
|
|
32
32
|
inputSchema: {
|
|
33
33
|
type: "object",
|
|
34
34
|
properties: {
|
|
@@ -40,7 +40,7 @@ module CompletionKit
|
|
|
40
40
|
if review.check?
|
|
41
41
|
sections << " #{review.metric_name}: #{review.passed ? "PASS" : "FAIL"}"
|
|
42
42
|
else
|
|
43
|
-
sections << " #{review.metric_name}: #{review.ai_score}/5
|
|
43
|
+
sections << " #{review.metric_name}: #{review.ai_score}/5. #{review.ai_feedback}"
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
sections << ""
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<% verdict_icons = { "agree" => "hand-thumb-up", "disagree" => "hand-thumb-down", "borderline" => "scale" } %>
|
|
22
22
|
<% verdict_hints = {
|
|
23
23
|
"agree" => "The score looks right.",
|
|
24
|
-
"disagree" => "The score is wrong
|
|
24
|
+
"disagree" => "The score is wrong. You'll pick the right one.",
|
|
25
25
|
"borderline" => "The rubric is unclear here; either score could be defensible."
|
|
26
26
|
} %>
|
|
27
27
|
<% CompletionKit::Agreement::VERDICTS.each do |verdict| %>
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
</div>
|
|
197
197
|
<%= render "completion_kit/api_reference/resource_list", title: "Your responses",
|
|
198
198
|
items: recent_runs.first(1).map { |r|
|
|
199
|
-
{ name: "#{r.name}
|
|
199
|
+
{ name: "#{r.name} · responses",
|
|
200
200
|
url: "#{base_url}/api/v1/runs/#{r.id}/responses", dom_id: "responses_ep_#{r.id}" }
|
|
201
201
|
} %>
|
|
202
202
|
</div>
|
|
@@ -329,7 +329,7 @@
|
|
|
329
329
|
</div>
|
|
330
330
|
<div class="ck-api-endpoint">
|
|
331
331
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses/:response_id/metrics/:metric_id/agreements</p>
|
|
332
|
-
<p class="ck-meta-copy">List agreements scoped to a specific run, response, and metric
|
|
332
|
+
<p class="ck-meta-copy">List agreements scoped to a specific run, response, and metric. It is the nested equivalent of the top-level index filtered by those three ids.</p>
|
|
333
333
|
</div>
|
|
334
334
|
<div class="ck-api-endpoint">
|
|
335
335
|
<p class="ck-api-method"><span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/agreements/:id</p>
|
|
@@ -2,10 +2,21 @@
|
|
|
2
2
|
<% metric_group_delete_id = "ck_delete_metric_group_#{metric_group.id}" %>
|
|
3
3
|
<% members_n = metric_group.metrics.count %>
|
|
4
4
|
<% metric_group_delete_confirm = members_n.zero? ?
|
|
5
|
-
"Delete \"#{metric_group.
|
|
6
|
-
"Delete \"#{metric_group.
|
|
5
|
+
"Delete \"#{metric_group.name_was}\"? It has no members." :
|
|
6
|
+
"Delete \"#{metric_group.name_was}\"? Removes this grouping. #{pluralize(members_n, 'member metric')} #{members_n == 1 ? 'is' : 'are'} kept." %>
|
|
7
7
|
<% end %>
|
|
8
8
|
<%= form_with(model: metric_group, url: metric_group.persisted? ? metric_group_path(metric_group) : metric_groups_path, local: true) do |form| %>
|
|
9
|
+
<% if metric_group.errors.any? %>
|
|
10
|
+
<div class="ck-flash ck-flash--alert" role="alert">
|
|
11
|
+
<p class="ck-flash__title"><%= pluralize(metric_group.errors.count, "problem") %> prevented this metric group from being saved.</p>
|
|
12
|
+
<ul class="ck-error-list">
|
|
13
|
+
<% metric_group.errors.full_messages.each do |message| %>
|
|
14
|
+
<li><%= message %></li>
|
|
15
|
+
<% end %>
|
|
16
|
+
</ul>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
9
20
|
<div class="ck-card ck-form-card">
|
|
10
21
|
<div class="ck-field">
|
|
11
22
|
<%= form.label :name, "Metric group name", class: "ck-label" %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<% content_for :ck_title, "Edit metric group" %>
|
|
2
2
|
<ol class="ck-breadcrumb">
|
|
3
3
|
<li><%= link_to "Metrics", metrics_path %></li>
|
|
4
|
-
<li><%= link_to @metric_group.
|
|
4
|
+
<li><%= link_to @metric_group.name_was, metric_group_path(@metric_group) %></li>
|
|
5
5
|
<li>Edit</li>
|
|
6
6
|
</ol>
|
|
7
7
|
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
|
|
73
73
|
<% if @checklist.sample_loadable? %>
|
|
74
74
|
<div class="ck-launch__sample">
|
|
75
|
-
<p class="ck-launch__sample-copy">Just exploring? Drop in a sample dataset and prompt to poke around
|
|
75
|
+
<p class="ck-launch__sample-copy">Just exploring? Drop in a sample dataset and prompt to poke around. They're labelled <code>Sample:</code>, and you can edit or delete them whenever.</p>
|
|
76
76
|
<%= button_to onboarding_sample_data_path, method: :post, class: "ck-launch__sample-cta", form: { style: "display:inline" } do %>
|
|
77
77
|
Load sample data <span aria-hidden="true">→</span>
|
|
78
78
|
<% end %>
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
|
|
82
82
|
<div class="ck-launch__panel-footer">
|
|
83
83
|
<%= button_to dismiss_onboarding_path, method: :post, class: "ck-launch__dismiss", form: { style: "display:inline" } do %>
|
|
84
|
-
Skip setup
|
|
84
|
+
Skip setup and go to the app <span aria-hidden="true">→</span>
|
|
85
85
|
<% end %>
|
|
86
86
|
<p class="ck-launch__dismiss-hint">You can come back to this from <strong>Settings → Getting started</strong> any time.</p>
|
|
87
87
|
</div>
|
|
@@ -47,13 +47,14 @@
|
|
|
47
47
|
<% available = CompletionKit::ApiConfig.available_models(scope: :generation) %>
|
|
48
48
|
<% if available.any? %>
|
|
49
49
|
<div class="ck-select-with-action">
|
|
50
|
-
<%= form.select :llm_model, ck_grouped_models(available, prompt.llm_model), { include_blank: "Select a model" }, { class: "ck-input", id: "prompt_llm_model" } %>
|
|
50
|
+
<%= form.select :llm_model, ck_grouped_models(available, prompt.llm_model, rejected: prompt.errors[:llm_model].any?), { include_blank: "Select a model" }, { class: "ck-input", id: "prompt_llm_model", **ck_field_aria(form, :llm_model) } %>
|
|
51
51
|
<% ck_refreshing = CompletionKit::ProviderCredential.discovery_in_progress? %>
|
|
52
52
|
<button type="button" class="ck-icon-btn<%= ' ck-icon-btn--spinning' if ck_refreshing %>" title="Refresh models" <%= 'disabled' if ck_refreshing %> onclick="ckRefreshModels()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="16" height="16"><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></button>
|
|
53
53
|
</div>
|
|
54
54
|
<% else %>
|
|
55
55
|
<p class="ck-meta-copy">No models available. <%= link_to "Add a provider", provider_credentials_path, class: "ck-link" %> or click refresh after configuring a provider.</p>
|
|
56
56
|
<% end %>
|
|
57
|
+
<%= ck_field_error(form, :llm_model) %>
|
|
57
58
|
<div hidden data-refresh-progress-carriers data-ck-refresh-url="<%= refresh_models_path %>" data-ck-statuses-url="<%= statuses_provider_credentials_path %>">
|
|
58
59
|
<% CompletionKit::ProviderCredential.find_each do |pc| %>
|
|
59
60
|
<%= turbo_stream_from "completion_kit_provider_#{pc.id}" %>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
Gen<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup ck-info-popup--right">Generation models produce the responses your prompts ask for. Pick one when creating a prompt.</span>
|
|
36
36
|
</th>
|
|
37
37
|
<th scope="col" class="ck-model-table__cap">
|
|
38
|
-
Judge<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup ck-info-popup--right">Judge models score generated responses against your metrics. Pick one when configuring a run. A <strong>?</strong> means we haven't confirmed this model works as a judge
|
|
38
|
+
Judge<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup ck-info-popup--right">Judge models score generated responses against your metrics. Pick one when configuring a run. A <strong>?</strong> means we haven't confirmed this model works as a judge. It's still selectable, and a successful run promotes it to ✓.</span>
|
|
39
39
|
</th>
|
|
40
40
|
</tr>
|
|
41
41
|
</thead>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<% if m.supports_judging %>
|
|
59
59
|
<span class="ck-model-table__tick" aria-label="Supports judging">✓</span>
|
|
60
60
|
<% elsif m.supports_judging.nil? %>
|
|
61
|
-
<span class="ck-model-table__unknown" aria-label="Untested as judge" title="Untested as a judge
|
|
61
|
+
<span class="ck-model-table__unknown" aria-label="Untested as judge" title="Untested as a judge but selectable. A successful run confirms it.">?</span>
|
|
62
62
|
<% else %>
|
|
63
63
|
<span class="ck-model-table__dash" aria-label="Not usable as judge">—</span>
|
|
64
64
|
<% end %>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<div class="ck-field">
|
|
21
21
|
<label class="ck-checkbox-label">
|
|
22
|
-
<%= check_box_tag "run[judge_only]", "1", run.persisted? && run.judge_only
|
|
22
|
+
<%= check_box_tag "run[judge_only]", "1", local_assigns[:judge_only] || (run.persisted? && run.judge_only?), id: "run_judge_only", class: "ck-checkbox" %>
|
|
23
23
|
<span class="ck-checkbox-label__box" aria-hidden="true"></span>
|
|
24
24
|
<span class="ck-checkbox-label__body">
|
|
25
25
|
<span class="ck-checkbox-label__text">Score existing outputs</span>
|
|
@@ -98,10 +98,11 @@
|
|
|
98
98
|
<% available = CompletionKit::ApiConfig.available_models(scope: :judging) %>
|
|
99
99
|
<% if available.any? %>
|
|
100
100
|
<div class="ck-select-with-action">
|
|
101
|
-
<%= form.select :judge_model, ck_grouped_models(available, run.judge_model), { include_blank: "None" }, { class: "ck-input", id: "run_judge_model" } %>
|
|
101
|
+
<%= form.select :judge_model, ck_grouped_models(available, run.judge_model, rejected: run.errors[:judge_model].any?), { include_blank: "None" }, { class: "ck-input", id: "run_judge_model", **ck_field_aria(form, :judge_model) } %>
|
|
102
102
|
<% ck_refreshing = CompletionKit::ProviderCredential.discovery_in_progress? %>
|
|
103
103
|
<button type="button" class="ck-icon-btn<%= ' ck-icon-btn--spinning' if ck_refreshing %>" title="Refresh models" <%= 'disabled' if ck_refreshing %> onclick="ckRefreshModels()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="16" height="16"><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></button>
|
|
104
104
|
</div>
|
|
105
|
+
<%= ck_field_error(form, :judge_model) %>
|
|
105
106
|
<p class="ck-field-hint" id="judge-hint"></p>
|
|
106
107
|
<div hidden data-refresh-progress-carriers data-ck-refresh-url="<%= refresh_models_path %>" data-ck-statuses-url="<%= statuses_provider_credentials_path %>">
|
|
107
108
|
<% CompletionKit::ProviderCredential.find_each do |pc| %>
|
|
@@ -112,7 +113,7 @@
|
|
|
112
113
|
<p class="ck-field-hint" id="refresh-status" role="status" aria-live="polite" style="min-height: 1.2em; margin-top: -0.25rem; font-size: 0.75rem;"> </p>
|
|
113
114
|
<% else %>
|
|
114
115
|
<p class="ck-field-hint" style="color: var(--ck-warning);">No judge models available. <%= link_to "Add a provider", provider_credentials_path, class: "ck-link" %></p>
|
|
115
|
-
<%= form.hidden_field :judge_model,
|
|
116
|
+
<%= form.hidden_field :judge_model, id: "run_judge_model" %>
|
|
116
117
|
<% end %>
|
|
117
118
|
</div>
|
|
118
119
|
|
|
@@ -157,6 +158,7 @@
|
|
|
157
158
|
</div>
|
|
158
159
|
<% end %>
|
|
159
160
|
|
|
161
|
+
<%= hidden_field_tag "run[metric_ids][]", "", id: nil %>
|
|
160
162
|
<div class="ck-metric-checkboxes" data-metric-checkboxes role="group" aria-labelledby="run_metrics_label">
|
|
161
163
|
<% @all_metrics.each do |metric| %>
|
|
162
164
|
<label class="ck-checkbox-label" data-metric-tags="<%= metric.tag_names.join(",") %>" data-compare-expected="<%= metric.check? && metric.check_config.to_h["compare_to"] == "expected" ? "1" : "0" %>">
|
|
@@ -194,6 +196,7 @@ function updateRunForm() {
|
|
|
194
196
|
|
|
195
197
|
var prompt = promptEl ? promptEl.value : '';
|
|
196
198
|
var judge = judgeEl ? judgeEl.value : '';
|
|
199
|
+
var judgeListed = !!judgeEl && judgeEl.type !== 'hidden';
|
|
197
200
|
var metrics = document.querySelectorAll('input[name="run[metric_ids][]"]:checked');
|
|
198
201
|
var judgeField = document.getElementById('judge-field');
|
|
199
202
|
var metricsField = document.getElementById('metrics-field');
|
|
@@ -206,13 +209,15 @@ function updateRunForm() {
|
|
|
206
209
|
if (judgeHint) judgeHint.textContent = '';
|
|
207
210
|
if (metricsHint) metricsHint.textContent = '';
|
|
208
211
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
var judgeUnpaired = judgeListed && !!judge && metrics.length === 0;
|
|
213
|
+
var metricsUnpaired = judgeListed && !judge && metrics.length > 0;
|
|
214
|
+
if (judgeListed && judge && metrics.length > 0) {
|
|
215
|
+
if (judgeHint) judgeHint.textContent = judgeOnly ? 'Existing outputs will be judged automatically.' : 'Responses will be generated then judged automatically.';
|
|
216
|
+
} else if (judgeUnpaired) {
|
|
212
217
|
if (judgeField) judgeField.className = 'ck-field ck-field--info';
|
|
213
218
|
if (metricsField) metricsField.className = 'ck-field ck-field--info';
|
|
214
219
|
if (metricsHint) metricsHint.textContent = 'Select at least one metric or group to enable judging.';
|
|
215
|
-
} else if (
|
|
220
|
+
} else if (metricsUnpaired) {
|
|
216
221
|
if (judgeField) judgeField.className = 'ck-field ck-field--info';
|
|
217
222
|
if (judgeHint) judgeHint.textContent = 'Select a judge model to enable judging.';
|
|
218
223
|
}
|
|
@@ -286,7 +291,7 @@ function updateRunForm() {
|
|
|
286
291
|
if (col === '' || headersJudge.indexOf(col) === -1) {
|
|
287
292
|
valid = false;
|
|
288
293
|
if (datasetField) datasetField.className = 'ck-field ck-field--error';
|
|
289
|
-
if (datasetHint) datasetHint.textContent = "Dataset has no \"" + col + "\" column
|
|
294
|
+
if (datasetHint) datasetHint.textContent = "Dataset has no \"" + col + "\" column. Pick a different output column or dataset.";
|
|
290
295
|
}
|
|
291
296
|
} else if (!dataset) {
|
|
292
297
|
if (datasetField) datasetField.className = 'ck-field ck-field--info';
|
|
@@ -297,8 +302,7 @@ function updateRunForm() {
|
|
|
297
302
|
if (hasVars && !dataset) valid = false;
|
|
298
303
|
if (missingVars.length > 0) valid = false;
|
|
299
304
|
}
|
|
300
|
-
if (
|
|
301
|
-
if (!judge && metrics.length > 0) valid = false;
|
|
305
|
+
if (judgeUnpaired || metricsUnpaired) valid = false;
|
|
302
306
|
if (submitBtn) submitBtn.disabled = !valid;
|
|
303
307
|
|
|
304
308
|
ckUpdateMetricGroupsState();
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
<td class="ck-response-cell__text">
|
|
5
5
|
<% if response.status == "failed" %>
|
|
6
6
|
<% err = response.error_payload %>
|
|
7
|
-
|
|
7
|
+
<% err_prefix = [err && err[:provider]&.titleize, err && err[:status]].compact.join(" ") %>
|
|
8
|
+
<span class="ck-response-cell__preview ck-response-cell__error"><%= "#{err_prefix}: " if err_prefix.present? %><%= truncate(err && err[:message].to_s, length: 600) %></span>
|
|
8
9
|
<% else %>
|
|
9
10
|
<span class="ck-response-cell__preview"><%= truncate(response.response_text.to_s, length: 600) %></span>
|
|
10
11
|
<% end %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
<title><%= [content_for(:ck_title).presence, "CompletionKit"].compact.join("
|
|
4
|
+
<title><%= [content_for(:ck_title).presence, "CompletionKit"].compact.join(" · ") %></title>
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
7
|
<%= csp_meta_tag %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class DropCompletionKitMcpSessions < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
drop_table :completion_kit_mcp_sessions, if_exists: true
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def down
|
|
7
|
+
create_table :completion_kit_mcp_sessions do |t|
|
|
8
|
+
t.string :session_id, null: false
|
|
9
|
+
t.datetime :expires_at, null: false
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :completion_kit_mcp_sessions, :session_id, unique: true
|
|
14
|
+
add_index :completion_kit_mcp_sessions, :expires_at
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -9,9 +9,9 @@ module CompletionKit
|
|
|
9
9
|
class UnknownModelError < ConfigurationError
|
|
10
10
|
attr_reader :model_name
|
|
11
11
|
|
|
12
|
-
def initialize(model_name = nil)
|
|
12
|
+
def initialize(model_name = nil, message = nil)
|
|
13
13
|
@model_name = model_name
|
|
14
|
-
super(%(No configured provider serves model "#{model_name}"))
|
|
14
|
+
super(message || %(No configured provider serves model "#{model_name}"))
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
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.28.
|
|
4
|
+
version: 0.28.46
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -452,6 +452,7 @@ files:
|
|
|
452
452
|
- db/migrate/20260730000002_create_completion_kit_prompt_serves.rb
|
|
453
453
|
- db/migrate/20260804000001_add_judge_temperature_ignored_to_runs.rb
|
|
454
454
|
- db/migrate/20260804000002_default_runs_to_no_temperature.rb
|
|
455
|
+
- db/migrate/20261013000001_drop_completion_kit_mcp_sessions.rb
|
|
455
456
|
- lib/completion-kit.rb
|
|
456
457
|
- lib/completion_kit.rb
|
|
457
458
|
- lib/completion_kit/concurrency_check.rb
|