completion-kit 0.5.19 → 0.5.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/completion_kit/application.css +127 -0
- data/app/controllers/completion_kit/dashboard_dismissals_controller.rb +41 -0
- data/app/models/completion_kit/dashboard_dismissal.rb +18 -0
- data/app/models/completion_kit/metric.rb +1 -0
- data/app/models/completion_kit/response.rb +1 -0
- data/app/models/completion_kit/review.rb +1 -0
- data/app/models/completion_kit/run.rb +1 -0
- data/app/services/completion_kit/dashboard_stats.rb +89 -21
- data/app/views/completion_kit/dashboard/_eye_icon.html.erb +1 -0
- data/app/views/completion_kit/dashboard/_eye_off_icon.html.erb +1 -0
- data/app/views/completion_kit/dashboard/_failures_card.html.erb +47 -0
- data/app/views/completion_kit/dashboard/_worst_metric_card.html.erb +54 -0
- data/app/views/completion_kit/dashboard_dismissals/refresh.turbo_stream.erb +8 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20260516000001_create_completion_kit_dashboard_dismissals.rb +15 -0
- data/lib/completion_kit/version.rb +1 -1
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b8563167a68aab93caf0e05022c5f1216d096eaae162173e39c5061ba253f3e0
|
|
4
|
+
data.tar.gz: 7de1eb04e23d658744195330b7d3f0383d9c02c5e3d3a78788aaf96bc9a0d1b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ad34abea12f77b56701f313aa471193e384698a0a2add21724510f210018ee86de287d7fc449df5574a0d9ca12c90883c16d8170b53f254852659b9c4e76ca1
|
|
7
|
+
data.tar.gz: 3b4118ce8416f45bf42dff0a88dbe20f0d2061cf66f456f5070c3dc51c80dc81e400329ea342b476a77fc44aa461ad161d784e8fe3c50e6d8cfff9df3331b042
|
|
@@ -404,6 +404,15 @@ form.button_to {
|
|
|
404
404
|
font-size: 0.78rem;
|
|
405
405
|
color: var(--ck-muted);
|
|
406
406
|
}
|
|
407
|
+
.ck-stat-card__foot--split {
|
|
408
|
+
display: flex;
|
|
409
|
+
align-items: center;
|
|
410
|
+
gap: 0.55rem;
|
|
411
|
+
}
|
|
412
|
+
.ck-stat-card__foot--split > :first-child {
|
|
413
|
+
margin-right: auto;
|
|
414
|
+
min-width: 0;
|
|
415
|
+
}
|
|
407
416
|
.ck-stat-card__figure { color: var(--ck-text); }
|
|
408
417
|
|
|
409
418
|
.ck-stat-card__body {
|
|
@@ -4630,3 +4639,121 @@ a.tag-mark {
|
|
|
4630
4639
|
.ck-launch__ready-panel,
|
|
4631
4640
|
.ck-launch__step { animation: none; }
|
|
4632
4641
|
}
|
|
4642
|
+
|
|
4643
|
+
.ck-icon-btn {
|
|
4644
|
+
display: inline-flex;
|
|
4645
|
+
align-items: center;
|
|
4646
|
+
justify-content: center;
|
|
4647
|
+
flex: none;
|
|
4648
|
+
width: 1.65rem;
|
|
4649
|
+
height: 1.65rem;
|
|
4650
|
+
padding: 0;
|
|
4651
|
+
color: var(--ck-dim);
|
|
4652
|
+
background: transparent;
|
|
4653
|
+
border: 1px solid var(--ck-line-strong);
|
|
4654
|
+
border-radius: 6px;
|
|
4655
|
+
cursor: pointer;
|
|
4656
|
+
transition: color 0.15s ease, border-color 0.15s ease;
|
|
4657
|
+
}
|
|
4658
|
+
.ck-icon-btn:hover {
|
|
4659
|
+
color: var(--ck-text);
|
|
4660
|
+
border-color: var(--ck-dim);
|
|
4661
|
+
}
|
|
4662
|
+
.ck-icon-btn svg { display: block; }
|
|
4663
|
+
.ck-icon-btn form,
|
|
4664
|
+
.ck-failure-list__item form,
|
|
4665
|
+
.ck-flyout__item form { display: inline-flex; margin: 0; }
|
|
4666
|
+
|
|
4667
|
+
.ck-failure-list {
|
|
4668
|
+
list-style: none;
|
|
4669
|
+
margin: 0.6rem 0 0;
|
|
4670
|
+
padding: 0;
|
|
4671
|
+
display: flex;
|
|
4672
|
+
flex-direction: column;
|
|
4673
|
+
gap: 0.35rem;
|
|
4674
|
+
}
|
|
4675
|
+
.ck-failure-list__item {
|
|
4676
|
+
display: flex;
|
|
4677
|
+
align-items: center;
|
|
4678
|
+
gap: 0.5rem;
|
|
4679
|
+
font-size: 0.8rem;
|
|
4680
|
+
}
|
|
4681
|
+
.ck-failure-list__surface {
|
|
4682
|
+
flex: none;
|
|
4683
|
+
padding: 0.06rem 0.45rem;
|
|
4684
|
+
font-size: 0.64rem;
|
|
4685
|
+
font-weight: 700;
|
|
4686
|
+
text-transform: uppercase;
|
|
4687
|
+
letter-spacing: 0.04em;
|
|
4688
|
+
border-radius: 4px;
|
|
4689
|
+
background: var(--ck-surface-soft);
|
|
4690
|
+
color: var(--ck-dim);
|
|
4691
|
+
}
|
|
4692
|
+
.ck-failure-list__surface--run { color: var(--ck-warning); }
|
|
4693
|
+
.ck-failure-list__surface--generation { color: var(--ck-danger); }
|
|
4694
|
+
.ck-failure-list__surface--judge { color: var(--ck-info); }
|
|
4695
|
+
.ck-failure-list__cause {
|
|
4696
|
+
overflow: hidden;
|
|
4697
|
+
text-overflow: ellipsis;
|
|
4698
|
+
white-space: nowrap;
|
|
4699
|
+
}
|
|
4700
|
+
.ck-failure-list__item .ck-icon-btn { margin-left: auto; }
|
|
4701
|
+
|
|
4702
|
+
/* Flyout: lives inline in the card's always-present footer row, so the
|
|
4703
|
+
toggle appearing or disappearing never changes the card's height. The
|
|
4704
|
+
panel is an absolute popover that opens upward over the card body. */
|
|
4705
|
+
.ck-flyout {
|
|
4706
|
+
position: relative;
|
|
4707
|
+
flex: none;
|
|
4708
|
+
}
|
|
4709
|
+
.ck-flyout__toggle {
|
|
4710
|
+
display: inline-flex;
|
|
4711
|
+
align-items: center;
|
|
4712
|
+
gap: 0.3rem;
|
|
4713
|
+
font-family: var(--ck-mono);
|
|
4714
|
+
font-size: 0.66rem;
|
|
4715
|
+
letter-spacing: 0.05em;
|
|
4716
|
+
text-transform: uppercase;
|
|
4717
|
+
color: var(--ck-dim);
|
|
4718
|
+
cursor: pointer;
|
|
4719
|
+
list-style: none;
|
|
4720
|
+
}
|
|
4721
|
+
.ck-flyout__toggle::-webkit-details-marker { display: none; }
|
|
4722
|
+
.ck-flyout__toggle::marker { content: ""; }
|
|
4723
|
+
.ck-flyout__toggle:hover { color: var(--ck-text); }
|
|
4724
|
+
.ck-flyout[open] .ck-flyout__toggle { color: var(--ck-text); }
|
|
4725
|
+
.ck-flyout__panel {
|
|
4726
|
+
position: absolute;
|
|
4727
|
+
bottom: calc(100% + 0.5rem);
|
|
4728
|
+
right: 0;
|
|
4729
|
+
z-index: 30;
|
|
4730
|
+
width: 16rem;
|
|
4731
|
+
max-height: 13rem;
|
|
4732
|
+
overflow-y: auto;
|
|
4733
|
+
margin: 0;
|
|
4734
|
+
padding: 0.45rem;
|
|
4735
|
+
list-style: none;
|
|
4736
|
+
display: flex;
|
|
4737
|
+
flex-direction: column;
|
|
4738
|
+
gap: 0.2rem;
|
|
4739
|
+
background: var(--ck-bg-strong);
|
|
4740
|
+
border: 1px solid var(--ck-line-strong);
|
|
4741
|
+
border-radius: var(--ck-radius);
|
|
4742
|
+
box-shadow: 0 -14px 30px rgba(0, 0, 0, 0.5);
|
|
4743
|
+
}
|
|
4744
|
+
.ck-flyout__item {
|
|
4745
|
+
display: flex;
|
|
4746
|
+
align-items: center;
|
|
4747
|
+
justify-content: space-between;
|
|
4748
|
+
gap: 0.6rem;
|
|
4749
|
+
padding: 0.3rem 0.4rem;
|
|
4750
|
+
font-size: 0.78rem;
|
|
4751
|
+
border-radius: 5px;
|
|
4752
|
+
}
|
|
4753
|
+
.ck-flyout__item:hover { background: var(--ck-surface-hover); }
|
|
4754
|
+
.ck-flyout__label { color: var(--ck-text); }
|
|
4755
|
+
.ck-flyout__meta {
|
|
4756
|
+
margin-left: 0.35rem;
|
|
4757
|
+
color: var(--ck-dim);
|
|
4758
|
+
font-size: 0.7rem;
|
|
4759
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module CompletionKit
|
|
2
|
+
class DashboardDismissalsController < ApplicationController
|
|
3
|
+
WINDOW = 7.days
|
|
4
|
+
|
|
5
|
+
def create
|
|
6
|
+
record = resolve_dismissable
|
|
7
|
+
DashboardDismissal.create(dismissable: record, baseline_score: baseline_for(record))
|
|
8
|
+
render_cards
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def destroy
|
|
12
|
+
DashboardDismissal.find(params[:id]).destroy
|
|
13
|
+
render_cards
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def dismissal_params
|
|
19
|
+
params.require(:dashboard_dismissal).permit(:dismissable_type, :dismissable_id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resolve_dismissable
|
|
23
|
+
type = dismissal_params[:dismissable_type]
|
|
24
|
+
raise ActiveRecord::RecordNotFound unless DashboardDismissal::DISMISSABLE_TYPES.include?(type)
|
|
25
|
+
type.constantize.find(dismissal_params[:dismissable_id])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def baseline_for(record)
|
|
29
|
+
return nil unless record.is_a?(Metric)
|
|
30
|
+
DashboardStats.metric_average(record.id, since: WINDOW.ago)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render_cards
|
|
34
|
+
@worst_metric = DashboardStats.worst_metric(since: WINDOW.ago)
|
|
35
|
+
@failures = DashboardStats.failures(since: WINDOW.ago)
|
|
36
|
+
@ignored_metrics = DashboardDismissal.metrics
|
|
37
|
+
@ignored_failures = DashboardDismissal.failures
|
|
38
|
+
render :refresh, formats: [:turbo_stream]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module CompletionKit
|
|
2
|
+
class DashboardDismissal < ApplicationRecord
|
|
3
|
+
FAILURE_TYPES = %w[
|
|
4
|
+
CompletionKit::Run
|
|
5
|
+
CompletionKit::Response
|
|
6
|
+
CompletionKit::Review
|
|
7
|
+
].freeze
|
|
8
|
+
DISMISSABLE_TYPES = (["CompletionKit::Metric"] + FAILURE_TYPES).freeze
|
|
9
|
+
|
|
10
|
+
belongs_to :dismissable, polymorphic: true
|
|
11
|
+
|
|
12
|
+
validates :dismissable_type, inclusion: { in: DISMISSABLE_TYPES }
|
|
13
|
+
validates :dismissable_id, uniqueness: { scope: :dismissable_type }
|
|
14
|
+
|
|
15
|
+
scope :metrics, -> { where(dismissable_type: "CompletionKit::Metric").includes(:dismissable) }
|
|
16
|
+
scope :failures, -> { where(dismissable_type: FAILURE_TYPES).includes(:dismissable) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -13,6 +13,7 @@ module CompletionKit
|
|
|
13
13
|
has_many :metric_group_memberships, dependent: :destroy
|
|
14
14
|
has_many :metric_groups, through: :metric_group_memberships, source: :metric_group
|
|
15
15
|
has_many :reviews, dependent: :nullify
|
|
16
|
+
has_many :dashboard_dismissals, as: :dismissable, dependent: :destroy
|
|
16
17
|
|
|
17
18
|
serialize :rubric_bands, coder: JSON
|
|
18
19
|
|
|
@@ -11,6 +11,7 @@ module CompletionKit
|
|
|
11
11
|
has_many :run_metrics, -> { order(:position) }, dependent: :destroy
|
|
12
12
|
has_many :metrics, through: :run_metrics
|
|
13
13
|
has_many :suggestions, dependent: :destroy
|
|
14
|
+
has_many :dashboard_dismissals, as: :dismissable, dependent: :destroy
|
|
14
15
|
|
|
15
16
|
validates :name, presence: true
|
|
16
17
|
validates :status, inclusion: { in: STATUSES }
|
|
@@ -17,32 +17,85 @@ module CompletionKit
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# The metric with the lowest average judge score across succeeded reviews
|
|
20
|
-
# in the window — the prompt-engineering target.
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# in the window — the prompt-engineering target. Dismissed metrics are
|
|
21
|
+
# skipped while their average holds at or above the score snapshotted when
|
|
22
|
+
# they were dismissed; a metric that regresses below that baseline
|
|
23
|
+
# resurfaces and its stale dismissal is cleared. Returns nil when nothing
|
|
24
|
+
# qualifies. `response` is the single worst-scoring response, for a deep
|
|
25
|
+
# link.
|
|
23
26
|
def self.worst_metric(since:)
|
|
24
|
-
averages = scored_reviews_since(since)
|
|
27
|
+
averages = scored_reviews_since(since)
|
|
28
|
+
.joins(:metric)
|
|
29
|
+
.group("completion_kit_metrics.id")
|
|
30
|
+
.average(:ai_score)
|
|
25
31
|
return nil if averages.empty?
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
dismissals = metric_dismissals
|
|
34
|
+
metrics = Metric.where(id: averages.keys).index_by(&:id)
|
|
35
|
+
|
|
36
|
+
averages.sort_by { |_id, avg| avg }.each do |metric_id, avg|
|
|
37
|
+
rounded = avg.to_f.round(2)
|
|
38
|
+
dismissal = dismissals[metric_id]
|
|
39
|
+
next if dismissal && rounded >= dismissal.baseline_score.to_f
|
|
40
|
+
|
|
41
|
+
dismissal&.destroy
|
|
42
|
+
worst = scored_reviews_since(since).where(metric_id: metric_id).order(:ai_score).first
|
|
43
|
+
metric = metrics[metric_id]
|
|
44
|
+
return {
|
|
45
|
+
metric: metric,
|
|
46
|
+
name: metric.name,
|
|
47
|
+
avg: rounded,
|
|
48
|
+
response: worst.response,
|
|
49
|
+
score: worst.ai_score.to_f
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The rounded average judge score for one metric across the window, or nil
|
|
56
|
+
# when it has no scored reviews. Used to snapshot a dismissal's baseline.
|
|
57
|
+
def self.metric_average(metric_id, since:)
|
|
58
|
+
scored_reviews_since(since).where(metric_id: metric_id).average(:ai_score)&.to_f&.round(2)
|
|
40
59
|
end
|
|
41
60
|
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
# Everything that terminally failed in the window across all three
|
|
62
|
+
# surfaces — failed runs, failed generations, failed judge reviews —
|
|
63
|
+
# excluding any the user has dismissed. Returns a count and an items list
|
|
64
|
+
# ordered most-recent-first; each item carries its surface, the failing
|
|
65
|
+
# record, the run it belongs to (for a deep link), and a cause string.
|
|
66
|
+
def self.failures(since:)
|
|
67
|
+
dismissed = failure_dismissal_keys
|
|
68
|
+
items = []
|
|
69
|
+
|
|
70
|
+
Run.where(status: "failed").where("created_at >= ?", since).find_each do |run|
|
|
71
|
+
next if dismissed.include?(["CompletionKit::Run", run.id])
|
|
72
|
+
items << {
|
|
73
|
+
surface: "run", record: run, run: run,
|
|
74
|
+
cause: run.failure_summary.presence || "Run failed", at: run.updated_at
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Response.where(status: "failed").where("created_at >= ?", since)
|
|
79
|
+
.includes(:run).find_each do |response|
|
|
80
|
+
next if dismissed.include?(["CompletionKit::Response", response.id])
|
|
81
|
+
items << {
|
|
82
|
+
surface: "generation", record: response, run: response.run,
|
|
83
|
+
cause: failure_cause(response), at: response.updated_at
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Review.where(status: "failed").where("completion_kit_reviews.created_at >= ?", since)
|
|
88
|
+
.includes(response: :run).find_each do |review|
|
|
89
|
+
next if dismissed.include?(["CompletionKit::Review", review.id])
|
|
90
|
+
items << {
|
|
91
|
+
surface: "judge", record: review, run: review.response.run,
|
|
92
|
+
cause: failure_cause(review), at: review.updated_at
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
items.sort_by! { |item| item[:at] }
|
|
97
|
+
items.reverse!
|
|
98
|
+
{ count: items.size, items: items }
|
|
46
99
|
end
|
|
47
100
|
|
|
48
101
|
# The most recent measurable change per prompt family — gains and
|
|
@@ -95,5 +148,20 @@ module CompletionKit
|
|
|
95
148
|
.where.not(ai_score: nil)
|
|
96
149
|
end
|
|
97
150
|
private_class_method :scored_reviews_since
|
|
151
|
+
|
|
152
|
+
def self.metric_dismissals
|
|
153
|
+
DashboardDismissal.where(dismissable_type: "CompletionKit::Metric").index_by(&:dismissable_id)
|
|
154
|
+
end
|
|
155
|
+
private_class_method :metric_dismissals
|
|
156
|
+
|
|
157
|
+
def self.failure_dismissal_keys
|
|
158
|
+
DashboardDismissal.failures.map { |d| [d.dismissable_type, d.dismissable_id] }.to_set
|
|
159
|
+
end
|
|
160
|
+
private_class_method :failure_dismissal_keys
|
|
161
|
+
|
|
162
|
+
def self.failure_cause(record)
|
|
163
|
+
record.error_class.presence || "Unknown error"
|
|
164
|
+
end
|
|
165
|
+
private_class_method :failure_cause
|
|
98
166
|
end
|
|
99
167
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<div class="ck-card ck-stat-card ck-rise" id="ck-failures-card" style="--rise-delay: 180ms;">
|
|
2
|
+
<p class="ck-kicker">Failures · last 7 days</p>
|
|
3
|
+
<div class="ck-stat-card__body">
|
|
4
|
+
<span class="ck-stat-card__count<%= failures[:count].positive? ? ' is-danger' : ' is-clean' %>"><%= failures[:count] %></span>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<% if failures[:items].any? %>
|
|
8
|
+
<ul class="ck-failure-list">
|
|
9
|
+
<% failures[:items].each do |item| %>
|
|
10
|
+
<li class="ck-failure-list__item">
|
|
11
|
+
<span class="ck-failure-list__surface ck-failure-list__surface--<%= item[:surface] %>"><%= item[:surface] %></span>
|
|
12
|
+
<% if item[:run] %>
|
|
13
|
+
<%= link_to item[:cause], completion_kit.run_path(item[:run]), class: "ck-link ck-failure-list__cause" %>
|
|
14
|
+
<% else %>
|
|
15
|
+
<span class="ck-failure-list__cause"><%= item[:cause] %></span>
|
|
16
|
+
<% end %>
|
|
17
|
+
<%= button_to completion_kit.dashboard_dismissals_path,
|
|
18
|
+
params: { dashboard_dismissal: { dismissable_type: item[:record].class.name,
|
|
19
|
+
dismissable_id: item[:record].id } },
|
|
20
|
+
class: "ck-icon-btn", title: "Ignore this failure",
|
|
21
|
+
"aria-label": "Ignore #{item[:surface]} failure" do %><%= render "completion_kit/dashboard/eye_off_icon" %><% end %>
|
|
22
|
+
</li>
|
|
23
|
+
<% end %>
|
|
24
|
+
</ul>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
<% if failures[:items].empty? || ignored_failures.any? %>
|
|
28
|
+
<div class="ck-stat-card__foot ck-stat-card__foot--split">
|
|
29
|
+
<span><% if failures[:items].empty? %>All clear — nothing failed this week.<% end %></span>
|
|
30
|
+
<% if ignored_failures.any? %>
|
|
31
|
+
<details class="ck-flyout">
|
|
32
|
+
<summary class="ck-flyout__toggle"><%= ignored_failures.size %> ignored</summary>
|
|
33
|
+
<ul class="ck-flyout__panel">
|
|
34
|
+
<% ignored_failures.each do |dismissal| %>
|
|
35
|
+
<li class="ck-flyout__item">
|
|
36
|
+
<span class="ck-flyout__label"><%= dismissal.dismissable_type.demodulize %> #<%= dismissal.dismissable_id %></span>
|
|
37
|
+
<%= button_to completion_kit.dashboard_dismissal_path(dismissal),
|
|
38
|
+
method: :delete, class: "ck-icon-btn", title: "Un-ignore",
|
|
39
|
+
"aria-label": "Un-ignore #{dismissal.dismissable_type.demodulize} #{dismissal.dismissable_id}" do %><%= render "completion_kit/dashboard/eye_icon" %><% end %>
|
|
40
|
+
</li>
|
|
41
|
+
<% end %>
|
|
42
|
+
</ul>
|
|
43
|
+
</details>
|
|
44
|
+
<% end %>
|
|
45
|
+
</div>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<div class="ck-card ck-stat-card ck-rise" id="ck-worst-metric-card" style="--rise-delay: 120ms;">
|
|
2
|
+
<p class="ck-kicker">Worst metric · last 7 days</p>
|
|
3
|
+
<div class="ck-stat-card__body">
|
|
4
|
+
<% if worst_metric %>
|
|
5
|
+
<span class="ck-stat-card__metric"><%= worst_metric[:name] %></span>
|
|
6
|
+
<span class="<%= ck_badge_classes(ck_score_kind(worst_metric[:avg])) %> ck-stat-card__score"><%= worst_metric[:avg] %></span>
|
|
7
|
+
<% else %>
|
|
8
|
+
<span class="ck-stat-card__metric ck-stat-card__metric--empty">No scored reviews</span>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="ck-stat-card__foot ck-stat-card__foot--split">
|
|
13
|
+
<span>
|
|
14
|
+
<% if worst_metric && worst_metric[:response] %>
|
|
15
|
+
<%= link_to "Worst response →",
|
|
16
|
+
completion_kit.run_response_path(worst_metric[:response].run, worst_metric[:response]),
|
|
17
|
+
class: "ck-link" %>
|
|
18
|
+
<% elsif worst_metric %>
|
|
19
|
+
Lowest score this week.
|
|
20
|
+
<% else %>
|
|
21
|
+
Run a judge to populate this.
|
|
22
|
+
<% end %>
|
|
23
|
+
</span>
|
|
24
|
+
|
|
25
|
+
<% if ignored_metrics.any? %>
|
|
26
|
+
<details class="ck-flyout">
|
|
27
|
+
<summary class="ck-flyout__toggle"><%= ignored_metrics.size %> ignored</summary>
|
|
28
|
+
<ul class="ck-flyout__panel">
|
|
29
|
+
<% ignored_metrics.each do |dismissal| %>
|
|
30
|
+
<li class="ck-flyout__item">
|
|
31
|
+
<span class="ck-flyout__label">
|
|
32
|
+
<%= dismissal.dismissable.name %>
|
|
33
|
+
<% if dismissal.baseline_score %>
|
|
34
|
+
<span class="ck-flyout__meta">baseline <%= dismissal.baseline_score %></span>
|
|
35
|
+
<% end %>
|
|
36
|
+
</span>
|
|
37
|
+
<%= button_to completion_kit.dashboard_dismissal_path(dismissal),
|
|
38
|
+
method: :delete, class: "ck-icon-btn", title: "Un-ignore",
|
|
39
|
+
"aria-label": "Un-ignore #{dismissal.dismissable.name}" do %><%= render "completion_kit/dashboard/eye_icon" %><% end %>
|
|
40
|
+
</li>
|
|
41
|
+
<% end %>
|
|
42
|
+
</ul>
|
|
43
|
+
</details>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
46
|
+
<% if worst_metric %>
|
|
47
|
+
<%= button_to completion_kit.dashboard_dismissals_path,
|
|
48
|
+
params: { dashboard_dismissal: { dismissable_type: "CompletionKit::Metric",
|
|
49
|
+
dismissable_id: worst_metric[:metric].id } },
|
|
50
|
+
class: "ck-icon-btn", title: "Ignore this metric",
|
|
51
|
+
"aria-label": "Ignore #{worst_metric[:name]}" do %><%= render "completion_kit/dashboard/eye_off_icon" %><% end %>
|
|
52
|
+
<% end %>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%= turbo_stream.replace "ck-worst-metric-card" do %>
|
|
2
|
+
<%= render "completion_kit/dashboard/worst_metric_card",
|
|
3
|
+
worst_metric: @worst_metric, ignored_metrics: @ignored_metrics %>
|
|
4
|
+
<% end %>
|
|
5
|
+
<%= turbo_stream.replace "ck-failures-card" do %>
|
|
6
|
+
<%= render "completion_kit/dashboard/failures_card",
|
|
7
|
+
failures: @failures, ignored_failures: @ignored_failures %>
|
|
8
|
+
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateCompletionKitDashboardDismissals < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :completion_kit_dashboard_dismissals do |t|
|
|
4
|
+
t.string :dismissable_type, null: false
|
|
5
|
+
t.bigint :dismissable_id, null: false
|
|
6
|
+
t.decimal :baseline_score, precision: 4, scale: 1
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :completion_kit_dashboard_dismissals,
|
|
11
|
+
[:dismissable_type, :dismissable_id],
|
|
12
|
+
unique: true,
|
|
13
|
+
name: "index_ck_dashboard_dismissals_on_dismissable"
|
|
14
|
+
end
|
|
15
|
+
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.
|
|
4
|
+
version: 0.5.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -242,6 +242,7 @@ files:
|
|
|
242
242
|
- app/controllers/completion_kit/api/v1/tags_controller.rb
|
|
243
243
|
- app/controllers/completion_kit/api_reference_controller.rb
|
|
244
244
|
- app/controllers/completion_kit/application_controller.rb
|
|
245
|
+
- app/controllers/completion_kit/dashboard_dismissals_controller.rb
|
|
245
246
|
- app/controllers/completion_kit/datasets_controller.rb
|
|
246
247
|
- app/controllers/completion_kit/mcp_controller.rb
|
|
247
248
|
- app/controllers/completion_kit/metric_groups_controller.rb
|
|
@@ -262,6 +263,7 @@ files:
|
|
|
262
263
|
- app/jobs/completion_kit/run_completion_check_job.rb
|
|
263
264
|
- app/mailers/completion_kit/application_mailer.rb
|
|
264
265
|
- app/models/completion_kit/application_record.rb
|
|
266
|
+
- app/models/completion_kit/dashboard_dismissal.rb
|
|
265
267
|
- app/models/completion_kit/dataset.rb
|
|
266
268
|
- app/models/completion_kit/mcp_session.rb
|
|
267
269
|
- app/models/completion_kit/metric.rb
|
|
@@ -309,6 +311,11 @@ files:
|
|
|
309
311
|
- app/views/completion_kit/api_reference/_resource_card.html.erb
|
|
310
312
|
- app/views/completion_kit/api_reference/_resource_list.html.erb
|
|
311
313
|
- app/views/completion_kit/api_reference/index.html.erb
|
|
314
|
+
- app/views/completion_kit/dashboard/_eye_icon.html.erb
|
|
315
|
+
- app/views/completion_kit/dashboard/_eye_off_icon.html.erb
|
|
316
|
+
- app/views/completion_kit/dashboard/_failures_card.html.erb
|
|
317
|
+
- app/views/completion_kit/dashboard/_worst_metric_card.html.erb
|
|
318
|
+
- app/views/completion_kit/dashboard_dismissals/refresh.turbo_stream.erb
|
|
312
319
|
- app/views/completion_kit/datasets/_form.html.erb
|
|
313
320
|
- app/views/completion_kit/datasets/edit.html.erb
|
|
314
321
|
- app/views/completion_kit/datasets/index.html.erb
|
|
@@ -383,6 +390,7 @@ files:
|
|
|
383
390
|
- db/migrate/20260509000002_create_completion_kit_taggings.rb
|
|
384
391
|
- db/migrate/20260513000001_create_completion_kit_mcp_sessions.rb
|
|
385
392
|
- db/migrate/20260514000001_allow_judge_only_runs.rb
|
|
393
|
+
- db/migrate/20260516000001_create_completion_kit_dashboard_dismissals.rb
|
|
386
394
|
- lib/completion-kit.rb
|
|
387
395
|
- lib/completion_kit.rb
|
|
388
396
|
- lib/completion_kit/concurrency_check.rb
|