rails_ai 0.1.6 → 0.1.8
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 +4 -4
- data/lib/rails_ai/version.rb +1 -1
- data/lib/rails_ai/web_search.rb +115 -0
- data/lib/rails_ai.rb +32 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 909d277e3d7288cb423211be454525d2ba2137aa17eeb437ee1c7df4f1f68d51
|
4
|
+
data.tar.gz: 7c7b3597e7cae56b30d6b9f24206a8c0658d36a12021cbf231ffac8cc559c588
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fb52882e68fbccb0d87a6ab9539c76d63de7e95476bb1eb255637da2450069fdc12d8e949370b0fc2992b1853a01b11413c9df59a855f9818631d591b0666d8
|
7
|
+
data.tar.gz: 2c2767b19fdd5979fce2f1f5c594929ae02dad406658a189095b2dc62c48acb5fbb692a5ea1c56acd3dc2a1ccbf8ad40c8323b0ced1a131b5ad13bdc4141dc17
|
data/lib/rails_ai/version.rb
CHANGED
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module RailsAi
|
8
|
+
module WebSearch
|
9
|
+
class SearchError < StandardError; end
|
10
|
+
|
11
|
+
class GoogleSearch
|
12
|
+
def initialize(api_key: nil, search_engine_id: nil)
|
13
|
+
@api_key = api_key || ENV['GOOGLE_SEARCH_API_KEY']
|
14
|
+
@search_engine_id = search_engine_id || ENV['GOOGLE_SEARCH_ENGINE_ID']
|
15
|
+
@base_url = 'https://www.googleapis.com/customsearch/v1'
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(query, num_results: 5)
|
19
|
+
raise SearchError, "Google Search API key not configured" unless @api_key
|
20
|
+
raise SearchError, "Google Search Engine ID not configured" unless @search_engine_id
|
21
|
+
|
22
|
+
uri = URI(@base_url)
|
23
|
+
params = {
|
24
|
+
key: @api_key,
|
25
|
+
cx: @search_engine_id,
|
26
|
+
q: query,
|
27
|
+
num: num_results
|
28
|
+
}
|
29
|
+
uri.query = URI.encode_www_form(params)
|
30
|
+
|
31
|
+
response = Net::HTTP.get_response(uri)
|
32
|
+
raise SearchError, "Search failed: #{response.code}" unless response.code == '200'
|
33
|
+
|
34
|
+
data = JSON.parse(response.body)
|
35
|
+
format_results(data)
|
36
|
+
rescue => e
|
37
|
+
raise SearchError, "Web search error: #{e.message}"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def format_results(data)
|
43
|
+
results = data['items'] || []
|
44
|
+
results.map do |item|
|
45
|
+
{
|
46
|
+
title: item['title'],
|
47
|
+
link: item['link'],
|
48
|
+
snippet: item['snippet']
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class DuckDuckGoSearch
|
55
|
+
def search(query, num_results: 5)
|
56
|
+
# Simple web scraping approach (for demo purposes)
|
57
|
+
# In production, you'd want to use a proper API
|
58
|
+
uri = URI("https://html.duckduckgo.com/html/?q=#{URI.encode_www_form_component(query)}")
|
59
|
+
|
60
|
+
response = Net::HTTP.get_response(uri)
|
61
|
+
raise SearchError, "Search failed: #{response.code}" unless response.code == '200'
|
62
|
+
|
63
|
+
# Parse HTML and extract results (simplified)
|
64
|
+
parse_duckduckgo_results(response.body, num_results)
|
65
|
+
rescue => e
|
66
|
+
raise SearchError, "Web search error: #{e.message}"
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def parse_duckduckgo_results(html, num_results)
|
72
|
+
# This is a simplified parser - in production you'd use Nokogiri
|
73
|
+
results = []
|
74
|
+
lines = html.split("\n")
|
75
|
+
|
76
|
+
lines.each_with_index do |line, index|
|
77
|
+
if line.include?('class="result__title"') && results.length < num_results
|
78
|
+
title_line = lines[index + 1] rescue ""
|
79
|
+
snippet_line = lines[index + 3] rescue ""
|
80
|
+
|
81
|
+
results << {
|
82
|
+
title: title_line.strip,
|
83
|
+
link: "https://duckduckgo.com",
|
84
|
+
snippet: snippet_line.strip
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
results
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class WebSearch
|
94
|
+
def initialize(provider: :google, **options)
|
95
|
+
@provider = case provider
|
96
|
+
when :google
|
97
|
+
GoogleSearch.new(**options)
|
98
|
+
when :duckduckgo
|
99
|
+
DuckDuckGoSearch.new(**options)
|
100
|
+
else
|
101
|
+
raise SearchError, "Unsupported search provider: #{provider}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def search(query, **options)
|
106
|
+
@provider.search(query, **options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Convenience method
|
111
|
+
def self.search(query, provider: :google, **options)
|
112
|
+
WebSearch.new(provider: provider, **options).search(query)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/rails_ai.rb
CHANGED
@@ -500,3 +500,35 @@ end
|
|
500
500
|
def self.handle_security_error(error, context = {})
|
501
501
|
Security::ErrorHandler.handle_security_error(error, context)
|
502
502
|
end
|
503
|
+
require_relative "rails_ai/web_search"
|
504
|
+
|
505
|
+
# Web-enhanced chat with real-time information
|
506
|
+
def self.chat_with_web_search(prompt, model: config.default_model, **opts)
|
507
|
+
# Check if the prompt needs web search
|
508
|
+
web_keywords = ['current', 'latest', 'today', 'now', 'recent', 'weather', 'news', 'stock', 'price']
|
509
|
+
needs_web_search = web_keywords.any? { |keyword| prompt.downcase.include?(keyword) }
|
510
|
+
|
511
|
+
if needs_web_search
|
512
|
+
begin
|
513
|
+
# Perform web search
|
514
|
+
search_results = WebSearch.search(prompt, num_results: 3)
|
515
|
+
|
516
|
+
# Enhance the prompt with web results
|
517
|
+
web_context = "\n\nRecent web search results:\n"
|
518
|
+
search_results.each_with_index do |result, index|
|
519
|
+
web_context += "#{index + 1}. #{result[:title]}\n #{result[:snippet]}\n Source: #{result[:link]}\n\n"
|
520
|
+
end
|
521
|
+
|
522
|
+
enhanced_prompt = "#{prompt}\n\nPlease use the following web search results to provide current, up-to-date information:#{web_context}"
|
523
|
+
|
524
|
+
# Get AI response with web context
|
525
|
+
chat(enhanced_prompt, model: model, **opts)
|
526
|
+
rescue WebSearch::SearchError => e
|
527
|
+
# Fallback to regular chat if web search fails
|
528
|
+
chat(prompt, model: model, **opts)
|
529
|
+
end
|
530
|
+
else
|
531
|
+
# Regular chat for non-time-sensitive queries
|
532
|
+
chat(prompt, model: model, **opts)
|
533
|
+
end
|
534
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Amah
|
@@ -339,6 +339,7 @@ files:
|
|
339
339
|
- lib/rails_ai/security/secure_file_handler.rb
|
340
340
|
- lib/rails_ai/security/secure_http_client.rb
|
341
341
|
- lib/rails_ai/version.rb
|
342
|
+
- lib/rails_ai/web_search.rb
|
342
343
|
- lib/rails_ai/window_context.rb
|
343
344
|
- monitoring/ci_setup_guide.md
|
344
345
|
- monitoring/enhanced_monitoring_script.rb
|