ragdoll-rails 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2ac0f85d4a125cc90ca0a5187ae327eeb1c88de8380c3a5929ec79c52d7db54a
4
+ data.tar.gz: 7eb055ea9faecbdbdb5a51cce6256ed5350de814447de02d20d09e6a7dd3a86f
5
+ SHA512:
6
+ metadata.gz: 9e52d30d90f0641b02f7b32fab536f3465666c38ab06d9008ae90a67c0fec9e8037614049adb63479607dad320d15e6a51d32e469eca7a80b4439029c5aa031a
7
+ data.tar.gz: a5397b0ae44775dd35bddf5a4551bfc456f6c856958695ee9fae0da0117b4ccfef89c612ca0e9182eb310866b61c0b42d5e7302b2160186c6144297a075239d6
data/README.md ADDED
@@ -0,0 +1,501 @@
1
+ <div align="center" style="background-color: yellow; color: black; padding: 20px; margin: 20px 0; border: 2px solid black; font-size: 48px; font-weight: bold;">
2
+ ⚠️ CAUTION ⚠️<br />
3
+ Software Under Development by a Crazy Man
4
+ </div>
5
+ <br />
6
+ <div align="center">
7
+ <table>
8
+ <tr>
9
+ <td width="50%">
10
+ <a href="https://research.ibm.com/blog/retrieval-augmented-generation-RAG" target="_blank">
11
+ <img src="ragdoll-rails.png" alt="Ragdoll Riding the Rails" width="800">
12
+ </a>
13
+ </td>
14
+ <td width="50%" valign="top">
15
+ <p>Multi-modal RAG (Retrieval-Augmented Generation) is an architecture that integrates multiple data types (such as text, images, and audio) to enhance AI response generation. It combines retrieval-based methods, which fetch relevant information from a knowledge base, with generative large language models (LLMs) that create coherent and contextually appropriate outputs. This approach allows for more comprehensive and engaging user interactions, such as chatbots that respond with both text and images or educational tools that incorporate visual aids into learning materials. By leveraging various modalities, multi-modal RAG systems improve context understanding and user experience.</p>
16
+ </td>
17
+ </tr>
18
+ </table>
19
+ </div>
20
+
21
+ # Ragdoll::Rails
22
+
23
+ **Ragdoll** is a powerful Rails engine that adds **Multi-modal Retrieval Augmented Generation (RAG)** capabilities to any Rails application. It provides semantic search, document ingestion, and context-enhanced AI prompts using vector embeddings and PostgreSQL with pgvector. With support for multiple LLM providers through [ruby_llm](https://rubyllm.com), you can use OpenAI, Anthropic, Google, Azure, Ollama, and more.
24
+
25
+ See Also:
26
+
27
+ - [Ragdoll::Core](https://github.com/MadBomber/ragdoll)
28
+ - [Ragdoll::CLI](https://github.com/MadBomber/ragdoll-cli)
29
+ - [Ragdoll::Rails](https://github.com/MadBomber/ragdoll-rails) this gem
30
+ - [Demo Rails App](https://github.com/madbomber/ragdoll_demo_app)
31
+
32
+ ## ✨ Features
33
+
34
+ - 🔍 **Semantic Search** - Vector similarity search with flexible embedding models and pgvector
35
+ - 🤖 **Multi-Provider Support** - OpenAI, Anthropic, Google, Azure, Ollama, HuggingFace via ruby_llm
36
+ - 📄 **Multi-format Support** - PDF, DOCX, text, HTML, JSON, XML, CSV document parsing
37
+ - 🧠 **Context Enhancement** - Automatically enhance AI prompts with relevant context
38
+ - ⚡ **Background Processing** - Asynchronous document processing with Sidekiq
39
+ - 🎛️ **Simple API** - Clean, intuitive interface for Rails integration
40
+ - 📊 **Analytics** - Search analytics and document management insights
41
+ - 🔧 **Configurable** - Flexible chunking, embedding, and search parameters
42
+ - 🔄 **Flexible Vectors** - Variable-length embeddings for different models
43
+
44
+ ## 🚀 Quick Start
45
+
46
+ ### Installation
47
+
48
+ Add Ragdoll to your Rails application:
49
+
50
+ ```ruby
51
+ # Gemfile
52
+ gem 'ragdoll-rails'
53
+ gem 'ragdoll-cli' # Optional CLI tool for managing documents and embeddings
54
+ ```
55
+
56
+ ```bash
57
+ bundle install
58
+ ```
59
+
60
+ ### Database Setup
61
+
62
+ Ragdoll requires PostgreSQL with the pgvector extension:
63
+
64
+ ```bash
65
+ # Run migrations
66
+ rails ragdoll:install:migrations
67
+ rails db:migrate
68
+ ```
69
+
70
+ ### Configuration
71
+
72
+ ```ruby
73
+ # config/initializers/ragdoll.rb
74
+ Ragdoll.configure do |config|
75
+ # LLM Provider Configuration
76
+ config.llm_provider = :openai # or :anthropic, :google, :azure, :ollama, :huggingface
77
+ config.embedding_provider = :openai # optional, defaults to llm_provider
78
+
79
+ # Provider-specific API keys
80
+ config.llm_config = {
81
+ openai: { api_key: ENV['OPENAI_API_KEY'] },
82
+ anthropic: { api_key: ENV['ANTHROPIC_API_KEY'] },
83
+ google: { api_key: ENV['GOOGLE_API_KEY'], project_id: ENV['GOOGLE_PROJECT_ID'] }
84
+ }
85
+
86
+ # Embedding and processing settings
87
+ config.embedding_model = 'text-embedding-3-small'
88
+ config.chunk_size = 1000
89
+ config.search_similarity_threshold = 0.7
90
+ config.max_embedding_dimensions = 3072 # supports variable-length vectors
91
+ end
92
+ ```
93
+
94
+ ### Basic Usage
95
+
96
+ ```ruby
97
+ # Add documents
98
+ Ragdoll.add_document('/path/to/manual.pdf')
99
+ Ragdoll.add_directory('/path/to/directory_of_documents', recursive: true)
100
+
101
+ # Enhance AI prompts with context
102
+ enhanced = Ragdoll.enhance_prompt(
103
+ 'How do I configure the database?',
104
+ context_limit: 5
105
+ )
106
+
107
+ # Use enhanced prompt with RubyLLM
108
+ ai_response = RubyLLM.ask(enhanced[:enhanced_prompt])
109
+ ```
110
+
111
+ ## 📖 API Reference
112
+
113
+ ### Context Enhancement for AI
114
+
115
+ The primary method for RAG applications - automatically finds relevant context and enhances prompts:
116
+
117
+ ```ruby
118
+ enhanced = Ragdoll.enhance_prompt(
119
+ "How do I deploy to production?",
120
+ context_limit: 3,
121
+ threshold: 0.8
122
+ )
123
+
124
+ # Returns:
125
+ {
126
+ enhanced_prompt: "...", # Prompt with context injected
127
+ original_prompt: "...", # Original user prompt
128
+ context_sources: [...], # Source documents
129
+ context_count: 2 # Number of context chunks
130
+ }
131
+ ```
132
+
133
+ ### Semantic Search
134
+
135
+ ```ruby
136
+ # Search for similar content
137
+ results = Ragdoll.search(
138
+ "database configuration",
139
+ limit: 10,
140
+ threshold: 0.6,
141
+ filters: { document_type: 'pdf' }
142
+ )
143
+
144
+ # Get raw context without prompt enhancement
145
+ context = Ragdoll.client.get_context(
146
+ "API authentication",
147
+ limit: 5
148
+ )
149
+ ```
150
+
151
+ ### Document Management
152
+
153
+ ```ruby
154
+ # Add documents
155
+ Ragdoll.add_file('/docs/manual.pdf')
156
+ Ragdoll.add_text('Content', title: 'Guide')
157
+ Ragdoll.add_directory('/knowledge-base', recursive: true)
158
+
159
+ # Manage documents
160
+ client = Ragdoll::Client.new
161
+ client.update_document(123, title: 'New Title')
162
+ client.delete_document(123)
163
+ client.list_documents(limit: 50)
164
+
165
+ # Bulk operations
166
+ client.reprocess_failed
167
+ client.add_directory('/docs', recursive: true)
168
+ ```
169
+
170
+ ## 🏗️ Rails Integration Examples
171
+
172
+ ### Chat Controller
173
+
174
+ ```ruby
175
+ class ChatController < ApplicationController
176
+ def ask
177
+ enhanced = Ragdoll.enhance_prompt(
178
+ params[:question],
179
+ context_limit: 5
180
+ )
181
+
182
+ ai_response = OpenAI.complete(enhanced[:enhanced_prompt])
183
+
184
+ render json: {
185
+ answer: ai_response,
186
+ sources: enhanced[:context_sources],
187
+ context_used: enhanced[:context_count] > 0
188
+ }
189
+ end
190
+ end
191
+ ```
192
+
193
+ ### Support Bot Service
194
+
195
+ ```ruby
196
+ class SupportBot
197
+ def initialize
198
+ @ragdoll = Ragdoll::Client.new
199
+ end
200
+
201
+ def answer_question(question, category: nil)
202
+ filters = { document_type: 'pdf' } if category == 'manual'
203
+
204
+ context = @ragdoll.get_context(
205
+ question,
206
+ limit: 3,
207
+ threshold: 0.8,
208
+ filters: filters
209
+ )
210
+
211
+ if context[:total_chunks] > 0
212
+ prompt = build_prompt(question, context[:combined_context])
213
+ ai_response = call_ai_service(prompt)
214
+
215
+ {
216
+ answer: ai_response,
217
+ confidence: :high,
218
+ sources: context[:context_chunks]
219
+ }
220
+ else
221
+ fallback_response(question)
222
+ end
223
+ end
224
+ end
225
+ ```
226
+
227
+ ### Background Processing
228
+
229
+ ```ruby
230
+ class ProcessDocumentsJob < ApplicationJob
231
+ def perform(file_paths)
232
+ ragdoll = Ragdoll::Client.new
233
+
234
+ file_paths.each do |path|
235
+ ragdoll.add_file(path, process_immediately: true)
236
+ end
237
+ end
238
+ end
239
+ ```
240
+
241
+ ## 🛠️ Command Line Tools
242
+
243
+ ### Thor Commands
244
+
245
+ ```bash
246
+ # Document management
247
+ thor ragdoll:document:add /path/to/file.pdf --process_now
248
+ thor ragdoll:document:list --status completed --limit 20
249
+ thor ragdoll:document:show 123
250
+ thor ragdoll:document:delete 123 --confirm
251
+
252
+ # Import operations
253
+ thor ragdoll:import:import /docs --recursive --jobs 4
254
+ ```
255
+
256
+ ### Rake Tasks
257
+
258
+ ```bash
259
+ # Add documents
260
+ rake ragdoll:document:add[/path/to/file.pdf] PROCESS_NOW=true
261
+ TITLE="Manual" rake ragdoll:document:add[content.txt]
262
+
263
+ # Bulk operations
264
+ rake ragdoll:document:bulk:reprocess_failed
265
+ rake ragdoll:document:bulk:cleanup_orphaned
266
+ STATUS=failed rake ragdoll:document:bulk:delete_by_status[failed]
267
+
268
+ # List and search
269
+ LIMIT=50 rake ragdoll:document:list
270
+ rake ragdoll:document:show[123]
271
+ ```
272
+
273
+ ## 📋 Supported Document Types
274
+
275
+ | Format | Extension | Features |
276
+ |--------|-----------|----------|
277
+ | PDF | `.pdf` | Text extraction, metadata, page info |
278
+ | DOCX | `.docx` | Paragraphs, tables, document properties |
279
+ | Text | `.txt`, `.md` | Plain text, markdown |
280
+ | HTML | `.html`, `.htm` | Tag stripping, content extraction |
281
+ | Data | `.json`, `.xml`, `.csv` | Structured data parsing |
282
+
283
+ ## ⚙️ Configuration Options
284
+
285
+ ### Multi-Provider Configuration
286
+
287
+ ```ruby
288
+ Ragdoll.configure do |config|
289
+ # Primary LLM provider for chat/completion
290
+ config.llm_provider = :anthropic
291
+
292
+ # Separate provider for embeddings (optional)
293
+ config.embedding_provider = :openai
294
+
295
+ # Provider-specific configurations
296
+ config.llm_config = {
297
+ openai: {
298
+ api_key: ENV['OPENAI_API_KEY'],
299
+ organization: ENV['OPENAI_ORGANIZATION'], # optional
300
+ project: ENV['OPENAI_PROJECT'] # optional
301
+ },
302
+ anthropic: {
303
+ api_key: ENV['ANTHROPIC_API_KEY']
304
+ },
305
+ google: {
306
+ api_key: ENV['GOOGLE_API_KEY'],
307
+ project_id: ENV['GOOGLE_PROJECT_ID']
308
+ },
309
+ azure: {
310
+ api_key: ENV['AZURE_API_KEY'],
311
+ endpoint: ENV['AZURE_ENDPOINT'],
312
+ api_version: ENV['AZURE_API_VERSION']
313
+ },
314
+ ollama: {
315
+ endpoint: ENV['OLLAMA_ENDPOINT'] || 'http://localhost:11434'
316
+ },
317
+ huggingface: {
318
+ api_key: ENV['HUGGINGFACE_API_KEY']
319
+ }
320
+ }
321
+ end
322
+ ```
323
+
324
+ ### Model and Processing Settings
325
+
326
+ ```ruby
327
+ Ragdoll.configure do |config|
328
+ # Embedding configuration
329
+ config.embedding_model = 'text-embedding-3-small'
330
+ config.max_embedding_dimensions = 3072 # supports variable dimensions
331
+ config.default_model = 'gpt-4' # for chat/completion
332
+
333
+ # Text chunking settings
334
+ config.chunk_size = 1000
335
+ config.chunk_overlap = 200
336
+
337
+ # Search and similarity settings
338
+ config.search_similarity_threshold = 0.7
339
+ config.max_search_results = 10
340
+
341
+ # Analytics and performance
342
+ config.enable_search_analytics = true
343
+ config.cache_embeddings = true
344
+
345
+ # Custom prompt template
346
+ config.prompt_template = <<~TEMPLATE
347
+ Context: {{context}}
348
+ Question: {{prompt}}
349
+ Answer:
350
+ TEMPLATE
351
+ end
352
+ ```
353
+
354
+ ### Provider Examples
355
+
356
+ ```ruby
357
+ # OpenAI Configuration
358
+ Ragdoll.configure do |config|
359
+ config.llm_provider = :openai
360
+ config.llm_config = {
361
+ openai: { api_key: ENV['OPENAI_API_KEY'] }
362
+ }
363
+ config.embedding_model = 'text-embedding-3-small'
364
+ end
365
+
366
+ # Anthropic + OpenAI Embeddings
367
+ Ragdoll.configure do |config|
368
+ config.llm_provider = :anthropic
369
+ config.embedding_provider = :openai
370
+ config.llm_config = {
371
+ anthropic: { api_key: ENV['ANTHROPIC_API_KEY'] },
372
+ openai: { api_key: ENV['OPENAI_API_KEY'] }
373
+ }
374
+ end
375
+
376
+ # Local Ollama Setup
377
+ Ragdoll.configure do |config|
378
+ config.llm_provider = :ollama
379
+ config.llm_config = {
380
+ ollama: { endpoint: 'http://localhost:11434' }
381
+ }
382
+ config.embedding_model = 'nomic-embed-text'
383
+ end
384
+ ```
385
+
386
+ ## 🏗️ Database Schema
387
+
388
+ Ragdoll creates three main tables:
389
+
390
+ - **`ragdoll_documents`** - Document metadata and content
391
+ - **`ragdoll_embeddings`** - Vector embeddings with pgvector (variable dimensions)
392
+ - **`ragdoll_searches`** - Search analytics and performance tracking
393
+
394
+ ### Key Features
395
+
396
+ - **Variable Vector Dimensions**: Supports different embedding models with different dimensions
397
+ - **Model Tracking**: Tracks which embedding model was used for each vector
398
+ - **Performance Indexes**: Optimized for similarity search and filtering
399
+ - **Search Analytics**: Comprehensive search performance and usage tracking
400
+
401
+ ## 📊 Analytics and Monitoring
402
+
403
+ ```ruby
404
+ # Document statistics
405
+ stats = Ragdoll.client.stats
406
+ # => { total_documents: 150, total_embeddings: 1250, ... }
407
+
408
+ # Search analytics
409
+ analytics = Ragdoll::Search.analytics(days: 30)
410
+ # => {
411
+ # total_searches: 500,
412
+ # unique_queries: 350,
413
+ # average_results: 8.5,
414
+ # average_search_time: 0.15,
415
+ # success_rate: 85.2,
416
+ # most_common_queries: [...],
417
+ # search_types: { semantic: 450, keyword: 50 },
418
+ # models_used: { "text-embedding-3-small": 400, "text-embedding-3-large": 100 },
419
+ # performance_stats: { fastest: 0.05, slowest: 2.3, median: 0.12 }
420
+ # }
421
+
422
+ # Performance monitoring
423
+ slow_searches = Ragdoll::Search.slow_searches(2.0) # > 2 seconds
424
+ failed_searches = Ragdoll::Search.failed
425
+
426
+ # Health check
427
+ healthy = Ragdoll.client.healthy?
428
+ # => true/false
429
+ ```
430
+
431
+ ## 🧪 Testing
432
+
433
+ ```ruby
434
+ # spec/support/ragdoll_helpers.rb
435
+ module RagdollHelpers
436
+ def setup_test_documents
437
+ @ragdoll = Ragdoll::Client.new
438
+ @doc = @ragdoll.add_text(
439
+ "Rails is a web framework",
440
+ title: "Rails Guide",
441
+ process_immediately: true
442
+ )
443
+ end
444
+ end
445
+
446
+ # In your specs
447
+ RSpec.describe ChatController do
448
+ include RagdollHelpers
449
+
450
+ before { setup_test_documents }
451
+
452
+ it "enhances prompts with context" do
453
+ enhanced = Ragdoll.enhance_prompt("What is Rails?")
454
+ expect(enhanced[:context_count]).to be > 0
455
+ end
456
+ end
457
+ ```
458
+
459
+ ## 📦 Dependencies
460
+
461
+ - **Rails** 8.0+
462
+ - **PostgreSQL** with pgvector extension
463
+ - **Sidekiq** for background processing
464
+ - **ruby_llm** for multi-provider LLM support
465
+ - **LLM Provider APIs** (OpenAI, Anthropic, Google, etc.)
466
+
467
+ ### Supported LLM Providers
468
+
469
+ | Provider | Chat/Completion | Embeddings | Notes |
470
+ |----------|----------------|------------|---------|
471
+ | OpenAI | ✅ | ✅ | GPT models, text-embedding-3-* |
472
+ | Anthropic | ✅ | ❌ | Claude models |
473
+ | Google | ✅ | ✅ | Gemini models |
474
+ | Azure OpenAI | ✅ | ✅ | Azure-hosted OpenAI |
475
+ | Ollama | ✅ | ✅ | Local models |
476
+ | HuggingFace | ✅ | ✅ | Various open-source models |
477
+
478
+ ## 🤝 Contributing
479
+
480
+ 1. Fork the repository
481
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
482
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
483
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
484
+ 5. Open a Pull Request
485
+
486
+ ## 📄 License
487
+
488
+ This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
489
+
490
+ ## 🆘 Support
491
+
492
+ - 📖 [Documentation](https://github.com/MadBomber/ragdoll)
493
+ - 🐛 [Issues](https://github.com/MadBomber/ragdoll/issues)
494
+ - 💬 [Discussions](https://github.com/MadBomber/ragdoll/discussions)
495
+
496
+ ---
497
+
498
+ <div align="center">
499
+ <p>Made with ❤️ for the Rails community</p>
500
+ <p>⭐ Star this repo if you find it useful!</p>
501
+ </div>
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ # This file defines the Rake tasks for the Ragdoll gem, including tasks for testing.
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/gem_tasks"
6
+ require "minitest/test_task"
7
+
8
+ Minitest::TestTask.create
9
+
10
+ require "bundler/gem_tasks"
11
+ require "rake"
12
+
13
+ require "bundler/gem_tasks"
14
+ require "rake"
15
+ require "active_record/railtie"
16
+
17
+ require "bundler/gem_tasks"
18
+ require "minitest/test_task"
19
+
20
+ Minitest::TestTask.create
21
+
22
+ # Load any additional tasks from the lib/tasks directory
23
+ Dir.glob('lib/tasks/**/*.rake').each { |r| load r }
24
+
25
+ task default: :test
26
+
27
+ # Load any additional tasks from the lib/tasks directory
28
+ Dir.glob('lib/tasks/**/*.rake').each { |r| load r }
29
+
30
+ task default: :test
31
+
32
+ # Load any additional tasks from the lib/tasks directory
33
+ Dir.glob('lib/tasks/**/*.rake').each { |r| load r }
34
+
35
+ task default: :test
36
+
37
+ # Load any additional tasks from the lib/tasks directory
38
+ Dir.glob('lib/tasks/**/*.rake').each { |r| load r }
39
+
40
+ task default: :test
@@ -0,0 +1,120 @@
1
+ # This file defines the Rails-specific Document model for the Ragdoll Rails engine.
2
+ # This model is separate from Ragdoll::Core::Models::Document to avoid conflicts.
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Ragdoll
7
+ module Rails
8
+ class Document < ApplicationRecord
9
+ self.table_name = 'ragdoll_documents'
10
+
11
+ # Associations
12
+ has_many :ragdoll_embeddings, class_name: 'Ragdoll::Rails::Embedding', foreign_key: 'document_id', dependent: :destroy
13
+ has_one_attached :file if respond_to?(:has_one_attached)
14
+
15
+ # Validations
16
+ validates :location, presence: true, uniqueness: true
17
+ validates :status, inclusion: { in: %w[pending processing completed failed] }
18
+ validates :chunk_size, numericality: { greater_than: 0 }, allow_nil: true
19
+ validates :chunk_overlap, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
20
+
21
+ # Scopes
22
+ scope :completed, -> { where(status: 'completed') }
23
+ scope :failed, -> { where(status: 'failed') }
24
+ scope :processing, -> { where(status: 'processing') }
25
+ scope :pending, -> { where(status: 'pending') }
26
+ scope :by_type, ->(type) { where(document_type: type) }
27
+ scope :with_summaries, -> { where.not(summary: nil) }
28
+ scope :needs_summary, -> { where(summary: nil).completed }
29
+
30
+ # Search configuration
31
+ searchkick text_middle: [:title, :summary, :content, :metadata_name, :metadata_summary] if defined?(Searchkick)
32
+
33
+ def search_data
34
+ return {} unless defined?(Searchkick)
35
+
36
+ {
37
+ title: title,
38
+ summary: summary,
39
+ content: content,
40
+ metadata_name: metadata&.dig('name'),
41
+ metadata_summary: metadata&.dig('summary'),
42
+ document_type: document_type,
43
+ status: status
44
+ }
45
+ end
46
+
47
+ # Summary-related methods
48
+ def has_summary?
49
+ summary.present?
50
+ end
51
+
52
+ def summary_stale?
53
+ return false unless has_summary?
54
+ return true unless summary_generated_at
55
+
56
+ # Consider summary stale if document was updated after summary generation
57
+ updated_at > summary_generated_at
58
+ end
59
+
60
+ def needs_summary?
61
+ return false unless content.present?
62
+ # Business logic should be handled by ragdoll gem
63
+ # TODO: Delegate to Ragdoll.needs_summary?(content, summary, summary_generated_at)
64
+
65
+ !has_summary? || summary_stale?
66
+ end
67
+
68
+ def summary_word_count
69
+ return 0 unless summary.present?
70
+ summary.split.length
71
+ end
72
+
73
+ def regenerate_summary!
74
+ # Business logic for summary generation should be handled by the ragdoll gem
75
+ # This is a placeholder that delegates to the core ragdoll functionality
76
+ return false unless content.present?
77
+
78
+ # TODO: Delegate to Ragdoll gem's summarization functionality
79
+ # summarization_result = Ragdoll.generate_summary(content, options)
80
+ # Update the model with the result
81
+
82
+ Rails.logger.warn "Summary regeneration not implemented - should delegate to ragdoll gem"
83
+ false
84
+ end
85
+
86
+ # Processing status helpers
87
+ def completed?
88
+ status == 'completed'
89
+ end
90
+
91
+ def failed?
92
+ status == 'failed'
93
+ end
94
+
95
+ def processing?
96
+ status == 'processing'
97
+ end
98
+
99
+ def pending?
100
+ status == 'pending'
101
+ end
102
+
103
+ # Content helpers
104
+ def word_count
105
+ return 0 unless content.present?
106
+ content.split.length
107
+ end
108
+
109
+ def character_count
110
+ return 0 unless content.present?
111
+ content.length
112
+ end
113
+
114
+ def processing_duration
115
+ return nil unless processing_started_at && processing_finished_at
116
+ processing_finished_at - processing_started_at
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,31 @@
1
+ # This file defines the Rails-specific Embedding model for the Ragdoll Rails engine.
2
+ # This model is separate from Ragdoll::Core::Models::Embedding to avoid conflicts.
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Ragdoll
7
+ module Rails
8
+ class Embedding < ApplicationRecord
9
+ searchkick text_middle: [:metadata_content, :metadata_propositions] if defined?(Searchkick)
10
+
11
+ belongs_to :document, class_name: 'Ragdoll::Rails::Document'
12
+
13
+ # Override dangerous attribute to allow access to model_name column
14
+ def self.dangerous_attribute_method?(name)
15
+ name.to_s == 'model_name' ? false : super
16
+ end
17
+
18
+ def search_data
19
+ return {} unless defined?(Searchkick)
20
+
21
+ {
22
+ metadata_content: metadata['content'],
23
+ metadata_propositions: metadata['propositions']
24
+ }
25
+ end
26
+
27
+ # Assuming the vector column is named 'vector'
28
+ neighbor :vector, method: :euclidean if respond_to?(:neighbor)
29
+ end
30
+ end
31
+ end