leva 0.1.10 → 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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/app/assets/stylesheets/leva/application.css +3083 -15
  4. data/app/controllers/leva/design_system_controller.rb +9 -0
  5. data/app/views/layouts/leva/application.html.erb +23 -24
  6. data/app/views/leva/dataset_records/index.html.erb +63 -61
  7. data/app/views/leva/dataset_records/show.html.erb +115 -25
  8. data/app/views/leva/datasets/_dataset.html.erb +11 -18
  9. data/app/views/leva/datasets/_form.html.erb +18 -14
  10. data/app/views/leva/datasets/edit.html.erb +16 -4
  11. data/app/views/leva/datasets/index.html.erb +33 -41
  12. data/app/views/leva/datasets/new.html.erb +15 -4
  13. data/app/views/leva/datasets/show.html.erb +120 -139
  14. data/app/views/leva/design_system/index.html.erb +1731 -0
  15. data/app/views/leva/experiments/_experiment.html.erb +46 -31
  16. data/app/views/leva/experiments/_form.html.erb +62 -35
  17. data/app/views/leva/experiments/edit.html.erb +17 -3
  18. data/app/views/leva/experiments/index.html.erb +41 -36
  19. data/app/views/leva/experiments/new.html.erb +40 -19
  20. data/app/views/leva/experiments/show.html.erb +155 -98
  21. data/app/views/leva/runner_results/show.html.erb +271 -54
  22. data/app/views/leva/workbench/_evaluation_area.html.erb +18 -4
  23. data/app/views/leva/workbench/_prompt_content.html.erb +116 -111
  24. data/app/views/leva/workbench/_prompt_form.html.erb +24 -23
  25. data/app/views/leva/workbench/_prompt_sidebar.html.erb +57 -12
  26. data/app/views/leva/workbench/_results_section.html.erb +274 -112
  27. data/app/views/leva/workbench/_top_bar.html.erb +16 -6
  28. data/app/views/leva/workbench/edit.html.erb +46 -15
  29. data/app/views/leva/workbench/index.html.erb +5 -8
  30. data/app/views/leva/workbench/new.html.erb +74 -42
  31. data/config/routes.rb +2 -0
  32. data/lib/leva/version.rb +1 -1
  33. metadata +4 -2
@@ -1,42 +1,57 @@
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
- <%= link_to experiment.name, experiment_path(experiment), class: "text-indigo-400 hover:text-indigo-300 transition-colors duration-200" %>
1
+ <%
2
+ # Pre-calculate status styling
3
+ status_dot = case experiment.status
4
+ when 'pending' then 'status-dot-pending'
5
+ when 'running' then 'status-dot-info'
6
+ when 'completed' then 'status-dot-success'
7
+ when 'failed' then 'status-dot-error'
8
+ else 'status-dot-pending'
9
+ end
10
+ run_count = experiment.runner_results.count
11
+ %>
12
+ <tr class="experiment-row" onclick="window.location='<%= experiment_path(experiment) %>'">
13
+ <td>
14
+ <div class="experiment-name-cell">
15
+ <span class="experiment-name"><%= experiment.name %></span>
16
+ <% if experiment.description.present? %>
17
+ <span class="experiment-desc"><%= truncate(experiment.description, length: 60) %></span>
18
+ <% end %>
19
+ </div>
20
+ </td>
21
+ <td>
22
+ <span class="cell-dataset"><%= experiment.dataset&.name || '—' %></span>
4
23
  </td>
5
- <td class="px-6 py-4 whitespace-nowrap text-sm">
6
- <% status_color = case experiment.status
7
- when 'pending' then 'bg-yellow-100 text-yellow-800'
8
- when 'running' then 'bg-blue-100 text-blue-800'
9
- when 'completed' then 'bg-green-100 text-green-800'
10
- when 'failed' then 'bg-red-100 text-red-800'
11
- else 'bg-gray-100 text-gray-800'
12
- end %>
13
- <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <%= status_color %>">
14
- <%= experiment.status&.capitalize || 'N/A' %>
24
+ <td class="text-right text-nowrap">
25
+ <span class="cell-timestamp"><%= time_ago_in_words(experiment.created_at) %></span>
26
+ </td>
27
+ <td class="text-center">
28
+ <span class="status-indicator">
29
+ <span class="status-dot <%= status_dot %>"></span>
30
+ <span class="status-text"><%= experiment.status&.capitalize || 'N/A' %></span>
15
31
  </span>
