rails-ai-gateway 0.2.0 → 0.3.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +48 -2
  5. data/app/assets/images/rails_ai_gateway/providers/cerebras.webp +0 -0
  6. data/app/assets/images/rails_ai_gateway/providers/deepseek.webp +0 -0
  7. data/app/assets/images/rails_ai_gateway/providers/groq.webp +0 -0
  8. data/app/assets/images/rails_ai_gateway/providers/mistral.webp +0 -0
  9. data/app/assets/images/rails_ai_gateway/providers/nebius.webp +0 -0
  10. data/app/assets/images/rails_ai_gateway/providers/openai.webp +0 -0
  11. data/app/assets/images/rails_ai_gateway/providers/openrouter.webp +0 -0
  12. data/app/assets/images/rails_ai_gateway/providers/perplexity.webp +0 -0
  13. data/app/assets/javascripts/rails_ai_gateway/admin.js +25 -0
  14. data/app/assets/stylesheets/rails_ai_gateway/admin.css +1111 -167
  15. data/app/controllers/rails_ai_gateway/admin_controller.rb +48 -3
  16. data/app/models/rails_ai_gateway/model_route.rb +34 -0
  17. data/app/models/rails_ai_gateway/provider.rb +11 -0
  18. data/app/views/layouts/rails_ai_gateway/admin.html.erb +70 -17
  19. data/app/views/rails_ai_gateway/admin/errors.html.erb +3 -3
  20. data/app/views/rails_ai_gateway/admin/index.html.erb +420 -90
  21. data/app/views/rails_ai_gateway/admin/key.html.erb +4 -4
  22. data/config/routes.rb +3 -0
  23. data/db/migrate/20260920000000_create_rails_ai_gateway.rb +1 -1
  24. data/db/migrate/20260921000000_add_prompts_and_token_counts_to_rails_ai_gateway.rb +9 -0
  25. data/db/migrate/20260921010000_add_capabilities_to_rails_ai_gateway_model_routes.rb +5 -0
  26. data/db/migrate/20260921020000_add_query_keywords_to_rails_ai_gateway_model_routes.rb +5 -0
  27. data/lib/generators/rails_ai_gateway/install_generator.rb +1 -1
  28. data/lib/generators/rails_ai_gateway/templates/initializer.rb +1 -1
  29. data/lib/rails_ai_gateway/configuration.rb +26 -3
  30. data/lib/rails_ai_gateway/model_helper.rb +46 -0
  31. data/lib/rails_ai_gateway/proxy.rb +94 -9
  32. data/lib/rails_ai_gateway/version.rb +1 -1
  33. data/lib/rails_ai_gateway.rb +56 -0
  34. metadata +16 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af35d6d1d275d95fdc5823eded924c98cd4e39e18e1f67388f1fd0f7e9f056f3
4
- data.tar.gz: bc9d7f02956947396a2801b27199328aba3b38b1d9917e2e00d9a70a1fc46b11
3
+ metadata.gz: b09f7d65429d12dbd10f742e683c8a366302b465da1869f62f9c0ffb17be37a5
4
+ data.tar.gz: fb68e097b3437b2008f4352bc1c6905a4e57afd03be22cc6bb9ad709ea24e05d
5
5
  SHA512:
