ruby_llm-agents 0.3.4 → 0.3.5

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +132 -1263
  3. data/app/controllers/concerns/ruby_llm/agents/filterable.rb +5 -1
  4. data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +2 -1
  5. data/app/controllers/ruby_llm/agents/agents_controller.rb +21 -2
  6. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +80 -16
  7. data/app/controllers/ruby_llm/agents/executions_controller.rb +83 -5
  8. data/app/models/ruby_llm/agents/execution/analytics.rb +17 -27
  9. data/app/models/ruby_llm/agents/execution/scopes.rb +25 -0
  10. data/app/models/ruby_llm/agents/execution/workflow.rb +299 -0
  11. data/app/models/ruby_llm/agents/execution.rb +9 -1
  12. data/app/models/ruby_llm/agents/tenant_budget.rb +165 -0
  13. data/app/services/ruby_llm/agents/agent_registry.rb +118 -7
  14. data/app/views/layouts/{rubyllm → ruby_llm}/agents/application.html.erb +91 -29
  15. data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +23 -0
  16. data/app/views/ruby_llm/agents/agents/_workflow.html.erb +125 -0
  17. data/app/views/ruby_llm/agents/agents/index.html.erb +93 -0
  18. data/app/views/{rubyllm → ruby_llm}/agents/agents/show.html.erb +77 -20
  19. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +112 -0
  20. data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_execution_item.html.erb +7 -4
  21. data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +115 -0
  22. data/app/views/{rubyllm → ruby_llm}/agents/dashboard/index.html.erb +9 -6
  23. data/app/views/{rubyllm → ruby_llm}/agents/executions/_execution.html.erb +1 -1
  24. data/app/views/{rubyllm → ruby_llm}/agents/executions/_filters.html.erb +39 -11
  25. data/app/views/{rubyllm → ruby_llm}/agents/executions/_list.html.erb +19 -9
  26. data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +101 -0
  27. data/app/views/ruby_llm/agents/executions/index.html.erb +88 -0
  28. data/app/views/{rubyllm → ruby_llm}/agents/executions/show.html.erb +137 -126
  29. data/app/views/{rubyllm → ruby_llm}/agents/shared/_breadcrumbs.html.erb +2 -2
  30. data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +251 -0
  31. data/app/views/{rubyllm → ruby_llm}/agents/shared/_filter_dropdown.html.erb +1 -1
  32. data/app/views/{rubyllm → ruby_llm}/agents/shared/_select_dropdown.html.erb +1 -1
  33. data/app/views/{rubyllm → ruby_llm}/agents/shared/_status_badge.html.erb +1 -1
  34. data/app/views/{rubyllm → ruby_llm}/agents/shared/_status_dot.html.erb +1 -1
  35. data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +26 -0
  36. data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +61 -0
  37. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +97 -0
  38. data/lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt +3 -3
  39. data/lib/generators/ruby_llm_agents/templates/add_tenant_to_executions_migration.rb.tt +23 -0
  40. data/lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt +2 -2
  41. data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +38 -0
  42. data/lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt +45 -0
  43. data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +17 -5
  44. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +13 -0
  45. data/lib/ruby_llm/agents/alert_manager.rb +20 -16
  46. data/lib/ruby_llm/agents/base/caching.rb +4 -7
  47. data/lib/ruby_llm/agents/base/cost_calculation.rb +5 -3
  48. data/lib/ruby_llm/agents/base/execution.rb +61 -9
  49. data/lib/ruby_llm/agents/base/reliability_execution.rb +14 -9
  50. data/lib/ruby_llm/agents/base.rb +26 -0
  51. data/lib/ruby_llm/agents/budget_tracker.rb +250 -139
  52. data/lib/ruby_llm/agents/cache_helper.rb +98 -0
  53. data/lib/ruby_llm/agents/circuit_breaker.rb +48 -30
  54. data/lib/ruby_llm/agents/configuration.rb +40 -1
  55. data/lib/ruby_llm/agents/engine.rb +65 -1
  56. data/lib/ruby_llm/agents/inflections.rb +14 -0
  57. data/lib/ruby_llm/agents/instrumentation.rb +66 -0
  58. data/lib/ruby_llm/agents/reliability.rb +8 -2
  59. data/lib/ruby_llm/agents/version.rb +1 -1
  60. data/lib/ruby_llm/agents/workflow/instrumentation.rb +254 -0
  61. data/lib/ruby_llm/agents/workflow/parallel.rb +282 -0
  62. data/lib/ruby_llm/agents/workflow/pipeline.rb +306 -0
  63. data/lib/ruby_llm/agents/workflow/result.rb +390 -0
  64. data/lib/ruby_llm/agents/workflow/router.rb +429 -0
  65. data/lib/ruby_llm/agents/workflow.rb +232 -0
  66. data/lib/ruby_llm/agents.rb +1 -0
  67. metadata +50 -60
  68. data/app/views/rubyllm/agents/agents/index.html.erb +0 -20
  69. data/app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb +0 -46
  70. data/app/views/rubyllm/agents/executions/index.html.erb +0 -28
  71. data/app/views/rubyllm/agents/executions/index.turbo_stream.erb +0 -18
  72. data/app/views/rubyllm/agents/shared/_executions_table.html.erb +0 -193
  73. /data/app/views/{rubyllm → ruby_llm}/agents/agents/_agent.html.erb +0 -0
  74. /data/app/views/{rubyllm → ruby_llm}/agents/agents/_version_comparison.html.erb +0 -0
  75. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_action_center.html.erb +0 -0
  76. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_alerts_feed.html.erb +0 -0
  77. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_breaker_strip.html.erb +0 -0
  78. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_budgets_bar.html.erb +0 -0
  79. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_now_strip.html.erb +0 -0
  80. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_top_errors.html.erb +0 -0
  81. /data/app/views/{rubyllm → ruby_llm}/agents/executions/dry_run.html.erb +0 -0
  82. /data/app/views/{rubyllm → ruby_llm}/agents/settings/show.html.erb +0 -0
  83. /data/app/views/{rubyllm → ruby_llm}/agents/shared/_nav_link.html.erb +0 -0
  84. /data/app/views/{rubyllm → ruby_llm}/agents/shared/_stat_card.html.erb +0 -0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - adham90
