rails_ai 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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec_status +96 -0
  3. data/AGENT_GUIDE.md +513 -0
  4. data/Appraisals +49 -0
  5. data/COMMERCIAL_LICENSE_TEMPLATE.md +92 -0
  6. data/FEATURES.md +204 -0
  7. data/LEGAL_PROTECTION_GUIDE.md +222 -0
  8. data/LICENSE +62 -0
  9. data/LICENSE_SUMMARY.md +74 -0
  10. data/MIT-LICENSE +62 -0
  11. data/PERFORMANCE.md +300 -0
  12. data/PROVIDERS.md +495 -0
  13. data/README.md +454 -0
  14. data/Rakefile +11 -0
  15. data/SPEED_OPTIMIZATIONS.md +217 -0
  16. data/STRUCTURE.md +139 -0
  17. data/USAGE_GUIDE.md +288 -0
  18. data/app/channels/ai_stream_channel.rb +33 -0
  19. data/app/components/ai/prompt_component.rb +25 -0
  20. data/app/controllers/concerns/ai/context_aware.rb +77 -0
  21. data/app/controllers/concerns/ai/streaming.rb +41 -0
  22. data/app/helpers/ai_helper.rb +164 -0
  23. data/app/jobs/ai/generate_embedding_job.rb +25 -0
  24. data/app/jobs/ai/generate_summary_job.rb +25 -0
  25. data/app/models/concerns/ai/embeddable.rb +38 -0
  26. data/app/views/rails_ai/dashboard/index.html.erb +51 -0
  27. data/config/routes.rb +19 -0
  28. data/lib/generators/rails_ai/install/install_generator.rb +38 -0
  29. data/lib/rails_ai/agents/agent_manager.rb +258 -0
  30. data/lib/rails_ai/agents/agent_team.rb +243 -0
  31. data/lib/rails_ai/agents/base_agent.rb +331 -0
  32. data/lib/rails_ai/agents/collaboration.rb +238 -0
  33. data/lib/rails_ai/agents/memory.rb +116 -0
  34. data/lib/rails_ai/agents/message_bus.rb +95 -0
  35. data/lib/rails_ai/agents/specialized_agents.rb +391 -0
  36. data/lib/rails_ai/agents/task_queue.rb +111 -0
  37. data/lib/rails_ai/cache.rb +14 -0
  38. data/lib/rails_ai/config.rb +40 -0
  39. data/lib/rails_ai/context.rb +7 -0
  40. data/lib/rails_ai/context_analyzer.rb +86 -0
  41. data/lib/rails_ai/engine.rb +48 -0
  42. data/lib/rails_ai/events.rb +9 -0
  43. data/lib/rails_ai/image_context.rb +110 -0
  44. data/lib/rails_ai/performance.rb +231 -0
  45. data/lib/rails_ai/provider.rb +8 -0
  46. data/lib/rails_ai/providers/anthropic_adapter.rb +256 -0
  47. data/lib/rails_ai/providers/base.rb +60 -0
  48. data/lib/rails_ai/providers/dummy_adapter.rb +29 -0
  49. data/lib/rails_ai/providers/gemini_adapter.rb +509 -0
  50. data/lib/rails_ai/providers/openai_adapter.rb +535 -0
  51. data/lib/rails_ai/providers/secure_anthropic_adapter.rb +206 -0
  52. data/lib/rails_ai/providers/secure_openai_adapter.rb +284 -0
  53. data/lib/rails_ai/railtie.rb +48 -0
  54. data/lib/rails_ai/redactor.rb +12 -0
  55. data/lib/rails_ai/security/api_key_manager.rb +82 -0
  56. data/lib/rails_ai/security/audit_logger.rb +46 -0
  57. data/lib/rails_ai/security/error_handler.rb +62 -0
  58. data/lib/rails_ai/security/input_validator.rb +176 -0
  59. data/lib/rails_ai/security/secure_file_handler.rb +45 -0
  60. data/lib/rails_ai/security/secure_http_client.rb +177 -0
  61. data/lib/rails_ai/security.rb +0 -0
  62. data/lib/rails_ai/version.rb +5 -0
  63. data/lib/rails_ai/window_context.rb +103 -0
  64. data/lib/rails_ai.rb +502 -0
  65. data/monitoring/ci_setup_guide.md +214 -0
  66. data/monitoring/enhanced_monitoring_script.rb +237 -0
  67. data/monitoring/google_alerts_setup.md +42 -0
  68. data/monitoring_log_20250921.txt +0 -0
  69. data/monitoring_script.rb +161 -0
  70. data/rails_ai.gemspec +54 -0
  71. data/scripts/security_scanner.rb +353 -0
  72. data/setup_monitoring.sh +163 -0
  73. data/wiki/API-Documentation.md +734 -0
  74. data/wiki/Architecture-Overview.md +672 -0
  75. data/wiki/Contributing-Guide.md +407 -0
  76. data/wiki/Development-Setup.md +532 -0
  77. data/wiki/Home.md +278 -0
  78. data/wiki/Installation-Guide.md +527 -0
  79. data/wiki/Quick-Start.md +186 -0
  80. data/wiki/README.md +135 -0
  81. data/wiki/Release-Process.md +467 -0
  82. metadata +385 -0
