completion-kit 0.28.24 → 0.28.34
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 +82 -5
- data/app/controllers/completion_kit/api/v1/prompts_controller.rb +1 -0
- data/app/controllers/completion_kit/api/v1/responses_controller.rb +6 -3
- data/app/controllers/completion_kit/api/v1/runs_controller.rb +1 -1
- data/app/controllers/completion_kit/dashboard_controller.rb +12 -1
- data/app/controllers/completion_kit/prompts_controller.rb +1 -0
- data/app/controllers/completion_kit/responses_controller.rb +1 -1
- data/app/controllers/completion_kit/runs_controller.rb +16 -9
- data/app/helpers/completion_kit/application_helper.rb +18 -0
- data/app/jobs/completion_kit/generate_row_job.rb +1 -1
- data/app/jobs/completion_kit/judge_review_job.rb +1 -2
- data/app/jobs/completion_kit/run_completion_check_job.rb +4 -0
- data/app/jobs/completion_kit/start_run_job.rb +29 -0
- data/app/models/completion_kit/prompt_serve.rb +41 -0
- data/app/models/completion_kit/review.rb +5 -1
- data/app/models/completion_kit/run.rb +110 -14
- data/app/services/completion_kit/dashboard_stats.rb +54 -0
- data/app/services/completion_kit/judge_service.rb +4 -1
- data/app/services/completion_kit/mcp_tools/base.rb +12 -0
- data/app/services/completion_kit/mcp_tools/responses.rb +38 -3
- data/app/services/completion_kit/mcp_tools/runs.rb +33 -5
- data/app/services/completion_kit/metric_improvement_validator.rb +1 -2
- data/app/services/completion_kit/prompt_improvement_validator.rb +2 -3
- data/app/services/completion_kit/response_query.rb +94 -0
- data/app/views/completion_kit/api_reference/_body.html.erb +5 -3
- data/app/views/completion_kit/dashboard/_failing_checks_card.html.erb +38 -7
- data/app/views/completion_kit/dashboard/_failures_card.html.erb +6 -5
- data/app/views/completion_kit/dashboard/_worst_metric_card.html.erb +1 -1
- data/app/views/completion_kit/dashboard/show.html.erb +48 -9
- data/app/views/completion_kit/prompts/show.html.erb +13 -0
- data/app/views/completion_kit/responses/show.html.erb +6 -5
- data/app/views/completion_kit/runs/_form.html.erb +17 -0
- data/app/views/completion_kit/runs/_responses_region.html.erb +20 -0
- data/app/views/completion_kit/runs/_status_header.html.erb +22 -0
- data/app/views/completion_kit/runs/_status_panel.html.erb +18 -0
- data/app/views/completion_kit/runs/show.html.erb +30 -30
- data/db/migrate/20260728000001_add_max_tokens_to_completion_kit_runs.rb +5 -0
- data/db/migrate/20260730000001_add_judge_temperature_to_completion_kit_runs.rb +5 -0
- data/db/migrate/20260730000002_create_completion_kit_prompt_serves.rb +17 -0
- data/lib/completion_kit/version.rb +1 -1
- data/lib/completion_kit.rb +4 -1
- metadata +9 -2
|
@@ -95,11 +95,12 @@
|
|
|
95
95
|
<% end %>
|
|
96
96
|
|
|
97
97
|
<% if @reviews.any? %>
|
|
98
|
+
<% review_header = ck_review_section_header(@reviews, @run.judge_model) %>
|
|
98
99
|
<section class="ck-card--spaced">
|
|
99
|
-
<div class="ck-prompt-preview__header">
|
|
100
|
-
<p class="ck-kicker"
|
|
101
|
-
<% if
|
|
102
|
-
<span class="ck-chip ck-chip--soft" style="text-transform: none;"><%=
|
|
100
|
+
<div class="ck-prompt-preview__header" data-ck-review-header>
|
|
101
|
+
<p class="ck-kicker"><%= review_header[:heading] %></p>
|
|
102
|
+
<% if review_header[:judge_model] %>
|
|
103
|
+
<span class="ck-chip ck-chip--soft" style="text-transform: none;"><%= review_header[:judge_model] %></span>
|
|
103
104
|
<% end %>
|
|
104
105
|
</div>
|
|
105
106
|
|
|
@@ -126,7 +127,7 @@
|
|
|
126
127
|
<% elsif review.check? && !review.passed.nil? %>
|
|
127
128
|
<%= ck_check_badge(review.passed) %>
|
|
128
129
|
<% elsif review.status == "failed" %>
|
|
129
|
-
<span class="<%= ck_badge_classes(:failed) %>" title="<%= review.error_message.presence || "Judge failed" %>">Failed</span>
|
|
130
|
+
<span class="<%= ck_badge_classes(:failed) %>" title="<%= review.error_message.presence || (review.check? ? "Check failed" : "Judge failed") %>">Failed</span>
|
|
130
131
|
<% else %>
|
|
131
132
|
<span class="<%= ck_badge_classes(:pending) %>">Pending</span>
|
|
132
133
|
<% end %>
|
|
@@ -93,6 +93,14 @@
|
|
|
93
93
|
</div>
|
|
94
94
|
</div>
|
|
95
95
|
|
|
96
|
+
<div class="ck-field">
|
|
97
|
+
<label class="ck-label" for="run_max_tokens" style="position: relative;">
|
|
98
|
+
Max tokens<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup">The ceiling on how many tokens the model may generate per row. Leave it blank to use the provider's default. Set it when your prompt produces long output: if generation hits the cap the response is cut off mid-sentence, and the judge scores the truncation rather than the prompt. Match whatever your production caller uses so the evaluation reflects reality.</span>
|
|
99
|
+
</label>
|
|
100
|
+
<%= form.number_field :max_tokens, min: 1, step: 1, class: "ck-input", id: "run_max_tokens", placeholder: "Provider default", **ck_field_aria(form, :max_tokens) %>
|
|
101
|
+
<%= ck_field_error(form, :max_tokens) %>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
96
104
|
<div class="ck-field" id="judge-field">
|
|
97
105
|
<label class="ck-label" for="run_judge_model" style="position: relative;">
|
|
98
106
|
Judge model<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup">Judge models score generated responses against your metrics. Pick one when configuring a run.</span>
|
|
@@ -118,6 +126,15 @@
|
|
|
118
126
|
<% end %>
|
|
119
127
|
</div>
|
|
120
128
|
|
|
129
|
+
<div class="ck-field" id="judge-temperature-field">
|
|
130
|
+
<label class="ck-label" for="run_judge_temperature" style="position: relative;">
|
|
131
|
+
Judge temperature<span class="ck-info-toggle" tabindex="0">?</span><span class="ck-info-popup">How random the judge is when it scores. This is separate from the generation temperature above. Keep it at 0 so re-judging the same output gives the same score, which is what makes one run comparable to another. Raise it only when you are deliberately measuring how much the judge disagrees with itself.</span>
|
|
132
|
+
</label>
|
|
133
|
+
<%= form.number_field :judge_temperature, in: 0..1, step: 0.1, class: "ck-input", id: "run_judge_temperature", **ck_field_aria(form, :judge_temperature) %>
|
|
134
|
+
<%= ck_field_error(form, :judge_temperature) %>
|
|
135
|
+
<p class="ck-field-hint" id="judge-temperature-hint">0 is deterministic and recommended. Above 0, the same output can score differently on a re-judge.</p>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
121
138
|
<div class="ck-field" id="metrics-field">
|
|
122
139
|
<p class="ck-label" id="run_metrics_label">Metrics</p>
|
|
123
140
|
<% if @all_metrics.empty? %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div id="run_responses_region">
|
|
2
|
+
<p class="ck-kicker" style="margin-top: 1.5rem;<%= ' display:none;' unless responses.any? %>" id="responses_kicker">Responses</p>
|
|
3
|
+
<table class="ck-results-table ck-responses-table"<%= ' hidden'.html_safe unless responses.any? %> id="responses_table">
|
|
4
|
+
<thead>
|
|
5
|
+
<tr>
|
|
6
|
+
<th scope="col">#</th>
|
|
7
|
+
<th scope="col">Response</th>
|
|
8
|
+
<th scope="col">Metrics</th>
|
|
9
|
+
<th scope="col">Avg score</th>
|
|
10
|
+
<th scope="col">Status</th>
|
|
11
|
+
<th scope="col"></th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody id="run_responses">
|
|
15
|
+
<% responses.each_with_index do |response, idx| %>
|
|
16
|
+
<%= render "response_row", run: run, response: response, index: responses_offset + idx + 1 %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</tbody>
|
|
19
|
+
</table>
|
|
20
|
+
</div>
|
|
@@ -12,6 +12,28 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<% end %>
|
|
14
14
|
|
|
15
|
+
<% if run.scores_at_ceiling? %>
|
|
16
|
+
<div class="ck-score-ceiling-banner" role="status">
|
|
17
|
+
<div class="ck-score-ceiling-banner__body">
|
|
18
|
+
<p class="ck-kicker">Scores are clustered near the top</p>
|
|
19
|
+
<p class="ck-meta-copy">
|
|
20
|
+
A near-perfect average usually means the judge is not separating good output from bad, not that every
|
|
21
|
+
output is good. Check it against your own reading before you trust the number.
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="ck-score-ceiling-banner__actions">
|
|
25
|
+
<%= link_to "Worst first", run_path(run, sort: "score_asc"),
|
|
26
|
+
class: ck_button_classes(:light, variant: :outline) + " ck-button--sm",
|
|
27
|
+
title: "Sort the responses lowest score first to see whether the judge ever penalises anything." %>
|
|
28
|
+
<% if CompletionKit.config.judge_agreement_enabled && run.calibratable_metric %>
|
|
29
|
+
<%= link_to "Check the judge", metric_path(run.calibratable_metric, anchor: "agreement"),
|
|
30
|
+
class: ck_button_classes(:dark) + " ck-button--sm",
|
|
31
|
+
title: "Score a few outputs yourself and see how often the judge agrees with you." %>
|
|
32
|
+
<% end %>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
|
|
15
37
|
<section class="ck-page-header">
|
|
16
38
|
<div>
|
|
17
39
|
<span class="ck-status-badge ck-status-badge--<%= run.status %>">
|
|
@@ -77,6 +77,24 @@
|
|
|
77
77
|
</div>
|
|
78
78
|
<% end %>
|
|
79
79
|
|
|
80
|
+
<% if has_llm_metric && CompletionKit.config.judge_agreement_enabled %>
|
|
81
|
+
<% agreement = run.judge_agreement %>
|
|
82
|
+
<div class="ck-run-status__cell">
|
|
83
|
+
<p class="ck-run-status__metric-label">Judge agreement</p>
|
|
84
|
+
<div class="ck-run-status__cell-value <%= agreement ? "ck-run-status__cell-value--split" : "ck-run-status__cell-value--stack" %>">
|
|
85
|
+
<% if agreement %>
|
|
86
|
+
<span class="<%= ck_badge_classes(ck_pass_rate_kind(agreement[:rate])) %> ck-badge--lg"><%= (agreement[:rate] * 100).round %>%</span>
|
|
87
|
+
<span class="ck-run-status__summary-text"><%= pluralize(agreement[:sample_size], "label") %></span>
|
|
88
|
+
<% else %>
|
|
89
|
+
<span class="ck-run-status__cell-unverified">Unverified</span>
|
|
90
|
+
<% if run.calibratable_metric %>
|
|
91
|
+
<%= link_to "Check the judge →".html_safe, metric_path(run.calibratable_metric, anchor: "agreement"), class: "ck-cal-link" %>
|
|
92
|
+
<% end %>
|
|
93
|
+
<% end %>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<% end %>
|
|
97
|
+
|
|
80
98
|
<% if has_checks %>
|
|
81
99
|
<div class="ck-run-status__cell">
|
|
82
100
|
<p class="ck-run-status__metric-label">Checks passed</p>
|
|
@@ -25,20 +25,26 @@
|
|
|
25
25
|
This run was scored against metric versions that are no longer live.
|
|
26
26
|
<% stale_summary.values.each_with_index do |s, i| %>
|
|
27
27
|
<%= ", " if i > 0 %><strong><%= s[:metric_name] %></strong> (scored by <%= s[:scored_labels].join(", ") %>; live is <%= s[:current_label] %>)<% end %>.
|
|
28
|
-
Re-
|
|
28
|
+
Re-grade to refresh the scores with the current metrics.
|
|
29
29
|
</p>
|
|
30
30
|
</div>
|
|
31
31
|
<% if @run.status == "completed" %>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
<div class="ck-banner-actions">
|
|
33
|
+
<div class="ck-banner-action">
|
|
34
|
+
<%= button_to "Re-grade with current metrics",
|
|
35
|
+
regrade_run_path(@run), method: :post,
|
|
36
|
+
class: ck_button_classes(:dark), form_class: "inline-block",
|
|
37
|
+
data: { turbo_confirm: "Re-grade this run's existing responses against the current metrics?" } %>
|
|
38
|
+
<p class="ck-banner-action__note">Re-scores the responses you already have. No generation cost.</p>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="ck-banner-action">
|
|
41
|
+
<%= button_to "Re-run from scratch",
|
|
42
|
+
rerun_run_path(@run), method: :post,
|
|
43
|
+
class: ck_button_classes(:light, variant: :outline), form_class: "inline-block",
|
|
44
|
+
data: { turbo_confirm: "Create a new run with fresh responses and the current metrics? The original run stays as a record." } %>
|
|
45
|
+
<p class="ck-banner-action__note">Generates fresh responses first. Costs a full run.</p>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
42
48
|
<% end %>
|
|
43
49
|
</div>
|
|
44
50
|
<% end %>
|
|
@@ -83,6 +89,17 @@
|
|
|
83
89
|
<span class="ck-run-config__warn" style="color: var(--ck-dim);" title="The model rejected the temperature parameter, so CompletionKit re-sent the request without it.">ignored by model</span>
|
|
84
90
|
<% end %>
|
|
85
91
|
</div>
|
|
92
|
+
<div class="ck-run-config__row">
|
|
93
|
+
<span class="ck-run-config__key">Max tokens</span>
|
|
94
|
+
<span><%= @run.max_tokens || "Provider default" %></span>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="ck-run-config__row">
|
|
97
|
+
<span class="ck-run-config__key">Judge temperature</span>
|
|
98
|
+
<span><%= @run.judge_temperature %></span>
|
|
99
|
+
<% if @run.nondeterministic_judge? %>
|
|
100
|
+
<span class="ck-run-config__warn" style="color: var(--ck-warning);" title="Judging above temperature 0 makes scores irreproducible. The same output can get a different score on a re-judge.">scores not reproducible</span>
|
|
101
|
+
<% end %>
|
|
102
|
+
</div>
|
|
86
103
|
</div>
|
|
87
104
|
|
|
88
105
|
<% if @run.prompt %>
|
|
@@ -109,7 +126,7 @@
|
|
|
109
126
|
<div class="ck-prompt-preview__header">
|
|
110
127
|
<p class="ck-kicker">Output source</p>
|
|
111
128
|
</div>
|
|
112
|
-
<p class="ck-prompt-preview__text">Dataset column <code><%= @run.output_column.presence || "actual_output" %></code
|
|
129
|
+
<p class="ck-prompt-preview__text">Dataset column <code><%= @run.output_column.presence || "actual_output" %></code>. No prompt generated these outputs.</p>
|
|
113
130
|
</div>
|
|
114
131
|
<% end %>
|
|
115
132
|
|
|
@@ -186,24 +203,7 @@
|
|
|
186
203
|
|
|
187
204
|
<%= render "sort_toolbar", run: @run %>
|
|
188
205
|
|
|
189
|
-
|
|
190
|
-
<table class="ck-results-table ck-responses-table"<%= ' hidden'.html_safe unless @responses.any? %> id="responses_table">
|
|
191
|
-
<thead>
|
|
192
|
-
<tr>
|
|
193
|
-
<th scope="col">#</th>
|
|
194
|
-
<th scope="col">Response</th>
|
|
195
|
-
<th scope="col">Metrics</th>
|
|
196
|
-
<th scope="col">Avg score</th>
|
|
197
|
-
<th scope="col">Status</th>
|
|
198
|
-
<th scope="col"></th>
|
|
199
|
-
</tr>
|
|
200
|
-
</thead>
|
|
201
|
-
<tbody id="run_responses">
|
|
202
|
-
<% @responses.each_with_index do |response, idx| %>
|
|
203
|
-
<%= render "response_row", run: @run, response: response, index: @responses_offset + idx + 1 %>
|
|
204
|
-
<% end %>
|
|
205
|
-
</tbody>
|
|
206
|
-
</table>
|
|
206
|
+
<%= render "responses_region", run: @run, responses: @responses, responses_offset: @responses_offset %>
|
|
207
207
|
|
|
208
208
|
<% if @responses_total_pages > 1 %>
|
|
209
209
|
<nav class="ck-pagination" aria-label="Response pages">
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateCompletionKitPromptServes < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :completion_kit_prompt_serves do |t|
|
|
4
|
+
t.integer :prompt_id
|
|
5
|
+
t.string :family_key, null: false
|
|
6
|
+
t.date :served_on, null: false
|
|
7
|
+
t.integer :serve_count, null: false, default: 0
|
|
8
|
+
t.datetime :last_served_at
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :completion_kit_prompt_serves, [:prompt_id, :served_on],
|
|
13
|
+
unique: true, name: "index_ck_prompt_serves_on_prompt_and_day"
|
|
14
|
+
add_index :completion_kit_prompt_serves, [:family_key, :served_on],
|
|
15
|
+
name: "index_ck_prompt_serves_on_family_and_day"
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/completion_kit.rb
CHANGED
|
@@ -53,8 +53,11 @@ module CompletionKit
|
|
|
53
53
|
yield(config) if block_given?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# current_prompt_payload and render_current_prompt both come through here,
|
|
57
|
+
# so this is the one place a library-side fetch is counted. The REST show
|
|
58
|
+
# action records its own, since it can also resolve by numeric id.
|
|
56
59
|
def current_prompt(identifier)
|
|
57
|
-
Prompt.current_for(identifier)
|
|
60
|
+
Prompt.current_for(identifier).tap { |prompt| PromptServe.record!(prompt) }
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def current_prompt_payload(identifier)
|
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.34
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -173,7 +173,7 @@ description: CompletionKit is a prompt testing platform that runs as a Rails eng
|
|
|
173
173
|
or a standalone app. Run prompts against real datasets, score every output with
|
|
174
174
|
an LLM judge against criteria you define, track prompt versions, and get AI-generated
|
|
175
175
|
improvement suggestions grounded in your actual results. Includes a web UI, REST
|
|
176
|
-
API, and a built-in MCP server with
|
|
176
|
+
API, and a built-in MCP server with 53 tools.
|
|
177
177
|
email:
|
|
178
178
|
- damien@homemade.software
|
|
179
179
|
executables: []
|
|
@@ -232,6 +232,7 @@ files:
|
|
|
232
232
|
- app/jobs/completion_kit/model_discovery_job.rb
|
|
233
233
|
- app/jobs/completion_kit/prompt_suggestion_job.rb
|
|
234
234
|
- app/jobs/completion_kit/run_completion_check_job.rb
|
|
235
|
+
- app/jobs/completion_kit/start_run_job.rb
|
|
235
236
|
- app/mailers/completion_kit/application_mailer.rb
|
|
236
237
|
- app/models/completion_kit/agreement.rb
|
|
237
238
|
- app/models/completion_kit/application_record.rb
|
|
@@ -244,6 +245,7 @@ files:
|
|
|
244
245
|
- app/models/completion_kit/metric_version.rb
|
|
245
246
|
- app/models/completion_kit/model.rb
|
|
246
247
|
- app/models/completion_kit/prompt.rb
|
|
248
|
+
- app/models/completion_kit/prompt_serve.rb
|
|
247
249
|
- app/models/completion_kit/provider_credential.rb
|
|
248
250
|
- app/models/completion_kit/response.rb
|
|
249
251
|
- app/models/completion_kit/review.rb
|
|
@@ -303,6 +305,7 @@ files:
|
|
|
303
305
|
- app/services/completion_kit/prompt_improvement_validator.rb
|
|
304
306
|
- app/services/completion_kit/promptfoo_importer.rb
|
|
305
307
|
- app/services/completion_kit/provider_endpoint.rb
|
|
308
|
+
- app/services/completion_kit/response_query.rb
|
|
306
309
|
- app/services/completion_kit/run_comparison.rb
|
|
307
310
|
- app/services/completion_kit/starter_metrics.rb
|
|
308
311
|
- app/services/completion_kit/worker_health.rb
|
|
@@ -368,6 +371,7 @@ files:
|
|
|
368
371
|
- app/views/completion_kit/runs/_compare_score.html.erb
|
|
369
372
|
- app/views/completion_kit/runs/_form.html.erb
|
|
370
373
|
- app/views/completion_kit/runs/_response_row.html.erb
|
|
374
|
+
- app/views/completion_kit/runs/_responses_region.html.erb
|
|
371
375
|
- app/views/completion_kit/runs/_row.html.erb
|
|
372
376
|
- app/views/completion_kit/runs/_sort_toolbar.html.erb
|
|
373
377
|
- app/views/completion_kit/runs/_status_header.html.erb
|
|
@@ -440,6 +444,9 @@ files:
|
|
|
440
444
|
- db/migrate/20260708000001_add_api_version_to_completion_kit_provider_credentials.rb
|
|
441
445
|
- db/migrate/20260713000001_add_catalog_model_count_to_completion_kit_provider_credentials.rb
|
|
442
446
|
- db/migrate/20260715000001_dedupe_and_unique_index_metric_versions.rb
|
|
447
|
+
- db/migrate/20260728000001_add_max_tokens_to_completion_kit_runs.rb
|
|
448
|
+
- db/migrate/20260730000001_add_judge_temperature_to_completion_kit_runs.rb
|
|
449
|
+
- db/migrate/20260730000002_create_completion_kit_prompt_serves.rb
|
|
443
450
|
- lib/completion-kit.rb
|
|
444
451
|
- lib/completion_kit.rb
|
|
445
452
|
- lib/completion_kit/concurrency_check.rb
|