completion-kit 0.5.6 → 0.5.7

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: bc297d90963b3ef6543ef3b2f14a94808c9a5a945047bd9ebb4b820fed1c9c9d
4
- data.tar.gz: dc730069a2f27764f384068d2fab146e3cb402e24f3dfdeea33d700d50118d3a
3
+ metadata.gz: 33daa5f930979539f0b757bd5ea85273b9620f90f3de0bf4d976c843cae24af5
4
+ data.tar.gz: 3387de72acf76fbeac7d79628144ac9cb3782678cf186ad883d1978bceb4daee
5
5
  SHA512:
6
- metadata.gz: c0fe08870fd298ca0ba0e5b850dc9992dbe8fc7cfebb81abb352d1543220c2925f195abd66113204f28ebfa2e48433d9ad389fc94f100ab1f8c9689edfdc6785
7
- data.tar.gz: 1fc05c857f889ea0e461196471eca63d630c82a84d5447f870db9cd6bdfec9f539125d6189de27e3ee6cfc6969089105212f73e8a48485fecb8282ca886305a5
6
+ metadata.gz: 3af6ed91c4c791859b41cddf24078fc0ecec5a79a5aa6ead1ff996c07f9173164693bea12ac223443ae33fcadb1499919734c112a453260f4e9e187cbd6cfd37
7
+ data.tar.gz: cf05c70a638f3ba9c70657b9c4ab90154af4b5d15e44dbff80b00bd2fc5804e2f3c6313f22fba5283ab36d35adb6d218ec3e1792ee533191c1a7bb66eef59de6
@@ -2752,9 +2752,15 @@ select.ck-input {
2752
2752
  }
2753
2753
  }
2754
2754
 
2755
+ /* the metrics field stacks several sub-sections (hint, groups, divider, tag
2756
+ filter, checkboxes) — give it more vertical breathing room than a plain field */
2757
+ #metrics-field {
2758
+ gap: 0.85rem;
2759
+ }
2760
+
2755
2761
  .ck-metric-groups {
2756
2762
  display: grid;
2757
- gap: 0.4rem;
2763
+ gap: 0.55rem;
2758
2764
  margin-bottom: 0.5rem;
2759
2765
  }
2760
2766
 
