rubyllm-observ 0.5.1 → 0.6.1

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +56 -8
  3. data/app/assets/stylesheets/observ/_annotations.scss +114 -103
  4. data/app/assets/stylesheets/observ/_card.scss +58 -49
  5. data/app/assets/stylesheets/observ/_chat.scss +247 -155
  6. data/app/assets/stylesheets/observ/_components.scss +622 -340
  7. data/app/assets/stylesheets/observ/_dashboard.scss +31 -28
  8. data/app/assets/stylesheets/observ/_datasets.scss +494 -547
  9. data/app/assets/stylesheets/observ/_drawer.scss +250 -228
  10. data/app/assets/stylesheets/observ/_filters.scss +139 -0
  11. data/app/assets/stylesheets/observ/_json_viewer.scss +103 -97
  12. data/app/assets/stylesheets/observ/_layout.scss +443 -178
  13. data/app/assets/stylesheets/observ/_metrics.scss +79 -76
  14. data/app/assets/stylesheets/observ/_namespace.scss +18 -0
  15. data/app/assets/stylesheets/observ/_observations.scss +122 -119
  16. data/app/assets/stylesheets/observ/_pagination.scss +129 -112
  17. data/app/assets/stylesheets/observ/_prompts.scss +485 -269
  18. data/app/assets/stylesheets/observ/_reset.scss +249 -0
  19. data/app/assets/stylesheets/observ/_table.scss +46 -38
  20. data/app/assets/stylesheets/observ/_variables.scss +54 -0
  21. data/app/assets/stylesheets/observ/application.scss +3 -0
  22. data/app/controllers/observ/dataset_run_items_controller.rb +0 -1
  23. data/app/controllers/observ/review_queue_controller.rb +154 -0
  24. data/app/controllers/observ/scores_controller.rb +64 -0
  25. data/app/controllers/observ/sessions_controller.rb +23 -0
  26. data/app/helpers/observ/application_helper.rb +1 -0
  27. data/app/helpers/observ/reviews_helper.rb +33 -0
  28. data/app/models/concerns/observ/json_queryable.rb +138 -0
  29. data/app/models/concerns/observ/reviewable.rb +41 -0
  30. data/app/models/concerns/observ/scoreable.rb +34 -0
  31. data/app/models/observ/dataset_run_item.rb +3 -13
  32. data/app/models/observ/review_item.rb +48 -0
  33. data/app/models/observ/score.rb +38 -6
  34. data/app/models/observ/session.rb +5 -1
  35. data/app/models/observ/trace.rb +3 -0
  36. data/app/services/observ/evaluators/base_evaluator.rb +0 -1
  37. data/app/services/observ/guardrail_service.rb +128 -0
  38. data/app/views/kaminari/_first_page.html.erb +1 -1
  39. data/app/views/kaminari/_gap.html.erb +1 -1
  40. data/app/views/kaminari/_last_page.html.erb +1 -1
  41. data/app/views/kaminari/_next_page.html.erb +1 -1
  42. data/app/views/kaminari/_page.html.erb +1 -1
  43. data/app/views/kaminari/_paginator.html.erb +1 -1
  44. data/app/views/kaminari/_prev_page.html.erb +1 -1
  45. data/app/views/kaminari/observ/_first_page.html.erb +1 -1
  46. data/app/views/kaminari/observ/_gap.html.erb +1 -1
  47. data/app/views/kaminari/observ/_last_page.html.erb +1 -1
  48. data/app/views/kaminari/observ/_next_page.html.erb +1 -1
  49. data/app/views/kaminari/observ/_page.html.erb +1 -1
  50. data/app/views/kaminari/observ/_paginator.html.erb +1 -1
  51. data/app/views/kaminari/observ/_prev_page.html.erb +1 -1
  52. data/app/views/layouts/observ/application.html.erb +97 -59
  53. data/app/views/observ/annotations/_form.html.erb +5 -5
  54. data/app/views/observ/annotations/index.html.erb +4 -4
  55. data/app/views/observ/annotations/sessions_index.html.erb +9 -9
  56. data/app/views/observ/annotations/traces_index.html.erb +9 -9
  57. data/app/views/observ/chats/_form.html.erb +7 -7
  58. data/app/views/observ/datasets/index.html.erb +6 -6
  59. data/app/views/observ/messages/_form.html.erb +11 -12
  60. data/app/views/observ/observations/index.html.erb +3 -4
  61. data/app/views/observ/prompts/_form.html.erb +37 -38
  62. data/app/views/observ/prompts/_new_form.html.erb +37 -38
  63. data/app/views/observ/prompts/compare.html.erb +59 -55
  64. data/app/views/observ/prompts/edit.html.erb +3 -3
  65. data/app/views/observ/prompts/index.html.erb +9 -9
  66. data/app/views/observ/prompts/new.html.erb +3 -3
  67. data/app/views/observ/prompts/show.html.erb +2 -2
  68. data/app/views/observ/prompts/versions.html.erb +22 -22
  69. data/app/views/observ/review_queue/_item.html.erb +39 -0
  70. data/app/views/observ/review_queue/_stats.html.erb +18 -0
  71. data/app/views/observ/review_queue/index.html.erb +49 -0
  72. data/app/views/observ/review_queue/show.html.erb +76 -0
  73. data/app/views/observ/review_queue/stats.html.erb +100 -0
  74. data/app/views/observ/scores/_form.html.erb +39 -0
  75. data/app/views/observ/scores/create.turbo_stream.erb +10 -0
  76. data/app/views/observ/sessions/_chat.html.erb +59 -0
  77. data/app/views/observ/sessions/_metadata.html.erb +17 -0
  78. data/app/views/observ/sessions/_metrics.html.erb +81 -0
  79. data/app/views/observ/sessions/_traces.html.erb +92 -0
  80. data/app/views/observ/sessions/annotations_drawer.turbo_stream.erb +8 -1
  81. data/app/views/observ/sessions/index.html.erb +60 -4
  82. data/app/views/observ/sessions/show.html.erb +4 -217
  83. data/app/views/observ/traces/_details.html.erb +47 -0
  84. data/app/views/observ/traces/_input.html.erb +10 -0
  85. data/app/views/observ/traces/_metadata.html.erb +10 -0
  86. data/app/views/observ/traces/_observations.html.erb +172 -0
  87. data/app/views/observ/traces/_output.html.erb +10 -0
  88. data/app/views/observ/traces/annotations_drawer.turbo_stream.erb +8 -1
  89. data/app/views/observ/traces/index.html.erb +3 -4
  90. data/app/views/observ/traces/show.html.erb +5 -232
  91. data/config/routes.rb +14 -0
  92. data/db/migrate/015_refactor_scores_to_polymorphic.rb +27 -0
  93. data/db/migrate/016_create_observ_review_items.rb +25 -0
  94. data/lib/generators/observ/install/install_generator.rb +61 -9
  95. data/lib/generators/observ/install/templates/observ.js +19 -0
  96. data/lib/observ/version.rb +1 -1
  97. metadata +31 -2
  98. data/app/assets/javascripts/observ/controllers/index.js +0 -52
