Module: RailsAi

Defined in:
lib/rails_ai.rb,
lib/rails_ai/cache.rb,
lib/rails_ai/config.rb,
lib/rails_ai/engine.rb,
lib/rails_ai/events.rb,
lib/rails_ai/context.rb,
lib/rails_ai/railtie.rb,
lib/rails_ai/version.rb,
lib/rails_ai/provider.rb,
lib/rails_ai/redactor.rb,
lib/rails_ai/security.rb,
lib/rails_ai/web_search.rb,
lib/rails_ai/performance.rb,
lib/rails_ai/agents/memory.rb,
lib/rails_ai/image_context.rb,
lib/rails_ai/providers/base.rb,
lib/rails_ai/window_context.rb,
lib/rails_ai/context_analyzer.rb,
app/channels/ai_stream_channel.rb,
lib/rails_ai/agents/agent_team.rb,
lib/rails_ai/agents/base_agent.rb,
lib/rails_ai/agents/task_queue.rb,
lib/rails_ai/agents/message_bus.rb,
app/jobs/ai/generate_summary_job.rb,
app/models/concerns/ai/embeddable.rb,
lib/rails_ai/agents/agent_manager.rb,
lib/rails_ai/agents/collaboration.rb,
app/components/ai/prompt_component.rb,
app/jobs/ai/generate_embedding_job.rb,
lib/rails_ai/security/audit_logger.rb,
lib/rails_ai/security/error_handler.rb,
lib/rails_ai/providers/dummy_adapter.rb,
app/controllers/concerns/ai/streaming.rb,
lib/rails_ai/providers/gemini_adapter.rb,
lib/rails_ai/providers/openai_adapter.rb,
lib/rails_ai/security/api_key_manager.rb,
lib/rails_ai/security/input_validator.rb,
lib/rails_ai/agents/specialized_agents.rb,
lib/rails_ai/providers/anthropic_adapter.rb,
lib/rails_ai/security/secure_http_client.rb,
app/controllers/concerns/ai/context_aware.rb,
lib/rails_ai/security/secure_file_handler.rb,
lib/rails_ai/providers/secure_openai_adapter.rb,
lib/rails_ai/providers/secure_anthropic_adapter.rb,
lib/generators/rails_ai/install/install_generator.rb

Defined Under Namespace

Modules: Agents, Cache, ContextAware, Embeddable, Events, Generators, Performance, Providers, Redactor, Security, Streaming, WebSearch Classes: AiStreamChannel, Config, Context, ContextAnalyzer, Engine, GenerateEmbeddingJob, GenerateSummaryJob, ImageContext, PromptComponent, Provider, Railtie, WindowContext

Constant Summary collapse

VERSION =
"0.2.8"

Class Method Summary collapse

Class Method Details

.agent_managerObject



98
99
100
# File 'lib/rails_ai.rb', line 98

def agent_manager
  @agent_manager ||= Agents::AgentManager.new
end

.analyze_audio(audio, prompt, model: "whisper-1", **opts) ⇒ Object



165
166
167
168
169
# File 'lib/rails_ai.rb', line 165

def analyze_audio(audio, prompt, model: "whisper-1", **opts)
  performance_monitor.measure(:analyze_audio) do
    provider.analyze_audio!(audio: audio, prompt: prompt, model: model, **opts)
  end
end

.analyze_image(image, prompt, model: "gpt-4o", **opts) ⇒ Object

Multimodal capabilities



141
142
143
144
145
# File 'lib/rails_ai.rb', line 141

def analyze_image(image, prompt, model: "gpt-4o", **opts)
  performance_monitor.measure(:analyze_image) do
    provider.analyze_image!(image: image, prompt: prompt, model: model, **opts)
  end
end

.analyze_image_context(image, **opts) ⇒ Object



193
194
195
196
197
198
# File 'lib/rails_ai.rb', line 193

def analyze_image_context(image, **opts)
  performance_monitor.measure(:analyze_image_context) do
    image_context = ImageContext.new(image)
    image_context.analyze(**opts)
  end
end

.analyze_video(video, prompt, model: "gpt-4o", **opts) ⇒ Object



153
154
155
156
157
# File 'lib/rails_ai.rb', line 153