@@ -2770,7 +2776,7 @@ select.ck-input {
2770
2776
  .ck-metric-groups__row {
2771
2777
  display: flex;
2772
2778
  flex-wrap: wrap;
2773
- gap: 0.4rem;
2779
+ gap: 0.5rem;
2774
2780
  }
2775
2781
 
2776
2782
  .ck-metric-group-pill {
@@ -28,6 +28,11 @@ module CompletionKit
28
28
 
29
29
  def new
30
30
  @run = Run.new(prompt_id: params[:prompt_id])
31
+ prompt = Prompt.find_by(id: @run.prompt_id)
32
+ if prompt
33
+ last_run = Run.where(prompt_id: prompt.family_versions.ids).order(created_at: :desc).first
34
+ @run.tag_names = last_run.tag_names if last_run
35
+ end
31
36
  end
32
37
 
33
38
  def edit
@@ -79,6 +84,7 @@ module CompletionKit
79
84
  dataset_id: @run.dataset_id,
80
85
  judge_model: @run.judge_model,
81
86
  temperature: @run.temperature,
87
+ tag_names: @run.tag_names,
82
88
  status: "pending"
83
89
  )
84
90
  new_run.replace_metrics!(@run.metric_ids)
@@ -72,6 +72,12 @@ module CompletionKit
72
72
  CompletionKit::ProviderCredential::PROVIDER_LABELS[provider.to_s] || provider.to_s.titleize
73
73
  end
74
74
 
75
+ def ck_masked_token(token)
76
+ return "YOUR_TOKEN" if token.blank?
77
+ return "••••••••" if token.length < 12
78
+ "#{token[0..3]}#{'•' * [token.length - 8, 4].max}#{token[-4..]}"
79
+ end
80
+
75
81
  OPENAI_MODEL_FAMILY_ORDER = ["GPT-5", "GPT-4", "o-series", "GPT-3.5", "GPT-OSS", "Other"].freeze
76
82
 
77
83
  def ck_openai_model_family(model_id)
@@ -1,6 +1,9 @@
1
1
  module CompletionKit
2
2
  class WorkerHealth
3
- HEARTBEAT_THRESHOLD = 30.seconds
3
+ # SolidQueue workers heartbeat every SolidQueue.process_heartbeat_interval
4
+ # (60s by default), so a 30s window flagged healthy workers as down between
5
+ # beats. Allow two missed heartbeats before we say the worker is gone.
6
+ HEARTBEAT_THRESHOLD = 2.minutes
4
7
 
5
8
  def self.healthy?
6
9
  return true unless defined?(::SolidQueue::Process)
@@ -0,0 +1,11 @@
1
+ <% if token.present? %>
2
+ <% masked = ck_masked_token(token) %>
3
+ <div style="display: flex; align-items: center; gap: 0.4rem; margin: 0.5rem 0;">
4
+ <button type="button" class="ck-chip ck-token-toggle" onclick="ckToggleToken(this)" data-masked="<%= masked %>" data-real="<%= token %>" aria-label="Reveal API token" aria-pressed="false" style="cursor: pointer;"><%= masked %></button>
5
+ <button type="button" class="ck-icon-btn" title="Copy token" aria-label="Copy API token" onclick="var b=this;navigator.clipboard.writeText('<%= token %>').then(function(){b.classList.add('ck-api-copy--done');setTimeout(function(){b.classList.remove('ck-api-copy--done')},1500)})">
6
+ <%= heroicon_tag "clipboard-document", variant: :outline, size: 14, "aria-hidden": "true" %>
7
+ </button>
8
+ </div>
9
+ <% else %>
10
+ <p class="ck-meta-copy">No API token configured. Set <code>COMPLETION_KIT_API_TOKEN</code> to enable API access.</p>
11
+ <% end %>
@@ -0,0 +1,311 @@
1
+ <%
2
+ token = local_assigns.fetch(:token, "YOUR_TOKEN")
3
+ real_token = local_assigns.fetch(:real_token, nil)
4
+ published_prompts = local_assigns.fetch(:published_prompts, [])
5
+ %>
6
+ <% if published_prompts.any? %>
7
+ <div class="ck-api-prompts-section">
8
+ <p class="ck-kicker">Your prompts</p>
9
+ <% published_prompts.each do |p| %>
10
+ <div class="ck-api-prompt-card">
11
+ <div class="ck-api-prompt-card__top">
12
+ <div>
13
+ <strong class="ck-api-prompt-card__name"><%= p.name %></strong>
14
+ <% if p.description.present? %>
15
+ <p class="ck-api-prompt-card__desc"><%= p.description %></p>
16
+ <% end %>
17
+ </div>
18
+ <span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= p.llm_model %></span>
19
+ </div>
20
+ <div class="ck-api-prompt-card__url">
21
+ <code class="ck-endpoint__url" id="prompt_ep_<%= p.id %>"><%= base_url %>/api/v1/prompts/<%= p.slug %></code>
22
+ <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>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+ </div>
27
+ <% end %>
28
+
29
+ <div class="ck-api-tabs">
30
+ <input type="radio" name="ck-api-tab" id="ck-tab-mcp" class="ck-api-tabs__radio" checked>
31
+ <input type="radio" name="ck-api-tab" id="ck-tab-prompts" class="ck-api-tabs__radio">
32
+ <input type="radio" name="ck-api-tab" id="ck-tab-runs" class="ck-api-tabs__radio">
33
+ <input type="radio" name="ck-api-tab" id="ck-tab-responses" class="ck-api-tabs__radio">
34
+ <input type="radio" name="ck-api-tab" id="ck-tab-datasets" class="ck-api-tabs__radio">
35
+ <input type="radio" name="ck-api-tab" id="ck-tab-metrics" class="ck-api-tabs__radio">
36
+ <input type="radio" name="ck-api-tab" id="ck-tab-metric-groups" class="ck-api-tabs__radio">
37
+ <input type="radio" name="ck-api-tab" id="ck-tab-tags" class="ck-api-tabs__radio">
38
+ <input type="radio" name="ck-api-tab" id="ck-tab-providers" class="ck-api-tabs__radio">
39
+
40
+ <nav class="ck-api-tabs__nav">
41
+ <label for="ck-tab-mcp" class="ck-api-tabs__label">MCP <span class="ck-api-tabs__count">35</span></label>
42
+ <label for="ck-tab-prompts" class="ck-api-tabs__label">Prompts <span class="ck-api-tabs__count">6</span></label>
43
+ <label for="ck-tab-runs" class="ck-api-tabs__label">Runs <span class="ck-api-tabs__count">7</span></label>
44
+ <label for="ck-tab-responses" class="ck-api-tabs__label">Responses <span class="ck-api-tabs__count">2</span></label>
45
+ <label for="ck-tab-datasets" class="ck-api-tabs__label">Datasets <span class="ck-api-tabs__count">5</span></label>
46
+ <label for="ck-tab-metrics" class="ck-api-tabs__label">Metrics <span class="ck-api-tabs__count">5</span></label>
47
+ <label for="ck-tab-metric-groups" class="ck-api-tabs__label">Metric Groups <span class="ck-api-tabs__count">5</span></label>
48
+ <label for="ck-tab-tags" class="ck-api-tabs__label">Tags <span class="ck-api-tabs__count">5</span></label>
49
+ <label for="ck-tab-providers" class="ck-api-tabs__label">Providers <span class="ck-api-tabs__count">5</span></label>
50
+ </nav>
51
+
52
+ <div class="ck-api-tabs__panels">
53
+
54
+ <div class="ck-api-tabs__panel">
55
+ <h2 class="ck-section-title">MCP Server</h2>
56
+ <p class="ck-copy">Connect Claude Code, Cursor, or any <a href="https://modelcontextprotocol.io" class="ck-link">MCP</a> client to manage prompts, runs, datasets, and metrics conversationally. 35 tools over streamable HTTP.</p>
57
+
58
+ <div class="ck-mcp-install-grid">
59
+ <div class="ck-mcp-install-card">
60
+ <div class="ck-mcp-install-card__header">
61
+ <span class="ck-mcp-install-card__icon">&#9654;</span>
62
+ Claude Code
63
+ </div>
64
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "claude mcp add completion-kit \\\n --transport http \\\n --url #{base_url}/mcp \\\n --header \"Authorization: Bearer #{token}\"" %>
65
+ </div>
66
+ <div class="ck-mcp-install-card">
67
+ <div class="ck-mcp-install-card__header">
68
+ <span class="ck-mcp-install-card__icon">{}</span>
69
+ Cursor / Generic
70
+ </div>
71
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "{\n \"mcpServers\": {\n \"completion-kit\": {\n \"url\": \"#{base_url}/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer #{token}\"\n }\n }\n }\n}" %>
72
+ </div>
73
+ </div>
74
+
75
+ <div class="ck-api-endpoint" style="padding-top: 1.5rem;">
76
+ <p class="ck-kicker" style="margin-bottom: 0.75rem;">Available tools</p>
77
+ <% grouped = CompletionKit::McpDispatcher.tool_definitions.group_by { |t| t[:name].split("_").first } %>
78
+ <% grouped.each do |group, tools| %>
79
+ <div class="ck-mcp-tool-group">
80
+ <p class="ck-mcp-tool-group__label"><%= group %> <span class="ck-api-tabs__count"><%= tools.size %></span></p>
81
+ <div class="ck-mcp-tools">
82
+ <% tools.each do |tool| %>
83
+ <div class="ck-mcp-tool">
84
+ <code class="ck-mcp-tool__name"><%= tool[:name] %></code>
85
+ <span class="ck-mcp-tool__desc"><%= tool[:description] %></span>
86
+ </div>
87
+ <% end %>
88
+ </div>
89
+ </div>
90
+ <% end %>
91
+ </div>
92
+ </div>
93
+
94
+ <div class="ck-api-tabs__panel">
95
+ <h2 class="ck-section-title">Prompts</h2>
96
+ <p class="ck-copy">Create, version, and manage LLM prompt templates.</p>
97
+ <div class="ck-api-endpoint">
98
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts</p>
99
+ <p class="ck-meta-copy">List all prompts, ordered by most recent.</p>
100
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl #{base_url}/api/v1/prompts \\\n -H \"Authorization: Bearer #{token}\"" %>
101
+ </div>
102
+ <div class="ck-api-endpoint">
103
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/prompts</p>
104
+ <p class="ck-meta-copy">Create a new prompt.</p>
105
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>, <code>template</code>, <code>llm_model</code>&emsp;<strong>Optional:</strong>&ensp;<code>description</code></p>
106
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/prompts \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"summarizer\", \"template\": \"Summarize: {{text}}\", \"llm_model\": \"gpt-4.1\"}'" %>
107
+ </div>
108
+ <div class="ck-api-endpoint">
109
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts/:id</p>
110
+ <p class="ck-meta-copy">Get a single prompt by ID.</p>
111
+ </div>
112
+ <div class="ck-api-endpoint">
113
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/prompts/:id</p>
114
+ <p class="ck-meta-copy">Update a prompt. Accepts same params as create.</p>
115
+ </div>
116
+ <div class="ck-api-endpoint">
117
+ <p class="ck-api-method"><span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/prompts/:id</p>
118
+ <p class="ck-meta-copy">Delete a prompt. Returns 204 No Content.</p>
119
+ </div>
120
+ <div class="ck-api-endpoint">
121
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/prompts/:id/publish</p>
122
+ <p class="ck-meta-copy">Publish a prompt version, making it the current version in its family.</p>
123
+ </div>
124
+ </div>
125
+
126
+ <div class="ck-api-tabs__panel">
127
+ <h2 class="ck-section-title">Runs</h2>
128
+ <p class="ck-copy">Create runs, generate LLM responses, and judge them with metrics.</p>
129
+ <div class="ck-api-endpoint">
130
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs</p>
131
+ <p class="ck-meta-copy">List all runs with response counts and average scores.</p>
132
+ </div>
133
+ <div class="ck-api-endpoint">
134
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs</p>
135
+ <p class="ck-meta-copy">Create a new run.</p>
136
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>prompt_id</code>&emsp;<strong>Optional:</strong>&ensp;<code>name</code>, <code>dataset_id</code>, <code>metric_ids</code>, <code>judge_model</code></p>
137
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/runs \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"prompt_id\": 1, \"dataset_id\": 1, \"metric_ids\": [1, 2]}'" %>
138
+ </div>
139
+ <div class="ck-api-endpoint">
140
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:id</p>
141
+ <p class="ck-meta-copy">Get a run with status, progress, response count, and average score.</p>
142
+ <%= 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}\"" %>
143
+ </div>
144
+ <div class="ck-api-endpoint">
145
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs/:id/generate</p>
146
+ <p class="ck-meta-copy">Start generating responses. Returns 202 Accepted. Poll the run to check progress.</p>
147
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/runs/1/generate \\\n -H \"Authorization: Bearer #{token}\"" %>
148
+ </div>
149
+ <div class="ck-api-endpoint">
150
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/runs/:id</p>
151
+ <p class="ck-meta-copy">Update a run. Accepts same params as create.</p>
152
+ </div>
153
+ <div class="ck-api-endpoint">
154
+ <p class="ck-api-method"><span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/runs/:id</p>
155
+ <p class="ck-meta-copy">Delete a run and all its responses. Returns 204 No Content.</p>
156
+ </div>
157
+ </div>
158
+
159
+ <div class="ck-api-tabs__panel">
160
+ <h2 class="ck-section-title">Responses</h2>
161
+ <p class="ck-copy">Read-only access to generated responses and their review scores. Nested under runs.</p>
162
+ <div class="ck-api-endpoint">
163
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses</p>
164
+ <p class="ck-meta-copy">List all responses for a run, including nested review scores.</p>
165
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl #{base_url}/api/v1/runs/1/responses \\\n -H \"Authorization: Bearer #{token}\"" %>
166
+ </div>
167
+ <div class="ck-api-endpoint">
168
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses/:id</p>
169
+ <p class="ck-meta-copy">Get a single response with its review scores and feedback.</p>
170
+ </div>
171
+ </div>
172
+
173
+ <div class="ck-api-tabs__panel">
174
+ <h2 class="ck-section-title">Datasets</h2>
175
+ <p class="ck-copy">Data used as input for runs.</p>
176
+ <div class="ck-api-endpoint">
177
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/datasets</p>
178
+ <p class="ck-meta-copy">List all datasets.</p>
179
+ </div>
180
+ <div class="ck-api-endpoint">
181
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/datasets</p>
182
+ <p class="ck-meta-copy">Create a dataset.</p>
183
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>, <code>csv_data</code></p>
184
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/datasets \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"tickets\", \"csv_data\": \"text,expected_output\\\\nHello,Hi\"}'" %>
185
+ </div>
186
+ <div class="ck-api-endpoint">
187
+ <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>
188
+ <p class="ck-meta-copy">Get, update, or delete a dataset.</p>
189
+ </div>
190
+ </div>
191
+
192
+ <div class="ck-api-tabs__panel">
193
+ <h2 class="ck-section-title">Metrics</h2>
194
+ <p class="ck-copy">Scoring dimensions used by the judge model.</p>
195
+ <div class="ck-api-endpoint">
196
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/metrics</p>
197
+ <p class="ck-meta-copy">List all metrics.</p>
198
+ </div>
199
+ <div class="ck-api-endpoint">
200
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/metrics</p>
201
+ <p class="ck-meta-copy">Create a metric.</p>
202
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>&emsp;<strong>Optional:</strong>&ensp;<code>instruction</code>, <code>rubric_bands</code> (array of {stars, description})</p>
203
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/metrics \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"relevance\", \"instruction\": \"Is the response relevant?\"}'" %>
204
+ </div>
205
+ <div class="ck-api-endpoint">
206
+ <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>
207
+ <p class="ck-meta-copy">Get, update, or delete a metric.</p>
208
+ </div>
209
+ </div>
210
+
211
+ <div class="ck-api-tabs__panel">
212
+ <h2 class="ck-section-title">Metric Groups</h2>
213
+ <p class="ck-copy">Named groups of metrics you can apply to a run as a set.</p>
214
+ <div class="ck-api-endpoint">
215
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/metric_groups</p>
216
+ <p class="ck-meta-copy">List all metric groups with their metric IDs.</p>
217
+ </div>
218
+ <div class="ck-api-endpoint">
219
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/metric_groups</p>
220
+ <p class="ck-meta-copy">Create a metric group.</p>
221
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>&emsp;<strong>Optional:</strong>&ensp;<code>description</code>, <code>metric_ids</code> (array)</p>
222
+ </div>
223
+ <div class="ck-api-endpoint">
224
+ <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>
225
+ <p class="ck-meta-copy">Get, update, or delete a metric group. PATCH with <code>metric_ids</code> replaces all metric associations.</p>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="ck-api-tabs__panel">
230
+ <h2 class="ck-section-title">Tags</h2>
231
+ <p class="ck-copy">Domain labels you can attach to metrics, prompts, runs, and datasets. Tags are auto-assigned a color from a 10-color palette. Each index page can be filtered by one or more tags using <code>?tag[]=name</code> query params (OR semantics).</p>
232
+ <div class="ck-api-endpoint">
233
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/tags</p>
234
+ <p class="ck-meta-copy">List all tags with name and color.</p>
235
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl #{base_url}/api/v1/tags \\\n -H \"Authorization: Bearer #{token}\"" %>
236
+ </div>
237
+ <div class="ck-api-endpoint">
238
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/tags</p>
239
+ <p class="ck-meta-copy">Create a tag.</p>
240
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code></p>
241
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/tags \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"real estate\"}'" %>
242
+ </div>
243
+ <div class="ck-api-endpoint">
244
+ <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>
245
+ <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>
246
+ </div>
247
+ <div class="ck-api-endpoint" style="padding-top: 1rem;">
248
+ <p class="ck-kicker" style="margin-bottom: 0.5rem;">Tagging resources</p>
249
+ <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>
250
+ <%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl -X POST #{base_url}/api/v1/metrics \\\n -H \"Authorization: Bearer #{token}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"Accuracy\", \"tag_names\": [\"real estate\"]}'" %>
251
+ </div>
252
+ <div class="ck-api-endpoint" style="padding-top: 1rem;">
253
+ <p class="ck-kicker" style="margin-bottom: 0.5rem;">MCP tools</p>
254
+ <div class="ck-mcp-tools">
255
+ <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_list</code><span class="ck-mcp-tool__desc">List all tags</span></div>
256
+ <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_get</code><span class="ck-mcp-tool__desc">Get a tag by ID</span></div>
257
+ <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_create</code><span class="ck-mcp-tool__desc">Create a tag (name required)</span></div>
258
+ <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_update</code><span class="ck-mcp-tool__desc">Update a tag's name</span></div>
259
+ <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_delete</code><span class="ck-mcp-tool__desc">Delete a tag and remove all its taggings</span></div>
260
+ </div>
261
+ <p class="ck-meta-copy" style="margin-top: 0.75rem;">The existing <code>metrics_create</code>, <code>metrics_update</code>, <code>prompts_create</code>, <code>prompts_update</code>, <code>runs_create</code>, <code>runs_update</code>, <code>datasets_create</code>, and <code>datasets_update</code> tools all accept a <code>tag_names</code> parameter with the same auto-create and replace semantics as the REST API.</p>
262
+ </div>
263
+ </div>
264
+
265
+ <div class="ck-api-tabs__panel">
266
+ <h2 class="ck-section-title">Provider Credentials</h2>
267
+ <p class="ck-copy">LLM provider API keys. The <code>api_key</code> field is write-only and never returned in responses.</p>
268
+ <div class="ck-api-endpoint">
269
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/provider_credentials</p>
270
+ <p class="ck-meta-copy">List all provider credentials (api_key excluded).</p>
271
+ </div>
272
+ <div class="ck-api-endpoint">
273
+ <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/provider_credentials</p>
274
+ <p class="ck-meta-copy">Create a provider credential.</p>
275
+ <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>provider</code> (openai, anthropic, ollama, openrouter), <code>api_key</code>&emsp;<strong>Optional:</strong>&ensp;<code>api_endpoint</code></p>
276
+ </div>
277
+ <div class="ck-api-endpoint">
278
+ <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>
279
+ <p class="ck-meta-copy">Get, update, or delete a provider credential.</p>
280
+ </div>
281
+ </div>
282
+
283
+ </div>
284
+ </div>
285
+
286
+ <script>
287
+ function ckToggleToken(el) {
288
+ var showing = el.textContent === el.dataset.masked;
289
+ el.textContent = showing ? el.dataset.real : el.dataset.masked;
290
+ el.setAttribute('aria-pressed', showing ? 'true' : 'false');
291
+ el.setAttribute('aria-label', showing ? 'Hide API token' : 'Reveal API token');
292
+ navigator.clipboard.writeText(el.dataset.real).then(function() {
293
+ el.classList.add('ck-api-copy--done');
294
+ setTimeout(function() { el.classList.remove('ck-api-copy--done'); }, 1500);
295
+ });
296
+ }
297
+
298
+ function ckCopyExample(btn) {
299
+ var pre = btn.closest('.ck-api-example').querySelector('pre');
300
+ var text = pre.textContent;
301
+ var realToken = btn.dataset.realToken;
302
+ var displayToken = btn.dataset.displayToken;
303
+ if (realToken && displayToken) {
304
+ text = text.split(displayToken).join(realToken);
305
+ }
306
+ navigator.clipboard.writeText(text).then(function() {
307
+ btn.classList.add('ck-api-copy--done');
308
+ setTimeout(function() { btn.classList.remove('ck-api-copy--done'); }, 1500);
309
+ });
310
+ }
311
+ </script>
@@ -1,11 +1,3 @@
1
- <% token_display = if @token.present? && @token.length >= 12
2
- "#{@token[0..3]}#{'•' * [@token.length - 8, 4].max}#{@token[-4..]}"
3
- elsif @token.present?
4
- "••••••••"
5
- else
6
- "YOUR_TOKEN"
7
- end %>
8
-
9
1
  <section class="ck-page-header">