6
- metadata.gz: 352f0608dbf281db2d14dcb452913cae5c758b4347d41a87bc38dc1fbc564792890a6b7f09edcab39741db71accdebb8ddeffb881a67c0b9900c358412da574f
7
- data.tar.gz: 28780f6a22d71ac9c77ddc1a1f236fbbcf2e9fd12f8ed5412c495b7977ead1096c94a797e42642a1f1c8f87459077d5d8d8d35559645bb3f8d5652c7e85bcdd1
6
+ metadata.gz: 2f7c4bce60576871c5853bc6b7becd05752ac10cfad18b0a1d697587797381d94e800bacb87e9ba4f9983c35b2cb22c4ba28155828f629b562c5ff2bfe60e5a1
7
+ data.tar.gz: 40c43dae901d3a2f4b431a85c2bda10d51d40c3b3f602c95ec5d8f74ef6603676c9f41a905ec695e8a1975eb757dfeb5828f205f531ca611b00ebf9663251b9f
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --protected
4
+ lib/**/*.rb
5
+ -
6
+ README.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 - 2026-09-21
4
+
5
+ - Add durable input/output token totals grouped by public model.
6
+ - Add per-route system prompt injection without prompt logging.
7
+ - Add `RailsAIGateway::ENDPOINT`, `AI` inflection, and `RailsAIGateway` namespace alias.
8
+ - Add one-key templates for popular OpenAI-compatible providers.
9
+ - Add model capability metadata to routes, admin UI, and `/v1/models`.
10
+ - Add provider logos, modal connection flow, and deterministic keyword query routing.
11
+ - Add `models`, `model`, and `route_for` helpers for host Rails code.
12
+ - Use same-origin referrer policy so browser form submissions pass Rails CSRF origin checks.
13
+ - Add safe delete actions for providers and model routes in admin console.
14
+ - Use Nebius Token Factory API endpoint in provider template.
15
+ - Validate and forward OpenAI-compatible text, image, and audio message parts.
16
+ - Filter query routing by declared model capabilities.
17
+
3
18
  ## 0.2.0 - 2026-09-20
4
19
 
