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.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -2
- data/.yardopts +14 -0
- data/CHANGELOG.md +64 -0
- data/CHANGELOG.pdf +0 -0
- data/INTEGRATION_GUIDE.md +363 -0
- data/README.md +96 -38
- data/Rakefile +2 -1
- data/VECTOR_SEARCH_GUIDE.md +42 -39
- data/lib/prescient/base.rb +123 -19
- data/lib/prescient/client.rb +125 -21
- data/lib/prescient/provider/huggingface.rb +1 -3
- data/lib/prescient/version.rb +1 -1
- data/lib/prescient.rb +103 -1
- data/prescient.gemspec +17 -15
- metadata +67 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2b41fc6acb2528a3d74e5bbc58f777a34ead604d3f7bc4e58cc1ff4f8709142
|
4
|
+
data.tar.gz: 665efb1e4b6cc1ed526e91470e7c06278461e98e9d81f0ace34b0872c456251f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23eeca8c5353bcb4219a634905a7f6dc565acbfde665e0e71a0682eb3277abb9384882f70afe168ee27c435f049048f0a715b92daac3fcdc8f34f4ff2b4f7de0
|
7
|
+
data.tar.gz: 77eccf05a9bedfad68de0a262b4ebf32d35b1c1a6d4c35f0327c1c6d16814841a6361b5123b27a8ba6f05e9d999256e2b330200d3e4c7eda0763010c98759c44
|
data/.rubocop.yml
CHANGED
@@ -9,8 +9,8 @@ plugins:
|
|
9
9
|
- rubocop-rake
|
10
10
|
|
11
11
|
AllCops:
|
12
|
-
# Target Ruby 3.
|
13
|
-
TargetRubyVersion: 3.
|
12
|
+
# Target Ruby 3.1+ (matches gemspec requirement)
|
13
|
+
TargetRubyVersion: 3.1
|
14
14
|
|
15
15
|
# Use new cops by default
|
16
16
|
NewCops: enable
|
@@ -169,11 +169,13 @@ Metrics/ModuleLength:
|
|
169
169
|
Max: 150
|
170
170
|
|
171
171
|
Metrics/ClassLength:
|
172
|
+
Enabled: false
|
172
173
|
Max: 150
|
173
174
|
Exclude:
|
174
175
|
- 'test/**/*'
|
175
176
|
|
176
177
|
Metrics/MethodLength:
|
178
|
+
Enabled: false
|
177
179
|
Max: 25
|
178
180
|
Exclude:
|
179
181
|
- 'test/**/*'
|
@@ -181,6 +183,7 @@ Metrics/MethodLength:
|
|
181
183
|
- 'lib/prescient/provider/*.rb'
|
182
184
|
|
183
185
|
Metrics/BlockLength:
|
186
|
+
Enabled: false
|
184
187
|
Max: 25
|
185
188
|
Exclude:
|
186
189
|
- 'test/**/*'
|
@@ -199,6 +202,7 @@ Metrics/CyclomaticComplexity:
|
|
199
202
|
- 'lib/prescient/provider/*.rb'
|
200
203
|
|
201
204
|
Metrics/PerceivedComplexity:
|
205
|
+
Enabled: false
|
202
206
|
Max: 8
|
203
207
|
Exclude:
|
204
208
|
- 'lib/prescient/provider/*.rb'
|
data/.yardopts
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
--markup markdown
|
2
|
+
--markup-provider kramdown
|
3
|
+
--main README.md
|
4
|
+
--output-dir doc
|
5
|
+
--protected
|
6
|
+
--private
|
7
|
+
--title "Prescient Documentation"
|
8
|
+
lib/**/*.rb
|
9
|
+
-
|
10
|
+
README.md
|
11
|
+
CHANGELOG.md
|
12
|
+
LICENSE.txt
|
13
|
+
INTEGRATION_GUIDE.md
|
14
|
+
VECTOR_SEARCH_GUIDE.md
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.1.0] - 2025-08-05
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Added new featire: Providers fallbacks mechanism
|
13
|
+
|
14
|
+
## [0.1.0] - 2025-08-05
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Initial release of Prescient gem
|
19
|
+
- Support for four AI providers:
|
20
|
+
- **Ollama**: Local AI provider with embedding and text generation
|
21
|
+
- **Anthropic**: Claude models for text generation
|
22
|
+
- **OpenAI**: GPT models and embeddings
|
23
|
+
- **HuggingFace**: Open-source models and embeddings
|
24
|
+
- Unified client interface for all providers
|
25
|
+
- Comprehensive error handling with provider-specific exceptions:
|
26
|
+
- `ConnectionError` for network issues
|
27
|
+
- `AuthenticationError` for API key problems
|
28
|
+
- `RateLimitError` for rate limiting
|
29
|
+
- `ModelNotAvailableError` for missing models
|
30
|
+
- `InvalidResponseError` for malformed responses
|
31
|
+
- Automatic retry logic with configurable attempts and delays
|
32
|
+
- Health monitoring capabilities for all providers
|
33
|
+
- Environment variable configuration support
|
34
|
+
- Programmatic configuration system
|
35
|
+
- Context-aware generation support with context items
|
36
|
+
- Text preprocessing and embedding normalization
|
37
|
+
- Provider availability checking
|
38
|
+
- Model listing capabilities (where supported)
|
39
|
+
- Comprehensive test suite with RSpec
|
40
|
+
- Documentation and usage examples
|
41
|
+
|
42
|
+
### Provider-Specific Features
|
43
|
+
|
44
|
+
- **Ollama**: Model management (pull, list), local deployment
|
45
|
+
- **Anthropic**: Latest Claude 3 models (Haiku, Sonnet, Opus)
|
46
|
+
- **OpenAI**: Multiple embedding dimensions, latest GPT models
|
47
|
+
- **HuggingFace**: Open-source model support, research-friendly API
|
48
|
+
|
49
|
+
### Development
|
50
|
+
|
51
|
+
- RSpec test suite with WebMock and VCR
|
52
|
+
- RuboCop code style enforcement
|
53
|
+
- SimpleCov test coverage reporting
|
54
|
+
- Comprehensive documentation
|
55
|
+
- Example usage scripts
|
56
|
+
- Rake tasks for testing and linting
|
57
|
+
|
58
|
+
## [0.0.0] - 2025-08-05
|
59
|
+
|
60
|
+
### Added
|
61
|
+
|
62
|
+
- Project initialization
|
63
|
+
- Basic gem structure
|
64
|
+
- Core interfaces defined
|
data/CHANGELOG.pdf
ADDED
Binary file
|
@@ -0,0 +1,363 @@
|
|
1
|
+
# AI Providers Integration Guide
|
2
|
+
|
3
|
+
This guide explains how to integrate the AI Providers gem into your existing Rails AI application.
|
4
|
+
|
5
|
+
## Integration Steps
|
6
|
+
|
7
|
+
### 1. Update Your Gemfile
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# Add to your Gemfile
|
11
|
+
gem 'prescient', path: './prescient_gem' # Local development
|
12
|
+
# OR when published:
|
13
|
+
# gem 'prescient', '~> 0.1.0'
|
14
|
+
```
|
15
|
+
|
16
|
+
### 2. Replace Existing AI Service
|
17
|
+
|
18
|
+
**Before (Original OllamaService):**
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# app/services/ollama_service.rb
|
22
|
+
class OllamaService
|
23
|
+
def generate_embedding(text)
|
24
|
+
# Direct Ollama API calls
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_response(prompt, context_items)
|
28
|
+
# Direct Ollama API calls
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
**After (Using AI Providers Gem):**
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
# app/services/ai_service.rb
|
37
|
+
class AIService
|
38
|
+
def self.client(provider = nil)
|
39
|
+
@clients ||= {}
|
40
|
+
provider_name = provider || Rails.application.config.default_ai_provider
|
41
|
+
@clients[provider_name] ||= Prescient.client(provider_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.generate_embedding(text, provider: nil)
|
45
|
+
client(provider).generate_embedding(text)
|
46
|
+
rescue Prescient::Error => e
|
47
|
+
Rails.logger.error "AI embedding generation failed: #{e.message}"
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.generate_response(prompt, context_items = [], provider: nil, **options)
|
52
|
+
client(provider).generate_response(prompt, context_items, **options)
|
53
|
+
rescue Prescient::Error => e
|
54
|
+
Rails.logger.error "AI response generation failed: #{e.message}"
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.health_check(provider: nil)
|
59
|
+
client(provider).health_check
|
60
|
+
rescue Prescient::Error => e
|
61
|
+
{ status: 'unhealthy', error: e.message }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### 3. Configuration
|
67
|
+
|
68
|
+
**Create initializer:**
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# config/initializers/prescient.rb
|
72
|
+
Prescient.configure do |config|
|
73
|
+
config.default_provider = Rails.env.production? ? :openai : :ollama
|
74
|
+
config.timeout = 60
|
75
|
+
config.retry_attempts = 3
|
76
|
+
config.retry_delay = 1.0
|
77
|
+
|
78
|
+
# Ollama (Local/Development)
|
79
|
+
config.add_provider(:ollama, Prescient::Ollama::Provider,
|
80
|
+
url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
|
81
|
+
embedding_model: ENV.fetch('OLLAMA_EMBEDDING_MODEL', 'nomic-embed-text'),
|
82
|
+
chat_model: ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1:8b'),
|
83
|
+
timeout: 120
|
84
|
+
)
|
85
|
+
|
86
|
+
# OpenAI (Production)
|
87
|
+
if ENV['OPENAI_API_KEY'].present?
|
88
|
+
config.add_provider(:openai, Prescient::OpenAI::Provider,
|
89
|
+
api_key: ENV['OPENAI_API_KEY'],
|
90
|
+
embedding_model: ENV.fetch('OPENAI_EMBEDDING_MODEL', 'text-embedding-3-small'),
|
91
|
+
chat_model: ENV.fetch('OPENAI_CHAT_MODEL', 'gpt-3.5-turbo')
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Anthropic (Alternative)
|
96
|
+
if ENV['ANTHROPIC_API_KEY'].present?
|
97
|
+
config.add_provider(:anthropic, Prescient::Anthropic::Provider,
|
98
|
+
api_key: ENV['ANTHROPIC_API_KEY'],
|
99
|
+
model: ENV.fetch('ANTHROPIC_MODEL', 'claude-3-haiku-20240307')
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
# HuggingFace (Research/Open Source)
|
104
|
+
if ENV['HUGGINGFACE_API_KEY'].present?
|
105
|
+
config.add_provider(:huggingface, Prescient::HuggingFace::Provider,
|
106
|
+
api_key: ENV['HUGGINGFACE_API_KEY'],
|
107
|
+
embedding_model: ENV.fetch('HUGGINGFACE_EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2'),
|
108
|
+
chat_model: ENV.fetch('HUGGINGFACE_CHAT_MODEL', 'microsoft/DialoGPT-medium')
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Set default provider for Rails
|
114
|
+
Rails.application.config.default_ai_provider = :ollama
|
115
|
+
```
|
116
|
+
|
117
|
+
### 4. Update Environment Variables
|
118
|
+
|
119
|
+
```bash
|
120
|
+
# .env or environment configuration
|
121
|
+
|
122
|
+
# Ollama (Local)
|
123
|
+
OLLAMA_URL=http://localhost:11434
|
124
|
+
OLLAMA_EMBEDDING_MODEL=nomic-embed-text
|
125
|
+
OLLAMA_CHAT_MODEL=llama3.1:8b
|
126
|
+
|
127
|
+
# OpenAI (Production)
|
128
|
+
OPENAI_API_KEY=your_openai_api_key
|
129
|
+
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
130
|
+
OPENAI_CHAT_MODEL=gpt-3.5-turbo
|
131
|
+
|
132
|
+
# Anthropic (Alternative)
|
133
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key
|
134
|
+
ANTHROPIC_MODEL=claude-3-haiku-20240307
|
135
|
+
|
136
|
+
# HuggingFace (Research)
|
137
|
+
HUGGINGFACE_API_KEY=your_huggingface_api_key
|
138
|
+
HUGGINGFACE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
139
|
+
HUGGINGFACE_CHAT_MODEL=microsoft/DialoGPT-medium
|
140
|
+
```
|
141
|
+
|
142
|
+
### 5. Update Controllers
|
143
|
+
|
144
|
+
**Before:**
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
class Api::V1::AiQueriesController < ApplicationController
|
148
|
+
def create
|
149
|
+
embedding = OllamaService.new.generate_embedding(params[:query])
|
150
|
+
# ... rest of the logic
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
**After:**
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class Api::V1::AiQueriesController < ApplicationController
|
159
|
+
def create
|
160
|
+
# Use default provider or specify one
|
161
|
+
embedding = AIService.generate_embedding(params[:query])
|
162
|
+
|
163
|
+
# Or use specific provider
|
164
|
+
# embedding = AIService.generate_embedding(params[:query], provider: :openai)
|
165
|
+
|
166
|
+
# ... rest of the logic remains the same
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def generate_ai_response(query, context_items)
|
172
|
+
# Automatically uses configured provider with fallback
|
173
|
+
response = AIService.generate_response(
|
174
|
+
query,
|
175
|
+
context_items,
|
176
|
+
max_tokens: 2000,
|
177
|
+
temperature: 0.7
|
178
|
+
)
|
179
|
+
|
180
|
+
response[:response]
|
181
|
+
rescue Prescient::Error => e
|
182
|
+
Rails.logger.error "AI response failed: #{e.message}"
|
183
|
+
"I apologize, but I'm currently unable to generate a response. Please try again later."
|
184
|
+
end
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
### 6. Health Check Integration
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
# app/controllers/api/v1/system/health_controller.rb
|
192
|
+
class Api::V1::System::HealthController < ApplicationController
|
193
|
+
def show
|
194
|
+
health_status = {
|
195
|
+
database: database_health,
|
196
|
+
prescient: prescient_health,
|
197
|
+
overall: 'healthy'
|
198
|
+
}
|
199
|
+
|
200
|
+
# Set overall status based on critical components
|
201
|
+
if health_status[:prescient][:primary][:status] != 'healthy'
|
202
|
+
health_status[:overall] = 'degraded'
|
203
|
+
end
|
204
|
+
|
205
|
+
render json: health_status
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def prescient_health
|
211
|
+
providers = {}
|
212
|
+
|
213
|
+
# Check primary provider
|
214
|
+
primary_provider = Rails.application.config.default_ai_provider
|
215
|
+
providers[:primary] = {
|
216
|
+
name: primary_provider,
|
217
|
+
**AIService.health_check(provider: primary_provider)
|
218
|
+
}
|
219
|
+
|
220
|
+
# Check backup providers
|
221
|
+
backup_providers = [:openai, :anthropic, :huggingface] - [primary_provider]
|
222
|
+
providers[:backups] = backup_providers.map do |provider|
|
223
|
+
{
|
224
|
+
name: provider,
|
225
|
+
**AIService.health_check(provider: provider)
|
226
|
+
}
|
227
|
+
end
|
228
|
+
|
229
|
+
providers
|
230
|
+
end
|
231
|
+
|
232
|
+
def database_health
|
233
|
+
ActiveRecord::Base.connection.execute('SELECT 1')
|
234
|
+
{ status: 'healthy' }
|
235
|
+
rescue StandardError => e
|
236
|
+
{ status: 'unhealthy', error: e.message }
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
### 7. Migration Strategy
|
242
|
+
|
243
|
+
1. **Phase 1: Side-by-side deployment**
|
244
|
+
|
245
|
+
- Keep existing OllamaService
|
246
|
+
- Add AI Providers gem alongside
|
247
|
+
- Test thoroughly in development
|
248
|
+
|
249
|
+
2. **Phase 2: Gradual migration**
|
250
|
+
|
251
|
+
- Update one controller at a time
|
252
|
+
- Use feature flags to switch between old/new systems
|
253
|
+
- Monitor performance and error rates
|
254
|
+
|
255
|
+
3. **Phase 3: Complete migration**
|
256
|
+
- Remove old OllamaService
|
257
|
+
- Update all controllers to use AIService
|
258
|
+
- Clean up unused code
|
259
|
+
|
260
|
+
### 8. Testing Updates
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
# spec/services/ai_service_spec.rb
|
264
|
+
RSpec.describe AIService do
|
265
|
+
before do
|
266
|
+
Prescient.configure do |config|
|
267
|
+
config.add_provider(:test, Prescient::Ollama::Provider,
|
268
|
+
url: 'http://localhost:11434',
|
269
|
+
embedding_model: 'test-embed',
|
270
|
+
chat_model: 'test-chat'
|
271
|
+
)
|
272
|
+
config.default_provider = :test
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe '.generate_embedding' do
|
277
|
+
it 'returns embedding vector' do
|
278
|
+
# Mock the provider response
|
279
|
+
allow_any_instance_of(Prescient::Ollama::Provider)
|
280
|
+
.to receive(:generate_embedding)
|
281
|
+
.and_return([0.1, 0.2, 0.3])
|
282
|
+
|
283
|
+
result = described_class.generate_embedding('test text')
|
284
|
+
expect(result).to eq([0.1, 0.2, 0.3])
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
### 9. Monitoring and Logging
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
# config/initializers/prescient_monitoring.rb
|
294
|
+
class PrescientMonitoring
|
295
|
+
def self.setup!
|
296
|
+
ActiveSupport::Notifications.subscribe('prescient.request') do |name, start, finish, id, payload|
|
297
|
+
duration = finish - start
|
298
|
+
|
299
|
+
Rails.logger.info "AI Provider Request: #{payload[:provider]} - #{payload[:operation]} - #{duration.round(3)}s"
|
300
|
+
|
301
|
+
# Send metrics to your monitoring system
|
302
|
+
# StatsD.increment('prescient.requests', tags: [
|
303
|
+
# "provider:#{payload[:provider]}",
|
304
|
+
# "operation:#{payload[:operation]}",
|
305
|
+
# "status:#{payload[:status]}"
|
306
|
+
# ])
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
PrescientMonitoring.setup! if Rails.env.production?
|
312
|
+
```
|
313
|
+
|
314
|
+
### 10. Performance Optimization
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
# app/services/ai_service.rb (enhanced)
|
318
|
+
class AIService
|
319
|
+
# Connection pooling for providers
|
320
|
+
def self.client(provider = nil)
|
321
|
+
@clients ||= {}
|
322
|
+
provider_name = provider || Rails.application.config.default_ai_provider
|
323
|
+
|
324
|
+
@clients[provider_name] ||= begin
|
325
|
+
# Use connection pooling for high-traffic applications
|
326
|
+
Prescient.client(provider_name)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Caching for embeddings (optional)
|
331
|
+
def self.generate_embedding(text, provider: nil)
|
332
|
+
cache_key = "ai_embedding:#{ Digest::SHA256.hexdigest(text)}:#{ provider}"
|
333
|
+
|
334
|
+
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
|
335
|
+
client(provider).generate_embedding(text)
|
336
|
+
end
|
337
|
+
rescue Prescient::Error => e
|
338
|
+
Rails.logger.error "AI embedding generation failed: #{e.message}"
|
339
|
+
raise
|
340
|
+
end
|
341
|
+
end
|
342
|
+
```
|
343
|
+
|
344
|
+
## Benefits of Migration
|
345
|
+
|
346
|
+
1. **Provider Flexibility**: Easy switching between AI providers
|
347
|
+
2. **Fallback Support**: Automatic fallback to backup providers
|
348
|
+
3. **Better Error Handling**: Comprehensive error classification
|
349
|
+
4. **Monitoring**: Built-in health checks and metrics
|
350
|
+
5. **Testing**: Easier mocking and testing
|
351
|
+
6. **Scalability**: Better support for different deployment scenarios
|
352
|
+
7. **Cost Optimization**: Use local models for development, cloud for production
|
353
|
+
|
354
|
+
## Rollback Plan
|
355
|
+
|
356
|
+
If issues arise, quickly rollback by:
|
357
|
+
|
358
|
+
1. Revert initializer changes
|
359
|
+
2. Switch controllers back to OllamaService
|
360
|
+
3. Deploy previous version
|
361
|
+
4. Debug issues separately
|
362
|
+
|
363
|
+
The gem structure allows for easy rollback since it's designed as a drop-in replacement.
|