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.
rubocop:disable Metrics/ClassLength
Direct Known Subclasses
Provider::Anthropic, Provider::DeepSeek, Provider::Gemini, Provider::HuggingFace, Provider::Mistral, Provider::Ollama, Provider::OpenAI, Provider::XAI
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
48 49 50 51 52 |
# File 'lib/prescient/base.rb', line 48 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.
35 36 37 |
# File 'lib/prescient/base.rb', line 35 def @options end |
#provider_name ⇒ Hash (readonly)
Returns Configuration options for this provider instance.
35 36 37 |
# File 'lib/prescient/base.rb', line 35 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.
106 107 108 109 110 111 |
# File 'lib/prescient/base.rb', line 106 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.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/prescient/base.rb', line 221 def build_prompt(query, context_items = []) templates = default_prompt_templates.merge(@options[:prompt_templates] || {}) system_prompt = templates[:system_prompt] if context_items.empty? format(templates[:no_context_template], system_prompt: system_prompt, query: query) else context_text = context_items.map.with_index(1) do |item, index| "#{index}. #{format_context_item(item)}" end.join("\n\n") format(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.
178 179 180 181 |
# File 'lib/prescient/base.rb', line 178 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
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/prescient/base.rb', line 238 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.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/prescient/base.rb', line 190 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
253 254 255 256 257 258 259 |
# File 'lib/prescient/base.rb', line 253 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
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/prescient/base.rb', line 262 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 = %w[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 do |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 end |
#format_context_item(item) ⇒ Object (protected)
Generic context item formatting using configurable contexts
281 282 283 284 285 286 287 |
# File 'lib/prescient/base.rb', line 281 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.
64 65 66 |
# File 'lib/prescient/base.rb', line 64 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.
82 83 84 |
# File 'lib/prescient/base.rb', line 82 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.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/prescient/base.rb', line 137 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.
95 96 97 |
# File 'lib/prescient/base.rb', line 95 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.
122 123 124 |
# File 'lib/prescient/base.rb', line 122 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.
162 163 164 165 166 167 168 169 |
# File 'lib/prescient/base.rb', line 162 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 |