10
2
  <div>
11
3
  <h1 class="ck-title">API</h1>
@@ -18,324 +10,14 @@ end %>
18
10
  <div class="ck-split">
19
11
  <div>
20
12
  <p class="ck-kicker">Authentication</p>
21
- <% if @token.present? %>
22
- <div style="display: flex; align-items: center; gap: 0.4rem; margin: 0.5rem 0;">
23
- <button type="button" class="ck-chip ck-token-toggle" onclick="ckToggleToken(this)" data-masked="<%= token_display %>" data-real="<%= @token %>" aria-label="Reveal API token" aria-pressed="false" style="cursor: pointer;"><%= token_display %></button>
24
- <button type="button" class="ck-icon-btn" title="Copy token" aria-label="Copy API token" onclick="var b=this;navigator.clipboard.writeText('<%= @token %>').then(function(){b.classList.add('ck-api-copy--done');setTimeout(function(){b.classList.remove('ck-api-copy--done')},1500)})">
25
- <%= heroicon_tag "clipboard-document", variant: :outline, size: 14, "aria-hidden": "true" %>
26
- </button>
27
- </div>
28
- <% else %>
29
- <p class="ck-meta-copy">No API token configured. Set <code>COMPLETION_KIT_API_TOKEN</code> to enable API access.</p>
30
- <% end %>
31
- </div>
32
- </div>
33
- </div>
34
- </div>
35
-
36
- <% if @published_prompts.any? %>
37
- <div class="ck-api-prompts-section">
38
- <p class="ck-kicker">Your prompts</p>
39
- <% @published_prompts.each do |p| %>
40
- <div class="ck-api-prompt-card">
41
- <div class="ck-api-prompt-card__top">
42
- <div>
43
- <strong class="ck-api-prompt-card__name"><%= p.name %></strong>
44
- <% if p.description.present? %>
45
- <p class="ck-api-prompt-card__desc"><%= p.description %></p>
46
- <% end %>
47
- </div>
48
- <span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= p.llm_model %></span>
49
- </div>
50
- <div class="ck-api-prompt-card__url">
51
- <code class="ck-endpoint__url" id="prompt_ep_<%= p.id %>"><%= @base_url %>/api/v1/prompts/<%= p.slug %></code>
52
- <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>
53
- </div>
54
- </div>
55
- <% end %>
56
- </div>
57
- <% end %>
58
-
59
- <div class="ck-api-tabs">
60
- <input type="radio" name="ck-api-tab" id="ck-tab-mcp" class="ck-api-tabs__radio" checked>
61
- <input type="radio" name="ck-api-tab" id="ck-tab-prompts" class="ck-api-tabs__radio">
62
- <input type="radio" name="ck-api-tab" id="ck-tab-runs" class="ck-api-tabs__radio">
63
- <input type="radio" name="ck-api-tab" id="ck-tab-responses" class="ck-api-tabs__radio">
64
- <input type="radio" name="ck-api-tab" id="ck-tab-datasets" class="ck-api-tabs__radio">
65
- <input type="radio" name="ck-api-tab" id="ck-tab-metrics" class="ck-api-tabs__radio">
66
- <input type="radio" name="ck-api-tab" id="ck-tab-metric-groups" class="ck-api-tabs__radio">
67
- <input type="radio" name="ck-api-tab" id="ck-tab-tags" class="ck-api-tabs__radio">
68
- <input type="radio" name="ck-api-tab" id="ck-tab-providers" class="ck-api-tabs__radio">
69
-
70
- <nav class="ck-api-tabs__nav">
71
- <label for="ck-tab-mcp" class="ck-api-tabs__label">MCP <span class="ck-api-tabs__count">35</span></label>
72
- <label for="ck-tab-prompts" class="ck-api-tabs__label">Prompts <span class="ck-api-tabs__count">6</span></label>
73
- <label for="ck-tab-runs" class="ck-api-tabs__label">Runs <span class="ck-api-tabs__count">7</span></label>
74
- <label for="ck-tab-responses" class="ck-api-tabs__label">Responses <span class="ck-api-tabs__count">2</span></label>
75
- <label for="ck-tab-datasets" class="ck-api-tabs__label">Datasets <span class="ck-api-tabs__count">5</span></label>
76
- <label for="ck-tab-metrics" class="ck-api-tabs__label">Metrics <span class="ck-api-tabs__count">5</span></label>
77
- <label for="ck-tab-metric-groups" class="ck-api-tabs__label">Metric Groups <span class="ck-api-tabs__count">5</span></label>
78
- <label for="ck-tab-tags" class="ck-api-tabs__label">Tags <span class="ck-api-tabs__count">5</span></label>
79
- <label for="ck-tab-providers" class="ck-api-tabs__label">Providers <span class="ck-api-tabs__count">5</span></label>
80
- </nav>
81
-
82
- <div class="ck-api-tabs__panels">
83
-
84
- <div class="ck-api-tabs__panel">
85
- <h2 class="ck-section-title">MCP Server</h2>
86
- <p class="ck-copy">Connect Claude Code, Cursor, or any <a href="https://modelcontextprotocol.io" class="ck-link">MCP</a> client to manage prompts, runs, datasets, and metrics conversationally. 35 tools over streamable HTTP.</p>
87
-
88
- <div class="ck-mcp-install-grid">
89
- <div class="ck-mcp-install-card">
90
- <div class="ck-mcp-install-card__header">
91
- <span class="ck-mcp-install-card__icon">&#9654;</span>
92
- Claude Code
93
- </div>
94
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "claude mcp add completion-kit \\\n --transport http \\\n --url #{@base_url}/mcp \\\n --header \"Authorization: Bearer #{token_display}\"" %>
95
- </div>
96
- <div class="ck-mcp-install-card">
97
- <div class="ck-mcp-install-card__header">
98
- <span class="ck-mcp-install-card__icon">{}</span>
99
- Cursor / Generic
100
- </div>
101
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "{\n \"mcpServers\": {\n \"completion-kit\": {\n \"url\": \"#{@base_url}/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer #{token_display}\"\n }\n }\n }\n}" %>
102
- </div>
103
- </div>
104
-
105
- <div class="ck-api-endpoint" style="padding-top: 1.5rem;">
106
- <p class="ck-kicker" style="margin-bottom: 0.75rem;">Available tools</p>
107
- <% grouped = CompletionKit::McpDispatcher.tool_definitions.group_by { |t| t[:name].split("_").first } %>
108
- <% grouped.each do |group, tools| %>
109
- <div class="ck-mcp-tool-group">
110
- <p class="ck-mcp-tool-group__label"><%= group %> <span class="ck-api-tabs__count"><%= tools.size %></span></p>
111
- <div class="ck-mcp-tools">
112
- <% tools.each do |tool| %>
113
- <div class="ck-mcp-tool">
114
- <code class="ck-mcp-tool__name"><%= tool[:name] %></code>
115
- <span class="ck-mcp-tool__desc"><%= tool[:description] %></span>
116
- </div>
117
- <% end %>
118
- </div>
119
- </div>
120
- <% end %>
121
- </div>
122
- </div>
123
-
124
- <div class="ck-api-tabs__panel">
125
- <h2 class="ck-section-title">Prompts</h2>
126
- <p class="ck-copy">Create, version, and manage LLM prompt templates.</p>
127
- <div class="ck-api-endpoint">
128
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts</p>
129
- <p class="ck-meta-copy">List all prompts, ordered by most recent.</p>
130
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl #{@base_url}/api/v1/prompts \\\n -H \"Authorization: Bearer #{token_display}\"" %>
131
- </div>
132
- <div class="ck-api-endpoint">
133
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/prompts</p>
134
- <p class="ck-meta-copy">Create a new prompt.</p>
135
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>, <code>template</code>, <code>llm_model</code>&emsp;<strong>Optional:</strong>&ensp;<code>description</code></p>
136
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/prompts \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"summarizer\", \"template\": \"Summarize: {{text}}\", \"llm_model\": \"gpt-4.1\"}'" %>
137
- </div>
138
- <div class="ck-api-endpoint">
139
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts/:id</p>
140
- <p class="ck-meta-copy">Get a single prompt by ID.</p>
141
- </div>
142
- <div class="ck-api-endpoint">
143
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/prompts/:id</p>
144
- <p class="ck-meta-copy">Update a prompt. Accepts same params as create.</p>
145
- </div>
146
- <div class="ck-api-endpoint">
147
- <p class="ck-api-method"><span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/prompts/:id</p>
148
- <p class="ck-meta-copy">Delete a prompt. Returns 204 No Content.</p>
149
- </div>
150
- <div class="ck-api-endpoint">
151
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/prompts/:id/publish</p>
152
- <p class="ck-meta-copy">Publish a prompt version, making it the current version in its family.</p>
153
- </div>
154
- </div>
155
-
156
- <div class="ck-api-tabs__panel">
157
- <h2 class="ck-section-title">Runs</h2>
158
- <p class="ck-copy">Create runs, generate LLM responses, and judge them with metrics.</p>
159
- <div class="ck-api-endpoint">
160
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs</p>
161
- <p class="ck-meta-copy">List all runs with response counts and average scores.</p>
162
- </div>
163
- <div class="ck-api-endpoint">
164
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs</p>
165
- <p class="ck-meta-copy">Create a new run.</p>
166
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>prompt_id</code>&emsp;<strong>Optional:</strong>&ensp;<code>name</code>, <code>dataset_id</code>, <code>metric_ids</code>, <code>judge_model</code></p>
167
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/runs \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"prompt_id\": 1, \"dataset_id\": 1, \"metric_ids\": [1, 2]}'" %>
168
- </div>
169
- <div class="ck-api-endpoint">
170
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:id</p>
171
- <p class="ck-meta-copy">Get a run with status, progress, response count, and average score.</p>
172
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl #{@base_url}/api/v1/runs/1 \\\n -H \"Authorization: Bearer #{token_display}\"" %>
173
- </div>
174
- <div class="ck-api-endpoint">
175
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs/:id/generate</p>
176
- <p class="ck-meta-copy">Start generating responses. Returns 202 Accepted. Poll the run to check progress.</p>
177
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/runs/1/generate \\\n -H \"Authorization: Bearer #{token_display}\"" %>
178
- </div>
179
- <div class="ck-api-endpoint">
180
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/runs/:id</p>
181
- <p class="ck-meta-copy">Update a run. Accepts same params as create.</p>
182
- </div>
183
- <div class="ck-api-endpoint">
184
- <p class="ck-api-method"><span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/runs/:id</p>
185
- <p class="ck-meta-copy">Delete a run and all its responses. Returns 204 No Content.</p>
186
- </div>
187
- </div>
188
-
189
- <div class="ck-api-tabs__panel">
190
- <h2 class="ck-section-title">Responses</h2>
191
- <p class="ck-copy">Read-only access to generated responses and their review scores. Nested under runs.</p>
192
- <div class="ck-api-endpoint">
193
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses</p>
194
- <p class="ck-meta-copy">List all responses for a run, including nested review scores.</p>
195
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl #{@base_url}/api/v1/runs/1/responses \\\n -H \"Authorization: Bearer #{token_display}\"" %>
196
- </div>
197
- <div class="ck-api-endpoint">
198
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses/:id</p>
199
- <p class="ck-meta-copy">Get a single response with its review scores and feedback.</p>
200
- </div>
201
- </div>
202
-
203
- <div class="ck-api-tabs__panel">
204
- <h2 class="ck-section-title">Datasets</h2>
205
- <p class="ck-copy">Data used as input for runs.</p>
206
- <div class="ck-api-endpoint">
207
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/datasets</p>
208
- <p class="ck-meta-copy">List all datasets.</p>
209
- </div>
210
- <div class="ck-api-endpoint">
211
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/datasets</p>
212
- <p class="ck-meta-copy">Create a dataset.</p>
213
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>, <code>csv_data</code></p>
214
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/datasets \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"tickets\", \"csv_data\": \"text,expected_output\\\\nHello,Hi\"}'" %>
215
- </div>
216
- <div class="ck-api-endpoint">
217
- <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>
218
- <p class="ck-meta-copy">Get, update, or delete a dataset.</p>
219
- </div>
220
- </div>
221
-
222
- <div class="ck-api-tabs__panel">
223
- <h2 class="ck-section-title">Metrics</h2>
224
- <p class="ck-copy">Scoring dimensions used by the judge model.</p>
225
- <div class="ck-api-endpoint">
226
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/metrics</p>
227
- <p class="ck-meta-copy">List all metrics.</p>
228
- </div>
229
- <div class="ck-api-endpoint">
230
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/metrics</p>
231
- <p class="ck-meta-copy">Create a metric.</p>
232
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>&emsp;<strong>Optional:</strong>&ensp;<code>instruction</code>, <code>rubric_bands</code> (array of {stars, description})</p>
233
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/metrics \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"relevance\", \"instruction\": \"Is the response relevant?\"}'" %>
234
- </div>
235
- <div class="ck-api-endpoint">
236
- <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>
237
- <p class="ck-meta-copy">Get, update, or delete a metric.</p>
238
- </div>
239
- </div>
240
-
241
- <div class="ck-api-tabs__panel">
242
- <h2 class="ck-section-title">Metric Groups</h2>
243
- <p class="ck-copy">Named groups of metrics you can apply to a run as a set.</p>
244
- <div class="ck-api-endpoint">
245
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/metric_groups</p>
246
- <p class="ck-meta-copy">List all metric groups with their metric IDs.</p>
247
- </div>
248
- <div class="ck-api-endpoint">
249
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/metric_groups</p>
250
- <p class="ck-meta-copy">Create a metric group.</p>
251
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code>&emsp;<strong>Optional:</strong>&ensp;<code>description</code>, <code>metric_ids</code> (array)</p>
252
- </div>
253
- <div class="ck-api-endpoint">
254
- <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>
255
- <p class="ck-meta-copy">Get, update, or delete a metric group. PATCH with <code>metric_ids</code> replaces all metric associations.</p>
256
- </div>
257
- </div>
258
-
259
- <div class="ck-api-tabs__panel">
260
- <h2 class="ck-section-title">Tags</h2>
261
- <p class="ck-copy">Domain labels you can attach to metrics, prompts, runs, and datasets. Tags are auto-assigned a color from a 10-color palette. Each index page can be filtered by one or more tags using <code>?tag[]=name</code> query params (OR semantics).</p>
262
- <div class="ck-api-endpoint">
263
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/tags</p>
264
- <p class="ck-meta-copy">List all tags with name and color.</p>
265
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl #{@base_url}/api/v1/tags \\\n -H \"Authorization: Bearer #{token_display}\"" %>
266
- </div>
267
- <div class="ck-api-endpoint">
268
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/tags</p>
269
- <p class="ck-meta-copy">Create a tag.</p>
270
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>name</code></p>
271
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/tags \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"real estate\"}'" %>
272
- </div>
273
- <div class="ck-api-endpoint">
274
- <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>
275
- <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>
276
- </div>
277
- <div class="ck-api-endpoint" style="padding-top: 1rem;">
278
- <p class="ck-kicker" style="margin-bottom: 0.5rem;">Tagging resources</p>
279
- <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>
280
- <%= render "example", base_url: @base_url, token: token_display, real_token: @token, cmd: "curl -X POST #{@base_url}/api/v1/metrics \\\n -H \"Authorization: Bearer #{token_display}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"Accuracy\", \"tag_names\": [\"real estate\"]}'" %>
281
- </div>
282
- <div class="ck-api-endpoint" style="padding-top: 1rem;">
283
- <p class="ck-kicker" style="margin-bottom: 0.5rem;">MCP tools</p>
284
- <div class="ck-mcp-tools">
285
- <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_list</code><span class="ck-mcp-tool__desc">List all tags</span></div>
286
- <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_get</code><span class="ck-mcp-tool__desc">Get a tag by ID</span></div>
287
- <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_create</code><span class="ck-mcp-tool__desc">Create a tag (name required)</span></div>
288
- <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_update</code><span class="ck-mcp-tool__desc">Update a tag's name</span></div>
289
- <div class="ck-mcp-tool"><code class="ck-mcp-tool__name">tags_delete</code><span class="ck-mcp-tool__desc">Delete a tag and remove all its taggings</span></div>
290
- </div>
291
- <p class="ck-meta-copy" style="margin-top: 0.75rem;">The existing <code>metrics_create</code>, <code>metrics_update</code>, <code>prompts_create</code>, <code>prompts_update</code>, <code>runs_create</code>, <code>runs_update</code>, <code>datasets_create</code>, and <code>datasets_update</code> tools all accept a <code>tag_names</code> parameter with the same auto-create and replace semantics as the REST API.</p>
292
- </div>
293
- </div>
294
-
295
- <div class="ck-api-tabs__panel">
296
- <h2 class="ck-section-title">Provider Credentials</h2>
297
- <p class="ck-copy">LLM provider API keys. The <code>api_key</code> field is write-only and never returned in responses.</p>
298
- <div class="ck-api-endpoint">
299
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/provider_credentials</p>
300
- <p class="ck-meta-copy">List all provider credentials (api_key excluded).</p>
301
- </div>
302
- <div class="ck-api-endpoint">
303
- <p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/provider_credentials</p>
304
- <p class="ck-meta-copy">Create a provider credential.</p>
305
- <p class="ck-api-params"><strong>Required:</strong>&ensp;<code>provider</code> (openai, anthropic, ollama, openrouter), <code>api_key</code>&emsp;<strong>Optional:</strong>&ensp;<code>api_endpoint</code></p>
306
- </div>
307
- <div class="ck-api-endpoint">
308
- <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>
309
- <p class="ck-meta-copy">Get, update, or delete a provider credential.</p>
13
+ <%= render CompletionKit.config.api_reference_authentication_partial, token: @token %>
310
14
  </div>
