completion-kit 0.4.8 → 0.5.1
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/README.md +12 -0
- data/app/assets/config/completion_kit_manifest.js +1 -0
- data/app/assets/javascripts/completion_kit/application.js +157 -0
- data/app/assets/stylesheets/completion_kit/application.css +382 -0
- data/app/controllers/completion_kit/api/v1/datasets_controller.rb +2 -2
- data/app/controllers/completion_kit/api/v1/metric_groups_controller.rb +2 -2
- data/app/controllers/completion_kit/api/v1/metrics_controller.rb +3 -2
- data/app/controllers/completion_kit/api/v1/prompts_controller.rb +5 -4
- data/app/controllers/completion_kit/api/v1/runs_controller.rb +3 -2
- data/app/controllers/completion_kit/api/v1/tags_controller.rb +51 -0
- data/app/controllers/completion_kit/datasets_controller.rb +3 -2
- data/app/controllers/completion_kit/metric_groups_controller.rb +7 -6
- data/app/controllers/completion_kit/metrics_controller.rb +4 -2
- data/app/controllers/completion_kit/prompts_controller.rb +7 -4
- data/app/controllers/completion_kit/runs_controller.rb +4 -3
- data/app/controllers/completion_kit/tags_controller.rb +50 -0
- data/app/controllers/concerns/completion_kit/tag_filtering.rb +22 -0
- data/app/helpers/completion_kit/application_helper.rb +11 -0
- data/app/models/completion_kit/dataset.rb +5 -2
- data/app/models/completion_kit/metric.rb +4 -1
- data/app/models/completion_kit/metric_group.rb +4 -1
- data/app/models/completion_kit/prompt.rb +4 -1
- data/app/models/completion_kit/run.rb +3 -1
- data/app/models/completion_kit/tag.rb +39 -0
- data/app/models/completion_kit/tagging.rb +12 -0
- data/app/models/concerns/completion_kit/taggable.rb +24 -0
- data/app/services/completion_kit/mcp_dispatcher.rb +3 -1
- data/app/services/completion_kit/mcp_tools/datasets.rb +6 -4
- data/app/services/completion_kit/mcp_tools/metric_groups.rb +6 -2
- data/app/services/completion_kit/mcp_tools/metrics.rb +8 -4
- data/app/services/completion_kit/mcp_tools/prompts.rb +10 -5
- data/app/services/completion_kit/mcp_tools/runs.rb +7 -3
- data/app/services/completion_kit/mcp_tools/tags.rb +74 -0
- data/app/views/completion_kit/api_reference/index.html.erb +38 -0
- data/app/views/completion_kit/datasets/_form.html.erb +20 -1
- data/app/views/completion_kit/datasets/index.html.erb +17 -1
- data/app/views/completion_kit/datasets/show.html.erb +6 -0
- data/app/views/completion_kit/metric_groups/_form.html.erb +74 -19
- data/app/views/completion_kit/metric_groups/index.html.erb +30 -4
- data/app/views/completion_kit/metrics/_form.html.erb +19 -1
- data/app/views/completion_kit/metrics/index.html.erb +18 -2
- data/app/views/completion_kit/metrics/show.html.erb +6 -0
- data/app/views/completion_kit/prompts/_form.html.erb +20 -1
- data/app/views/completion_kit/prompts/index.html.erb +17 -1
- data/app/views/completion_kit/prompts/show.html.erb +6 -0
- data/app/views/completion_kit/provider_credentials/_form.html.erb +1 -1
- data/app/views/completion_kit/provider_credentials/index.html.erb +3 -1
- data/app/views/completion_kit/runs/_form.html.erb +25 -3
- data/app/views/completion_kit/runs/_row.html.erb +5 -0
- data/app/views/completion_kit/runs/index.html.erb +9 -0
- data/app/views/completion_kit/runs/show.html.erb +6 -0
- data/app/views/completion_kit/shared/_settings_nav.html.erb +9 -0
- data/app/views/completion_kit/tags/_filter_bar.html.erb +15 -0
- data/app/views/completion_kit/tags/_form.html.erb +40 -0
- data/app/views/completion_kit/tags/_marks.html.erb +3 -0
- data/app/views/completion_kit/tags/_picker.html.erb +20 -0
- data/app/views/completion_kit/tags/edit.html.erb +20 -0
- data/app/views/completion_kit/tags/index.html.erb +45 -0
- data/app/views/completion_kit/tags/new.html.erb +20 -0
- data/app/views/layouts/completion_kit/application.html.erb +11 -132
- data/config/routes.rb +2 -0
- data/db/migrate/20260509000001_create_completion_kit_tags.rb +10 -0
- data/db/migrate/20260509000002_create_completion_kit_taggings.rb +16 -0
- data/lib/completion_kit/engine.rb +5 -1
- data/lib/completion_kit/version.rb +1 -1
- metadata +19 -1
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
|
|
29
29
|
<div class="ck-actions">
|
|
30
|
-
<%= link_to "Cancel", provider_credentials_path, class: ck_button_classes(:light, variant: :outline) %>
|
|
30
|
+
<%= link_to "Cancel", provider_credentials_path, class: ck_button_classes(:light, variant: :outline), tabindex: "0" %>
|
|
31
31
|
<%= form.submit(provider_credential.persisted? ? "Save provider" : "Create provider", class: ck_button_classes(:dark)) %>
|
|
32
32
|
</div>
|
|
33
33
|
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
<%= turbo_stream_from "completion_kit_provider_#{pc.id}" %>
|
|
3
3
|
<% end %>
|
|
4
4
|
|
|
5
|
+
<%= render "completion_kit/shared/settings_nav", section_label: "Providers" %>
|
|
6
|
+
|
|
5
7
|
<section class="ck-page-header">
|
|
6
8
|
<div>
|
|
7
|
-
<h1 class="ck-title">
|
|
9
|
+
<h1 class="ck-title">Providers</h1>
|
|
8
10
|
<p class="ck-lead">API keys for LLM providers. These are used for both generating prompt completions and running judge evaluations.</p>
|
|
9
11
|
</div>
|
|
10
12
|
<div class="ck-actions">
|
|
@@ -115,9 +115,24 @@
|
|
|
115
115
|
</div>
|
|
116
116
|
<div class="ck-metric-divider"><span>or pick individually</span></div>
|
|
117
117
|
<% end %>
|
|
118
|
-
|
|
118
|
+
|
|
119
|
+
<% available_filter_tags = @all_metrics.flat_map(&:tags).uniq.sort_by(&:name) %>
|
|
120
|
+
<% if available_filter_tags.any? %>
|
|
121
|
+
<div class="ck-metric-tag-filter" data-metric-tag-filter>
|
|
122
|
+
<span class="ck-metric-tag-filter__label">Filter metrics by tag</span>
|
|
123
|
+
<% available_filter_tags.each do |tag| %>
|
|
124
|
+
<button type="button"
|
|
125
|
+
class="tag-mark tag-mark--off ck-metric-tag-filter__chip"
|
|
126
|
+
style="--mark-color: var(--tag-<%= tag.color %>);"
|
|
127
|
+
data-filter-tag="<%= tag.name %>"
|
|
128
|
+
onclick="ckToggleMetricGroupFilter(this)"><%= tag.name %></button>
|
|
129
|
+
<% end %>
|
|
130
|
+
</div>
|
|
131
|
+
<% end %>
|
|
132
|
+
|
|
133
|
+
<div class="ck-metric-checkboxes" data-metric-checkboxes>
|
|
119
134
|
<% @all_metrics.each do |metric| %>
|
|
120
|
-
<label class="ck-checkbox-label">
|
|
135
|
+
<label class="ck-checkbox-label" data-metric-tags="<%= metric.tag_names.join(",") %>">
|
|
121
136
|
<%= check_box_tag "run[metric_ids][]", metric.id, run.metric_ids.include?(metric.id), class: "ck-checkbox", id: "run_metric_#{metric.id}" %>
|
|
122
137
|
<span class="ck-checkbox-label__box" aria-hidden="true"></span>
|
|
123
138
|
<span class="ck-checkbox-label__body">
|
|
@@ -125,6 +140,11 @@
|
|
|
125
140
|
<% if metric.instruction.present? %>
|
|
126
141
|
<span class="ck-checkbox-label__hint"><%= truncate(metric.instruction.to_s, length: 90) %></span>
|
|
127
142
|
<% end %>
|
|
143
|
+
<% if metric.tags.any? %>
|
|
144
|
+
<div class="tag-marks-row">
|
|
145
|
+
<%= render "completion_kit/tags/marks", tags: metric.tags %>
|
|
146
|
+
</div>
|
|
147
|
+
<% end %>
|
|
128
148
|
</span>
|
|
129
149
|
</label>
|
|
130
150
|
<% end %>
|
|
@@ -248,8 +268,10 @@ document.querySelectorAll('input[name="run[metric_ids][]"]').forEach(function(cb
|
|
|
248
268
|
updateRunForm();
|
|
249
269
|
</script>
|
|
250
270
|
|
|
271
|
+
<%= render "completion_kit/tags/picker", record: run, param_namespace: :run %>
|
|
272
|
+
|
|
251
273
|
<div class="ck-actions">
|
|
252
|
-
<%= link_to "Cancel", run.persisted? ? run_path(run) : runs_path, class: ck_button_classes(:light, variant: :outline) %>
|
|
274
|
+
<%= link_to "Cancel", run.persisted? ? run_path(run) : runs_path, class: ck_button_classes(:light, variant: :outline), tabindex: "0" %>
|
|
253
275
|
<%= form.submit(run.persisted? ? "Save run" : "Create run", class: ck_button_classes(:dark), id: "run-submit") %>
|
|
254
276
|
</div>
|
|
255
277
|
</div>
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
<%= link_to run.dataset.name, dataset_path(run.dataset), class: "ck-runs-table__config-link", onclick: "event.stopPropagation();" %>
|
|
14
14
|
<% end %>
|
|
15
15
|
</div>
|
|
16
|
+
<% if run.tags.any? %>
|
|
17
|
+
<div class="tag-marks-row">
|
|
18
|
+
<%= render "completion_kit/tags/marks", tags: run.tags %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
16
21
|
</div>
|
|
17
22
|
</td>
|
|
18
23
|
<td>
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
</section>
|
|
10
10
|
|
|
11
|
+
<%= render "completion_kit/tags/filter_bar",
|
|
12
|
+
available: @available_tags,
|
|
13
|
+
selected: @selected_tags,
|
|
14
|
+
base_path: runs_path %>
|
|
15
|
+
|
|
11
16
|
<% if @runs.any? %>
|
|
12
17
|
<table class="ck-results-table ck-runs-table">
|
|
13
18
|
<thead>
|
|
@@ -26,6 +31,10 @@
|
|
|
26
31
|
<% end %>
|
|
27
32
|
</tbody>
|
|
28
33
|
</table>
|
|
34
|
+
<% elsif @selected_tags.any? %>
|
|
35
|
+
<div class="ck-empty">
|
|
36
|
+
<p>No runs match these tags. <%= link_to "Clear filters", runs_path, class: "ck-link" %>.</p>
|
|
37
|
+
</div>
|
|
29
38
|
<% else %>
|
|
30
39
|
<div class="ck-empty">No runs yet. <%= link_to "Create your first run →", new_run_path, class: "ck-link" %></div>
|
|
31
40
|
<% end %>
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
<%= render "status_header", run: @run %>
|
|
9
9
|
|
|
10
|
+
<% if @run.tags.any? %>
|
|
11
|
+
<div class="tag-marks-row tag-marks-row--header">
|
|
12
|
+
<%= render "completion_kit/tags/marks", tags: @run.tags %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
10
16
|
<% if @run.dataset %>
|
|
11
17
|
<% dataset_lines = @run.dataset.csv_data.to_s.lines %>
|
|
12
18
|
<% dataset_preview_lines = dataset_lines.first(50) %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<p class="ck-settings-kicker">
|
|
2
|
+
Settings <span class="ck-settings-kicker__sep">/</span>
|
|
3
|
+
<% link = local_assigns[:section_link] %>
|
|
4
|
+
<%= link ? link_to(section_label, link, class: "ck-settings-kicker__link") : section_label %>
|
|
5
|
+
<% if local_assigns[:trail].present? %>
|
|
6
|
+
<span class="ck-settings-kicker__sep">/</span>
|
|
7
|
+
<%= trail.html_safe %>
|
|
8
|
+
<% end %>
|
|
9
|
+
</p>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% if available.any? %>
|
|
2
|
+
<div class="ck-tag-filter">
|
|
3
|
+
<span class="ck-tag-filter__label">Filter by tag</span>
|
|
4
|
+
<% available.each do |tag| %>
|
|
5
|
+
<% on = selected.any? { |s| s.id == tag.id } %>
|
|
6
|
+
<%= link_to tag.name,
|
|
7
|
+
tag_filter_url(base_path, selected, tag),
|
|
8
|
+
class: ["tag-mark", ("tag-mark--off" unless on)].compact.join(" "),
|
|
9
|
+
style: "--mark-color: var(--tag-#{tag.color});" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% if selected.any? %>
|
|
12
|
+
<%= link_to "× clear", base_path, class: "ck-tag-filter__clear" %>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
<% end %>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<%= form_with(model: tag, local: true) do |form| %>
|
|
2
|
+
<div class="ck-card ck-form-card">
|
|
3
|
+
<% name_error = tag.errors[:name].first %>
|
|
4
|
+
<div class="ck-field">
|
|
5
|
+
<%= form.label :name, "Tag name", class: "ck-label" %>
|
|
6
|
+
<%= form.text_field :name,
|
|
7
|
+
id: "tag_name",
|
|
8
|
+
class: ["ck-input", ("ck-input--error" if name_error)].compact.join(" "),
|
|
9
|
+
placeholder: "e.g. production",
|
|
10
|
+
autofocus: true,
|
|
11
|
+
"aria-invalid": name_error ? "true" : nil %>
|
|
12
|
+
<% if name_error %>
|
|
13
|
+
<p class="ck-field-error" role="alert"><%= name_error %></p>
|
|
14
|
+
<% elsif !tag.persisted? %>
|
|
15
|
+
<p class="ck-hint">Color is auto-assigned from a 10-color palette.</p>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="ck-actions">
|
|
20
|
+
<% if tag.persisted? %>
|
|
21
|
+
<% applied_n = tag.taggings.count %>
|
|
22
|
+
<% confirm = if applied_n.zero?
|
|
23
|
+
"Delete \"#{tag.name}\"? It's not currently applied to anything."
|
|
24
|
+
else
|
|
25
|
+
"Delete \"#{tag.name}\"? It's currently applied to #{pluralize(applied_n, 'item')} — they'll lose this tag."
|
|
26
|
+
end %>
|
|
27
|
+
<%= button_to tag_path(tag), method: :delete,
|
|
28
|
+
form_class: "inline-block",
|
|
29
|
+
class: "ck-icon-btn",
|
|
30
|
+
title: "Delete tag",
|
|
31
|
+
"aria-label": "Delete tag",
|
|
32
|
+
data: { turbo_confirm: confirm } do %>
|
|
33
|
+
<%= heroicon_tag "trash", variant: :outline, size: 16, "aria-hidden": "true" %>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% end %>
|
|
36
|
+
<%= link_to "Cancel", tags_path, class: ck_button_classes(:light, variant: :outline), tabindex: "0" %>
|
|
37
|
+
<%= form.submit(tag.persisted? ? "Save tag" : "Create tag", class: ck_button_classes(:dark)) %>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div class="ck-field">
|
|
2
|
+
<% label_text = local_assigns[:label].presence || "#{param_namespace.to_s.titleize} tags" %>
|
|
3
|
+
<%= label_tag "#{param_namespace}_tags", label_text, class: "ck-label" %>
|
|
4
|
+
<% all_tags = CompletionKit::Tag.order(:name) %>
|
|
5
|
+
<% selected_ids = record.persisted? ? record.tags.pluck(:id) : [] %>
|
|
6
|
+
<div class="ck-tag-picker">
|
|
7
|
+
<% all_tags.each do |tag| %>
|
|
8
|
+
<% checked = selected_ids.include?(tag.id) %>
|
|
9
|
+
<label class="tag-mark" style="--mark-color: var(--tag-<%= tag.color %>);">
|
|
10
|
+
<%= check_box_tag "#{param_namespace}[tag_names][]", tag.name, checked, hidden: true %>
|
|
11
|
+
<%= tag.name %>
|
|
12
|
+
</label>
|
|
13
|
+
<% end %>
|
|
14
|
+
<%= text_field_tag "#{param_namespace}[tag_names][]", "",
|
|
15
|
+
id: "#{param_namespace}_tags",
|
|
16
|
+
class: "ck-tag-picker__input",
|
|
17
|
+
placeholder: all_tags.any? ? "+ create tag" : "+ create first tag",
|
|
18
|
+
autocomplete: "off" %>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<% original = @tag.name_was.presence || "tag" %>
|
|
2
|
+
<% display_name = @tag.name.presence || original %>
|
|
3
|
+
<% pill = content_tag(:span,
|
|
4
|
+
content_tag(:span, display_name, id: "tag-pill-text", data: { placeholder: original }),
|
|
5
|
+
id: "tag-breadcrumb-pill",
|
|
6
|
+
class: "tag-mark",
|
|
7
|
+
style: "--mark-color: var(--tag-#{@tag.color});") %>
|
|
8
|
+
|
|
9
|
+
<%= render "completion_kit/shared/settings_nav",
|
|
10
|
+
section_label: "Tags",
|
|
11
|
+
section_link: tags_path,
|
|
12
|
+
trail: pill %>
|
|
13
|
+
|
|
14
|
+
<section class="ck-page-header">
|
|
15
|
+
<div>
|
|
16
|
+
<h1 class="ck-title">Edit tag</h1>
|
|
17
|
+
</div>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<%= render "form", tag: @tag %>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<%= render "completion_kit/shared/settings_nav", section_label: "Tags" %>
|
|
2
|
+
|
|
3
|
+
<section class="ck-page-header">
|
|
4
|
+
<div>
|
|
5
|
+
<h1 class="ck-title">Tags</h1>
|
|
6
|
+
<p class="ck-lead">Domain labels for organizing metrics, prompts, runs, and datasets.</p>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="ck-actions">
|
|
9
|
+
<%= link_to "New tag", new_tag_path, class: ck_button_classes(:dark) %>
|
|
10
|
+
</div>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<% if @tags.any? %>
|
|
14
|
+
<table class="ck-results-table ck-tags-table">
|
|
15
|
+
<thead>
|
|
16
|
+
<tr>
|
|
17
|
+
<th>Tag</th>
|
|
18
|
+
<th>Applied to</th>
|
|
19
|
+
<th></th>
|
|
20
|
+
</tr>
|
|
21
|
+
</thead>
|
|
22
|
+
<tbody>
|
|
23
|
+
<% @tags.each do |tag| %>
|
|
24
|
+
<% count = @tagging_counts.fetch(tag.id, 0) %>
|
|
25
|
+
<% by_type = @tagging_by_type.select { |(tid, _), _| tid == tag.id } %>
|
|
26
|
+
<% breakdown = by_type.map { |(_, type), n| pluralize(n, type.demodulize.titleize.downcase) }.join(" · ") %>
|
|
27
|
+
<tr onclick="window.location='<%= edit_tag_path(tag) %>'" style="cursor: pointer;">
|
|
28
|
+
<td><span class="tag-mark tag-mark--lg" style="--mark-color: var(--tag-<%= tag.color %>);"><%= tag.name %></span></td>
|
|
29
|
+
<td class="ck-meta-copy">
|
|
30
|
+
<% if count.zero? %>
|
|
31
|
+
<span class="ck-tags-table__unused">Not used yet</span>
|
|
32
|
+
<% else %>
|
|
33
|
+
<%= breakdown %>
|
|
34
|
+
<% end %>
|
|
35
|
+
</td>
|
|
36
|
+
<td class="ck-results-table__arrow">→</td>
|
|
37
|
+
</tr>
|
|
38
|
+
<% end %>
|
|
39
|
+
</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
<% else %>
|
|
42
|
+
<div class="ck-empty">
|
|
43
|
+
<p>No tags yet. <%= link_to "Create your first tag", new_tag_path, class: "ck-link" %> to start organizing.</p>
|
|
44
|
+
</div>
|
|
45
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<% display_name = @tag.name.presence.to_s %>
|
|
2
|
+
<% pill = content_tag(:span,
|
|
3
|
+
content_tag(:span, display_name, id: "tag-pill-text", data: { placeholder: "" }),
|
|
4
|
+
id: "tag-breadcrumb-pill",
|
|
5
|
+
class: "tag-mark",
|
|
6
|
+
style: "--mark-color: var(--tag-#{@tag.color});") %>
|
|
7
|
+
|
|
8
|
+
<%= render "completion_kit/shared/settings_nav",
|
|
9
|
+
section_label: "Tags",
|
|
10
|
+
section_link: tags_path,
|
|
11
|
+
trail: pill %>
|
|
12
|
+
|
|
13
|
+
<section class="ck-page-header">
|
|
14
|
+
<div>
|
|
15
|
+
<h1 class="ck-title">New tag</h1>
|
|
16
|
+
<p class="ck-lead">Tags help you filter metrics, prompts, runs, and datasets by domain.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<%= render "form", tag: @tag %>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<%= favicon_link_tag "completion_kit/logo.svg", type: "image/svg+xml" %>
|
|
10
10
|
<%= stylesheet_link_tag "completion_kit/application", media: "all" %>
|
|
11
11
|
<%= javascript_include_tag "turbo", type: "module" %>
|
|
12
|
+
<%= javascript_include_tag "completion_kit/application", defer: true %>
|
|
12
13
|
<%= action_cable_meta_tag %>
|
|
13
14
|
</head>
|
|
14
15
|
<body class="ck-app">
|
|
@@ -22,7 +23,16 @@
|
|
|
22
23
|
<%= link_to "Metrics", metrics_path, class: request.path.start_with?(metrics_path) || request.path.start_with?(metric_groups_path) ? ck_button_classes(:dark) : ck_button_classes(:light, variant: :outline) %>
|
|
23
24
|
<%= link_to "Datasets", datasets_path, class: active.(datasets_path) %>
|
|
24
25
|
<%= link_to "Runs", runs_path, class: active.(runs_path) %>
|
|
25
|
-
|
|
26
|
+
<% settings_active = request.path.start_with?(provider_credentials_path) || request.path.start_with?(tags_path) %>
|
|
27
|
+
<details class="ck-settings-menu">
|
|
28
|
+
<summary class="<%= settings_active ? ck_button_classes(:dark) : ck_button_classes(:light, variant: :outline) %> ck-settings-menu__trigger" aria-label="Settings">
|
|
29
|
+
Settings
|
|
30
|
+
</summary>
|
|
31
|
+
<div class="ck-settings-menu__panel" role="menu">
|
|
32
|
+
<%= link_to "Providers", provider_credentials_path, class: "ck-settings-menu__item" %>
|
|
33
|
+
<%= link_to "Tags", tags_path, class: "ck-settings-menu__item" %>
|
|
34
|
+
</div>
|
|
35
|
+
</details>
|
|
26
36
|
<%= link_to "API", api_reference_path, class: active.(api_reference_path) %>
|
|
27
37
|
<% if main_app.respond_to?(:logout_path) %>
|
|
28
38
|
<%= button_to "Log out", main_app.logout_path, method: :delete, class: ck_button_classes(:light, variant: :outline) %>
|
|
@@ -42,136 +52,5 @@
|
|
|
42
52
|
<%= yield %>
|
|
43
53
|
</div>
|
|
44
54
|
</main>
|
|
45
|
-
<script>
|
|
46
|
-
document.addEventListener("turbo:load", function() {
|
|
47
|
-
document.querySelectorAll("[data-local-time]").forEach(function(el) {
|
|
48
|
-
var d = new Date(el.getAttribute("datetime"));
|
|
49
|
-
el.textContent = d.toLocaleString(undefined, {year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});
|
|
50
|
-
});
|
|
51
|
-
ckTickRelativeTimes();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
function ckRelativeTime(then) {
|
|
55
|
-
var seconds = Math.round((Date.now() - then.getTime()) / 1000);
|
|
56
|
-
if (seconds < 5) return "just now";
|
|
57
|
-
if (seconds < 60) return "less than a minute";
|
|
58
|
-
var minutes = Math.round(seconds / 60);
|
|
59
|
-
if (minutes < 60) return minutes === 1 ? "1 minute" : minutes + " minutes";
|
|
60
|
-
var hours = Math.round(minutes / 60);
|
|
61
|
-
if (hours < 24) return hours === 1 ? "about 1 hour" : "about " + hours + " hours";
|
|
62
|
-
var days = Math.round(hours / 24);
|
|
63
|
-
if (days < 30) return days === 1 ? "1 day" : days + " days";
|
|
64
|
-
var months = Math.round(days / 30);
|
|
65
|
-
if (months < 12) return months === 1 ? "about 1 month" : "about " + months + " months";
|
|
66
|
-
var years = Math.round(days / 365);
|
|
67
|
-
return years === 1 ? "about 1 year" : "about " + years + " years";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function ckRelativeTimeCompact(then) {
|
|
71
|
-
var seconds = Math.round((Date.now() - then.getTime()) / 1000);
|
|
72
|
-
if (seconds < 60) return "now";
|
|
73
|
-
var minutes = Math.round(seconds / 60);
|
|
74
|
-
if (minutes < 60) return minutes + "m";
|
|
75
|
-
var hours = Math.round(minutes / 60);
|
|
76
|
-
if (hours < 24) return hours + "h";
|
|
77
|
-
var days = Math.round(hours / 24);
|
|
78
|
-
if (days < 30) return days + "d";
|
|
79
|
-
var months = Math.round(days / 30);
|
|
80
|
-
if (months < 12) return months + "mo";
|
|
81
|
-
var years = Math.round(days / 365);
|
|
82
|
-
return years + "y";
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function ckTickRelativeTimes() {
|
|
86
|
-
document.querySelectorAll("[data-relative-time]").forEach(function(el) {
|
|
87
|
-
var then = new Date(el.getAttribute("datetime"));
|
|
88
|
-
if (isNaN(then.getTime())) return;
|
|
89
|
-
var verbose = el.getAttribute("data-relative-time") === "verbose";
|
|
90
|
-
el.textContent = verbose ? ckRelativeTime(then) : ckRelativeTimeCompact(then);
|
|
91
|
-
el.setAttribute("title", then.toLocaleString());
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!window.ckRelativeTimeInterval) {
|
|
96
|
-
window.ckRelativeTimeInterval = setInterval(ckTickRelativeTimes, 30000);
|
|
97
|
-
}
|
|
98
|
-
document.addEventListener("turbo:before-stream-render", function() {
|
|
99
|
-
requestAnimationFrame(ckTickRelativeTimes);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
var ckCsvHoverTimer = null;
|
|
103
|
-
var ckCsvHoverRow = null;
|
|
104
|
-
document.addEventListener("mouseover", function(e) {
|
|
105
|
-
var row = e.target.closest && e.target.closest(".ck-csv-table tbody tr");
|
|
106
|
-
if (!row || row === ckCsvHoverRow) return;
|
|
107
|
-
if (ckCsvHoverRow) ckCsvHoverRow.classList.remove("ck-csv-row--expanded");
|
|
108
|
-
ckCsvHoverRow = row;
|
|
109
|
-
clearTimeout(ckCsvHoverTimer);
|
|
110
|
-
ckCsvHoverTimer = setTimeout(function() {
|
|
111
|
-
if (ckCsvHoverRow === row) row.classList.add("ck-csv-row--expanded");
|
|
112
|
-
}, 350);
|
|
113
|
-
});
|
|
114
|
-
document.addEventListener("mouseout", function(e) {
|
|
115
|
-
var row = e.target.closest && e.target.closest(".ck-csv-table tbody tr");
|
|
116
|
-
if (!row) return;
|
|
117
|
-
var related = e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest(".ck-csv-table tbody tr");
|
|
118
|
-
if (related === row) return;
|
|
119
|
-
clearTimeout(ckCsvHoverTimer);
|
|
120
|
-
row.classList.remove("ck-csv-row--expanded");
|
|
121
|
-
if (ckCsvHoverRow === row) ckCsvHoverRow = null;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
var ckRefreshing = false;
|
|
125
|
-
function ckRefreshModels() {
|
|
126
|
-
if (ckRefreshing) return;
|
|
127
|
-
ckRefreshing = true;
|
|
128
|
-
var btn = document.querySelector('.ck-icon-btn[title="Refresh models"]');
|
|
129
|
-
if (btn) btn.classList.add('ck-icon-btn--spinning');
|
|
130
|
-
ckUpdateRefreshProgress();
|
|
131
|
-
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute("content");
|
|
132
|
-
fetch("/completion_kit/refresh_models", {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: { "X-CSRF-Token": csrfToken }
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function ckUpdateRefreshProgress() {
|
|
139
|
-
var status = document.getElementById('refresh-status');
|
|
140
|
-
if (!status) return;
|
|
141
|
-
var carriers = document.querySelectorAll('[data-refresh-progress-carriers] [id^="discovery_status_"]');
|
|
142
|
-
var totalCurrent = 0, totalTotal = 0, anyDiscovering = false;
|
|
143
|
-
carriers.forEach(function(node) {
|
|
144
|
-
if (!node.querySelector('.ck-discovery-bar')) return;
|
|
145
|
-
if (node.querySelector('.ck-discovery-bar--failed') || node.querySelector('.ck-discovery-bar--completed')) return;
|
|
146
|
-
anyDiscovering = true;
|
|
147
|
-
var match = node.textContent.match(/(\d+)\s*\/\s*(\d+)/);
|
|
148
|
-
if (match) {
|
|
149
|
-
totalCurrent += parseInt(match[1], 10);
|
|
150
|
-
totalTotal += parseInt(match[2], 10);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
if (anyDiscovering || ckRefreshing) {
|
|
154
|
-
if (totalTotal > 0) {
|
|
155
|
-
status.textContent = 'Refreshing models\u2026 ' + totalCurrent + '/' + totalTotal;
|
|
156
|
-
} else {
|
|
157
|
-
status.textContent = 'Refreshing models\u2026';
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
document.addEventListener("turbo:before-stream-render", function(event) {
|
|
163
|
-
var target = event.target.getAttribute("target");
|
|
164
|
-
if (target && target.indexOf("discovery_status_") === 0) {
|
|
165
|
-
requestAnimationFrame(ckUpdateRefreshProgress);
|
|
166
|
-
}
|
|
167
|
-
if (target === "prompt_llm_model" || target === "run_judge_model") {
|
|
168
|
-
ckRefreshing = false;
|
|
169
|
-
var btn = document.querySelector('.ck-icon-btn[title="Refresh models"]');
|
|
170
|
-
if (btn) btn.classList.remove('ck-icon-btn--spinning');
|
|
171
|
-
var status = document.getElementById('refresh-status');
|
|
172
|
-
if (status) { status.textContent = 'Models updated.'; setTimeout(function() { status.textContent = '\u00a0'; }, 3000); }
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
</script>
|
|
176
55
|
</body>
|
|
177
56
|
</html>
|
data/config/routes.rb
CHANGED
|
@@ -10,6 +10,7 @@ CompletionKit::Engine.routes.draw do
|
|
|
10
10
|
resources :datasets
|
|
11
11
|
resources :metrics
|
|
12
12
|
resources :metric_groups
|
|
13
|
+
resources :tags
|
|
13
14
|
|
|
14
15
|
resources :runs do
|
|
15
16
|
member do
|
|
@@ -52,6 +53,7 @@ CompletionKit::Engine.routes.draw do
|
|
|
52
53
|
resources :datasets
|
|
53
54
|
resources :metrics
|
|
54
55
|
resources :metric_groups
|
|
56
|
+
resources :tags
|
|
55
57
|
resources :provider_credentials
|
|
56
58
|
end
|
|
57
59
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class CreateCompletionKitTags < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :completion_kit_tags do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :color, null: false
|
|
6
|
+
t.timestamps
|
|
7
|
+
end
|
|
8
|
+
add_index :completion_kit_tags, :name, unique: true
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateCompletionKitTaggings < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :completion_kit_taggings do |t|
|
|
4
|
+
t.references :tag, null: false,
|
|
5
|
+
foreign_key: { to_table: :completion_kit_tags }
|
|
6
|
+
t.string :taggable_type, null: false
|
|
7
|
+
t.bigint :taggable_id, null: false
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
add_index :completion_kit_taggings, [:taggable_type, :taggable_id]
|
|
11
|
+
add_index :completion_kit_taggings,
|
|
12
|
+
[:tag_id, :taggable_type, :taggable_id],
|
|
13
|
+
unique: true,
|
|
14
|
+
name: "idx_taggings_unique"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -8,7 +8,11 @@ module CompletionKit
|
|
|
8
8
|
paths.add "app/services", eager_load: true
|
|
9
9
|
|
|
10
10
|
def self.register_assets(app)
|
|
11
|
-
app.config.assets.precompile += %w(
|
|
11
|
+
app.config.assets.precompile += %w(
|
|
12
|
+
completion_kit/application.css
|
|
13
|
+
completion_kit/application.js
|
|
14
|
+
completion_kit/logo.svg
|
|
15
|
+
)
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
initializer("completion_kit.assets") { |app| Engine.register_assets(app) }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: completion-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -228,6 +228,7 @@ files:
|
|
|
228
228
|
- app/assets/config/completion_kit_manifest.js
|
|
229
229
|
- app/assets/config/manifest.js
|
|
230
230
|
- app/assets/images/completion_kit/logo.svg
|
|
231
|
+
- app/assets/javascripts/completion_kit/application.js
|
|
231
232
|
- app/assets/stylesheets/completion_kit/application.css
|
|
232
233
|
- app/controllers/completion_kit/api/v1/base_controller.rb
|
|
233
234
|
- app/controllers/completion_kit/api/v1/datasets_controller.rb
|
|
@@ -237,6 +238,7 @@ files:
|
|
|
237
238
|
- app/controllers/completion_kit/api/v1/provider_credentials_controller.rb
|
|
238
239
|
- app/controllers/completion_kit/api/v1/responses_controller.rb
|
|
239
240
|
- app/controllers/completion_kit/api/v1/runs_controller.rb
|
|
241
|
+
- app/controllers/completion_kit/api/v1/tags_controller.rb
|
|
240
242
|
- app/controllers/completion_kit/api_reference_controller.rb
|
|
241
243
|
- app/controllers/completion_kit/application_controller.rb
|
|
242
244
|
- app/controllers/completion_kit/datasets_controller.rb
|
|
@@ -248,6 +250,8 @@ files:
|
|
|
248
250
|
- app/controllers/completion_kit/responses_controller.rb
|
|
249
251
|
- app/controllers/completion_kit/runs_controller.rb
|
|
250
252
|
- app/controllers/completion_kit/suggestions_controller.rb
|
|
253
|
+
- app/controllers/completion_kit/tags_controller.rb
|
|
254
|
+
- app/controllers/concerns/completion_kit/tag_filtering.rb
|
|
251
255
|
- app/helpers/completion_kit/application_helper.rb
|
|
252
256
|
- app/jobs/completion_kit/application_job.rb
|
|
253
257
|
- app/jobs/completion_kit/generate_row_job.rb
|
|
@@ -268,6 +272,9 @@ files:
|
|
|
268
272
|
- app/models/completion_kit/run.rb
|
|
269
273
|
- app/models/completion_kit/run_metric.rb
|
|
270
274
|
- app/models/completion_kit/suggestion.rb
|
|
275
|
+
- app/models/completion_kit/tag.rb
|
|
276
|
+
- app/models/completion_kit/tagging.rb
|
|
277
|
+
- app/models/concerns/completion_kit/taggable.rb
|
|
271
278
|
- app/services/completion_kit/anthropic_client.rb
|
|
272
279
|
- app/services/completion_kit/api_config.rb
|
|
273
280
|
- app/services/completion_kit/csv_processor.rb
|
|
@@ -282,6 +289,7 @@ files:
|
|
|
282
289
|
- app/services/completion_kit/mcp_tools/provider_credentials.rb
|
|
283
290
|
- app/services/completion_kit/mcp_tools/responses.rb
|
|
284
291
|
- app/services/completion_kit/mcp_tools/runs.rb
|
|
292
|
+
- app/services/completion_kit/mcp_tools/tags.rb
|
|
285
293
|
- app/services/completion_kit/model_discovery_service.rb
|
|
286
294
|
- app/services/completion_kit/ollama_client.rb
|
|
287
295
|
- app/services/completion_kit/open_ai_client.rb
|
|
@@ -329,7 +337,15 @@ files:
|
|
|
329
337
|
- app/views/completion_kit/runs/index.html.erb
|
|
330
338
|
- app/views/completion_kit/runs/new.html.erb
|
|
331
339
|
- app/views/completion_kit/runs/show.html.erb
|
|
340
|
+
- app/views/completion_kit/shared/_settings_nav.html.erb
|
|
332
341
|
- app/views/completion_kit/suggestions/show.html.erb
|
|
342
|
+
- app/views/completion_kit/tags/_filter_bar.html.erb
|
|
343
|
+
- app/views/completion_kit/tags/_form.html.erb
|
|
344
|
+
- app/views/completion_kit/tags/_marks.html.erb
|
|
345
|
+
- app/views/completion_kit/tags/_picker.html.erb
|
|
346
|
+
- app/views/completion_kit/tags/edit.html.erb
|
|
347
|
+
- app/views/completion_kit/tags/index.html.erb
|
|
348
|
+
- app/views/completion_kit/tags/new.html.erb
|
|
333
349
|
- app/views/layouts/completion_kit/application.html.erb
|
|
334
350
|
- config/routes.rb
|
|
335
351
|
- db/migrate/20260311000001_create_completion_kit_tables.rb
|
|
@@ -351,6 +367,8 @@ files:
|
|
|
351
367
|
- db/migrate/20260501000005_collapse_run_status_and_add_failure_summary.rb
|
|
352
368
|
- db/migrate/20260507000001_add_discovery_error_to_provider_credentials.rb
|
|
353
369
|
- db/migrate/20260507150000_add_temperature_ignored_to_runs.rb
|
|
370
|
+
- db/migrate/20260509000001_create_completion_kit_tags.rb
|
|
371
|
+
- db/migrate/20260509000002_create_completion_kit_taggings.rb
|
|
354
372
|
- lib/completion-kit.rb
|
|
355
373
|
- lib/completion_kit.rb
|
|
356
374
|
- lib/completion_kit/concurrency_check.rb
|