16
32
  </td>
17
- <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
18
- <%= experiment.runner_results.count %>
33
+ <td class="text-right">
34
+ <span class="cell-count"><%= run_count %></span>
19
35
  </td>
20
36
  <% Leva::EvaluationResult.distinct.pluck(:evaluator_class).each do |evaluator_class| %>
21
- <td class="px-6 py-4 whitespace-nowrap text-sm">
37
+ <td class="text-right">
22
38
  <% results = experiment.evaluation_results.where(evaluator_class: evaluator_class) %>
23
39
  <% if results.any? %>
24
- <% avg_score = (results.sum(&:score) / results.size.to_f).round(2) %>
25
- <% color_class = case avg_score
26
- when 0...0.2 then 'text-red-500'
27
- when 0.2...0.4 then 'text-orange-500'
28
- when 0.4...0.6 then 'text-yellow-500'
29
- when 0.6...0.8 then 'text-lime-500'
30
- when 0.8...1.0 then 'text-green-400'
31
- else 'text-green-300'
32
- end %>
33
- <span class="<%= color_class %> font-semibold"><%= sprintf('%.2f', avg_score) %></span>
40
+ <%
41
+ avg_score = (results.sum(&:score) / results.size.to_f)
42
+ score_pct = (avg_score * 100).round
43
+ score_class = case avg_score
44
+ when 0...0.2 then 'score-bad'
45
+ when 0.2...0.4 then 'score-poor'
46
+ when 0.4...0.6 then 'score-fair'
47
+ when 0.6...0.8 then 'score-good'
48
+ else 'score-excellent'
49
+ end
50
+ %>
51
+ <span class="score-pill <%= score_class %>"><%= score_pct %>%</span>
34
52
  <% else %>
35
- <span class="text-gray-400">N/A</span>
53
+ <span class="score-empty">—</span>
36
54
  <% end %>
37
55
  </td>
38
56
  <% end %>
39
- <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
40
- <%= link_to 'View Results', experiment_path(experiment), class: "text-indigo-400 hover:text-indigo-300 transition-colors duration-200" %>
41
- </td>
42
- </tr>
57
+ </tr>
@@ -1,49 +1,76 @@
1
- <%= form_with(model: @experiment, url: @experiment.new_record? ? experiments_path : experiment_path(@experiment), local: true, class: "bg-gray-800 rounded-lg shadow-lg p-6") do |form| %>
1
+ <%= form_with(model: @experiment, url: @experiment.new_record? ? experiments_path : experiment_path(@experiment), local: true, class: "card p-6") do |form| %>
2
2
  <% if @experiment.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">
3
+ <div class="form-errors">
4
+ <p class="form-errors-title">Please fix the following errors:</p>
5
+ <ul>
6
6
  <% @experiment.errors.full_messages.each do |message| %>
7
7
  <li><%= message %></li>
8
8
  <% end %>
9
9
  </ul>
10
10
  </div>
11
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-4">
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="mb-4">
21
- <%= form.label :dataset_id, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
22
- <%= form.collection_select :dataset_id, Leva::Dataset.all, :id, :name, {}, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
23
- </div>
24
- <div class="mb-4">
25
- <%= form.label :prompt_id, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
26
- <%= form.collection_select :prompt_id, Leva::Prompt.all, :id, :name, {}, class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
12
+
13
+ <%# Basic Information %>
14
+ <div class="form-section">
15
+ <div class="form-group">
16
+ <%= form.label :name, class: "form-label" %>
17
+ <%= form.text_field :name, autofocus: true, class: "form-input", placeholder: "e.g., Sentiment Analysis v2" %>
18
+ </div>
19
+
20
+ <div class="form-group">
21
+ <%= form.label :description, class: "form-label" %>
22
+ <%= form.text_area :description, rows: 3, class: "form-textarea", placeholder: "Optional description of this experiment..." %>
23
+ <p class="form-hint">Describe the purpose or hypothesis of this experiment.</p>
24
+ </div>
27
25
  </div>
