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,99 @@
|
|
1
|
+
<div class="admin-header">
|
2
|
+
<div>
|
3
|
+
<h1>Test Run History</h1>
|
4
|
+
<% if @prompt %>
|
5
|
+
<p class="text-muted">All test runs for <%= @prompt.name %></p>
|
6
|
+
<% elsif @prompt_version %>
|
7
|
+
<p class="text-muted">Test runs for <%= @prompt_version.prompt.name %> - Version <%= @prompt_version.version_number %></p>
|
8
|
+
<% else %>
|
9
|
+
<p class="text-muted">All test runs across all prompts</p>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="btn-group">
|
14
|
+
<% if @prompt %>
|
15
|
+
<%= link_to "Back to Prompt", prompt_path(@prompt), class: "btn btn--secondary btn--medium" %>
|
16
|
+
<%= link_to "Test Prompt", playground_prompt_path(@prompt), class: "btn btn--primary btn--medium" %>
|
17
|
+
<% elsif @prompt_version %>
|
18
|
+
<%= link_to "Back to Version", prompt_version_path(@prompt_version.prompt, @prompt_version), class: "btn btn--secondary btn--medium" %>
|
19
|
+
<% else %>
|
20
|
+
<%= link_to "All Prompts", prompts_path, class: "btn btn--secondary btn--medium" %>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="table-container">
|
26
|
+
<% if @playground_run_results.any? %>
|
27
|
+
<table class="table">
|
28
|
+
<thead>
|
29
|
+
<tr class="table__row">
|
30
|
+
<th class="table__cell table__cell--header">Prompt</th>
|
31
|
+
<th class="table__cell table__cell--header">Run Date</th>
|
32
|
+
<th class="table__cell table__cell--header">Version</th>
|
33
|
+
<th class="table__cell table__cell--header">Provider</th>
|
34
|
+
<th class="table__cell table__cell--header table__cell--hide-mobile">Model</th>
|
35
|
+
<th class="table__cell table__cell--header">Execution Time</th>
|
36
|
+
<th class="table__cell table__cell--header">Tokens</th>
|
37
|
+
<th class="table__cell table__cell--header table__cell--actions">Actions</th>
|
38
|
+
</tr>
|
39
|
+
</thead>
|
40
|
+
<tbody>
|
41
|
+
<% @playground_run_results.each do |result| %>
|
42
|
+
<tr class="table__row">
|
43
|
+
<td class="table__cell">
|
44
|
+
<%= link_to result.prompt_version.prompt.name, prompt_path(result.prompt_version.prompt), class: "table__action" %>
|
45
|
+
</td>
|
46
|
+
<td class="table__cell">
|
47
|
+
<div class="table__primary">
|
48
|
+
<%= result.created_at.strftime("%b %d, %Y") %>
|
49
|
+
</div>
|
50
|
+
<div class="table__secondary">
|
51
|
+
<%= result.created_at.strftime("%I:%M %p") %>
|
52
|
+
</div>
|
53
|
+
</td>
|
54
|
+
<td class="table__cell">
|
55
|
+
<span class="table__badge table__badge--info">v<%= result.prompt_version.version_number %></span>
|
56
|
+
</td>
|
57
|
+
<td class="table__cell">
|
58
|
+
<span class="table__badge table__badge--<%= result.provider == 'anthropic' ? 'warning' : 'success' %>">
|
59
|
+
<%= result.provider %>
|
60
|
+
</span>
|
61
|
+
</td>
|
62
|
+
<td class="table__cell table__cell--hide-mobile">
|
63
|
+
<span class="table__secondary"><%= result.model %></span>
|
64
|
+
</td>
|
65
|
+
<td class="table__cell">
|
66
|
+
<div class="table__metric">
|
67
|
+
<span class="table__metric-value"><%= result.execution_time %></span>
|
68
|
+
<span class="table__metric-unit">s</span>
|
69
|
+
</div>
|
70
|
+
</td>
|
71
|
+
<td class="table__cell">
|
72
|
+
<% if result.token_count %>
|
73
|
+
<div class="table__metric">
|
74
|
+
<span class="table__metric-value"><%= result.token_count %></span>
|
75
|
+
<span class="table__metric-unit">tokens</span>
|
76
|
+
</div>
|
77
|
+
<% else %>
|
78
|
+
<span class="table__secondary">—</span>
|
79
|
+
<% end %>
|
80
|
+
</td>
|
81
|
+
<td class="table__cell table__cell--actions">
|
82
|
+
<%= link_to "View Details", playground_run_result_path(result), class: "table__action" %>
|
83
|
+
</td>
|
84
|
+
</tr>
|
85
|
+
<% end %>
|
86
|
+
</tbody>
|
87
|
+
</table>
|
88
|
+
<% else %>
|
89
|
+
<div class="table__empty">
|
90
|
+
<div class="table__empty-title">No test runs yet</div>
|
91
|
+
<% if @prompt %>
|
92
|
+
<p class="table__empty-text">Start testing your prompt to see results here.</p>
|
93
|
+
<%= link_to "Test this prompt", playground_prompt_path(@prompt), class: "btn btn--primary btn--medium" %>
|
94
|
+
<% else %>
|
95
|
+
<p class="table__empty-text">Test runs will appear here once you start testing prompts.</p>
|
96
|
+
<% end %>
|
97
|
+
</div>
|
98
|
+
<% end %>
|
99
|
+
</div>
|
@@ -0,0 +1,123 @@
|
|
1
|
+
<div class="admin-header">
|
2
|
+
<div>
|
3
|
+
<h1>Test Run Details</h1>
|
4
|
+
<p class="text-muted"><%= @playground_run_result.created_at.strftime("%B %d, %Y at %I:%M %p") %></p>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="btn-group">
|
8
|
+
<%= link_to "Back to Test Runs", prompt_playground_run_results_path(@playground_run_result.prompt_version.prompt), class: "btn btn--secondary btn--medium" %>
|
9
|
+
<%= link_to "View Prompt", prompt_path(@playground_run_result.prompt_version.prompt), class: "btn btn--secondary btn--medium" %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="prompt-details">
|
14
|
+
<div class="card mb-lg">
|
15
|
+
<div class="card__header">
|
16
|
+
<h3 class="card__title">Run Information</h3>
|
17
|
+
</div>
|
18
|
+
<div class="card__body">
|
19
|
+
<div class="detail-grid">
|
20
|
+
<div class="detail-item">
|
21
|
+
<label class="detail-label">Prompt</label>
|
22
|
+
<div class="detail-value">
|
23
|
+
<%= link_to @playground_run_result.prompt_version.prompt.name, prompt_path(@playground_run_result.prompt_version.prompt), class: "link" %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="detail-item">
|
28
|
+
<label class="detail-label">Version</label>
|
29
|
+
<div class="detail-value">
|
30
|
+
<%= link_to "Version #{@playground_run_result.prompt_version.version_number}", prompt_version_path(@playground_run_result.prompt_version.prompt, @playground_run_result.prompt_version), class: "link" %>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="detail-item">
|
35
|
+
<label class="detail-label">Provider</label>
|
36
|
+
<div class="detail-value">
|
37
|
+
<span class="table__badge table__badge--info"><%= @playground_run_result.provider %></span>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div class="detail-item">
|
42
|
+
<label class="detail-label">Model</label>
|
43
|
+
<div class="detail-value"><%= @playground_run_result.model %></div>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="detail-item">
|
47
|
+
<label class="detail-label">Execution Time</label>
|
48
|
+
<div class="detail-value"><%= @playground_run_result.execution_time %>s</div>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
<div class="detail-item">
|
52
|
+
<label class="detail-label">Token Count</label>
|
53
|
+
<div class="detail-value"><%= @playground_run_result.token_count || "Not available" %></div>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
<% if @playground_run_result.temperature.present? %>
|
57
|
+
<div class="detail-item">
|
58
|
+
<label class="detail-label">Temperature</label>
|
59
|
+
<div class="detail-value"><%= @playground_run_result.temperature %></div>
|
60
|
+
</div>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
<% if @playground_run_result.max_tokens.present? %>
|
64
|
+
<div class="detail-item">
|
65
|
+
<label class="detail-label">Max Tokens</label>
|
66
|
+
<div class="detail-value"><%= @playground_run_result.max_tokens %></div>
|
67
|
+
</div>
|
68
|
+
<% end %>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<% if @playground_run_result.parameters.present? && @playground_run_result.parameters.any? %>
|
74
|
+
<div class="card mb-lg">
|
75
|
+
<div class="card__header">
|
76
|
+
<h3 class="card__title">Parameters Used</h3>
|
77
|
+
</div>
|
78
|
+
<div class="card__body">
|
79
|
+
<div class="parameters-table">
|
80
|
+
<% @playground_run_result.parameters.each do |key, value| %>
|
81
|
+
<div class="parameter-row">
|
82
|
+
<div class="parameter-name"><%= key %></div>
|
83
|
+
<div class="parameter-value"><%= value %></div>
|
84
|
+
</div>
|
85
|
+
<% end %>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
</div>
|
89
|
+
<% end %>
|
90
|
+
|
91
|
+
<% if @playground_run_result.system_message.present? %>
|
92
|
+
<div class="card mb-lg">
|
93
|
+
<div class="card__header">
|
94
|
+
<h3 class="card__title">System Message</h3>
|
95
|
+
</div>
|
96
|
+
<div class="card__body">
|
97
|
+
<pre class="prompt-content"><%= @playground_run_result.system_message %></pre>
|
98
|
+
</div>
|
99
|
+
</div>
|
100
|
+
<% end %>
|
101
|
+
|
102
|
+
<div class="card mb-lg">
|
103
|
+
<div class="card__header">
|
104
|
+
<h3 class="card__title">Rendered Prompt</h3>
|
105
|
+
</div>
|
106
|
+
<div class="card__body">
|
107
|
+
<pre class="prompt-content"><%= @playground_run_result.rendered_prompt %></pre>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
|
111
|
+
<div class="card">
|
112
|
+
<div class="card__header">
|
113
|
+
<h3 class="card__title">AI Response</h3>
|
114
|
+
</div>
|
115
|
+
<div class="card__body">
|
116
|
+
<pre class="prompt-content"><%= @playground_run_result.response %></pre>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
|
121
|
+
<div class="form__actions mt-lg">
|
122
|
+
<%= link_to "Back to Test Runs", prompt_playground_run_results_path(@playground_run_result.prompt_version.prompt), class: "btn btn--secondary btn--medium" %>
|
123
|
+
</div>
|
@@ -0,0 +1,224 @@
|
|
1
|
+
<%= form_with(model: prompt, local: true, html: { class: "form" }) do |form| %>
|
2
|
+
<% if prompt.errors.any? %>
|
3
|
+
<div class="form__errors">
|
4
|
+
<h3 class="text-danger">Please fix the following errors:</h3>
|
5
|
+
<ul>
|
6
|
+
<% prompt.errors.full_messages.each do |message| %>
|
7
|
+
<li class="text-danger text-sm"><%= message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<div class="form__group">
|
14
|
+
<%= form.label :name, class: "form__label form__label--required" %>
|
15
|
+
<%= form.text_field :name, class: "form__input", required: true,
|
16
|
+
placeholder: "e.g., Customer Support Response" %>
|
17
|
+
<div class="form__help">A descriptive name for this prompt</div>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<div class="form__group">
|
21
|
+
<%= form.label :description, class: "form__label" %>
|
22
|
+
<%= form.text_area :description, class: "form__textarea", rows: 2,
|
23
|
+
placeholder: "Brief description of what this prompt does" %>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div class="form__group">
|
27
|
+
<%= form.label :system_message, "System Message", class: "form__label" %>
|
28
|
+
<%= form.text_area :system_message, class: "form__textarea", rows: 4,
|
29
|
+
placeholder: "Instructions for the AI system (optional)" %>
|
30
|
+
<div class="form__help">Sets the behavior and context for the AI</div>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<div class="form__group">
|
34
|
+
<%= form.label :content, "Prompt Content", class: "form__label form__label--required" %>
|
35
|
+
<%= form.text_area :content, class: "form__textarea", rows: 6, required: true,
|
36
|
+
placeholder: "The main prompt content that will be sent to the AI",
|
37
|
+
data: { action: "input->variable-detector#detectVariables" } %>
|
38
|
+
<div class="form__help">The main prompt template. Use {{variable_name}} syntax for variables.</div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div id="parameters-section" class="parameters-section" style="display: none;">
|
42
|
+
<h3 class="form__section-title">Parameters</h3>
|
43
|
+
<div class="form__help mb-md">Configure the parameters detected in your prompt:</div>
|
44
|
+
|
45
|
+
<div id="parameters-list" class="parameters-list">
|
46
|
+
<!-- Parameters will be dynamically inserted here -->
|
47
|
+
</div>
|
48
|
+
|
49
|
+
<%= form.fields_for :parameters do |parameter_form| %>
|
50
|
+
<div class="parameter-fields" style="display: none;">
|
51
|
+
<%= parameter_form.hidden_field :id %>
|
52
|
+
<%= parameter_form.hidden_field :_destroy %>
|
53
|
+
</div>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<div class="form__inline">
|
58
|
+
<div class="form__group">
|
59
|
+
<%= form.label :model, "AI Model", class: "form__label" %>
|
60
|
+
<%= form.select :model,
|
61
|
+
options_for_select([
|
62
|
+
["GPT-4", "gpt-4"],
|
63
|
+
["GPT-4 Turbo", "gpt-4-turbo-preview"],
|
64
|
+
["GPT-3.5 Turbo", "gpt-3.5-turbo"],
|
65
|
+
["Claude 3 Opus", "claude-3-opus"],
|
66
|
+
["Claude 3 Sonnet", "claude-3-sonnet"],
|
67
|
+
["Claude 3 Haiku", "claude-3-haiku"]
|
68
|
+
], prompt.model),
|
69
|
+
{ include_blank: "Select a model" },
|
70
|
+
class: "form__select" %>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<div class="form__group">
|
74
|
+
<%= form.label :temperature, class: "form__label" %>
|
75
|
+
<%= form.number_field :temperature, class: "form__input",
|
76
|
+
step: 0.1, min: 0, max: 2, placeholder: "0.7" %>
|
77
|
+
<div class="form__help">0-2 (default: 0.7)</div>
|
78
|
+
</div>
|
79
|
+
|
80
|
+
<div class="form__group">
|
81
|
+
<%= form.label :max_tokens, "Max Tokens", class: "form__label" %>
|
82
|
+
<%= form.number_field :max_tokens, class: "form__input",
|
83
|
+
min: 1, placeholder: "500" %>
|
84
|
+
<div class="form__help">Maximum response length</div>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
|
88
|
+
<div class="form__group">
|
89
|
+
<%= form.label :status, class: "form__label" %>
|
90
|
+
<%= form.select :status,
|
91
|
+
options_for_select([
|
92
|
+
["Draft", "draft"],
|
93
|
+
["Active", "active"],
|
94
|
+
["Archived", "archived"]
|
95
|
+
], prompt.status),
|
96
|
+
{},
|
97
|
+
class: "form__select" %>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
<div class="form__actions">
|
101
|
+
<%= link_to "Cancel", prompts_path, class: "btn btn--secondary btn--medium" %>
|
102
|
+
<%= form.submit prompt.new_record? ? "Create Prompt" : "Update Prompt",
|
103
|
+
class: "btn btn--primary btn--medium" %>
|
104
|
+
</div>
|
105
|
+
<% end %>
|
106
|
+
|
107
|
+
|
108
|
+
<script>
|
109
|
+
document.addEventListener('DOMContentLoaded', function() {
|
110
|
+
const contentField = document.querySelector('[data-action="input->variable-detector#detectVariables"]');
|
111
|
+
const parametersSection = document.getElementById('parameters-section');
|
112
|
+
const parametersList = document.getElementById('parameters-list');
|
113
|
+
|
114
|
+
// Store existing parameters data (for edit form)
|
115
|
+
const existingParameters = <%= raw((prompt.persisted? ? prompt.parameters : []).map { |p| {
|
116
|
+
name: p.name,
|
117
|
+
description: p.description,
|
118
|
+
parameter_type: p.parameter_type,
|
119
|
+
required: p.required,
|
120
|
+
default_value: p.default_value,
|
121
|
+
id: p.id
|
122
|
+
}}.to_json) %>;
|
123
|
+
|
124
|
+
function detectVariables() {
|
125
|
+
const content = contentField.value;
|
126
|
+
const variablePattern = /\{\{([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\}\}/g;
|
127
|
+
const matches = [...content.matchAll(variablePattern)];
|
128
|
+
const uniqueVariables = [...new Set(matches.map(match => match[1]))];
|
129
|
+
|
130
|
+
if (uniqueVariables.length > 0) {
|
131
|
+
parametersSection.style.display = 'block';
|
132
|
+
updateParametersDisplay(uniqueVariables);
|
133
|
+
} else {
|
134
|
+
parametersSection.style.display = 'none';
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
function updateParametersDisplay(variables) {
|
139
|
+
parametersList.innerHTML = '';
|
140
|
+
|
141
|
+
// Add fields for parameters that should be kept
|
142
|
+
variables.forEach((variable, index) => {
|
143
|
+
const existingParam = existingParameters.find(p => p.name === variable);
|
144
|
+
const parameterItem = createParameterItem(variable, index, existingParam);
|
145
|
+
parametersList.appendChild(parameterItem);
|
146
|
+
});
|
147
|
+
|
148
|
+
// Add hidden fields to destroy parameters that are no longer in content
|
149
|
+
let destroyIndex = variables.length;
|
150
|
+
existingParameters.forEach(param => {
|
151
|
+
if (!variables.includes(param.name)) {
|
152
|
+
const destroyField = document.createElement('div');
|
153
|
+
destroyField.style.display = 'none';
|
154
|
+
destroyField.innerHTML = `
|
155
|
+
<input type="hidden" name="prompt[parameters_attributes][${destroyIndex}][id]" value="${param.id}">
|
156
|
+
<input type="hidden" name="prompt[parameters_attributes][${destroyIndex}][_destroy]" value="1">
|
157
|
+
`;
|
158
|
+
parametersList.appendChild(destroyField);
|
159
|
+
destroyIndex++;
|
160
|
+
}
|
161
|
+
});
|
162
|
+
}
|
163
|
+
|
164
|
+
function createParameterItem(variableName, index, existingData) {
|
165
|
+
const div = document.createElement('div');
|
166
|
+
div.className = 'parameter-item';
|
167
|
+
div.innerHTML = `
|
168
|
+
<div class="parameter-field">
|
169
|
+
<label>Name</label>
|
170
|
+
<span class="parameter-name">${variableName}</span>
|
171
|
+
<input type="hidden" name="prompt[parameters_attributes][${index}][name]" value="${variableName}">
|
172
|
+
${existingData ? `<input type="hidden" name="prompt[parameters_attributes][${index}][id]" value="${existingData.id}">` : ''}
|
173
|
+
</div>
|
174
|
+
|
175
|
+
<div class="parameter-field">
|
176
|
+
<label>Type</label>
|
177
|
+
<select name="prompt[parameters_attributes][${index}][parameter_type]" class="form__select form__select--small">
|
178
|
+
<option value="string" ${existingData?.parameter_type === 'string' ? 'selected' : ''}>String</option>
|
179
|
+
<option value="number" ${existingData?.parameter_type === 'number' ? 'selected' : ''}>Number</option>
|
180
|
+
<option value="boolean" ${existingData?.parameter_type === 'boolean' ? 'selected' : ''}>Boolean</option>
|
181
|
+
<option value="array" ${existingData?.parameter_type === 'array' ? 'selected' : ''}>Array</option>
|
182
|
+
<option value="object" ${existingData?.parameter_type === 'object' ? 'selected' : ''}>Object</option>
|
183
|
+
</select>
|
184
|
+
</div>
|
185
|
+
|
186
|
+
<div class="parameter-field">
|
187
|
+
<label>Required</label>
|
188
|
+
<div class="parameter-checkbox">
|
189
|
+
<input type="hidden" name="prompt[parameters_attributes][${index}][required]" value="0">
|
190
|
+
<input type="checkbox"
|
191
|
+
name="prompt[parameters_attributes][${index}][required]"
|
192
|
+
value="1"
|
193
|
+
${existingData?.required !== false ? 'checked' : ''}>
|
194
|
+
</div>
|
195
|
+
</div>
|
196
|
+
|
197
|
+
<div class="parameter-field">
|
198
|
+
<label>Default Value</label>
|
199
|
+
<input type="text"
|
200
|
+
name="prompt[parameters_attributes][${index}][default_value]"
|
201
|
+
class="form__input form__input--small"
|
202
|
+
placeholder="Optional"
|
203
|
+
value="${existingData?.default_value || ''}">
|
204
|
+
</div>
|
205
|
+
|
206
|
+
<div class="parameter-field">
|
207
|
+
<label>Description</label>
|
208
|
+
<input type="text"
|
209
|
+
name="prompt[parameters_attributes][${index}][description]"
|
210
|
+
class="form__input form__input--small"
|
211
|
+
placeholder="Optional"
|
212
|
+
value="${existingData?.description || ''}">
|
213
|
+
</div>
|
214
|
+
`;
|
215
|
+
return div;
|
216
|
+
}
|
217
|
+
|
218
|
+
if (contentField) {
|
219
|
+
contentField.addEventListener('input', detectVariables);
|
220
|
+
// Detect variables on page load
|
221
|
+
detectVariables();
|
222
|
+
}
|
223
|
+
});
|
224
|
+
</script>
|
@@ -0,0 +1,80 @@
|
|
1
|
+
<div class="admin-header">
|
2
|
+
<h1>Prompts</h1>
|
3
|
+
<%= link_to "New Prompt", new_prompt_path, class: "btn btn--primary btn--medium" %>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="table-container">
|
7
|
+
<% if @prompts.any? %>
|
8
|
+
<table class="table">
|
9
|
+
<thead>
|
10
|
+
<tr class="table__row">
|
11
|
+
<th class="table__cell table__cell--header">Name</th>
|
12
|
+
<th class="table__cell table__cell--header table__cell--hide-mobile">Model</th>
|
13
|
+
<th class="table__cell table__cell--header table__cell--center">Version</th>
|
14
|
+
<th class="table__cell table__cell--header table__cell--center">Status</th>
|
15
|
+
<th class="table__cell table__cell--header table__cell--hide-mobile">Created</th>
|
16
|
+
<th class="table__cell table__cell--header table__cell--actions">Actions</th>
|
17
|
+
</tr>
|
18
|
+
</thead>
|
19
|
+
<tbody>
|
20
|
+
<% @prompts.each do |prompt| %>
|
21
|
+
<tr class="table__row">
|
22
|
+
<td class="table__cell">
|
23
|
+
<%= link_to prompt.name, prompt_path(prompt) %>
|
24
|
+
<% if prompt.description.present? %>
|
25
|
+
<div class="text-sm text-muted"><%= truncate(prompt.description, length: 60) %></div>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
<td class="table__cell table__cell--hide-mobile">
|
29
|
+
<%= prompt.model || "Not set" %>
|
30
|
+
</td>
|
31
|
+
<td class="table__cell table__cell--center">
|
32
|
+
<% if prompt.current_version %>
|
33
|
+
<%= link_to "v#{prompt.current_version.version_number}",
|
34
|
+
prompt_versions_path(prompt),
|
35
|
+
class: "text-link" %>
|
36
|
+
<% else %>
|
37
|
+
<span class="text-muted">v1</span>
|
38
|
+
<% end %>
|
39
|
+
</td>
|
40
|
+
<td class="table__cell table__cell--center">
|
41
|
+
<span class="table__badge table__badge--<%= prompt.status %>">
|
42
|
+
<%= prompt.status %>
|
43
|
+
</span>
|
44
|
+
</td>
|
45
|
+
<td class="table__cell table__cell--hide-mobile text-muted">
|
46
|
+
<%= prompt.created_at.strftime("%b %d, %Y") %>
|
47
|
+
</td>
|
48
|
+
<td class="table__cell table__cell--actions">
|
49
|
+
<div class="btn-group">
|
50
|
+
<%= link_to "Edit", edit_prompt_path(prompt), class: "btn btn--secondary btn--small" %>
|
51
|
+
<%= button_to "Delete", prompt_path(prompt), method: :delete,
|
52
|
+
data: { confirm: "Are you sure you want to delete this prompt?" },
|
53
|
+
class: "btn btn--danger btn--small" %>
|
54
|
+
</div>
|
55
|
+
</td>
|
56
|
+
</tr>
|
57
|
+
<% end %>
|
58
|
+
</tbody>
|
59
|
+
</table>
|
60
|
+
<% else %>
|
61
|
+
<div class="table__empty">
|
62
|
+
<div class="table__empty-title">No prompts yet</div>
|
63
|
+
<p class="table__empty-text">Get started by creating your first prompt.</p>
|
64
|
+
<%= link_to "Create First Prompt", new_prompt_path, class: "btn btn--primary btn--medium" %>
|
65
|
+
</div>
|
66
|
+
<% end %>
|
67
|
+
</div>
|
68
|
+
|
69
|
+
<style>
|
70
|
+
.admin-header {
|
71
|
+
display: flex;
|
72
|
+
justify-content: space-between;
|
73
|
+
align-items: center;
|
74
|
+
margin-bottom: var(--spacing-xl);
|
75
|
+
}
|
76
|
+
|
77
|
+
.admin-header h1 {
|
78
|
+
margin: 0;
|
79
|
+
}
|
80
|
+
</style>
|