ragdoll 0.1.0 → 0.1.3

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +318 -40
  3. data/Rakefile +66 -4
  4. data/app/jobs/ragdoll/extract_keywords_job.rb +28 -0
  5. data/app/jobs/ragdoll/extract_text_job.rb +38 -0
  6. data/app/jobs/ragdoll/generate_embeddings_job.rb +28 -0
  7. data/app/jobs/ragdoll/generate_summary_job.rb +25 -0
  8. data/app/lib/ragdoll/metadata_schemas.rb +332 -0
  9. data/app/models/ragdoll/audio_content.rb +142 -0
  10. data/app/models/ragdoll/content.rb +95 -0
  11. data/app/models/ragdoll/document.rb +606 -4
  12. data/app/models/ragdoll/embedding.rb +172 -5
  13. data/app/models/ragdoll/image_content.rb +194 -0
  14. data/app/models/ragdoll/text_content.rb +137 -0
  15. data/app/services/ragdoll/configuration_service.rb +113 -0
  16. data/app/services/ragdoll/document_management.rb +108 -0
  17. data/app/services/ragdoll/document_processor.rb +342 -0
  18. data/app/services/ragdoll/embedding_service.rb +202 -0
  19. data/app/services/ragdoll/image_description_service.rb +230 -0
  20. data/app/services/ragdoll/metadata_generator.rb +329 -0
  21. data/app/services/ragdoll/model_resolver.rb +72 -0
  22. data/app/services/ragdoll/search_engine.rb +51 -0
  23. data/app/services/ragdoll/text_chunker.rb +208 -0
  24. data/app/services/ragdoll/text_generation_service.rb +355 -0
  25. data/db/migrate/001_enable_postgresql_extensions.rb +23 -0
  26. data/db/migrate/004_create_ragdoll_documents.rb +70 -0
  27. data/db/migrate/005_create_ragdoll_embeddings.rb +41 -0
  28. data/db/migrate/006_create_ragdoll_contents.rb +47 -0
  29. data/lib/ragdoll/core/client.rb +306 -0
  30. data/lib/ragdoll/core/configuration.rb +257 -0
  31. data/lib/ragdoll/core/database.rb +141 -0
  32. data/lib/ragdoll/core/errors.rb +11 -0
  33. data/lib/ragdoll/core/model.rb +45 -0
  34. data/lib/ragdoll/core/shrine_config.rb +71 -0
  35. data/lib/ragdoll/core/version.rb +8 -0
  36. data/lib/ragdoll/core.rb +91 -0
  37. data/lib/ragdoll-core.rb +3 -0
  38. data/lib/ragdoll.rb +243 -6
  39. data/lib/tasks/annotate.rake +126 -0
  40. data/lib/tasks/db.rake +338 -0
  41. metadata +42 -35
  42. data/config/initializers/ragdoll.rb +0 -6
  43. data/config/routes.rb +0 -5
  44. data/db/migrate/20250218123456_create_documents.rb +0 -20
  45. data/lib/config/database.yml +0 -28
  46. data/lib/config/ragdoll.yml +0 -31
  47. data/lib/ragdoll/engine.rb +0 -16
  48. data/lib/ragdoll/import_job.rb +0 -15
  49. data/lib/ragdoll/ingestion.rb +0 -30
  50. data/lib/ragdoll/search.rb +0 -18
  51. data/lib/ragdoll/version.rb +0 -7
  52. data/lib/tasks/import_task.thor +0 -32
  53. data/lib/tasks/jobs_task.thor +0 -40
  54. data/lib/tasks/ragdoll_tasks.thor +0 -7
  55. data/lib/tasks/search_task.thor +0 -55
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "logger"
5
+
6
+ module Ragdoll
7
+ module Core
8
+ class Database
9
+ def self.setup(config = {})
10
+ database_config = default_config.merge(config)
11
+
12
+ # Set up ActiveRecord connection
13
+ ActiveRecord::Base.establish_connection(database_config)
14
+
15
+ # Set up logging if specified
16
+ ActiveRecord::Base.logger = database_config[:logger] if database_config[:logger]
17
+
18
+ # Auto-migrate if specified
19
+ return unless database_config[:auto_migrate]
20
+
21
+ migrate!
22
+ end
23
+
24
+ def self.migrate!
25
+ # Get the path to the gem root directory
26
+ # Current file is lib/ragdoll/core/database.rb, so go up 3 levels to get to gem root
27
+ gem_root = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", ".."))
28
+ migration_paths = [
29
+ File.join(gem_root, "db", "migrate")
30
+ ]
31
+
32
+ ActiveRecord::Migration.verbose = true
33
+
34
+ # Ensure schema_migrations table exists first
35
+ unless ActiveRecord::Base.connection.table_exists?("schema_migrations")
36
+ ActiveRecord::Base.connection.create_table("schema_migrations", id: false) do |t|
37
+ t.string :version, null: false
38
+ end
39
+ ActiveRecord::Base.connection.add_index("schema_migrations", :version, unique: true)
40
+ end
41
+
42
+ # Debug migration path (silenced for clean test output)
43
+ # puts "Migration path: #{migration_paths.first}" if ActiveRecord::Migration.verbose
44
+ migration_files = Dir[File.join(migration_paths.first, "*.rb")].sort
45
+ # puts "Found #{migration_files.length} migration files" if ActiveRecord::Migration.verbose
46
+
47
+ # Load and run each migration manually since ActiveRecord migration context seems broken
48
+ migration_files.each do |migration_file|
49
+ # Extract version from filename
50
+ version = File.basename(migration_file, ".rb").split("_").first
51
+
52
+ # Skip if already migrated
53
+ next if ActiveRecord::Base.connection.select_values(
54
+ "SELECT version FROM schema_migrations WHERE version = '#{version}'"
55
+ ).any?
56
+
57
+ # Load the migration file to define the class
58
+ require migration_file
59
+
60
+ # Get the migration class - convert snake_case to CamelCase
61
+ filename_parts = File.basename(migration_file, ".rb").split("_")[1..]
62
+ migration_class_name = filename_parts.map(&:capitalize).join
63
+
64
+ begin
65
+ migration_class = Object.const_get(migration_class_name)
66
+ rescue NameError
67
+ puts "Warning: Could not find migration class #{migration_class_name} in #{migration_file}"
68
+ next
69
+ end
70
+
71
+ # Run the migration quietly
72
+ old_verbose = ActiveRecord::Migration.verbose
73
+ ActiveRecord::Migration.verbose = false
74
+ migration_class.migrate(:up)
75
+ ActiveRecord::Migration.verbose = old_verbose
76
+
77
+ # Record the migration
78
+ ActiveRecord::Base.connection.insert(
79
+ "INSERT INTO schema_migrations (version) VALUES ('#{version}')"
80
+ )
81
+
82
+ # Silenced migration progress - uncomment for debugging
83
+ # puts "Migrated #{migration_class_name}" if ActiveRecord::Migration.verbose
84
+ end
85
+ end
86
+
87
+ def self.reset!
88
+ ActiveRecord::Migration.verbose = false
89
+
90
+ # Drop all tables in correct order (respecting foreign key constraints)
91
+ # Order: dependent tables first, then parent tables
92
+ tables_to_drop = %w[
93
+ ragdoll_embeddings
94
+ ragdoll_text_contents
95
+ ragdoll_image_contents
96
+ ragdoll_audio_contents
97
+ ragdoll_documents
98
+ schema_migrations
99
+ ]
100
+
101
+ tables_to_drop.each do |table|
102
+ if ActiveRecord::Base.connection.table_exists?(table)
103
+ # For PostgreSQL, we can use CASCADE to drop dependent objects
104
+ if ActiveRecord::Base.connection.adapter_name.downcase.include?("postgresql")
105
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{table} CASCADE")
106
+ else
107
+ ActiveRecord::Base.connection.drop_table(table)
108
+ end
109
+ end
110
+ end
111
+
112
+ migrate!
113
+ end
114
+
115
+ def self.connected?
116
+ ActiveRecord::Base.connected?
117
+ end
118
+
119
+ def self.disconnect!
120
+ ActiveRecord::Base.clear_all_connections!
121
+ end
122
+
123
+ def self.default_config
124
+ {
125
+ adapter: "postgresql",
126
+ database: "ragdoll_development",
127
+ username: "ragdoll",
128
+ password: ENV.fetch("RAGDOLL_DATABASE_PASSWORD", nil),
129
+ host: "localhost",
130
+ port: 5432,
131
+ auto_migrate: true,
132
+ logger: Logger.new($stdout, level: Logger::WARN)
133
+ }
134
+ end
135
+
136
+ def self.migration_paths
137
+ [File.join(File.dirname(__FILE__), "..", "..", "..", "..", "db", "migrate")]
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ragdoll
4
+ module Core
5
+ class Error < StandardError; end
6
+ class EmbeddingError < Error; end
7
+ class SearchError < Error; end
8
+ class DocumentError < Error; end
9
+ class ConfigurationError < Error; end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ # lib/ragdoll/core/model.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Ragdoll
5
+ module Core
6
+ # Model represents a provider and model name.
7
+ # It is initialized with a string in the format "provider/model".
8
+ # The provider is optional.
9
+ # Can be initialized with nil or empty string.
10
+ Model = Data.define(:name) do
11
+ # @return [Symbol, nil] the provider part of the name, or nil if not present.
12
+ def provider
13
+ return nil if name.nil? || name.empty?
14
+
15
+ parts = name.split("/", 2)
16
+ return nil if parts.length < 2 || parts.first.empty?
17
+
18
+ parts.first.to_sym
19
+ end
20
+
21
+ # @return [String, nil] the model part of the name, or nil if name is nil/empty.
22
+ def model
23
+ return nil if name.nil? || name.empty?
24
+
25
+ parts = name.split("/", 2)
26
+ parts.length < 2 ? name : parts.last
27
+ end
28
+
29
+ # @return [String] the original name string, or empty string if name is nil.
30
+ def to_s
31
+ name.nil? ? "" : name
32
+ end
33
+
34
+ # @return [Hash] a hash representation of the model.
35
+ def to_h
36
+ { provider: provider, model: model }
37
+ end
38
+
39
+ # YAML serialization - save as string name
40
+ def encode_with(coder)
41
+ coder.scalar = name
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shrine"
4
+ require "shrine/storage/file_system"
5
+
6
+ # Configure Shrine with filesystem storage
7
+ Shrine.storages = {
8
+ cache: Shrine::Storage::FileSystem.new("tmp/uploads", prefix: "cache"),
9
+ store: Shrine::Storage::FileSystem.new("uploads")
10
+ }
11
+
12
+ Shrine.plugin :activerecord
13
+ Shrine.plugin :cached_attachment_data
14
+ Shrine.plugin :restore_cached_data
15
+ Shrine.plugin :rack_file
16
+ Shrine.plugin :validation_helpers
17
+ Shrine.plugin :determine_mime_type
18
+
19
+ # File uploader for documents
20
+ class FileUploader < Shrine
21
+ plugin :validation_helpers
22
+ plugin :determine_mime_type
23
+
24
+ Attacher.validate do
25
+ validate_max_size 50.megabytes
26
+ validate_mime_type %w[
27
+ application/pdf
28
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
29
+ text/plain
30
+ text/html
31
+ text/markdown
32
+ application/json
33
+ ]
34
+ end
35
+ end
36
+
37
+ # Image uploader for image content
38
+ class ImageUploader < Shrine
39
+ plugin :validation_helpers
40
+ plugin :determine_mime_type
41
+
42
+ Attacher.validate do
43
+ validate_max_size 10.megabytes
44
+ validate_mime_type %w[
45
+ image/jpeg
46
+ image/png
47
+ image/gif
48
+ image/webp
49
+ image/bmp
50
+ image/tiff
51
+ ]
52
+ end
53
+ end
54
+
55
+ # Audio uploader for audio content
56
+ class AudioUploader < Shrine
57
+ plugin :validation_helpers
58
+ plugin :determine_mime_type
59
+
60
+ Attacher.validate do
61
+ validate_max_size 100.megabytes
62
+ validate_mime_type %w[
63
+ audio/mpeg
64
+ audio/wav
65
+ audio/mp4
66
+ audio/webm
67
+ audio/ogg
68
+ audio/flac
69
+ ]
70
+ end
71
+ end
@@ -0,0 +1,8 @@
1
+ # ragdoll/core/version.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Ragdoll
5
+ module Core
6
+ VERSION = "0.1.3"
7
+ end
8
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+ require "debug_me"
5
+ include DebugMe
6
+ $DEBUG_ME = true
7
+
8
+ # require_relative "../extensions/openstruct_merge" # File doesn't exist
9
+
10
+ # Add app/models, app/jobs, app/services, and app/lib to the load path
11
+ $LOAD_PATH.unshift(File.expand_path("../../app/models", __dir__))
12
+ $LOAD_PATH.unshift(File.expand_path("../../app/jobs", __dir__))
13
+ $LOAD_PATH.unshift(File.expand_path("../../app/services", __dir__))
14
+ $LOAD_PATH.unshift(File.expand_path("../../app/lib", __dir__))
15
+
16
+ require_relative "core/version"
17
+ require_relative "core/errors"
18
+ require_relative "core/model"
19
+ require_relative "core/configuration"
20
+ # Require services from app/services/ragdoll
21
+ require "ragdoll/configuration_service"
22
+ require "ragdoll/model_resolver"
23
+ require_relative "core/database"
24
+ require_relative "core/shrine_config"
25
+
26
+ # Require models from app/models/ragdoll
27
+ require "ragdoll/document"
28
+ require "ragdoll/embedding"
29
+ require "ragdoll/content"
30
+ require "ragdoll/text_content"
31
+ require "ragdoll/audio_content"
32
+ require "ragdoll/image_content"
33
+ require "ragdoll/document_processor"
34
+ require "ragdoll/document_management"
35
+ require "ragdoll/text_chunker"
36
+ require "ragdoll/embedding_service"
37
+ require "ragdoll/text_generation_service"
38
+ require "ragdoll/search_engine"
39
+ require "ragdoll/image_description_service"
40
+ require "ragdoll/metadata_generator"
41
+ # Require from app/lib/ragdoll
42
+ require "ragdoll/metadata_schemas"
43
+ # Require jobs from app/jobs/ragdoll
44
+ require "ragdoll/generate_embeddings_job"
45
+ require "ragdoll/generate_summary_job"
46
+ require "ragdoll/extract_keywords_job"
47
+ require "ragdoll/extract_text_job"
48
+ require_relative "core/client"
49
+
50
+ module Ragdoll
51
+ def self.config
52
+ @config ||= Core::Configuration.new
53
+ end
54
+
55
+ module Core
56
+ extend SingleForwardable
57
+
58
+ def self.config
59
+ @config ||= Configuration.new
60
+ end
61
+
62
+ def self.configuration
63
+ config
64
+ end
65
+
66
+ def self.configure
67
+ yield(config)
68
+ end
69
+
70
+ # Reset configuration (useful for testing)
71
+ def self.reset_configuration!
72
+ @config = Configuration.new
73
+ @default_client = nil
74
+ end
75
+
76
+ # Factory method for creating clients
77
+ def self.client(_config = nil)
78
+ Client.new
79
+ end
80
+
81
+ # Delegate high-level API methods to default client
82
+ def_delegators :default_client, :add_document, :search, :enhance_prompt,
83
+ :get_document, :document_status, :list_documents, :delete_document,
84
+ :update_document, :get_context, :search_similar_content,
85
+ :add_directory, :stats, :healthy?, :hybrid_search
86
+
87
+ def self.default_client
88
+ @default_client ||= Client.new
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ragdoll/core"
data/lib/ragdoll.rb CHANGED
@@ -1,12 +1,249 @@
1
- # This file is the main entry point for the Ragdoll gem, requiring all necessary components.
2
-
3
1
  # frozen_string_literal: true