28
- <div class="mb-4">
29
- <%= form.label :runner_class, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
30
- <%= form.select :runner_class,
31
- options_for_select(@runners.map { |r| [r.name.demodulize, r.name] }, @runners.first.name),
32
- {},
33
- class: "w-full bg-gray-700 text-white p-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none" %>
26
+
27
+ <hr class="form-divider">
28
+
29
+ <%# Configuration %>
30
+ <div class="form-section">
31
+ <h4 class="form-section-title">Configuration</h4>
32
+
33
+ <div class="form-row">
34
+ <div class="form-group flex-1">
35
+ <%= form.label :dataset_id, "Dataset", class: "form-label" %>
36
+ <%= form.collection_select :dataset_id, Leva::Dataset.all, :id, :name, { prompt: "Select a dataset..." }, class: "form-select" %>
37
+ </div>
38
+
39
+ <div class="form-group flex-1">
40
+ <%= form.label :prompt_id, "Prompt", class: "form-label" %>
41
+ <%= form.collection_select :prompt_id, Leva::Prompt.all, :id, :name, { prompt: "Select a prompt...", include_blank: "None" }, class: "form-select" %>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="form-group">
46
+ <%= form.label :runner_class, "Runner", class: "form-label" %>
47
+ <%= form.select :runner_class,
48
+ options_for_select(@runners.map { |r| [r.name.demodulize, r.name] }, @experiment.runner_class || @runners.first&.name),
49
+ {},
50
+ class: "form-select" %>
51
+ <p class="form-hint">The runner executes your model logic for each dataset record.</p>
52
+ </div>
34
53
  </div>
35
- <div class="mb-4">
36
- <%= form.label :evaluator_classes, class: "block text-sm font-semibold mb-2 text-indigo-300" %>
37
- <div class="space-y-2">
54
+
55
+ <hr class="form-divider">
56
+
57
+ <%# Evaluators %>
58
+ <div class="form-section">
59
+ <h4 class="form-section-title">Evaluators</h4>
60
+ <p class="form-section-desc">Select which evaluators to run on the experiment results.</p>
61
+
62
+ <div class="evaluator-checkbox-grid">
38
63
  <%= form.collection_check_boxes :evaluator_classes, @evaluators, :name, ->(e) { e.name.demodulize } do |b| %>
39
- <div class="flex items-center">
40
- <%= b.check_box(class: "mr-2 bg-gray-700 text-indigo-600 focus:ring-indigo-500", checked: !@experiment.persisted?) %>
41
- <%= b.label(class: "text-sm text-white") %>
42
- </div>
64
+ <label class="evaluator-checkbox">
65
+ <%= b.check_box(class: "form-check-input", checked: !@experiment.persisted?) %>
66
+ <span class="evaluator-checkbox-label"><%= b.text %></span>
67
+ </label>
43
68
  <% end %>
44
69
  </div>
45
70
  </div>
46
- <div class="flex items-center justify-end">
47
- <%= form.submit @experiment.persisted? ? "Update Experiment" : "Create Experiment", 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" %>
71
+
72
+ <div class="form-actions">
73
+ <%= link_to "Cancel", experiments_path, class: "btn btn-ghost" %>
74
+ <%= form.submit @experiment.persisted? ? "Update Experiment" : "Create & Run Experiment", class: "btn btn-primary" %>
48
75
  </div>
49
- <% end %>
76
+ <% end %>
@@ -1,5 +1,19 @@
1
1
  <% content_for :title, "Edit #{@experiment.name}" %>
2
- <div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
3
- <h1 class="text-3xl font-bold text-indigo-400 mb-6">Edit Experiment</h1>
2
+ <div class="container page">
3
+ <nav class="breadcrumb mb-4">
4
+ <%= link_to "Experiments", experiments_path, class: "breadcrumb-link" %>
5
+ <span class="breadcrumb-sep">/</span>
6
+ <%= link_to @experiment.name, experiment_path(@experiment), class: "breadcrumb-link" %>
7
+ <span class="breadcrumb-sep">/</span>
8
+ <span class="breadcrumb-current">Edit</span>
9
+ </nav>
10
+
11
+ <div class="page-header mb-6">
12
+ <div>
13
+ <h1>Edit Experiment</h1>
14
+ <p class="text-muted text-sm mt-1">Update configuration for "<%= @experiment.name %>"</p>
15
+ </div>
16
+ </div>
17
+
4
18
  <%= render 'form', experiment: @experiment %>
