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.
- checksums.yaml +7 -0
- data/.rspec_status +96 -0
- data/AGENT_GUIDE.md +513 -0
- data/Appraisals +49 -0
- data/COMMERCIAL_LICENSE_TEMPLATE.md +92 -0
- data/FEATURES.md +204 -0
- data/LEGAL_PROTECTION_GUIDE.md +222 -0
- data/LICENSE +62 -0
- data/LICENSE_SUMMARY.md +74 -0
- data/MIT-LICENSE +62 -0
- data/PERFORMANCE.md +300 -0
- data/PROVIDERS.md +495 -0
- data/README.md +454 -0
- data/Rakefile +11 -0
- data/SPEED_OPTIMIZATIONS.md +217 -0
- data/STRUCTURE.md +139 -0
- data/USAGE_GUIDE.md +288 -0
- data/app/channels/ai_stream_channel.rb +33 -0
- data/app/components/ai/prompt_component.rb +25 -0
- data/app/controllers/concerns/ai/context_aware.rb +77 -0
- data/app/controllers/concerns/ai/streaming.rb +41 -0
- data/app/helpers/ai_helper.rb +164 -0
- data/app/jobs/ai/generate_embedding_job.rb +25 -0
- data/app/jobs/ai/generate_summary_job.rb +25 -0
- data/app/models/concerns/ai/embeddable.rb +38 -0
- data/app/views/rails_ai/dashboard/index.html.erb +51 -0
- data/config/routes.rb +19 -0
- data/lib/generators/rails_ai/install/install_generator.rb +38 -0
- data/lib/rails_ai/agents/agent_manager.rb +258 -0
- data/lib/rails_ai/agents/agent_team.rb +243 -0
- data/lib/rails_ai/agents/base_agent.rb +331 -0
- data/lib/rails_ai/agents/collaboration.rb +238 -0
- data/lib/rails_ai/agents/memory.rb +116 -0
- data/lib/rails_ai/agents/message_bus.rb +95 -0
- data/lib/rails_ai/agents/specialized_agents.rb +391 -0
- data/lib/rails_ai/agents/task_queue.rb +111 -0
- data/lib/rails_ai/cache.rb +14 -0
- data/lib/rails_ai/config.rb +40 -0
- data/lib/rails_ai/context.rb +7 -0
- data/lib/rails_ai/context_analyzer.rb +86 -0
- data/lib/rails_ai/engine.rb +48 -0
- data/lib/rails_ai/events.rb +9 -0
- data/lib/rails_ai/image_context.rb +110 -0
- data/lib/rails_ai/performance.rb +231 -0
- data/lib/rails_ai/provider.rb +8 -0
- data/lib/rails_ai/providers/anthropic_adapter.rb +256 -0
- data/lib/rails_ai/providers/base.rb +60 -0
- data/lib/rails_ai/providers/dummy_adapter.rb +29 -0
- data/lib/rails_ai/providers/gemini_adapter.rb +509 -0
- data/lib/rails_ai/providers/openai_adapter.rb +535 -0
- data/lib/rails_ai/providers/secure_anthropic_adapter.rb +206 -0
- data/lib/rails_ai/providers/secure_openai_adapter.rb +284 -0
- data/lib/rails_ai/railtie.rb +48 -0
- data/lib/rails_ai/redactor.rb +12 -0
- data/lib/rails_ai/security/api_key_manager.rb +82 -0
- data/lib/rails_ai/security/audit_logger.rb +46 -0
- data/lib/rails_ai/security/error_handler.rb +62 -0
- data/lib/rails_ai/security/input_validator.rb +176 -0
- data/lib/rails_ai/security/secure_file_handler.rb +45 -0
- data/lib/rails_ai/security/secure_http_client.rb +177 -0
- data/lib/rails_ai/security.rb +0 -0
- data/lib/rails_ai/version.rb +5 -0
- data/lib/rails_ai/window_context.rb +103 -0
- data/lib/rails_ai.rb +502 -0
- data/monitoring/ci_setup_guide.md +214 -0
- data/monitoring/enhanced_monitoring_script.rb +237 -0
- data/monitoring/google_alerts_setup.md +42 -0
- data/monitoring_log_20250921.txt +0 -0
- data/monitoring_script.rb +161 -0
- data/rails_ai.gemspec +54 -0
- data/scripts/security_scanner.rb +353 -0
- data/setup_monitoring.sh +163 -0
- data/wiki/API-Documentation.md +734 -0
- data/wiki/Architecture-Overview.md +672 -0
- data/wiki/Contributing-Guide.md +407 -0
- data/wiki/Development-Setup.md +532 -0
- data/wiki/Home.md +278 -0
- data/wiki/Installation-Guide.md +527 -0
- data/wiki/Quick-Start.md +186 -0
- data/wiki/README.md +135 -0
- data/wiki/Release-Process.md +467 -0
- 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
|