prescient 0.1.0 → 0.2.0

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.
@@ -1,26 +1,95 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Base class for all AI provider implementations
4
+ #
5
+ # This abstract base class defines the common interface that all AI providers
6
+ # must implement. It provides shared functionality for text processing, context
7
+ # formatting, prompt building, and error handling.
8
+ #
9
+ # @abstract Subclass and implement {#generate_embedding}, {#generate_response},
10
+ # {#health_check}, and {#validate_configuration!}
11
+ #
12
+ # @example Creating a custom provider
13
+ # class MyProvider < Prescient::Base
14
+ # def generate_embedding(text, **options)
15
+ # # Implementation here
16
+ # end
17
+ #
18
+ # def generate_response(prompt, context_items = [], **options)
19
+ # # Implementation here
20
+ # end
21
+ #
22
+ # def health_check
23
+ # # Implementation here
24
+ # end
25
+ # end
26
+ #
27
+ # @author Claude Code
28
+ # @since 1.0.0
3
29
  class Prescient::Base
30
+ # @return [Hash] Configuration options for this provider instance
4
31
  attr_reader :options
5
32
 
33
+ # Initialize the provider with configuration options
34
+ #
35
+ # @param options [Hash] Provider-specific configuration options
36
+ # @option options [String] :api_key API key for authenticated providers
37
+ # @option options [String] :url Base URL for self-hosted providers
38
+ # @option options [Integer] :timeout Request timeout in seconds
39
+ # @option options [Hash] :prompt_templates Custom prompt templates
40
+ # @option options [Hash] :context_configs Context formatting configurations
6
41
  def initialize(**options)
7
42
  @options = options
8
43
  validate_configuration!
9
44
  end
10
45
 
11
- # Abstract methods that must be implemented by subclasses
12
- def generate_embedding(text)
46
+ # Generate embeddings for the given text
47
+ #
48
+ # This method must be implemented by subclasses to provide embedding
49
+ # generation functionality.
50
+ #
51
+ # @param text [String] The text to generate embeddings for
52
+ # @param options [Hash] Provider-specific options
53
+ # @return [Array<Float>] Array of embedding values
54
+ # @raise [NotImplementedError] If not implemented by subclass
55
+ # @abstract
56
+ def generate_embedding(text, **options)
13
57
  raise NotImplementedError, "#{self.class} must implement #generate_embedding"
14
58
  end
15
59
 
60
+ # Generate text response for the given prompt
61
+ #
62
+ # This method must be implemented by subclasses to provide text generation
63
+ # functionality with optional context items.
64
+ #
65
+ # @param prompt [String] The prompt to generate a response for
66
+ # @param context_items [Array<Hash, String>] Optional context items to include
67
+ # @param options [Hash] Provider-specific generation options
68
+ # @option options [Float] :temperature Sampling temperature (0.0-2.0)
69
+ # @option options [Integer] :max_tokens Maximum tokens to generate
70
+ # @option options [Float] :top_p Nucleus sampling parameter
71
+ # @return [Hash] Response hash with :response, :model, :provider keys
72
+ # @raise [NotImplementedError] If not implemented by subclass
73
+ # @abstract
16
74
  def generate_response(prompt, context_items = [], **options)
17
75
  raise NotImplementedError, "#{self.class} must implement #generate_response"
18
76
  end
19
77
 
78
+ # Check the health and availability of the provider
79
+ #
80
+ # This method must be implemented by subclasses to provide health check
81
+ # functionality.
82
+ #
83
+ # @return [Hash] Health status with :status, :provider keys and optional details
84
+ # @raise [NotImplementedError] If not implemented by subclass
85
+ # @abstract
20
86
  def health_check
21
87
  raise NotImplementedError, "#{self.class} must implement #health_check"
22
88
  end
23
89
 
90
+ # Check if the provider is currently available
91
+ #
92
+ # @return [Boolean] true if provider is healthy and available
24
93
  def available?
25
94
  health_check[:status] == 'healthy'
26
95
  rescue StandardError
@@ -29,10 +98,28 @@ class Prescient::Base
29
98
 
30
99
  protected
31
100
 
101
+ # Validate provider configuration
102
+ #
103
+ # Override this method in subclasses to validate required configuration
104
+ # options and raise appropriate errors for missing or invalid settings.
105
+ #
106
+ # @return [void]
107
+ # @raise [Prescient::Error] If configuration is invalid
32
108
  def validate_configuration!
33
109
  # Override in subclasses to validate required configuration
34
110
  end
35
111
 
112
+ # Handle and standardize errors from provider operations
113
+ #
114
+ # Wraps provider-specific operations and converts common exceptions
115
+ # into standardized Prescient error types while preserving existing
116
+ # Prescient errors.
117
+ #
118
+ # @yield The operation block to execute with error handling
119
+ # @return [Object] The result of the yielded block
120
+ # @raise [Prescient::ConnectionError] For network/timeout errors
121
+ # @raise [Prescient::InvalidResponseError] For JSON parsing errors
122
+ # @raise [Prescient::Error] For other unexpected errors
36
123
  def handle_errors
37
124
  yield
38
125
  rescue Prescient::Error
@@ -48,31 +135,40 @@ class Prescient::Base
48
135
  raise Prescient::Error, "Unexpected error: #{e.message}"
49
136
  end
50
137
 
138
+ # Normalize embedding dimensions to match expected size
139
+ #
140
+ # Ensures embedding vectors have consistent dimensions by truncating
141
+ # longer vectors or padding shorter ones with zeros.
142
+ #
143
+ # @param embedding [Array<Float>] The embedding vector to normalize
144
+ # @param target_dimensions [Integer] The desired number of dimensions
145
+ # @return [Array<Float>, nil] Normalized embedding or nil if input invalid
51
146
  def normalize_embedding(embedding, target_dimensions)
52
147
  return nil unless embedding.is_a?(Array)
53
- return embedding if embedding.length == target_dimensions
148
+ return embedding.first(target_dimensions) if embedding.length >= target_dimensions
54
149
 
55
- if embedding.length > target_dimensions
56
- # Truncate
57
- embedding.first(target_dimensions)
58
- else
59
- # Pad with zeros
60
- embedding + Array.new(target_dimensions - embedding.length, 0.0)
61
- end
150
+ embedding + Array.new(target_dimensions - embedding.length, 0.0)
62
151
  end
63
152
 
153
+ # Clean and preprocess text for AI processing
154
+ #
155
+ # Removes excess whitespace, normalizes spacing, and enforces length
156
+ # limits suitable for most AI models.
157
+ #
158
+ # @param text [String, nil] The text to clean
159
+ # @return [String] Cleaned text, empty string if input was nil/empty
64
160
  def clean_text(text)
65
- return '' if text.nil? || text.to_s.strip.empty?
66
-
67
- cleaned = text.to_s
68
- .strip
69
- .gsub(/\s+/, ' ')
70
-
71
161
  # Limit length for most models
72
- cleaned.length > 8000 ? cleaned[0, 8000] : cleaned
162
+ text.to_s.gsub(/\s+/, ' ').strip.slice(0, 8000)
73
163
  end
74
164
 
75
- # Default prompt templates - can be overridden in provider options
165
+ # Get default prompt templates
166
+ #
167
+ # Provides standard templates for system prompts and context handling
168
+ # that can be overridden via provider options.
169
+ #
170
+ # @return [Hash] Hash containing template strings with placeholders
171
+ # @private
76
172
  def default_prompt_templates
77
173
  {
78
174
  system_prompt: 'You are a helpful AI assistant. Answer questions clearly and accurately.',
@@ -96,7 +192,14 @@ class Prescient::Base
96
192
  }
97
193
  end
98
194
 
99
- # Build prompt using configurable templates
195
+ # Build formatted prompt from query and context items
196
+ #
197
+ # Creates a properly formatted prompt using configurable templates,
198
+ # incorporating context items when provided.
199
+ #
200
+ # @param query [String] The user's question or prompt
201
+ # @param context_items [Array<Hash, String>] Optional context items
202
+ # @return [String] Formatted prompt ready for AI processing
100
203
  def build_prompt(query, context_items = [])
101
204
  templates = default_prompt_templates.merge(@options[:prompt_templates] || {})
102
205
  system_prompt = templates[:system_prompt]
@@ -143,6 +246,7 @@ class Prescient::Base
143
246
  # Extract text values from hash, excluding non-textual fields
144
247
  def extract_text_values(item)
145
248
  # Common fields to exclude from embedding text
249
+ # TODO: configurable fields to exclude aside from the common ones below
146
250
  exclude_fields = ['id', '_id', 'uuid', 'created_at', 'updated_at', 'timestamp', 'version', 'status', 'active']
147
251
 