def analyze_video(video, prompt, model: "gpt-4o", **opts)
  performance_monitor.measure(:analyze_video) do
    provider.analyze_video!(video: video, prompt: prompt, model: model, **opts)
  end
end

.analyze_window_context(url, referrer, user_agent, **opts) ⇒ Object



186
187
188
189
190
191
# File 'lib/rails_ai.rb', line 186

def analyze_window_context(url, referrer, user_agent, **opts)
  performance_monitor.measure(:analyze_window_context) do
    window_context = WindowContext.new(url: url, referrer: referrer, user_agent: user_agent)
    window_context.analyze(**opts)
  end
end

.batch_processorObject



77
78
79
80
81
82
83
84
# File 'lib/rails_ai.rb', line 77

def batch_processor
  @batch_processor ||= Concurrent::ThreadPoolExecutor.new(
    min_threads: 1,
    max_threads: 5,
    max_queue: 50,
    auto_terminate: true
  )
end

.chat(prompt_or_messages, model: config.default_model, **opts) ⇒ Object

Core AI methods



103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rails_ai.rb', line 103

def chat(prompt_or_messages, model: config.default_model, **opts)
  performance_monitor.measure(:chat) do
    messages = normalize_messages(prompt_or_messages)
    cache_key = [:chat, model, messages.hash]
    
    smart_cache.fetch(cache_key, expires_in: config.cache_ttl) do
      request_deduplicator.deduplicate(cache_key) do
        provider.chat!(messages: messages, model: model, **opts)
      end
    end
  end
end

.chat_clean(prompt_or_messages, model: config.default_model, **opts) ⇒ Object

Enhanced chat method with automatic response cleaning



299
300
301
302
# File 'lib/rails_ai.rb', line 299

def chat_clean(prompt_or_messages, model: config.default_model, **opts)
  raw_response = chat(prompt_or_messages, model: model, **opts)
  clean_response(raw_response)
end

.chat_with_context(prompt, context_objects = [], model: config.default_model, **opts) ⇒ Object

Context awareness



178
179
180
181
182
183
184
# File 'lib/rails_ai.rb', line 178

def chat_with_context(prompt, context_objects = [], model: config.default_model, **opts)
  performance_monitor.measure(:chat_with_context) do
    context_analyzer = ContextAnalyzer.new
    enhanced_prompt = context_analyzer.enhance_prompt(prompt, context_objects)
    chat(enhanced_prompt, model: model, **opts)
  end
end

.chat_with_web_search(prompt, model: RailsAi.config.default_model, **opts) ⇒ Object

Web-enhanced chat with real-time information



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/rails_ai.rb', line 315

def RailsAi.chat_with_web_search(prompt, model: RailsAi.config.default_model, **opts)
  # Check if the prompt needs web search
  web_keywords = ['current', 'latest', 'today', 'now', 'recent', 'weather', 'news', 'stock', 'price']
  needs_web_search = web_keywords.any? { |keyword| prompt.downcase.include?(keyword) }
  
  if needs_web_search
    begin
      # Perform web search
      search_results = WebSearch.search(prompt, num_results: 3)
      
      # Enhance the prompt with web results
      web_context = "\n\nRecent web search results:\n"
      search_results.each_with_index do |result, index|
        web_context += "#{index + 1}. #{result[:title]}\n   #{result[:snippet]}\n   Source: #{result[:link]}\n\n"
      end
      
      enhanced_prompt = "#{prompt}\n\nPlease use the following web search results to provide current, up-to-date information:#{web_context}"
      
      # Get AI response with web context
      RailsAi.chat(enhanced_prompt, model: model, **opts)
    rescue WebSearch::SearchError => e
      # Fallback to regular chat if web search fails
      RailsAi.chat(prompt, model: model, **opts)
    end
  else
    # Regular chat for non-time-sensitive queries
    RailsAi.chat(prompt, model: model, **opts)
  end
end

.chat_with_web_search_clean(prompt, model: config.default_model, **opts) ⇒ Object

Enhanced web search chat with automatic response cleaning



305
306
307
308
# File 'lib/rails_ai.rb', line 305