@@ -37,34 +37,6 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.0'
40
- - !ruby/object:Gem::Dependency
41
- name: turbo-rails
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '1.0'
47
- type: :runtime
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '1.0'
54
- - !ruby/object:Gem::Dependency
55
- name: stimulus-rails
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '1.0'
61
- type: :runtime
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: '1.0'
68
40
  - !ruby/object:Gem::Dependency
69
41
  name: csv
70
42
  requirement: !ruby/object:Gem::Requirement
@@ -101,50 +73,61 @@ files:
101
73
  - app/models/ruby_llm/agents/execution/analytics.rb
102
74
  - app/models/ruby_llm/agents/execution/metrics.rb
103
75
  - app/models/ruby_llm/agents/execution/scopes.rb
76
+ - app/models/ruby_llm/agents/execution/workflow.rb
77
+ - app/models/ruby_llm/agents/tenant_budget.rb
104
78
  - app/services/ruby_llm/agents/agent_registry.rb
105
- - app/views/layouts/rubyllm/agents/application.html.erb
106
- - app/views/rubyllm/agents/agents/_agent.html.erb
107
- - app/views/rubyllm/agents/agents/_version_comparison.html.erb
108
- - app/views/rubyllm/agents/agents/index.html.erb
109
- - app/views/rubyllm/agents/agents/show.html.erb
110
- - app/views/rubyllm/agents/dashboard/_action_center.html.erb
111
- - app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb
112
- - app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb
113
- - app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb
114
- - app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb
115
- - app/views/rubyllm/agents/dashboard/_execution_item.html.erb
116
- - app/views/rubyllm/agents/dashboard/_now_strip.html.erb
117
- - app/views/rubyllm/agents/dashboard/_top_errors.html.erb
118
- - app/views/rubyllm/agents/dashboard/index.html.erb
119
- - app/views/rubyllm/agents/executions/_execution.html.erb
120
- - app/views/rubyllm/agents/executions/_filters.html.erb
121
- - app/views/rubyllm/agents/executions/_list.html.erb
122
- - app/views/rubyllm/agents/executions/dry_run.html.erb
123
- - app/views/rubyllm/agents/executions/index.html.erb
124
- - app/views/rubyllm/agents/executions/index.turbo_stream.erb
125
- - app/views/rubyllm/agents/executions/show.html.erb
126
- - app/views/rubyllm/agents/settings/show.html.erb
127
- - app/views/rubyllm/agents/shared/_breadcrumbs.html.erb
128
- - app/views/rubyllm/agents/shared/_executions_table.html.erb
129
- - app/views/rubyllm/agents/shared/_filter_dropdown.html.erb
130
- - app/views/rubyllm/agents/shared/_nav_link.html.erb
131
- - app/views/rubyllm/agents/shared/_select_dropdown.html.erb
132
- - app/views/rubyllm/agents/shared/_stat_card.html.erb
133
- - app/views/rubyllm/agents/shared/_status_badge.html.erb
134
- - app/views/rubyllm/agents/shared/_status_dot.html.erb
79
+ - app/views/layouts/ruby_llm/agents/application.html.erb
80
+ - app/views/ruby_llm/agents/agents/_agent.html.erb
81
+ - app/views/ruby_llm/agents/agents/_empty_state.html.erb
82
+ - app/views/ruby_llm/agents/agents/_version_comparison.html.erb
83
+ - app/views/ruby_llm/agents/agents/_workflow.html.erb
84
+ - app/views/ruby_llm/agents/agents/index.html.erb
85
+ - app/views/ruby_llm/agents/agents/show.html.erb
86
+ - app/views/ruby_llm/agents/dashboard/_action_center.html.erb
87
+ - app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb
88
+ - app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb
89
+ - app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb
90
+ - app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb
91
+ - app/views/ruby_llm/agents/dashboard/_execution_item.html.erb
92
+ - app/views/ruby_llm/agents/dashboard/_now_strip.html.erb
93
+ - app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb
94
+ - app/views/ruby_llm/agents/dashboard/_top_errors.html.erb
95
+ - app/views/ruby_llm/agents/dashboard/index.html.erb
96
+ - app/views/ruby_llm/agents/executions/_execution.html.erb
97
+ - app/views/ruby_llm/agents/executions/_filters.html.erb
98
+ - app/views/ruby_llm/agents/executions/_list.html.erb
99
+ - app/views/ruby_llm/agents/executions/_workflow_summary.html.erb
100
+ - app/views/ruby_llm/agents/executions/dry_run.html.erb
101
+ - app/views/ruby_llm/agents/executions/index.html.erb
102
+ - app/views/ruby_llm/agents/executions/show.html.erb
103
+ - app/views/ruby_llm/agents/settings/show.html.erb
104
+ - app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb
105
+ - app/views/ruby_llm/agents/shared/_executions_table.html.erb
106
+ - app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb
107
+ - app/views/ruby_llm/agents/shared/_nav_link.html.erb
108
+ - app/views/ruby_llm/agents/shared/_select_dropdown.html.erb
109
+ - app/views/ruby_llm/agents/shared/_stat_card.html.erb
110
+ - app/views/ruby_llm/agents/shared/_status_badge.html.erb
111
+ - app/views/ruby_llm/agents/shared/_status_dot.html.erb
112
+ - app/views/ruby_llm/agents/shared/_tenant_filter.html.erb
113
+ - app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb
135
114
  - config/routes.rb
