prescient 0.1.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62c70c521da514a785f44beb284287d56a3affd451160bcc8bcad26fff713303
4
- data.tar.gz: 4b7a41690ae8395cb57ccefee607764d850256d6fe9c4341db038e28ee67c7ce
3
+ metadata.gz: a54256e23ba55fc2492fae38d687b275dcb2362df73449d3212bd03659150717
4
+ data.tar.gz: 73ab805b7a5781a1d03488605ae8d7890feda7344b41b1ac550343484b3b597a
5
5
  SHA512:
6
- metadata.gz: 5cff60610144acef5b2be9693dbe87f1bb7b231dae219ac5cc826c2720fb383ed7c6df580cfe9a9ac26eb56812bc841178d605272adde63054506006cfdd9575
7
- data.tar.gz: c6f3eef2edcd1f53eb0c41a779db4b4aae36990e40b91ff406077353fd8c9d3b10332c2c5701a17ba88c3fb2debec6eb4e8560eb14a9c483e387dc146d5f6b18
6
+ metadata.gz: e918a87b3e09affe9d5a002a3955051e7729ee565f84c42c534c99dc3fc70371fda930e63cc90801da07b4c4f5e48031c41e952088dfc3de24f0f38b579e9567
7
+ data.tar.gz: feb707947b7cc0b67d8bff933b4c584a69595b9b5e1dd3f214b4c4aa2d48c0577c996b542c238fbcef337f0f276a1bb995acc9653e8fad32262a6f6fa17caaf4
data/.rubocop.yml CHANGED
@@ -9,12 +9,10 @@ plugins:
9
9
  - rubocop-rake
10
10
 
11
11
  AllCops:
12
- # Target Ruby 3.0+ (matches gemspec requirement)
13
- TargetRubyVersion: 3.0
14
-
12
+ # Target Ruby 3.1+ (matches gemspec requirement)
13
+ TargetRubyVersion: 3.1
15
14
  # Use new cops by default
16
15
  NewCops: enable
17
-
18
16
  # Exclude common directories
19
17
  Exclude:
20
18
  - 'vendor/**/*'
@@ -169,11 +167,13 @@ Metrics/ModuleLength:
169
167
  Max: 150
170
168
 
171
169
  Metrics/ClassLength:
170
+ Enabled: false
172
171
  Max: 150
173
172
  Exclude:
174
173
  - 'test/**/*'
175
174
 
176
175
  Metrics/MethodLength:
176
+ Enabled: false
177
177
  Max: 25
178
178
  Exclude:
179
179
  - 'test/**/*'
@@ -181,6 +181,7 @@ Metrics/MethodLength:
181
181
  - 'lib/prescient/provider/*.rb'
182
182
 
183
183
  Metrics/BlockLength:
184
+ Enabled: false
184
185
  Max: 25
185
186
  Exclude:
186
187
  - 'test/**/*'
@@ -199,6 +200,7 @@ Metrics/CyclomaticComplexity:
199
200
  - 'lib/prescient/provider/*.rb'
200
201
 
201
202
  Metrics/PerceivedComplexity:
203
+ Enabled: false
202
204
  Max: 8
203
205
  Exclude:
204
206
  - 'lib/prescient/provider/*.rb'