data/STRUCTURE.md ADDED
@@ -0,0 +1,139 @@
1
+ # Rails AI - Complete Structure
2
+
3
+ This Rails AI gem is designed to be a comprehensive AI toolkit that's deeply integrated into Rails applications, supporting Rails 5.2+ through Rails 8 with Ruby 3.x compatibility.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ rails_ai/
9
+ ├─ README.md # Comprehensive documentation
10
+ ├─ rails_ai.gemspec # Gem specification with Rails 5.2-8.0 support
11
+ ├─ Gemfile # Development dependencies
12
+ ├─ Rakefile # Rake tasks for testing and linting
13
+ ├─ LICENSE # MIT License
14
+ ├─ .gitignore # Git ignore patterns
15
+ ├─ Appraisals # Multi-Rails version testing
16
+ ├─ lib/
17
+ │ ├─ rails_ai.rb # Main module with version compatibility
18
+ │ ├─ rails_ai/
19
+ │ │ ├─ version.rb # Version constant
20
+ │ │ ├─ config.rb # Configuration with Struct
21
+ │ │ ├─ context.rb # CurrentAttributes for context
22
+ │ │ ├─ cache.rb # Intelligent caching
23
+ │ │ ├─ redactor.rb # Content redaction for security
24
+ │ │ ├─ events.rb # Event logging and metrics
25
+ │ │ ├─ provider.rb # Provider base classes
26
+ │ │ ├─ engine.rb # Rails engine with version compatibility
27
+ │ │ ├─ railtie.rb # Railtie with version-specific configs
28
+ │ │ └─ providers/
29
+ │ │ ├─ base.rb # Base provider interface
30
+ │ │ ├─ openai_adapter.rb # OpenAI integration
31
+ │ │ └─ dummy_adapter.rb # Dummy provider for testing
32
+ │ └─ generators/
33
+ │ └─ rails_ai/
34
+ │ ├─ install/
35
+ │ │ ├─ install_generator.rb
36
+ │ │ └─ templates/ # Generator templates
37
+ │ └─ feature/
38
+ │ ├─ feature_generator.rb
39
+ │ └─ templates/ # Feature templates
40
+ ├─ app/
41
+ │ ├─ channels/
42
+ │ │ └─ ai_stream_channel.rb # Action Cable streaming
43
+ │ ├─ components/
44
+ │ │ └─ ai/
45
+ │ │ └─ prompt_component.rb # ViewComponent for AI UI
46
+ │ ├─ controllers/
47
+ │ │ └─ concerns/ai/
48
+ │ │ └─ streaming.rb # Streaming controller concern
49
+ │ ├─ helpers/
50
+ │ │ └─ ai_helper.rb # AI helper methods
51
+ │ ├─ jobs/
52
+ │ │ └─ ai/
53
+ │ │ ├─ generate_embedding_job.rb
54
+ │ │ └─ generate_summary_job.rb
55
+ │ ├─ models/
56
+ │ │ └─ concerns/ai/
57
+ │ │ └─ embeddable.rb # Model concern for embeddings
58
+ │ └─ views/
59
+ │ └─ rails_ai/
60
+ │ └─ dashboard/
61
+ │ └─ index.html.erb # Dashboard view
62
+ ├─ config/
63
+ │ └─ routes.rb # Engine routes
64
+ └─ spec/
65
+ ├─ spec_helper.rb # RSpec configuration
66
+ └─ rails_ai_spec.rb # Main test suite
67
+ ```
68
+
69
+ ## Key Features
70
+
71
+ ### Rails Version Compatibility
72
+ - **Rails 5.2+**: Full backward compatibility
73
+ - **Rails 6.0, 6.1**: Enhanced features
74
+ - **Rails 7.0, 7.1**: Importmap support, enhanced streaming
75
+ - **Rails 8.0**: Latest features, enhanced performance
76
+
77
+ ### Ruby Version Support
78
+ - **Ruby 3.0+**: Primary target
79
+ - **Ruby 2.7+**: Backward compatibility
80
+
81
+ ### Core AI Capabilities
82
+ - **Universal Interface**: `RailsAi.chat()`, `RailsAi.stream()`, `RailsAi.embed()`
83
+ - **Multiple Providers**: OpenAI, custom adapters, dummy for testing
84
+ - **Intelligent Caching**: Cost optimization with configurable TTL
85
+ - **Content Security**: Built-in redaction for PII
86
+ - **Observability**: Comprehensive event logging
87
+ - **Streaming**: Real-time AI responses via Action Cable
88
+
89
+ ### Rails Integration
90
+ - **Generators**: Easy setup and feature generation
91
+ - **View Components**: Reusable AI UI components
92
+ - **Jobs**: Background AI processing
93
+ - **Models**: Embeddable concern for semantic search
94
+ - **Helpers**: Convenient AI methods
95
+ - **Controllers**: Streaming concerns
96
+
97
+ ### Testing & Quality
98
+ - **Multi-Rails Testing**: Appraisal for version compatibility
99
+ - **RSpec**: Comprehensive test suite
100
+ - **Standard**: Code style enforcement
101
+ - **CI/CD**: GitHub Actions for multiple Rails/Ruby versions
102
+
103
+ ## Usage Examples
104
+
105
+ ```ruby
106
+ # Basic AI operations
107
+ RailsAi.chat("Write a blog post about Ruby")
108
+ RailsAi.stream("Explain quantum computing") { |token| puts token }
109
+ RailsAi.embed(["Ruby on Rails", "Django"])
110
+
111
+ # Convenience methods
112
+ RailsAi.summarize(long_article)
113
+ RailsAi.translate("Hello", "Spanish")
114
+ RailsAi.classify("Great product!", ["positive", "negative"])
115
+ RailsAi.generate_code("User authentication", language: "ruby")
116
+
117
+ # Rails integration
118
+ class Article < ApplicationRecord
119
+ include RailsAi::Embeddable
120
+ end
121
+
122
+ # View components
123
+ <%= render RailsAi::PromptComponent.new(prompt: "Ask me anything") %>
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ ```ruby
129
+ # config/initializers/rails_ai.rb
130
+ RailsAi.configure do |config|
131
+ config.provider = :openai
132
+ config.default_model = "gpt-4o-mini"
133
+ config.token_limit = 4000
134
+ config.cache_ttl = 1.hour
135
+ config.stub_responses = Rails.env.test?
136
+ end
137
+ ```
138
+
139
+ This structure provides a solid foundation for building AI-powered Rails applications with full backward compatibility and forward-looking features.
data/USAGE_GUIDE.md ADDED
@@ -0,0 +1,288 @@
1
+ # Rails AI - Quick Usage Guide
2
+
3
+ ## 🚀 Getting Started
4
+
5
+ ### 1. Installation
6
+ ```bash
7
+ # Add to Gemfile
8
+ gem 'rails_ai'
9
+
10
+ # Install
11
+ bundle install
12
+
13
+ # Generate initial files
14
+ rails generate rails_ai:install
15
+ ```
16
+
17
+ ### 2. Configuration
18
+ ```ruby
19
+ # config/initializers/rails_ai.rb
20
+ RailsAi.configure do |config|
21
+ config.provider = :openai
22
+ config.default_model = "gpt-4o-mini"
23
+ config.api_key = ENV['OPENAI_API_KEY']
24
+ config.cache_ttl = 1.hour
25
+ config.stub_responses = Rails.env.test?
26
+ end
27
+ ```
28
+
29
+ ## 📝 Basic Usage
30
+
31
+ ### Text Operations
32
+ ```ruby
33
+ # Simple chat
34
+ RailsAi.chat("Write a blog post about Ruby")
35
+
36
+ # Streaming
37
+ RailsAi.stream("Explain quantum computing") do |token|
38
+ puts token
39
+ end
40
+
41
+ # Common tasks
42
+ RailsAi.summarize(long_text)
43
+ RailsAi.translate("Hello", "Spanish")
44
+ RailsAi.classify("Great product!", ["positive", "negative"])
45
+ ```
46
+
47
+ ### Image Operations
48
+ ```ruby
49
+ # Generate image
50
+ image = RailsAi.generate_image("A sunset over mountains")
51
+
52
+ # Analyze image
53
+ analysis = RailsAi.analyze_image(image_file, "What do you see?")
54
+
55
+ # Edit image
56
+ edited = RailsAi.edit_image(image_file, "Add a rainbow")
57
+ ```
58
+
59
+ ### Video Operations
60
+ ```ruby
61
+ # Generate video
62
+ video = RailsAi.generate_video("A cat playing", duration: 10)
63
+
64
+ # Analyze video
65
+ analysis = RailsAi.analyze_video(video_file, "What's happening?")
66
+ ```
67
+
68
+ ### Audio Operations
69
+ ```ruby
70
+ # Generate speech
71
+ speech = RailsAi.generate_speech("Hello world", voice: "alloy")
72
+
73
+ # Transcribe audio
74
+ text = RailsAi.transcribe_audio(audio_file)
75
+ ```
76
+
77
+ ## 🧠 Context-Aware Usage
78
+
79
+ ### In Controllers
80
+ ```ruby
81
+ class PostsController < ApplicationController
82
+ include RailsAi::ContextAware
83
+
84
+ def analyze_image
85
+ # Automatically includes user and window context
86
+ result = analyze_image_with_context(
87
+ params[:image],
88
+ "What's in this image?"
89
+ )
90
+ render json: { analysis: result }
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### Manual Context
96
+ ```ruby
97
+ # With custom context
98
+ RailsAi.analyze_image_with_context(
99
+ image_file,
100
+ "What do you see?",
101
+ user_context: { id: 1, role: "admin" },
102
+ window_context: { controller: "PostsController" },
103
+ image_context: { format: "png" }
104
+ )
105
+ ```
106
+
107
+ ## 🎨 Rails Integration
108
+
109
+ ### View Components
110
+ ```erb
111
+ <!-- AI Chat -->
112
+ <%= render RailsAi::PromptComponent.new(
113
+ prompt: "Ask me anything",
114
+ stream_id: ai_stream_id
115
+ ) %>
116
+
117
+ <!-- Image Generation Form -->
118
+ <%= form_with url: ai_generate_image_path do |form| %>
119
+ <%= form.text_area :prompt, placeholder: "Describe the image" %>
120
+ <%= form.submit "Generate Image" %>
121
+ <% end %>
122
+ ```
123
+
124
+ ### Helpers
125
+ ```erb
126
+ <!-- In views -->
127
+ <%= ai_chat("Write a summary") %>
128
+ <%= ai_generate_image("A beautiful landscape") %>
129
+ <%= ai_analyze_image_with_context(image, "What do you see?") %>
130
+ ```
131
+
132
+ ### Jobs
133
+ ```ruby
134
+ class GenerateContentJob < ApplicationJob
135
+ def perform(prompt)
136
+ RailsAi.chat(prompt)
137
+ end
138
+ end
139
+
140
+ # Queue the job
141
+ GenerateContentJob.perform_later("Write a blog post")
142
+ ```
143
+
144
+ ### Models
145
+ ```ruby
146
+ class Article < ApplicationRecord
147
+ include RailsAi::Embeddable
148
+
149
+ def generate_summary!
150
+ RailsAi.summarize(content)
151
+ end
152
+
153
+ def generate_thumbnail!
154
+ RailsAi.generate_image("Thumbnail for: #{title}")
155
+ end
156
+ end
157
+ ```
158
+
159
+ ## 🔧 Advanced Usage
160
+
161
+ ### Custom Providers
162
+ ```ruby
163
+ class MyProvider < RailsAi::Providers::Base
164
+ def chat!(messages:, model:, **opts)
165
+ # Your custom implementation
166
+ end
167
+ end
168
+
169
+ # Use in configuration
170
+ RailsAi.configure do |config|
171
+ config.provider = :my_provider
172
+ end
173
+ ```
174
+
175
+ ### Streaming with Action Cable
176
+ ```ruby
177
+ class ChatController < ApplicationController
178
+ def stream
179
+ RailsAi.stream(params[:prompt]) do |token|
180
+ ActionCable.server.broadcast("chat_#{params[:room_id]}", {
181
+ type: "token",
182
+ content: token
183
+ })
184
+ end
185
+ end
186
+ end
187
+ ```
188
+
189
+ ### Caching
190
+ ```ruby
191
+ # Automatic caching
192
+ RailsAi.chat("Expensive operation") # Cached automatically
193
+
194
+ # Custom cache
195
+ RailsAi::Cache.fetch([:custom, user.id, prompt.hash]) do
196
+ RailsAi.chat(prompt)
197
+ end
198
+ ```
199
+
200
+ ## 🧪 Testing
201
+
202
+ ```ruby
203
+ # In your tests
204
+ RSpec.describe "AI Features" do
205
+ before do
206
+ RailsAi.configure do |config|
207
+ config.provider = :dummy
208
+ config.stub_responses = true
209
+ end
210
+ end
211
+
212
+ it "generates content" do
213
+ result = RailsAi.chat("Hello")
214
+ expect(result).to be_a(String)
215
+ end
216
+ end
217
+ ```
218
+
219
+ ## 🎯 Common Patterns
220
+
221
+ ### Content Generation
222
+ ```ruby
223
+ # Blog post
224
+ post = RailsAi.chat("Write a blog post about #{topic}")
225
+
226
+ # Product description
227
+ description = RailsAi.chat("Write a product description for #{product_name}")
228
+
229
+ # Email content
230
+ email = RailsAi.chat("Write a marketing email about #{campaign}")
231
+ ```
232
+
233
+ ### Image Processing
234
+ ```ruby
235
+ # Generate product images
236
+ image = RailsAi.generate_image("Product photo of #{product_name}")
237
+
238
+ # Analyze user uploads
239
+ analysis = RailsAi.analyze_image(uploaded_file, "Is this appropriate for our site?")
240
+
241
+ # Create variations
242
+ variations = RailsAi.create_variation(existing_image)
243
+ ```
244
+
245
+ ### Data Analysis
246
+ ```ruby
247
+ # Sentiment analysis
248
+ sentiment = RailsAi.classify(user_feedback, ["positive", "negative", "neutral"])
249
+
250
+ # Extract information
251
+ entities = RailsAi.extract_entities("Apple Inc. was founded by Steve Jobs")
252
+
253
+ # Summarize content
254
+ summary = RailsAi.summarize(long_document)
255
+ ```
256
+
257
+ ## 🚨 Best Practices
258
+
259
+ 1. **Always use context** when available for better results
260
+ 2. **Cache expensive operations** to reduce costs
261
+ 3. **Use background jobs** for heavy AI operations
262
+ 4. **Test with dummy providers** in development
263
+ 5. **Monitor API usage** and costs
264
+ 6. **Handle errors gracefully** in production
265
+ 7. **Use streaming** for better user experience
266
+ 8. **Sanitize inputs** before sending to AI
267
+
268
+ ## 🔍 Troubleshooting
269
+
270
+ ### Common Issues
271
+ - **API Key Missing**: Set `OPENAI_API_KEY` environment variable
272
+ - **Rate Limits**: Implement retry logic and rate limiting
273
+ - **Memory Issues**: Use background jobs for large operations
274
+ - **Context Not Working**: Ensure controller includes `RailsAi::ContextAware`
275
+
276
+ ### Debug Mode
277
+ ```ruby
278
+ # Enable debug logging
279
+ Rails.logger.level = :debug
280
+
281
+ # Check configuration
282
+ RailsAi.config.provider
283
+ RailsAi.config.default_model
284
+ ```
285
+
286
+ ---
287
+
288
+ **Need Help?** Check the [full documentation](README.md) or [open an issue](https://github.com/yourusername/rails_ai/issues)!
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ class AiStreamChannel < ApplicationCable::Channel
5
+ def subscribed
6
+ stream_from "ai_stream_#{params[:stream_id]}"
7
+ end
8
+
9
+ def unsubscribed
10
+ # Any cleanup needed when channel is unsubscribed
11
+ end
12
+
13
+ def stream_ai(data)
14
+ stream_id = data["stream_id"]
15
+ prompt = data["prompt"]
16
+ model = data["model"] || RailsAi.config.default_model
17
+
18
+ RailsAi.provider.stream_chat!(
19
+ messages: [{role: "user", content: prompt}],
20
+ model: model
21
+ ) do |token|
22
+ ActionCable.server.broadcast("ai_stream_#{stream_id}", {
23
+ type: "token",
24
+ content: token
25
+ })
26
+ end
27
+
28
+ ActionCable.server.broadcast("ai_stream_#{stream_id}", {
29
+ type: "complete"
30
+ })
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ class PromptComponent < ViewComponent::Base
5
+ def initialize(prompt:, model: nil, stream_id: nil, placeholder: "Ask me anything...", **options)
6
+ @prompt = prompt
7
+ @model = model || RailsAi.config.default_model
8
+ @stream_id = stream_id || SecureRandom.uuid
9
+ @placeholder = placeholder
10
+ @options = options
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :prompt, :model, :stream_id, :placeholder, :options
16
+
17
+ def ai_endpoint
18
+ Rails.application.routes.url_helpers.rails_ai_api_v1_chat_index_path
19
+ end
20
+
21
+ def stream_endpoint
22
+ Rails.application.routes.url_helpers.rails_ai_streams_path
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ module ContextAware
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_action :set_ai_context, if: :ai_context_enabled?
9
+ end
10
+
11
+ def ai_context_analyzer
12
+ @ai_context_analyzer ||= RailsAi::ContextAnalyzer.new(
13
+ user_context: ai_user_context,
14
+ window_context: ai_window_context,
15
+ image_context: ai_image_context
16
+ )
17
+ end
18
+
19
+ def analyze_image_with_context(image, prompt, **opts)
20
+ ai_context_analyzer.analyze_with_context(image, prompt, **opts)
21
+ end
22
+
23
+ def generate_with_context(prompt, **opts)
24
+ ai_context_analyzer.generate_with_context(prompt, **opts)
25
+ end
26
+
27
+ def generate_image_with_context(prompt, **opts)
28
+ ai_context_analyzer.generate_image_with_context(prompt, **opts)
29
+ end
30
+
31
+ private
32
+
33
+ def set_ai_context
34
+ RailsAi::Context.current_user_id = current_user&.id
35
+ RailsAi::Context.current_request_id = request.uuid
36
+ end
37
+
38
+ def ai_context_enabled?
39
+ respond_to?(:current_user) || respond_to?(:current_admin)
40
+ end
41
+
42
+ def ai_user_context
43
+ return {} unless respond_to?(:current_user) && current_user
44
+
45
+ {
46
+ id: current_user.id,
47
+ email: current_user.respond_to?(:email) ? current_user.email : nil,
48
+ role: current_user.respond_to?(:role) ? current_user.role : 'user',
49
+ created_at: current_user.created_at,
50
+ last_sign_in_at: current_user.respond_to?(:last_sign_in_at) ? current_user.last_sign_in_at : nil,
51
+ preferences: extract_user_preferences
52
+ }.compact
53
+ end
54
+
55
+ def ai_window_context
56
+ RailsAi::WindowContext.from_controller(self).to_h
57
+ end
58
+
59
+ def ai_image_context
60
+ # This can be overridden in controllers to provide image-specific context
61
+ {}
62
+ end
63
+
64
+ def extract_user_preferences
65
+ return {} unless current_user.respond_to?(:preferences)
66
+
67
+ case current_user.preferences
68
+ when Hash
69
+ current_user.preferences
70
+ when String
71
+ JSON.parse(current_user.preferences) rescue {}
72
+ else
73
+ {}
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ module Streaming
5
+ extend ActiveSupport::Concern
6
+
7
+ def stream_ai_response(prompt, model: nil, &block)
8
+ model ||= RailsAi.config.default_model
9
+
10
+ RailsAi.provider.stream_chat!(
11
+ messages: [{role: "user", content: prompt}],
12
+ model: model
13
+ ) do |token|
14
+ block.call(token) if block_given?
15
+ end
16
+ end
17
+
18
+ def broadcast_ai_stream(stream_id, prompt, model: nil)
19
+ model ||= RailsAi.config.default_model
20
+
21
+ ActionCable.server.broadcast("ai_stream_#{stream_id}", {
22
+ type: "start",
23
+ stream_id: stream_id
24
+ })
25
+
26
+ RailsAi.provider.stream_chat!(
27
+ messages: [{role: "user", content: prompt}],
28
+ model: model
29
+ ) do |token|
30
+ ActionCable.server.broadcast("ai_stream_#{stream_id}", {
31
+ type: "token",
32
+ content: token
33
+ })
34
+ end
35
+
36
+ ActionCable.server.broadcast("ai_stream_#{stream_id}", {
37
+ type: "complete"
38
+ })
39
+ end
40
+ end
41
+ end