ruby_llm-agents 1.3.3 → 2.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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +101 -334
  3. data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
  4. data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
  5. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
  6. data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
  7. data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
  8. data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
  9. data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
  10. data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
  11. data/app/models/ruby_llm/agents/execution.rb +46 -10
  12. data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
  13. data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
  14. data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
  15. data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
  16. data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
  17. data/app/models/ruby_llm/agents/tenant.rb +2 -3
  18. data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
  19. data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
  20. data/app/views/layouts/ruby_llm/agents/application.html.erb +87 -252
  21. data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
  22. data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
  23. data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
  24. data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
  25. data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
  26. data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
  27. data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
  28. data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
  29. data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
  30. data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
  31. data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
  32. data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
  33. data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
  34. data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
  35. data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
  36. data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
  37. data/app/views/ruby_llm/agents/executions/show.html.erb +528 -989
  38. data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
  39. data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
  40. data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
  41. data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
  42. data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
  43. data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
  44. data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
  45. data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
  46. data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
  47. data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
  48. data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
  49. data/config/routes.rb +0 -13
  50. data/lib/generators/ruby_llm_agents/install_generator.rb +9 -14
  51. data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
  52. data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
  53. data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
  54. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
  55. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
  56. data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
  57. data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
  58. data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
  59. data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
  60. data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +9 -12
  61. data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
  62. data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
  63. data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
  64. data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
  65. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
  66. data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
  67. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +58 -262
  68. data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
  69. data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
  70. data/lib/ruby_llm/agents/base_agent.rb +52 -6
  71. data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
  72. data/lib/ruby_llm/agents/core/base.rb +23 -55
  73. data/lib/ruby_llm/agents/core/configuration.rb +58 -117
  74. data/lib/ruby_llm/agents/core/errors.rb +0 -58
  75. data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
  76. data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
  77. data/lib/ruby_llm/agents/core/version.rb +1 -1
  78. data/lib/ruby_llm/agents/dsl/base.rb +157 -17
  79. data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
  80. data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
  81. data/lib/ruby_llm/agents/dsl.rb +1 -2
  82. data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
  83. data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
  84. data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
  85. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
  86. data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
  87. data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
  88. data/lib/ruby_llm/agents/image/editor.rb +0 -1
  89. data/lib/ruby_llm/agents/image/generator.rb +0 -21
  90. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
  91. data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
  92. data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
  93. data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
  94. data/lib/ruby_llm/agents/image/transformer.rb +0 -1
  95. data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
  96. data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
  97. data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
  98. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +1 -0
  99. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
  100. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
  101. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
  102. data/lib/ruby_llm/agents/infrastructure/reliability.rb +37 -2
  103. data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
  104. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
  105. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
  106. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
  107. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
  108. data/lib/ruby_llm/agents/rails/engine.rb +6 -6
  109. data/lib/ruby_llm/agents/results/base.rb +1 -49
  110. data/lib/ruby_llm/agents/text/embedder.rb +0 -1
  111. data/lib/ruby_llm/agents.rb +1 -9
  112. data/lib/tasks/ruby_llm_agents.rake +34 -0
  113. metadata +12 -81
  114. data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
  115. data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
  116. data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
  117. data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
  118. data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
  119. data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
  120. data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
  121. data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
  122. data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
  123. data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
  124. data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
  125. data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
  126. data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
  127. data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
  128. data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
  129. data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
  130. data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
  131. data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
  132. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
  133. data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
  134. data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
  135. data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
  136. data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
  137. data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
  138. data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
  139. data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
  140. data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
  141. data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
  142. data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
  143. data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
  144. data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
  145. data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
  146. data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
  147. data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
  148. data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
  149. data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
  150. data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
  151. data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
  152. data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
  153. data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
  154. data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
  155. data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
  156. data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
  157. data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
  158. data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
  159. data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
  160. data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
  161. data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
  162. data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
  163. data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
  164. data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
  165. data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
  166. data/lib/ruby_llm/agents/text/moderator.rb +0 -237
  167. data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
  168. data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
  169. data/lib/ruby_llm/agents/workflow/async.rb +0 -220
  170. data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
  171. data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
  172. data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
  173. data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
  174. data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
  175. data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
  176. data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
  177. data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
  178. data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
  179. data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
  180. data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
  181. data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
  182. data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
  183. data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
  184. data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
  185. data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
  186. data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
  187. data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
  188. data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
  189. data/lib/ruby_llm/agents/workflow/result.rb +0 -592
  190. data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
  191. data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
  192. data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
