Class: Prescient::Base Abstract
- Inherits:
-
Object
- Object
- Prescient::Base
- Defined in:
- lib/prescient/base.rb
Overview
Subclass and implement #generate_embedding, #generate_response, #health_check, and any configuration validation required by #validate_configuration!
Base class for all AI provider implementations
This abstract base class defines the common interface that all AI providers must implement. It provides shared functionality for text processing, context formatting, prompt building, and error handling.
Direct Known Subclasses
Provider::Anthropic, Provider::HuggingFace, Provider::Ollama, Provider::OpenAI
Instance Attribute Summary collapse
-
#options ⇒ Hash
readonly
Configuration options for this provider instance.
-
#provider_name ⇒ Hash
readonly
Configuration options for this provider instance.
Instance Method Summary collapse
-
#available? ⇒ Boolean
Check if the provider is currently available.
-
#build_prompt(query, context_items = []) ⇒ String
protected
Build formatted prompt from query and context items.
-
#clean_text(text) ⇒ String
protected
Clean and preprocess text for AI processing.
-
#default_context_configs ⇒ Object
protected
Minimal default context configuration - users should define their own contexts.
-
#default_prompt_templates ⇒ Hash
protected
Get default prompt templates.
-
#extract_embedding_text(item, context_type = nil) ⇒ Object
protected
Extract text for embedding generation based on context configuration.
-
#extract_text_values(item) ⇒ Object
protected
Extract text values from hash, excluding non-textual fields.
-
#format_context_item(item) ⇒ Object
protected
Generic context item formatting using configurable contexts.
-
#generate_embedding(text, **options) ⇒ Array<Float>
abstract
Generate embeddings for the given text.
-
#generate_response(prompt, context_items = [], **options) ⇒ Hash
abstract
Generate text response for the given prompt.
-
#handle_errors { ... } ⇒ Object
protected
Handle and standardize errors from provider operations.
-
#health_check ⇒ Hash
abstract
Check the health and availability of the provider.
-
#initialize(**options) ⇒ Base
constructor
Initialize the provider with configuration options.
-
#validate_configuration! ⇒ void
protected
Validate provider configuration.
-
#validate_embedding_dimensions(embedding, target_dimensions) ⇒ Array<Float>
protected
Validate embedding dimensions against the configured model dimension.
Constructor Details
#initialize(**options) ⇒ Base
Initialize the provider with configuration options
46 47 48 49 50 |
# File 'lib/prescient/base.rb', line 46 def initialize(**) @options = @provider_name = .fetch(:provider_name, self.class.to_s.split('::').last).to_s.sub(/\A./, &:upcase) validate_configuration! end |
Instance Attribute Details
#options ⇒ Hash (readonly)
Returns Configuration options for this provider instance.
33 34 35 |
# File 'lib/prescient/base.rb', line 33 def @options end |
#provider_name ⇒ Hash (readonly)
Returns Configuration options for this provider instance.
33 34 35 |
# File 'lib/prescient/base.rb', line 33 def provider_name @provider_name end |
Instance Method Details
#available? ⇒ Boolean
Check if the provider is currently available
Returns true when the health check reports reachable: true.
For legacy adapters that only return a status string, status == "healthy"
is also treated as available.
104 105 106 107 108 109 |
# File 'lib/prescient/base.rb', line 104 def available? health = health_check health.key?(:reachable) ? health[:reachable] == true : health[:status] == 'healthy' rescue StandardError false end |
#build_prompt(query, context_items = []) ⇒ String (protected)
Build formatted prompt from query and context items
Creates a properly formatted prompt using configurable templates, incorporating context items when provided.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/prescient/base.rb', line 219 def build_prompt(query, context_items = []) templates = default_prompt_templates.merge(@options[:prompt_templates] || {}) system_prompt = templates[:system_prompt] if context_items.empty? templates[:no_context_template] % { system_prompt: system_prompt, query: query, } else context_text = context_items.map.with_index(1) { |item, index| "#{index}. #{format_context_item(item)}" }.join("\n\n") templates[:with_context_template] % { system_prompt: system_prompt, context: context_text, query: query, } end end |
#clean_text(text) ⇒ String (protected)
Clean and preprocess text for AI processing
Removes excess whitespace, normalizes spacing, and truncates to the library's current 8,000-character input ceiling.
176 177 178 179 |
# File 'lib/prescient/base.rb', line 176 def clean_text(text) # Limit length for most models text.to_s.gsub(/\s+/, ' ').strip.slice(0, 8000) end |
#default_context_configs ⇒ Object (protected)
Minimal default context configuration - users should define their own contexts
242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/prescient/base.rb', line 242 def default_context_configs = [] # : Array[untyped] fields = [] # : Array[untyped] { # Generic fallback configuration - works with any hash structure 'default' => { fields: fields, # Will be dynamically determined from item keys format: nil, # Will use fallback formatting embedding_fields: , # Will use all string/text fields }, } end |
#default_prompt_templates ⇒ Hash (protected)
Get default prompt templates
Provides standard templates for system prompts and context handling that can be overridden via provider options.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/prescient/base.rb', line 188 def default_prompt_templates { system_prompt: 'You are a helpful AI assistant. Answer questions clearly and accurately.', no_context_template: <<~TEMPLATE.strip, with_context_template: <<~TEMPLATE.strip, %<system_prompt>s Use the following context to answer the question. If the context doesn't contain relevant information, say so clearly. Context: %<context>s Question: %<query>s Please provide a helpful response based on the context above. TEMPLATE } end |
#extract_embedding_text(item, context_type = nil) ⇒ Object (protected)
Extract text for embedding generation based on context configuration
257 258 259 260 261 262 263 |
# File 'lib/prescient/base.rb', line 257 def (item, context_type = nil) return item.to_s unless item.is_a?(Hash) config = resolve_context_config(item, context_type) text_values = extract_configured_fields(item, config) || extract_text_values(item) text_values.join(' ').strip end |
#extract_text_values(item) ⇒ Object (protected)
Extract text values from hash, excluding non-textual fields
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/prescient/base.rb', line 266 def extract_text_values(item) # Common fields to exclude from embedding text. Provider-specific fields can # be added with the :context_excluded_fields option. default_excluded_fields = ['id', '_id', 'uuid', 'created_at', 'updated_at', 'timestamp', 'version', 'status', 'active'] configured_fields = Array(@options[:context_excluded_fields]) # : Array[untyped] configured_excluded_fields = configured_fields.map { |field| field.to_s.downcase } exclude_fields = default_excluded_fields | configured_excluded_fields item.filter_map { |key, value| next if exclude_fields.include?(key.to_s.downcase) next unless value.is_a?(String) || value.is_a?(Numeric) next if value.to_s.strip.empty? value.to_s } end |
#format_context_item(item) ⇒ Object (protected)
Generic context item formatting using configurable contexts
285 286 287 288 289 290 291 |
# File 'lib/prescient/base.rb', line 285 def format_context_item(item) case item when Hash then format_hash_item(item) when String then item else item.to_s end end |
#generate_embedding(text, **options) ⇒ Array<Float>
Generate embeddings for the given text
This method must be implemented by subclasses to provide embedding generation functionality.
62 63 64 |
# File 'lib/prescient/base.rb', line 62 def (text, **) raise NotImplementedError, "#{self.class} must implement #generate_embedding" end |
#generate_response(prompt, context_items = [], **options) ⇒ Hash
Generate text response for the given prompt
This method must be implemented by subclasses to provide text generation functionality with optional context items.
80 81 82 |
# File 'lib/prescient/base.rb', line 80 def generate_response(prompt, context_items = [], **) raise NotImplementedError, "#{self.class} must implement #generate_response" end |
#handle_errors { ... } ⇒ Object (protected)
Handle and standardize errors from provider operations
Wraps provider-specific operations and converts common exceptions into standardized Prescient error types while preserving existing Prescient errors.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/prescient/base.rb', line 135 def handle_errors yield rescue Prescient::Error # Re-raise Prescient errors without wrapping raise rescue Net::ReadTimeout, Net::OpenTimeout => e raise Prescient::ConnectionError, "Request timeout: #{e.}" rescue Net::HTTPError => e raise Prescient::ConnectionError, "HTTP error: #{e.}" rescue JSON::ParserError => e raise Prescient::InvalidResponseError, "Invalid JSON response: #{e.}" rescue StandardError => e raise Prescient::Error, "Unexpected error: #{e.}" end |
#health_check ⇒ Hash
Check the health and availability of the provider
This method must be implemented by subclasses to provide health check functionality.
93 94 95 |
# File 'lib/prescient/base.rb', line 93 def health_check raise NotImplementedError, "#{self.class} must implement #health_check" end |
#validate_configuration! ⇒ void (protected)
This method returns an undefined value.
Validate provider configuration
Override this method in subclasses to validate required configuration options and raise appropriate errors for missing or invalid settings.
120 121 122 |
# File 'lib/prescient/base.rb', line 120 def validate_configuration! # Override in subclasses to validate required configuration end |
#validate_embedding_dimensions(embedding, target_dimensions) ⇒ Array<Float> (protected)
Validate embedding dimensions against the configured model dimension.
Embedding dimensions are part of the vector-storage contract. Vectors are never padded or truncated because either operation changes their meaning.
160 161 162 163 164 165 166 167 |
# File 'lib/prescient/base.rb', line 160 def (, target_dimensions) raise Prescient::InvalidResponseError, 'Embedding response is not an array' unless .is_a?(Array) return if .length == target_dimensions raise Prescient::InvalidResponseError, "Invalid embedding dimensions: expected #{target_dimensions}, got #{.length}" end |