leva 0.1.5 → 0.1.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 +4 -4
- data/README.md +55 -16
- data/app/controllers/leva/dataset_records_controller.rb +21 -0
- data/app/controllers/leva/datasets_controller.rb +9 -2
- data/app/controllers/leva/experiments_controller.rb +34 -9
- data/app/controllers/leva/runner_results_controller.rb +8 -0
- data/app/controllers/leva/workbench_controller.rb +85 -12
- data/app/helpers/leva/application_helper.rb +39 -0
- data/app/javascript/controllers/prompt_form_controller.js +45 -0
- data/app/javascript/controllers/prompt_selector_controller.js +31 -0
- data/app/jobs/leva/experiment_job.rb +9 -4
- data/app/jobs/leva/run_eval_job.rb +40 -0
- data/app/models/concerns/leva/recordable.rb +37 -0
- data/app/models/leva/dataset.rb +15 -6
- data/app/models/leva/dataset_record.rb +40 -1
- data/app/models/leva/evaluation_result.rb +15 -7
- data/app/models/leva/experiment.rb +24 -12
- data/app/models/leva/prompt.rb +14 -1
- data/app/models/leva/runner_result.rb +56 -0
- data/app/views/layouts/leva/application.html.erb +24 -13
- data/app/views/leva/dataset_records/index.html.erb +49 -0
- data/app/views/leva/dataset_records/show.html.erb +30 -0
- data/app/views/leva/datasets/_dataset.html.erb +18 -0
- data/app/views/leva/datasets/_form.html.erb +24 -0
- data/app/views/leva/datasets/edit.html.erb +5 -0
- data/app/views/leva/datasets/index.html.erb +51 -38
- data/app/views/leva/datasets/new.html.erb +5 -0
- data/app/views/leva/datasets/show.html.erb +160 -8
- data/app/views/leva/experiments/_experiment.html.erb +42 -0
- data/app/views/leva/experiments/_form.html.erb +49 -0
- data/app/views/leva/experiments/edit.html.erb +5 -0
- data/app/views/leva/experiments/index.html.erb +53 -37
- data/app/views/leva/experiments/new.html.erb +5 -0
- data/app/views/leva/experiments/show.html.erb +115 -19
- data/app/views/leva/runner_results/show.html.erb +64 -0
- data/app/views/leva/workbench/_evaluation_area.html.erb +5 -0
- data/app/views/leva/workbench/_prompt_content.html.erb +216 -0
- data/app/views/leva/workbench/_prompt_form.html.erb +89 -0
- data/app/views/leva/workbench/_prompt_sidebar.html.erb +21 -0
- data/app/views/leva/workbench/_results_section.html.erb +159 -0
- data/app/views/leva/workbench/_top_bar.html.erb +10 -0
- data/app/views/leva/workbench/edit.html.erb +20 -0
- data/app/views/leva/workbench/index.html.erb +5 -91
- data/app/views/leva/workbench/new.html.erb +79 -36
- data/config/routes.rb +15 -6
- data/db/migrate/20240813172916_create_leva_datasets.rb +1 -0
- data/db/migrate/20240813173035_create_leva_experiments.rb +1 -0
- data/db/migrate/20240816201419_create_leva_runner_results.rb +11 -0
- data/db/migrate/20240816201433_update_leva_evaluation_results.rb +8 -0
- data/db/migrate/20240821163608_make_experiment_optional_for_runner_results.rb +6 -0
- data/db/migrate/20240821181934_add_prompt_to_leva_runner_results.rb +5 -0
- data/db/migrate/20240821183153_add_runner_and_evaluator_to_leva_experiments.rb +6 -0
- data/db/migrate/20240821191713_add_actual_result_to_leva_dataset_records.rb +5 -0
- data/db/migrate/20240822143201_remove_actual_result_from_leva_runner_results.rb +5 -0
- data/db/migrate/20240912183556_add_runner_class_to_leva_runner_results.rb +5 -0
- data/lib/generators/leva/templates/eval.rb.erb +7 -8
- data/lib/generators/leva/templates/runner.rb.erb +25 -0
- data/lib/leva/version.rb +1 -1
- data/lib/leva.rb +84 -44
- metadata +49 -5
- data/app/evals/test_sentiment_accuracy_eval.rb +0 -6
- data/app/runners/test_sentiment_run.rb +0 -13
- data/lib/leva/base_eval.rb +0 -75
@@ -3,6 +3,7 @@
|
|
3
3
|
# Table name: leva_dataset_records
|
4
4
|
#
|
5
5
|
# id :integer not null, primary key
|
6
|
+
# actual_result :text
|
6
7
|
# recordable_type :string not null
|
7
8
|
# created_at :datetime not null
|
8
9
|
# updated_at :datetime not null
|
@@ -16,11 +17,49 @@
|
|
16
17
|
#
|
17
18
|
# Foreign Keys
|
18
19
|
#
|
19
|
-
# dataset_id (dataset_id =>
|
20
|
+
# dataset_id (dataset_id => leva_datasets.id)
|
20
21
|
#
|
21
22
|
module Leva
|
22
23
|
class DatasetRecord < ApplicationRecord
|
23
24
|
belongs_to :dataset
|
24
25
|
belongs_to :recordable, polymorphic: true
|
26
|
+
|
27
|
+
has_many :runner_results, dependent: :destroy
|
28
|
+
has_many :evaluation_results, dependent: :destroy, through: :runner_results
|
29
|
+
|
30
|
+
delegate :ground_truth, to: :recordable
|
31
|
+
|
32
|
+
# @return [Hash] A hash of attributes to be displayed in the dataset records index
|
33
|
+
def index_attributes
|
34
|
+
if recordable.respond_to?(:index_attributes)
|
35
|
+
recordable.index_attributes
|
36
|
+
elsif recordable.respond_to?(:name)
|
37
|
+
{ name: recordable.name }
|
38
|
+
else
|
39
|
+
{ to_s: recordable.to_s }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Hash] A hash of attributes to be displayed in the dataset record show view
|
44
|
+
def show_attributes
|
45
|
+
if recordable.respond_to?(:show_attributes)
|
46
|
+
recordable.show_attributes
|
47
|
+
elsif recordable.respond_to?(:dataset_attributes)
|
48
|
+
recordable.dataset_attributes
|
49
|
+
else
|
50
|
+
{ to_s: recordable.to_s }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String] A string representation of the record for display purposes
|
55
|
+
def display_name
|
56
|
+
if recordable.respond_to?(:name)
|
57
|
+
recordable.name
|
58
|
+
elsif recordable.respond_to?(:title)
|
59
|
+
recordable.title
|
60
|
+
else
|
61
|
+
"#{recordable_type} ##{recordable_id}"
|
62
|
+
end
|
63
|
+
end
|
25
64
|
end
|
26
65
|
end
|
@@ -3,29 +3,37 @@
|
|
3
3
|
# Table name: leva_evaluation_results
|
4
4
|
#
|
5
5
|
# id :integer not null, primary key
|
6
|
-
#
|
7
|
-
# prediction :string
|
6
|
+
# evaluator_class :string not null
|
8
7
|
# score :float
|
9
8
|
# created_at :datetime not null
|
10
9
|
# updated_at :datetime not null
|
11
10
|
# dataset_record_id :integer not null
|
12
|
-
# experiment_id :integer
|
11
|
+
# experiment_id :integer
|
12
|
+
# runner_result_id :integer not null
|
13
13
|
#
|
14
14
|
# Indexes
|
15
15
|
#
|
16
16
|
# index_leva_evaluation_results_on_dataset_record_id (dataset_record_id)
|
17
17
|
# index_leva_evaluation_results_on_experiment_id (experiment_id)
|
18
|
+
# index_leva_evaluation_results_on_runner_result_id (runner_result_id)
|
18
19
|
#
|
19
20
|
# Foreign Keys
|
20
21
|
#
|
21
|
-
# dataset_record_id (dataset_record_id =>
|
22
|
-
# experiment_id (experiment_id =>
|
22
|
+
# dataset_record_id (dataset_record_id => leva_dataset_records.id)
|
23
|
+
# experiment_id (experiment_id => leva_experiments.id)
|
24
|
+
# runner_result_id (runner_result_id => leva_runner_results.id)
|
23
25
|
#
|
24
26
|
module Leva
|
25
27
|
class EvaluationResult < ApplicationRecord
|
26
|
-
belongs_to :
|
28
|
+
belongs_to :runner_result
|
29
|
+
belongs_to :experiment, optional: true
|
27
30
|
belongs_to :dataset_record
|
28
31
|
|
32
|
+
validates :score, presence: true
|
33
|
+
validates :evaluator_class, presence: true
|
34
|
+
|
29
35
|
delegate :record, to: :dataset_record, allow_nil: true
|
36
|
+
|
37
|
+
scope :for_evaluator, ->(evaluator_class) { where(evaluator_class: evaluator_class.name) }
|
30
38
|
end
|
31
|
-
end
|
39
|
+
end
|
@@ -2,14 +2,17 @@
|
|
2
2
|
#
|
3
3
|
# Table name: leva_experiments
|
4
4
|
#
|
5
|
-
# id
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# description :text
|
7
|
+
# evaluator_classes :text
|
8
|
+
# metadata :text
|
9
|
+
# name :string
|
10
|
+
# runner_class :string
|
11
|
+
# status :integer
|
12
|
+
# created_at :datetime not null
|
13
|
+
# updated_at :datetime not null
|
14
|
+
# dataset_id :integer not null
|
15
|
+
# prompt_id :integer
|
13
16
|
#
|
14
17
|
# Indexes
|
15
18
|
#
|
@@ -18,14 +21,23 @@
|
|
18
21
|
#
|
19
22
|
# Foreign Keys
|
20
23
|
#
|
21
|
-
# dataset_id (dataset_id =>
|
22
|
-
# prompt_id (prompt_id =>
|
24
|
+
# dataset_id (dataset_id => leva_datasets.id)
|
25
|
+
# prompt_id (prompt_id => leva_prompts.id)
|
23
26
|
#
|
24
27
|
module Leva
|
25
28
|
class Experiment < ApplicationRecord
|
26
29
|
belongs_to :dataset
|
27
30
|
belongs_to :prompt, optional: true
|
31
|
+
has_many :runner_results, dependent: :destroy
|
32
|
+
has_many :evaluation_results, through: :runner_results
|
28
33
|
|
29
|
-
|
34
|
+
validates :name, presence: true
|
35
|
+
validates :dataset, presence: true
|
36
|
+
validates :runner_class, presence: true
|
37
|
+
validates :evaluator_classes, presence: true
|
38
|
+
|
39
|
+
enum :status, { pending: 0, running: 1, completed: 2, failed: 3 }, default: :pending
|
40
|
+
|
41
|
+
serialize :evaluator_classes, coder: JSON, type: Array
|
30
42
|
end
|
31
|
-
end
|
43
|
+
end
|
data/app/models/leva/prompt.rb
CHANGED
@@ -13,5 +13,18 @@
|
|
13
13
|
#
|
14
14
|
module Leva
|
15
15
|
class Prompt < ApplicationRecord
|
16
|
+
has_many :experiments
|
17
|
+
|
18
|
+
validates :name, presence: true
|
19
|
+
validates :system_prompt, presence: true
|
20
|
+
validates :user_prompt, presence: true
|
21
|
+
|
22
|
+
before_save :increment_version
|
23
|
+
|
24
|
+
# @return [void]
|
25
|
+
def increment_version
|
26
|
+
self.version ||= 0
|
27
|
+
self.version += 1
|
28
|
+
end
|
16
29
|
end
|
17
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: leva_runner_results
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# prediction :text
|
7
|
+
# prompt_version :integer
|
8
|
+
# runner_class :string
|
9
|
+
# created_at :datetime not null
|
10
|
+
# updated_at :datetime not null
|
11
|
+
# dataset_record_id :integer not null
|
12
|
+
# experiment_id :integer
|
13
|
+
# prompt_id :integer not null
|
14
|
+
#
|
15
|
+
# Indexes
|
16
|
+
#
|
17
|
+
# index_leva_runner_results_on_dataset_record_id (dataset_record_id)
|
18
|
+
# index_leva_runner_results_on_experiment_id (experiment_id)
|
19
|
+
# index_leva_runner_results_on_prompt_id (prompt_id)
|
20
|
+
#
|
21
|
+
# Foreign Keys
|
22
|
+
#
|
23
|
+
# dataset_record_id (dataset_record_id => leva_dataset_records.id)
|
24
|
+
# experiment_id (experiment_id => leva_experiments.id)
|
25
|
+
# prompt_id (prompt_id => leva_prompts.id)
|
26
|
+
#
|
27
|
+
module Leva
|
28
|
+
class RunnerResult < ApplicationRecord
|
29
|
+
belongs_to :experiment, optional: true
|
30
|
+
belongs_to :dataset_record
|
31
|
+
belongs_to :prompt
|
32
|
+
has_many :evaluation_results, dependent: :destroy
|
33
|
+
|
34
|
+
validates :prediction, presence: true
|
35
|
+
validates :prompt, presence: true
|
36
|
+
validates :runner_class, presence: true
|
37
|
+
|
38
|
+
delegate :ground_truth, to: :dataset_record
|
39
|
+
|
40
|
+
# @return [Array<String>] The parsed draft responses
|
41
|
+
def parsed_predictions
|
42
|
+
@parsed_predictions ||= runner&.parsed_predictions(self) || []
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String] The ground truth for this runner result
|
46
|
+
def ground_truth
|
47
|
+
@ground_truth ||= runner&.ground_truth(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def runner
|
53
|
+
@runner ||= runner_class&.constantize&.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html lang="en" class="bg-gray-
|
2
|
+
<html lang="en" class="bg-gray-950">
|
3
3
|
<head>
|
4
4
|
<meta charset="UTF-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
@@ -7,23 +7,34 @@
|
|
7
7
|
<%= csrf_meta_tags %>
|
8
8
|
<%= csp_meta_tag %>
|
9
9
|
<script src="https://cdn.tailwindcss.com"></script>
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/stimulus@3.2.2/dist/stimulus.umd.min.js"></script>
|
11
|
+
<%= yield(:head) %>
|
10
12
|
</head>
|
11
|
-
<body class="bg-gray-
|
12
|
-
<nav class="bg-gray-
|
13
|
-
<div class="
|
14
|
-
<div class="flex
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
<body class="bg-gray-950 text-white">
|
14
|
+
<nav class="bg-gray-900 border-b border-gray-800">
|
15
|
+
<div class="px-4">
|
16
|
+
<div class="flex h-16 items-center">
|
17
|
+
<%= link_to root_path, class: 'flex items-center text-2xl font-bold text-indigo-400' do %>
|
18
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 mr-2" viewBox="0 0 24 24" fill="none" stroke="url(#gradient)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
19
|
+
<defs>
|
20
|
+
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
21
|
+
<stop offset="0%" stop-color="#4F46E5" />
|
22
|
+
<stop offset="100%" stop-color="#9333EA" />
|
23
|
+
</linearGradient>
|
24
|
+
</defs>
|
25
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
26
|
+
</svg>
|
27
|
+
LLM Evals
|
28
|
+
<% end %>
|
29
|
+
<div class="ml-10 flex space-x-6">
|
30
|
+
<%= link_to 'Workbench', workbench_index_path, class: "px-3 py-2 rounded-md text-sm font-medium transition-colors duration-150 ease-in-out #{request.path.start_with?(workbench_index_path) ? 'bg-indigo-600 text-white shadow-lg' : 'text-gray-300 hover:bg-gray-800 hover:text-white'}" %>
|
31
|
+
<%= link_to 'Datasets', datasets_path, class: "px-3 py-2 rounded-md text-sm font-medium transition-colors duration-150 ease-in-out #{request.path.start_with?(datasets_path) ? 'bg-indigo-600 text-white shadow-lg' : 'text-gray-300 hover:bg-gray-800 hover:text-white'}" %>
|
32
|
+
<%= link_to 'Experiments', experiments_path, class: "px-3 py-2 rounded-md text-sm font-medium transition-colors duration-150 ease-in-out #{request.path.start_with?(experiments_path) ? 'bg-indigo-600 text-white shadow-lg' : 'text-gray-300 hover:bg-gray-800 hover:text-white'}" %>
|
22
33
|
</div>
|
23
34
|
</div>
|
24
35
|
</div>
|
25
36
|
</nav>
|
26
|
-
<main class="bg-gray-
|
37
|
+
<main class="bg-gray-950">
|
27
38
|
<%= yield %>
|
28
39
|
</main>
|
29
40
|
</body>
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<% content_for :title, "#{@dataset.name} - Records" %>
|
2
|
+
<div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
|
3
|
+
<div class="mb-8">
|
4
|
+
<div class="flex justify-between items-center">
|
5
|
+
<h1 class="text-3xl font-bold text-indigo-400 mb-2"><%= @dataset.name %> - Records</h1>
|
6
|
+
<%= link_to dataset_path(@dataset), class: 'btn btn-secondary flex items-center' do %>
|
7
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
8
|
+
<path fill-rule="evenodd" d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z" clip-rule="evenodd" />
|
9
|
+
</svg>
|
10
|
+
Back to Dataset
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="bg-gray-800 rounded-lg shadow-lg overflow-hidden">
|
16
|
+
<table class="min-w-full divide-y divide-gray-700">
|
17
|
+
<thead class="bg-gray-700">
|
18
|
+
<tr>
|
19
|
+
<% @records.first.index_attributes.keys.each do |key| %>
|
20
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">
|
21
|
+
<%= key.to_s.humanize %>
|
22
|
+
</th>
|
23
|
+
<% end %>
|
24
|
+
<th scope="col" class="relative px-6 py-3">
|
25
|
+
<span class="sr-only">Actions</span>
|
26
|
+
</th>
|
27
|
+
</tr>
|
28
|
+
</thead>
|
29
|
+
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
30
|
+
<% @records.each do |record| %>
|
31
|
+
<tr class="hover:bg-gray-700 transition-colors duration-200">
|
32
|
+
<% record.index_attributes.values.each do |value| %>
|
33
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
34
|
+
<%= value %>
|
35
|
+
</td>
|
36
|
+
<% end %>
|
37
|
+
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
38
|
+
<%= link_to 'View', dataset_dataset_record_path(@dataset, record), class: 'text-indigo-400 hover:text-indigo-300 transition-colors duration-200' %>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<% end %>
|
42
|
+
</tbody>
|
43
|
+
</table>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="mt-4 text-gray-400 text-sm">
|
47
|
+
Total records: <%= @records.count %>
|
48
|
+
</div>
|
49
|
+
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<% content_for :title, "#{@dataset.name} - Record Details" %>
|
2
|
+
<div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
|
3
|
+
<div class="mb-8">
|
4
|
+
<div class="flex justify-between items-center">
|
5
|
+
<h1 class="text-3xl font-bold text-indigo-400 mb-2"><%= @dataset.name %> - Record Details</h1>
|
6
|
+
<div class="flex space-x-4">
|
7
|
+
<%= link_to dataset_dataset_records_path(@dataset), class: 'btn btn-secondary flex items-center' do %>
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
9
|
+
<path fill-rule="evenodd" d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z" clip-rule="evenodd" />
|
10
|
+
</svg>
|
11
|
+
Back to Records
|
12
|
+
<% end %>
|
13
|
+
<%= link_to workbench_index_path(dataset_record_id: @record.id), class: 'btn btn-primary flex items-center' do %>
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
15
|
+
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
|
16
|
+
</svg>
|
17
|
+
Use in Workbench
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<div class="bg-gray-800 rounded-lg shadow-lg p-6">
|
23
|
+
<% @record.show_attributes.each do |key, value| %>
|
24
|
+
<div class="mb-4">
|
25
|
+
<h3 class="text-lg font-semibold text-indigo-300 mb-2"><%= key.to_s.humanize %></h3>
|
26
|
+
<p class="text-gray-300"><%= value %></p>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<tr class="hover:bg-gray-700 transition-colors duration-200">
|
2
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
3
|
+
<%= dataset.name %>
|
4
|
+
</td>
|
5
|
+
<td class="px-6 py-4 text-sm text-gray-300">
|
6
|
+
<%= truncate(dataset.description, length: 100) %>
|
7
|
+
</td>
|
8
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
9
|
+
<%= dataset.dataset_records.count %>
|
10
|
+
</td>
|
11
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
12
|
+
<%= dataset.experiments.count %>
|
13
|
+
</td>
|
14
|
+
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
15
|
+
<%= link_to 'View', dataset_path(dataset), class: 'text-indigo-400 hover:text-indigo-300 transition-colors duration-200 mr-3' %>
|
16
|
+
<%= link_to 'Edit', edit_dataset_path(dataset), class: 'text-indigo-400 hover:text-indigo-300 transition-colors duration-200' %>
|
17
|
+
</td>
|
18
|
+
</tr>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<%= form_with(model: dataset, local: true, class: "bg-gray-800 rounded-lg shadow-lg p-6") do |form| %>
|
2
|
+
<% if dataset.errors.any? %>
|
3
|
+
<div class="bg-red-900 border border-red-700 text-red-100 px-4 py-3 rounded-lg mb-4" role="alert">
|
4
|
+
<strong class="font-bold">Error:</strong>
|
5
|
+
<ul class="list-disc list-inside">
|
6
|
+
<% dataset.errors.full_messages.each do |message| %>
|
7
|
+
<li><%= message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
<div class="mb-4">
|
13
|
+
<%= form.label :name, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
|
14
|
+
<%= form.text_field :name, autofocus: true, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
|
15
|
+
</div>
|
16
|
+
<div class="mb-6">
|
17
|
+
<%= form.label :description, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
|
18
|
+
<%= form.text_area :description, rows: 4, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
|
19
|
+
</div>
|
20
|
+
<div class="flex items-center justify-end space-x-4">
|
21
|
+
<%= link_to "Cancel", dataset.persisted? ? dataset_path(dataset) : datasets_path, class: "px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:bg-gray-800 hover:text-white transition-colors duration-150 ease-in-out" %>
|
22
|
+
<%= form.submit dataset.persisted? ? "Update Dataset" : "Create Dataset", class: "px-3 py-2 rounded-md text-sm font-medium bg-indigo-600 text-white shadow-lg hover:bg-indigo-700 transition-colors duration-150 ease-in-out" %>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
@@ -1,43 +1,56 @@
|
|
1
1
|
<% content_for :title, 'Datasets' %>
|
2
|
-
<div class="px-4
|
3
|
-
<div class="
|
4
|
-
<
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
<div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
|
3
|
+
<div class="flex justify-between items-center mb-6">
|
4
|
+
<h1 class="text-3xl font-bold text-indigo-400">Datasets</h1>
|
5
|
+
<%= link_to new_dataset_path, class: "btn btn-primary flex items-center" do %>
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
7
|
+
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
|
8
|
+
</svg>
|
9
|
+
Create New Dataset
|
10
|
+
<% end %>
|
11
11
|
</div>
|
12
|
-
|
13
|
-
<div class="-
|
14
|
-
<
|
15
|
-
<
|
16
|
-
<
|
17
|
-
<
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
</
|
26
|
-
<
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
12
|
+
<% if @datasets.any? %>
|
13
|
+
<div class="bg-gray-800 rounded-lg shadow-lg overflow-hidden">
|
14
|
+
<table class="min-w-full divide-y divide-gray-700">
|
15
|
+
<thead class="bg-gray-700">
|
16
|
+
<tr>
|
17
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">
|
18
|
+
Name
|
19
|
+
</th>
|
20
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">
|
21
|
+
Description
|
22
|
+
</th>
|
23
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">
|
24
|
+
Records
|
25
|
+
</th>
|
26
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">
|
27
|
+
Experiments
|
28
|
+
</th>
|
29
|
+
<th scope="col" class="relative px-6 py-3">
|
30
|
+
<span class="sr-only">Actions</span>
|
31
|
+
</th>
|
32
|
+
</tr>
|
33
|
+
</thead>
|
34
|
+
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
35
|
+
<%= render @datasets %>
|
36
|
+
</tbody>
|
37
|
+
</table>
|
38
|
+
</div>
|
39
|
+
<% else %>
|
40
|
+
<div class="bg-gray-800 rounded-lg shadow-lg p-12 text-center">
|
41
|
+
<svg class="mx-auto h-12 w-12 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
42
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
43
|
+
</svg>
|
44
|
+
<h3 class="mt-2 text-xl font-medium text-indigo-300">No datasets yet</h3>
|
45
|
+
<p class="mt-1 text-gray-400">Get started by creating a new dataset.</p>
|
46
|
+
<div class="mt-6">
|
47
|
+
<%= link_to new_dataset_path, class: "btn btn-primary inline-flex items-center" do %>
|
48
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
49
|
+
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
|
50
|
+
</svg>
|
51
|
+
Create your first dataset
|
52
|
+
<% end %>
|
40
53
|
</div>
|
41
54
|
</div>
|
42
|
-
|
55
|
+
<% end %>
|
43
56
|
</div>
|