prompt_engine 1.0.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +67 -0
- data/Rakefile +22 -0
- data/app/assets/stylesheets/prompt_engine/application.css +22 -0
- data/app/assets/stylesheets/prompt_engine/buttons.css +124 -0
- data/app/assets/stylesheets/prompt_engine/cards.css +63 -0
- data/app/assets/stylesheets/prompt_engine/comparison.css +244 -0
- data/app/assets/stylesheets/prompt_engine/components/_test_runs.css +144 -0
- data/app/assets/stylesheets/prompt_engine/dashboard.css +343 -0
- data/app/assets/stylesheets/prompt_engine/evaluations.css +124 -0
- data/app/assets/stylesheets/prompt_engine/forms.css +198 -0
- data/app/assets/stylesheets/prompt_engine/foundation.css +182 -0
- data/app/assets/stylesheets/prompt_engine/layout.css +75 -0
- data/app/assets/stylesheets/prompt_engine/loading.css +229 -0
- data/app/assets/stylesheets/prompt_engine/notifications.css +78 -0
- data/app/assets/stylesheets/prompt_engine/overrides.css +42 -0
- data/app/assets/stylesheets/prompt_engine/prompts.css +237 -0
- data/app/assets/stylesheets/prompt_engine/sidebar.css +90 -0
- data/app/assets/stylesheets/prompt_engine/tables.css +250 -0
- data/app/assets/stylesheets/prompt_engine/utilities.css +52 -0
- data/app/assets/stylesheets/prompt_engine/versions.css +370 -0
- data/app/clients/prompt_engine/open_ai_evals_client.rb +135 -0
- data/app/controllers/prompt_engine/admin/base_controller.rb +7 -0
- data/app/controllers/prompt_engine/application_controller.rb +4 -0
- data/app/controllers/prompt_engine/dashboard_controller.rb +24 -0
- data/app/controllers/prompt_engine/eval_runs_controller.rb +23 -0
- data/app/controllers/prompt_engine/eval_sets_controller.rb +200 -0
- data/app/controllers/prompt_engine/evaluations_controller.rb +32 -0
- data/app/controllers/prompt_engine/playground_controller.rb +57 -0
- data/app/controllers/prompt_engine/playground_run_results_controller.rb +41 -0
- data/app/controllers/prompt_engine/prompts_controller.rb +70 -0
- data/app/controllers/prompt_engine/settings_controller.rb +28 -0
- data/app/controllers/prompt_engine/test_cases_controller.rb +231 -0
- data/app/controllers/prompt_engine/versions_controller.rb +90 -0
- data/app/helpers/prompt_engine/application_helper.rb +4 -0
- data/app/jobs/prompt_engine/application_job.rb +4 -0
- data/app/mailers/prompt_engine/application_mailer.rb +6 -0
- data/app/models/prompt_engine/application_record.rb +5 -0
- data/app/models/prompt_engine/eval_result.rb +19 -0
- data/app/models/prompt_engine/eval_run.rb +40 -0
- data/app/models/prompt_engine/eval_set.rb +97 -0
- data/app/models/prompt_engine/parameter.rb +126 -0
- data/app/models/prompt_engine/parameter_parser.rb +39 -0
- data/app/models/prompt_engine/playground_run_result.rb +20 -0
- data/app/models/prompt_engine/prompt.rb +192 -0
- data/app/models/prompt_engine/prompt_version.rb +72 -0
- data/app/models/prompt_engine/setting.rb +45 -0
- data/app/models/prompt_engine/test_case.rb +29 -0
- data/app/services/prompt_engine/evaluation_runner.rb +258 -0
- data/app/services/prompt_engine/playground_executor.rb +124 -0
- data/app/services/prompt_engine/variable_detector.rb +97 -0
- data/app/views/layouts/prompt_engine/admin.html.erb +65 -0
- data/app/views/layouts/prompt_engine/application.html.erb +17 -0
- data/app/views/prompt_engine/dashboard/index.html.erb +230 -0
- data/app/views/prompt_engine/eval_runs/show.html.erb +204 -0
- data/app/views/prompt_engine/eval_sets/compare.html.erb +229 -0
- data/app/views/prompt_engine/eval_sets/edit.html.erb +111 -0
- data/app/views/prompt_engine/eval_sets/index.html.erb +63 -0
- data/app/views/prompt_engine/eval_sets/metrics.html.erb +371 -0
- data/app/views/prompt_engine/eval_sets/new.html.erb +113 -0
- data/app/views/prompt_engine/eval_sets/show.html.erb +235 -0
- data/app/views/prompt_engine/evaluations/index.html.erb +194 -0
- data/app/views/prompt_engine/playground/result.html.erb +58 -0
- data/app/views/prompt_engine/playground/show.html.erb +129 -0
- data/app/views/prompt_engine/playground_run_results/index.html.erb +99 -0
- data/app/views/prompt_engine/playground_run_results/show.html.erb +123 -0
- data/app/views/prompt_engine/prompts/_form.html.erb +224 -0
- data/app/views/prompt_engine/prompts/edit.html.erb +9 -0
- data/app/views/prompt_engine/prompts/index.html.erb +80 -0
- data/app/views/prompt_engine/prompts/new.html.erb +9 -0
- data/app/views/prompt_engine/prompts/show.html.erb +297 -0
- data/app/views/prompt_engine/settings/edit.html.erb +93 -0
- data/app/views/prompt_engine/shared/_form_errors.html.erb +16 -0
- data/app/views/prompt_engine/test_cases/edit.html.erb +72 -0
- data/app/views/prompt_engine/test_cases/import.html.erb +92 -0
- data/app/views/prompt_engine/test_cases/import_preview.html.erb +103 -0
- data/app/views/prompt_engine/test_cases/new.html.erb +79 -0
- data/app/views/prompt_engine/versions/_version_card.html.erb +56 -0
- data/app/views/prompt_engine/versions/compare.html.erb +82 -0
- data/app/views/prompt_engine/versions/index.html.erb +96 -0
- data/app/views/prompt_engine/versions/show.html.erb +98 -0
- data/config/routes.rb +61 -0
- data/db/migrate/20250124000001_create_eval_tables.rb +43 -0
- data/db/migrate/20250124000002_add_open_ai_fields_to_evals.rb +11 -0
- data/db/migrate/20250125000001_add_grader_fields_to_eval_sets.rb +8 -0
- data/db/migrate/20250723161909_create_prompts.rb +17 -0
- data/db/migrate/20250723184757_create_prompt_engine_versions.rb +24 -0
- data/db/migrate/20250723203838_create_prompt_engine_parameters.rb +20 -0
- data/db/migrate/20250724160623_create_prompt_engine_playground_run_results.rb +30 -0
- data/db/migrate/20250724165118_create_prompt_engine_settings.rb +14 -0
- data/lib/prompt_engine/engine.rb +25 -0
- data/lib/prompt_engine/version.rb +3 -0
- data/lib/prompt_engine.rb +33 -0
- data/lib/tasks/active_prompt_tasks.rake +32 -0
- data/lib/tasks/eval_demo.rake +149 -0
- metadata +293 -0
@@ -0,0 +1,371 @@
|
|
1
|
+
<% content_for :head do %>
|
2
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<div class="admin-header">
|
6
|
+
<div>
|
7
|
+
<h1>Evaluation Metrics</h1>
|
8
|
+
<p class="text-muted">Performance insights for <%= @eval_set.name %></p>
|
9
|
+
</div>
|
10
|
+
<div class="btn-group">
|
11
|
+
<%= link_to "Back to Eval Set", prompt_eval_set_path(@prompt, @eval_set),
|
12
|
+
class: "btn btn--secondary btn--medium" %>
|
13
|
+
<% if @eval_runs.any? %>
|
14
|
+
<%= link_to "Compare Runs", prompt_eval_set_path(@prompt, @eval_set),
|
15
|
+
class: "btn btn--secondary btn--medium" %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<!-- Summary Cards -->
|
21
|
+
<div class="metrics-summary">
|
22
|
+
<div class="metrics-card">
|
23
|
+
<div class="metrics-card__icon">
|
24
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
25
|
+
<path d="M9 11l3 3L22 4"></path>
|
26
|
+
<path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"></path>
|
27
|
+
</svg>
|
28
|
+
</div>
|
29
|
+
<div class="metrics-card__content">
|
30
|
+
<h3 class="metrics-card__label">Total Test Cases</h3>
|
31
|
+
<p class="metrics-card__value"><%= @total_test_cases %></p>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div class="metrics-card">
|
36
|
+
<div class="metrics-card__icon">
|
37
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
38
|
+
<path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
39
|
+
</svg>
|
40
|
+
</div>
|
41
|
+
<div class="metrics-card__content">
|
42
|
+
<h3 class="metrics-card__label">Total Runs</h3>
|
43
|
+
<p class="metrics-card__value"><%= @total_runs %></p>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="metrics-card">
|
48
|
+
<div class="metrics-card__icon">
|
49
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
50
|
+
<path d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
|
51
|
+
</svg>
|
52
|
+
</div>
|
53
|
+
<div class="metrics-card__content">
|
54
|
+
<h3 class="metrics-card__label">Overall Pass Rate</h3>
|
55
|
+
<p class="metrics-card__value"><%= number_to_percentage(@overall_pass_rate, precision: 1) %></p>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<% if @eval_runs.any? %>
|
61
|
+
<!-- Charts Row 1 -->
|
62
|
+
<div class="chart-grid">
|
63
|
+
<div class="card">
|
64
|
+
<div class="card__header">
|
65
|
+
<h3 class="card__title">Success Rate Trend</h3>
|
66
|
+
</div>
|
67
|
+
<div class="card__body">
|
68
|
+
<div class="chart-container">
|
69
|
+
<canvas id="successRateTrendChart"></canvas>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
|
74
|
+
<div class="card">
|
75
|
+
<div class="card__header">
|
76
|
+
<h3 class="card__title">Success Rate by Version</h3>
|
77
|
+
</div>
|
78
|
+
<div class="card__body">
|
79
|
+
<div class="chart-container">
|
80
|
+
<canvas id="successRateByVersionChart"></canvas>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
|
86
|
+
<!-- Charts Row 2 -->
|
87
|
+
<div class="chart-grid">
|
88
|
+
<div class="card">
|
89
|
+
<div class="card__header">
|
90
|
+
<h3 class="card__title">Test Results Distribution</h3>
|
91
|
+
</div>
|
92
|
+
<div class="card__body">
|
93
|
+
<div class="chart-container chart-container--small">
|
94
|
+
<canvas id="testDistributionChart"></canvas>
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
|
99
|
+
<div class="card">
|
100
|
+
<div class="card__header">
|
101
|
+
<h3 class="card__title">Average Evaluation Duration</h3>
|
102
|
+
</div>
|
103
|
+
<div class="card__body">
|
104
|
+
<div class="chart-container">
|
105
|
+
<canvas id="durationTrendChart"></canvas>
|
106
|
+
</div>
|
107
|
+
</div>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
|
111
|
+
<!-- Recent Activity -->
|
112
|
+
<div class="card">
|
113
|
+
<div class="card__header">
|
114
|
+
<h3 class="card__title">Recent Evaluation Activity</h3>
|
115
|
+
</div>
|
116
|
+
<div class="card__body">
|
117
|
+
<div class="table-container">
|
118
|
+
<table class="table table--simple">
|
119
|
+
<thead>
|
120
|
+
<tr>
|
121
|
+
<th>Date</th>
|
122
|
+
<th>Version</th>
|
123
|
+
<th>Success Rate</th>
|
124
|
+
<th>Duration</th>
|
125
|
+
<th class="table__actions">Actions</th>
|
126
|
+
</tr>
|
127
|
+
</thead>
|
128
|
+
<tbody>
|
129
|
+
<% @recent_activity.each do |run| %>
|
130
|
+
<tr>
|
131
|
+
<td>
|
132
|
+
<div class="table__primary">
|
133
|
+
<%= run.created_at.strftime("%b %d, %Y %I:%M %p") %>
|
134
|
+
</div>
|
135
|
+
</td>
|
136
|
+
<td>
|
137
|
+
<span class="table__badge table__badge--info">v<%= run.prompt_version.version_number %></span>
|
138
|
+
</td>
|
139
|
+
<td>
|
140
|
+
<% success_rate = run.total_count > 0 ? (run.passed_count.to_f / run.total_count * 100) : 0 %>
|
141
|
+
<div class="table__metric">
|
142
|
+
<span class="table__metric-value
|
143
|
+
<%= success_rate >= 80 ? 'text-success' : success_rate >= 60 ? 'text-warning' : 'text-danger' %>">
|
144
|
+
<%= number_to_percentage(success_rate, precision: 1) %>
|
145
|
+
</span>
|
146
|
+
</div>
|
147
|
+
</td>
|
148
|
+
<td>
|
149
|
+
<% if run.completed_at && run.started_at %>
|
150
|
+
<span class="table__secondary">
|
151
|
+
<%= distance_of_time_in_words(run.started_at, run.completed_at) %>
|
152
|
+
</span>
|
153
|
+
<% else %>
|
154
|
+
<span class="table__secondary text-muted">—</span>
|
155
|
+
<% end %>
|
156
|
+
</td>
|
157
|
+
<td class="table__actions">
|
158
|
+
<%= link_to "View", prompt_eval_run_path(@prompt, run),
|
159
|
+
class: "table__action" %>
|
160
|
+
</td>
|
161
|
+
</tr>
|
162
|
+
<% end %>
|
163
|
+
</tbody>
|
164
|
+
</table>
|
165
|
+
</div>
|
166
|
+
</div>
|
167
|
+
</div>
|
168
|
+
|
169
|
+
<script>
|
170
|
+
// Chart.js configuration
|
171
|
+
Chart.defaults.font.family = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
172
|
+
Chart.defaults.color = '#374151';
|
173
|
+
|
174
|
+
// Success Rate Trend Chart
|
175
|
+
const successRateTrendCtx = document.getElementById('successRateTrendChart').getContext('2d');
|
176
|
+
new Chart(successRateTrendCtx, {
|
177
|
+
type: 'line',
|
178
|
+
data: {
|
179
|
+
labels: <%= @success_rate_trend.map { |d| d[:date] }.to_json.html_safe %>,
|
180
|
+
datasets: [{
|
181
|
+
label: 'Success Rate (%)',
|
182
|
+
data: <%= @success_rate_trend.map { |d| d[:rate] }.to_json.html_safe %>,
|
183
|
+
borderColor: '#3b82f6',
|
184
|
+
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
185
|
+
borderWidth: 2,
|
186
|
+
pointRadius: 4,
|
187
|
+
pointHoverRadius: 6,
|
188
|
+
tension: 0.1
|
189
|
+
}]
|
190
|
+
},
|
191
|
+
options: {
|
192
|
+
responsive: true,
|
193
|
+
maintainAspectRatio: false,
|
194
|
+
plugins: {
|
195
|
+
legend: {
|
196
|
+
display: false
|
197
|
+
},
|
198
|
+
tooltip: {
|
199
|
+
callbacks: {
|
200
|
+
afterLabel: function(context) {
|
201
|
+
const index = context.dataIndex;
|
202
|
+
const version = <%= @success_rate_trend.map { |d| d[:version] }.to_json.html_safe %>[index];
|
203
|
+
return 'Version: ' + version;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
},
|
208
|
+
scales: {
|
209
|
+
y: {
|
210
|
+
beginAtZero: true,
|
211
|
+
max: 100,
|
212
|
+
ticks: {
|
213
|
+
callback: function(value) {
|
214
|
+
return value + '%';
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
});
|
221
|
+
|
222
|
+
// Success Rate by Version Chart
|
223
|
+
const successRateByVersionCtx = document.getElementById('successRateByVersionChart').getContext('2d');
|
224
|
+
new Chart(successRateByVersionCtx, {
|
225
|
+
type: 'bar',
|
226
|
+
data: {
|
227
|
+
labels: <%= @success_rate_by_version.map { |d| d[:version] }.to_json.html_safe %>,
|
228
|
+
datasets: [{
|
229
|
+
label: 'Success Rate (%)',
|
230
|
+
data: <%= @success_rate_by_version.map { |d| d[:rate] }.to_json.html_safe %>,
|
231
|
+
backgroundColor: '#10b981',
|
232
|
+
borderColor: '#059669',
|
233
|
+
borderWidth: 1
|
234
|
+
}]
|
235
|
+
},
|
236
|
+
options: {
|
237
|
+
responsive: true,
|
238
|
+
maintainAspectRatio: false,
|
239
|
+
plugins: {
|
240
|
+
legend: {
|
241
|
+
display: false
|
242
|
+
},
|
243
|
+
tooltip: {
|
244
|
+
callbacks: {
|
245
|
+
afterLabel: function(context) {
|
246
|
+
const index = context.dataIndex;
|
247
|
+
const runs = <%= @success_rate_by_version.map { |d| d[:runs] }.to_json.html_safe %>[index];
|
248
|
+
return 'Runs: ' + runs;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
},
|
253
|
+
scales: {
|
254
|
+
y: {
|
255
|
+
beginAtZero: true,
|
256
|
+
max: 100,
|
257
|
+
ticks: {
|
258
|
+
callback: function(value) {
|
259
|
+
return value + '%';
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}
|
265
|
+
});
|
266
|
+
|
267
|
+
// Test Distribution Chart
|
268
|
+
const testDistributionCtx = document.getElementById('testDistributionChart').getContext('2d');
|
269
|
+
const totalPassed = <%= @eval_runs.sum(&:passed_count) %>;
|
270
|
+
const totalFailed = <%= @eval_runs.sum(&:failed_count) %>;
|
271
|
+
|
272
|
+
new Chart(testDistributionCtx, {
|
273
|
+
type: 'doughnut',
|
274
|
+
data: {
|
275
|
+
labels: ['Passed', 'Failed'],
|
276
|
+
datasets: [{
|
277
|
+
data: [totalPassed, totalFailed],
|
278
|
+
backgroundColor: ['#10b981', '#ef4444'],
|
279
|
+
borderWidth: 0
|
280
|
+
}]
|
281
|
+
},
|
282
|
+
options: {
|
283
|
+
responsive: true,
|
284
|
+
maintainAspectRatio: false,
|
285
|
+
plugins: {
|
286
|
+
legend: {
|
287
|
+
position: 'bottom'
|
288
|
+
},
|
289
|
+
tooltip: {
|
290
|
+
callbacks: {
|
291
|
+
label: function(context) {
|
292
|
+
const total = totalPassed + totalFailed;
|
293
|
+
const percentage = ((context.parsed / total) * 100).toFixed(1);
|
294
|
+
return context.label + ': ' + context.parsed + ' (' + percentage + '%)';
|
295
|
+
}
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
});
|
301
|
+
|
302
|
+
// Duration Trend Chart
|
303
|
+
const durationTrendCtx = document.getElementById('durationTrendChart').getContext('2d');
|
304
|
+
const durationData = <%= @duration_trend.to_json.html_safe %>;
|
305
|
+
|
306
|
+
new Chart(durationTrendCtx, {
|
307
|
+
type: 'line',
|
308
|
+
data: {
|
309
|
+
labels: durationData.map(d => d.date),
|
310
|
+
datasets: [{
|
311
|
+
label: 'Duration (seconds)',
|
312
|
+
data: durationData.map(d => d.duration),
|
313
|
+
borderColor: '#8b5cf6',
|
314
|
+
backgroundColor: 'rgba(139, 92, 246, 0.1)',
|
315
|
+
borderWidth: 2,
|
316
|
+
pointRadius: 4,
|
317
|
+
pointHoverRadius: 6,
|
318
|
+
tension: 0.1
|
319
|
+
}]
|
320
|
+
},
|
321
|
+
options: {
|
322
|
+
responsive: true,
|
323
|
+
maintainAspectRatio: false,
|
324
|
+
plugins: {
|
325
|
+
legend: {
|
326
|
+
display: false
|
327
|
+
},
|
328
|
+
tooltip: {
|
329
|
+
callbacks: {
|
330
|
+
afterLabel: function(context) {
|
331
|
+
const index = context.dataIndex;
|
332
|
+
const version = durationData[index].version;
|
333
|
+
return 'Version: ' + version;
|
334
|
+
},
|
335
|
+
label: function(context) {
|
336
|
+
const value = context.parsed.y;
|
337
|
+
const minutes = Math.floor(value / 60);
|
338
|
+
const seconds = value % 60;
|
339
|
+
if (minutes > 0) {
|
340
|
+
return minutes + 'm ' + seconds + 's';
|
341
|
+
}
|
342
|
+
return seconds + ' seconds';
|
343
|
+
}
|
344
|
+
}
|
345
|
+
}
|
346
|
+
},
|
347
|
+
scales: {
|
348
|
+
y: {
|
349
|
+
beginAtZero: true,
|
350
|
+
ticks: {
|
351
|
+
callback: function(value) {
|
352
|
+
return value + 's';
|
353
|
+
}
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
}
|
358
|
+
});
|
359
|
+
</script>
|
360
|
+
<% else %>
|
361
|
+
<div class="card">
|
362
|
+
<div class="card__body">
|
363
|
+
<div class="table-empty">
|
364
|
+
<p class="text-muted">No evaluation runs completed yet.</p>
|
365
|
+
<p class="text-muted">Run some evaluations to see metrics and insights.</p>
|
366
|
+
<%= link_to "Back to Eval Set", prompt_eval_set_path(@prompt, @eval_set),
|
367
|
+
class: "btn btn--primary btn--medium mt-md" %>
|
368
|
+
</div>
|
369
|
+
</div>
|
370
|
+
</div>
|
371
|
+
<% end %>
|
@@ -0,0 +1,113 @@
|
|
1
|
+
<div class="admin-header">
|
2
|
+
<div>
|
3
|
+
<h1>New Evaluation Set</h1>
|
4
|
+
<p class="text-muted">Create a new evaluation set for <%= @prompt.name %></p>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="card">
|
9
|
+
<div class="card__body">
|
10
|
+
<%= form_with model: [@prompt, @eval_set], url: prompt_eval_sets_path(@prompt), local: true do |form| %>
|
11
|
+
<%= render 'prompt_engine/shared/form_errors', object: @eval_set %>
|
12
|
+
|
13
|
+
<div class="form__group">
|
14
|
+
<%= form.label :name, class: "form__label" %>
|
15
|
+
<span class="form__required">*</span>
|
16
|
+
<%= form.text_field :name,
|
17
|
+
class: "form__input",
|
18
|
+
placeholder: "e.g., Customer Support Responses",
|
19
|
+
required: true,
|
20
|
+
minlength: 3,
|
21
|
+
maxlength: 255,
|
22
|
+
pattern: "[A-Za-z0-9 _-]+",
|
23
|
+
title: "Name can contain letters, numbers, spaces, hyphens, and underscores" %>
|
24
|
+
<p class="form__help">A descriptive name for this evaluation set (3-255 characters)</p>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="form__group">
|
28
|
+
<%= form.label :description, class: "form__label" %>
|
29
|
+
<%= form.text_area :description, class: "form__input", rows: 3,
|
30
|
+
placeholder: "e.g., Tests various customer support scenarios to ensure consistent and helpful responses" %>
|
31
|
+
<p class="form__help">Optional description of what this evaluation set tests</p>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="form__group">
|
35
|
+
<%= form.label :grader_type, "Grader Type", class: "form__label" %>
|
36
|
+
<span class="form__required">*</span>
|
37
|
+
<%= form.select :grader_type,
|
38
|
+
options_for_select(PromptEngine::EvalSet::GRADER_TYPES.map { |k, v| [v, k] }, @eval_set.grader_type || 'exact_match'),
|
39
|
+
{ include_blank: false },
|
40
|
+
{ class: "form__input", id: "grader-type-select",
|
41
|
+
data: { controller: "grader-config", action: "change->grader-config#toggleConfig" } } %>
|
42
|
+
<p class="form__help">Select how the expected output should be compared to the actual output</p>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div id="grader-config-fields" class="form__group" style="display: none;">
|
46
|
+
<label class="form__label">Grader Configuration</label>
|
47
|
+
|
48
|
+
<div id="regex-config" class="grader-config-section" style="display: none;">
|
49
|
+
<%= form.label :grader_config_pattern, "Regular Expression Pattern", class: "form__label" %>
|
50
|
+
<span class="form__required">*</span>
|
51
|
+
<%= text_field_tag "eval_set[grader_config][pattern]",
|
52
|
+
@eval_set.grader_config&.dig('pattern'),
|
53
|
+
class: "form__input",
|
54
|
+
placeholder: "e.g., ^Hello.*world$",
|
55
|
+
data: { grader_config_field: "regex" } %>
|
56
|
+
<p class="form__help">Enter a valid regular expression pattern to match against the output</p>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div id="json-schema-config" class="grader-config-section" style="display: none;">
|
60
|
+
<%= form.label :grader_config_schema, "JSON Schema", class: "form__label" %>
|
61
|
+
<span class="form__required">*</span>
|
62
|
+
<%= text_area_tag "eval_set[grader_config][schema]",
|
63
|
+
@eval_set.grader_config&.dig('schema')&.to_json,
|
64
|
+
rows: 8,
|
65
|
+
class: "form__input",
|
66
|
+
placeholder: '{ "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"] }',
|
67
|
+
data: { grader_config_field: "json_schema" } %>
|
68
|
+
<p class="form__help">Note: Currently validates exact JSON match. Full schema validation coming soon.</p>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
|
72
|
+
<div class="form__actions">
|
73
|
+
<%= form.submit "Create Evaluation Set", class: "btn btn--primary btn--medium",
|
74
|
+
data: { disable_with: "Creating..." } %>
|
75
|
+
<%= link_to "Cancel", prompt_eval_sets_path(@prompt), class: "btn btn--secondary btn--medium" %>
|
76
|
+
</div>
|
77
|
+
<% end %>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<script>
|
82
|
+
document.addEventListener('DOMContentLoaded', function() {
|
83
|
+
const graderTypeSelect = document.getElementById('grader-type-select');
|
84
|
+
const graderConfigFields = document.getElementById('grader-config-fields');
|
85
|
+
const regexConfig = document.getElementById('regex-config');
|
86
|
+
const jsonSchemaConfig = document.getElementById('json-schema-config');
|
87
|
+
|
88
|
+
function toggleGraderConfig() {
|
89
|
+
const selectedGrader = graderTypeSelect.value;
|
90
|
+
|
91
|
+
// Hide all config sections first
|
92
|
+
regexConfig.style.display = 'none';
|
93
|
+
jsonSchemaConfig.style.display = 'none';
|
94
|
+
|
95
|
+
// Show appropriate config section based on grader type
|
96
|
+
if (selectedGrader === 'regex') {
|
97
|
+
graderConfigFields.style.display = 'block';
|
98
|
+
regexConfig.style.display = 'block';
|
99
|
+
} else if (selectedGrader === 'json_schema') {
|
100
|
+
graderConfigFields.style.display = 'block';
|
101
|
+
jsonSchemaConfig.style.display = 'block';
|
102
|
+
} else {
|
103
|
+
graderConfigFields.style.display = 'none';
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
// Add event listener
|
108
|
+
graderTypeSelect.addEventListener('change', toggleGraderConfig);
|
109
|
+
|
110
|
+
// Initialize on page load
|
111
|
+
toggleGraderConfig();
|
112
|
+
});
|
113
|
+
</script>
|