5
20
  - Redesign admin UI as a responsive AI gateway operations console.
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rails-ai-gateway.svg)](https://rubygems.org/gems/rails-ai-gateway)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![RubyDoc](https://img.shields.io/badge/docs-RubyDoc-blue.svg)](https://www.rubydoc.info/gems/rails-ai-gateway)
5
6
 
6
7
  Put one OpenAI-compatible endpoint in front of your AI providers without running another
7
8
  service. Rails AI Gateway mounts inside your Rails app, stores configuration and request
@@ -15,6 +16,11 @@ metadata with ActiveRecord, and includes a Web UI for providers, model routes, a
15
16
  - Encrypted provider API keys using ActiveRecord encryption
16
17
  - Scoped gateway keys stored as SHA-256 digests
17
18
  - Request status, latency, attempt, and reported token-usage logs
19
+ - Durable input/output token totals grouped by public model
20
+ - Optional system prompt injection configured per model route
21
+ - One-key provider templates for OpenAI, OpenRouter, Groq, DeepSeek, Mistral, Cerebras,
22
+ Nebius AI, and Perplexity
23
+ - Explicit model capabilities exposed through UI and `/v1/models`
18
24
  - Configurable timeouts, body limits, fallback attempts, and network policy
19
25
  - SSRF protection with DNS validation and address pinning
20
26
  - Server-rendered, responsive, accessible admin UI
@@ -54,6 +60,9 @@ Web UI guides initial setup:
54
60
  2. Add public model route, such as `fast-chat` mapped to `gpt-5.6-luna`.
55
61
  3. Create gateway key and save token when shown. Raw token cannot be displayed again.
56
62
 
63
+ Popular provider templates need only API key. Custom OpenAI-compatible provider form remains
64
+ available for self-hosted and less common endpoints.
65
+
57
66
  Routes with same public model name form fallback chain. Lower priority runs first. Gateway
58
67
  retries connection failures before upstream accepts request and HTTP `429`, `500`, `502`,
59
68
  `503`, or `504`. Ambiguous timeouts and started streams are never retried.
@@ -63,7 +72,7 @@ retries connection failures before upstream accepts request and HTTP `429`, `500
63
72
  Runtime and security policy live in `config/initializers/rails_ai_gateway.rb`:
64
73
 
65
74
  ```ruby
66
- RailsAiGateway.configure do |config|
75
+ RailsAIGateway.configure do |config|
67
76
  config.admin_controller = "ApplicationController"
68
77
  config.admin_authorization = ->(controller) {
69
78
  controller.current_user&.admin? == true
@@ -82,6 +91,16 @@ RailsAiGateway.configure do |config|
82
91
  end
83
92
  ```
84
93
 
94
+ Rails acronym inflection exposes `RailsAIGateway` as the preferred constant while keeping
95
+ `RailsAiGateway` compatible. Use the endpoint constant instead of repeating a URL path:
96
+
97
+ ```ruby
98
+ OpenAI::Client.new(uri_base: "#{ENV.fetch("APP_URL")}#{RailsAIGateway::ENDPOINT}")
99
+ ```
100
+
101
+ `RailsAIGateway::ENDPOINT` is `/ai/v1`, matching the generator's default mount. If host app
102
+ changes mount path, build endpoint from that route instead.
103
+
85
104
  Admin requests are denied until `admin_authorization` returns exactly `true`. Provider
86
105
  definitions, model routes, gateway keys, and logs stay database-backed and editable through
87
106
  Web UI.
@@ -89,7 +108,7 @@ Web UI.
89
108
  Need another mount path? Change host route:
90
109
 
91
110
  ```ruby
92
- mount RailsAiGateway::Engine, at: "/gateway"
111
+ mount RailsAIGateway::Engine, at: "/gateway"
93
112
  ```
94
113
 
95
114
  ## Make Requests
@@ -109,6 +128,32 @@ Available endpoints:
109
128
 
110
129
  OpenAI clients can use `http://localhost:3000/ai/v1` as base URL.
111
130
 
131
+ Each model route can prepend an optional system message to chat requests. Injected prompts
132
+ are stored on route and are never copied into request logs. Token dashboards use only usage
133
+ reported by upstream providers; missing usage remains zero rather than being estimated.
134
+ Routes declare capabilities from `text`, `vision`, `embedding`, `audio`, `video`, `tools`,
135
+ and `reasoning`; clients receive merged capabilities in each `/v1/models` entry. Capability
136
+ metadata does not add unsupported transport endpoints by itself.
137
+
138
+ Chat requests support OpenAI-compatible multimodal content arrays containing `text`,
139
+ `image_url` (HTTPS or image data URL), and `input_audio` (base64 WAV/MP3) parts. Gateway
140
+ selects only routes advertising every required capability and forwards accepted parts without
141
+ conversion. Request-size limit still applies to encoded media.
142
+
143
+ Host Rails code can inspect active models without making an HTTP request:
144
+
145
+ ```ruby
146
+ RailsAIGateway.models
147
+ RailsAIGateway.models(capabilities: %w[vision tools])
148
+ RailsAIGateway.models(provider: "OpenAI")
149
+ RailsAIGateway.model("fast-chat")
150
+ RailsAIGateway.route_for(model: "fast-chat", query: "Review this Ruby code", capabilities: %w[text tools])
151
+ ```
152
+
153
+ Helpers return provider names and routing metadata, never provider API keys. `route_for`
154
+ uses same literal keyword ranking as proxy: matching specialized routes first, then generic
155
+ fallback routes.
156
+
112
157
  ## Development
113
158
 
114
159
  ```bash
@@ -117,6 +162,7 @@ cd rails-ai-gateway
117
162
  bundle install
118
163
  bundle exec ruby test/check.rb
119
164
  gem build rails_ai_gateway.gemspec
165
+ rdoc --main README.md README.md lib
120
166
  ```
121
167
 
122
168
  SQLite integration runs by default. Run same suite against PostgreSQL with an empty test
@@ -0,0 +1,25 @@
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ const dialog = document.querySelector("#provider-connect-dialog");
3
+ if (!dialog || typeof dialog.showModal !== "function") return;
4
+
5
+ document.querySelectorAll("[data-provider-connect]").forEach((button) => {
6
+ button.addEventListener("click", () => {
7
+ const name = button.dataset.providerName;
8
+ dialog.querySelector("[data-dialog-template]").value = button.dataset.template;
9
+ dialog.querySelector("[data-dialog-logo]").src = button.dataset.logo;
10
+ dialog.querySelector("[data-dialog-key-label]").textContent = `${name} API Key`;
11
+ dialog.querySelector("[data-dialog-submit]").value = `Connect ${name}`;
12
+ dialog.querySelector("#connect-dialog-title").textContent = `Connect ${name}`;
13
+ dialog.showModal();
14
+ dialog.querySelector("[data-dialog-api-key]").focus();
15
+ });
16
+ });
17
+
18
+ dialog.querySelectorAll("[data-dialog-close]").forEach((button) => button.addEventListener("click", () => dialog.close()));
19
+ dialog.addEventListener("click", (event) => {
20
+ if (event.target === dialog) dialog.close();
21
+ });
22
+ dialog.addEventListener("close", () => {
23
+ dialog.querySelector("[data-dialog-api-key]").value = "";
24
+ });
25
+ });