@@ -37,26 +37,26 @@
37
37
  credits: { enabled: false },
38
38
  chart: {
39
39
  backgroundColor: 'transparent',
40
- style: { fontFamily: 'inherit' }
40
+ style: { fontFamily: 'ui-monospace, monospace' }
41
41
  },
42
42
  title: { text: null },
43
43
  xAxis: {
44
- labels: { style: { color: '#9CA3AF' } },
45
- lineColor: 'rgba(156, 163, 175, 0.2)',
46
- tickColor: 'rgba(156, 163, 175, 0.2)'
44
+ labels: { style: { color: '#6B7280' } },
45
+ lineColor: 'rgba(107, 114, 128, 0.1)',
46
+ tickColor: 'rgba(107, 114, 128, 0.1)'
47
47
  },
48
48
  yAxis: {
49
- labels: { style: { color: '#9CA3AF' } },
50
- gridLineColor: 'rgba(156, 163, 175, 0.2)'
49
+ labels: { style: { color: '#6B7280' } },
50
+ gridLineColor: 'rgba(107, 114, 128, 0.1)'
51
51
  },
52
52
  legend: {
53
- itemStyle: { color: '#9CA3AF' },
54
- itemHoverStyle: { color: '#D1D5DB' }
53
+ itemStyle: { color: '#6B7280' },
54
+ itemHoverStyle: { color: '#9CA3AF' }
55
55
  },
56
56
  tooltip: {
57
- backgroundColor: 'rgba(17, 24, 39, 0.9)',
58
- borderColor: 'rgba(75, 85, 99, 0.5)',
59
- style: { color: '#F3F4F6' }
57
+ backgroundColor: 'rgba(0, 0, 0, 0.85)',
58
+ borderColor: 'transparent',
59
+ style: { color: '#E5E7EB', fontFamily: 'ui-monospace, monospace' }
60
60
  }
61
61
  });
62
62
  </script>
@@ -71,12 +71,9 @@
71
71
 
72
72
  <!-- Clickable rows -->
73
73
  <script type="module">