5
- </div>
19
+ </div>
@@ -1,55 +1,60 @@
1
1
  <% content_for :title, 'Experiments' %>
2
- <div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
3
- <div class="flex flex-col sm:flex-row justify-between items-center mb-6">
4
- <h1 class="text-3xl font-bold text-indigo-400 mb-4 sm:mb-0">Experiments</h1>
5
- <%= link_to new_experiment_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">
2
+ <div class="container page">
3
+ <div class="page-header">
4
+ <div>
5
+ <h1>Experiments</h1>
6
+ <p class="text-muted text-sm mt-1">Run evaluations across datasets to measure model performance</p>
7
+ </div>
8
+ <%= link_to new_experiment_path, class: "btn btn-primary" do %>
9
+ <svg class="icon-sm" viewBox="0 0 20 20" fill="currentColor">
7
10
  <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
11
  </svg>
9
- Create New Experiment
12
+ New Experiment
10
13
  <% end %>
11
14
  </div>
15
+
12
16
  <% if @experiments.any? %>
13
- <div class="bg-gray-800 rounded-lg shadow-lg overflow-x-auto">
14
- <div class="min-w-max">
15
- <table class="w-full divide-y divide-gray-700">
16
- <thead class="bg-gray-700">
17
+ <div class="table-wrapper">
18
+ <div class="table-scroll">
19
+ <table class="table table-experiments">
20
+ <thead>
17
21
  <tr>
18
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider whitespace-nowrap">
19
- Name
20
- </th>
21
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider whitespace-nowrap">
22
- Status
23
- </th>
24
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider whitespace-nowrap">
25
- Total Results
26
- </th>
22
+ <th>Experiment</th>
23
+ <th style="width: 140px;">Dataset</th>
24
+ <th class="text-right" style="width: 90px;">Created</th>
25
+ <th class="text-center" style="width: 90px;">Status</th>
26
+ <th class="text-right" style="width: 60px;">Runs</th>
27
27
  <% Leva::EvaluationResult.distinct.pluck(:evaluator_class).each do |evaluator_class| %>
28
- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider whitespace-nowrap">
29
- <%= evaluator_class %>
30
- </th>
28
+ <%
29
+ # Clean up evaluator name: "SentimentAccuracyEval" -> "Accuracy"
30
+ # Remove common prefixes/suffixes and module names
31
+ short_name = evaluator_class.demodulize
32
+ .gsub(/Evaluator$/, '')
33
+ .gsub(/Eval$/, '')
34
+ .gsub(/^Sentiment/, '') # Remove domain prefix
35
+ .gsub(/^[A-Z][a-z]+(?=[A-Z])/, '') # Remove leading word if followed by another
36
+ short_name = short_name.presence || evaluator_class.demodulize.gsub(/Eval(uator)?$/, '')
37
+ %>
38
+ <th class="text-right cell-numeric" style="width: 90px;"><%= short_name %></th>
31
39
  <% end %>
32
- <th scope="col" class="relative px-6 py-3">
33
- <span class="sr-only">Actions</span>
34
- </th>
35
40
  </tr>
36
41
  </thead>
37
- <tbody class="bg-gray-800 divide-y divide-gray-700">
42
+ <tbody>
38
43
  <%= render @experiments %>
39
44
  </tbody>
40
45
  </table>
41
46
  </div>
42
47
  </div>
43
48
  <% else %>
44
- <div class="bg-gray-800 rounded-lg shadow-lg p-12 text-center">
45
- <svg class="mx-auto h-12 w-12 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
46
- <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" />
47
- </svg>
48
- <h3 class="mt-2 text-xl font-medium text-indigo-300">No experiments yet</h3>
49
- <p class="mt-1 text-gray-400">Get started by creating a new experiment.</p>
50
- <div class="mt-6">
51
- <%= link_to new_experiment_path, class: "btn btn-primary inline-flex items-center" do %>
52
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
49
+ <div class="card">
50
+ <div class="empty-state">
51
+ <svg class="empty-state-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
52
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
53
+ </svg>
54
+ <h3 class="empty-state-title">No experiments yet</h3>
55
+ <p class="empty-state-description">Experiments let you run evaluations across datasets and track model performance over time.</p>
56
+ <%= link_to new_experiment_path, class: "btn btn-primary" do %>
57
+ <svg class="icon-sm" viewBox="0 0 20 20" fill="currentColor">
53
58
  <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" />
