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
@@ -0,0 +1,391 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ module Agents
5
+ # Research Agent - Specialized in gathering and analyzing information
6
+ class ResearchAgent < BaseAgent
7
+ def initialize(name: "ResearchAgent", **opts)
8
+ super(
9
+ name: name,
10
+ role: "Research Specialist",
11
+ capabilities: [:research, :analysis, :data_gathering, :fact_checking],
12
+ **opts
13
+ )
14
+ end
15
+
16
+ def research_topic(topic, depth: :standard)
17
+ return "[stubbed] Research on #{topic}" if RailsAi.config.stub_responses
18
+
19
+ research_prompt = build_research_prompt(topic, depth)
20
+ result = think(research_prompt, context: { topic: topic, depth: depth })
21
+
22
+ remember("research_#{topic}_#{Time.now.to_i}", result, importance: :high)
23
+ result
24
+ end
25
+
26
+ def fact_check(claim)
27
+ return "[stubbed] Fact check: #{claim}" if RailsAi.config.stub_responses
28
+
29
+ fact_check_prompt = build_fact_check_prompt(claim)
30
+ result = think(fact_check_prompt, context: { claim: claim })
31
+
32
+ remember("fact_check_#{claim.hash}", result, importance: :normal)
33
+ result
34
+ end
35
+
36
+ private
37
+
38
+ def build_research_prompt(topic, depth)
39
+ depth_instructions = case depth
40
+ when :shallow
41
+ "Provide a brief overview"
42
+ when :standard
43
+ "Provide a comprehensive analysis"
44
+ when :deep
45
+ "Provide an in-depth, detailed analysis with multiple perspectives"
46
+ end
47
+
48
+ <<~PROMPT
49
+ As a research specialist, conduct #{depth_instructions} on the topic: #{topic}
50
+
51
+ Include:
52
+ - Key facts and data points
53
+ - Multiple perspectives and viewpoints
54
+ - Recent developments and trends
55
+ - Potential implications and consequences
56
+ - Sources and references where applicable
57
+
58
+ Ensure accuracy and objectivity in your research.
59
+ PROMPT
60
+ end
61
+
62
+ def build_fact_check_prompt(claim)
63
+ <<~PROMPT
64
+ As a fact-checking specialist, verify the following claim: "#{claim}"
65
+
66
+ Provide:
67
+ - Verification status (true, false, partially true, unverifiable)
68
+ - Evidence supporting or refuting the claim
69
+ - Context and nuances
70
+ - Confidence level in your assessment
71
+ - Sources used for verification
72
+
73
+ Be objective and evidence-based in your analysis.
74
+ PROMPT
75
+ end
76
+ end
77
+
78
+ # Creative Agent - Specialized in creative tasks and ideation
79
+ class CreativeAgent < BaseAgent
80
+ def initialize(name: "CreativeAgent", **opts)
81
+ super(
82
+ name: name,
83
+ role: "Creative Specialist",
84
+ capabilities: [:creative_writing, :ideation, :design_thinking, :storytelling],
85
+ **opts
86
+ )
87
+ end
88
+
89
+ def brainstorm(topic, quantity: 10)
90
+ return Array.new(quantity) { "[stubbed] Creative idea #{_1 + 1}" } if RailsAi.config.stub_responses
91
+
92
+ brainstorm_prompt = build_brainstorm_prompt(topic, quantity)
93
+ result = think(brainstorm_prompt, context: { topic: topic, quantity: quantity })
94
+
95
+ ideas = parse_ideas(result)
96
+ remember("brainstorm_#{topic}_#{Time.now.to_i}", ideas, importance: :high)
97
+ ideas
98
+ end
99
+
100
+ def write_story(prompt, genre: :general, length: :medium)
101
+ return "[stubbed] Story: #{prompt}" if RailsAi.config.stub_responses
102
+
103
+ story_prompt = build_story_prompt(prompt, genre, length)
104
+ result = think(story_prompt, context: { prompt: prompt, genre: genre, length: length })
105
+
106
+ remember("story_#{prompt.hash}", result, importance: :normal)
107
+ result
108
+ end
109
+
110
+ def design_concept(description, style: :modern)
111
+ return "[stubbed] Design concept: #{description}" if RailsAi.config.stub_responses
112
+
113
+ design_prompt = build_design_prompt(description, style)
114
+ result = think(design_prompt, context: { description: description, style: style })
115
+
116
+ remember("design_#{description.hash}", result, importance: :normal)
117
+ result
118
+ end
119
+
120
+ private
121
+
122
+ def build_brainstorm_prompt(topic, quantity)
123
+ <<~PROMPT
124
+ As a creative specialist, brainstorm #{quantity} innovative and creative ideas related to: #{topic}
125
+
126
+ Requirements:
127
+ - Be creative and think outside the box
128
+ - Ideas should be practical and implementable
129
+ - Include both conventional and unconventional approaches
130
+ - Consider different perspectives and angles
131
+ - Make each idea unique and distinct
132
+
133
+ Format your response as a numbered list of ideas.
134
+ PROMPT
135
+ end
136
+
137
+ def build_story_prompt(prompt, genre, length)
138
+ length_guidance = case length
139
+ when :short then "Write a short story (1-2 pages)"
140
+ when :medium then "Write a medium-length story (3-5 pages)"
141
+ when :long then "Write a longer story (6+ pages)"
142
+ end
143
+
144
+ <<~PROMPT
145
+ As a creative writer, #{length_guidance} based on: #{prompt}
146
+
147
+ Genre: #{genre}
148
+ Requirements:
149
+ - Engaging plot and characters
150
+ - Clear narrative structure
151
+ - Appropriate tone and style for the genre
152
+ - Creative and original content
153
+ - Well-developed dialogue and descriptions
154
+
155
+ Create an immersive and compelling story.
156
+ PROMPT
157
+ end
158
+
159
+ def build_design_prompt(description, style)
160
+ <<~PROMPT
161
+ As a design specialist, create a design concept for: #{description}
162
+
163
+ Style: #{style}
164
+ Requirements:
165
+ - Clear visual description
166
+ - Functional considerations
167
+ - Aesthetic appeal
168
+ - User experience focus
169
+ - Innovative elements
170
+
171
+ Provide a detailed design concept with visual descriptions and rationale.
172
+ PROMPT
173
+ end
174
+
175
+ def parse_ideas(result)
176
+ # Extract numbered ideas from the response
177
+ result.scan(/^\d+\.\s*(.+)$/m).flatten.map(&:strip)
178
+ end
179
+ end
180
+
181
+ # Technical Agent - Specialized in technical tasks and problem-solving
182
+ class TechnicalAgent < BaseAgent
183
+ def initialize(name: "TechnicalAgent", **opts)
184
+ super(
185
+ name: name,
186
+ role: "Technical Specialist",
187
+ capabilities: [:programming, :debugging, :system_design, :troubleshooting],
188
+ **opts
189
+ )
190
+ end
191
+
192
+ def solve_problem(problem_description, approach: :systematic)
193
+ return "[stubbed] Solution for: #{problem_description}" if RailsAi.config.stub_responses
194
+
195
+ problem_prompt = build_problem_solving_prompt(problem_description, approach)
196
+ result = think(problem_prompt, context: { problem: problem_description, approach: approach })
197
+
198
+ remember("solution_#{problem_description.hash}", result, importance: :high)
199
+ result
200
+ end
201
+
202
+ def code_review(code, language: :ruby)
203
+ return "[stubbed] Code review for #{language} code" if RailsAi.config.stub_responses
204
+
205
+ review_prompt = build_code_review_prompt(code, language)
206
+ result = think(review_prompt, context: { code: code, language: language })
207
+
208
+ remember("code_review_#{code.hash}", result, importance: :normal)
209
+ result
210
+ end
211
+
212
+ def design_system(requirements)
213
+ return "[stubbed] System design for: #{requirements}" if RailsAi.config.stub_responses
214
+
215
+ design_prompt = build_system_design_prompt(requirements)
216
+ result = think(design_prompt, context: { requirements: requirements })
217
+
218
+ remember("system_design_#{requirements.hash}", result, importance: :high)
219
+ result
220
+ end
221
+
222
+ private
223
+
224
+ def build_problem_solving_prompt(problem, approach)
225
+ approach_guidance = case approach
226
+ when :systematic
227
+ "Use a systematic, step-by-step approach"
228
+ when :creative
229
+ "Think creatively and consider unconventional solutions"
230
+ when :analytical
231
+ "Focus on data analysis and logical reasoning"
232
+ end
233
+
234
+ <<~PROMPT
235
+ As a technical specialist, solve this problem: #{problem}
236
+
237
+ Approach: #{approach_guidance}
238
+
239
+ Provide:
240
+ - Problem analysis and root cause identification
241
+ - Multiple solution options with pros/cons
242
+ - Recommended solution with implementation steps
243
+ - Risk assessment and mitigation strategies
244
+ - Testing and validation approach
245
+
246
+ Be thorough and technically sound in your analysis.
247
+ PROMPT
248
+ end
249
+
250
+ def build_code_review_prompt(code, language)
251
+ <<~PROMPT
252
+ As a technical specialist, review this #{language} code:
253
+
254
+ ```#{language}
255
+ #{code}
256
+ ```
257
+
258
+ Provide:
259
+ - Code quality assessment
260
+ - Potential bugs or issues
261
+ - Performance considerations
262
+ - Best practices adherence
263
+ - Suggestions for improvement
264
+ - Security considerations
265
+
266
+ Be constructive and specific in your feedback.
267
+ PROMPT
268
+ end
269
+
270
+ def build_system_design_prompt(requirements)
271
+ <<~PROMPT
272
+ As a technical specialist, design a system based on these requirements: #{requirements}
273
+
274
+ Provide:
275
+ - High-level architecture overview
276
+ - Component breakdown and responsibilities
277
+ - Data flow and interactions
278
+ - Technology stack recommendations
279
+ - Scalability considerations
280
+ - Security and reliability measures
281
+ - Implementation phases
282
+
283
+ Be comprehensive and consider real-world constraints.
284
+ PROMPT
285
+ end
286
+ end
287
+
288
+ # Coordinator Agent - Specialized in managing and coordinating other agents
289
+ class CoordinatorAgent < BaseAgent
290
+ def initialize(name: "CoordinatorAgent", **opts)
291
+ super(
292
+ name: name,
293
+ role: "Coordination Specialist",
294
+ capabilities: [:coordination, :project_management, :resource_allocation, :conflict_resolution],
295
+ **opts
296
+ )
297
+ end
298
+
299
+ def coordinate_task(task, available_agents)
300
+ return "[stubbed] Coordination plan for: #{task[:description]}" if RailsAi.config.stub_responses
301
+
302
+ coordination_prompt = build_coordination_prompt(task, available_agents)
303
+ result = think(coordination_prompt, context: { task: task, agents: available_agents })
304
+
305
+ remember("coordination_#{task[:id]}", result, importance: :high)
306
+ result
307
+ end
308
+
309
+ def resolve_conflict(conflict_description, involved_agents)
310
+ return "[stubbed] Conflict resolution for: #{conflict_description}" if RailsAi.config.stub_responses
311
+
312
+ conflict_prompt = build_conflict_resolution_prompt(conflict_description, involved_agents)
313
+ result = think(conflict_prompt, context: { conflict: conflict_description, agents: involved_agents })
314
+
315
+ remember("conflict_resolution_#{Time.now.to_i}", result, importance: :high)
316
+ result
317
+ end
318
+
319
+ def optimize_workflow(workflow_description, constraints)
320
+ return "[stubbed] Workflow optimization for: #{workflow_description}" if RailsAi.config.stub_responses
321
+
322
+ optimization_prompt = build_optimization_prompt(workflow_description, constraints)
323
+ result = think(optimization_prompt, context: { workflow: workflow_description, constraints: constraints })
324
+
325
+ remember("workflow_optimization_#{Time.now.to_i}", result, importance: :normal)
326
+ result
327
+ end
328
+
329
+ private
330
+
331
+ def build_coordination_prompt(task, agents)
332
+ agent_info = agents.map { |a| "#{a.name} (#{a.role}): #{a.capabilities.join(', ')}" }.join("\n")
333
+
334
+ <<~PROMPT
335
+ As a coordination specialist, create a coordination plan for this task: #{task[:description]}
336
+
337
+ Available agents:
338
+ #{agent_info}
339
+
340
+ Create a plan that:
341
+ - Assigns appropriate roles to each agent
342
+ - Defines clear responsibilities and deliverables
343
+ - Establishes communication protocols
344
+ - Sets timelines and milestones
345
+ - Identifies dependencies and coordination points
346
+ - Includes quality assurance measures
347
+
348
+ Provide a detailed coordination strategy.
349
+ PROMPT
350
+ end
351
+
352
+ def build_conflict_resolution_prompt(conflict, agents)
353
+ agent_info = agents.map { |a| "#{a.name} (#{a.role})" }.join(", ")
354
+
355
+ <<~PROMPT
356
+ As a coordination specialist, resolve this conflict: #{conflict}
357
+
358
+ Involved agents: #{agent_info}
359
+
360
+ Provide:
361
+ - Conflict analysis and root causes
362
+ - Mediation strategy
363
+ - Resolution approach
364
+ - Prevention measures
365
+ - Communication guidelines
366
+ - Follow-up procedures
367
+
368
+ Focus on fair and constructive resolution.
369
+ PROMPT
370
+ end
371
+
372
+ def build_optimization_prompt(workflow, constraints)
373
+ <<~PROMPT
374
+ As a coordination specialist, optimize this workflow: #{workflow}
375
+
376
+ Constraints: #{constraints}
377
+
378
+ Provide:
379
+ - Current workflow analysis
380
+ - Bottleneck identification
381
+ - Optimization opportunities
382
+ - Improved workflow design
383
+ - Implementation strategy
384
+ - Performance metrics
385
+
386
+ Focus on efficiency and effectiveness improvements.
387
+ PROMPT
388
+ end
389
+ end
390
+ end
391
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ module Agents
5
+ class TaskQueue
6
+ PRIORITY_LEVELS = {
7
+ critical: 4,
8
+ high: 3,
9
+ normal: 2,
10
+ low: 1
11
+ }.freeze
12
+
13
+ def initialize
14
+ @tasks = []
15
+ @total_processed = 0
16
+ @mutex = Mutex.new
17
+ end
18
+
19
+ def enqueue(task, priority: :normal)
20
+ task_with_priority = task.merge(
21
+ id: task[:id] || SecureRandom.uuid,
22
+ priority: priority,
23
+ priority_score: PRIORITY_LEVELS[priority] || 2,
24
+ enqueued_at: Time.now,
25
+ status: :pending
26
+ )
27
+
28
+ @mutex.synchronize do
29
+ @tasks << task_with_priority
30
+ @tasks.sort_by! { |t| [-t[:priority_score], t[:enqueued_at]] }
31
+ end
32
+
33
+ defined?(Rails) && Rails.logger && Rails.logger.info("Task enqueued: #{task[:description]} (priority: #{priority})")
34
+ task_with_priority
35
+ end
36
+
37
+ def dequeue(timeout: nil)
38
+ start_time = Time.now
39
+
40
+ loop do
41
+ @mutex.synchronize do
42
+ return @tasks.shift if @tasks.any?
43
+ end
44
+
45
+ if timeout && (Time.now - start_time) > timeout
46
+ return nil
47
+ end
48
+
49
+ sleep(0.1)
50
+ end
51
+ end
52
+
53
+ def peek
54
+ @mutex.synchronize { @tasks.first }
55
+ end
56
+
57
+ def size
58
+ @mutex.synchronize { @tasks.length }
59
+ end
60
+
61
+ def empty?
62
+ @mutex.synchronize { @tasks.empty? }
63
+ end
64
+
65
+ def clear!
66
+ @mutex.synchronize { @tasks.clear }
67
+ defined?(Rails) && Rails.logger && Rails.logger.info("Task queue cleared")
68
+ end
69
+
70
+ def remove_task(task_id)
71
+ @mutex.synchronize do
72
+ task = @tasks.find { |t| t[:id] == task_id }
73
+ @tasks.delete(task) if task
74
+ end
75
+ end
76
+
77
+ def get_tasks_by_status(status)
78
+ @mutex.synchronize do
79
+ @tasks.select { |t| t[:status] == status }
80
+ end
81
+ end
82
+
83
+ def get_tasks_by_priority(priority)
84
+ @mutex.synchronize do
85
+ @tasks.select { |t| t[:priority] == priority }
86
+ end
87
+ end
88
+
89
+ def mark_processed(task_id)
90
+ @total_processed += 1
91
+ defined?(Rails) && Rails.logger && Rails.logger.info("Task processed: #{task_id} (total: #{@total_processed})")
92
+ end
93
+
94
+ def stats
95
+ @mutex.synchronize do
96
+ {
97
+ total_tasks: @tasks.length,
98
+ total_processed: @total_processed,
99
+ by_priority: @tasks.group_by { |t| t[:priority] }.transform_values(&:length),
100
+ by_status: @tasks.group_by { |t| t[:status] }.transform_values(&:length),
101
+ oldest_task: @tasks.min_by { |t| t[:enqueued_at] }&.dig(:enqueued_at)
102
+ }
103
+ end
104
+ end
105
+
106
+ def total_processed
107
+ @total_processed
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ module Cache
5
+ def self.fetch(key, **opts, &block)
6
+ if defined?(Rails) && Rails.cache
7
+ Rails.cache.fetch([:rails_ai, key], {expires_in: RailsAi.config.cache_ttl}.merge(opts), &block)
8
+ elsif block_given?
9
+ # Fallback for when Rails cache is not available (e.g., in tests)
10
+ block.call
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ Config = Struct.new(
5
+ :provider,
6
+ :default_model,
7
+ :token_limit,
8
+ :cache_ttl,
9
+ :stub_responses,
10
+ :connection_pool_size,
11
+ :compression_threshold,
12
+ :batch_size,
13
+ :flush_interval,
14
+ :enable_performance_monitoring,
15
+ :enable_request_deduplication,
16
+ :enable_compression,
17
+ keyword_init: true
18
+ )
19
+
20
+ def self.config
21
+ @config ||= Config.new(
22
+ provider: :openai,
23
+ default_model: "gpt-4o-mini",
24
+ token_limit: 4000,
25
+ cache_ttl: 3600, # 1 hour in seconds
26
+ stub_responses: false,
27
+ connection_pool_size: 10,
28
+ compression_threshold: 1024, # bytes
29
+ batch_size: 10,
30
+ flush_interval: 0.1, # seconds
31
+ enable_performance_monitoring: true,
32
+ enable_request_deduplication: true,
33
+ enable_compression: true
34
+ )
35
+ end
36
+
37
+ def self.configure
38
+ yield config
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ class Context < ActiveSupport::CurrentAttributes
5
+ attribute :user_id, :request_id
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ class ContextAnalyzer
5
+ attr_reader :user_context, :window_context, :image_context
6
+
7
+ def initialize(user_context: nil, window_context: nil, image_context: nil)
8
+ @user_context = user_context || {}
9
+ @window_context = window_context || {}
10
+ @image_context = image_context || {}
11
+ end
12
+
13
+ def analyze_with_context(image, prompt, **opts)
14
+ enhanced_prompt = build_context_aware_prompt(image, prompt)
15
+
16
+ RailsAi.analyze_image(image, enhanced_prompt, **opts)
17
+ end
18
+
19
+ def generate_with_context(prompt, **opts)
20
+ enhanced_prompt = build_context_aware_prompt(nil, prompt)
21
+
22
+ RailsAi.chat(enhanced_prompt, **opts)
23
+ end
24
+
25
+ def generate_image_with_context(prompt, **opts)
26
+ enhanced_prompt = build_context_aware_prompt(nil, prompt)
27
+
28
+ RailsAi.generate_image(enhanced_prompt, **opts)
29
+ end
30
+
31
+ private
32
+
33
+ def build_context_aware_prompt(image, original_prompt)
34
+ context_parts = []
35
+
36
+ # Add user context
37
+ if user_context.any?
38
+ context_parts << "User Context: #{format_context(user_context)}"
39
+ end
40
+
41
+ # Add window/application context
42
+ if window_context.any?
43
+ context_parts << "Application Context: #{format_context(window_context)}"
44
+ end
45
+
46
+ # Add image context if analyzing an image
47
+ if image && image_context.any?
48
+ context_parts << "Image Context: #{format_context(image_context)}"
49
+ end
50
+
51
+ # Add current time and date context
52
+ context_parts << "Current Time: #{current_time}"
53
+
54
+ # Add Rails environment context
55
+ context_parts << "Environment: #{Rails.env}" if defined?(Rails)
56
+
57
+ # Combine all context with the original prompt
58
+ if context_parts.any?
59
+ "#{context_parts.join('\n\n')}\n\nOriginal Request: #{original_prompt}"
60
+ else
61
+ original_prompt
62
+ end
63
+ end
64
+
65
+ def format_context(context_hash)
66
+ context_hash.map do |key, value|
67
+ case value
68
+ when Hash
69
+ "#{key}: #{format_context(value)}"
70
+ when Array
71
+ "#{key}: #{value.join(', ')}"
72
+ else
73
+ "#{key}: #{value}"
74
+ end
75
+ end.join('\n')
76
+ end
77
+
78
+ def current_time
79
+ if defined?(Time.current)
80
+ Time.current.strftime('%Y-%m-%d %H:%M:%S %Z')
81
+ else
82
+ Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAi
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace RailsAi
6
+
7
+ # Rails version compatibility
8
+ if Rails.version >= "8.0"
9
+ # Rails 8 specific configuration
10
+ config.generators do |g|
11
+ g.test_framework :rspec
12
+ g.fixture_replacement :factory_bot
13
+ g.orm :active_record
14
+ end
15
+ elsif Rails.version >= "7.0"
16
+ # Rails 7 specific configuration
17
+ config.generators do |g|
18
+ g.test_framework :rspec
19
+ g.fixture_replacement :factory_bot
20
+ end
21
+
22
+ # Importmap support for Rails 7+
23
+ initializer "rails_ai.importmap", before: "importmap" do |app|
24
+ app.config.importmap.paths << root.join("app/assets/javascripts")
25
+ end
26
+ else
27
+ # Rails 5.2+ and 6.x configuration
28
+ config.generators do |g|
29
+ g.test_framework :rspec
30
+ g.fixture_replacement :factory_bot
31
+ end
32
+ end
33
+
34
+ # Asset precompilation (compatible with all Rails versions)
35
+ initializer "rails_ai.assets.precompile" do |app|
36
+ if app.config.respond_to?(:assets)
37
+ app.config.assets.precompile += %w[rails_ai_manifest.js]
38
+ end
39
+ end
40
+
41
+ # Routes configuration
42
+ initializer "rails_ai.routes" do |app|
43
+ app.routes.prepend do
44
+ mount RailsAi::Engine, at: "/rails_ai"
45
+ end
46
+ end
47
+ end
48
+ end