smart_rag 0.1.0 → 0.2.1
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/.env.example +252 -0
- data/.rspec +2 -0
- data/AGENTS.md +33 -0
- data/API_DOCUMENTATION.md +828 -0
- data/CHANGELOG.md +16 -1
- data/ER-diagram.mmd +144 -0
- data/Gemfile +50 -0
- data/Gemfile.lock +398 -0
- data/Hybrid_Reranking.md +171 -0
- data/README.en.md +420 -28
- data/README.md +534 -63
- data/Rakefile +268 -0
- data/SETUP_GUIDE.md +650 -0
- data/SmartChunking.md +180 -0
- data/USAGE_EXAMPLES.md +1002 -0
- data/config/llm_config.yml +4 -2
- data/config/smart_rag.yml +45 -1
- data/config.ru +15 -0
- data/db/migrations/006_create_text_search_configs.rb +3 -2
- data/db/migrations/008_create_embeddings.rb +5 -4
- data/db/migrations/012_add_metadata_to_source_sections.rb +11 -0
- data/db/migrations/013_create_media_jobs.rb +25 -0
- data/db/migrations/014_add_media_job_operations_indexes.rb +11 -0
- data/db/migrations/015_add_media_leases_and_objects.rb +80 -0
- data/db/migrations/016_add_document_principals_and_staging_references.rb +38 -0
- data/db/migrations/017_add_media_job_request_fingerprint.rb +48 -0
- data/db/seeds/text_search_configs.sql +3 -3
- data/design.md +1057 -0
- data/docs/API_DOCUMENTATION.md +838 -0
- data/docs/DOCUMENTATION_INDEX.en.md +60 -0
- data/docs/DOCUMENTATION_INDEX.md +65 -0
- data/docs/FIX_SUMMARY.md +256 -0
- data/docs/FIX_SUMMARY_COMPLETE.md +273 -0
- data/docs/Hybrid_Reranking.md +171 -0
- data/docs/MIGRATION_GUIDE.md +151 -0
- data/docs/PERFORMANCE_GUIDE.md +58 -0
- data/docs/SETUP_GUIDE.md +659 -0
- data/docs/SmartChunking.md +180 -0
- data/docs/USAGE_EXAMPLES.md +1008 -0
- data/docs/design.md +1057 -0
- data/docs/evidence_pack.md +211 -0
- data/docs/requirements.md +376 -0
- data/docs/retrieval_plan.md +251 -0
- data/docs/smartrag_improvement_plan.md +201 -0
- data/docs/smartrag_refactor.md +216 -0
- data/docs/todo.md +931 -0
- data/examples/common.rb +1 -1
- data/exe/smart-rag-db +163 -0
- data/exe/smart-rag-media-worker +34 -0
- data/lib/smart_rag/config.rb +12 -0
- data/lib/smart_rag/core/document_processor.rb +80 -16
- data/lib/smart_rag/core/local_content_store.rb +51 -0
- data/lib/smart_rag/core/media_extractors.rb +140 -0
- data/lib/smart_rag/core/media_job_queue.rb +353 -0
- data/lib/smart_rag/core/media_metadata_extractor.rb +188 -0
- data/lib/smart_rag/core/media_object_registry.rb +79 -0
- data/lib/smart_rag/core/media_processor.rb +228 -0
- data/lib/smart_rag/core/media_safety_policy.rb +61 -0
- data/lib/smart_rag/core/s3_content_store.rb +78 -0
- data/lib/smart_rag/core/transcript_normalizer.rb +44 -0
- data/lib/smart_rag/core/video_semantic_extractor.rb +130 -0
- data/lib/smart_rag/http_access_policy.rb +86 -0
- data/lib/smart_rag/http_app.rb +188 -0
- data/lib/smart_rag/models/embedding.rb +1 -1
- data/lib/smart_rag/models/research_topic.rb +1 -1
- data/lib/smart_rag/models/research_topic_section.rb +5 -0
- data/lib/smart_rag/models/research_topic_tag.rb +5 -0
- data/lib/smart_rag/models/search_log.rb +1 -1
- data/lib/smart_rag/models/section_fts.rb +5 -0
- data/lib/smart_rag/models/section_tag.rb +5 -0
- data/lib/smart_rag/models/source_document.rb +1 -1
- data/lib/smart_rag/models/source_section.rb +1 -1
- data/lib/smart_rag/models/tag.rb +1 -1
- data/lib/smart_rag/models/text_search_config.rb +5 -0
- data/lib/smart_rag/retrieve.rb +72 -1
- data/lib/smart_rag/services/embedding_service.rb +1 -1
- data/lib/smart_rag/services/fulltext_search_service.rb +11 -13
- data/lib/smart_rag/services/hybrid_search_service.rb +15 -11
- data/lib/smart_rag/services/summarization_service.rb +1 -1
- data/lib/smart_rag/services/tag_service.rb +1 -1
- data/lib/smart_rag/version.rb +1 -1
- data/lib/smart_rag.rb +264 -30
- data/patch_language.rb +27 -0
- data/requirements.md +376 -0
- data/source_documents_export.json +11072 -0
- data/todo.md +931 -0
- data/workers/analyze_content.rb +6 -2
- data/workers/get_embedding.rb +1 -1
- metadata +151 -38
data/Rakefile
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Remove bundler/gem_tasks to avoid conflicts
|
|
2
|
+
|
|
3
|
+
# Load environment variables from .env file if it exists
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require "dotenv"
|
|
7
|
+
Dotenv.load
|
|
8
|
+
puts "Loaded environment variables from .env file"
|
|
9
|
+
rescue LoadError
|
|
10
|
+
# dotenv not available, skip loading
|
|
11
|
+
puts "Note: dotenv gem not found. Install with: gem install dotenv"
|
|
12
|
+
rescue => e
|
|
13
|
+
puts "Warning: Could not load .env file: #{e.message}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require "rbconfig"
|
|
17
|
+
|
|
18
|
+
def windows?
|
|
19
|
+
/mswin|mingw|cygwin/i.match?(RbConfig::CONFIG["host_os"])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def running_as_root?
|
|
23
|
+
Process.respond_to?(:uid) && Process.uid == 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_db_config
|
|
27
|
+
require_relative "lib/smart_rag/config"
|
|
28
|
+
SmartRAG::Config.load[:database]
|
|
29
|
+
rescue => e
|
|
30
|
+
puts "Warning: Could not load config: #{e.message}"
|
|
31
|
+
puts "Using environment variables instead..."
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
adapter: "postgresql",
|
|
35
|
+
host: ENV["SMARTRAG_DB_HOST"] || "localhost",
|
|
36
|
+
port: ENV["SMARTRAG_DB_PORT"] || 5432,
|
|
37
|
+
database: ENV["SMARTRAG_DB_NAME"] || "smart_rag_development",
|
|
38
|
+
username: ENV["SMARTRAG_DB_USER"] || "postgres",
|
|
39
|
+
password: ENV["SMARTRAG_DB_PASSWORD"],
|
|
40
|
+
pool: 5,
|
|
41
|
+
encoding: "UTF8",
|
|
42
|
+
timeout: 5000,
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
RSpec::Core::RakeTask.new(:spec) if defined?(RSpec)
|
|
47
|
+
|
|
48
|
+
task :default => :spec if Rake::Task.task_defined?(:spec)
|
|
49
|
+
|
|
50
|
+
namespace :db do
|
|
51
|
+
desc "Create database"
|
|
52
|
+
task :create do
|
|
53
|
+
require "sequel"
|
|
54
|
+
config = load_db_config
|
|
55
|
+
database = config.delete(:database)
|
|
56
|
+
|
|
57
|
+
# Try to create the database using psql command for better compatibility
|
|
58
|
+
host = config[:host] || "localhost"
|
|
59
|
+
port = config[:port] || 5432
|
|
60
|
+
username = config[:username] || "postgres"
|
|
61
|
+
|
|
62
|
+
if host == "localhost" && running_as_root? && !windows?
|
|
63
|
+
# Running as root, try with sudo
|
|
64
|
+
begin
|
|
65
|
+
system("sudo", "-u", username, "createdb", "-h", host, "-p", port.to_s, database)
|
|
66
|
+
if $?.success?
|
|
67
|
+
puts "Database #{database} created"
|
|
68
|
+
next
|
|
69
|
+
end
|
|
70
|
+
rescue
|
|
71
|
+
# Fall through to Sequel method
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
begin
|
|
76
|
+
# Remove extensions from config when connecting to postgres database
|
|
77
|
+
# pgvector extension shouldn't be loaded when just creating a database
|
|
78
|
+
connect_config = config.dup
|
|
79
|
+
connect_config.delete(:extensions)
|
|
80
|
+
db = Sequel.connect(connect_config.merge(database: "postgres"))
|
|
81
|
+
db.execute("CREATE DATABASE #{database}")
|
|
82
|
+
db.disconnect
|
|
83
|
+
puts "Database #{database} created"
|
|
84
|
+
rescue => e
|
|
85
|
+
puts "Failed to create database: #{e.message}"
|
|
86
|
+
puts "You may need to:"
|
|
87
|
+
puts " 1. Ensure PostgreSQL is running"
|
|
88
|
+
puts " 2. Create a .env file with database credentials"
|
|
89
|
+
puts " 3. Use sudo -u postgres rake db:create if running as root"
|
|
90
|
+
exit 1
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
desc "Drop database"
|
|
95
|
+
task :drop do
|
|
96
|
+
require "sequel"
|
|
97
|
+
config = load_db_config
|
|
98
|
+
database = config.delete(:database)
|
|
99
|
+
|
|
100
|
+
host = config[:host] || "localhost"
|
|
101
|
+
port = config[:port] || 5432
|
|
102
|
+
username = config[:username] || "postgres"
|
|
103
|
+
|
|
104
|
+
if host == "localhost" && running_as_root? && !windows?
|
|
105
|
+
# Running as root, try with sudo
|
|
106
|
+
begin
|
|
107
|
+
system("sudo", "-u", username, "dropdb", "-h", host, "-p", port.to_s, "--if-exists", database)
|
|
108
|
+
if $?.success?
|
|
109
|
+
puts "Database #{database} dropped"
|
|
110
|
+
next
|
|
111
|
+
end
|
|
112
|
+
rescue
|
|
113
|
+
# Fall through to Sequel method
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
begin
|
|
118
|
+
# Remove extensions from config when connecting to postgres database
|
|
119
|
+
connect_config = config.dup
|
|
120
|
+
connect_config.delete(:extensions)
|
|
121
|
+
db = Sequel.connect(connect_config.merge(database: "postgres"))
|
|
122
|
+
db.execute("DROP DATABASE IF EXISTS #{database}")
|
|
123
|
+
db.disconnect
|
|
124
|
+
puts "Database #{database} dropped"
|
|
125
|
+
rescue => e
|
|
126
|
+
puts "Failed to drop database: #{e.message}"
|
|
127
|
+
exit 1
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
desc "Run migrations"
|
|
132
|
+
task :migrate do
|
|
133
|
+
require "sequel"
|
|
134
|
+
Sequel.extension :migration
|
|
135
|
+
# Remove extensions from config - they're installed via SQL, not Sequel
|
|
136
|
+
config = load_db_config
|
|
137
|
+
config.delete(:extensions)
|
|
138
|
+
db = Sequel.connect(config)
|
|
139
|
+
|
|
140
|
+
# Enable extensions
|
|
141
|
+
db.run "CREATE EXTENSION IF NOT EXISTS vector"
|
|
142
|
+
begin
|
|
143
|
+
db.run "CREATE EXTENSION IF NOT EXISTS pg_jieba"
|
|
144
|
+
rescue Sequel::DatabaseError => e
|
|
145
|
+
puts "Warning: pg_jieba extension not available (#{e.message}); Chinese full-text will fall back to 'simple'"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
migrations_dir = File.join(__dir__, "db", "migrations")
|
|
149
|
+
Sequel::Migrator.run(db, migrations_dir)
|
|
150
|
+
|
|
151
|
+
puts "Migrations completed"
|
|
152
|
+
db.disconnect
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
desc "Rollback migrations"
|
|
156
|
+
task :rollback, [:steps] do |t, args|
|
|
157
|
+
args.with_defaults(steps: 1)
|
|
158
|
+
|
|
159
|
+
require "sequel"
|
|
160
|
+
Sequel.extension :migration
|
|
161
|
+
config = load_db_config
|
|
162
|
+
config.delete(:extensions)
|
|
163
|
+
db = Sequel.connect(config)
|
|
164
|
+
|
|
165
|
+
migrations_dir = File.join(__dir__, "db", "migrations")
|
|
166
|
+
Sequel::Migrator.run(db, migrations_dir, target: -args[:steps].to_i)
|
|
167
|
+
|
|
168
|
+
puts "Rolled back #{args[:steps]} migration(s)"
|
|
169
|
+
db.disconnect
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
desc "Seed database with initial data"
|
|
173
|
+
task :seed do
|
|
174
|
+
require "sequel"
|
|
175
|
+
config = load_db_config
|
|
176
|
+
config.delete(:extensions)
|
|
177
|
+
db = Sequel.connect(config)
|
|
178
|
+
|
|
179
|
+
seeds_file = File.join(__dir__, "db", "seeds", "text_search_configs.sql")
|
|
180
|
+
if File.exist?(seeds_file)
|
|
181
|
+
sql = File.read(seeds_file)
|
|
182
|
+
db.run sql
|
|
183
|
+
puts "Database seeded"
|
|
184
|
+
else
|
|
185
|
+
puts "Seeds file not found: #{seeds_file}"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
db.disconnect
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
desc "Backfill source_type/source_uri/content_hash for existing source_documents"
|
|
192
|
+
task :backfill_source_fields do
|
|
193
|
+
require_relative "lib/smart_rag"
|
|
194
|
+
config = SmartRAG::Config.load
|
|
195
|
+
client = SmartRAG::SmartRAG.new(config)
|
|
196
|
+
|
|
197
|
+
dry_run = ENV["DRY_RUN"] == "1"
|
|
198
|
+
limit = ENV["LIMIT"]&.to_i
|
|
199
|
+
result = client.backfill_source_fields(dry_run: dry_run, limit: limit)
|
|
200
|
+
|
|
201
|
+
puts "Backfill completed: #{result[:updated_documents]}/#{result[:total_documents]} updated, #{result[:skipped_documents]} skipped"
|
|
202
|
+
unless result[:errors].empty?
|
|
203
|
+
puts "Backfill errors: #{result[:errors].length}"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
desc "Prepare retrieval release indexes (backfill -> dedupe -> reindex)"
|
|
208
|
+
task :prepare_release do
|
|
209
|
+
require_relative "lib/smart_rag"
|
|
210
|
+
config = SmartRAG::Config.load
|
|
211
|
+
client = SmartRAG::SmartRAG.new(config)
|
|
212
|
+
|
|
213
|
+
dry_run = ENV["DRY_RUN"] == "1"
|
|
214
|
+
document_id = ENV["DOCUMENT_ID"]
|
|
215
|
+
result = client.prepare_release_indexes(document_id: document_id, dry_run: dry_run)
|
|
216
|
+
|
|
217
|
+
puts "Prepare release success=#{result[:success]}"
|
|
218
|
+
puts "Backfill: updated=#{result.dig(:backfill, :updated_documents)} skipped=#{result.dig(:backfill, :skipped_documents)}"
|
|
219
|
+
puts "Dedupe: groups=#{result.dig(:dedupe, :deduped_groups)} deleted=#{result.dig(:dedupe, :deleted_documents)}"
|
|
220
|
+
puts "Reindex success=#{result.dig(:reindex, :success)}"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
desc "Reset database (drop, create, migrate, seed)"
|
|
224
|
+
task :reset => [:drop, :create, :migrate, :seed]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
namespace :test do
|
|
228
|
+
desc "Setup test database"
|
|
229
|
+
task :db_setup do
|
|
230
|
+
# Configure test environment
|
|
231
|
+
ENV["RACK_ENV"] = "test"
|
|
232
|
+
|
|
233
|
+
# This will be handled by spec_helper
|
|
234
|
+
puts "Test database will be set up by spec_helper"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
desc "Run tests"
|
|
238
|
+
task :run => :db_setup do
|
|
239
|
+
if Rake::Task.task_defined?(:spec)
|
|
240
|
+
Rake::Task["spec"].execute
|
|
241
|
+
else
|
|
242
|
+
puts "RSpec is not available. Install with: gem install rspec"
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Only define build tasks if we have a VERSION constant
|
|
248
|
+
desc "Build gem"
|
|
249
|
+
task :build do
|
|
250
|
+
# Simple gem build without relying on Bundler tasks
|
|
251
|
+
sh "gem build smart_rag.gemspec"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Simple install task that doesn't require loading the gem
|
|
255
|
+
desc "Install gem locally"
|
|
256
|
+
task :install => :build do
|
|
257
|
+
gemspec_file = "smart_rag.gemspec"
|
|
258
|
+
if File.exist?(gemspec_file)
|
|
259
|
+
# Extract version from gemspec
|
|
260
|
+
require "yaml"
|
|
261
|
+
spec = YAML.load_file(gemspec_file)
|
|
262
|
+
version = spec[:version] || ENV["VERSION"] || "0.1.0"
|
|
263
|
+
|
|
264
|
+
sh "gem install ./smart_rag-#{version}.gem"
|
|
265
|
+
else
|
|
266
|
+
puts "Gemspec file not found: #{gemspec_file}"
|
|
267
|
+
end
|
|
268
|
+
end
|