@@ -7,7 +7,7 @@
7
7
  paginator: the paginator that renders the pagination tags inside
8
8
  -%>
9
9
  <%= paginator.render do -%>
10
- <nav class="pagination" role="navigation" aria-label="pager">
10
+ <nav class="observ-pagination" role="navigation" aria-label="pager">
11
11
  <%= first_page_tag unless current_page.first? %>
12
12
  <%= prev_page_tag unless current_page.first? %>
13
13
  <% each_page do |page| -%>
@@ -6,6 +6,6 @@
6
6
  per_page: number of items to fetch per page
7
7
  remote: data-remote
8
8
  -%>
9
- <span class="prev">
9
+ <span class="observ-pagination__prev">
10
10
  <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote %>
11
11
  </span>
@@ -9,79 +9,117 @@
9
9
  <%# Load Vite assets in environments where the helper is available (e.g., demo app) %>
10
10
  <% if respond_to?(:vite_client_tag) %>
11
11
  <%= vite_client_tag %>
12
- <%= vite_javascript_tag 'application' %>
12
+ <%= vite_javascript_tag 'observ' %>
13
13
  <% end %>
14
14
  </head>
15
15
 
16
16
  <body class="observ-layout" data-controller="observ--drawer">
17
- <nav class="observ-nav">
18
- <div class="observ-nav__container">
19
- <div class="observ-nav__brand">
20
- <%= link_to "Observability", root_path, class: "observ-nav__brand-link" %>
21
- </div>
22
-
23
- <ul class="observ-nav__menu">
24
- <li class="observ-nav__item">
25
- <%= link_to "Dashboard", dashboard_path, class: "observ-nav__link #{current_page?(dashboard_path) ? 'observ-nav__link--active' : ''}" %>
26
- </li>
27
- <% if Observ.config.chat_ui_enabled? && respond_to?(:chats_path) %>
28
- <li class="observ-nav__item">
29
- <%= link_to "Chats", chats_path, class: "observ-nav__link #{current_page?(chats_path) ? 'observ-nav__link--active' : ''}" %>
30
- </li>
31
- <% end %>
32
- <li class="observ-nav__item">
33
- <%= link_to "Sessions", sessions_path, class: "observ-nav__link #{current_page?(sessions_path) ? 'observ-nav__link--active' : ''}" %>
34
- </li>
35
- <li class="observ-nav__item">
36
- <%= link_to "Traces", traces_path, class: "observ-nav__link #{current_page?(traces_path) ? 'observ-nav__link--active' : ''}" %>
37
- </li>
38
- <li class="observ-nav__item">
39
- <%= link_to "Observations", observations_path, class: "observ-nav__link #{current_page?(observations_path) ? 'observ-nav__link--active' : ''}" %>
40
- </li>
41
- <li class="observ-nav__item">
42
- <%= link_to "Annotations", sessions_annotations_path, class: "observ-nav__link #{current_page?(sessions_annotations_path) || current_page?(traces_annotations_path) ? 'observ-nav__link--active' : ''}" %>
43
- </li>
44
- <li class="observ-nav__item">
45
- <%= link_to "Prompts", prompts_path, class: "observ-nav__link #{controller_name == 'prompts' || controller_name == 'prompt_versions' ? 'observ-nav__link--active' : ''}" %>
46
- </li>
47
- <li class="observ-nav__item">
48
- <%= link_to "Datasets", datasets_path, class: "observ-nav__link #{controller_name == 'datasets' || controller_name == 'dataset_items' || controller_name == 'dataset_runs' ? 'observ-nav__link--active' : ''}" %>
49
- </li>
50
- </ul>
51
-
52
- <div class="observ-nav__actions">
53
- <%= link_to Observ.config.back_to_app_label, main_app.instance_exec(&Observ.config.back_to_app_path), class: "observ-nav__link observ-nav__link--secondary" %>
54
- </div>
17
+ <aside class="observ-sidebar">
18
+ <div class="observ-sidebar__header">
19
+ <%= link_to "Observ", root_path, class: "observ-sidebar__brand" %>
55
20
  </div>