74
- // Initialize on page load
75
74
  document.addEventListener('DOMContentLoaded', function() {
76
- // Handle data-href clickable rows (semantic alternative to onclick)
77
75
  document.querySelectorAll('[data-href]').forEach(function(element) {
78
76
  element.addEventListener('click', function(e) {
79
- // Don't navigate if clicking on a link or button inside the row
80
77
  if (e.target.closest('a, button')) return;
81
78
  window.location = element.dataset.href;
82
79
  });
@@ -109,291 +106,129 @@
109
106
  }
110
107
  </script>
111
108
 
112
- <style>
113
- /* Custom styles for the dashboard */
114
-
115
- /* Standard card components */
116
- .card {
117
- @apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6;
118
- }
119
- .card-compact {
120
- @apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4 mb-4;
121
- }
122
-
123
- .stat-card {
124
- @apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-4 transition-shadow;
125
- }
126
- .stat-card:hover {
127
- @apply shadow-md;
128
- }
129
- .stat-value {
130
- @apply text-2xl font-bold text-gray-900 dark:text-gray-100 mt-2;
131
- }
132
- @media (min-width: 1024px) {
133
- .stat-value {
134
- font-size: 1.5rem;
135
- }
136
- }
137
- .stat-label {
138
- @apply text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide font-medium;
139
- }
140
- .chart-card {
141
- @apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-6;
142
- }
143
- .nav-link {
144
- @apply px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors;
145
- }
146
- .nav-link.active {
147
- @apply bg-blue-50 dark:bg-blue-900 text-blue-700 dark:text-blue-300;
148
- }
109
+ <style type="text/tailwindcss">
149
110
  .badge {
150
111
  @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
151
112
  }
152
113
  .badge-running {
153
- @apply bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200;
114
+ @apply bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-300;
154
115
  }
155
116
  .badge-success {
156
- @apply bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200;
117
+ @apply bg-green-100 dark:bg-green-500/20 text-green-700 dark:text-green-300;
157
118
  }
158
119
  .badge-error {
159
- @apply bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200;
120
+ @apply bg-red-100 dark:bg-red-500/20 text-red-700 dark:text-red-300;
160
121
  }
161
122
  .badge-timeout {
162
- @apply bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200;
123
+ @apply bg-yellow-100 dark:bg-yellow-500/20 text-yellow-700 dark:text-yellow-300;
163
124
  }
164
125
  .badge-cyan {
165
- @apply bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300;
126
+ @apply bg-cyan-100 dark:bg-cyan-500/20 text-cyan-700 dark:text-cyan-300;
166
127
  }
167
128
  .badge-purple {
168
- @apply bg-purple-100 dark:bg-purple-900/50 text-purple-700 dark:text-purple-300;
129
+ @apply bg-purple-100 dark:bg-purple-500/20 text-purple-700 dark:text-purple-300;
169
130
  }
170
131
  .badge-orange {
171
- @apply bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300;
132
+ @apply bg-orange-100 dark:bg-orange-500/20 text-orange-700 dark:text-orange-300;
172
133
  }
173
-
174
- /* Workflow type badges */
175
- .badge-pipeline {
176
- @apply bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300;
177
- }
178
- .badge-parallel {
179
- @apply bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300;
180
- }
181
- .badge-router {
182
- @apply bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300;
183
- }
184
-
185
- /* Alpine.js utilities */
186
- [x-cloak] {
187
- display: none !important;
134
+ .badge-sm {
135
+ @apply inline-flex items-center px-1.5 py-px rounded-full text-[10px] font-medium;
188
136
  }
189
-
190
- </style>
137
+ </style>
191
138
  </head>
192
139
 
193
- <body class="bg-gray-50 dark:bg-gray-900 min-h-screen flex flex-col">
140
+ <body class="bg-gray-50 dark:bg-gray-950 min-h-screen flex flex-col font-sans">
194
141
  <!-- Header -->
195
- <header
196
- class="
197
- bg-white dark:bg-gray-800 border-b border-gray-200
198
- dark:border-gray-700
199
- "
200
- x-data="{ mobileMenuOpen: false }"
201
- >
202
- <div class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center">
203
- <div class="flex justify-between items-center w-full">
204
- <div class="flex items-center space-x-8">
205
- <%= link_to ruby_llm_agents.root_path, class: "flex items-center space-x-2" do %>
206
- <span class="text-lg">🤖</span>
207
-
208
- <span class="font-semibold text-gray-900 dark:text-gray-100">
209
- RubyLLM Agents
210
- </span>
211
- <% end %>
142
+ <header class="border-b border-gray-200 dark:border-gray-800" x-data="{ mobileMenuOpen: false }">
143
+ <div class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 h-12 flex items-center">
144
+ <!-- Brand -->
145
+ <%= link_to ruby_llm_agents.root_path, class: "font-mono text-sm font-medium text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-400 transition-colors" do %>
146
+ ruby_llm<span class="text-gray-400 dark:text-gray-600">::</span>agents
147
+ <% end %>
148
+
149
+ <!-- Desktop Navigation -->
150
+ <nav class="hidden md:flex items-center ml-8 gap-0.5 font-mono text-xs">
151
+ <% [
152
+ [ruby_llm_agents.root_path, "dashboard"],
153
+ [ruby_llm_agents.agents_path, "agents"],
154
+ [ruby_llm_agents.executions_path, "executions"],
155
+ [ruby_llm_agents.tenants_path, "tenants"]
156
+ ].each do |path, label| %>
157
+ <% active = current_page?(path) %>
158
+ <%= link_to label, path, class: "px-2.5 py-1 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
159
+ <% end %>
160
+ </nav>
212
161
 
213
- <!-- Desktop Navigation -->
214
- <%
215
- nav_items = [
216
- { path: ruby_llm_agents.root_path, label: "Dashboard", icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />' },
217
- { path: ruby_llm_agents.agents_path, label: "Agents", icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/>' },
218
- { path: ruby_llm_agents.workflows_path, label: "Workflows", icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zm0 8a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zm12 0a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>' },
219
- { path: ruby_llm_agents.executions_path, label: "Executions", icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />' },
220
- { path: ruby_llm_agents.tenants_path, label: "Tenants", icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />' }
221
- ]
222
- %>
223
- <nav class="hidden md:flex items-center space-x-1">
224
- <% nav_items.each do |item| %>
225
- <%= render "ruby_llm/agents/shared/nav_link", path: item[:path], label: item[:label], icon: item[:icon], mobile: false %>
226
- <% end %>
227
- </nav>
228
- </div>
162
+ <!-- Right Side -->
163
+ <div class="ml-auto flex items-center gap-1">
164
+ <%= link_to ruby_llm_agents.system_config_path, class: "hidden md:flex p-1.5 text-gray-400 dark:text-gray-600 hover:text-gray-700 dark:hover:text-gray-300 rounded transition-colors", title: "Config" do %>
165
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
166
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
167
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
168
+ </svg>
169
+ <% end %>
229
170
 
230
- <div
231
- class="
232
- flex items-center space-x-2 text-xs text-gray-500
233
- dark:text-gray-400
234
- "
171
+ <!-- Mobile menu button -->
172
+ <button
173
+ type="button"
174
+ @click="mobileMenuOpen = !mobileMenuOpen"
175
+ :aria-expanded="mobileMenuOpen"
176
+ class="md:hidden p-1.5 text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 rounded transition-colors"
235
177
  >
236
- <%# Settings Dropdown %>
237
- <div x-data="{ settingsOpen: false }" class="relative hidden md:block">
238
- <button
239
- @click="settingsOpen = !settingsOpen"
240
- @click.outside="settingsOpen = false"
241
- type="button"
242
- class="p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 transition-colors"
243
- aria-label="Settings"
244
- >
245
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
246
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
247
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
248
- </svg>
249
- </button>
250
-
251
- <div
252
- x-show="settingsOpen"
253
- x-cloak
254
- x-transition:enter="transition ease-out duration-100"
255
- x-transition:enter-start="opacity-0 scale-95"
256
- x-transition:enter-end="opacity-100 scale-100"
257
- x-transition:leave="transition ease-in duration-75"
258
- x-transition:leave-start="opacity-100 scale-100"
259
- x-transition:leave-end="opacity-0 scale-95"
260
- class="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50 py-1"
261
- >
262
- <a href="<%= ruby_llm_agents.system_config_path %>"
263
- class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 <%= 'bg-gray-100 dark:bg-gray-700' if request.path == ruby_llm_agents.system_config_path %>">
264
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
265
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
266
- </svg>
267
- System Config
268
- </a>
269
- <a href="<%= ruby_llm_agents.api_configuration_path %>"
270
- class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 <%= 'bg-gray-100 dark:bg-gray-700' if request.path == ruby_llm_agents.api_configuration_path %>">
271
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
272
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
273
- </svg>
274
- API Keys
275
- </a>
276
- <div class="border-t border-gray-100 dark:border-gray-700 my-1"></div>
277
- <a href="#" class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700">
278
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
279
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
280
- </svg>
281
- Logout
282
- </a>
283
- </div>
284
- </div>
285
-
286
- <!-- Mobile menu button -->
287
- <button
288
- type="button"
289
- @click="mobileMenuOpen = !mobileMenuOpen"
290
- :aria-expanded="mobileMenuOpen"
291
- aria-controls="mobile-menu"
292
- class="
293
- md:hidden inline-flex items-center justify-center p-2
294
- rounded-md text-gray-500 dark:text-gray-400
295
- hover:text-gray-900 dark:hover:text-gray-100
296
- hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none
297
- focus:ring-2 focus:ring-inset focus:ring-blue-500
298
- "
299
- >
300
- <span class="sr-only">Open main menu</span>
301
-
302
- <svg
303
- class="w-5 h-5"
304
- fill="none"
305
- stroke="currentColor"
306
- viewBox="0 0 24 24"
307
- >
308
- <path
309
- stroke-linecap="round"
310
- stroke-linejoin="round"
311
- stroke-width="2"
312
- d="M4 6h16M4 12h16M4 18h16"
313
- />
314
- </svg>
315
- </button>
316
- </div>
178
+ <span class="sr-only">Open main menu</span>
179
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
180
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/>
181
+ </svg>
182
+ </button>
317
183
  </div>
318
184
  </div>
319
185
 
320
- <!-- Mobile Navigation Menu -->
186
+ <!-- Mobile Navigation -->
321
187
  <div
322
- id="mobile-menu"
323
188
  x-show="mobileMenuOpen"
324
189
  x-cloak
325
- x-transition:enter="transition ease-out duration-200"
190
+ x-transition:enter="transition ease-out duration-150"
326
191
  x-transition:enter-start="opacity-0 -translate-y-1"
327
192
  x-transition:enter-end="opacity-100 translate-y-0"
328
- x-transition:leave="transition ease-in duration-150"
193
+ x-transition:leave="transition ease-in duration-100"
329
194
  x-transition:leave-start="opacity-100 translate-y-0"
330
195
  x-transition:leave-end="opacity-0 -translate-y-1"
331
196
  @click.outside="mobileMenuOpen = false"
332
- class="
333
- md:hidden border-t border-gray-200 dark:border-gray-700 bg-white
334
- dark:bg-gray-800
335
- "
197
+ class="md:hidden border-t border-gray-200 dark:border-gray-800"
336
198
  >
337
- <nav class="max-w-7xl mx-auto px-4 py-3 space-y-1">
338
- <% nav_items.each do |item| %>
339
- <%= render "ruby_llm/agents/shared/nav_link", path: item[:path], label: item[:label], icon: item[:icon], mobile: true %>
199
+ <nav class="max-w-7xl mx-auto px-4 py-2 font-mono text-xs space-y-0.5">
200
+ <% [
201
+ [ruby_llm_agents.root_path, "dashboard"],
202
+ [ruby_llm_agents.agents_path, "agents"],
203
+ [ruby_llm_agents.executions_path, "executions"],
204
+ [ruby_llm_agents.tenants_path, "tenants"]
205
+ ].each do |path, label| %>
206
+ <% active = current_page?(path) %>
207
+ <%= link_to label, path, class: "block px-3 py-2 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
340
208
  <% end %>
341
-
342
- <%# Settings section divider %>
343
- <div class="border-t border-gray-200 dark:border-gray-700 my-2 pt-2">
344
- <span class="px-3 text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider">Settings</span>
345
- </div>
346
-
347
- <%= render "ruby_llm/agents/shared/nav_link",
348
- path: ruby_llm_agents.system_config_path,
349
- label: "System Config",
350
- icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>',
351
- mobile: true %>
352
-
353
- <%= render "ruby_llm/agents/shared/nav_link",
354
- path: ruby_llm_agents.api_configuration_path,
355
- label: "API Keys",
356
- icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>',
357
- mobile: true %>
358
-
359
- <a href="#" class="flex items-center px-3 py-2 text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
360
- <svg class="w-5 h-5 mr-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
361
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
362
- </svg>
363
- Logout
364
- </a>
209
+ <div class="border-t border-gray-200 dark:border-gray-800 my-1.5"></div>
210
+ <%= link_to "config", ruby_llm_agents.system_config_path, class: "block px-3 py-2 rounded text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition-colors" %>
365
211
  </nav>
366
212
  </div>
367
213
  </header>
368
214
 
369
215
  <!-- Main content -->
370
- <main class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-8 h-full">
216
+ <main class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 flex-1">
371
217
  <%= yield %>
372
218
  </main>
373
219
 
374
220
  <!-- Footer -->
375
- <footer
376
- class="border-t bg-white dark:bg-gray-800 dark:border-gray-700 mt-auto"
377
- x-data="themeManager()"
378
- >
379
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
380
- <div class="flex flex-col sm:flex-row items-center sm:justify-between gap-3 sm:gap-0">
381
- <div class="flex items-center space-x-2">
382
- <label for="theme-select" class="text-sm text-gray-500 dark:text-gray-400">Theme:</label>
383
- <select
384
- id="theme-select"
385
- x-model="preference"
386
- @change="setTheme($event.target.value)"
387
- class="text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 py-1 px-2"
388
- >
389
- <option value="light">Light</option>
390
- <option value="dark">Dark</option>
391
- <option value="auto">Auto</option>
392
- </select>
221
+ <footer class="border-t border-gray-200 dark:border-gray-800 mt-auto" x-data="themeManager()">
222
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3">
223
+ <div class="flex items-center justify-between font-mono text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-widest">
224
+ <div class="flex items-center gap-1">
225
+ <button @click="setTheme('light')" :class="preference === 'light' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">light</button>
226
+ <span class="text-gray-300 dark:text-gray-700">&middot;</span>
227
+ <button @click="setTheme('dark')" :class="preference === 'dark' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">dark</button>
228
+ <span class="text-gray-300 dark:text-gray-700">&middot;</span>
229
+ <button @click="setTheme('auto')" :class="preference === 'auto' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">auto</button>
393
230
  </div>
394
- <p class="text-sm text-gray-500 dark:text-gray-400 text-center">
395
- Powered by <a href="https://github.com/adham90/ruby_llm-agents" class="text-blue-600 dark:text-blue-400 hover:underline">ruby_llm-agents</a>
396
- </p>
231
+ <a href="https://github.com/adham90/ruby_llm-agents" class="hover:text-gray-600 dark:hover:text-gray-400 transition-colors normal-case tracking-normal">ruby_llm-agents</a>
397
232
  </div>
398
233
  </div>
399
234
  </footer>