311
15
  </div>
312
-
313
16
  </div>
314
17
  </div>
315
18
 
316
- <script>
317
- function ckToggleToken(el) {
318
- var showing = el.textContent === el.dataset.masked;
319
- el.textContent = showing ? el.dataset.real : el.dataset.masked;
320
- el.setAttribute('aria-pressed', showing ? 'true' : 'false');
321
- el.setAttribute('aria-label', showing ? 'Hide API token' : 'Reveal API token');
322
- navigator.clipboard.writeText(el.dataset.real).then(function() {
323
- el.classList.add('ck-api-copy--done');
324
- setTimeout(function() { el.classList.remove('ck-api-copy--done'); }, 1500);
325
- });
326
- }
327
-
328
- function ckCopyExample(btn) {
329
- var pre = btn.closest('.ck-api-example').querySelector('pre');
330
- var text = pre.textContent;
331
- var realToken = btn.dataset.realToken;
332
- var displayToken = btn.dataset.displayToken;
333
- if (realToken && displayToken) {
334
- text = text.split(displayToken).join(realToken);
335
- }
336
- navigator.clipboard.writeText(text).then(function() {
337
- btn.classList.add('ck-api-copy--done');
338
- setTimeout(function() { btn.classList.remove('ck-api-copy--done'); }, 1500);
339
- });
340
- }
341
- </script>
19
+ <%= render "completion_kit/api_reference/body",
20
+ base_url: @base_url,
21
+ token: ck_masked_token(@token),
22
+ real_token: @token,
23
+ published_prompts: @published_prompts %>
@@ -2,7 +2,7 @@
2
2
  <% label_text = local_assigns[:label].presence || "#{param_namespace.to_s.titleize} tags" %>
3
3
  <%= label_tag "#{param_namespace}_tags", label_text, class: "ck-label" %>
4
4
  <% all_tags = CompletionKit::Tag.order(:name) %>
5
- <% selected_ids = record.persisted? ? record.tags.pluck(:id) : [] %>
5
+ <% selected_ids = (record.persisted? || record.tags.loaded?) ? record.tags.map(&:id) : [] %>
6
6
  <div class="ck-tag-picker">
7
7
  <% all_tags.each do |tag| %>
8
8
  <% checked = selected_ids.include?(tag.id) %>
@@ -1,3 +1,3 @@
1
1
  module CompletionKit
2
- VERSION = "0.5.6"
2
+ VERSION = "0.5.7"
3
3
  end
@@ -9,6 +9,7 @@ module CompletionKit
9
9
  attr_accessor :judge_model, :high_quality_threshold, :medium_quality_threshold
10
10
  attr_accessor :username, :password, :auth_strategy, :api_token
11
11
  attr_accessor :tenant_scope, :tenant_scope_columns
12
+ attr_accessor :api_reference_authentication_partial
12
13
 
13
14
  def initialize
14
15
  @openai_api_key = ENV['OPENAI_API_KEY']
@@ -19,6 +20,8 @@ module CompletionKit
19
20
  @judge_model = "gpt-4.1"
20
21
  @high_quality_threshold = 4
21
22
  @medium_quality_threshold = 3
23
+
24
+ @api_reference_authentication_partial = "completion_kit/api_reference/authentication"
22
25
  end
23
26
 
24
27
  def tenant_scope_columns
@@ -37,6 +37,10 @@ CompletionKit.configure do |config|
37
37
  # API Authentication
38
38
  # config.api_token = ENV['COMPLETION_KIT_API_TOKEN']
39
39
 
40
+ # API reference page: swap in your own Authentication card (e.g. a multi-tenant
41
+ # token-management panel). The partial receives a `token:` local.
42
+ # config.api_reference_authentication_partial = "my_app/api_token_panel"
43
+
40
44
  # Web UI Authentication
41
45
  # config.username = "admin"
42
46
  # config.password = ENV['COMPLETION_KIT_PASSWORD']
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.6
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damien Bastin
@@ -300,6 +300,8 @@ files:
300
300
  - app/services/completion_kit/prompt_improvement_service.rb
301
301
  - app/services/completion_kit/worker_health.rb
302
302
  - app/validators/completion_kit/tenant_scoped_uniqueness_validator.rb
303
+ - app/views/completion_kit/api_reference/_authentication.html.erb
304
+ - app/views/completion_kit/api_reference/_body.html.erb
303
305
  - app/views/completion_kit/api_reference/_example.html.erb
304
306
  - app/views/completion_kit/api_reference/index.html.erb
305
307
  - app/views/completion_kit/datasets/_form.html.erb