56
- </nav>
57
21
 
58
- <main class="observ-main">
59
- <% if content_for?(:page_header) %>
60
- <div class="observ-page-header">
61
- <%= yield :page_header %>
22
+ <nav class="observ-sidebar__nav">
23
+ <%# Primary navigation %>
24
+ <div class="observ-sidebar__section">
25
+ <ul class="observ-sidebar__menu">
26
+ <li class="observ-sidebar__item">
27
+ <%= link_to "Dashboard", dashboard_path, class: "observ-sidebar__link #{current_page?(dashboard_path) ? 'observ-sidebar__link--active' : ''}" %>
28
+ </li>
29
+ <% if Observ.config.chat_ui_enabled? && respond_to?(:chats_path) %>
30
+ <li class="observ-sidebar__item">
31
+ <%= link_to "Chats", chats_path, class: "observ-sidebar__link #{current_page?(chats_path) ? 'observ-sidebar__link--active' : ''}" %>
32
+ </li>
33
+ <% end %>
34
+ </ul>
62
35
  </div>
63
- <% end %>
64
36
 
65
- <% if notice.present? %>
66
- <div class="observ-alert observ-alert--success">
67
- <%= notice %>
37
+ <%# Data section %>
38
+ <div class="observ-sidebar__section">
39
+ <div class="observ-sidebar__section-title">Data</div>
40
+ <ul class="observ-sidebar__menu">
41
+ <li class="observ-sidebar__item">
42
+ <%= link_to "Sessions", sessions_path, class: "observ-sidebar__link #{current_page?(sessions_path) ? 'observ-sidebar__link--active' : ''}" %>
43
+ </li>
44
+ <li class="observ-sidebar__item">
45
+ <%= link_to "Traces", traces_path, class: "observ-sidebar__link #{current_page?(traces_path) ? 'observ-sidebar__link--active' : ''}" %>
46
+ </li>
47
+ <li class="observ-sidebar__item">
48
+ <%= link_to "Observations", observations_path, class: "observ-sidebar__link #{current_page?(observations_path) ? 'observ-sidebar__link--active' : ''}" %>
49
+ </li>
50
+ </ul>
68
51
  </div>
69
- <% end %>
70
52
 
71
- <% if alert.present? %>
72
- <div class="observ-alert observ-alert--danger">
73
- <%= alert %>
53
+ <%# Tools section %>
54
+ <div class="observ-sidebar__section">
55
+ <div class="observ-sidebar__section-title">Tools</div>
56
+ <ul class="observ-sidebar__menu">
57
+ <li class="observ-sidebar__item">
58
+ <%= link_to "Prompts", prompts_path, class: "observ-sidebar__link #{controller_name == 'prompts' || controller_name == 'prompt_versions' ? 'observ-sidebar__link--active' : ''}" %>
59
+ </li>
60
+ <li class="observ-sidebar__item">
61
+ <%= link_to "Datasets", datasets_path, class: "observ-sidebar__link #{controller_name == 'datasets' || controller_name == 'dataset_items' || controller_name == 'dataset_runs' ? 'observ-sidebar__link--active' : ''}" %>
62
+ </li>
63
+ <li class="observ-sidebar__item">
64
+ <%= link_to "Annotations", sessions_annotations_path, class: "observ-sidebar__link #{current_page?(sessions_annotations_path) || current_page?(traces_annotations_path) ? 'observ-sidebar__link--active' : ''}" %>
65
+ </li>
66
+ </ul>
74
67
  </div>
