ruby_llm-monitoring 0.1.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +179 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/stylesheets/ruby_llm/monitoring/application.css +15 -0
  5. data/app/assets/stylesheets/ruby_llm/monitoring/bulma.min.css +3 -0
  6. data/app/controllers/ruby_llm/monitoring/alerts_controller.rb +7 -0
  7. data/app/controllers/ruby_llm/monitoring/application_controller.rb +12 -0
  8. data/app/controllers/ruby_llm/monitoring/metrics_controller.rb +91 -0
  9. data/app/helpers/ruby_llm/monitoring/alerts_helper.rb +4 -0
  10. data/app/helpers/ruby_llm/monitoring/application_helper.rb +6 -0
  11. data/app/helpers/ruby_llm/monitoring/metrics_helper.rb +7 -0
  12. data/app/javascript/ruby_llm/monitoring/application.js +5 -0
  13. data/app/javascript/ruby_llm/monitoring/controllers/application.js +11 -0
  14. data/app/javascript/ruby_llm/monitoring/controllers/chart_controller.js +93 -0
  15. data/app/javascript/ruby_llm/monitoring/controllers/index.js +4 -0
  16. data/app/jobs/ruby_llm/monitoring/application_job.rb +6 -0
  17. data/app/mailers/ruby_llm/monitoring/alert_mailer.rb +13 -0
  18. data/app/mailers/ruby_llm/monitoring/application_mailer.rb +8 -0
  19. data/app/models/concerns/ruby_llm/monitoring/alertable.rb +53 -0
  20. data/app/models/ruby_llm/monitoring/application_record.rb +7 -0
  21. data/app/models/ruby_llm/monitoring/event.rb +22 -0
  22. data/app/views/layouts/ruby_llm/monitoring/application.html.erb +29 -0
  23. data/app/views/layouts/ruby_llm/monitoring/mailer.html.erb +13 -0
  24. data/app/views/layouts/ruby_llm/monitoring/mailer.text.erb +1 -0
  25. data/app/views/ruby_llm/monitoring/alert_mailer/alert_notification.html.erb +1 -0
  26. data/app/views/ruby_llm/monitoring/alert_mailer/alert_notification.text.erb +1 -0
  27. data/app/views/ruby_llm/monitoring/alerts/_alert.html.erb +2 -0
  28. data/app/views/ruby_llm/monitoring/alerts/index.html.erb +26 -0
  29. data/app/views/ruby_llm/monitoring/application/_flashes.html.erb +9 -0
  30. data/app/views/ruby_llm/monitoring/application/_nav.html.erb +12 -0
  31. data/app/views/ruby_llm/monitoring/application/_pagination.html.erb +7 -0
  32. data/app/views/ruby_llm/monitoring/application/_tabs.html.erb +6 -0
  33. data/app/views/ruby_llm/monitoring/metrics/_filters.html.erb +25 -0
  34. data/app/views/ruby_llm/monitoring/metrics/_totals.html.erb +51 -0
  35. data/app/views/ruby_llm/monitoring/metrics/index.html.erb +16 -0
  36. data/config/importmap.rb +8 -0
  37. data/config/routes.rb +6 -0
  38. data/db/migrate/20251208171258_create_ruby_llm_monitoring_events.rb +70 -0
  39. data/lib/ruby_llm/monitoring/channel_registry.rb +19 -0
  40. data/lib/ruby_llm/monitoring/channels/base.rb +11 -0
  41. data/lib/ruby_llm/monitoring/channels/email.rb +18 -0
  42. data/lib/ruby_llm/monitoring/channels/slack.rb +18 -0
  43. data/lib/ruby_llm/monitoring/engine.rb +61 -0
  44. data/lib/ruby_llm/monitoring/event_subscriber.rb +20 -0
  45. data/lib/ruby_llm/monitoring/version.rb +5 -0
  46. data/lib/ruby_llm/monitoring.rb +24 -0
  47. data/lib/tasks/ruby_llm/monitoring_tasks.rake +4 -0
  48. metadata +185 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8823877c782a9d6e67c1aa975c2ed5f64312735141d456e7408d03b7fee0356f
