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 +4 -4
- data/.rubocop.yml +6 -4
- data/.yardopts +11 -0
- data/CHANGELOG.md +101 -0
- data/INTEGRATION_GUIDE.md +361 -0
- data/LICENSE.txt +1 -1
- data/README.md +217 -100
- data/Rakefile +84 -8
- data/Steepfile +17 -0
- data/VECTOR_SEARCH_GUIDE.md +74 -47
- data/docker-compose.yml +3 -5
- data/examples/README.md +45 -0
- data/examples/basic_usage.rb +2 -2
- data/examples/custom_contexts.rb +6 -6
- data/examples/custom_prompts.rb +5 -5
- data/examples/vector_search.rb +2 -2
- data/lib/prescient/base.rb +187 -28
- data/lib/prescient/client.rb +169 -31
- data/lib/prescient/errors.rb +51 -0
- data/lib/prescient/pgvector.rb +194 -0
- data/lib/prescient/provider/anthropic.rb +55 -54
- data/lib/prescient/provider/huggingface.rb +77 -79
- data/lib/prescient/provider/ollama.rb +46 -35
- data/lib/prescient/provider/openai.rb +38 -31
- data/lib/prescient/version.rb +2 -1
- data/lib/prescient.rb +160 -36
- data/scripts/setup-ollama-models.sh +2 -2
- data/sig/prescient.rbs +221 -1
- metadata +23 -184
- data/prescient.gemspec +0 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a54256e23ba55fc2492fae38d687b275dcb2362df73449d3212bd03659150717
|
|
4
|
+
data.tar.gz: 73ab805b7a5781a1d03488605ae8d7890feda7344b41b1ac550343484b3b597a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
13
|
-
TargetRubyVersion: 3.
|
|
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
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