75
- <% end %>
76
68
 
77
- <%= yield %>
78
- </main>
69
+ <%# Quality section %>
70
+ <div class="observ-sidebar__section">
71
+ <div class="observ-sidebar__section-title">Quality</div>
72
+ <ul class="observ-sidebar__menu">
73
+ <li class="observ-sidebar__item">
74
+ <%= link_to reviews_path, class: "observ-sidebar__link #{controller_name == 'review_queue' ? 'observ-sidebar__link--active' : ''}" do %>
75
+ <span>Review Queue</span>
76
+ <% pending_count = Observ::ReviewItem.actionable.count rescue 0 %>
77
+ <% if pending_count > 0 %>
78
+ <span class="observ-sidebar__badge"><%= pending_count %></span>
79
+ <% end %>
80
+ <% end %>
81
+ </li>
82
+ </ul>
83
+ </div>
84
+ </nav>
79
85
 
80
- <footer class="observ-footer">
81
- <div class="observ-footer__container">
82
- <p class="observ-footer__text">Observability Dashboard · Rails Observ POC</p>
86
+ <div class="observ-sidebar__footer">
87
+ <%= link_to main_app.instance_exec(&Observ.config.back_to_app_path), class: "observ-sidebar__back-link" do %>
88
+ <span>&larr;</span>
89
+ <span><%= Observ.config.back_to_app_label %></span>
90
+ <% end %>
83
91
  </div>
84
- </footer>
92
+ </aside>
93
+
94
+ <div class="observ-main-wrapper">
95
+ <main class="observ-main">
96
+ <% if content_for?(:page_header) %>
97
+ <div class="observ-page-header">
98
+ <%= yield :page_header %>
99
+ </div>
100
+ <% end %>
101
+
102
+ <% if notice.present? %>
103
+ <div class="observ-alert observ-alert--success">
104
+ <%= notice %>
105
+ </div>
106
+ <% end %>
107
+
108
+ <% if alert.present? %>
109
+ <div class="observ-alert observ-alert--danger">
110
+ <%= alert %>
111
+ </div>
112
+ <% end %>
113
+
114
+ <%= yield %>
115
+ </main>
116
+
117
+ <footer class="observ-footer">
118
+ <div class="observ-footer__container">
119
+ <p class="observ-footer__text">Observability Dashboard</p>
120
+ </div>
121
+ </footer>
122
+ </div>
85
123
 
86
124
  <%= render "shared/drawer" %>
87
125
  </body>
@@ -2,7 +2,7 @@
2
2
  model: annotation,
3
3
  url: annotatable.is_a?(Observ::Session) ? session_annotations_path(annotatable) : trace_annotations_path(annotatable),
4
4
  id: "annotation-form",
5
- class: "observ-annotation-form"
5
+ class: "observ-form"
6
6
  ) do |form| %>
7
7
  <% if annotation.errors.any? %>
8
8
  <div class="observ-alert observ-alert--danger">
@@ -14,15 +14,15 @@
14
14
  </div>
15
15
  <% end %>
16
16
 
17
- <div class="observ-form-field">
18
- <%= form.label :content, "Add annotation", class: "observ-form-label" %>
17
+ <div class="observ-form__group">
18
+ <%= form.label :content, "Add annotation", class: "observ-form__label" %>
19
19
  <%= form.text_area :content,
20
- class: "observ-form-textarea",
20
+ class: "observ-form__textarea",
21
21
  placeholder: "Enter your annotation...",
22
22
  rows: 3 %>
23
23
  </div>
24
24
 
25
- <div class="observ-form-actions">
25
+ <div class="observ-form__actions">
26
26
  <%= form.submit "Add Annotation", class: "observ-button observ-button--primary" %>
27
27
  </div>
28
28
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <div class="observ-container">
2
- <div class="observ-header">
3
- <h1 class="observ-title">
2
+ <div class="observ-page__header">
3
+ <h1 class="observ-page__title">
4
4
  Annotations for <%= @annotatable.is_a?(Observ::Session) ? "Session" : "Trace" %>
