completion-kit 0.5.9 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e1641413d1ed8d27bb6344094b788f33ab521e4d738bf1468fba148185a58f6
4
- data.tar.gz: 60b3676c0b8100430a7841a6281b5656184198583b40565c0ef5f7b24663ebc4
3
+ metadata.gz: 64a36635f11bc5109afed180697b763a1c8231347b4912aa452271fd19694a45
4
+ data.tar.gz: 366ef22faa5914db74402b7ae1c9769f50f14ae8204e2144a245b8d29f89b464
5
5
  SHA512:
6
- metadata.gz: 003f3af4e5eaa28bc5c9b800e3788a63134a8bc9750d170ed03c6c1707b2e57675c79e25994abd763fb133eaec484f07133e12a74be9179f90c360b1d290dafd
7
- data.tar.gz: 6b73b15e6c1eb9af3d8f5228997553a7489db9c948a2e7298d2c9b40eb19b27a431af84b52368716bac0705f4c62acc2b5b7a8f937192445416b131c59b661f9
6
+ metadata.gz: f84d7a7e0bcd5a3a054e84231cb390026dd07ac469b34ff8bb8a10a6f9f77fbbcb53748b4814d7b86aa94d1e67f41f3ad1e9c8eb1907f0ac56388169765174d6
7
+ data.tar.gz: ab627c627f3c88d1ef4a8f14afa0d72d7a92acc83b1107a4ebfeb25dacaf7a04282dfe3cfa5657d3c474171e508dec9944db95da526cbe8b3d94bbd0a9ef32a7
@@ -126,13 +126,21 @@ form.button_to {
126
126
  font-weight: 700;
127
127
  letter-spacing: 0.02em;
128
128
  text-decoration: none;
129
- color: var(--ck-accent);
129
+ color: #3AD0E6;
130
130
  }
131
131
 
132
132
  .ck-brand img {
133
133
  display: block;
134
134
  }
135
135
 
136
+ .ck-brand__name {
137
+ padding-top: 0.75rem;
138
+ }
139
+
140
+ .ck-brand__kit {
141
+ color: #AFEDF7;
142
+ }
143
+
136
144
  .ck-topbar__copy {
137
145
  display: none;
138
146
  }