def chat_with_web_search_clean(prompt, model: config.default_model, **opts)
  raw_response = chat_with_web_search(prompt, model: model, **opts)
  clean_response(raw_response)
end

.check_rate_limit(identifier, limit: 100, window: 1.hour) ⇒ Object



270
271
272
# File 'lib/rails_ai.rb', line 270

def check_rate_limit(identifier, limit: 100, window: 1.hour)
  Security::RateLimiter.check(identifier, limit: limit, window: window)
end

.clean_response(raw_response) ⇒ Object

Response cleaning utility



283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/rails_ai.rb', line 283

def clean_response(raw_response)
  return nil if raw_response.nil?

  # Convert to string
  response = raw_response.to_s
  
  # Ensure UTF-8 encoding
  response = response.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '?')
  
  # Remove any control characters that might cause issues
  response = response.gsub(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/, '')
  
  response
end

.configObject

Configuration



59
60
61
# File 'lib/rails_ai.rb', line 59

def config
  @config ||= Config.new
end

.configure {|config| ... } ⇒ Object

Yields:



63
64
65
# File 'lib/rails_ai.rb', line 63

def configure
  yield(config)
end

.connection_poolObject

Performance optimizations



68
69
70
71
72
73
74
75
# File 'lib/rails_ai.rb', line 68

def connection_pool
  @connection_pool ||= Concurrent::ThreadPoolExecutor.new(
    min_threads: 2,
    max_threads: 10,
    max_queue: 100,
    auto_terminate: true
  )
end

.create_agent(name, type: :general, **opts) ⇒ Object

Agent system



201
202
203
# File 'lib/rails_ai.rb', line 201

def create_agent(name, type: :general, **opts)
  agent_manager.create_agent(name, type: type, **opts)
end

.create_agent_team(name, agents, strategy: :round_robin, **opts) ⇒ Object



217
218
219
# File 'lib/rails_ai.rb', line 217

def create_agent_team(name, agents, strategy: :round_robin, **opts)
  agent_manager.create_agent_team(name, agents, collaboration_strategy: strategy, **opts)
end

.create_creative_agent(name: "Creative Agent", **opts) ⇒ Object



209
210
211
# File 'lib/rails_ai.rb', line 209

def create_creative_agent(name: "Creative Agent", **opts)
  agent_manager.create_agent(name, type: :creative, **opts)
end

.create_research_agent(name: "Research Agent", **opts) ⇒ Object



205
206
207
# File 'lib/rails_ai.rb', line 205

def create_research_agent(name: "Research Agent", **opts)
  agent_manager.create_agent(name, type: :research, **opts)
end

.create_technical_agent(name: "Technical Agent", **opts) ⇒ Object



213
214
215
# File 'lib/rails_ai.rb', line 213

def create_technical_agent(name: "Technical Agent", **opts)
  agent_manager.create_agent(name, type: :technical, **opts)
end

.embed(texts, model: "text-embedding-3-small", **opts) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/rails_ai.rb', line 127

def embed(texts, model: "text-embedding-3-small", **opts)
  performance_monitor.measure(:embed) do
    texts_array = Array(texts)
    cache_key = [:embed, model, texts_array.hash]
    
    smart_cache.fetch(cache_key, expires_in: config.cache_ttl) do
      request_deduplicator.deduplicate(cache_key) do
        provider.embed!(texts: texts_array, model: model, **opts)
      end
    end
  end
end

.generate_audio(prompt, model: "tts-1", **opts) ⇒ Object



171
172
173
174
175
# File 'lib/rails_ai.rb', line 171

def generate_audio(prompt, model: "tts-1", **opts)
  performance_monitor.measure(:generate_audio) do
    provider.generate_audio!(prompt: prompt, model: model, **opts)
  end
end

.generate_image(prompt, model: "dall-e-3", **opts) ⇒ Object



147
148
149
150
151
# File 'lib/rails_ai.rb', line 147

def generate_image(prompt, model: "dall-e-3", **opts)
  performance_monitor.measure(:generate_image) do
    provider.generate_image!(prompt: prompt, model: model, **opts)
  end
end

.generate_video(prompt, model: "runway-gen-3", **opts) ⇒ Object



159
160
161
162
163
# File 'lib/rails_ai.rb', line 159