5
5
  <%= link_to @annotatable.id, @annotatable.is_a?(Observ::Session) ? session_path(@annotatable) : trace_path(@annotatable), class: "observ-link" %>
6
6
  </h1>
@@ -11,7 +11,7 @@
11
11
  <h2 class="observ-card__title">Annotations (<span id="annotations-count"><%= @annotations.count %></span>)</h2>
12
12
  </div>
13
13
 
14
- <div class="observ-card__content">
14
+ <div class="observ-card__body">
15
15
  <%= render partial: "observ/annotations/form", locals: { annotatable: @annotatable, annotation: @annotatable.annotations.build } %>
16
16
 
17
17
  <div id="annotations-list" class="observ-annotations-list">
@@ -19,7 +19,7 @@
19
19
  <%= render partial: "observ/annotations/annotation", collection: @annotations, locals: { annotatable: @annotatable } %>
20
20
  <% else %>
21
21
  <div id="annotations-empty-state" class="observ-empty-state">
22
- <p>No annotations yet.</p>
22
+ <p class="observ-empty-state__text">No annotations yet.</p>
23
23
  </div>
24
24
  <% end %>
25
25
  </div>
@@ -1,22 +1,22 @@
1
1
  <div class="observ-container">
2
- <div class="observ-header">
3
- <h1 class="observ-title">Annotations</h1>
2
+ <div class="observ-page__header">
3
+ <h1 class="observ-page__title">Annotations</h1>
4
4
  </div>
5
5
 
6
6
  <div class="observ-tabs">
7
- <div class="observ-tabs__list">
8
- <%= link_to "Sessions", sessions_annotations_path, class: "observ-tabs__tab observ-tabs__tab--active" %>
9
- <%= link_to "Traces", traces_annotations_path, class: "observ-tabs__tab" %>
10
- </div>
7
+ <%= link_to "Sessions", sessions_annotations_path, class: "observ-tab observ-tab--active" %>
8
+ <%= link_to "Traces", traces_annotations_path, class: "observ-tab" %>
11
9
  </div>
12
10
 
13
11
  <div class="observ-card">
14
12
  <div class="observ-card__header">
15
13
  <h2 class="observ-card__title">All Annotations (<%= @annotations.count %>)</h2>
16
- <%= link_to "Export CSV", export_annotations_path(type: "sessions", format: :csv), class: "observ-button observ-button--secondary", style: "margin-left: auto;" %>
14
+ <div class="observ-card__actions">
15
+ <%= link_to "Export CSV", export_annotations_path(type: "sessions", format: :csv), class: "observ-button observ-button--secondary" %>
16
+ </div>
17
17
  </div>
18
18
 
19
- <div class="observ-card__content">
19
+ <div class="observ-card__body">
20
20
  <% if @annotations.any? %>
21
21
  <div class="observ-annotations-list">
22
22
  <% @annotations.each do |annotation| %>
@@ -38,7 +38,7 @@
38
38
  </div>
39
39
  <% else %>
40
40
  <div class="observ-empty-state">
41
- <p>No annotations for sessions yet.</p>
41
+ <p class="observ-empty-state__text">No annotations for sessions yet.</p>
42
42
  </div>
43
43
  <% end %>
44
44
  </div>
@@ -1,22 +1,22 @@
1
1
  <div class="observ-container">
2
- <div class="observ-header">
3
- <h1 class="observ-title">Annotations</h1>
2
+ <div class="observ-page__header">
3
+ <h1 class="observ-page__title">Annotations</h1>
4
4
  </div>
5
5
 
6
6
  <div class="observ-tabs">
7
- <div class="observ-tabs__list">
8
- <%= link_to "Sessions", sessions_annotations_path, class: "observ-tabs__tab" %>
9
- <%= link_to "Traces", traces_annotations_path, class: "observ-tabs__tab observ-tabs__tab--active" %>
10
- </div>
7
+ <%= link_to "Sessions", sessions_annotations_path, class: "observ-tab" %>
8
+ <%= link_to "Traces", traces_annotations_path, class: "observ-tab observ-tab--active" %>
11
9
  </div>
12
10
 
13
11
  <div class="observ-card">
14
12
  <div class="observ-card__header">
15
13
  <h2 class="observ-card__title">All Annotations (<%= @annotations.count %>)</h2>
16
- <%= link_to "Export CSV", export_annotations_path(type: "traces", format: :csv), class: "observ-button observ-button--secondary", style: "margin-left: auto;" %>
14
+ <div class="observ-card__actions">
15
+ <%= link_to "Export CSV", export_annotations_path(type: "traces", format: :csv), class: "observ-button observ-button--secondary" %>
16
+ </div>
17
17
  </div>
18
18
 
19
- <div class="observ-card__content">
19
+ <div class="observ-card__body">
20
20
  <% if @annotations.any? %>
