class Ragdoll::Core::Client
Public Class Methods
Source
# File lib/ragdoll/core/client.rb, line 8 def initialize # Setup configuration services @config_service = Ragdoll::ConfigurationService.new @model_resolver = Ragdoll::ModelResolver.new(@config_service) # Setup logging setup_logging # Setup database connection Database.setup(@config_service.config.database) @embedding_service = Ragdoll::EmbeddingService.new( client: nil, config_service: @config_service, model_resolver: @model_resolver ) @search_engine = Ragdoll::SearchEngine.new(@embedding_service, config_service: @config_service) end
Public Instance Methods
Source
# File lib/ragdoll/core/client.rb, line 245 def add_directory(path:, recursive: false) results = [] pattern = recursive ? File.join(path, "**", "*") : File.join(path, "*") Dir.glob(pattern).each do |file_path| next unless File.file?(file_path) begin doc_id = add_document(path: file_path) results << { file: file_path, document_id: doc_id, status: "success" } rescue StandardError => e results << { file: file_path, error: e.message, status: "error" } end end results end
Source
# File lib/ragdoll/core/client.rb, line 187 def add_document(path:) # Parse the document parsed = Ragdoll::DocumentProcessor.parse(path) # Extract title from metadata or use filename title = parsed[:metadata][:title] || File.basename(path, File.extname(path)) # Add document to database doc_id = Ragdoll::DocumentManagement.add_document(path, parsed[:content], { title: title, document_type: parsed[:document_type], **parsed[:metadata] }) # Queue background jobs for processing if content is available embeddings_queued = false if parsed[:content].present? Ragdoll::GenerateEmbeddingsJob.perform_later(doc_id) Ragdoll::GenerateSummaryJob.perform_later(doc_id) Ragdoll::ExtractKeywordsJob.perform_later(doc_id) embeddings_queued = true end # Return success information { success: true, document_id: doc_id, title: title, document_type: parsed[:document_type], content_length: parsed[:content]&.length || 0, embeddings_queued: embeddings_queued, message: "Document '#{title}' added successfully with ID #{doc_id}" } rescue StandardError => e # StandardError => e { success: false, error: e.message, message: "Failed to add document: #{e.message}" } end
Document management
Source
# File lib/ragdoll/core/client.rb, line 229 def add_text(content:, title:, **options) # Add document to database doc_id = Ragdoll::DocumentManagement.add_document(title, content, { title: title, document_type: "text", **options }) # Queue background job for embeddings Ragdoll::GenerateEmbeddingsJob.perform_later(doc_id, chunk_size: options[:chunk_size], chunk_overlap: options[:chunk_overlap]) doc_id end
Source
# File lib/ragdoll/core/client.rb, line 307 def delete_document(id:) Ragdoll::DocumentManagement.delete_document(id) end
Source
# File lib/ragdoll/core/client.rb, line 271 def document_status(id:) document = Ragdoll::Document.find(id) embeddings_count = document.all_embeddings.count { id: document.id, title: document.title, status: document.status, embeddings_count: embeddings_count, embeddings_ready: embeddings_count.positive?, content_preview: document.content&.first(200) || "No content", message: case document.status when "processed" "Document processed successfully with #{embeddings_count} embeddings" when "processing" "Document is being processed" when "pending" "Document is pending processing" when "error" "Document processing failed" else "Document status: #{document.status}" end } rescue ActiveRecord::RecordNotFound { success: false, error: "Document not found", message: "Document with ID #{id} does not exist" } end
Source
# File lib/ragdoll/core/client.rb, line 29 def enhance_prompt(prompt:, context_limit: 5, **options) context_data = get_context(query: prompt, limit: context_limit, **options) if context_data[:context_chunks].any? enhanced_prompt = build_enhanced_prompt(prompt, context_data[:combined_context]) { enhanced_prompt: enhanced_prompt, original_prompt: prompt, context_sources: context_data[:context_chunks].map { |chunk| chunk[:source] }, context_count: context_data[:total_chunks] } else { enhanced_prompt: prompt, original_prompt: prompt, context_sources: [], context_count: 0 } end end
Primary method for RAG applications Returns context-enhanced content for AI prompts
Source
# File lib/ragdoll/core/client.rb, line 51 def get_context(query:, limit: 10, **options) search_response = search_similar_content(query: query, limit: limit, **options) # Handle both old format (array) and new format (hash with results/statistics) if search_response.is_a?(Hash) && search_response.key?(:results) results = search_response[:results] else # Fallback for old format results = search_response || [] end context_chunks = results.map do |result| { content: result[:content], source: result[:document_location], similarity: result[:similarity], chunk_index: result[:chunk_index] } end combined_context = context_chunks.map { |chunk| chunk[:content] }.join("\n\n") { context_chunks: context_chunks, combined_context: combined_context, total_chunks: context_chunks.length } end
Get relevant context without prompt enhancement
Source
# File lib/ragdoll/core/client.rb, line 263 def get_document(id:) document_hash = Ragdoll::DocumentManagement.get_document(id) return nil unless document_hash # DocumentManagement.get_document already returns a hash with all needed info document_hash end
Source
# File lib/ragdoll/core/client.rb, line 328 def healthy? Database.connected? && stats[:total_documents] >= 0 rescue StandardError false end
Health check
Source
# File lib/ragdoll/core/client.rb, line 120 def hybrid_search(query:, **options) start_time = Time.current # Extract tracking options session_id = options[:session_id] user_id = options[:user_id] track_search = options.fetch(:track_search, true) # Generate embedding for the query query_embedding = @embedding_service.generate_embedding(query) # Perform hybrid search results = Ragdoll::Document.hybrid_search(query, query_embedding: query_embedding, **options) execution_time = ((Time.current - start_time) * 1000).round # Record search if tracking enabled if track_search && query && !query.empty? begin # Format results for search recording - hybrid search returns different format search_results = results.map do |result| { embedding_id: result[:embedding_id] || result[:id], similarity: result[:similarity] || result[:score] || 0.0 } end # Extract filters from options filters = options.slice(:document_type, :status).compact search_options = options.slice(:limit, :semantic_weight, :text_weight).compact Ragdoll::Search.record_search( query: query, query_embedding: query_embedding, results: search_results, search_type: "hybrid", filters: filters, options: search_options, execution_time_ms: execution_time, session_id: session_id, user_id: user_id ) rescue => e # Log error but don't fail the search puts "Warning: Hybrid search tracking failed: #{e.message}" if ENV["RAGDOLL_DEBUG"] end end { query: query, search_type: "hybrid", results: results, total_results: results.length, semantic_weight: options[:semantic_weight] || 0.7, text_weight: options[:text_weight] || 0.3 } rescue StandardError => e { query: query, search_type: "hybrid", results: [], total_results: 0, error: "Hybrid search failed: #{e.message}" } end
Hybrid search combining semantic and full-text search
Source
# File lib/ragdoll/core/client.rb, line 311 def list_documents(**options) Ragdoll::DocumentManagement.list_documents(options) end
Source
# File lib/ragdoll/core/client.rb, line 86 def search(query:, **options) # Pass through tracking options to the search engine search_response = search_similar_content(query: query, **options) # Handle both old format (array) and new format (hash with results/statistics) if search_response.is_a?(Hash) && search_response.key?(:results) results = search_response[:results] statistics = search_response[:statistics] execution_time_ms = search_response[:execution_time_ms] { query: query, results: results, total_results: results.length, statistics: statistics, execution_time_ms: execution_time_ms } else # Fallback for old format results = search_response || [] { query: query, results: results, total_results: results.length } end end
Semantic search++ should incorporate hybrid search
Source
# File lib/ragdoll/core/client.rb, line 320 def search_analytics(days: 30) # This could be implemented with additional database queries Ragdoll::Embedding.where("returned_at > ?", days.days.ago) .group("DATE(returned_at)") .count end
Source
# File lib/ragdoll/core/client.rb, line 115 def search_similar_content(query:, **options) @search_engine.search_similar_content(query, **options) end
Search similar content (core functionality)
Source
# File lib/ragdoll/core/client.rb, line 316 def stats Ragdoll::DocumentManagement.get_document_stats end
Analytics and stats
Source
# File lib/ragdoll/core/client.rb, line 303 def update_document(id:, **updates) Ragdoll::DocumentManagement.update_document(id, **updates) end