@@ -262,7 +270,7 @@ form.button_to {
262
270
  .ck-meta-copy,
263
271
  .ck-note,
264
272
  .ck-hint {
265
- font-size: 0.95rem;
273
+ font-size: 0.9rem;
266
274
  line-height: 1.6;
267
275
  }
268
276
 
@@ -1269,6 +1277,13 @@ tr:hover .ck-chip--publish {
1269
1277
  color: var(--ck-accent);
1270
1278
  }
1271
1279
 
1280
+ /* the main prompt template block on prompts/show — bigger padding + a
1281
+ touch more line-height since this is the page's primary content */
1282
+ .ck-code--prompt {
1283
+ padding: 1.5rem;
1284
+ line-height: 1.75;
1285
+ }
1286
+
1272
1287
  .ck-note-box {
1273
1288
  background: var(--ck-surface-soft);
1274
1289
  border: 1px solid var(--ck-line);
@@ -1855,7 +1870,18 @@ tr:hover .ck-chip--publish {
1855
1870
  background: var(--ck-bg-strong);
1856
1871
  overflow: auto;
1857
1872
  max-height: 60vh;
1873
+ scrollbar-width: thin;
1874
+ scrollbar-color: var(--ck-line-strong) transparent;
1858
1875
  }
1876
+ .ck-csv-table-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
1877
+ .ck-csv-table-wrap::-webkit-scrollbar-track { background: transparent; }
1878
+ .ck-csv-table-wrap::-webkit-scrollbar-thumb {
1879
+ background: var(--ck-line-strong);
1880
+ border-radius: 5px;
1881
+ border: 2px solid var(--ck-bg-strong);
1882
+ }
1883
+ .ck-csv-table-wrap::-webkit-scrollbar-thumb:hover { background: var(--ck-muted); }
1884
+ .ck-csv-table-wrap::-webkit-scrollbar-corner { background: transparent; }
1859
1885
 
1860
1886
  .ck-modal__body .ck-csv-table-wrap {
1861
1887
  margin-top: 0;
@@ -2763,8 +2789,10 @@ select.ck-input {
2763
2789
 
2764
2790
  /* the metrics field stacks several sub-sections (hint, groups, divider, tag
2765
2791
  filter, checkboxes) — give it more vertical breathing room than a plain field,
2766
- and extra separation from the run-tags field that follows it */
2767
- #metrics-field {
2792
+ and extra separation from the run-tags field that follows it. Only when the
2793
+ checkboxes are actually present, though — when there are no metrics the field
2794
+ is just "label + warning" and should be a normal compact field. */
2795
+ #metrics-field:has(.ck-metric-checkboxes) {
2768
2796
  gap: 0.85rem;
2769
2797
  margin-bottom: 1.25rem;
2770
2798
  }
@@ -3571,7 +3599,7 @@ a.ck-metric-group-pill {
3571
3599
  }
3572
3600
 
3573
3601
  .ck-mcp-tool__desc {
3574
- font-size: 0.8rem;
3602
+ font-size: 0.9rem;
3575
3603
  color: var(--ck-muted);
3576
3604
  }
3577
3605
 
@@ -3595,7 +3623,7 @@ a.ck-metric-group-pill {
3595
3623
  gap: 0.5rem;
3596
3624
  padding: 0.6rem 0.85rem;
3597
3625
  font-family: var(--ck-mono);
3598
- font-size: 0.78rem;
3626
+ font-size: 0.9rem;
3599
3627
  font-weight: 500;
3600
3628
  color: var(--ck-text);
3601
3629
  border-bottom: 1px solid var(--ck-line);
@@ -3669,7 +3697,7 @@ a.ck-metric-group-pill {
3669
3697
  }
3670
3698
 
3671
3699
  .ck-api-prompt-card__desc {
3672
- font-size: 0.78rem;
3700
+ font-size: 0.9rem;
3673
3701
  color: var(--ck-muted);
3674
3702
  margin: 0.2rem 0 0;
3675
3703
  line-height: 1.4;
@@ -2,6 +2,12 @@ module CompletionKit
2
2
  class ApiReferenceController < ApplicationController
3
3
  def index
4
4
  @published_prompts = Prompt.current_versions.order(name: :asc)
5
+ @recent_runs = Run.includes(:prompt).order(created_at: :desc).limit(10)
6
+ @datasets = Dataset.order(name: :asc)
7
+ @metrics = Metric.order(name: :asc)
8
+ @metric_groups = MetricGroup.includes(:metrics).order(name: :asc)
9
+ @tags = Tag.order(name: :asc)
10
+ @provider_credentials = ProviderCredential.order(:provider)
5
11
  @token = CompletionKit.config.api_token
6
12
  @base_url = request.base_url + request.script_name
7
13
  end
@@ -9,6 +9,16 @@ module CompletionKit
9
9
 
10
10
  def show
11
11
  @runs = @dataset.runs.includes(:prompt, :responses).order(created_at: :desc)
12
+ respond_to do |format|
13
+ format.html
14
+ format.csv do
15
+ slug = @dataset.name.to_s.parameterize.presence || "dataset-#{@dataset.id}"
16
+ send_data @dataset.csv_data.to_s,
17
+ type: "text/csv",
18
+ filename: "#{slug}.csv",
19
+ disposition: "attachment"
20
+ end
21
+ end
12
22
  end
13
23
 
14
24
  def new
@@ -17,7 +17,7 @@ module CompletionKit
17
17
  end
18
18
 
19
19
  session_id = request.headers["Mcp-Session-Id"]
20
- unless session_id && Rails.cache.exist?("mcp_session:#{session_id}")
20
+ unless McpSession.active?(session_id)
21
21
  render json: jsonrpc_error(request_body["id"], -32000, "Session not initialized. Send initialize first."), status: :bad_request
22
22
  return
23
23
  end
@@ -40,7 +40,7 @@ module CompletionKit
40
40
 
41
41
  def destroy
42
42
  session_id = request.headers["Mcp-Session-Id"]
43
- Rails.cache.delete("mcp_session:#{session_id}") if session_id
43
+ McpSession.destroy_session(session_id) if session_id
44
44
  head :ok
45
45
  end
46
46
 
@@ -0,0 +1,29 @@
1
+ module CompletionKit
2
+ # MCP session marker — one row per active client session, kept in the
3
+ # database so sessions survive Puma restarts, deploys, and Rails.cache
4
+ # eviction. Expired rows are opportunistically pruned on every new
5
+ # session start, so the table stays bounded by recent activity.
6
+ class McpSession < ApplicationRecord
7
+ self.table_name = "completion_kit_mcp_sessions"
8
+
9
+ SESSION_TTL = 1.hour
10
+
11
+ def self.start!
12
+ prune_expired!
13
+ create!(session_id: SecureRandom.uuid, expires_at: SESSION_TTL.from_now).session_id
14
+ end
15
+
16
+ def self.active?(session_id)
17
+ return false if session_id.blank?
18
+ where(session_id: session_id).where("expires_at > ?", Time.current).exists?
19
+ end
20
+
21
+ def self.destroy_session(session_id)
22
+ where(session_id: session_id).delete_all
23
+ end
24
+
25
+ def self.prune_expired!
26
+ where("expires_at < ?", Time.current).delete_all
27
+ end
28
+ end
29
+ end
@@ -6,10 +6,8 @@ module CompletionKit
6
6
  PROTOCOL_VERSION = "2025-03-26"
7
7
 
8
8
  def self.initialize_session
9
- session_id = SecureRandom.uuid
10
- Rails.cache.write("mcp_session:#{session_id}", true, expires_in: 1.hour)
11
9
  {
12
- session_id: session_id,
10
+ session_id: McpSession.start!,
13
11
  protocolVersion: PROTOCOL_VERSION,
14
12
  serverInfo: {name: "CompletionKit", version: CompletionKit::VERSION},
15
13
  capabilities: {tools: {listChanged: false}}
@@ -2,6 +2,12 @@
2
2
  token = local_assigns.fetch(:token, "YOUR_TOKEN")
3
3
  real_token = local_assigns.fetch(:real_token, nil)
4
4
  published_prompts = local_assigns.fetch(:published_prompts, [])
5
+ recent_runs = local_assigns.fetch(:recent_runs, [])
6
+ datasets = local_assigns.fetch(:datasets, [])
7
+ metrics = local_assigns.fetch(:metrics, [])
8
+ metric_groups = local_assigns.fetch(:metric_groups, [])
9
+ tags = local_assigns.fetch(:tags, [])
10
+ provider_credentials = local_assigns.fetch(:provider_credentials, [])
5
11
  %>
6
12
  <div class="ck-api-tabs">
7
13
  <input type="radio" name="ck-api-tab" id="ck-tab-mcp" class="ck-api-tabs__radio" checked>
@@ -86,28 +92,11 @@
86
92
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts/:id</p>
87
93
  <p class="ck-meta-copy">Get a single prompt by ID.</p>
88
94
  </div>
89
- <% if published_prompts.any? %>
90
- <div class="ck-api-endpoint" style="padding-top: 1rem;">
91
- <p class="ck-kicker" style="margin-bottom: 0.5rem;">Your published prompts</p>
92
- <% published_prompts.each do |p| %>
93
- <div class="ck-api-prompt-card">
94
- <div class="ck-api-prompt-card__top">
95
- <div>
96
- <strong class="ck-api-prompt-card__name"><%= p.name %></strong>
97
- <% if p.description.present? %>
98
- <p class="ck-api-prompt-card__desc"><%= p.description %></p>
99
- <% end %>
100
- </div>
101
- <span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= p.llm_model %></span>
102
- </div>
103
- <div class="ck-api-prompt-card__url">
104
- <code class="ck-endpoint__url" id="prompt_ep_<%= p.id %>"><%= base_url %>/api/v1/prompts/<%= p.slug %></code>
105
- <button type="button" class="ck-icon-btn" title="Copy endpoint" aria-label="Copy endpoint URL" onclick="navigator.clipboard.writeText(document.getElementById('prompt_ep_<%= p.id %>').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
106
- </div>
107
- </div>
108
- <% end %>
109
- </div>
110
- <% end %>
95
+ <%= render "completion_kit/api_reference/resource_list", title: "Your published prompts",
96
+ items: published_prompts.map { |p|
97
+ { name: p.name, subtitle: p.description, meta: p.llm_model,
98
+ url: "#{base_url}/api/v1/prompts/#{p.slug}", dom_id: "prompt_ep_#{p.id}" }
99
+ } %>
111
100
  <div class="ck-api-endpoint">
112
101
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/prompts/:id</p>
113
102
  <p class="ck-meta-copy">Update a prompt. Accepts same params as create.</p>
@@ -140,6 +129,11 @@
140
129
  <p class="ck-meta-copy">Get a run with status, progress, response count, and average score.</p>
141
130
  <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl #{base_url}/api/v1/runs/1 \\\n -H \"Authorization: Bearer #{token}\"" %>
142
131
  </div>
132
+ <%= render "completion_kit/api_reference/resource_list", title: "Your recent runs",
133
+ items: recent_runs.map { |r|
134
+ { name: r.name, meta: r.status.to_s.titleize,
135
+ url: "#{base_url}/api/v1/runs/#{r.id}", dom_id: "run_ep_#{r.id}" }
136
+ } %>
143
137
  <div class="ck-api-endpoint">
144
138
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs/:id/generate</p>
145
139
  <p class="ck-meta-copy">Start generating responses. Returns 202 Accepted. Poll the run to check progress.</p>
@@ -167,6 +161,11 @@
167
161
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses/:id</p>
168
162
  <p class="ck-meta-copy">Get a single response with its review scores and feedback.</p>
169
163
  </div>
164
+ <%= render "completion_kit/api_reference/resource_list", title: "Your responses",
165
+ items: recent_runs.first(1).map { |r|
166
+ { name: "#{r.name} — responses",
167
+ url: "#{base_url}/api/v1/runs/#{r.id}/responses", dom_id: "responses_ep_#{r.id}" }
168
+ } %>
170
169
  </div>
171
170
 
172
171
  <div class="ck-api-tabs__panel">
@@ -186,6 +185,11 @@
186
185
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span>&ensp;<span class="ck-chip ck-chip--soft">PATCH</span>&ensp;<span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/datasets/:id</p>
187
186
  <p class="ck-meta-copy">Get, update, or delete a dataset.</p>
188
187
  </div>
188
+ <%= render "completion_kit/api_reference/resource_list", title: "Your datasets",
189
+ items: datasets.map { |d|
190
+ { name: d.name, meta: pluralize([d.csv_data.to_s.lines.count - 1, 0].max, "row"),
191
+ url: "#{base_url}/api/v1/datasets/#{d.id}", dom_id: "dataset_ep_#{d.id}" }
192
+ } %>
189
193
  </div>
190
194
 
191
195
  <div class="ck-api-tabs__panel">
@@ -205,6 +209,11 @@
205
209
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span>&ensp;<span class="ck-chip ck-chip--soft">PATCH</span>&ensp;<span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/metrics/:id</p>
206
210
  <p class="ck-meta-copy">Get, update, or delete a metric.</p>
207
211
  </div>
212
+ <%= render "completion_kit/api_reference/resource_list", title: "Your metrics",
213
+ items: metrics.map { |m|
214
+ { name: m.name, subtitle: m.instruction.presence&.truncate(100),
215
+ url: "#{base_url}/api/v1/metrics/#{m.id}", dom_id: "metric_ep_#{m.id}" }
216
+ } %>
208
217
  </div>
209
218
 
210
219
  <div class="ck-api-tabs__panel">
@@ -223,6 +232,11 @@
223
232
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span>&ensp;<span class="ck-chip ck-chip--soft">PATCH</span>&ensp;<span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/metric_groups/:id</p>
224
233
  <p class="ck-meta-copy">Get, update, or delete a metric group. PATCH with <code>metric_ids</code> replaces all metric associations.</p>
225
234
  </div>
235
+ <%= render "completion_kit/api_reference/resource_list", title: "Your metric groups",
236
+ items: metric_groups.map { |g|
237
+ { name: g.name, subtitle: g.description.presence, meta: pluralize(g.metric_ids.size, "metric"),
238
+ url: "#{base_url}/api/v1/metric_groups/#{g.id}", dom_id: "metric_group_ep_#{g.id}" }
239
+ } %>
226
240
  </div>
227
241
 
228
242
  <div class="ck-api-tabs__panel">
@@ -243,6 +257,11 @@
243
257
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span>&ensp;<span class="ck-chip ck-chip--soft">PATCH</span>&ensp;<span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/tags/:id</p>
244
258
  <p class="ck-meta-copy">Get, update, or delete a tag. PATCH accepts <code>name</code>. DELETE returns 204 No Content and removes all taggings for this tag.</p>
245
259
  </div>
260
+ <%= render "completion_kit/api_reference/resource_list", title: "Your tags",
261
+ items: tags.map { |t|
262
+ { name: t.name, meta: t.color,
263
+ url: "#{base_url}/api/v1/tags/#{t.id}", dom_id: "tag_ep_#{t.id}" }
264
+ } %>
246
265
  <div class="ck-api-endpoint" style="padding-top: 1rem;">
247
266
  <p class="ck-kicker" style="margin-bottom: 0.5rem;">Tagging resources</p>
248
267
  <p class="ck-meta-copy">Metrics, prompts, runs, and datasets accept a <code>tag_names</code> array on their create and update endpoints. Passing a name that does not yet exist silently creates the tag. On PATCH, the list replaces all existing tags for that record (omit the field to leave tags unchanged).</p>
@@ -277,6 +296,11 @@
277
296
  <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span>&ensp;<span class="ck-chip ck-chip--soft">PATCH</span>&ensp;<span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/provider_credentials/:id</p>
278
297
  <p class="ck-meta-copy">Get, update, or delete a provider credential.</p>
279
298
  </div>
299
+ <%= render "completion_kit/api_reference/resource_list", title: "Your providers",
300
+ items: provider_credentials.map { |pc|
301
+ { name: ck_provider_label(pc.provider), meta: pluralize(pc.model_count, "model"),
302
+ url: "#{base_url}/api/v1/provider_credentials/#{pc.id}", dom_id: "provider_ep_#{pc.id}" }
303
+ } %>
280
304
  </div>
281
305
 
282
306
  </div>
@@ -0,0 +1,24 @@
1
+ <%
2
+ name = local_assigns.fetch(:name)
3
+ url = local_assigns.fetch(:url)
4
+ dom_id = local_assigns.fetch(:dom_id)
5
+ subtitle = local_assigns[:subtitle]
6
+ meta = local_assigns[:meta]
7
+ %>
8
+ <div class="ck-api-prompt-card">
9
+ <div class="ck-api-prompt-card__top">
10
+ <div>
11
+ <strong class="ck-api-prompt-card__name"><%= name %></strong>
12
+ <% if subtitle.present? %>
13
+ <p class="ck-api-prompt-card__desc"><%= subtitle %></p>
14
+ <% end %>
15
+ </div>
16
+ <% if meta.present? %>
17
+ <span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= meta %></span>
18
+ <% end %>
19
+ </div>
20
+ <div class="ck-api-prompt-card__url">
21
+ <code class="ck-endpoint__url" id="<%= dom_id %>"><%= url %></code>
22
+ <button type="button" class="ck-icon-btn" title="Copy endpoint" aria-label="Copy endpoint URL" onclick="navigator.clipboard.writeText(document.getElementById('<%= dom_id %>').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
23
+ </div>
24
+ </div>
@@ -0,0 +1,10 @@
1
+ <%# title: heading text; items: array of hashes for _resource_card (name:, url:, dom_id:, subtitle?:, meta?:) %>
2
+ <% items = Array(local_assigns.fetch(:items, [])) %>
3
+ <% if items.any? %>
4
+ <div class="ck-api-endpoint" style="padding-top: 1rem;">
5
+ <p class="ck-kicker" style="margin-bottom: 0.5rem;"><%= local_assigns.fetch(:title) %></p>
6
+ <% items.each do |item| %>
7
+ <%= render "completion_kit/api_reference/resource_card", item %>
8
+ <% end %>
9
+ </div>
10
+ <% end %>
@@ -20,4 +20,10 @@
20
20
  base_url: @base_url,
21
21
  token: ck_masked_token(@token),
22
22
  real_token: @token,
23
- published_prompts: @published_prompts %>
23
+ published_prompts: @published_prompts,
24
+ recent_runs: @recent_runs,
25
+ datasets: @datasets,
26
+ metrics: @metrics,
27
+ metric_groups: @metric_groups,
28
+ tags: @tags,
29
+ provider_credentials: @provider_credentials %>
@@ -8,6 +8,7 @@
8
8
  <h1 class="ck-title"><%= @dataset.name %></h1>
9
9
  </div>
10
10
  <div class="ck-actions">
11
+ <%= link_to "Download CSV", dataset_path(@dataset, format: :csv), class: ck_button_classes(:light, variant: :outline) %>
11
12
  <%= link_to "Edit", edit_dataset_path(@dataset), class: ck_button_classes(:light, variant: :outline) %>
12
13
  </div>
13
14
  </section>
@@ -67,23 +68,6 @@
67
68
  <% if @runs.any? %>
68
69
  <section class="ck-card--spaced">
69
70
  <p class="ck-kicker">Runs</p>
70
-
71
- <table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
72
- <thead>
73
- <tr>
74
- <th>Run</th>
75
- <th>Responses</th>
76
- <th>Metrics</th>
77
- <th>Avg score</th>
78
- <th>When</th>
79
- <th></th>
80
- </tr>
81
- </thead>
82
- <tbody>
83
- <% @runs.each do |run| %>
84
- <%= render "completion_kit/runs/row", run: run %>
85
- <% end %>
86
- </tbody>
87
- </table>
71
+ <%= render "completion_kit/runs/table", runs: @runs %>
88
72
  </section>
89
73
  <% end %>
@@ -17,6 +17,11 @@
17
17
  <code class="ck-endpoint__url" id="prompt_endpoint"><%= request.base_url %><%= api_v1_prompt_path(@prompt.slug) %></code>
18
18
  <button type="button" class="ck-icon-btn" title="Copy endpoint" onclick="navigator.clipboard.writeText(document.getElementById('prompt_endpoint').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
19
19
  </div>
20
+ <% if @prompt.tags.any? %>
21
+ <div class="tag-marks-row">
22
+ <%= render "completion_kit/tags/marks", tags: @prompt.tags %>
23
+ </div>
24
+ <% end %>
20
25
  </div>
21
26
 
22
27
  <div class="ck-actions">
@@ -26,13 +31,7 @@
26
31
  </div>
27
32
  </section>
28
33
 
29
- <% if @prompt.tags.any? %>
30
- <div class="tag-marks-row tag-marks-row--header">
31
- <%= render "completion_kit/tags/marks", tags: @prompt.tags %>
32
- </div>
33
- <% end %>
34
-
35
- <section>
34
+ <section class="ck-card--spaced">
36
35
  <div class="ck-prompt-preview__header">
37
36
  <p class="ck-kicker">Prompt</p>
38
37
  <% judged_run = @runs.select { |r| r.prompt_id == @prompt.id && r.status == "completed" }.find { |r| r.responses.joins(:reviews).exists? } %>
@@ -45,7 +44,7 @@
45
44
  <p class="ck-hint"><%= link_to "Run a test with judging configured", new_run_path(prompt_id: @prompt.id), class: "ck-link" %> to get AI-suggested improvements.</p>
46
45
  <% end %>
47
46
  </div>
48
- <pre class="ck-code ck-code--dark"><%= @prompt.template %></pre>
47
+ <pre class="ck-code ck-code--dark ck-code--prompt"><%= @prompt.template %></pre>
49
48
  </section>
50
49
 
51
50
  <% versions = @prompt.family_versions.includes(runs: { responses: :reviews }).to_a %>
@@ -140,24 +139,7 @@
140
139
  <% if @runs.any? %>
141
140
  <section class="ck-card--spaced">
142
141
  <p class="ck-kicker">Runs</p>
143
-
144
- <table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
145
- <thead>
146
- <tr>
147
- <th>Run</th>
148
- <th>Responses</th>
149
- <th>Metrics</th>
150
- <th>Avg score</th>
151
- <th>When</th>
152
- <th></th>
153
- </tr>
154
- </thead>
155
- <tbody>
156
- <% @runs.each do |run| %>
157
- <%= render "completion_kit/runs/row", run: run %>
158
- <% end %>
159
- </tbody>
160
- </table>
142
+ <%= render "completion_kit/runs/table", runs: @runs %>
161
143
  </section>
162
144
  <% end %>
163
145
 
@@ -93,10 +93,10 @@
93
93
 
94
94
  <div class="ck-field" id="metrics-field">
95
95
  <label class="ck-label">Metrics</label>
96
- <p class="ck-field-hint" id="metrics-hint"></p>
97
96
  <% if @all_metrics.empty? %>
98
97
  <p class="ck-field-hint" style="color: var(--ck-warning);">No metrics yet.&ensp;<%= link_to "Create a metric", new_metric_path, class: "ck-link" %></p>
99
98
  <% else %>
99
+ <p class="ck-field-hint" id="metrics-hint"></p>
100
100
  <% if @metric_groups.any? %>
101
101
  <div class="ck-metric-groups">
102
102
  <span class="ck-metric-groups__label">Groups</span>
@@ -0,0 +1,19 @@
1
+ <%# Renders a runs table. Locals: runs: enumerable of CompletionKit::Run. %>
2
+ <% runs = local_assigns.fetch(:runs) %>
3
+ <table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
4
+ <thead>
5
+ <tr>
6
+ <th>Run</th>
7
+ <th>Responses</th>
8
+ <th>Metrics</th>
9
+ <th>Avg score</th>
10
+ <th>When</th>
11
+ <th></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% runs.each do |run| %>
16
+ <%= render "completion_kit/runs/row", run: run %>
17
+ <% end %>
18
+ </tbody>
19
+ </table>
@@ -14,23 +14,7 @@
14
14
  base_path: runs_path %>
15
15
 
16
16
  <% if @runs.any? %>
17
- <table class="ck-results-table ck-runs-table">
18
- <thead>
19
- <tr>
20
- <th>Run</th>
21
- <th>Responses</th>
22
- <th>Metrics</th>
23
- <th>Avg score</th>
24
- <th>When</th>
25
- <th></th>
26
- </tr>
27
- </thead>
28
- <tbody>
29
- <% @runs.each do |run| %>
30
- <%= render "row", run: run %>
31
- <% end %>
32
- </tbody>
33
- </table>
17
+ <%= render "completion_kit/runs/table", runs: @runs %>
34
18
  <% elsif @selected_tags.any? %>
35
19
  <div class="ck-empty">
36
20
  <p>No runs match these tags. <%= link_to "Clear filters", runs_path, class: "ck-link" %>.</p>
@@ -6,7 +6,7 @@
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
8
8
 
9
- <%= favicon_link_tag "completion_kit/logo.svg", type: "image/svg+xml" %>
9
+ <%= favicon_link_tag "completion_kit/favicon.ico" %>
10
10
  <%= stylesheet_link_tag "completion_kit/application", media: "all" %>
11
11
  <%= javascript_include_tag "turbo", type: "module" %>
12
12
  <%= javascript_include_tag "completion_kit/application", defer: true %>
@@ -15,7 +15,7 @@
15
15
  <body class="ck-app">
16
16
  <header class="ck-topbar">
17
17
  <div class="ck-wrap ck-topbar__inner">
18
- <%= link_to (main_app.respond_to?(:root_path) ? main_app.root_path : prompts_path), class: "ck-brand" do %><%= image_tag "completion_kit/logo.svg", alt: "CompletionKit", style: "height: 52px; width: auto; margin-bottom: 10px;" %> CompletionKit<% end %>
18
+ <%= link_to (main_app.respond_to?(:root_path) ? main_app.root_path : prompts_path), class: "ck-brand" do %><%= image_tag "completion_kit/logo.png", alt: "CompletionKit", style: "height: 64px; width: auto;" %><span class="ck-brand__name">Completion<span class="ck-brand__kit">Kit</span></span><% end %>
19
19
 
20
20
  <nav class="ck-nav">
21
21
  <% active = ->(path) { request.path.start_with?(path) ? ck_button_classes(:dark) : ck_button_classes(:light, variant: :outline) } %>
@@ -0,0 +1,12 @@
1
+ class CreateCompletionKitMcpSessions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :completion_kit_mcp_sessions do |t|
4
+ t.string :session_id, null: false
5
+ t.datetime :expires_at, null: false
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :completion_kit_mcp_sessions, :session_id, unique: true
10
+ add_index :completion_kit_mcp_sessions, :expires_at
11
+ end
12
+ end
@@ -11,7 +11,8 @@ module CompletionKit
11
11
  app.config.assets.precompile += %w(
12
12
  completion_kit/application.css
13
13
  completion_kit/application.js
14
- completion_kit/logo.svg
14
+ completion_kit/logo.png
15
+ completion_kit/favicon.ico
15
16
  )
16
17
  end
17
18
 
@@ -1,3 +1,3 @@
1
1
  module CompletionKit
2
- VERSION = "0.5.9"
2
+ VERSION = "0.5.10"
3
3
  end
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.5.9
4
+ version: 0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damien Bastin
@@ -227,7 +227,8 @@ files:
227
227
  - Rakefile
228
228
  - app/assets/config/completion_kit_manifest.js
229
229
  - app/assets/config/manifest.js
230
- - app/assets/images/completion_kit/logo.svg
230
+ - app/assets/images/completion_kit/favicon.ico
231
+ - app/assets/images/completion_kit/logo.png
231
232
  - app/assets/javascripts/completion_kit/application.js
232
233
  - app/assets/stylesheets/completion_kit/application.css
233
234
  - app/controllers/completion_kit/api/v1/base_controller.rb
@@ -262,6 +263,7 @@ files:
262
263
  - app/mailers/completion_kit/application_mailer.rb
263
264
  - app/models/completion_kit/application_record.rb
264
265
  - app/models/completion_kit/dataset.rb
266
+ - app/models/completion_kit/mcp_session.rb
265
267
  - app/models/completion_kit/metric.rb
266
268
  - app/models/completion_kit/metric_group.rb
267
269
  - app/models/completion_kit/metric_group_membership.rb
@@ -303,6 +305,8 @@ files:
303
305
  - app/views/completion_kit/api_reference/_authentication.html.erb
304
306
  - app/views/completion_kit/api_reference/_body.html.erb
305
307
  - app/views/completion_kit/api_reference/_example.html.erb
308
+ - app/views/completion_kit/api_reference/_resource_card.html.erb
309
+ - app/views/completion_kit/api_reference/_resource_list.html.erb
306
310
  - app/views/completion_kit/api_reference/index.html.erb
307
311
  - app/views/completion_kit/datasets/_form.html.erb
308
312
  - app/views/completion_kit/datasets/edit.html.erb
@@ -339,6 +343,7 @@ files:
339
343
  - app/views/completion_kit/runs/_sort_toolbar.html.erb
340
344
  - app/views/completion_kit/runs/_status_header.html.erb
341
345
  - app/views/completion_kit/runs/_status_panel.html.erb
346
+ - app/views/completion_kit/runs/_table.html.erb
342
347
  - app/views/completion_kit/runs/edit.html.erb
343
348
  - app/views/completion_kit/runs/index.html.erb
344
349
  - app/views/completion_kit/runs/new.html.erb
@@ -375,6 +380,7 @@ files:
375
380
  - db/migrate/20260507150000_add_temperature_ignored_to_runs.rb
376
381
  - db/migrate/20260509000001_create_completion_kit_tags.rb
377
382
  - db/migrate/20260509000002_create_completion_kit_taggings.rb
383
+ - db/migrate/20260513000001_create_completion_kit_mcp_sessions.rb
378
384
  - lib/completion-kit.rb
379
385
  - lib/completion_kit.rb
380
386
  - lib/completion_kit/concurrency_check.rb
@@ -1,6 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="2.4 2.3 88.74 89.1" width="512" height="512">
2
- <path d="M57.7,56.4c1.2-1.2,1.8-3.1,1.4-5c-0.5-2-2.1-3.6-4.1-4c-3.5-0.7-6.6,1.9-6.6,5.3c0,1.5,0.6,2.8,1.5,3.8H37.4l0-12.6c-1.2,1.2-3.1,1.8-5,1.4c-2-0.5-3.6-2.1-4-4.1c-0.7-3.5,1.9-6.6,5.3-6.6c1.5,0,2.8,0.6,3.8,1.5V23.5H10.1c-3.1,0-5.7,2.5-5.7,5.7v54.5c0,3.1,2.5,5.7,5.7,5.7h27.2h27.2c3.1,0,5.7-2.5,5.7-5.7V56.4H57.7z" fill="#0891B2"/>
3
- <path d="M54.7,37.5c-0.1-1.7,0.6-3.5,2.2-4.8c1.6-1.2,3.9-1.4,5.7-0.5c3.2,1.7,3.8,5.7,1.6,8.3c-0.9,1.1-2.3,1.8-3.6,1.9l9.6,8.1l17.5-20.9c2-2.4,1.7-6-0.7-8L66.2,4.3l-8.1,9.6c-0.2-1.7-1.2-3.4-3-4.3c-1.8-0.9-4.1-0.7-5.7,0.5c-2.8,2.2-2.8,6.3-0.2,8.4c1.1,0.9,2.5,1.4,3.9,1.2L45,29.5L54.7,37.5z" fill="#06B6D4"/>
4
- <path d="M70.2,52.6c-0.5,0-0.9-0.2-1.3-0.5L59.3,44c-0.6-0.5-0.9-1.4-0.6-2.1c0.2-0.8,0.9-1.3,1.7-1.4c0.9-0.1,1.7-0.5,2.3-1.2c0.7-0.8,0.9-1.8,0.7-2.8c-0.2-1-0.8-1.9-1.8-2.4c-1.1-0.6-2.6-0.5-3.6,0.3c-1,0.7-1.5,1.8-1.4,3c0.1,0.8-0.4,1.6-1.1,1.9c-0.7,0.4-1.6,0.3-2.2-0.2L43.8,31c-0.4-0.3-0.7-0.8-0.7-1.4c0-0.5,0.1-1.1,0.5-1.5l5.9-7.1c-0.5-0.3-1-0.6-1.5-1c-1.7-1.4-2.7-3.6-2.6-5.8c0-2.2,1.1-4.3,2.9-5.7c2.2-1.8,5.3-2.1,7.9-0.7c1.1,0.6,2,1.3,2.6,2.3l6-7.1C65,2.6,65.5,2.3,66,2.3c0.5,0,1.1,0.1,1.5,0.5l20.9,17.5c1.6,1.3,2.5,3.2,2.7,5.2s-0.4,4-1.8,5.6L71.8,51.9c-0.3,0.4-0.8,0.7-1.4,0.7C70.4,52.6,70.3,52.6,70.2,52.6z M64.4,43.1l5.6,4.7l16.2-19.3c0.6-0.8,0.9-1.7,0.8-2.7s-0.5-1.9-1.3-2.5L66.4,7.1l-6.8,8.1c-0.5,0.6-1.4,0.9-2.1,0.6c-0.8-0.2-1.3-0.9-1.4-1.7c-0.1-1.2-0.8-2.2-1.9-2.7c-1.1-0.6-2.6-0.4-3.6,0.3c-0.9,0.7-1.3,1.6-1.4,2.7c0,1,0.4,2,1.2,2.7c0.7,0.6,1.5,0.9,2.4,0.8c0.8-0.1,1.6,0.4,1.9,1.1s0.3,1.6-0.2,2.2l-6.8,8.1l5.6,4.7c0.5-1,1.3-2,2.2-2.7c2.3-1.7,5.4-2,7.9-0.6c2,1.1,3.4,3,3.8,5.2s-0.2,4.5-1.6,6.2C65.4,42.4,64.9,42.8,64.4,43.1z" fill="#475569"/>
5
- <path d="M70.3,54.4H61c0.3-1.1,0.3-2.3,0-3.5c-0.6-2.8-2.8-5-5.6-5.5c-2.2-0.5-4.5,0.1-6.3,1.5c-1.7,1.4-2.7,3.5-2.7,5.8c0,0.6,0.1,1.2,0.2,1.8h-7.3V43.8c0-0.8-0.5-1.5-1.2-1.8c-0.7-0.3-1.6-0.1-2.2,0.4c-0.8,0.8-2,1.2-3.2,0.9c-1.2-0.3-2.2-1.3-2.5-2.5c-0.2-1.1,0-2.1,0.7-2.9c0.7-0.8,1.6-1.3,2.6-1.3c0.9,0,1.7,0.3,2.4,1c0.6,0.6,1.4,0.7,2.2,0.4c0.7-0.3,1.2-1,1.2-1.8V23.5c0-1.1-0.9-2-2-2H10.1c-4.2,0-7.7,3.4-7.7,7.7v54.5c0,4.2,3.4,7.7,7.7,7.7h54.5c4.2,0,7.7-3.4,7.7-7.7V56.4C72.3,55.3,71.4,54.4,70.3,54.4z M10.1,25.5h25.2v7.3c-0.6-0.1-1.2-0.2-1.8-0.2c-2.2,0-4.3,1-5.8,2.7s-2,4-1.5,6.3c0.6,2.8,2.8,5,5.5,5.6c1.2,0.3,2.4,0.2,3.5,0v7.3H24.8c-0.8,0-1.5,0.5-1.8,1.2c-0.3,0.7-0.1,1.6,0.4,2.2c0.8,0.8,1.1,2,0.9,3.2c-0.3,1.2-1.3,2.2-2.6,2.5c-1.1,0.2-2.1,0-2.9-0.7c-0.8-0.7-1.3-1.6-1.3-2.6c0-0.9,0.3-1.7,1-2.4c0.6-0.6,0.7-1.4,0.4-2.2c-0.3-0.7-1-1.2-1.8-1.2H6.5V29.2C6.5,27.1,8.1,25.5,10.1,25.5z M36.6,74.9c-0.7,0.3-1.2,1-1.2,1.8v10.6H10.1c-2,0-3.7-1.6-3.7-3.7V58.4h7.3c-0.1,0.6-0.2,1.2-0.2,1.8c0,2.2,1,4.3,2.7,5.8c1.7,1.4,4,2,6.3,1.5c2.8-0.6,5-2.8,5.6-5.5c0.3-1.2,0.2-2.4,0-3.5h7.3V69c0,0.8,0.5,1.5,1.2,1.8c0.7,0.3,1.6,0.1,2.2-0.4c0.8-0.8,2-1.1,3.2-0.9c1.2,0.3,2.2,1.3,2.5,2.6c0.2,1.1,0,2.1-0.7,2.9c-0.7,0.8-1.6,1.3-2.6,1.3c-0.9,0-1.7-0.3-2.4-1C38.2,74.7,37.3,74.6,36.6,74.9z M68.3,83.6c0,2-1.6,3.7-3.7,3.7H39.4V80c0.6,0.1,1.2,0.2,1.8,0.2c2.2,0,4.3-1,5.8-2.7c1.4-1.7,2-4,1.5-6.3c-0.6-2.8-2.8-5-5.5-5.6c-1.2-0.3-2.4-0.2-3.5,0v-7.3H50c0.8,0,1.5-0.5,1.8-1.2c0.3-0.7,0.1-1.6-0.4-2.2c-0.6-0.6-1-1.5-1-2.4c0-1,0.5-2,1.3-2.6c0.8-0.7,1.8-0.9,2.9-0.7c1.2,0.3,2.3,1.3,2.6,2.5c0.3,1.2-0.1,2.4-0.9,3.2c-0.6,0.6-0.7,1.4-0.4,2.2c0.3,0.7,1,1.2,1.8,1.2h10.6V83.6z" fill="#475569"/>
6
- </svg>