data/.yardopts ADDED
@@ -0,0 +1,11 @@
1
+ --title "Prescient API Documentation"
2
+ --protected
3
+ --markup markdown
4
+
5
+ lib/**/*.rb
6
+ -
7
+ README.md
8
+ CHANGELOG.md
9
+ LICENSE.txt
10
+ INTEGRATION_GUIDE.md
11
+ VECTOR_SEARCH_GUIDE.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,101 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## [0.3.0] - 2025-08-14
6
+
7
+ ### Added
8
+
9
+ - Added a development quality harness covering tests, RuboCop, YARD, and RBS tasks.
10
+ - Added 99% minimum line and branch coverage requirements with expanded provider and context coverage tests.
11
+ - Added an actionlint Rake task for GitHub Actions validation.
12
+ - Added Dependabot configuration for Bundler, Docker, and GitHub Actions updates.
13
+ - Added an opt-in `Prescient::Pgvector::Store` boundary for validated embedding storage and similarity search.
14
+ - Added configurable Ollama embedding dimensions while preserving strict validation when configured.
15
+ - Added a default example-syntax quality gate that validates Ruby examples without contacting providers.
16
+
17
+ ### Changed
18
+
19
+ - Updated Ollama embeddings to use `/api/embed` with strict vector-dimension validation.
20
+ - Restricted fallback to transient/provider-service failures and added the public `ProviderError` exception for provider-side service errors.
21
+ - Reused registered provider instances and removed redundant health checks during fallback discovery.
22
+ - Updated default chat model names and README examples for current Ollama, Anthropic, OpenAI, and Hugging Face model selections.
23
+ - Updated Hugging Face inference to use the current router feature-extraction and OpenAI-compatible chat-completion APIs.
24
+ - Standardized provider reachability in health results and removed embedding padding/truncation across OpenAI and Hugging Face.
25
+ - Updated Anthropic model listing and health checks to use its `/v1/models` catalog endpoint.
26
+ - Retained OpenAI Chat Completions for the current normalized public response contract; Responses API migration remains a separately scoped compatibility change.
27
+ - Raised YARD API documentation coverage enforcement to 99% or higher and modernized gem development metadata.
28
+ - Compacted GitHub Actions into focused CI, Pages, release, and security workflows.
29
+ - Added credentialed, provider-selected live smoke tests that remain skipped by default.
30
+ - Restored the RBS/Steep development tasks with a committed Steepfile and curated core API signatures.
31
+ - Expanded Steep coverage to the base abstraction and all provider adapters, including their public operations and HTTP boundaries.
32
+ - Added configurable provider-info sensitive-key sanitization and configurable generic context-field exclusions.
33
+ - Audited README and documentation examples against the current codebase.
34
+ - Removed client `method_missing` delegation so provider-specific behavior is not exposed through the public client.
35
+ - Prevented default provider registration when required credentials are absent from the environment.
36
+ - Sanitized provider HTTP errors so raw response bodies are not exposed in exception messages.
37
+ - Removed fallback health probes before provider operations to avoid duplicate network requests and race conditions.
38
+ - Clarified health semantics: `reachable` reports transport availability and `ready` reports configured-model readiness.
39
+ - Added shared provider health-contract coverage for status, reachability, readiness, and provider identity.
40
+
41
+ ### Removed
42
+
43
+ - Removed the obsolete PDF changelog and superseded workflow definitions.
44
+
45
+ ## [0.2.0] - 2025-08-05
46
+
47
+ ### Added
48
+
49
+ - Added new featire: Providers fallbacks mechanism
50
+
51
+ ## [0.1.0] - 2025-08-05
52
+
53
+ ### Added
54
+
55
+ - Initial release of Prescient gem
56
+ - Support for four AI providers:
57
+ - **Ollama**: Local AI provider with embedding and text generation
58
+ - **Anthropic**: Claude models for text generation
59
+ - **OpenAI**: GPT models and embeddings
60
+ - **HuggingFace**: Open-source models and embeddings
61
+ - Unified client interface for all providers
62
+ - Comprehensive error handling with provider-specific exceptions:
63
+ - `ConnectionError` for network issues
64
+ - `AuthenticationError` for API key problems
65
+ - `RateLimitError` for rate limiting
66
+ - `ModelNotAvailableError` for missing models
67
+ - `InvalidResponseError` for malformed responses
68
+ - Automatic retry logic with configurable attempts and delays
69
+ - Health monitoring capabilities for all providers
70
+ - Environment variable configuration support
71
+ - Programmatic configuration system
72
+ - Context-aware generation support with context items
73
+ - Text preprocessing and embedding normalization
74
+ - Provider availability checking
75
+ - Model listing capabilities (where supported)
76
+ - Comprehensive test suite with RSpec
77
+ - Documentation and usage examples
78
+
79
+ ### Provider-Specific Features
80
+
81
+ - **Ollama**: Model management (pull, list), local deployment
82
+ - **Anthropic**: Latest Claude 3 models (Haiku, Sonnet, Opus)
83
+ - **OpenAI**: Multiple embedding dimensions, latest GPT models
84
+ - **HuggingFace**: Open-source model support, research-friendly API
85
+
86
+ ### Development
87
+
88
+ - RSpec test suite with WebMock and VCR
89
+ - RuboCop code style enforcement
90
+ - SimpleCov test coverage reporting
91
+ - Comprehensive documentation
92
+ - Example usage scripts
93
+ - Rake tasks for testing and linting
94
+
95
+ ## [0.0.0] - 2025-08-05
96
+
97
+ ### Added
98
+
99
+ - Project initialization
100
+ - Basic gem structure
101
+ - Core interfaces defined
@@ -0,0 +1,361 @@
1
+ # AI Providers Integration Guide
2
+
3
+ This guide explains how to integrate the Prescient gem into an existing Rails AI application.
4
+
5
+ For the canonical public API and runnable scripts, see the [README](README.md)
6
+ and [examples guide](examples/README.md).
7
+
8
+ ## Integration Steps
9
+
10
+ ### 1. Update Your Gemfile
11
+
12
+ ```ruby
13
+ # Add to your Gemfile
14
+ gem 'prescient', path: './prescient_gem' # Local development
15
+ # OR when published:
16
+ # gem 'prescient', '~> 0.3.0'
17
+ ```
18
+
19
+ ### 2. Replace Existing AI Service
20
+
21
+ **Before (Original OllamaService):**
22
+
23
+ ```ruby
24
+ # app/services/ollama_service.rb
25
+ class OllamaService
26
+ def generate_embedding(text)
27
+ # Direct Ollama API calls
28
+ end
29
+
30
+ def generate_response(prompt, context_items)
31
+ # Direct Ollama API calls
32
+ end
33
+ end
34
+ ```
35
+
36
+ **After (Using Prescient):**
37
+
38
+ ```ruby
39
+ # app/services/ai_service.rb
40
+ class AIService
41
+ def self.client(provider = nil)
42
+ @clients ||= {}
43
+ provider_name = provider || Rails.application.config.default_ai_provider
44
+ @clients[provider_name] ||= Prescient.client(provider_name)
45
+ end
46
+
47
+ def self.generate_embedding(text, provider: nil)
48
+ client(provider).generate_embedding(text)
49
+ rescue Prescient::Error => e
50
+ Rails.logger.error "AI embedding generation failed: #{e.message}"
51
+ raise
52
+ end
53
+
54
+ def self.generate_response(prompt, context_items = [], provider: nil, **options)
55
+ client(provider).generate_response(prompt, context_items, **options)
56
+ rescue Prescient::Error => e
57
+ Rails.logger.error "AI response generation failed: #{e.message}"
58
+ raise
59
+ end
60
+
61
+ def self.health_check(provider: nil)
62
+ client(provider).health_check
63
+ rescue Prescient::Error => e
64
+ { status: 'unhealthy', error: e.message }
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### 3. Configuration
70
+
71
+ **Create initializer:**
72
+
73
+ ```ruby
74
+ # config/initializers/prescient.rb
75
+ Prescient.configure do |config|
76
+ config.default_provider = Rails.env.production? ? :openai : :ollama
77
+ config.timeout = 60
78
+ config.retry_attempts = 3
79
+ config.retry_delay = 1.0
80
+
81
+ # Ollama (Local/Development)
82
+ config.add_provider(:ollama, Prescient::Provider::Ollama,
83
+ url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
84
+ embedding_model: ENV.fetch('OLLAMA_EMBEDDING_MODEL', 'nomic-embed-text'),
85
+ chat_model: ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.2:3b'),
86
+ timeout: 120
87
+ )
88
+
89
+ # OpenAI (Production)
90
+ if ENV['OPENAI_API_KEY'].present?
91
+ config.add_provider(:openai, Prescient::Provider::OpenAI,
92
+ api_key: ENV['OPENAI_API_KEY'],
93
+ embedding_model: ENV.fetch('OPENAI_EMBEDDING_MODEL', 'text-embedding-3-small'),
94
+ chat_model: ENV.fetch('OPENAI_CHAT_MODEL', 'gpt-4.1-mini')
95
+ )
96
+ end
97
+
98
+ # Anthropic (Alternative)
99
+ if ENV['ANTHROPIC_API_KEY'].present?
100
+ config.add_provider(:anthropic, Prescient::Provider::Anthropic,
101
+ api_key: ENV['ANTHROPIC_API_KEY'],
102
+ model: ENV.fetch('ANTHROPIC_MODEL', 'claude-sonnet-4-20250514')
103
+ )
104
+ end
105
+
106
+ # HuggingFace (Research/Open Source)
107
+ if ENV['HUGGINGFACE_API_KEY'].present?
108
+ config.add_provider(:huggingface, Prescient::Provider::HuggingFace,
109
+ api_key: ENV['HUGGINGFACE_API_KEY'],
110
+ embedding_model: ENV.fetch('HUGGINGFACE_EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2'),
111
+ chat_model: ENV.fetch('HUGGINGFACE_CHAT_MODEL', 'google/gemma-2-2b-it')
112
+ )
113
+ end
114
+ end
115
+
116
+ # Set default provider for Rails
117
+ Rails.application.config.default_ai_provider = :ollama
118
+ ```
119
+
120
+ ### 4. Update Environment Variables
121
+
122
+ ```bash
123
+ # .env or environment configuration
124
+
125
+ # Ollama (Local)
126
+ OLLAMA_URL=http://localhost:11434
127
+ OLLAMA_EMBEDDING_MODEL=nomic-embed-text
128
+ OLLAMA_CHAT_MODEL=llama3.2:3b
129
+
130
+ # OpenAI (Production)
131
+ OPENAI_API_KEY=your_openai_api_key
132
+ OPENAI_EMBEDDING_MODEL=text-embedding-3-small
133
+ OPENAI_CHAT_MODEL=gpt-4.1-mini
134
+
135
+ # Anthropic (Alternative)
136
+ ANTHROPIC_API_KEY=your_anthropic_api_key
137
+ ANTHROPIC_MODEL=claude-sonnet-4-20250514
138
+
139
+ # HuggingFace (Research)
140
+ HUGGINGFACE_API_KEY=your_huggingface_api_key
141
+ HUGGINGFACE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
142
+ HUGGINGFACE_CHAT_MODEL=google/gemma-2-2b-it
143
+ ```
144
+
145
+ ### 5. Update Controllers
146
+
147
+ **Before:**
148
+
149
+ ```ruby
150
+ class Api::V1::AiQueriesController < ApplicationController
151
+ def create
152
+ embedding = OllamaService.new.generate_embedding(params[:query])
153
+ # ... rest of the logic
154
+ end
155
+ end
156
+ ```
157
+
158
+ **After:**
159
+
160
+ ```ruby
161
+ class Api::V1::AiQueriesController < ApplicationController
162
+ def create
163
+ # Use default provider or specify one
164
+ embedding = AIService.generate_embedding(params[:query])
165
+
166
+ # Or use specific provider
167
+ # embedding = AIService.generate_embedding(params[:query], provider: :openai)
168
+
169
+ # ... rest of the logic remains the same
170
+ end
171
+
172
+ private
173
+
174
+ def generate_ai_response(query, context_items)
175
+ # Automatically uses configured provider with fallback
176
+ response = AIService.generate_response(
177
+ query,
178
+ context_items,
179
+ max_tokens: 2000,
180
+ temperature: 0.7
181
+ )
182
+
183
+ response[:response]
184
+ rescue Prescient::Error => e
185
+ Rails.logger.error "AI response failed: #{e.message}"
186
+ "I apologize, but I'm currently unable to generate a response. Please try again later."
187
+ end
188
+ end
189
+ ```
190
+
191
+ ### 6. Health Check Integration
192
+
193
+ ```ruby
194
+ # app/controllers/api/v1/system/health_controller.rb
195
+ class Api::V1::System::HealthController < ApplicationController
196
+ def show
197
+ health_status = {
198
+ database: database_health,
199
+ prescient: prescient_health,
200
+ overall: 'healthy'
201
+ }
202
+
203
+ # Set overall status based on critical components
204
+ if health_status[:prescient][:primary][:status] != 'healthy'
205
+ health_status[:overall] = 'degraded'
206
+ end
207
+
208
+ render json: health_status
209
+ end
210
+
211
+ private
212
+
213
+ def prescient_health
214
+ providers = {}
215
+
216
+ # Check primary provider
217
+ primary_provider = Rails.application.config.default_ai_provider
218
+ providers[:primary] = {
219
+ name: primary_provider,
220
+ **AIService.health_check(provider: primary_provider)
221
+ }
222
+
223
+ # Check backup providers
224
+ backup_providers = [:openai, :anthropic, :huggingface] - [primary_provider]
225
+ providers[:backups] = backup_providers.map do |provider|
226
+ {
227
+ name: provider,
228
+ **AIService.health_check(provider: provider)
229
+ }
230
+ end
231
+
232
+ providers
233
+ end
234
+
235
+ def database_health
236
+ ActiveRecord::Base.connection.execute('SELECT 1')
237
+ { status: 'healthy' }
238
+ rescue StandardError => e
239
+ { status: 'unhealthy', error: e.message }
240
+ end
241
+ end
242
+ ```
243
+
244
+ ### 7. Migration Strategy
245
+
246
+ 1. **Phase 1: Side-by-side deployment**
247
+
248
+ - Keep existing OllamaService
249
+ - Add Prescient alongside
250
+ - Test thoroughly in development
251
+
252
+ 2. **Phase 2: Gradual migration**
253
+
254
+ - Update one controller at a time
255
+ - Use feature flags to switch between old/new systems
256
+ - Monitor performance and error rates
257
+
258
+ 3. **Phase 3: Complete migration**
259
+ - Remove old OllamaService
260
+ - Update all controllers to use AIService
261
+ - Clean up unused code
262
+
263
+ ### 8. Testing Updates
264
+
265
+ ```ruby
266
+ # test/services/ai_service_test.rb
267
+ class AIServiceTest < ActiveSupport::TestCase
268
+ setup do
269
+ Prescient.configure do |config|
270
+ config.add_provider(:test, Prescient::Provider::Ollama,
271
+ url: 'http://localhost:11434',
272
+ embedding_model: 'test-embed',
273
+ chat_model: 'test-chat'
274
+ )
275
+ config.default_provider = :test
276
+ end
277
+ end
278
+
279
+ test 'generate_embedding returns an embedding vector' do
280
+ Prescient.stub(:generate_embedding, [0.1, 0.2, 0.3]) do
281
+ result = Prescient.generate_embedding('test text', provider: :test, enable_fallback: false)
282
+ assert_equal [0.1, 0.2, 0.3], result
283
+ end
284
+ end
285
+ end
286
+ ```
287
+
288
+ ### 9. Monitoring and Logging
289
+
290
+ ```ruby
291
+ # config/initializers/prescient_monitoring.rb
292
+ class PrescientMonitoring
293
+ def self.setup!
294
+ ActiveSupport::Notifications.subscribe('prescient.request') do |name, start, finish, id, payload|
295
+ duration = finish - start
296
+
297
+ Rails.logger.info "AI Provider Request: #{payload[:provider]} - #{payload[:operation]} - #{duration.round(3)}s"
298
+
299
+ # Send metrics to your monitoring system
300
+ # StatsD.increment('prescient.requests', tags: [
301
+ # "provider:#{payload[:provider]}",
302
+ # "operation:#{payload[:operation]}",
303
+ # "status:#{payload[:status]}"
304
+ # ])
305
+ end
306
+ end
307
+ end
308
+
309
+ PrescientMonitoring.setup! if Rails.env.production?
310
+ ```
311
+
312
+ ### 10. Performance Optimization
313
+
314
+ ```ruby
315
+ # app/services/ai_service.rb (enhanced)
316
+ class AIService
317
+ # Connection pooling for providers
318
+ def self.client(provider = nil)
319
+ @clients ||= {}
320
+ provider_name = provider || Rails.application.config.default_ai_provider
321
+
322
+ @clients[provider_name] ||= begin
323
+ # Use connection pooling for high-traffic applications
324
+ Prescient.client(provider_name)
325
+ end
326
+ end
327
+
328
+ # Caching for embeddings (optional)
329
+ def self.generate_embedding(text, provider: nil)
330
+ cache_key = "ai_embedding:#{ Digest::SHA256.hexdigest(text)}:#{ provider}"
331
+
332
+ Rails.cache.fetch(cache_key, expires_in: 1.hour) do
333
+ client(provider).generate_embedding(text)
334
+ end
335
+ rescue Prescient::Error => e
336
+ Rails.logger.error "AI embedding generation failed: #{e.message}"
337
+ raise
338
+ end
339
+ end
340
+ ```
341
+
342
+ ## Benefits of Migration
343
+
344
+ 1. **Provider Flexibility**: Easy switching between AI providers
345
+ 2. **Fallback Support**: Automatic fallback to backup providers
346
+ 3. **Better Error Handling**: Comprehensive error classification
347
+ 4. **Monitoring**: Built-in health checks and metrics
348
+ 5. **Testing**: Easier mocking and testing
349
+ 6. **Scalability**: Better support for different deployment scenarios
350
+ 7. **Cost Optimization**: Use local models for development, cloud for production
351
+
352
+ ## Rollback Plan
353
+
354
+ If issues arise, quickly rollback by:
355
+
356
+ 1. Revert initializer changes
357
+ 2. Switch controllers back to OllamaService
358
+ 3. Deploy previous version
359
+ 4. Debug issues separately
360
+
361
+ The gem structure allows for easy rollback since it's designed as a drop-in replacement.
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2025 Ken C. Demanawa
3
+ Copyright (c) 2025 Kenneth C. Demanawa
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal