completion-kit 0.28.46 → 0.29.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/completion_kit/application.js +52 -23
- data/app/assets/stylesheets/completion_kit/application.css +44 -14
- data/app/controllers/completion_kit/api/v1/metric_versions_controller.rb +2 -0
- data/app/controllers/completion_kit/api/v1/metrics_controller.rb +1 -1
- data/app/controllers/completion_kit/api/v1/runs_controller.rb +4 -1
- data/app/controllers/completion_kit/metrics_controller.rb +48 -46
- data/app/controllers/completion_kit/runs_controller.rb +4 -1
- data/app/helpers/completion_kit/application_helper.rb +99 -45
- data/app/jobs/completion_kit/check_review_job.rb +27 -6
- data/app/models/completion_kit/metric.rb +103 -28
- data/app/models/completion_kit/metric_version.rb +16 -1
- data/app/models/completion_kit/review.rb +2 -0
- data/app/models/completion_kit/run.rb +19 -8
- data/app/services/completion_kit/checks/contains.rb +2 -8
- data/app/services/completion_kit/checks/equals.rb +2 -7
- data/app/services/completion_kit/checks/expected_resolver.rb +17 -3
- data/app/services/completion_kit/checks/flag.rb +13 -0
- data/app/services/completion_kit/checks/json_path_equals.rb +2 -15
- data/app/services/completion_kit/checks/labels.rb +104 -0
- data/app/services/completion_kit/checks/length_bounds.rb +4 -4
- data/app/services/completion_kit/checks/list_overlap.rb +96 -0
- data/app/services/completion_kit/checks/not_contains.rb +2 -8
- data/app/services/completion_kit/checks/numeric_bounds.rb +22 -0
- data/app/services/completion_kit/checks/numeric_equals.rb +37 -0
- data/app/services/completion_kit/checks/numeric_value.rb +45 -0
- data/app/services/completion_kit/checks/regex.rb +2 -2
- data/app/services/completion_kit/checks/registry.rb +107 -4
- data/app/services/completion_kit/checks/result.rb +5 -1
- data/app/services/completion_kit/checks/target_resolver.rb +43 -8
- data/app/services/completion_kit/checks/textual.rb +19 -0
- data/app/services/completion_kit/mcp_tools/metrics.rb +21 -7
- data/app/services/completion_kit/promptfoo_importer.rb +4 -4
- data/app/services/completion_kit/run_comparison.rb +6 -0
- data/app/views/completion_kit/api_reference/_body.html.erb +7 -3
- data/app/views/completion_kit/metrics/_check_spec.html.erb +5 -11
- data/app/views/completion_kit/metrics/_form.html.erb +62 -47
- data/app/views/completion_kit/responses/show.html.erb +1 -1
- data/app/views/completion_kit/runs/_compare_score.html.erb +1 -1
- data/app/views/completion_kit/runs/_form.html.erb +9 -3
- data/app/views/completion_kit/runs/_response_row.html.erb +3 -2
- data/app/views/completion_kit/runs/_row.html.erb +10 -8
- data/app/views/completion_kit/runs/_run_config.html.erb +1 -1
- data/app/views/completion_kit/runs/_status_panel.html.erb +2 -2
- data/app/views/completion_kit/runs/compare.html.erb +10 -6
- data/app/views/completion_kit/shared/_branded_select.html.erb +1 -1
- data/config/locales/en.yml +2 -0
- data/db/migrate/20260903000001_add_check_score_to_completion_kit_reviews.rb +5 -0
- data/lib/completion_kit/version.rb +1 -1
- metadata +9 -1
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
<% pub = local_assigns[:published_metric_version] %>
|
|
18
18
|
<% draft_instr_changed = pub && pub.instruction.to_s != edit_draft.instruction.to_s %>
|
|
19
19
|
<% draft_rubric_changed = pub && pub.rubric_bands != edit_draft.rubric_bands %>
|
|
20
|
+
<% draft_subject = edit_draft.check? ? "check" : [("instruction" if draft_instr_changed), ("rubric" if draft_rubric_changed)].compact.join(" and ").presence || "version" %>
|
|
20
21
|
<div class="ck-suggestion-banner" role="status">
|
|
21
22
|
<div class="ck-suggestion-banner__body">
|
|
22
23
|
<p class="ck-kicker">Draft pending</p>
|
|
23
|
-
<p class="ck-meta-copy">The form below shows your unpublished draft. Publish to replace the live<%=
|
|
24
|
+
<p class="ck-meta-copy">The form below shows your unpublished draft. Publish to replace the live <%= draft_subject %> for future runs, or keep editing.</p>
|
|
24
25
|
</div>
|
|
25
26
|
<div class="ck-suggestion-banner__actions">
|
|
26
27
|
<%= button_to "Discard draft", dismiss_suggestion_metric_path(metric, draft_id: edit_draft.id, back_to: "edit"),
|
|
@@ -164,99 +165,113 @@
|
|
|
164
165
|
|
|
165
166
|
<% if show_check %>
|
|
166
167
|
<% check = metric.check_config || {} %>
|
|
167
|
-
|
|
168
|
+
<% kind = check["check_kind"] || CompletionKit::Checks::Registry.kinds.first %>
|
|
169
|
+
<div class="ck-field ck-field--spacious" data-ck-metric-editor="check" data-ck-check-form="<%= ck_check_form_data.to_json %>" <%= "hidden" unless metric.check? %>>
|
|
168
170
|
<p class="ck-section-title">Check</p>
|
|
169
171
|
<p class="ck-hint">A pass or fail rule with no AI and no cost. You only fill in the fields the chosen rule needs.</p>
|
|
170
172
|
|
|
171
173
|
<div class="ck-field">
|
|
172
|
-
<p class="ck-label" id="metric_check_kind_label"
|
|
174
|
+
<p class="ck-label" id="metric_check_kind_label"><%= ck_check_field_label("check_kind") %></p>
|
|
173
175
|
<%= render "completion_kit/shared/branded_select",
|
|
174
176
|
name: "metric[check_config][check_kind]",
|
|
175
|
-
options: CompletionKit::Checks::Registry.kinds.map { |
|
|
176
|
-
selected:
|
|
177
|
-
labelledby: "metric_check_kind_label"
|
|
177
|
+
options: CompletionKit::Checks::Registry.kinds.map { |option| [option, ck_check_kind_label(option)] },
|
|
178
|
+
selected: kind,
|
|
179
|
+
labelledby: "metric_check_kind_label",
|
|
180
|
+
invalid: ck_check_field_invalid?(metric, "check_kind") %>
|
|
178
181
|
</div>
|
|
179
182
|
|
|
180
183
|
<div class="ck-field">
|
|
181
|
-
<p class="ck-label" id="metric_check_target_label"
|
|
184
|
+
<p class="ck-label" id="metric_check_target_label"><%= ck_check_field_label("target") %></p>
|
|
182
185
|
<%= render "completion_kit/shared/branded_select",
|
|
183
186
|
name: "metric[check_config][target]",
|
|
184
187
|
options: CompletionKit::Checks::TargetResolver::TARGETS.map { |target| [target, ck_check_target_label(target)] },
|
|
185
188
|
selected: check["target"] || CompletionKit::Checks::TargetResolver::TARGETS.first,
|
|
186
|
-
labelledby: "metric_check_target_label"
|
|
189
|
+
labelledby: "metric_check_target_label",
|
|
190
|
+
invalid: ck_check_field_invalid?(metric, "target") %>
|
|
187
191
|
</div>
|
|
188
192
|
|
|
189
193
|
<div class="ck-field" data-ck-check-field="target_path">
|
|
190
|
-
<label class="ck-label" for="metric_check_target_path"
|
|
191
|
-
<p class="ck-hint">Where to look inside the response, like data.items.0.name.</p>
|
|
192
|
-
<input type="text" name="metric[check_config][target_path]" id="metric_check_target_path" class="ck-input" value="<%= check["target_path"] %>"
|
|
194
|
+
<label class="ck-label" for="metric_check_target_path"><%= ck_check_field_label("target_path") %></label>
|
|
195
|
+
<p class="ck-hint">Where to look inside the response, like data.items.0.name. Use a number to pick an item out of a list.</p>
|
|
196
|
+
<input type="text" name="metric[check_config][target_path]" id="metric_check_target_path" class="ck-input" value="<%= check["target_path"] %>" <%= tag.attributes(ck_check_field_aria(metric, "target_path")) %>>
|
|
193
197
|
</div>
|
|
194
198
|
|
|
195
199
|
<div class="ck-field" data-ck-check-field="compare_to">
|
|
196
|
-
<p class="ck-label"
|
|
200
|
+
<p class="ck-label"><%= ck_check_field_label("compare_to") %></p>
|
|
197
201
|
<p class="ck-hint">Check every output against one fixed value, or against each row's own expected value (its answer key).</p>
|
|
198
202
|
<label class="ck-radio">
|
|
199
|
-
<input type="radio" name="metric[check_config][compare_to]" value="constant" <%= "checked" unless check["compare_to"] == "expected" %>>
|
|
200
|
-
<span
|
|
203
|
+
<input type="radio" name="metric[check_config][compare_to]" value="constant" <%= "checked" unless check["compare_to"] == "expected" %> <%= tag.attributes(ck_check_field_aria(metric, "compare_to")) %>>
|
|
204
|
+
<span><%= ck_check_field_value("compare_to", "constant") %></span>
|
|
201
205
|
</label>
|
|
202
206
|
<label class="ck-radio">
|
|
203
|
-
<input type="radio" name="metric[check_config][compare_to]" value="expected" <%= "checked" if check["compare_to"] == "expected" %>>
|
|
204
|
-
<span
|
|
207
|
+
<input type="radio" name="metric[check_config][compare_to]" value="expected" <%= "checked" if check["compare_to"] == "expected" %> <%= tag.attributes(ck_check_field_aria(metric, "compare_to")) %>>
|
|
208
|
+
<span><%= ck_check_field_value("compare_to", "expected") %></span>
|
|
205
209
|
</label>
|
|
206
210
|
</div>
|
|
207
211
|
|
|
208
212
|
<div class="ck-field" data-ck-check-field="value">
|
|
209
|
-
<label class="ck-label" for="metric_check_value"
|
|
210
|
-
<input type="text" name="metric[check_config][value]" id="metric_check_value" class="ck-input" value="<%= check["value"] %>"
|
|
213
|
+
<label class="ck-label" for="metric_check_value"><span data-ck-check-label="value"><%= ck_check_field_label("value", kind) %></span></label>
|
|
214
|
+
<input type="text" name="metric[check_config][value]" id="metric_check_value" class="ck-input" value="<%= ck_check_input_value(check["value"]) %>" <%= tag.attributes(ck_check_field_aria(metric, "value")) %>>
|
|
211
215
|
</div>
|
|
212
216
|
|
|
213
217
|
<div class="ck-field" data-ck-check-field="pattern">
|
|
214
|
-
<label class="ck-label" for="metric_check_pattern"
|
|
218
|
+
<label class="ck-label" for="metric_check_pattern"><%= ck_check_field_label("pattern") %></label>
|
|
215
219
|
<p class="ck-hint">A regular expression to match against.</p>
|
|
216
|
-
<input type="text" name="metric[check_config][pattern]" id="metric_check_pattern" class="ck-input" value="<%= check["pattern"] %>"
|
|
220
|
+
<input type="text" name="metric[check_config][pattern]" id="metric_check_pattern" class="ck-input" value="<%= check["pattern"] %>" <%= tag.attributes(ck_check_field_aria(metric, "pattern")) %>>
|
|
217
221
|
</div>
|
|
218
222
|
|
|
219
223
|
<div class="ck-field" data-ck-check-field="json_path">
|
|
220
|
-
<label class="ck-label" for="metric_check_json_path"
|
|
224
|
+
<label class="ck-label" for="metric_check_json_path"><%= ck_check_field_label("json_path") %></label>
|
|
221
225
|
<p class="ck-hint">The path to the value inside the response, like data.status.</p>
|
|
222
|
-
<input type="text" name="metric[check_config][json_path]" id="metric_check_json_path" class="ck-input" value="<%= check["json_path"] %>"
|
|
226
|
+
<input type="text" name="metric[check_config][json_path]" id="metric_check_json_path" class="ck-input" value="<%= check["json_path"] %>" <%= tag.attributes(ck_check_field_aria(metric, "json_path")) %>>
|
|
223
227
|
</div>
|
|
224
228
|
|
|
225
229
|
<div class="ck-field" data-ck-check-field="expected">
|
|
226
|
-
<label class="ck-label" for="metric_check_expected"
|
|
227
|
-
<p class="ck-hint">The value it should equal.</p>
|
|
228
|
-
<input type="text" name="metric[check_config][expected]" id="metric_check_expected" class="ck-input" value="<%= check["expected"] %>"
|
|
230
|
+
<label class="ck-label" for="metric_check_expected"><%= ck_check_field_label("expected") %></label>
|
|
231
|
+
<p class="ck-hint">The value it should equal. A number, true or false matches that JSON value. Put it in quotes, like "200", to match it as text.</p>
|
|
232
|
+
<input type="text" name="metric[check_config][expected]" id="metric_check_expected" class="ck-input" value="<%= ck_check_expected_input_value(check["expected"]) %>" <%= tag.attributes(ck_check_field_aria(metric, "expected")) %>>
|
|
229
233
|
</div>
|
|
230
234
|
|
|
231
235
|
<div class="ck-field" data-ck-check-field="expected_path">
|
|
232
|
-
<label class="ck-label" for="metric_check_expected_path"
|
|
236
|
+
<label class="ck-label" for="metric_check_expected_path"><%= ck_check_field_label("expected_path") %></label>
|
|
233
237
|
<p class="ck-hint">Optional. If the expected value is JSON, name the field to read from it, like data.answer. Leave blank to compare against the whole value.</p>
|
|
234
|
-
<input type="text" name="metric[check_config][expected_path]" id="metric_check_expected_path" class="ck-input" value="<%= check["expected_path"] %>"
|
|
238
|
+
<input type="text" name="metric[check_config][expected_path]" id="metric_check_expected_path" class="ck-input" value="<%= check["expected_path"] %>" <%= tag.attributes(ck_check_field_aria(metric, "expected_path")) %>>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div class="ck-field" data-ck-check-field="score_by">
|
|
242
|
+
<p class="ck-label" id="metric_check_score_by_label"><%= ck_check_field_label("score_by") %></p>
|
|
243
|
+
<p class="ck-hint"><%= ck_check_field_hint("score_by") %></p>
|
|
244
|
+
<%= render "completion_kit/shared/branded_select",
|
|
245
|
+
name: "metric[check_config][score_by]",
|
|
246
|
+
options: ck_check_score_by_options,
|
|
247
|
+
selected: check["score_by"] || CompletionKit::Checks::ListOverlap::DEFAULT_MEASURE,
|
|
248
|
+
labelledby: "metric_check_score_by_label",
|
|
249
|
+
invalid: ck_check_field_invalid?(metric, "score_by") %>
|
|
235
250
|
</div>
|
|
236
251
|
|
|
237
252
|
<div class="ck-field-row">
|
|
238
|
-
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
253
|
+
<% %w[min max].each do |bound| %>
|
|
254
|
+
<div class="ck-field" data-ck-check-field="<%= bound %>">
|
|
255
|
+
<label class="ck-label" for="metric_check_<%= bound %>"><span data-ck-check-label="<%= bound %>"><%= ck_check_field_label(bound, kind) %></span></label>
|
|
256
|
+
<p class="ck-hint" data-ck-check-hint="<%= bound %>"><%= ck_check_field_hint(bound, kind) %></p>
|
|
257
|
+
<input type="number" step="any" name="metric[check_config][<%= bound %>]" id="metric_check_<%= bound %>" class="ck-input" value="<%= check[bound] %>" <%= tag.attributes(ck_check_field_aria(metric, bound)) %>>
|
|
258
|
+
</div>
|
|
259
|
+
<% end %>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div class="ck-field" data-ck-check-field="tolerance">
|
|
263
|
+
<label class="ck-label" for="metric_check_tolerance"><%= ck_check_field_label("tolerance") %></label>
|
|
264
|
+
<p class="ck-hint">How far a number can be out and still count as correct. Write a plain number for a fixed amount, or add a percent sign, like 2%, for a share of the expected number. Leave blank to require an exact match.</p>
|
|
265
|
+
<input type="text" inputmode="decimal" name="metric[check_config][tolerance]" id="metric_check_tolerance" class="ck-input" value="<%= check["tolerance"] %>" <%= tag.attributes(ck_check_field_aria(metric, "tolerance")) %>>
|
|
246
266
|
</div>
|
|
247
267
|
|
|
248
|
-
|
|
249
|
-
<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
</label>
|
|
256
|
-
<label class="ck-checkbox" data-ck-check-field="trim">
|
|
257
|
-
<input type="checkbox" name="metric[check_config][trim]" value="true"<%= " checked" if check["trim"] %>>
|
|
258
|
-
<span>Trim whitespace</span>
|
|
259
|
-
</label>
|
|
268
|
+
<% CompletionKit::Checks::Registry::FLAG_KEYS.each do |flag| %>
|
|
269
|
+
<label class="ck-checkbox" data-ck-check-field="<%= flag %>">
|
|
270
|
+
<input type="hidden" name="metric[check_config][<%= flag %>]" value="false">
|
|
271
|
+
<input type="checkbox" name="metric[check_config][<%= flag %>]" value="true"<%= " checked" if CompletionKit::Checks::Flag.on?(check, flag) %>>
|
|
272
|
+
<span><%= ck_check_field_label(flag) %></span>
|
|
273
|
+
</label>
|
|
274
|
+
<% end %>
|
|
260
275
|
</div>
|
|
261
276
|
<% end %>
|
|
262
277
|
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
<svg viewBox="0 0 24 24" width="16" height="16" stroke-width="1.75" class="ck-star <%= i < review.ai_score.to_i ? "ck-star--filled" : "ck-star--empty" %>"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
|
|
126
126
|
<% end %>
|
|
127
127
|
<% elsif review.check? && !review.passed.nil? %>
|
|
128
|
-
<%= ck_check_badge(review.passed) %>
|
|
128
|
+
<%= ck_check_badge(review.passed, review.check_score) %>
|
|
129
129
|
<% elsif review.status == "failed" %>
|
|
130
130
|
<span class="<%= ck_badge_classes(:failed) %>" title="<%= review.error_message.presence || (review.check? ? "Check failed" : "Judge failed") %>">Failed</span>
|
|
131
131
|
<% else %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% if has_value && response %>
|
|
3
3
|
<%= link_to run_response_path(run, response), class: "ck-cmp-score__link", title: "#{side}: open this response (#{run.name})" do %>
|
|
4
4
|
<% if kind == "check" %>
|
|
5
|
-
<%= ck_check_badge(passed) %>
|
|
5
|
+
<%= ck_check_badge(passed, local_assigns[:check_score]) %>
|
|
6
6
|
<% else %>
|
|
7
7
|
<span class="<%= ck_badge_classes(ck_score_kind(score.to_f)) %>"><%= score %></span>
|
|
8
8
|
<% end %>
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
<%= hidden_field_tag "run[metric_ids][]", "", id: nil %>
|
|
162
162
|
<div class="ck-metric-checkboxes" data-metric-checkboxes role="group" aria-labelledby="run_metrics_label">
|
|
163
163
|
<% @all_metrics.each do |metric| %>
|
|
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" %>">
|
|
164
|
+
<label class="ck-checkbox-label" data-metric-tags="<%= metric.tag_names.join(",") %>" data-needs-judge="<%= metric.llm_judge? ? "1" : "0" %>" data-compare-expected="<%= metric.check? && metric.check_config.to_h["compare_to"] == "expected" ? "1" : "0" %>">
|
|
165
165
|
<%= check_box_tag "run[metric_ids][]", metric.id, run.selected_metric_ids.include?(metric.id), class: "ck-checkbox", id: "run_metric_#{metric.id}" %>
|
|
166
166
|
<span class="ck-checkbox-label__box" aria-hidden="true"></span>
|
|
167
167
|
<span class="ck-checkbox-label__body">
|
|
@@ -209,9 +209,15 @@ function updateRunForm() {
|
|
|
209
209
|
if (judgeHint) judgeHint.textContent = '';
|
|
210
210
|
if (metricsHint) metricsHint.textContent = '';
|
|
211
211
|
|
|
212
|
+
var judgedMetrics = Array.prototype.filter.call(metrics, function(cb) {
|
|
213
|
+
var lbl = cb.closest('[data-needs-judge]');
|
|
214
|
+
return !!lbl && lbl.dataset.needsJudge === '1';
|
|
215
|
+
});
|
|
212
216
|
var judgeUnpaired = judgeListed && !!judge && metrics.length === 0;
|
|
213
|
-
var metricsUnpaired = judgeListed && !judge &&
|
|
214
|
-
if (judgeListed && judge && metrics.length > 0) {
|
|
217
|
+
var metricsUnpaired = judgeListed && !judge && judgedMetrics.length > 0;
|
|
218
|
+
if (judgeListed && judge && metrics.length > 0 && judgedMetrics.length === 0) {
|
|
219
|
+
if (judgeHint) judgeHint.textContent = 'Checks need no judge, so this run will not use it.';
|
|
220
|
+
} else if (judgeListed && judge && metrics.length > 0) {
|
|
215
221
|
if (judgeHint) judgeHint.textContent = judgeOnly ? 'Existing outputs will be judged automatically.' : 'Responses will be generated then judged automatically.';
|
|
216
222
|
} else if (judgeUnpaired) {
|
|
217
223
|
if (judgeField) judgeField.className = 'ck-field ck-field--info';
|
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
<span class="ck-metric-pip__label" aria-hidden="true"><%= r.metric_name %> <strong><%= r.ai_score %></strong></span>
|
|
22
22
|
</span>
|
|
23
23
|
<% else %>
|
|
24
|
-
|
|
24
|
+
<% verdict = ck_check_verdict(r.passed, r.check_score) %>
|
|
25
|
+
<span class="ck-metric-pip ck-metric-pip--<%= ck_pass_rate_kind(r.passed ? 1.0 : 0.0) %>" role="img" aria-label="<%= r.metric_name %> <%= verdict %>">
|
|
25
26
|
<span class="ck-metric-pip__bar"></span>
|
|
26
|
-
<span class="ck-metric-pip__label" aria-hidden="true"><%= r.metric_name %> <strong><%=
|
|
27
|
+
<span class="ck-metric-pip__label" aria-hidden="true"><%= r.metric_name %> <strong><%= verdict %></strong></span>
|
|
27
28
|
</span>
|
|
28
29
|
<% end %>
|
|
29
30
|
<% end %>
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
<div class="ck-metric-bar ck-metric-bar--compact">
|
|
37
37
|
<% metrics.each do |m| %>
|
|
38
38
|
<% if m[:kind] == "check" %>
|
|
39
|
-
<span class="ck-metric-pip ck-metric-pip--<%= ck_pass_rate_kind(m[:pass_rate]) %>" role="img" aria-label="<%= m[:name]
|
|
39
|
+
<span class="ck-metric-pip ck-metric-pip--<%= ck_pass_rate_kind(m[:pass_rate]) %>" role="img" aria-label="<%= ck_check_average_label(m[:name], m) %>">
|
|
40
40
|
<span class="ck-metric-pip__bar"></span>
|
|
41
|
-
<span class="ck-metric-pip__label" aria-hidden="true"><%= m[:name] %> <strong><%= (m
|
|
41
|
+
<span class="ck-metric-pip__label" aria-hidden="true"><%= m[:name] %> <strong><%= ck_check_average(m) %></strong></span>
|
|
42
42
|
</span>
|
|
43
43
|
<% else %>
|
|
44
44
|
<span class="ck-metric-pip ck-metric-pip--<%= ck_score_kind(m[:avg]) %>" role="img" aria-label="<%= m[:name] %> average score <%= m[:avg] %>">
|
|
@@ -56,12 +56,14 @@
|
|
|
56
56
|
<% avg = run.avg_score %>
|
|
57
57
|
<% pass_rate = run.check_pass_rate %>
|
|
58
58
|
<% if avg || pass_rate %>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
<div class="ck-chip-row">
|
|
60
|
+
<% if avg %>
|
|
61
|
+
<span class="<%= ck_badge_classes(ck_score_kind(avg)) %>"><%= avg %></span>
|
|
62
|
+
<% end %>
|
|
63
|
+
<% if pass_rate %>
|
|
64
|
+
<span class="<%= ck_badge_classes(ck_pass_rate_kind(pass_rate)) %>"><%= (pass_rate * 100).round %>%</span>
|
|
65
|
+
<% end %>
|
|
66
|
+
</div>
|
|
65
67
|
<% else %>
|
|
66
68
|
<span class="ck-runs-table__dim">—</span>
|
|
67
69
|
<% end %>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<div class="ck-run-config__row">
|
|
20
20
|
<span class="ck-run-config__key">Judge</span>
|
|
21
21
|
<span style="text-transform: none;"><%= run.judge_model %></span>
|
|
22
|
-
<%
|
|
22
|
+
<% if run.llm_metrics.any? && !run.llm_judge_configured? %>
|
|
23
23
|
<span class="ck-run-config__warn">provider not configured</span>
|
|
24
24
|
<% end %>
|
|
25
25
|
</div>
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
<% run.metrics.order(:name).each do |metric| %>
|
|
42
42
|
<% avg_for_metric = metric_lookup[metric.name.to_s.downcase] %>
|
|
43
43
|
<% if avg_for_metric && avg_for_metric[:kind] == "check" %>
|
|
44
|
-
<span class="ck-metric-pip ck-metric-pip--<%= ck_pass_rate_kind(avg_for_metric[:pass_rate]) %>">
|
|
44
|
+
<span class="ck-metric-pip ck-metric-pip--<%= ck_pass_rate_kind(avg_for_metric[:pass_rate]) %>" title="<%= ck_check_average_label(metric.name, avg_for_metric) %>">
|
|
45
45
|
<span class="ck-metric-pip__bar"></span>
|
|
46
|
-
<span class="ck-metric-pip__label"><%= metric.name %> <strong><%= (avg_for_metric
|
|
46
|
+
<span class="ck-metric-pip__label"><%= metric.name %> <strong><%= ck_check_average(avg_for_metric) %></strong></span>
|
|
47
47
|
</span>
|
|
48
48
|
<% elsif avg_for_metric %>
|
|
49
49
|
<span class="ck-metric-pip ck-metric-pip--<%= ck_score_kind(avg_for_metric[:avg]) %>">
|
|
@@ -56,18 +56,22 @@
|
|
|
56
56
|
<td class="ck-compare-metric" data-label="Metric" title="<%= pm[:metric_name] %>"><%= pm[:metric_name] %></td>
|
|
57
57
|
<td class="ck-cmp-score" data-label="A → B">
|
|
58
58
|
<span class="ck-cmp-score__pair">
|
|
59
|
-
<%= render "compare_score", kind: pm[:kind], passed: pm[:left_passed], score: pm[:left_score], response: row[:left_response], run: @run, side: "A" %>
|
|
59
|
+
<%= render "compare_score", kind: pm[:kind], passed: pm[:left_passed], score: pm[:left_score], check_score: pm[:left_check_score], response: row[:left_response], run: @run, side: "A" %>
|
|
60
60
|
<span class="ck-cmp-score__arrow" aria-hidden="true">→</span>
|
|
61
|
-
<%= render "compare_score", kind: pm[:kind], passed: pm[:right_passed], score: pm[:right_score], response: row[:right_response], run: @other_run, side: "B" %>
|
|
61
|
+
<%= render "compare_score", kind: pm[:kind], passed: pm[:right_passed], score: pm[:right_score], check_score: pm[:right_check_score], response: row[:right_response], run: @other_run, side: "B" %>
|
|
62
62
|
</span>
|
|
63
63
|
</td>
|
|
64
64
|
<td class="ck-num ck-cmp-delta" data-label="Δ">
|
|
65
65
|
<% if pm[:kind] == "check" %>
|
|
66
|
-
|
|
66
|
+
<% points = ck_check_score_change(pm[:left_check_score], pm[:right_check_score]) %>
|
|
67
|
+
<% badge = ck_result_change_badge(pm[:result_change]) unless points && pm[:result_change] == "same" %>
|
|
68
|
+
<% if badge || points %>
|
|
69
|
+
<span class="ck-cmp-delta__pair"><%= badge %><%= ck_delta_chip(points, "%") if points %></span>
|
|
70
|
+
<% else %>
|
|
71
|
+
<span class="ck-cmp-score__none">—</span>
|
|
72
|
+
<% end %>
|
|
67
73
|
<% elsif pm[:delta] %>
|
|
68
|
-
|
|
69
|
-
<% dcls = d > 0 ? "ck-delta--positive" : d < 0 ? "ck-delta--negative" : "ck-delta--zero" %>
|
|
70
|
-
<span class="ck-delta <%= dcls %>"><span class="ck-delta__arrow" aria-hidden="true"><%= d > 0 ? "▲" : d < 0 ? "▼" : "=" %></span><%= d > 0 ? "+#{d}" : d.to_s %></span>
|
|
74
|
+
<%= ck_delta_chip(pm[:delta]) %>
|
|
71
75
|
<% else %>
|
|
72
76
|
<span class="ck-cmp-score__none">—</span>
|
|
73
77
|
<% end %>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="ck-select" data-ck-select>
|
|
3
3
|
<input type="hidden" name="<%= name %>" value="<%= chosen && chosen.first %>" data-ck-select-value>
|
|
4
4
|
<button type="button" class="ck-select__trigger" data-ck-select-trigger
|
|
5
|
-
aria-haspopup="listbox" aria-expanded="false" aria-labelledby="<%= labelledby %>"
|
|
5
|
+
aria-haspopup="listbox" aria-expanded="false" aria-labelledby="<%= labelledby %>" <%= tag.attributes("aria-invalid": ("true" if local_assigns[:invalid])) %>>
|
|
6
6
|
<span class="ck-select__label" data-ck-select-label><%= chosen && chosen.last %></span>
|
|
7
7
|
<span class="ck-select__chevron" aria-hidden="true"><%= heroicon_tag "chevron-down" %></span>
|
|
8
8
|
</button>
|
data/config/locales/en.yml
CHANGED
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.
|
|
4
|
+
version: 0.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -264,13 +264,20 @@ files:
|
|
|
264
264
|
- app/services/completion_kit/checks/contains.rb
|
|
265
265
|
- app/services/completion_kit/checks/equals.rb
|
|
266
266
|
- app/services/completion_kit/checks/expected_resolver.rb
|
|
267
|
+
- app/services/completion_kit/checks/flag.rb
|
|
267
268
|
- app/services/completion_kit/checks/json_path_equals.rb
|
|
269
|
+
- app/services/completion_kit/checks/labels.rb
|
|
268
270
|
- app/services/completion_kit/checks/length_bounds.rb
|
|
271
|
+
- app/services/completion_kit/checks/list_overlap.rb
|
|
269
272
|
- app/services/completion_kit/checks/not_contains.rb
|
|
273
|
+
- app/services/completion_kit/checks/numeric_bounds.rb
|
|
274
|
+
- app/services/completion_kit/checks/numeric_equals.rb
|
|
275
|
+
- app/services/completion_kit/checks/numeric_value.rb
|
|
270
276
|
- app/services/completion_kit/checks/regex.rb
|
|
271
277
|
- app/services/completion_kit/checks/registry.rb
|
|
272
278
|
- app/services/completion_kit/checks/result.rb
|
|
273
279
|
- app/services/completion_kit/checks/target_resolver.rb
|
|
280
|
+
- app/services/completion_kit/checks/textual.rb
|
|
274
281
|
- app/services/completion_kit/checks/valid_json.rb
|
|
275
282
|
- app/services/completion_kit/csv_processor.rb
|
|
276
283
|
- app/services/completion_kit/dashboard_stats.rb
|
|
@@ -452,6 +459,7 @@ files:
|
|
|
452
459
|
- db/migrate/20260730000002_create_completion_kit_prompt_serves.rb
|
|
453
460
|
- db/migrate/20260804000001_add_judge_temperature_ignored_to_runs.rb
|
|
454
461
|
- db/migrate/20260804000002_default_runs_to_no_temperature.rb
|
|
462
|
+
- db/migrate/20260903000001_add_check_score_to_completion_kit_reviews.rb
|
|
455
463
|
- db/migrate/20261013000001_drop_completion_kit_mcp_sessions.rb
|
|
456
464
|
- lib/completion-kit.rb
|
|
457
465
|
- lib/completion_kit.rb
|