21
21
  <div class="observ-annotations-list">
22
22
  <% @annotations.each do |annotation| %>
@@ -38,7 +38,7 @@
38
38
  </div>
39
39
  <% else %>
40
40
  <div class="observ-empty-state">
41
- <p>No annotations for traces yet.</p>
41
+ <p class="observ-empty-state__text">No annotations for traces yet.</p>
42
42
  </div>
43
43
  <% end %>
44
44
  </div>
@@ -14,27 +14,27 @@
14
14
  </div>
15
15
  <% end %>
16
16
 
17
- <div class="observ-form-group">
18
- <%= form.label :agent_class_name, "Select Agent Type:", class: "observ-label" %>
17
+ <div class="observ-form__group">
18
+ <%= form.label :agent_class_name, "Select Agent Type:", class: "observ-form__label" %>
19
19
  <%= form.select :agent_class_name,
20
20
  agent_select_options,
21
21
  {},
22
- class: "observ-select",
22
+ class: "observ-form__select",
23
23
  data: {
24
24
  observ__chat_form_target: "agentSelect",
25
25
  action: "change->observ--chat-form#agentChanged"
26
26
  } %>
27
27
  </div>
28
28
 
29
- <div class="observ-form-group observ-hidden"
29
+ <div class="observ-form__group observ-hidden"
30
30
  data-observ--chat-form-target="promptVersionGroup">
31
- <%= form.label :prompt_version, "Select Prompt Version:", class: "observ-label" %>
31
+ <%= form.label :prompt_version, "Select Prompt Version:", class: "observ-form__label" %>
32
32
  <%= form.select :prompt_version,
33
33
  [],
34
34
  { include_blank: "Use default (production)" },
35
- class: "observ-select",
35
+ class: "observ-form__select",
36
36
  data: { observ__chat_form_target: "promptVersionSelect" } %>
37
- <p class="observ-form-help-text">
37
+ <p class="observ-form__hint">
38
38
  Leave as default to use the production version, or select a specific version to test.
39
39
  </p>
40
40
  </div>
@@ -11,18 +11,18 @@
11
11
 
12
12
  <div class="observ-container">
13
13
  <!-- Search -->
14
- <section class="observ-card observ-datasets-filters">
14
+ <section class="observ-card observ-filters">
15
15
  <div class="observ-card__body">
16
- <%= form_with url: datasets_path, method: :get, class: "observ-datasets-filters__form" do |f| %>
17
- <div class="observ-datasets-filters__field observ-datasets-filters__field--search">
18
- <%= f.label :search, "Search datasets", class: "observ-datasets-filters__label" %>
16
+ <%= form_with url: datasets_path, method: :get, class: "observ-filters__form" do |f| %>
17
+ <div class="observ-filters__field observ-filters__field--flex">
18
+ <%= f.label :search, "Search datasets", class: "observ-filters__label" %>
19
19
  <%= f.text_field :search,
20
20
  value: params[:search],
21
21
  placeholder: "Search by name...",
22
- class: "observ-datasets-filters__input" %>
22
+ class: "observ-filters__input" %>
23
23
  </div>
24
24
 
25
- <div class="observ-datasets-filters__actions">
25
+ <div class="observ-filters__actions">
26
26
  <%= f.submit "Search", class: "observ-button observ-button--secondary" %>
27
27
  <%= link_to "Clear", datasets_path, class: "observ-button" %>
28
28
  </div>
@@ -1,4 +1,4 @@
1
- <%= form_with(model: message, url: chat_messages_path(chat), id: "new_message", data: { controller: "observ--message-form" }) do |form| %>
1
+ <%= form_with(model: message, url: chat_messages_path(chat), id: "new_message", class: "observ-message-form", data: { controller: "observ--message-form" }) do |form| %>
2
2
  <% if message.errors.any? %>
3
3
  <div class="observ-alert observ-alert--danger">
4
4
  <h3 class="observ-alert__title"><%= pluralize(message.errors.count, "error") %> prohibited this message from being saved:</h3>
@@ -10,24 +10,23 @@
10
10
  </div>
11
11
  <% end %>
12
12
 
13
- <div class="observ-form-group">
14
- <%= form.text_field :content,
15
- class: "observ-input observ-input--wide",
13
+ <div class="observ-message-form__group">
14
+ <%= form.text_area :content,
15
+ rows: 2,
16
+ class: "observ-message-form__input",
16
17
  placeholder: "Type your message...",
17
18
  autofocus: true,
18
19
  data: {
19
20
  observ__message_form_target: "input",
20
21
  action: "input->observ--message-form#toggleSubmit"
21
22
  } %>