4
+ data.tar.gz: 9568944cf43cbd83acbb4a455e240f7241515b74f38290547847e6cabe0b8a9d
5
+ SHA512:
6
+ metadata.gz: b53443b86fca0140c277a54f9738cd100886b409b3aecf594626223d2dfcdd97d77518494b1e7f40696190b4d7a988fe3b2142f4f2d3dcf0f2f9b3930ceb256a
7
+ data.tar.gz: 36fcc184799318b6c70d2233cf5ecb598d52449bf7cbee71d7ec6f1d79949aca5c5f1482ee42b99f364c7a502de59fed09e64dbed240523fbe9d8e57429e089c
data/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # RubyLLM::Monitoring
2
+
3
+ Monitor your LLM usage within your Rails application.
4
+
5
+ ## Installation
6
+
7
+ > [!NOTE]
8
+ > This engine relies on [RubyLLM](https://github.com/crmne/ruby_llm). Make sure you have it installed and configured.
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem "ruby_llm-monitoring"
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```bash
19
+ $ bundle
20
+ ```
21
+
22
+ To copy and migrate RubyLLM::Monitoring's migrations, run:
23
+
24
+ ```
25
+ $ rails ruby_llm_monitoring:install:migrations db:migrate
26
+ ```
27
+
28
+ And then mount the engine in your `config/routes.rb`:
29
+
30
+ ```ruby
31
+ Rails.application.routes.draw do
32
+ # ...
33
+
34
+ mount RubyLLM::Monitoring::Engine, at: "/monitoring"
35
+ end
36
+ ```
37
+
38
+ Now you should be able to browse to `/monitoring` and monitor your LLM usage.
39
+
40
+ ![metrics](./assets/metrics.png)
41
+ ![alerts](./assets/alerts.png)
42
+
43
+ ### Authentication and authorization
44
+
45
+ RubyLLM::Monitoring leaves authentication and authorization to the user. If no authentication is enforced, `/monitoring` will be available to everyone.
46
+
47
+ To enforce authentication, you can use route [constraints](https://guides.rubyonrails.org/routing.html#advanced-constraints), or set up a HTTP Basic auth middleware.
48
+
49
+ For example, if you're using devise, you can do this:
50
+
51
+ ```ruby
52
+ # config/routes.rb
53
+ authenticate :user do
54
+ mount RubyLLM::Monitoring::Engine, at: "/monitoring"
55
+ end
56
+ ```
57
+
58
+ See more examples [here](https://github.com/heartcombo/devise/wiki/How-To%3A-Define-resource-actions-that-require-authentication-using-routes.rb).
59
+
60
+ However, if you're using Rails' default authentication generator, or an authentication solution that doesn't provide constraints, you need to roll out your own solution:
61
+
62
+ ```ruby
63
+ # config/routes.rb
64
+ constraints ->(request) { Constraints::Auth.authenticated?(request) } do
65
+ mount RubyLLM::Monitoring::Engine, at: "/monitoring"
66
+ end
67
+
68
+ # lib/constraints/auth.rb
69
+ class Constraints::Auth
70
+ def self.authenticated?(request)
71
+ cookies = ActionDispatch::Cookies::CookieJar.build(request, request.cookies)
72
+
73
+ Session.find_by id: cookies.signed[:session_id]
74
+ end
75
+ end
76
+ ```
77
+
78
+ You can also set up a HTTP Basic auth middleware in the engine:
79
+
80
+ ```ruby
81
+ # config/initializers/ruby_llm-monitoring.rb
82
+ RubyLLM::Monitoring::Engine.middleware.use(Rack::Auth::Basic) do |username, password|
83
+ ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.ruby_llm_monitoring_username, username) &
84
+ ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.ruby_llm_monitoring_password, password)
85
+ end
86
+ ```
87
+
88
+ ## Alerts
89
+
90
+ RubyLLM::Monitoring can send alerts when certain conditions are met. Useful for monitoring cost, errors, etc.
91
+
92
+ ### Configuration
93
+
94
+ In `config/initializers/ruby_llm_monitoring.rb`, you can set the notification channels and alert rules:
95
+
96
+ ```ruby
97
+ RubyLLM::Monitoring.channels = {
98
+ email: { to: "team@example.com" },
99
+ slack: { webhook_url: ENV["SLACK_WEBHOOK_URL"] },
100
+ }
101
+
102
+ # Default cooldown between repeated alerts (optional, defaults to 5 minutes)
103
+ RubyLLM::Monitoring.alert_cooldown = 15.minutes
104
+
105
+ RubyLLM::Monitoring.alert_rules += [{
106
+ time_range: -> { 1.hour.ago.. },
107
+ rule: ->(events) { events.where.not(exception_class: nil).count > 10 },
108
+ channels: [:slack],
109
+ message: { text: "More than 10 errors in the last hour" }
110
+ }, {
111
+ time_range: -> { Time.current.at_beginning_of_month.. },
112
+ rule: ->(events) { events.sum(:cost) >= 500 },
113
+ channels: [:email, :slack],
114
+ message: { text: "More than $500 spent this month" }
115
+ }]
116
+ ```
117
+
118
+ ### Rule options
119
+
120
+ | Option | Required | Description |
121
+ | ------------ | -------- | --------------------------------------------------------------------------- |
122
+ | `time_range` | Yes | Lambda returning a range for filtering events (e.g., `-> { 1.hour.ago.. }`) |
123
+ | `rule` | Yes | Lambda receiving events scope, returns true to trigger alert |
124
+ | `channels` | Yes | Array of channel names to notify |
125
+ | `message` | Yes | Hash with `:text` key for the alert message |
126
+ | `id` | No | Identifier for debugging and cooldown tracking |
127
+ | `cooldown` | No | Override default cooldown for this rule |
128
+
129
+ ### Built-in channels
130
+
131
+ #### Slack
132
+
133
+ ```ruby
134
+ RubyLLM::Monitoring.channels = {
135
+ slack: {
136
+ webhook_url: ENV["SLACK_WEBHOOK_URL"]
137
+ }
138
+ }
139
+ ```
140
+
141
+ #### Email
142
+
143
+ ```ruby
144
+ RubyLLM::Monitoring.channels = {
145
+ email: {
146
+ to: "team@example.com",
147
+ from: "alerts@example.com", # optional
148
+ subject: "LLM Alert" # optional
149
+ }
150
+ }
151
+ ```
152
+
153
+ ### Custom channels
154
+
155
+ Register custom notification channels:
156
+
157
+ ```ruby
158
+ class PagerDutyChannel < RubyLLM::Monitoring::Channels::Base
159
+ def self.deliver(message, config)
160
+ # Your implementation
161
+ # message[:text] contains the alert text
162
+ # config contains channel configuration
163
+ end
164
+ end
165
+
166
+ RubyLLM::Monitoring.channel_registry.register(:pagerduty, PagerDutyChannel)
167
+
168
+ RubyLLM::Monitoring.channels = {
169
+ pagerduty: { api_key: ENV["PAGERDUTY_API_KEY"] }
170
+ }
171
+ ```
172
+
173
+ ## Contributing
174
+
175
+ You can open an issue or a PR in GitHub.
176
+
177
+ ## License
178
+
179
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */