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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +252 -0
  3. data/.rspec +2 -0
  4. data/AGENTS.md +33 -0
  5. data/API_DOCUMENTATION.md +828 -0
  6. data/CHANGELOG.md +16 -1
  7. data/ER-diagram.mmd +144 -0
  8. data/Gemfile +50 -0
  9. data/Gemfile.lock +398 -0
  10. data/Hybrid_Reranking.md +171 -0
  11. data/README.en.md +420 -28
  12. data/README.md +534 -63
  13. data/Rakefile +268 -0
  14. data/SETUP_GUIDE.md +650 -0
  15. data/SmartChunking.md +180 -0
  16. data/USAGE_EXAMPLES.md +1002 -0
  17. data/config/llm_config.yml +4 -2
  18. data/config/smart_rag.yml +45 -1
  19. data/config.ru +15 -0
  20. data/db/migrations/006_create_text_search_configs.rb +3 -2
  21. data/db/migrations/008_create_embeddings.rb +5 -4
  22. data/db/migrations/012_add_metadata_to_source_sections.rb +11 -0
  23. data/db/migrations/013_create_media_jobs.rb +25 -0
  24. data/db/migrations/014_add_media_job_operations_indexes.rb +11 -0
  25. data/db/migrations/015_add_media_leases_and_objects.rb +80 -0
  26. data/db/migrations/016_add_document_principals_and_staging_references.rb +38 -0
  27. data/db/migrations/017_add_media_job_request_fingerprint.rb +48 -0
  28. data/db/seeds/text_search_configs.sql +3 -3
  29. data/design.md +1057 -0
  30. data/docs/API_DOCUMENTATION.md +838 -0
  31. data/docs/DOCUMENTATION_INDEX.en.md +60 -0
  32. data/docs/DOCUMENTATION_INDEX.md +65 -0
  33. data/docs/FIX_SUMMARY.md +256 -0
  34. data/docs/FIX_SUMMARY_COMPLETE.md +273 -0
  35. data/docs/Hybrid_Reranking.md +171 -0
  36. data/docs/MIGRATION_GUIDE.md +151 -0
  37. data/docs/PERFORMANCE_GUIDE.md +58 -0
  38. data/docs/SETUP_GUIDE.md +659 -0
  39. data/docs/SmartChunking.md +180 -0
  40. data/docs/USAGE_EXAMPLES.md +1008 -0
  41. data/docs/design.md +1057 -0
  42. data/docs/evidence_pack.md +211 -0
  43. data/docs/requirements.md +376 -0
  44. data/docs/retrieval_plan.md +251 -0
  45. data/docs/smartrag_improvement_plan.md +201 -0
  46. data/docs/smartrag_refactor.md +216 -0
  47. data/docs/todo.md +931 -0
  48. data/examples/common.rb +1 -1
  49. data/exe/smart-rag-db +163 -0
  50. data/exe/smart-rag-media-worker +34 -0
  51. data/lib/smart_rag/config.rb +12 -0
  52. data/lib/smart_rag/core/document_processor.rb +80 -16
  53. data/lib/smart_rag/core/local_content_store.rb +51 -0
  54. data/lib/smart_rag/core/media_extractors.rb +140 -0
  55. data/lib/smart_rag/core/media_job_queue.rb +353 -0
  56. data/lib/smart_rag/core/media_metadata_extractor.rb +188 -0
  57. data/lib/smart_rag/core/media_object_registry.rb +79 -0
  58. data/lib/smart_rag/core/media_processor.rb +228 -0
  59. data/lib/smart_rag/core/media_safety_policy.rb +61 -0
  60. data/lib/smart_rag/core/s3_content_store.rb +78 -0
  61. data/lib/smart_rag/core/transcript_normalizer.rb +44 -0
  62. data/lib/smart_rag/core/video_semantic_extractor.rb +130 -0
  63. data/lib/smart_rag/http_access_policy.rb +86 -0
  64. data/lib/smart_rag/http_app.rb +188 -0
  65. data/lib/smart_rag/models/embedding.rb +1 -1
  66. data/lib/smart_rag/models/research_topic.rb +1 -1
  67. data/lib/smart_rag/models/research_topic_section.rb +5 -0
  68. data/lib/smart_rag/models/research_topic_tag.rb +5 -0
  69. data/lib/smart_rag/models/search_log.rb +1 -1
  70. data/lib/smart_rag/models/section_fts.rb +5 -0
  71. data/lib/smart_rag/models/section_tag.rb +5 -0
  72. data/lib/smart_rag/models/source_document.rb +1 -1
  73. data/lib/smart_rag/models/source_section.rb +1 -1
  74. data/lib/smart_rag/models/tag.rb +1 -1
  75. data/lib/smart_rag/models/text_search_config.rb +5 -0
  76. data/lib/smart_rag/retrieve.rb +72 -1
  77. data/lib/smart_rag/services/embedding_service.rb +1 -1
  78. data/lib/smart_rag/services/fulltext_search_service.rb +11 -13
  79. data/lib/smart_rag/services/hybrid_search_service.rb +15 -11
  80. data/lib/smart_rag/services/summarization_service.rb +1 -1
  81. data/lib/smart_rag/services/tag_service.rb +1 -1
  82. data/lib/smart_rag/version.rb +1 -1
  83. data/lib/smart_rag.rb +264 -30
  84. data/patch_language.rb +27 -0
  85. data/requirements.md +376 -0
  86. data/source_documents_export.json +11072 -0
  87. data/todo.md +931 -0
  88. data/workers/analyze_content.rb +6 -2
  89. data/workers/get_embedding.rb +1 -1
  90. 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