22
- </div>
23
-
24
- <div class="observ-form-actions">
25
- <%= form.submit "Send message",
23
+ <%= form.submit "Send",
26
24
  class: "observ-button observ-button--primary",
27
25
  data: { observ__message_form_target: "submit" } %>
28
- <span class="observ-loading-indicator" data-observ--message-form-target="loadingIndicator" style="display: none;">
29
- <span class="observ-spinner"></span>
30
- <span>Waiting for response...</span>
31
- </span>
26
+ </div>
27
+
28
+ <div class="observ-message-form__loading" data-observ--message-form-target="loadingIndicator" style="display: none;">
29
+ <span class="observ-spinner"></span>
30
+ <span>Waiting for response...</span>
32
31
  </div>
33
32
  <% end %>
@@ -87,11 +87,10 @@
87
87
  <% else %>
88
88
  <div class="observ-card__empty">
89
89
  <p>No observations found.</p>
90
- </div>
91
- <% end %>
90
+ </div>
91
+ <% end %>
92
+ </div>
92
93
  </section>
93
94
 
94
95
  <%= observ_pagination(@observations) %>
95
- </div>
96
- </section>
97
96
  </div>
@@ -2,30 +2,28 @@
2
2
  method: prompt.persisted? ? :patch : :post do |f| %>
3
3
 
4
4
  <% if prompt.errors.any? %>
5
- <div class="observ-card" style="margin-bottom: 1.5rem; border-left: 4px solid #dc2626;">
6
- <div class="observ-card__body">
7
- <h3 style="color: #dc2626; font-weight: 500; margin: 0 0 0.5rem;">
8
- <%= pluralize(prompt.errors.count, "error") %> prohibited this prompt from being saved:
9
- </h3>
10
- <ul style="list-style: disc; padding-left: 1.5rem; color: #dc2626; font-size: 0.875rem; margin: 0;">
11
- <% prompt.errors.full_messages.each do |message| %>
12
- <li><%= message %></li>
13
- <% end %>
14
- </ul>
15
- </div>
5
+ <div class="observ-alert observ-alert--danger">
6
+ <h3 class="observ-alert__title">
7
+ <%= pluralize(prompt.errors.count, "error") %> prohibited this prompt from being saved:
8
+ </h3>
9
+ <ul class="observ-alert__list">
10
+ <% prompt.errors.full_messages.each do |message| %>
11
+ <li><%= message %></li>
12
+ <% end %>
13
+ </ul>
16
14
  </div>
17
15
  <% end %>
18
16
 
19
17
  <div class="observ-card">
20
18
  <div class="observ-card__body">
21
19
  <!-- Prompt Name -->
22
- <div style="margin-bottom: 1.5rem;">
23
- <%= f.label :name, "Prompt Name", style: "display: block; margin-bottom: 0.5rem; font-weight: 500;" %>
20
+ <div class="observ-form__group">
21
+ <%= f.label :name, "Prompt Name", class: "observ-form__label" %>
24
22
  <%= f.text_field :name,
25
23
  disabled: prompt.persisted?,
26
24
  placeholder: "e.g., research-agent-system-prompt",
27
- style: "width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; #{prompt.persisted? ? 'background-color: #f3f4f6;' : ''}" %>
28
- <p style="margin-top: 0.25rem; font-size: 0.875rem; color: #6b7280;">
25
+ class: "observ-form__input" %>
26
+ <p class="observ-form__hint">
29
27
  <% if prompt.persisted? %>
30
28
  Name cannot be changed. Create a new prompt to use a different name.
31
29
  <% else %>
@@ -35,70 +33,71 @@
35
33
  </div>
36
34
 
37
35
  <!-- Commit Message -->
38
- <div style="margin-bottom: 1.5rem;">
39
- <%= f.label :commit_message, "Commit Message", style: "display: block; margin-bottom: 0.5rem; font-weight: 500;" %>
36
+ <div class="observ-form__group">
37
+ <%= f.label :commit_message, "Commit Message", class: "observ-form__label" %>
40
38
  <%= f.text_area :commit_message,
41
39
  rows: 2,
42
40
  placeholder: "Describe what changed in this version...",
43
- style: "width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-family: inherit;" %>
44
- <p style="margin-top: 0.25rem; font-size: 0.875rem; color: #6b7280;">
41
+ class: "observ-form__textarea" %>
42
+ <p class="observ-form__hint">
45
43
  Optional but recommended for tracking changes
46
44
  </p>
47
45
  </div>
48
46
 
49
47
  <!-- Prompt Content -->
