leva 0.1.9.1 → 0.1.11
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 +21 -5
- data/app/assets/stylesheets/leva/application.css +3083 -15
- data/app/controllers/leva/application_controller.rb +1 -1
- data/app/controllers/leva/dataset_records_controller.rb +1 -1
- data/app/controllers/leva/datasets_controller.rb +6 -6
- data/app/controllers/leva/design_system_controller.rb +9 -0
- data/app/controllers/leva/experiments_controller.rb +8 -8
- data/app/controllers/leva/runner_results_controller.rb +1 -1
- data/app/controllers/leva/workbench_controller.rb +26 -15
- data/app/helpers/leva/application_helper.rb +7 -7
- data/app/jobs/leva/experiment_job.rb +1 -1
- data/app/jobs/leva/run_eval_job.rb +1 -1
- data/app/models/concerns/leva/recordable.rb +5 -5
- data/app/models/leva/dataset.rb +1 -1
- data/app/models/leva/evaluation_result.rb +1 -1
- data/app/models/leva/experiment.rb +1 -1
- data/app/models/leva/prompt.rb +1 -1
- data/app/views/layouts/leva/application.html.erb +23 -24
- data/app/views/leva/dataset_records/index.html.erb +70 -43
- data/app/views/leva/dataset_records/show.html.erb +115 -25
- data/app/views/leva/datasets/_dataset.html.erb +11 -18
- data/app/views/leva/datasets/_form.html.erb +18 -14
- data/app/views/leva/datasets/edit.html.erb +16 -4
- data/app/views/leva/datasets/index.html.erb +33 -41
- data/app/views/leva/datasets/new.html.erb +15 -4
- data/app/views/leva/datasets/show.html.erb +120 -139
- data/app/views/leva/design_system/index.html.erb +1731 -0
- data/app/views/leva/experiments/_experiment.html.erb +46 -31
- data/app/views/leva/experiments/_form.html.erb +62 -35
- data/app/views/leva/experiments/edit.html.erb +17 -3
- data/app/views/leva/experiments/index.html.erb +41 -36
- data/app/views/leva/experiments/new.html.erb +52 -4
- data/app/views/leva/experiments/show.html.erb +155 -98
- data/app/views/leva/runner_results/show.html.erb +271 -54
- data/app/views/leva/workbench/_evaluation_area.html.erb +18 -4
- data/app/views/leva/workbench/_prompt_content.html.erb +124 -73
- data/app/views/leva/workbench/_prompt_form.html.erb +24 -23
- data/app/views/leva/workbench/_prompt_sidebar.html.erb +57 -12
- data/app/views/leva/workbench/_results_section.html.erb +274 -112
- data/app/views/leva/workbench/_top_bar.html.erb +16 -6
- data/app/views/leva/workbench/edit.html.erb +46 -15
- data/app/views/leva/workbench/index.html.erb +5 -8
- data/app/views/leva/workbench/new.html.erb +74 -42
- data/config/routes.rb +11 -9
- data/db/migrate/20240813173033_create_leva_dataset_records.rb +1 -0
- data/db/migrate/20240813173035_create_leva_experiments.rb +2 -0
- data/db/migrate/{20240816201419_create_leva_runner_results.rb → 20240813173040_create_leva_runner_results.rb} +4 -1
- data/db/migrate/20240813173050_create_leva_evaluation_results.rb +3 -3
- data/lib/generators/leva/eval_generator.rb +4 -4
- data/lib/generators/leva/runner_generator.rb +4 -4
- data/lib/generators/leva/templates/runner.rb.erb +20 -0
- data/lib/leva/version.rb +1 -1
- data/lib/leva.rb +24 -2
- metadata +5 -11
- data/db/migrate/20240816201433_update_leva_evaluation_results.rb +0 -8
- data/db/migrate/20240821163608_make_experiment_optional_for_runner_results.rb +0 -6
- data/db/migrate/20240821181934_add_prompt_to_leva_runner_results.rb +0 -5
- data/db/migrate/20240821183153_add_runner_and_evaluator_to_leva_experiments.rb +0 -6
- data/db/migrate/20240821191713_add_actual_result_to_leva_dataset_records.rb +0 -5
- data/db/migrate/20240822143201_remove_actual_result_from_leva_runner_results.rb +0 -5
- data/db/migrate/20240912183556_add_runner_class_to_leva_runner_results.rb +0 -5
- data/lib/tasks/auto_annotate_models.rake +0 -59
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Leva
|
|
4
4
|
class DatasetsController < ApplicationController
|
|
5
|
-
before_action :set_dataset, only: [:show, :edit, :update, :destroy]
|
|
5
|
+
before_action :set_dataset, only: [ :show, :edit, :update, :destroy ]
|
|
6
6
|
|
|
7
7
|
# GET /datasets
|
|
8
8
|
# @return [void]
|
|
@@ -35,7 +35,7 @@ module Leva
|
|
|
35
35
|
@dataset = Dataset.new(dataset_params)
|
|
36
36
|
|
|
37
37
|
if @dataset.save
|
|
38
|
-
redirect_to @dataset, notice:
|
|
38
|
+
redirect_to @dataset, notice: "Dataset was successfully created."
|
|
39
39
|
else
|
|
40
40
|
render :new
|
|
41
41
|
end
|
|
@@ -45,7 +45,7 @@ module Leva
|
|
|
45
45
|
# @return [void]
|
|
46
46
|
def update
|
|
47
47
|
if @dataset.update(dataset_params)
|
|
48
|
-
redirect_to @dataset, notice:
|
|
48
|
+
redirect_to @dataset, notice: "Dataset was successfully updated."
|
|
49
49
|
else
|
|
50
50
|
render :edit
|
|
51
51
|
end
|
|
@@ -55,10 +55,10 @@ module Leva
|
|
|
55
55
|
# @return [void]
|
|
56
56
|
def destroy
|
|
57
57
|
if @dataset.dataset_records.any?
|
|
58
|
-
redirect_to @dataset, alert:
|
|
58
|
+
redirect_to @dataset, alert: "Cannot delete dataset with existing records."
|
|
59
59
|
else
|
|
60
60
|
@dataset.destroy
|
|
61
|
-
redirect_to datasets_url, notice:
|
|
61
|
+
redirect_to datasets_url, notice: "Dataset was successfully destroyed."
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -76,4 +76,4 @@ module Leva
|
|
|
76
76
|
params.require(:dataset).permit(:name, :description)
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
|
-
end
|
|
79
|
+
end
|
|
@@ -4,9 +4,9 @@ module Leva
|
|
|
4
4
|
class ExperimentsController < ApplicationController
|
|
5
5
|
include ApplicationHelper
|
|
6
6
|
|
|
7
|
-
before_action :set_experiment, only: [:show, :edit, :update]
|
|
8
|
-
before_action :check_editable, only: [:edit, :update]
|
|
9
|
-
before_action :load_runners_and_evaluators, only: [:new, :edit, :create, :update]
|
|
7
|
+
before_action :set_experiment, only: [ :show, :edit, :update ]
|
|
8
|
+
before_action :check_editable, only: [ :edit, :update ]
|
|
9
|
+
before_action :load_runners_and_evaluators, only: [ :new, :edit, :create, :update ]
|
|
10
10
|
|
|
11
11
|
# GET /experiments
|
|
12
12
|
# @return [void]
|
|
@@ -39,7 +39,7 @@ module Leva
|
|
|
39
39
|
|
|
40
40
|
if @experiment.save
|
|
41
41
|
ExperimentJob.perform_later(@experiment) unless @experiment.completed?
|
|
42
|
-
redirect_to @experiment, notice:
|
|
42
|
+
redirect_to @experiment, notice: "Experiment was successfully created and is now running."
|
|
43
43
|
else
|
|
44
44
|
render :new
|
|
45
45
|
end
|
|
@@ -49,7 +49,7 @@ module Leva
|
|
|
49
49
|
# @return [void]
|
|
50
50
|
def update
|
|
51
51
|
if @experiment.update(experiment_params)
|
|
52
|
-
redirect_to @experiment, notice:
|
|
52
|
+
redirect_to @experiment, notice: "Experiment was successfully updated."
|
|
53
53
|
else
|
|
54
54
|
render :edit
|
|
55
55
|
end
|
|
@@ -69,7 +69,7 @@ module Leva
|
|
|
69
69
|
# Queue the job again
|
|
70
70
|
ExperimentJob.perform_later(@experiment)
|
|
71
71
|
|
|
72
|
-
redirect_to @experiment, notice:
|
|
72
|
+
redirect_to @experiment, notice: "Experiment has been reset and is now running again."
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
private
|
|
@@ -92,7 +92,7 @@ module Leva
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def check_editable
|
|
95
|
-
redirect_to @experiment, alert:
|
|
95
|
+
redirect_to @experiment, alert: "Completed experiments cannot be edited." if @experiment.completed?
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
|
-
end
|
|
98
|
+
end
|
|
@@ -4,9 +4,9 @@ module Leva
|
|
|
4
4
|
class WorkbenchController < ApplicationController
|
|
5
5
|
include ApplicationHelper
|
|
6
6
|
|
|
7
|
-
before_action :set_prompt, only: [:index, :edit, :update, :run, :run_all_evals, :run_evaluator]
|
|
8
|
-
before_action :set_dataset_record, only: [:index, :run, :run_all_evals, :run_evaluator]
|
|
9
|
-
before_action :set_runner_result, only: [:index, :run_all_evals, :run_evaluator]
|
|
7
|
+
before_action :set_prompt, only: [ :index, :edit, :update, :run, :run_all_evals, :run_evaluator ]
|
|
8
|
+
before_action :set_dataset_record, only: [ :index, :run, :run_all_evals, :run_evaluator ]
|
|
9
|
+
before_action :set_runner_result, only: [ :index, :run_all_evals, :run_evaluator ]
|
|
10
10
|
|
|
11
11
|
# GET /workbench
|
|
12
12
|
# @return [void]
|
|
@@ -17,6 +17,17 @@ module Leva
|
|
|
17
17
|
@runners = load_runners
|
|
18
18
|
@selected_runner = params[:runner] || @runners.first&.name
|
|
19
19
|
@selected_dataset_record = params[:dataset_record_id] || DatasetRecord.first&.id
|
|
20
|
+
|
|
21
|
+
# Get merged context if runner and dataset record are available
|
|
22
|
+
if @selected_runner && @dataset_record
|
|
23
|
+
runner_class = @selected_runner.constantize rescue nil
|
|
24
|
+
if runner_class && runner_class < Leva::BaseRun
|
|
25
|
+
runner = runner_class.new
|
|
26
|
+
@record_context = @dataset_record.recordable.to_llm_context
|
|
27
|
+
@runner_context = runner.to_llm_context(@dataset_record.recordable)
|
|
28
|
+
@merged_context = @record_context.merge(@runner_context)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
20
31
|
end
|
|
21
32
|
|
|
22
33
|
# GET /workbench/new
|
|
@@ -31,7 +42,7 @@ module Leva
|
|
|
31
42
|
def create
|
|
32
43
|
@prompt = Prompt.new(prompt_params)
|
|
33
44
|
if @prompt.save
|
|
34
|
-
redirect_to workbench_index_path(prompt_id: @prompt.id), notice:
|
|
45
|
+
redirect_to workbench_index_path(prompt_id: @prompt.id), notice: "Prompt was successfully created."
|
|
35
46
|
else
|
|
36
47
|
render :new
|
|
37
48
|
end
|
|
@@ -47,45 +58,45 @@ module Leva
|
|
|
47
58
|
def update
|
|
48
59
|
@prompt = Prompt.find(params[:id])
|
|
49
60
|
if @prompt.update(prompt_params)
|
|
50
|
-
render json: { status:
|
|
61
|
+
render json: { status: "success", message: "Prompt updated successfully" }
|
|
51
62
|
else
|
|
52
|
-
render json: { status:
|
|
63
|
+
render json: { status: "error", errors: @prompt.errors.full_messages }, status: :unprocessable_entity
|
|
53
64
|
end
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
def run
|
|
57
|
-
return redirect_to workbench_index_path, alert:
|
|
68
|
+
return redirect_to workbench_index_path, alert: "Please select a record and a runner" unless @dataset_record && run_params[:runner]
|
|
58
69
|
|
|
59
70
|
runner_class = run_params[:runner].constantize
|
|
60
|
-
return redirect_to workbench_index_path, alert:
|
|
71
|
+
return redirect_to workbench_index_path, alert: "Invalid runner selected" unless runner_class < Leva::BaseRun
|
|
61
72
|
|
|
62
73
|
runner = runner_class.new
|
|
63
74
|
runner_result = runner.execute_and_store(nil, @dataset_record, @prompt)
|
|
64
75
|
|
|
65
|
-
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: run_params[:runner]), notice:
|
|
76
|
+
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: run_params[:runner]), notice: "Run completed successfully"
|
|
66
77
|
end
|
|
67
78
|
|
|
68
79
|
def run_all_evals
|
|
69
|
-
return redirect_to workbench_index_path, alert:
|
|
80
|
+
return redirect_to workbench_index_path, alert: "No runner result available" unless @runner_result
|
|
70
81
|
|
|
71
82
|
load_evaluators.each do |evaluator_class|
|
|
72
83
|
evaluator = evaluator_class.new
|
|
73
84
|
evaluator.evaluate_and_store(nil, @runner_result)
|
|
74
85
|
end
|
|
75
86
|
|
|
76
|
-
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: params[:runner]), notice:
|
|
87
|
+
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: params[:runner]), notice: "All evaluations completed successfully"
|
|
77
88
|
end
|
|
78
89
|
|
|
79
90
|
def run_evaluator
|
|
80
|
-
return redirect_to workbench_index_path, alert:
|
|
91
|
+
return redirect_to workbench_index_path, alert: "No runner result available" unless @runner_result
|
|
81
92
|
|
|
82
93
|
evaluator_class = params[:evaluator].constantize
|
|
83
|
-
return redirect_to workbench_index_path, alert:
|
|
94
|
+
return redirect_to workbench_index_path, alert: "Invalid evaluator selected" unless evaluator_class < Leva::BaseEval
|
|
84
95
|
|
|
85
96
|
evaluator = evaluator_class.new
|
|
86
97
|
evaluator.evaluate_and_store(nil, @runner_result)
|
|
87
98
|
|
|
88
|
-
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: params[:runner]), notice:
|
|
99
|
+
redirect_to workbench_index_path(prompt_id: @prompt.id, dataset_record_id: @dataset_record.id, runner: params[:runner]), notice: "Evaluator run successfully"
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
private
|
|
@@ -110,4 +121,4 @@ module Leva
|
|
|
110
121
|
@runner_result = @dataset_record.runner_results.last if @dataset_record
|
|
111
122
|
end
|
|
112
123
|
end
|
|
113
|
-
end
|
|
124
|
+
end
|
|
@@ -4,24 +4,24 @@ module Leva
|
|
|
4
4
|
#
|
|
5
5
|
# @return [Array<Class>] An array of evaluator classes
|
|
6
6
|
def load_evaluators
|
|
7
|
-
load_classes_from_directory(
|
|
7
|
+
load_classes_from_directory("app/evals", Leva::BaseEval) || []
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
# Loads all runner classes that inherit from Leva::BaseRun
|
|
11
11
|
#
|
|
12
12
|
# @return [Array<Class>] An array of runner classes
|
|
13
13
|
def load_runners
|
|
14
|
-
load_classes_from_directory(
|
|
14
|
+
load_classes_from_directory("app/runners", Leva::BaseRun) || []
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Loads predefined prompts from markdown files
|
|
18
18
|
#
|
|
19
19
|
# @return [Array<Array<String, String>>] An array of prompt name and content pairs
|
|
20
20
|
def load_predefined_prompts
|
|
21
|
-
prompts = Dir.glob(Rails.root.join(
|
|
22
|
-
name = File.basename(file,
|
|
21
|
+
prompts = Dir.glob(Rails.root.join("app", "prompts", "*.md")).map do |file|
|
|
22
|
+
name = File.basename(file, ".md").titleize
|
|
23
23
|
content = File.read(file)
|
|
24
|
-
[name, content]
|
|
24
|
+
[ name, content ]
|
|
25
25
|
end
|
|
26
26
|
prompts
|
|
27
27
|
end
|
|
@@ -34,8 +34,8 @@ module Leva
|
|
|
34
34
|
# @param base_class [Class] The base class that loaded classes should inherit from
|
|
35
35
|
# @return [Array<Class>] An array of loaded classes
|
|
36
36
|
def load_classes_from_directory(directory, base_class)
|
|
37
|
-
classes = Dir[Rails.root.join(directory,
|
|
38
|
-
File.basename(file,
|
|
37
|
+
classes = Dir[Rails.root.join(directory, "*.rb")].map do |file|
|
|
38
|
+
File.basename(file, ".rb").camelize.constantize
|
|
39
39
|
end.select { |klass| klass < base_class }
|
|
40
40
|
classes.empty? ? [] : classes
|
|
41
41
|
end
|
|
@@ -3,10 +3,10 @@ module Leva
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
included do
|
|
6
|
-
has_many :dataset_records, as: :recordable, class_name:
|
|
7
|
-
has_many :datasets, through: :dataset_records, class_name:
|
|
8
|
-
has_many :runner_results, through: :dataset_records, class_name:
|
|
9
|
-
has_many :evaluation_results, through: :runner_results, class_name:
|
|
6
|
+
has_many :dataset_records, as: :recordable, class_name: "Leva::DatasetRecord", dependent: :destroy
|
|
7
|
+
has_many :datasets, through: :dataset_records, class_name: "Leva::Dataset"
|
|
8
|
+
has_many :runner_results, through: :dataset_records, class_name: "Leva::RunnerResult"
|
|
9
|
+
has_many :evaluation_results, through: :runner_results, class_name: "Leva::EvaluationResult"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
# @return [String] The ground truth label for the record
|
|
@@ -34,4 +34,4 @@ module Leva
|
|
|
34
34
|
false
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
|
-
end
|
|
37
|
+
end
|
data/app/models/leva/dataset.rb
CHANGED
data/app/models/leva/prompt.rb
CHANGED
|
@@ -1,40 +1,39 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="en"
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Leva - <%= yield(:title) || 'AI Evaluation Engine' %></title>
|
|
7
7
|
<%= csrf_meta_tags %>
|
|
8
8
|
<%= csp_meta_tag %>
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
<%# Fira Code for monospace, Helvetica is system font %>
|
|
11
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
12
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
13
|
+
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
|
14
|
+
|
|
15
|
+
<%= stylesheet_link_tag "leva/application", media: "all" %>
|
|
10
16
|
<script src="https://cdn.jsdelivr.net/npm/stimulus@3.2.2/dist/stimulus.umd.min.js"></script>
|
|
11
17
|
<%= yield(:head) %>
|
|
12
18
|
</head>
|
|
13
|
-
<body
|
|
14
|
-
<nav class="
|
|
15
|
-
<div class="
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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'}" %>
|
|
33
|
-
</div>
|
|
19
|
+
<body>
|
|
20
|
+
<nav class="nav">
|
|
21
|
+
<div class="nav-inner">
|
|
22
|
+
<%= link_to root_path, class: 'nav-brand' do %>
|
|
23
|
+
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="nav-logo">
|
|
24
|
+
<rect x="2" y="2" width="16" height="16" rx="3" fill="currentColor" fill-opacity="0.15"/>
|
|
25
|
+
<path d="M6 6V14H14" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
26
|
+
</svg>
|
|
27
|
+
<span>leva</span>
|
|
28
|
+
<% end %>
|
|
29
|
+
<div class="nav-links">
|
|
30
|
+
<%= link_to 'Workbench', workbench_index_path, class: "nav-link #{request.path == root_path || request.path.start_with?(workbench_index_path) ? 'active' : ''}" %>
|
|
31
|
+
<%= link_to 'Datasets', datasets_path, class: "nav-link #{request.path.start_with?(datasets_path) ? 'active' : ''}" %>
|
|
32
|
+
<%= link_to 'Experiments', experiments_path, class: "nav-link #{request.path.start_with?(experiments_path) ? 'active' : ''}" %>
|
|
34
33
|
</div>
|
|
35
34
|
</div>
|
|
36
35
|
</nav>
|
|
37
|
-
<main
|
|
36
|
+
<main>
|
|
38
37
|
<%= yield %>
|
|
39
38
|
</main>
|
|
40
39
|
</body>
|
|
@@ -1,49 +1,76 @@
|
|
|
1
1
|
<% content_for :title, "#{@dataset.name} - Records" %>
|
|
2
|
-
<div class="container
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
<div class="container page">
|
|
3
|
+
<%# Breadcrumb Navigation %>
|
|
4
|
+
<nav class="breadcrumb mb-4">
|
|
5
|
+
<%= link_to datasets_path, class: 'breadcrumb-link' do %>Datasets<% end %>
|
|
6
|
+
<span class="breadcrumb-sep">/</span>
|
|
7
|
+
<%= link_to @dataset.name, dataset_path(@dataset), class: 'breadcrumb-link' %>
|
|
8
|
+
<span class="breadcrumb-sep">/</span>
|
|
9
|
+
<span class="breadcrumb-current">Records</span>
|
|
10
|
+
</nav>
|
|
11
|
+
|
|
12
|
+
<div class="page-header">
|
|
13
|
+
<div>
|
|
14
|
+
<h1 class="page-title">Dataset Records</h1>
|
|
15
|
+
<p class="text-muted mt-2">All records in <strong><%= @dataset.name %></strong></p>
|
|
12
16
|
</div>
|
|
13
17
|
</div>
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
<% if @records.any? %>
|
|
20
|
+
<%# Section Header with Count %>
|
|
21
|
+
<div class="section-header">
|
|
22
|
+
<h3 class="section-title">Records</h3>
|
|
23
|
+
<span class="section-count"><%= @records.count %> total</span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="table-wrapper">
|
|
27
|
+
<div class="table-scroll">
|
|
28
|
+
<table class="table table-clickable">
|
|
29
|
+
<thead>
|
|
30
|
+
<tr>
|
|
31
|
+
<% @records.first.index_attributes.keys.each do |key| %>
|
|
32
|
+
<th><%= key.to_s.humanize %></th>
|
|
33
|
+
<% end %>
|
|
34
|
+
<th class="text-right" style="width: 50px;"></th>
|
|
35
|
+
</tr>
|
|
36
|
+
</thead>
|
|
37
|
+
<tbody>
|
|
38
|
+
<% @records.each do |record| %>
|
|
39
|
+
<tr class="clickable-row" onclick="window.location='<%= dataset_dataset_record_path(@dataset, record) %>'">
|
|
40
|
+
<% record.index_attributes.each_with_index do |(key, value), index| %>
|
|
41
|
+
<td class="<%= index == 0 ? '' : 'cell-truncate' %>">
|
|
42
|
+
<%= index == 0 ? content_tag(:span, value, class: 'row-title') : value %>
|
|
43
|
+
</td>
|
|
44
|
+
<% end %>
|
|
45
|
+
<td class="text-right">
|
|
46
|
+
<svg class="icon-sm text-muted" viewBox="0 0 20 20" fill="currentColor" style="opacity: 0.5;">
|
|
47
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
|
48
|
+
</svg>
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
36
51
|
<% end %>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<% else %>
|
|
57
|
+
<div class="card p-6">
|
|
58
|
+
<div class="empty-state" style="padding: var(--space-10) var(--space-6);">
|
|
59
|
+
<svg class="empty-state-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
60
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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" />
|
|
61
|
+
</svg>
|
|
62
|
+
<h3 class="empty-state-title">No records in this dataset</h3>
|
|
63
|
+
<p class="empty-state-description">Add records to start building your evaluation dataset.</p>
|
|
64
|
+
</div>
|
|
45
65
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</
|
|
66
|
+
<div class="divider"></div>
|
|
67
|
+
|
|
68
|
+
<div class="p-5" style="background: var(--gray-850, #222120); border-radius: var(--radius-md);">
|
|
69
|
+
<h4 class="text-sm font-semibold mb-3" style="color: var(--gray-300);">Adding Records</h4>
|
|
70
|
+
<p class="text-sm text-muted mb-3">Use the Rails console to add records to this dataset:</p>
|
|
71
|
+
<pre style="margin: 0;"><code>dataset = Leva::Dataset.find(<%= @dataset.id %>)
|
|
72
|
+
dataset.add_record(your_model_instance)</code></pre>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<% end %>
|
|
76
|
+
</div>
|