def generate_video(prompt, model: "runway-gen-3", **opts)
  performance_monitor.measure(:generate_video) do
    provider.generate_video!(prompt: prompt, model: model, **opts)
  end
end

.handle_security_error(error, context = {}) ⇒ Object



278
279
280
# File 'lib/rails_ai.rb', line 278

def handle_security_error(error, context = {})
  Security::ErrorHandler.handle_security_error(error, context)
end

.log_event(kind:, name:, payload: {}, latency_ms: nil) ⇒ Object



257
258
259
# File 'lib/rails_ai.rb', line 257

def log_event(kind:, name:, payload: {}, latency_ms: nil)
  Events.log!(kind: kind, name: name, payload: payload, latency_ms: latency_ms)
end

.normalize_messages(prompt_or_messages) ⇒ Object

Utility methods



245
246
247
248
249
250
251
# File 'lib/rails_ai.rb', line 245

def normalize_messages(prompt_or_messages)
  if prompt_or_messages.is_a?(String)
    [{ role: "user", content: prompt_or_messages }]
  else
    prompt_or_messages
  end
end

.performance_monitorObject



94
95
96
# File 'lib/rails_ai.rb', line 94

def performance_monitor
  @performance_monitor ||= Performance::PerformanceMonitor.new
end

.providerObject

Provider management



230
231
232
233
234
235
236
237
238
# File 'lib/rails_ai.rb', line 230

def provider
  case config.provider.to_sym
  when :openai then Providers::SecureOpenAIAdapter.new
  when :anthropic then Providers::SecureAnthropicAdapter.new
  when :gemini then Providers::GeminiAdapter.new
  when :dummy then Providers::DummyAdapter.new
  else Providers::DummyAdapter.new
  end
end

.provider=(new_provider) ⇒ Object



240
241
242
# File 'lib/rails_ai.rb', line 240

def provider=(new_provider)
  config.provider = new_provider
end

.rails_versionObject

Version compatibility helpers



50
51
52
# File 'lib/rails_ai.rb', line 50

def rails_version
  Rails.version.to_f
end

.redact_sensitive_data(text) ⇒ Object



253
254
255
# File 'lib/rails_ai.rb', line 253

def redact_sensitive_data(text)
  Redactor.call(text)
end

.request_deduplicatorObject



90
91
92
# File 'lib/rails_ai.rb', line 90

def request_deduplicator
  @request_deduplicator ||= Performance::RequestDeduplicator.new
end

.ruby_versionObject



54
55
56
# File 'lib/rails_ai.rb', line 54

def ruby_version
  RUBY_VERSION.to_f
end

.sanitize_content(content) ⇒ Object



266
267
268
# File 'lib/rails_ai.rb', line 266

def sanitize_content(content)
  Security::ContentSanitizer.sanitize(content)
end

.scan_for_vulnerabilitiesObject



274
275
276
# File 'lib/rails_ai.rb', line 274

def scan_for_vulnerabilities
  Security::VulnerabilityScanner.scan
end

.smart_cacheObject



86
87
88
# File 'lib/rails_ai.rb', line 86

def smart_cache
  @smart_cache ||= Cache
end

.stream(prompt_or_messages, model: config.default_model, **opts, &block) ⇒ Object



116
117
118
119
120
121
122
123
124
125
# File 'lib/rails_ai.rb', line 116

def stream(prompt_or_messages, model: config.default_model, **opts, &block)
  performance_monitor.measure(:stream) do
    messages = normalize_messages(prompt_or_messages)
    
    # Use connection pool for streaming
    connection_pool.with_connection do |connection|
      provider.stream_chat!(messages: messages, model: model, **opts, &block)
    end
  end
end

.submit_task(task, agent_team = nil) ⇒ Object



221
222
223
224
225
226
227
# File 'lib/rails_ai.rb', line 221

def submit_task(task, agent_team = nil)
  if agent_team
    agent_team.assign_task(task)
  else
    agent_manager.submit_task(task)
  end
end

.validate_input(input, type: :text) ⇒ Object

Security methods



262
263
264
# File 'lib/rails_ai.rb', line 262

def validate_input(input, type: :text)
  Security::InputValidator.validate(input, type: type)
end