4
2
 
5
- # frozen_string_literal: true
3
+ require "debug_me"
4
+ include DebugMe
5
+ $DEBUG_ME = true
6
6
 
7
- require "ragdoll/version"
8
- require "ragdoll/engine"
7
+ require "delegate"
8
+ require_relative "ragdoll/core"
9
9
 
10
10
  module Ragdoll
11
- class Error < StandardError; end
11
+ class << self
12
+
13
+ #################
14
+ # Configuration #
15
+ #################
16
+
17
+ # Retrieve the current configuration.
18
+ # @example
19
+ # config = Ragdoll.config
20
+ # puts config.database_config[:adapter]
21
+ # @example
22
+ # current_config = Ragdoll.configuration
23
+ # puts current_config.models[:default]
24
+ # @return [Ragdoll::Core::Configuration] the current configuration instance.
25
+ def config
26
+ Core.config
27
+ end
28
+
29
+ # Configure the Ragdoll module.
30
+ # @yieldparam config [Ragdoll::Core::Configuration] the configuration instance to modify.
31
+ # @example
32
+ # Ragdoll.configure do |config|
33
+ # config.database_config[:adapter] = "postgres"
34
+ # end
35
+ # @yield [Ragdoll::Core::Configuration] yields the configuration instance for modification.
36
+ def configure(*args, **kwargs, &block)
37
+ Ragdoll::Core.configure(*args, **kwargs, &block)
38
+ end
39
+
40
+ # Access the current configuration.
41
+ # @param args [Array] additional arguments for configuration.
42
+ # @param kwargs [Hash] keyword arguments for configuration.
43
+ # @return [Ragdoll::Core::Configuration] the current configuration instance.
44
+ def configuration(*args, **kwargs)
45
+ Ragdoll::Core.configuration(*args, **kwargs)
46
+ end
47
+
48
+ # @example
49
+ # Ragdoll.reset_configuration!
50
+ # puts Ragdoll.config.models[:default]
51
+ def reset_configuration!(*args, **kwargs)
52
+ Ragdoll::Core.reset_configuration!(*args, **kwargs)
53
+ end
54
+
55
+
56
+ #######################
57
+ # Document Management #
58
+ #######################
59
+
60
+ # Add a directory of documents to the system.
61
+ # @param path [String] the path to the directory containing documents.
62
+ # @example
63
+ # Ragdoll.add_directory(path: "/path/to/documents", recursive: true)
64
+ # @param recursive [Boolean] whether to add documents from subdirectories.
65
+ def add_directory(*args, **kwargs)
66
+ Ragdoll::Core.add_directory(*args, **kwargs)
67
+ end
68
+
69
+ # Add a single document to the system.
70
+ # @example
71
+ # Ragdoll.add_document(path: "/path/to/document.txt")
72
+ # @param path [String] the file path of the document to add.
73
+ def add_document(*args, **kwargs)
74
+ Ragdoll::Core.add_document(*args, **kwargs)
75
+ end
76
+ alias_method :add, :add_document
77
+
78
+ # Retrieve a document by its identifier.
79
+ # @param id [String] the identifier of the document to retrieve.
80
+ # @example
81
+ # document = Ragdoll.get_document(id: "123")
82
+ # puts document[:title] if document
83
+ # @return [Hash, nil] the document data or nil if not found.
84
+ def get_document(*args, **kwargs)
85
+ Ragdoll::Core.get_document(*args, **kwargs)
86
+ end
87
+ alias_method :get, :get_document
88
+
89
+ # List all documents in the system.
90
+ # @param options [Hash] options for listing documents, such as limit and offset.
91
+ # @example
92
+ # documents = Ragdoll.list_documents(limit: 10)
93
+ # documents.each { |doc| puts doc[:title] }
94
+ # @return [Array<Hash>] an array of document data.
95
+ def list_documents(*args, **kwargs)
96
+ Ragdoll::Core.list_documents(*args, **kwargs)
97
+ end
98
+ alias_method :list, :list_documents
99
+
100
+ # Delete a document by its identifier.
101
+ # @param id [String] the identifier of the document to delete.
102
+ # @example
103
+ # success = Ragdoll.delete_document(id: "123")
104
+ # puts "Deleted" if success
105
+ # @return [Boolean] true if the document was successfully deleted.
106
+ def delete_document(*args, **kwargs)
107
+ Ragdoll::Core.delete_document(*args, **kwargs)
108
+ end
109
+ alias_method :delete, :delete_document
110
+
111
+ # Get the status of a document.
112
+ # @param id [String] the identifier of the document to check status.
113
+ # @example
114
+ # status = Ragdoll.document_status(id: "123")
115
+ # puts status[:status]
116
+ # @return [Hash] the status information of the document.
117
+ def document_status(*args, **kwargs)
118
+ Ragdoll::Core.document_status(*args, **kwargs)
119
+ end
120
+ alias_method :status, :document_status
121
+
122
+ # Update a document's information.
123
+ # @param id [String] the identifier of the document to update.
124
+ # @param updates [Hash] the fields to update in the document.
125
+ # @example
126
+ # updated_doc = Ragdoll.update_document(id: "123", title: "New Title")
127
+ # puts updated_doc[:title]
128
+ # @return [Hash] the updated document data.
129
+ def update_document(*args, **kwargs)
130
+ Ragdoll::Core.update_document(*args, **kwargs)
131
+ end
132
+ alias_method :update, :update_document
133
+
134
+ # Retrieve all documents.
135
+ # @example
136
+ # all_docs = Ragdoll.documents
137
+ # all_docs.each { |doc| puts doc.title }
138
+ # @return [ActiveRecord::Relation] a relation of all documents.
139
+ def documents
140
+ Ragdoll::Document.all
141
+ end
142
+ alias_method :docs, :documents
143
+
144
+ #############
145
+ # Retrieval #
146
+ #############
147
+
148
+ # FIXME: This high-level API method should be able to take a query that is
149
+ # a string or a file. If its a file, then the downstream Process will
150
+ # be responsible for reading the file and passing the contents to the
151
+ # search method based upon whether the content is text, image or audio.
152
+
153
+ # Perform a search for documents based on a query.
154
+ # @param query [String] the search query string.
155
+ # @param options [Hash] additional search options, such as filters and limits.
156
+ # @example
157
+ # response = Ragdoll.search(query: "example search")
158
+ # response[:results].each { |result| puts result[:document_title] }
159
+ # @return [Hash] the search results.
160
+ def search(*args, **kwargs)
161
+ Ragdoll::Core.search(*args, **kwargs)
162
+ end
163
+
164
+ # Enhance a prompt with additional context.
165
+ # @param prompt [String] the original prompt to enhance.
166
+ # @param context_limit [Integer] the number of context chunks to include.
167
+ # @param options [Hash] additional options for enhancing the prompt.
168
+ # @example
169
+ # enhanced = Ragdoll.enhance_prompt(prompt: "What is AI?", context_limit: 3)
170
+ # puts enhanced[:enhanced_prompt]
171
+ # @return [Hash] the enhanced prompt data.
172
+ def enhance_prompt(*args, **kwargs)
173
+ Ragdoll::Core.enhance_prompt(*args, **kwargs)
174
+ end
175
+
176
+ # Retrieve context for a given query.
177
+ # @param query [String] the query to retrieve context for.
178
+ # @param limit [Integer] the number of context chunks to retrieve.
179
+ # @param options [Hash] additional options for context retrieval.
180
+ # @example
181
+ # context = Ragdoll.get_context(query: "AI", limit: 5)
182
+ # puts context[:combined_context]
183
+ # @return [Hash] the context data.
184
+ def get_context(*args, **kwargs)
185
+ Ragdoll::Core.get_context(*args, **kwargs)
186
+ end
187
+
188
+ # Search for content similar to a given query.
189
+ # @param query [String] the query to find similar content for.
190
+ # @param options [Hash] additional options for the search, such as filters and limits.
191
+ # @example
192
+ # similar_content = Ragdoll.search_similar_content(query: "AI")
193
+ # similar_content.each { |content| puts content[:document_title] }
194
+ # @return [Array<Hash>] an array of similar content data.
195
+ def search_similar_content(*args, **kwargs)
196
+ Ragdoll::Core.search_similar_content(*args, **kwargs)
197
+ end
198
+
199
+
200
+ ###############
201
+ # Misc. Stuff #
202
+ ###############
203
+
204
+ # Retrieve statistics about the system.
205
+ # @example
206
+ # stats = Ragdoll.stats
207
+ # puts stats[:total_documents]
208
+ # @return [Hash] the system statistics.
209
+ def stats(*args, **kwargs)
210
+ Ragdoll::Core.stats(*args, **kwargs)
211
+ end
212
+
213
+ # Check if the system is healthy.
214
+ # @example
215
+ # puts "System is healthy" if Ragdoll.healthy?
216
+ # @return [Boolean] true if the system is healthy.
217
+ def healthy?(*args, **kwargs)
218
+ Ragdoll::Core.healthy?(*args, **kwargs)
219
+ end
220
+
221
+ # Retrieve the client instance.
222
+ # @example
223
+ # client = Ragdoll.client
224
+ # puts client.inspect
225
+ # @return [Ragdoll::Core::Client] the client instance.
226
+ def client(*args, **kwargs)
227
+ Ragdoll::Core.client(*args, **kwargs)
228
+ end
229
+
230
+ # Retrieve the version information of the Ragdoll modules.
231
+ # @example
232
+ # versions = Ragdoll.version
233
+ # versions.each { |version| puts version }
234
+ # @return [Array<String>] an array of version strings for each module.
235
+ def version
236
+ versions = []
237
+
238
+ ObjectSpace.each_object(Module) do |mod|
239
+ if mod.name =~ /^Ragdoll::\w+$/
240
+ if defined?(mod::VERSION) && mod::VERSION.is_a?(String)
241
+ versions << "#{mod.name}: #{mod::VERSION}"
242
+ end
243
+ end
244
+ end
245
+
246
+ versions
247
+ end
248
+ end
12
249
  end