136
115
  - lib/generators/ruby_llm_agents/agent_generator.rb
137
116
  - lib/generators/ruby_llm_agents/install_generator.rb
117
+ - lib/generators/ruby_llm_agents/multi_tenancy_generator.rb
138
118
  - lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt
139
119
  - lib/generators/ruby_llm_agents/templates/add_caching_migration.rb.tt
140
120
  - lib/generators/ruby_llm_agents/templates/add_finish_reason_migration.rb.tt
141
121
  - lib/generators/ruby_llm_agents/templates/add_prompts_migration.rb.tt
142
122
  - lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt
143
123
  - lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt
124
+ - lib/generators/ruby_llm_agents/templates/add_tenant_to_executions_migration.rb.tt
144
125
  - lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt
145
126
  - lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt
127
+ - lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt
146
128
  - lib/generators/ruby_llm_agents/templates/agent.rb.tt
147
129
  - lib/generators/ruby_llm_agents/templates/application_agent.rb.tt
130
+ - lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt
148
131
  - lib/generators/ruby_llm_agents/templates/initializer.rb.tt
149
132
  - lib/generators/ruby_llm_agents/templates/migration.rb.tt
150
133
  - lib/generators/ruby_llm_agents/upgrade_generator.rb
@@ -161,6 +144,7 @@ files:
161
144
  - lib/ruby_llm/agents/base/response_building.rb