50
- <div style="margin-bottom: 1.5rem;" data-controller="observ--prompt-variables">
51
- <%= f.label :prompt, "Prompt Content", style: "display: block; margin-bottom: 0.5rem; font-weight: 500;" %>
48
+ <div class="observ-form__group" data-controller="observ--prompt-variables">
49
+ <%= f.label :prompt, "Prompt Content", class: "observ-form__label" %>
52
50
  <%= f.text_area :prompt,
53
51
  rows: 15,
54
52
  placeholder: "Enter your prompt here...\n\nUse {{variable_name}} for dynamic variables.",
55
- style: "width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-family: monospace; font-size: 0.875rem;",
53
+ class: "observ-form__textarea observ-form__textarea--code",
56
54
  data: {
57
55
  observ__prompt_variables_target: "input",
58
56
  action: "input->observ--prompt-variables#detectVariables"
59
57
  } %>
60
- <p style="margin-top: 0.25rem; font-size: 0.875rem; color: #6b7280;">
61
- Use <code style="background-color: #f3f4f6; padding: 0.125rem 0.25rem; border-radius: 0.25rem;">{{variable_name}}</code> syntax for variables
58
+ <p class="observ-form__hint">
59
+ Use <code class="observ-code">{{variable_name}}</code> syntax for variables
62
60
  </p>
63
61
 
64
62
  <!-- Variable Preview -->
65
- <div data-observ--prompt-variables-target="preview" style="margin-top: 0.75rem; padding: 0.75rem; background-color: #eff6ff; border-radius: 0.375rem; display: none;">
66
- <p style="font-size: 0.875rem; font-weight: 500; color: #1e40af; margin: 0 0 0.5rem;">Detected Variables:</p>
67
- <div data-observ--prompt-variables-target="list" style="display: flex; flex-wrap: wrap; gap: 0.5rem;"></div>
63
+ <div data-observ--prompt-variables-target="preview" class="observ-prompt-form__variable-preview">
64
+ <p class="observ-prompt-form__variable-title">Detected Variables:</p>
65
+ <div data-observ--prompt-variables-target="list" class="observ-prompt-form__variable-list"></div>
68
66
  </div>
69
67
  </div>
70
68
 
71
69
  <!-- Configuration (JSON) -->
72
- <div style="margin-bottom: 1.5rem;">
73
- <%= f.label :config, "Configuration (JSON)", style: "display: block; margin-bottom: 0.5rem; font-weight: 500;" %>
70
+ <div class="observ-form__group">
71
+ <%= f.label :config, "Configuration (JSON)", class: "observ-form__label" %>
74
72
  <%= f.text_area :config,
75
73
  value: prompt.config.present? ? JSON.pretty_generate(prompt.config) : "",
76
74
  rows: 8,
77
75
  placeholder: '{\n "model": "gpt-4o",\n "temperature": 0.7,\n "max_tokens": 2000\n}',
78
- style: "width: 100%; padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 0.375rem; font-family: monospace; font-size: 0.875rem;",
76
+ class: "observ-form__textarea observ-form__textarea--code",
79
77
  data: { controller: "json-editor" } %>
80
- <p style="margin-top: 0.25rem; font-size: 0.875rem; color: #6b7280;">
78
+ <p class="observ-form__hint">
81
79
  Optional JSON configuration for model parameters and metadata
82
80
  </p>
83
81
  </div>
84
82
 
85
83
  <!-- Actions -->
86
- <div style="display: flex; align-items: center; justify-content: space-between; padding-top: 1.5rem; border-top: 1px solid #e5e7eb;">
87
- <div>
84
+ <div class="observ-form__actions observ-form__actions--between">
85
+ <div class="observ-form__checkbox-group">
88
86
  <% unless prompt.persisted? %>
89
- <label style="display: flex; align-items: center;">
90
- <%= f.check_box :promote_to_production, style: "margin-right: 0.5rem;" %>
91
- <span style="font-size: 0.875rem; color: #374151;">
87
+ <label class="observ-form__checkbox-label">
88
+ <%= f.check_box :promote_to_production, class: "observ-form__checkbox" %>
89
+ <span class="observ-form__checkbox-custom"></span>
90
+ <span class="observ-form__checkbox-text">
92
91
  Promote to production immediately
93
92
  </span>
94
93
  </label>
95
- <p style="margin-left: 1.5rem; font-size: 0.75rem; color: #6b7280; margin-top: 0.25rem;">
94
+ <p class="observ-form__checkbox-hint">
96
95
  If unchecked, prompt will be saved as draft
97
96
  </p>
98
97
  <% end %>
99
98
  </div>
100
99
 
101
- <div style="display: flex; gap: 0.75rem;">
100
+ <div class="observ-form__actions-group">
102
101
  <%= link_to "Cancel",
103
102
  prompt.persisted? ? prompt_path(prompt.name) : prompts_path,
104
103
  class: "observ-button" %>