148
252
  item.filter_map { |key, value|
@@ -1,45 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prescient
4
+ # Client class for interacting with AI providers
5
+ #
6
+ # The Client provides a high-level interface for working with AI providers,
7
+ # handling error recovery, retries, and method delegation. It acts as a
8
+ # facade over the configured providers.
9
+ #
10
+ # @example Basic usage
11
+ # client = Prescient::Client.new(:openai)
12
+ # response = client.generate_response("Hello, world!")
13
+ # embedding = client.generate_embedding("Text to embed")
14
+ #
15
+ # @example Using default provider
16
+ # client = Prescient::Client.new # Uses configured default
17
+ # puts client.provider_name # => :ollama (or configured default)
18
+ #
19
+ # @author Claude Code
20
+ # @since 1.0.0
4
21
  class Client
22
+ # @return [Symbol] The name of the provider being used
5
23
  attr_reader :provider_name
24
+
25
+ # @return [Base] The underlying provider instance
6
26
  attr_reader :provider
7
27
 
8
- def initialize(provider_name = nil)
28
+ # Initialize a new client with the specified provider
29
+ #
30
+ # @param provider_name [Symbol, nil] Name of provider to use, or nil for default
31
+ # @param enable_fallback [Boolean] Whether to enable automatic fallback to other providers
32
+ # @raise [Prescient::Error] If the specified provider is not configured
33
+ def initialize(provider_name = nil, enable_fallback: true)
9
34
  @provider_name = provider_name || Prescient.configuration.default_provider
10
35
  @provider = Prescient.configuration.provider(@provider_name)
36
+ @enable_fallback = enable_fallback
11
37
 
12
38
  raise Prescient::Error, "Provider not found: #{@provider_name}" unless @provider
13
39
  end
14
40
 
41
+ # Generate embeddings for the given text
42
+ #
43
+ # Delegates to the underlying provider with automatic retry logic
44
+ # for transient failures. If fallback is enabled, tries other providers
45
+ # on persistent failures.
46
+ #
47
+ # @param text [String] The text to generate embeddings for
48
+ # @param options [Hash] Provider-specific options
49
+ # @return [Array<Float>] Array of embedding values
50
+ # @raise [Prescient::Error] If embedding generation fails on all providers
15
51
  def generate_embedding(text, **options)
16
- with_error_handling do
17
- if options.any?
52
+ if @enable_fallback
53
+ with_fallback_handling(:generate_embedding, text, **options)
54
+ else
55
+ with_error_handling do
18
56
  @provider.generate_embedding(text, **options)
19
- else
20
- @provider.generate_embedding(text)
21
57
  end
22
58
  end
23
59
  end
24
60
 
61
+ # Generate text response for the given prompt
62
+ #
63
+ # Delegates to the underlying provider with automatic retry logic
64
+ # for transient failures. Supports optional context items for RAG.
65
+ # If fallback is enabled, tries other providers on persistent failures.
66
+ #
67
+ # @param prompt [String] The prompt to generate a response for
68
+ # @param context_items [Array<Hash, String>] Optional context items
69
+ # @param options [Hash] Provider-specific generation options
70
+ # @option options [Float] :temperature Sampling temperature (0.0-2.0)
71
+ # @option options [Integer] :max_tokens Maximum tokens to generate
72
+ # @option options [Float] :top_p Nucleus sampling parameter
73
+ # @return [Hash] Response hash with :response, :model, :provider keys
74
+ # @raise [Prescient::Error] If response generation fails on all providers
25
75
  def generate_response(prompt, context_items = [], **options)
26
- with_error_handling do
27
- if options.any?
76
+ if @enable_fallback
77
+ with_fallback_handling(:generate_response, prompt, context_items, **options)
78
+ else
79
+ with_error_handling do
28
80
  @provider.generate_response(prompt, context_items, **options)
29
- else
30
- @provider.generate_response(prompt, context_items)
31
81
  end
32
82
  end
33
83
  end
34
84
 
85
+ # Check the health status of the provider
86
+ #
87
+ # @return [Hash] Health status information
35
88
  def health_check
36
89
  @provider.health_check
37
90
  end
38
91
 
92
+ # Check if the provider is currently available
93
+ #
94
+ # @return [Boolean] true if provider is healthy and available
39
95
  def available?
40
96
  @provider.available?
41
97
  end
42
98
 
99
+ # Get comprehensive information about the provider
100
+ #
101
+ # Returns details about the provider including its availability
102
+ # and configuration options (with sensitive data removed).
103
+ #
104
+ # @return [Hash] Provider information including :name, :class, :available, :options
43
105
  def provider_info
44
106
  {
45
107
  name: @provider_name,
@@ -50,11 +112,7 @@ module Prescient
50
112
  end
51
113
 
52
114
  def method_missing(method_name, ...)
53
- if @provider.respond_to?(method_name)
54
- @provider.send(method_name, ...)
55
- else
56
- super
57
- end
115
+ @provider.respond_to?(method_name) ? @provider.send(method_name, ...) : super
58
116
  end
59
117
 
60
118
  def respond_to_missing?(method_name, include_private = false)
@@ -63,6 +121,7 @@ module Prescient
63
121
 
64
122
  private
65
123
 
124
+ # TODO: configurable keys to sanitize
66
125
  def sanitize_options(options)
67
126
  sensitive_keys = [:api_key, :password, :token, :secret]
68
127
  options.reject { |key, _| sensitive_keys.include?(key.to_sym) }
@@ -86,22 +145,67 @@ module Prescient
86
145
  retry
87
146
  end
88
147
  end
148
+
149
+ def with_fallback_handling(method_name, *args, **options)
150
+ last_error = nil
151
+
152
+ providers_to_try.each_with_index do |provider_name, index|
153
+ # Use existing provider instance for primary provider, create new ones for fallbacks
154
+ provider = if index.zero? && provider_name == @provider_name
155
+ @provider
156
+ else
157
+ Prescient.configuration.provider(provider_name)
158
+ end
159
+ next unless provider
160
+
161
+ # Check if provider is available before trying
162
+ next unless provider.available?
163
+
164
+ # Use retry logic for each provider
165
+ return with_error_handling do
166
+ provider.send(method_name, *args, **options)
167
+ end
168
+ rescue Prescient::Error => e
169
+ last_error = e
170
+ # Log the error and continue to next provider
171
+ next
172
+ end
173
+
174
+ # If we get here, all providers failed
175
+ raise last_error || Prescient::Error.new("No available providers for #{method_name}")
176
+ end
177
+
178
+ def providers_to_try
179
+ providers = [@provider_name]
180
+
181
+ # Add configured fallback providers
182
+ fallback_providers = Prescient.configuration.fallback_providers
183
+ if fallback_providers && !fallback_providers.empty?
184
+ providers += fallback_providers.reject { |p| p == @provider_name }
185
+ else
186
+ # If no explicit fallbacks configured, try all available providers
187
+ available = Prescient.configuration.available_providers
188
+ providers += available.reject { |p| p == @provider_name }
189
+ end
190
+
191
+ providers.uniq
192
+ end
89
193
  end
90
194
 
91
195
  # Convenience methods for quick access
92
- def self.client(provider_name = nil)
93
- Client.new(provider_name)
196
+ def self.client(provider_name = nil, enable_fallback: true)
197
+ Client.new(provider_name, enable_fallback: enable_fallback)
94
198
  end
95
199
 
96
- def self.generate_embedding(text, provider: nil, **options)
97
- client(provider).generate_embedding(text, **options)
200
+ def self.generate_embedding(text, provider: nil, enable_fallback: true, **options)
201
+ client(provider, enable_fallback: enable_fallback).generate_embedding(text, **options)
98
202
  end
99
203
 
100
- def self.generate_response(prompt, context_items = [], provider: nil, **options)
101
- client(provider).generate_response(prompt, context_items, **options)
204
+ def self.generate_response(prompt, context_items = [], provider: nil, enable_fallback: true, **options)
205
+ client(provider, enable_fallback: enable_fallback).generate_response(prompt, context_items, **options)
102
206
  end
103
207
 
104
208
  def self.health_check(provider: nil)
105
- client(provider).health_check
209
+ client(provider, enable_fallback: false).health_check
106
210
  end
107
211
  end
@@ -157,9 +157,7 @@ class Prescient::Provider::HuggingFace < Prescient::Base
157
157
  protected
158
158
 
159
159
  def validate_configuration!
160
- required_options = [:api_key, :embedding_model, :chat_model]
161
- missing_options = required_options.select { |opt| @options[opt].nil? }
162
-
160
+ missing_options = [:api_key, :embedding_model, :chat_model].select { |opt| @options[opt].nil? }
163
161
  return unless missing_options.any?
164
162
 
165
163
  raise Prescient::Error, "Missing required options: #{missing_options.join(', ')}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prescient
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/prescient.rb CHANGED
@@ -2,14 +2,50 @@
2
2
 
3
3
  require_relative 'prescient/version'
4
4
 
5
+ # Main Prescient module for AI provider abstraction
6
+ #
7
+ # Prescient provides a unified interface for working with multiple AI providers
8
+ # including Ollama, OpenAI, Anthropic, and HuggingFace. It supports both
9
+ # embedding generation and text completion with configurable context handling.
10
+ #
11
+ # @example Basic usage
12
+ # Prescient.configure do |config|
13
+ # config.add_provider(:openai, Prescient::Provider::OpenAI,
14
+ # api_key: 'your-api-key')
15
+ # end
16
+ #
17
+ # client = Prescient.client(:openai)
18
+ # response = client.generate_response("Hello, world!")
19
+ #
20
+ # @example Embedding generation
21
+ # embedding = client.generate_embedding("Some text to embed")
22
+ # puts embedding.length # => 1536 (for OpenAI text-embedding-3-small)
23
+ #
24
+ # @author Claude Code
25
+ # @since 1.0.0
5
26
  module Prescient
27
+ # Base error class for all Prescient-specific errors
6
28
  class Error < StandardError; end
29
+
30
+ # Raised when there are connection issues with AI providers
7
31
  class ConnectionError < Error; end
32
+
33
+ # Raised when API authentication fails
8
34
  class AuthenticationError < Error; end
35
+
36
+ # Raised when API rate limits are exceeded
9
37
  class RateLimitError < Error; end
38
+
39
+ # Raised when a requested model is not available
10
40
  class ModelNotAvailableError < Error; end
41
+
42
+ # Raised when AI provider returns invalid or malformed responses
11
43
  class InvalidResponseError < Error; end
12
44
 
45
+ # Container module for AI provider implementations
46
+ #
47
+ # All provider classes should be defined within this module and inherit
48
+ # from {Prescient::Base}.
13
49
  module Provider
14
50
  # Module for AI provider implementations
15
51
  end
@@ -23,34 +59,85 @@ require_relative 'prescient/provider/huggingface'
23
59
  require_relative 'prescient/client'
24
60
 
25
61
  module Prescient
26
- # Configure the gem
62
+ # Configure Prescient with custom settings and providers
63
+ #
64
+ # @example Configure with custom provider
65
+ # Prescient.configure do |config|
66
+ # config.default_provider = :openai
67
+ # config.timeout = 60
68
+ # config.add_provider(:openai, Prescient::Provider::OpenAI,
69
+ # api_key: 'your-key')
70
+ # end
71
+ #
72
+ # @yield [config] Configuration block
73
+ # @yieldparam config [Configuration] The configuration object
74
+ # @return [void]
27
75
  def self.configure
28
76
  yield(configuration)
29
77
  end
30
78
 
79
+ # Get the current configuration instance
80
+ #
81
+ # @return [Configuration] The current configuration
31
82
  def self.configuration
32
83
  @_configuration ||= Configuration.new
33
84
  end
34
85
 
86
+ # Reset configuration to defaults
87
+ #
88
+ # @return [Configuration] New configuration instance
35
89
  def self.reset_configuration!
36
90
  @_configuration = Configuration.new
37
91
  end
38
92
 
93
+ # Configuration class for managing Prescient settings and providers
94
+ #
95
+ # Handles global settings like timeouts and retry behavior, as well as
96
+ # provider registration and instantiation.
39
97
  class Configuration
98
+ # @return [Symbol] The default provider to use when none specified
40
99
  attr_accessor :default_provider
100
+
101
+ # @return [Integer] Default timeout in seconds for API requests
41
102
  attr_accessor :timeout
103
+
104
+ # @return [Integer] Number of retry attempts for failed requests
42
105
  attr_accessor :retry_attempts
106
+
107
+ # @return [Float] Delay between retry attempts in seconds
43
108
  attr_accessor :retry_delay
109
+
110
+ # @return [Array<Symbol>] List of fallback providers to try when primary fails
111
+ attr_accessor :fallback_providers
112
+
113
+ # @return [Hash] Registered providers configuration
44
114
  attr_reader :providers
45
115
 
116
+ # Initialize configuration with default values
46
117
  def initialize
47
118
  @default_provider = :ollama
48
119
  @timeout = 30
49
120
  @retry_attempts = 3
50
121
  @retry_delay = 1.0
122
+ @fallback_providers = []
51
123
  @providers = {}
52
124
  end
53
125
 
126
+ # Register a new AI provider
127
+ #
128
+ # @param name [Symbol] Unique identifier for the provider
129
+ # @param provider_class [Class] Provider class that inherits from Base
130
+ # @param options [Hash] Configuration options for the provider
131
+ # @option options [String] :api_key API key for authenticated providers
132
+ # @option options [String] :url Base URL for self-hosted providers
133
+ # @option options [String] :model, :chat_model Model name for text generation
134
+ # @option options [String] :embedding_model Model name for embeddings
135
+ # @return [void]
136
+ #
137
+ # @example Add OpenAI provider
138
+ # config.add_provider(:openai, Prescient::Provider::OpenAI,
139
+ # api_key: 'sk-...',
140
+ # chat_model: 'gpt-4')
54
141
  def add_provider(name, provider_class, **options)
55
142
  @providers[name.to_sym] = {
56
143
  class: provider_class,
@@ -58,12 +145,27 @@ module Prescient
58
145
  }
59
146
  end
60
147
 
148
+ # Instantiate a provider by name
149
+ #
150
+ # @param name [Symbol] The provider name
151
+ # @return [Base, nil] Provider instance or nil if not found
61
152
  def provider(name)
62
153
  provider_config = @providers[name.to_sym]
63
154
  return nil unless provider_config
64
155
 
65
156
  provider_config[:class].new(**provider_config[:options])
66
157
  end
158
+
159
+ # Get list of available providers (those that are configured and healthy)
160
+ #
161
+ # @return [Array<Symbol>] List of available provider names
162
+ def available_providers
163
+ @providers.keys.select do |name|
164
+ provider(name)&.available?
165
+ rescue StandardError
166
+ false
167
+ end
168
+ end
67
169
  end
68
170
 
69
171
  # Default configuration
data/prescient.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = "Prescient provides a unified interface for AI providers including local Ollama, Anthropic Claude, OpenAI GPT, and HuggingFace models. Built for AI applications with error handling, health monitoring, and provider switching."
13
13
  spec.homepage = "https://github.com/yourcompany/prescient"
14
14
  spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.0.0"
15
+ spec.required_ruby_version = ">= 3.1.0"
16
16
 
17
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
18
  spec.metadata["homepage_uri"] = spec.homepage
@@ -31,21 +31,23 @@ Gem::Specification.new do |spec|
31
31
  spec.require_paths = ["lib"]
32
32
 
33
33
  # Runtime dependencies
34
- spec.add_dependency "httparty", "~> 0.22.0"
35
-
34
+ spec.add_dependency "httparty", "~> 0.23.1"
35
+
36
36
  # Optional dependencies for vector database integration
37
- spec.add_development_dependency "pg", "~> 1.5" # PostgreSQL adapter for pgvector integration
37
+ spec.add_development_dependency "pg", "~> 1.6" # PostgreSQL adapter for pgvector integration
38
38
 
39
39
  # Development dependencies
40
- spec.add_development_dependency "minitest", "~> 5.20"
41
- spec.add_development_dependency "mocha", "~> 2.1"
42
- spec.add_development_dependency "webmock", "~> 3.18"
43
- spec.add_development_dependency "vcr", "~> 6.1"
44
- spec.add_development_dependency "rubocop", "~> 1.50"
45
- spec.add_development_dependency "rubocop-minitest", "~> 0.35"
46
- spec.add_development_dependency "rubocop-performance", "~> 1.19"
47
- spec.add_development_dependency "rubocop-rake", "~> 0.6"
48
- spec.add_development_dependency "simplecov", "~> 0.22"
49
- spec.add_development_dependency "rake", "~> 13.0"
50
- spec.add_development_dependency "irb"
40
+ spec.add_development_dependency "minitest", "~> 5.25"
41
+ spec.add_development_dependency "mocha", "~> 2.7"
42
+ spec.add_development_dependency "webmock", "~> 3.25"
43
+ spec.add_development_dependency "vcr", "~> 6.3"
44
+ spec.add_development_dependency "rubocop", "~> 1.79"
45
+ spec.add_development_dependency "rubocop-minitest", "~> 0.38.1"
46
+ spec.add_development_dependency "rubocop-performance", "~> 1.25"
47
+ spec.add_development_dependency "rubocop-rake", "~> 0.7.1"
48
+ spec.add_development_dependency "simplecov", "~> 0.22.0"
49
+ spec.add_development_dependency "rake", "~> 13.3"
50
+ spec.add_development_dependency "irb", "~> 1.15"
51
+ spec.add_development_dependency "yard", "~> 0.9.37"
52
+ spec.add_development_dependency "kramdown", "~> 2.5"
51
53
  end