162
145
  - lib/ruby_llm/agents/base/tool_tracking.rb
163
146
  - lib/ruby_llm/agents/budget_tracker.rb
147
+ - lib/ruby_llm/agents/cache_helper.rb
164
148
  - lib/ruby_llm/agents/circuit_breaker.rb
165
149
  - lib/ruby_llm/agents/configuration.rb
166
150
  - lib/ruby_llm/agents/engine.rb
@@ -171,6 +155,12 @@ files:
171
155
  - lib/ruby_llm/agents/reliability.rb
172
156
  - lib/ruby_llm/agents/result.rb
173
157
  - lib/ruby_llm/agents/version.rb
158
+ - lib/ruby_llm/agents/workflow.rb
159
+ - lib/ruby_llm/agents/workflow/instrumentation.rb
160
+ - lib/ruby_llm/agents/workflow/parallel.rb
161
+ - lib/ruby_llm/agents/workflow/pipeline.rb
162
+ - lib/ruby_llm/agents/workflow/result.rb
163
+ - lib/ruby_llm/agents/workflow/router.rb
174
164
  homepage: https://github.com/adham90/ruby_llm-agents
175
165
  licenses:
176
166
  - MIT
@@ -192,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
182
  - !ruby/object:Gem::Version
193
183
  version: '0'
194
184
  requirements: []
195
- rubygems_version: 3.6.9
185
+ rubygems_version: 4.0.3
196
186
  specification_version: 4
197
187
  summary: Agent framework for building LLM-powered agents with RubyLLM
198
188
  test_files: []