54
59
  </svg>
55
60
  Create your first experiment
@@ -57,4 +62,4 @@
57
62
  </div>
58
63
  </div>
59
64
  <% end %>
60
- </div>
65
+ </div>
@@ -1,27 +1,48 @@
1
1
  <% content_for :title, 'New Experiment' %>
2
- <div class="container mx-auto px-4 py-8 bg-gray-950 text-white">
3
- <h1 class="text-3xl font-bold text-indigo-400 mb-6">New Experiment</h1>
2
+ <div class="container page">
3
+ <nav class="breadcrumb mb-4">
4
+ <%= link_to "Experiments", experiments_path, class: "breadcrumb-link" %>
5
+ <span class="breadcrumb-sep">/</span>
6
+ <span class="breadcrumb-current">New</span>
7
+ </nav>
8
+
9
+ <div class="page-header mb-6">
10
+ <div>
11
+ <h1>New Experiment</h1>
12
+ <p class="text-muted text-sm mt-1">Configure and run a new evaluation experiment</p>
13
+ </div>
14
+ </div>
15
+
4
16
  <% if @runners.present? %>
5
17
  <%= render 'form', experiment: @experiment %>
6
18
  <% else %>
7
- <div class="bg-gray-800 rounded-lg shadow-lg p-12 text-center">
8
- <svg class="mx-auto h-12 w-12 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
9
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
10
- </svg>
11
- <h3 class="mt-2 text-xl font-medium text-yellow-300">No runners available</h3>
12
- <p class="mt-1 text-gray-400">You need at least one runner to create an experiment.</p>
13
- <div class="mt-6 text-left bg-gray-900 rounded-lg p-6">
14
- <h4 class="text-lg font-semibold text-indigo-300 mb-3">What are runners?</h4>
15
- <p class="text-gray-300 mb-4">Runners define how your model processes dataset records. They contain the execution logic that takes a dataset record and returns a prediction.</p>
16
- <h4 class="text-lg font-semibold text-indigo-300 mb-3">Create your first runner:</h4>
17
- <div class="bg-gray-800 rounded p-3 mb-4">
18
- <code class="text-green-400">rails generate leva:runner sentiment</code>
19
+ <div class="card p-6">
20
+ <div class="setup-required">
21
+ <div class="setup-required-icon">
22
+ <svg class="icon-xl" fill="none" viewBox="0 0 24 24" stroke="currentColor">
23
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
24
+ </svg>
25
+ </div>
26
+ <h3 class="setup-required-title">Setup Required</h3>
27
+ <p class="setup-required-desc">You need at least one runner to create an experiment.</p>
28
+ </div>
29
+
30
+ <hr class="form-divider">
31
+
32
+ <div class="setup-help">
33
+ <h4 class="setup-help-title">What are runners?</h4>
34
+ <p class="setup-help-text">Runners define how your model processes dataset records. They contain the execution logic that takes a dataset record and returns a prediction.</p>
35
+
36
+ <h4 class="setup-help-title mt-6">Create your first runner</h4>
37
+ <div class="setup-code-block">
38
+ <code>rails generate leva:runner sentiment</code>
19
39
  </div>
20
- <p class="text-gray-400 text-sm">This will create <code class="text-gray-300">app/runners/sentiment_run.rb</code> with a template to implement your model logic.</p>
40
+ <p class="setup-help-hint">This creates <code>app/runners/sentiment_run.rb</code> with a template to implement your model logic.</p>
21
41
  </div>
22
- <div class="mt-6">
23
- <%= link_to experiments_path, class: "btn btn-secondary inline-flex items-center" do %>
24
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
42
+
43
+ <div class="form-actions">
44
+ <%= link_to experiments_path, class: "btn btn-ghost" do %>
45
+ <svg class="icon-sm" viewBox="0 0 20 20" fill="currentColor">
25
46
  <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" />
26
47
  </svg>
27
48
  Back to Experiments
@@ -29,4 +50,4 @@
29
50
  </div>
30
51
  </div>
31
52
  <% end %>
32
- </div>
53
+ </div>