ruby_llm-agents 0.3.3 → 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 (97) 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 +137 -9
  7. data/app/controllers/ruby_llm/agents/executions_controller.rb +83 -5
  8. data/app/models/ruby_llm/agents/execution/analytics.rb +103 -12
  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 +28 -59
  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/ruby_llm/agents/application.html.erb +430 -0
  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/ruby_llm/agents/agents/show.html.erb +775 -0
  19. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +112 -0
  20. data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +75 -0
  21. data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_execution_item.html.erb +7 -4
  22. data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +84 -0
  23. data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +115 -0
  24. data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +49 -0
  25. data/app/views/ruby_llm/agents/dashboard/index.html.erb +155 -0
  26. data/app/views/{rubyllm → ruby_llm}/agents/executions/_execution.html.erb +1 -1
  27. data/app/views/{rubyllm → ruby_llm}/agents/executions/_filters.html.erb +39 -11
  28. data/app/views/{rubyllm → ruby_llm}/agents/executions/_list.html.erb +19 -9
  29. data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +101 -0
  30. data/app/views/ruby_llm/agents/executions/index.html.erb +88 -0
  31. data/app/views/{rubyllm → ruby_llm}/agents/executions/show.html.erb +260 -200
  32. data/app/views/{rubyllm → ruby_llm}/agents/settings/show.html.erb +1 -1
  33. data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +48 -0
  34. data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +251 -0
  35. data/app/views/{rubyllm → ruby_llm}/agents/shared/_filter_dropdown.html.erb +1 -1
  36. data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +27 -0
  37. data/app/views/{rubyllm → ruby_llm}/agents/shared/_select_dropdown.html.erb +1 -1
  38. data/app/views/{rubyllm → ruby_llm}/agents/shared/_status_badge.html.erb +1 -1
  39. data/app/views/{rubyllm → ruby_llm}/agents/shared/_status_dot.html.erb +1 -1
  40. data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +26 -0
  41. data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +61 -0
  42. data/config/routes.rb +2 -0
  43. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +97 -0
  44. data/lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt +3 -3
  45. data/lib/generators/ruby_llm_agents/templates/add_tenant_to_executions_migration.rb.tt +23 -0
  46. data/lib/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt +2 -2
  47. data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +38 -0
  48. data/lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt +45 -0
  49. data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +17 -5
  50. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +13 -0
  51. data/lib/ruby_llm/agents/alert_manager.rb +20 -16
  52. data/lib/ruby_llm/agents/base/caching.rb +40 -0
  53. data/lib/ruby_llm/agents/base/cost_calculation.rb +105 -0
  54. data/lib/ruby_llm/agents/base/dsl.rb +261 -0
  55. data/lib/ruby_llm/agents/base/execution.rb +258 -0
  56. data/lib/ruby_llm/agents/base/reliability_execution.rb +136 -0
  57. data/lib/ruby_llm/agents/base/response_building.rb +86 -0
  58. data/lib/ruby_llm/agents/base/tool_tracking.rb +57 -0
  59. data/lib/ruby_llm/agents/base.rb +37 -801
  60. data/lib/ruby_llm/agents/budget_tracker.rb +250 -139
  61. data/lib/ruby_llm/agents/cache_helper.rb +98 -0
  62. data/lib/ruby_llm/agents/circuit_breaker.rb +48 -30
  63. data/lib/ruby_llm/agents/configuration.rb +40 -1
  64. data/lib/ruby_llm/agents/engine.rb +65 -1
  65. data/lib/ruby_llm/agents/inflections.rb +14 -0
  66. data/lib/ruby_llm/agents/instrumentation.rb +66 -0
  67. data/lib/ruby_llm/agents/reliability.rb +8 -2
  68. data/lib/ruby_llm/agents/version.rb +1 -1
  69. data/lib/ruby_llm/agents/workflow/instrumentation.rb +254 -0
  70. data/lib/ruby_llm/agents/workflow/parallel.rb +282 -0
  71. data/lib/ruby_llm/agents/workflow/pipeline.rb +306 -0
  72. data/lib/ruby_llm/agents/workflow/result.rb +390 -0
  73. data/lib/ruby_llm/agents/workflow/router.rb +429 -0
  74. data/lib/ruby_llm/agents/workflow.rb +232 -0
  75. data/lib/ruby_llm/agents.rb +1 -0
  76. metadata +57 -75
  77. data/app/channels/ruby_llm/agents/executions_channel.rb +0 -46
  78. data/app/javascript/ruby_llm/agents/controllers/filter_controller.js +0 -56
  79. data/app/javascript/ruby_llm/agents/controllers/index.js +0 -12
  80. data/app/javascript/ruby_llm/agents/controllers/refresh_controller.js +0 -83
  81. data/app/views/layouts/rubyllm/agents/application.html.erb +0 -626
  82. data/app/views/rubyllm/agents/agents/index.html.erb +0 -20
  83. data/app/views/rubyllm/agents/agents/show.html.erb +0 -772
  84. data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +0 -165
  85. data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +0 -10
  86. data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +0 -71
  87. data/app/views/rubyllm/agents/dashboard/index.html.erb +0 -197
  88. data/app/views/rubyllm/agents/executions/index.html.erb +0 -28
  89. data/app/views/rubyllm/agents/executions/index.turbo_stream.erb +0 -18
  90. data/app/views/rubyllm/agents/shared/_executions_table.html.erb +0 -193
  91. /data/app/views/{rubyllm → ruby_llm}/agents/agents/_agent.html.erb +0 -0
  92. /data/app/views/{rubyllm → ruby_llm}/agents/agents/_version_comparison.html.erb +0 -0
  93. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_action_center.html.erb +0 -0
  94. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_alerts_feed.html.erb +0 -0
  95. /data/app/views/{rubyllm → ruby_llm}/agents/dashboard/_breaker_strip.html.erb +0 -0
  96. /data/app/views/{rubyllm → ruby_llm}/agents/executions/dry_run.html.erb +0 -0
  97. /data/app/views/{rubyllm → ruby_llm}/agents/shared/_stat_card.html.erb +0 -0
@@ -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>