@@ -1,20 +0,0 @@
1
- <div class="mb-6">
2
- <h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Agents</h1>
3
- <p class="text-gray-500 dark:text-gray-400 mt-1">All available agents and their execution statistics</p>
4
- </div>
5
-
6
- <% if @agents.empty? %>
7
- <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-8 text-center">
8
- <svg class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
10
- </svg>
11
- <h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-gray-100">No agents found</h3>
12
- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Create an agent by running <code class="bg-gray-100 dark:bg-gray-700 dark:text-gray-200 px-1 rounded">rails g ruby_llm_agents:agent YourAgentName</code></p>
13
- </div>
14
- <% else %>
15
- <div class="space-y-3 sm:space-y-4">
16
- <% @agents.each do |agent| %>
17
- <%= render partial: "rubyllm/agents/agents/agent", locals: { agent: agent } %>
18
- <% end %>
19
- </div>
20
- <% end %>
@@ -1,46 +0,0 @@
1
- <div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 h-full">
2
- <div class="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
3
- <h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Agents</h3>
4
- </div>
5
-
6
- <% if agent_stats.any? %>
7
- <div class="overflow-x-auto">
8
- <table class="w-full text-sm">
9
- <thead>
10
- <tr class="text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
11
- <th class="px-4 py-2">Agent</th>
12
- <th class="px-4 py-2 text-right">Runs</th>
13
- <th class="px-4 py-2 text-right">Cost</th>
14
- <th class="px-4 py-2 text-right">Success</th>
15
- </tr>
16
- </thead>
17
- <tbody class="divide-y divide-gray-100 dark:divide-gray-700">
18
- <% agent_stats.first(5).each do |agent| %>
19
- <tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
20
- <td class="px-4 py-2">
21
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate block max-w-[120px]" title="<%= agent[:agent_type] %>">
22
- <%= agent[:agent_type].to_s.demodulize %>
23
- </span>
24
- </td>
25
- <td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
26
- <%= number_with_delimiter(agent[:executions]) %>
27
- </td>
28
- <td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
29
- $<%= number_with_precision(agent[:total_cost], precision: 2) %>
30
- </td>
31
- <td class="px-4 py-2 text-right">
32
- <span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium <%= agent[:success_rate] >= 95 ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' : agent[:success_rate] >= 80 ? 'bg-yellow-100 dark:bg-yellow-900 text-yellow-700 dark:text-yellow-300' : 'bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300' %>">
33
- <%= agent[:success_rate].round %>%
34
- </span>
35
- </td>
36
- </tr>
37
- <% end %>
38
- </tbody>
39
- </table>
40
- </div>
41
- <% else %>
42
- <div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
43
- <p class="text-sm">No agent data yet</p>
44
- </div>
45
- <% end %>
46
- </div>
@@ -1,28 +0,0 @@
1
- <h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-6">Executions</h2>
2
-
3
- <%= turbo_frame_tag "executions_content" do %>
4
- <%= render partial: "rubyllm/agents/executions/filters", locals: { agent_types: @agent_types, model_ids: @model_ids, filter_stats: @filter_stats } %>
5
- <%= render partial: "rubyllm/agents/executions/list", locals: { executions: @executions, pagination: @pagination, filter_stats: @filter_stats } %>
6
- <% end %>
7
-
8
- <script>
9
- // Toggle attempts expansion (used by executions list)
10
- function toggleAttempts(executionId) {
11
- const attemptsRow = document.getElementById('attempts-row-' + executionId);
12
- const expandBtn = document.querySelector(`button[data-execution-id="${executionId}"]`);
13
-
14
- if (attemptsRow) {
15
- const isHidden = attemptsRow.classList.contains('hidden');
16
- attemptsRow.classList.toggle('hidden');
17
-
18
- // Rotate the chevron
19
- if (expandBtn) {
20
- const svg = expandBtn.querySelector('svg');
21
- if (svg) {
22
- svg.classList.toggle('rotate-90', isHidden);
23
- }
24
- expandBtn.setAttribute('aria-expanded', isHidden ? 'true' : 'false');
25
- }
26
- }
27
- }
28
- </script>
@@ -1,18 +0,0 @@
1
- <%# Only update the stats (not the whole filter container to preserve Alpine.js state) %>
2
- <%= turbo_stream.replace "filter-stats" do %>
3
- <div id="filter-stats" class="flex items-center justify-between md:justify-end gap-3 py-2 px-3 md:p-0 bg-gray-50 md:bg-transparent dark:bg-gray-700/50 md:dark:bg-transparent rounded-lg md:rounded-none">
4
- <span class="text-xs text-gray-500 dark:text-gray-400 md:hidden">Results</span>
5
- <div class="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
6
- <span class="tabular-nums"><%= number_to_human_short(@filter_stats[:total_count]) %><span class="md:hidden"> executions</span></span>
7
- <span class="text-gray-300 dark:text-gray-600">|</span>
8
- <span class="tabular-nums"><%= number_to_human_short(@filter_stats[:total_cost], prefix: "$", precision: 2) %></span>
9
- <span class="hidden md:inline text-gray-300 dark:text-gray-600">|</span>
10
- <span class="hidden md:inline tabular-nums"><%= number_to_human_short(@filter_stats[:total_tokens]) %>t</span>
11
- </div>
12
- </div>
13
- <% end %>
14
-
15
- <%# Update the executions list %>
16
- <%= turbo_stream.replace "executions-list" do %>
17
- <%= render partial: "rubyllm/agents/executions/list", locals: { executions: @executions, pagination: @pagination, filter_stats: @filter_stats } %>
18
- <% end %>
@@ -1,193 +0,0 @@
1
- <% if executions.empty? %>
2
- <p class="text-gray-500 dark:text-gray-400 text-center py-8">No executions found.</p>
3
- <% else %>
4
- <div class="overflow-x-auto">
5
- <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
6
- <thead>
7
- <tr>
8
- <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
9
- <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Version</th>
10
- <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Model</th>
11
- <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Temp</th>
12
- <th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Tokens</th>
13
- <th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cost</th>
14
- <th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Duration</th>
15
- <th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Time</th>
16
- </tr>
17
- </thead>
18
- <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-100 dark:divide-gray-700">
19
- <% executions.each do |execution| %>
20
- <!-- Main Row (clickable to expand) -->
21
- <tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors cursor-pointer group"
22
- onclick="toggleExecutionDetails('<%= execution.id %>')"
23
- id="execution-row-<%= execution.id %>">
24
- <td class="px-4 py-3 whitespace-nowrap">
25
- <div class="flex items-center">
26
- <svg class="w-4 h-4 mr-2 text-gray-400 dark:text-gray-500 transform transition-transform duration-200"
27
- id="chevron-<%= execution.id %>" fill="none" stroke="currentColor" viewBox="0 0 24 24">
28
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
29
- </svg>
30
- <%= render "rubyllm/agents/shared/status_badge", status: execution.status %>
31
- </div>
32
- </td>
33
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
34
- v<%= execution.agent_version %>
35
- </td>
36
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
37
- <%= execution.model_id %>
38
- </td>
39
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
40
- <%= execution.temperature %>
41
- </td>
42
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 text-right font-medium">
43
- <%= number_to_human_short(execution.total_tokens || 0) %>
44
- </td>
45
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 text-right font-medium">
46
- <%= number_to_human_short(execution.total_cost || 0, prefix: "$", precision: 2) %>
47
- </td>
48
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100 text-right font-medium">
49
- <%= number_with_delimiter(execution.duration_ms || 0) %>ms
50
- </td>
51
- <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 text-right">
52
- <%= time_ago_in_words(execution.created_at) %> ago
53
- </td>
54
- </tr>
55
-
56
- <!-- Error Row (if applicable) -->
57
- <% if execution.status_error? && execution.error_message.present? %>
58
- <tr class="bg-red-50 dark:bg-red-900/30">
59
- <td colspan="8" class="px-4 py-2">
60
- <p class="text-xs text-red-600 dark:text-red-400">
61
- <span class="font-medium"><%= execution.error_class %>:</span>
62
- <%= truncate(execution.error_message, length: 150) %>
63
- </p>
64
- </td>
65
- </tr>
66
- <% end %>
67
-
68
- <!-- Expandable Details Row (hidden by default) -->
69
- <tr id="execution-details-<%= execution.id %>" class="hidden bg-gray-50 dark:bg-gray-900">
70
- <td colspan="8" class="px-4 py-4">
71
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
72
- <% if execution.respond_to?(:system_prompt) && execution.system_prompt.present? %>
73
- <div>
74
- <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">System Prompt</h4>
75
- <pre class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs text-gray-900 dark:text-gray-100 overflow-x-auto max-h-48 overflow-y-auto font-mono whitespace-pre-wrap"><%= execution.system_prompt %></pre>
76
- </div>
77
- <% end %>
78
-
79
- <% if execution.respond_to?(:user_prompt) && execution.user_prompt.present? %>
80
- <div>
81
- <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">User Prompt</h4>
82
- <pre class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs text-gray-900 dark:text-gray-100 overflow-x-auto max-h-48 overflow-y-auto font-mono whitespace-pre-wrap"><%= execution.user_prompt %></pre>
83
- </div>
84
- <% end %>
85
-
86
- <% if execution.parameters.present? && execution.parameters.any? %>
87
- <div>
88
- <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">Parameters</h4>
89
- <pre class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs text-gray-900 dark:text-gray-100 overflow-x-auto max-h-48 overflow-y-auto font-mono"><%= JSON.pretty_generate(execution.parameters) %></pre>
90
- </div>
91
- <% end %>
92
-
93
- <% if execution.response.present? && execution.response.any? %>
94
- <div>
95
- <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">Response</h4>
96
- <pre class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs text-gray-900 dark:text-gray-100 overflow-x-auto max-h-48 overflow-y-auto font-mono"><%= JSON.pretty_generate(execution.response) %></pre>
97
- </div>
98
- <% end %>
99
-
100
- <% if execution.metadata.present? && execution.metadata.any? %>
101
- <div>
102
- <h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">Metadata</h4>
103
- <pre class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs text-gray-900 dark:text-gray-100 overflow-x-auto max-h-48 overflow-y-auto font-mono"><%= JSON.pretty_generate(execution.metadata) %></pre>
104
- </div>
105
- <% end %>
106
- </div>
107
-
108
- <!-- View Full Details Link -->
109
- <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
110
- <%= link_to ruby_llm_agents.execution_path(execution), class: "text-sm text-blue-600 dark:text-blue-400 hover:underline", onclick: "event.stopPropagation();" do %>
111
- View Full Details &rarr;
112
- <% end %>
113
- </div>
114
- </td>
115
- </tr>
116
- <% end %>
117
- </tbody>
118
- </table>
119
- </div>
120
-
121
- <% if pagination[:total_pages] > 1 %>
122
- <%
123
- current_page = pagination[:current_page]
124
- total_pages = pagination[:total_pages]
125
- total_count = pagination[:total_count]
126
- per_page = pagination[:per_page]
127
-
128
- from_record = ((current_page - 1) * per_page) + 1
129
- to_record = [current_page * per_page, total_count].min
130
- %>
131
- <div class="mt-4 flex items-center justify-between border-t border-gray-100 dark:border-gray-700 pt-4">
132
- <p class="text-sm text-gray-500 dark:text-gray-400">
133
- Showing <%= from_record %>-<%= to_record %> of <%= number_with_delimiter(total_count) %> executions
134
- </p>
135
- <nav class="flex items-center space-x-1">
136
- <% if current_page > 1 %>
137
- <%= link_to "Previous", url_for(request.query_parameters.merge(page: current_page - 1)), data: { turbo_frame: "executions_table" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
138
- <% else %>
139
- <span class="px-3 py-1.5 text-sm font-medium text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md cursor-not-allowed">Previous</span>
140
- <% end %>
141
-
142
- <%
143
- # Calculate which page numbers to show
144
- window = 2
145
- left_edge = 1
146
- right_edge = 1
147
-
148
- pages_to_show = []
149
- (1..total_pages).each do |page|
150
- if page <= left_edge ||
151
- page > total_pages - right_edge ||
152
- (page >= current_page - window && page <= current_page + window)
153
- pages_to_show << page
154
- elsif pages_to_show.last != :gap
155
- pages_to_show << :gap
156
- end
157
- end
158
- %>
159
-
160
- <% pages_to_show.each do |page| %>
161
- <% if page == :gap %>
162
- <span class="px-2 py-1.5 text-sm text-gray-500 dark:text-gray-400">...</span>
163
- <% elsif page == current_page %>
164
- <span class="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 border border-blue-600 rounded-md"><%= page %></span>
165
- <% else %>
166
- <%= link_to page, url_for(request.query_parameters.merge(page: page)), data: { turbo_frame: "executions_table" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
167
- <% end %>
168
- <% end %>
169
-
170
- <% if current_page < total_pages %>
171
- <%= link_to "Next", url_for(request.query_parameters.merge(page: current_page + 1)), data: { turbo_frame: "executions_table" }, class: "px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700" %>
172
- <% else %>
173
- <span class="px-3 py-1.5 text-sm font-medium text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md cursor-not-allowed">Next</span>
174
- <% end %>
175
- </nav>
176
- </div>
177
- <% end %>
178
- <% end %>
179
-
180
- <script>
181
- function toggleExecutionDetails(id) {
182
- const detailsRow = document.getElementById(`execution-details-${id}`);
183
- const chevron = document.getElementById(`chevron-${id}`);
184
-
185
- if (detailsRow.classList.contains('hidden')) {
186
- detailsRow.classList.remove('hidden');
187
- chevron.classList.add('rotate-90');
188
- } else {
189
- detailsRow.classList.add('hidden');
190
- chevron.classList.remove('rotate-90');
191
- }
192
- }
193
- </script>