ragdoll-rails 0.1.8 → 0.1.11

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -21
  3. data/app/assets/javascripts/ragdoll/application.js +129 -0
  4. data/app/assets/javascripts/ragdoll/bulk_upload_status.js +454 -0
  5. data/app/assets/stylesheets/ragdoll/application.css +84 -0
  6. data/app/assets/stylesheets/ragdoll/bulk_upload_status.css +379 -0
  7. data/app/channels/application_cable/channel.rb +6 -0
  8. data/app/channels/application_cable/connection.rb +6 -0
  9. data/app/channels/ragdoll/bulk_upload_status_channel.rb +27 -0
  10. data/app/channels/ragdoll/file_processing_channel.rb +26 -0
  11. data/app/components/ragdoll/alert_component.html.erb +4 -0
  12. data/app/components/ragdoll/alert_component.rb +32 -0
  13. data/app/components/ragdoll/application_component.rb +6 -0
  14. data/app/components/ragdoll/card_component.html.erb +15 -0
  15. data/app/components/ragdoll/card_component.rb +21 -0
  16. data/app/components/ragdoll/document_list_component.html.erb +41 -0
  17. data/app/components/ragdoll/document_list_component.rb +13 -0
  18. data/app/components/ragdoll/document_table_component.html.erb +76 -0
  19. data/app/components/ragdoll/document_table_component.rb +13 -0
  20. data/app/components/ragdoll/empty_state_component.html.erb +12 -0
  21. data/app/components/ragdoll/empty_state_component.rb +17 -0
  22. data/app/components/ragdoll/flash_messages_component.html.erb +3 -0
  23. data/app/components/ragdoll/flash_messages_component.rb +37 -0
  24. data/app/components/ragdoll/navbar_component.html.erb +24 -0
  25. data/app/components/ragdoll/navbar_component.rb +31 -0
  26. data/app/components/ragdoll/page_header_component.html.erb +13 -0
  27. data/app/components/ragdoll/page_header_component.rb +15 -0
  28. data/app/components/ragdoll/stats_card_component.html.erb +11 -0
  29. data/app/components/ragdoll/stats_card_component.rb +17 -0
  30. data/app/components/ragdoll/status_badge_component.html.erb +3 -0
  31. data/app/components/ragdoll/status_badge_component.rb +30 -0
  32. data/app/controllers/ragdoll/api/v1/analytics_controller.rb +72 -0
  33. data/app/controllers/ragdoll/api/v1/base_controller.rb +29 -0
  34. data/app/controllers/ragdoll/api/v1/documents_controller.rb +148 -0
  35. data/app/controllers/ragdoll/api/v1/search_controller.rb +87 -0
  36. data/app/controllers/ragdoll/api/v1/system_controller.rb +97 -0
  37. data/app/controllers/ragdoll/application_controller.rb +17 -0
  38. data/app/controllers/ragdoll/configuration_controller.rb +82 -0
  39. data/app/controllers/ragdoll/dashboard_controller.rb +98 -0
  40. data/app/controllers/ragdoll/documents_controller.rb +460 -0
  41. data/app/controllers/ragdoll/documents_controller_backup.rb +68 -0
  42. data/app/controllers/ragdoll/jobs_controller.rb +116 -0
  43. data/app/controllers/ragdoll/search_controller.rb +368 -0
  44. data/app/jobs/application_job.rb +9 -0
  45. data/app/jobs/ragdoll/bulk_document_processing_job.rb +280 -0
  46. data/app/jobs/ragdoll/process_file_job.rb +166 -0
  47. data/app/services/ragdoll/worker_health_service.rb +111 -0
  48. data/app/views/layouts/ragdoll/application.html.erb +162 -0
  49. data/app/views/ragdoll/dashboard/analytics.html.erb +333 -0
  50. data/app/views/ragdoll/dashboard/index.html.erb +208 -0
  51. data/app/views/ragdoll/documents/edit.html.erb +91 -0
  52. data/app/views/ragdoll/documents/index.html.erb +302 -0
  53. data/app/views/ragdoll/documents/new.html.erb +1518 -0
  54. data/app/views/ragdoll/documents/show.html.erb +188 -0
  55. data/app/views/ragdoll/documents/upload_results.html.erb +248 -0
  56. data/app/views/ragdoll/jobs/index.html.erb +669 -0
  57. data/app/views/ragdoll/jobs/show.html.erb +129 -0
  58. data/app/views/ragdoll/search/index.html.erb +324 -0
  59. data/config/cable.yml +12 -0
  60. data/config/routes.rb +57 -2
  61. data/lib/generators/ragdoll/init/templates/INSTALL +3 -2
  62. data/lib/generators/ragdoll/init_generator.rb +68 -0
  63. data/lib/ragdoll/rails/engine.rb +48 -0
  64. data/lib/ragdoll/rails/version.rb +1 -1
  65. metadata +231 -6
  66. data/lib/generators/ragdoll/init/init_generator.rb +0 -26
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ragdoll
4
+ module Api
5
+ module V1
6
+ class SystemController < BaseController
7
+ def stats
8
+ begin
9
+ client = ::Ragdoll::Client.new
10
+ ragdoll_stats = client.stats
11
+
12
+ system_stats = {
13
+ ragdoll_version: ::Ragdoll::VERSION,
14
+ rails_version: Rails.version,
15
+ ruby_version: RUBY_VERSION,
16
+
17
+ database_stats: {
18
+ documents: ::Ragdoll::Document.count,
19
+ embeddings: ::Ragdoll::Embedding.count,
20
+ searches: ::Ragdoll::Search.count,
21
+ database_size: calculate_database_size
22
+ },
23
+
24
+ configuration: {
25
+ llm_provider: ::Ragdoll.configuration.llm_provider,
26
+ embedding_provider: ::Ragdoll.configuration.embedding_provider,
27
+ embedding_model: ::Ragdoll.configuration.embedding_model,
28
+ chunk_size: ::Ragdoll.configuration.chunk_size,
29
+ chunk_overlap: ::Ragdoll.configuration.chunk_overlap,
30
+ max_search_results: ::Ragdoll.configuration.max_search_results,
31
+ search_similarity_threshold: ::Ragdoll.configuration.search_similarity_threshold,
32
+ enable_search_analytics: ::Ragdoll.configuration.enable_search_analytics,
33
+ enable_document_summarization: ::Ragdoll.configuration.enable_document_summarization,
34
+ enable_usage_tracking: ::Ragdoll.configuration.enable_usage_tracking,
35
+ usage_ranking_enabled: ::Ragdoll.configuration.usage_ranking_enabled
36
+ },
37
+
38
+ performance_metrics: {
39
+ average_search_time: calculate_average_search_time,
40
+ embedding_dimensions: ::Ragdoll::Embedding.first&.embedding_dimensions || 0,
41
+ average_document_size: ::Ragdoll::Document.average('LENGTH(summary)')&.round || 0,
42
+ average_chunks_per_document: ::Ragdoll::Document.count > 0 ? (::Ragdoll::Embedding.count.to_f / ::Ragdoll::Document.count).round || 0 : 0
43
+ },
44
+
45
+ health_check: {
46
+ database_connection: database_healthy?,
47
+ embedding_service: embedding_service_healthy?
48
+ }
49
+ }.merge(ragdoll_stats)
50
+
51
+ render json: system_stats
52
+ rescue => e
53
+ render_error("Error retrieving system stats: #{e.message}")
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def calculate_database_size
60
+ # Simple approximation - in production you might want more sophisticated calculation
61
+ result = ActiveRecord::Base.connection.execute(
62
+ "SELECT pg_size_pretty(pg_database_size(current_database()))"
63
+ )
64
+ result.first['pg_size_pretty']
65
+ rescue
66
+ "Unknown"
67
+ end
68
+
69
+ def calculate_average_search_time
70
+ # Calculate from actual search execution times if available
71
+ avg_time = ::Ragdoll::Search.where.not(execution_time_ms: nil).average(:execution_time_ms)
72
+ if avg_time
73
+ "#{avg_time.round}ms"
74
+ else
75
+ "< 100ms"
76
+ end
77
+ end
78
+
79
+ def database_healthy?
80
+ ActiveRecord::Base.connection.active?
81
+ rescue
82
+ false
83
+ end
84
+
85
+ def embedding_service_healthy?
86
+ begin
87
+ client = ::Ragdoll::Client.new
88
+ # Try a simple test to see if the embedding service is available
89
+ true
90
+ rescue
91
+ false
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ragdoll
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+ before_action :set_current_user
7
+
8
+ layout 'ragdoll/application'
9
+
10
+ private
11
+
12
+ def set_current_user
13
+ # This can be overridden in the host application
14
+ # to set the current user for the Ragdoll engine
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ragdoll
4
+ class ConfigurationController < ApplicationController
5
+ def index
6
+ @configuration = ::Ragdoll.configuration
7
+ @available_providers = %w[openai anthropic google azure ollama huggingface]
8
+ @available_models = {
9
+ openai: ['text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'],
10
+ anthropic: ['claude-3-haiku-20240307', 'claude-3-sonnet-20240229', 'claude-3-opus-20240229'],
11
+ google: ['gemini-pro', 'gemini-1.5-flash', 'gemini-1.5-pro'],
12
+ azure: ['text-embedding-3-small', 'text-embedding-3-large'],
13
+ ollama: ['llama2', 'mistral', 'codellama'],
14
+ huggingface: ['sentence-transformers/all-MiniLM-L6-v2', 'sentence-transformers/all-mpnet-base-v2']
15
+ }
16
+
17
+ @current_stats = {
18
+ total_documents: ::Ragdoll::Document.count,
19
+ total_embeddings: ::Ragdoll::Embedding.count,
20
+ embedding_dimensions: ::Ragdoll::Embedding.first&.embedding_dimensions || 0,
21
+ average_chunk_size: ::Ragdoll::Embedding.average('LENGTH(content)')&.round || 0
22
+ }
23
+ end
24
+
25
+ def update
26
+ config_params = params.require(:configuration).permit(
27
+ :llm_provider,
28
+ :embedding_provider,
29
+ :embedding_model,
30
+ :chunk_size,
31
+ :chunk_overlap,
32
+ :max_search_results,
33
+ :search_similarity_threshold,
34
+ :enable_search_analytics,
35
+ :enable_document_summarization,
36
+ :enable_usage_tracking,
37
+ :usage_ranking_enabled,
38
+ :openai_api_key,
39
+ :anthropic_api_key,
40
+ :google_api_key,
41
+ :azure_api_key,
42
+ :ollama_url,
43
+ :huggingface_api_key
44
+ )
45
+
46
+ begin
47
+ # Update configuration
48
+ config = ::Ragdoll.configuration
49
+
50
+ config_params.each do |key, value|
51
+ # Convert string values to appropriate types
52
+ case key
53
+ when 'chunk_size', 'chunk_overlap', 'max_search_results'
54
+ config.send("#{key}=", value.to_i)
55
+ when 'search_similarity_threshold'
56
+ config.send("#{key}=", value.to_f)
57
+ when 'enable_search_analytics', 'enable_document_summarization', 'enable_usage_tracking', 'usage_ranking_enabled'
58
+ config.send("#{key}=", value == '1' || value == 'true')
59
+ else
60
+ config.send("#{key}=", value) if value.present?
61
+ end
62
+ end
63
+
64
+ flash[:notice] = 'Configuration updated successfully.'
65
+
66
+ # Test the configuration
67
+ begin
68
+ client = ::Ragdoll::Client.new
69
+ test_result = client.stats
70
+ flash[:notice] += " Configuration test successful."
71
+ rescue => e
72
+ flash[:warning] = "Configuration saved but test failed: #{e.message}"
73
+ end
74
+
75
+ rescue => e
76
+ flash[:alert] = "Error updating configuration: #{e.message}"
77
+ end
78
+
79
+ redirect_to ragdoll.configuration_path
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ragdoll
4
+ class DashboardController < ApplicationController
5
+ def index
6
+ @stats = {
7
+ total_documents: ::Ragdoll::Document.count,
8
+ processed_documents: ::Ragdoll::Document.where(status: 'processed').count,
9
+ failed_documents: ::Ragdoll::Document.where(status: 'failed').count,
10
+ pending_documents: ::Ragdoll::Document.where(status: 'pending').count,
11
+ total_embeddings: ::Ragdoll::Embedding.count,
12
+ total_searches: ::Ragdoll::Search.count,
13
+ recent_searches: ::Ragdoll::Search.order(created_at: :desc).limit(5)
14
+ }
15
+
16
+ @document_types = ::Ragdoll::Document.group(:document_type).count
17
+ @recent_documents = ::Ragdoll::Document.order(created_at: :desc).limit(10)
18
+
19
+ # Usage analytics - join through embeddable (Content) to get to documents
20
+ @top_searched_documents = ::Ragdoll::Embedding
21
+ .joins("JOIN ragdoll_contents ON ragdoll_contents.id = ragdoll_embeddings.embeddable_id")
22
+ .joins("JOIN ragdoll_documents ON ragdoll_documents.id = ragdoll_contents.document_id")
23
+ .group('ragdoll_documents.title')
24
+ .order(Arel.sql('SUM(ragdoll_embeddings.usage_count) DESC'))
25
+ .limit(5)
26
+ .sum(:usage_count)
27
+ end
28
+
29
+ def analytics
30
+ today = Date.current
31
+ week_start = today.beginning_of_week
32
+ month_start = today.beginning_of_month
33
+
34
+ # Calculate search statistics
35
+ all_searches = ::Ragdoll::Search.all
36
+ searches_today = all_searches.where(created_at: today.beginning_of_day..today.end_of_day)
37
+ searches_this_week = all_searches.where(created_at: week_start.beginning_of_day..today.end_of_day)
38
+ searches_this_month = all_searches.where(created_at: month_start.beginning_of_day..today.end_of_day)
39
+
40
+ # Comprehensive search analytics combining both pages
41
+ @search_analytics = {
42
+ total_searches: all_searches.count,
43
+ unique_queries: all_searches.distinct.count(:query),
44
+ searches_today: searches_today.count,
45
+ searches_this_week: searches_this_week.count,
46
+ searches_this_month: searches_this_month.count,
47
+ average_results: all_searches.average(:results_count)&.round(1) || 0,
48
+ average_similarity: all_searches.where.not(avg_similarity_score: nil).average(:avg_similarity_score)&.round(3) || 0,
49
+ avg_execution_time: all_searches.average(:execution_time_ms)&.round(1) || 0,
50
+ search_types: all_searches.group(:search_type).count
51
+ }
52
+
53
+ # Top queries (most frequent)
54
+ @top_queries = all_searches
55
+ .group(:query)
56
+ .count
57
+ .sort_by { |query, count| -count }
58
+ .first(10)
59
+ .to_h
60
+
61
+ # Search trends by day for the last 7 days
62
+ @search_trends = (6.days.ago.to_date..today).map do |date|
63
+ count = all_searches.where(created_at: date.beginning_of_day..date.end_of_day).count
64
+ [date.strftime('%m/%d'), count]
65
+ end.to_h
66
+
67
+ # Most searched documents (using embedding usage as proxy)
68
+ @top_documents = ::Ragdoll::Embedding
69
+ .joins("JOIN ragdoll_contents ON ragdoll_contents.id = ragdoll_embeddings.embeddable_id")
70
+ .joins("JOIN ragdoll_documents ON ragdoll_documents.id = ragdoll_contents.document_id")
71
+ .group('ragdoll_documents.title')
72
+ .order(Arel.sql('SUM(ragdoll_embeddings.usage_count) DESC'))
73
+ .limit(10)
74
+ .sum(:usage_count)
75
+
76
+ # Similarity score distribution
77
+ similarity_scores = all_searches.where.not(avg_similarity_score: nil).pluck(:avg_similarity_score)
78
+ @similarity_distribution = {
79
+ "0.9-1.0" => similarity_scores.count { |s| s >= 0.9 },
80
+ "0.8-0.9" => similarity_scores.count { |s| s >= 0.8 && s < 0.9 },
81
+ "0.7-0.8" => similarity_scores.count { |s| s >= 0.7 && s < 0.8 },
82
+ "0.6-0.7" => similarity_scores.count { |s| s >= 0.6 && s < 0.7 },
83
+ "0.5-0.6" => similarity_scores.count { |s| s >= 0.5 && s < 0.6 },
84
+ "< 0.5" => similarity_scores.count { |s| s < 0.5 }
85
+ }
86
+
87
+ # System statistics
88
+ @system_stats = {
89
+ total_documents: ::Ragdoll::Document.count,
90
+ processed_documents: ::Ragdoll::Document.where(status: 'processed').count,
91
+ failed_documents: ::Ragdoll::Document.where(status: 'failed').count,
92
+ pending_documents: ::Ragdoll::Document.where(status: 'pending').count,
93
+ total_embeddings: ::Ragdoll::Embedding.count,
94
+ total_embedding_usage: ::Ragdoll::Embedding.sum(:usage_count)
95
+ }
96
+ end
97
+ end
98
+ end