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
|
@@ -2,33 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
require 'httparty'
|
|
4
4
|
|
|
5
|
+
# Ollama local or hosted API provider adapter.
|
|
5
6
|
class Prescient::Provider::Ollama < Prescient::Base
|
|
6
7
|
include HTTParty
|
|
7
8
|
|
|
8
|
-
EMBEDDING_DIMENSIONS = 768 # nomic-embed-text dimensions
|
|
9
|
-
|
|
10
9
|
def initialize(**options)
|
|
11
10
|
super
|
|
12
11
|
self.class.base_uri(@options[:url])
|
|
13
12
|
self.class.default_timeout(@options[:timeout] || 60)
|
|
14
13
|
end
|
|
15
14
|
|
|
15
|
+
# Generate an embedding through Ollama's `/api/embed` endpoint.
|
|
16
|
+
# @param text [String] Text to embed
|
|
17
|
+
# @return [Array<Float>] Embedding vector from the first returned input item
|
|
16
18
|
def generate_embedding(text, **_options)
|
|
17
19
|
handle_errors do
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
embeddings = fetch_and_parse('post', '/api/embed',
|
|
21
|
+
root_key: 'embeddings',
|
|
22
|
+
headers: { 'Content-Type' => 'application/json' },
|
|
23
|
+
body: {
|
|
24
|
+
model: @options[:embedding_model],
|
|
25
|
+
input: clean_text(text),
|
|
26
|
+
}.to_json)
|
|
27
|
+
|
|
28
|
+
embedding = embeddings.is_a?(Array) ? embeddings.first : nil # : Array[Float]?
|
|
29
|
+
raise Prescient::InvalidResponseError, 'No embedding returned' unless embedding.is_a?(Array)
|
|
30
|
+
|
|
31
|
+
expected_dimensions = @options[:embedding_dimensions]
|
|
32
|
+
if expected_dimensions && embedding.length != expected_dimensions
|
|
33
|
+
raise Prescient::InvalidResponseError,
|
|
34
|
+
"Invalid embedding dimensions: expected #{expected_dimensions}, got #{embedding.length}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
embedding
|
|
29
38
|
end
|
|
30
39
|
end
|
|
31
40
|
|
|
41
|
+
# Generate text through Ollama's generation endpoint.
|
|
42
|
+
# @param prompt [String] Prompt to send
|
|
43
|
+
# @param context_items [Array<Hash, String>] Optional context items
|
|
44
|
+
# @return [Hash] Normalized response data
|
|
32
45
|
def generate_response(prompt, context_items = [], **options)
|
|
33
46
|
handle_errors do
|
|
34
47
|
request_options = prepare_generate_response(prompt, context_items, **options)
|
|
@@ -54,6 +67,12 @@ class Prescient::Provider::Ollama < Prescient::Base
|
|
|
54
67
|
end
|
|
55
68
|
end
|
|
56
69
|
|
|
70
|
+
# Check whether the configured Ollama models are available locally.
|
|
71
|
+
#
|
|
72
|
+
# `reachable` indicates the Ollama API answered successfully. `ready`
|
|
73
|
+
# indicates that both configured models are present in the local model list.
|
|
74
|
+
#
|
|
75
|
+
# @return [Hash] Provider health information
|
|
57
76
|
def health_check
|
|
58
77
|
handle_errors do
|
|
59
78
|
models = available_models
|
|
@@ -63,6 +82,7 @@ class Prescient::Provider::Ollama < Prescient::Base
|
|
|
63
82
|
{
|
|
64
83
|
status: 'healthy',
|
|
65
84
|
provider: 'ollama',
|
|
85
|
+
reachable: true,
|
|
66
86
|
url: @options[:url],
|
|
67
87
|
models_available: models.map { |m| m[:name] },
|
|
68
88
|
embedding_model: {
|
|
@@ -78,14 +98,19 @@ class Prescient::Provider::Ollama < Prescient::Base
|
|
|
78
98
|
end
|
|
79
99
|
rescue Prescient::Error => e
|
|
80
100
|
{
|
|
81
|
-
status:
|
|
82
|
-
provider:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
status: 'unavailable',
|
|
102
|
+
provider: 'ollama',
|
|
103
|
+
reachable: false,
|
|
104
|
+
error: e.class.name,
|
|
105
|
+
message: e.message,
|
|
106
|
+
url: @options[:url],
|
|
107
|
+
ready: false,
|
|
86
108
|
}
|
|
87
109
|
end
|
|
88
110
|
|
|
111
|
+
# List models currently available from Ollama.
|
|
112
|
+
# @return [Array<Hash>] Model descriptors including name, size, digest,
|
|
113
|
+
# modified_at, and booleans for configured embedding/chat roles
|
|
89
114
|
def available_models
|
|
90
115
|
return @_available_models if defined?(@_available_models)
|
|
91
116
|
|
|
@@ -98,6 +123,9 @@ class Prescient::Provider::Ollama < Prescient::Base
|
|
|
98
123
|
end
|
|
99
124
|
end
|
|
100
125
|
|
|
126
|
+
# Pull a model into the Ollama installation.
|
|
127
|
+
# @param model_name [String] Model identifier to download
|
|
128
|
+
# @return [Hash] Pull result
|
|
101
129
|
def pull_model(model_name)
|
|
102
130
|
handle_errors do
|
|
103
131
|
fetch_and_parse('post', '/api/pull',
|
|
@@ -152,21 +180,4 @@ class Prescient::Provider::Ollama < Prescient::Base
|
|
|
152
180
|
response.parsed_response[root_key]
|
|
153
181
|
end
|
|
154
182
|
|
|
155
|
-
def validate_response!(response, operation)
|
|
156
|
-
return if response.success?
|
|
157
|
-
|
|
158
|
-
case response.code
|
|
159
|
-
when 404
|
|
160
|
-
raise Prescient::ModelNotAvailableError, "Model not available for #{operation}"
|
|
161
|
-
when 429
|
|
162
|
-
raise Prescient::RateLimitError, "Rate limit exceeded for #{operation}"
|
|
163
|
-
when 401, 403
|
|
164
|
-
raise Prescient::AuthenticationError, "Authentication failed for #{operation}"
|
|
165
|
-
when 500..599
|
|
166
|
-
raise Prescient::Error, "Ollama server error during #{operation}: #{response.body}"
|
|
167
|
-
else
|
|
168
|
-
raise Prescient::Error,
|
|
169
|
-
"Ollama request failed for #{operation}: HTTP #{response.code} - #{response.message}"
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
183
|
end
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require 'httparty'
|
|
4
4
|
|
|
5
|
+
# OpenAI API provider adapter.
|
|
5
6
|
class Prescient::Provider::OpenAI < Prescient::Base
|
|
6
7
|
include HTTParty
|
|
7
8
|
|
|
8
9
|
base_uri 'https://api.openai.com'
|
|
9
10
|
|
|
11
|
+
# Known embedding dimensions for OpenAI embedding models.
|
|
10
12
|
EMBEDDING_DIMENSIONS = {
|
|
11
13
|
'text-embedding-3-small' => 1536,
|
|
12
14
|
'text-embedding-3-large' => 3072,
|
|
@@ -18,6 +20,9 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
18
20
|
self.class.default_timeout(@options[:timeout] || 60)
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# Generate an embedding through the OpenAI embeddings API.
|
|
24
|
+
# @param text [String] Text to embed
|
|
25
|
+
# @return [Array<Float>] Embedding vector
|
|
21
26
|
def generate_embedding(text, **_options)
|
|
22
27
|
handle_errors do
|
|
23
28
|
clean_text_input = clean_text(text)
|
|
@@ -38,11 +43,20 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
38
43
|
embedding_data = response.parsed_response.dig('data', 0, 'embedding')
|
|
39
44
|
raise Prescient::InvalidResponseError, 'No embedding returned' unless embedding_data
|
|
40
45
|
|
|
41
|
-
expected_dimensions = EMBEDDING_DIMENSIONS[@options[:embedding_model]] ||
|
|
42
|
-
|
|
46
|
+
expected_dimensions = EMBEDDING_DIMENSIONS[@options[:embedding_model]] || @options[:embedding_dimensions]
|
|
47
|
+
unless expected_dimensions
|
|
48
|
+
raise Prescient::Error,
|
|
49
|
+
"Embedding dimensions are required for model #{@options[:embedding_model]}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
validate_embedding_dimensions(embedding_data, expected_dimensions)
|
|
43
53
|
end
|
|
44
54
|
end
|
|
45
55
|
|
|
56
|
+
# Generate a response through the OpenAI chat completions API.
|
|
57
|
+
# @param prompt [String] Prompt to send
|
|
58
|
+
# @param context_items [Array<Hash, String>] Optional context items
|
|
59
|
+
# @return [Hash] Normalized response data
|
|
46
60
|
def generate_response(prompt, context_items = [], **options)
|
|
47
61
|
handle_errors do
|
|
48
62
|
formatted_prompt = build_prompt(prompt, context_items)
|
|
@@ -83,6 +97,12 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
83
97
|
end
|
|
84
98
|
end
|
|
85
99
|
|
|
100
|
+
# Check OpenAI model availability via `/v1/models`.
|
|
101
|
+
#
|
|
102
|
+
# `reachable` indicates the API answered successfully. `ready` indicates that
|
|
103
|
+
# both configured models appear in the returned model list.
|
|
104
|
+
#
|
|
105
|
+
# @return [Hash] Provider health information
|
|
86
106
|
def health_check
|
|
87
107
|
handle_errors do
|
|
88
108
|
response = self.class.get('/v1/models',
|
|
@@ -98,6 +118,7 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
98
118
|
{
|
|
99
119
|
status: 'healthy',
|
|
100
120
|
provider: 'openai',
|
|
121
|
+
reachable: true,
|
|
101
122
|
models_available: models.map { |m| m['id'] },
|
|
102
123
|
embedding_model: {
|
|
103
124
|
name: @options[:embedding_model],
|
|
@@ -111,22 +132,29 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
111
132
|
}
|
|
112
133
|
else
|
|
113
134
|
{
|
|
114
|
-
status:
|
|
115
|
-
provider:
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
status: 'unhealthy',
|
|
136
|
+
provider: 'openai',
|
|
137
|
+
reachable: true,
|
|
138
|
+
error: "HTTP #{response.code}",
|
|
139
|
+
message: response.message,
|
|
140
|
+
ready: false,
|
|
118
141
|
}
|
|
119
142
|
end
|
|
120
143
|
end
|
|
121
144
|
rescue Prescient::Error => e
|
|
122
145
|
{
|
|
123
|
-
status:
|
|
124
|
-
provider:
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
status: 'unavailable',
|
|
147
|
+
provider: 'openai',
|
|
148
|
+
reachable: false,
|
|
149
|
+
error: e.class.name,
|
|
150
|
+
message: e.message,
|
|
151
|
+
ready: false,
|
|
127
152
|
}
|
|
128
153
|
end
|
|
129
154
|
|
|
155
|
+
# List models available to the configured OpenAI account.
|
|
156
|
+
#
|
|
157
|
+
# @return [Array<Hash>] Model descriptors
|
|
130
158
|
def list_models
|
|
131
159
|
handle_errors do
|
|
132
160
|
response = self.class.get('/v1/models',
|
|
@@ -157,25 +185,4 @@ class Prescient::Provider::OpenAI < Prescient::Base
|
|
|
157
185
|
raise Prescient::Error, "Missing required options: #{missing_options.join(', ')}"
|
|
158
186
|
end
|
|
159
187
|
|
|
160
|
-
private
|
|
161
|
-
|
|
162
|
-
def validate_response!(response, operation)
|
|
163
|
-
return if response.success?
|
|
164
|
-
|
|
165
|
-
case response.code
|
|
166
|
-
when 400
|
|
167
|
-
raise Prescient::Error, "Bad request for #{operation}: #{response.body}"
|
|
168
|
-
when 401
|
|
169
|
-
raise Prescient::AuthenticationError, "Authentication failed for #{operation}"
|
|
170
|
-
when 403
|
|
171
|
-
raise Prescient::AuthenticationError, "Forbidden access for #{operation}"
|
|
172
|
-
when 429
|
|
173
|
-
raise Prescient::RateLimitError, "Rate limit exceeded for #{operation}"
|
|
174
|
-
when 500..599
|
|
175
|
-
raise Prescient::Error, "OpenAI server error during #{operation}: #{response.body}"
|
|
176
|
-
else
|
|
177
|
-
raise Prescient::Error,
|
|
178
|
-
"OpenAI request failed for #{operation}: HTTP #{response.code} - #{response.message}"
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
188
|
end
|
data/lib/prescient/version.rb
CHANGED
data/lib/prescient.rb
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'prescient/version'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Error < StandardError; end
|
|
7
|
-
class ConnectionError < Error; end
|
|
8
|
-
class AuthenticationError < Error; end
|
|
9
|
-
class RateLimitError < Error; end
|
|
10
|
-
class ModelNotAvailableError < Error; end
|
|
11
|
-
class InvalidResponseError < Error; end
|
|
12
|
-
|
|
13
|
-
module Provider
|
|
14
|
-
# Module for AI provider implementations
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
4
|
+
require_relative 'prescient/errors'
|
|
5
|
+
require_relative 'prescient/pgvector'
|
|
18
6
|
require_relative 'prescient/base'
|
|
19
7
|
require_relative 'prescient/provider/ollama'
|
|
20
8
|
require_relative 'prescient/provider/anthropic'
|
|
@@ -22,69 +10,205 @@ require_relative 'prescient/provider/openai'
|
|
|
22
10
|
require_relative 'prescient/provider/huggingface'
|
|
23
11
|
require_relative 'prescient/client'
|
|
24
12
|
|
|
13
|
+
# Main Prescient module for AI provider abstraction
|
|
14
|
+
#
|
|
15
|
+
# Prescient provides a unified interface for working with multiple AI providers
|
|
16
|
+
# including Ollama, OpenAI, Anthropic, and Hugging Face. It supports both
|
|
17
|
+
# embedding generation and text completion with configurable context handling.
|
|
18
|
+
#
|
|
19
|
+
# @example Basic usage
|
|
20
|
+
# Prescient.configure do |config|
|
|
21
|
+
# config.add_provider(:openai, Prescient::Provider::OpenAI,
|
|
22
|
+
# api_key: 'your-api-key')
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# client = Prescient.client(:openai)
|
|
26
|
+
# response = client.generate_response("Hello, world!")
|
|
27
|
+
#
|
|
28
|
+
# @example Embedding generation
|
|
29
|
+
# embedding = client.generate_embedding("Some text to embed")
|
|
30
|
+
# puts embedding.length # => 1536 (for OpenAI text-embedding-3-small)
|
|
25
31
|
module Prescient
|
|
26
|
-
# Configure
|
|
32
|
+
# Configure Prescient with custom settings and providers
|
|
33
|
+
#
|
|
34
|
+
# @example Configure with custom provider
|
|
35
|
+
# Prescient.configure do |config|
|
|
36
|
+
# config.default_provider = :openai
|
|
37
|
+
# config.timeout = 60
|
|
38
|
+
# config.add_provider(:openai, Prescient::Provider::OpenAI,
|
|
39
|
+
# api_key: 'your-key')
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# @yield [config] Configuration block
|
|
43
|
+
# @yieldparam config [Configuration] The configuration object
|
|
44
|
+
# @return [void]
|
|
27
45
|
def self.configure
|
|
28
46
|
yield(configuration)
|
|
29
47
|
end
|
|
30
48
|
|
|
49
|
+
# Get the current configuration instance
|
|
50
|
+
#
|
|
51
|
+
# @return [Configuration] The current configuration
|
|
31
52
|
def self.configuration
|
|
32
53
|
@_configuration ||= Configuration.new
|
|
33
54
|
end
|
|
34
55
|
|
|
56
|
+
# Reset configuration to defaults
|
|
57
|
+
#
|
|
58
|
+
# @return [Configuration] New configuration instance
|
|
35
59
|
def self.reset_configuration!
|
|
36
60
|
@_configuration = Configuration.new
|
|
37
61
|
end
|
|
38
62
|
|
|
63
|
+
# Configuration class for managing Prescient settings and providers
|
|
64
|
+
#
|
|
65
|
+
# Handles global settings like timeouts and retry behavior, as well as
|
|
66
|
+
# provider registration and instantiation.
|
|
39
67
|
class Configuration
|
|
68
|
+
# @return [Array<Symbol>] Built-in provider option keys removed from output
|
|
69
|
+
DEFAULT_SENSITIVE_KEYS = [:api_key, :password, :token, :secret].freeze
|
|
70
|
+
|
|
71
|
+
# @return [Symbol] The default provider to use when none specified
|
|
40
72
|
attr_accessor :default_provider
|
|
73
|
+
|
|
74
|
+
# @return [Integer] Default timeout in seconds for API requests
|
|
41
75
|
attr_accessor :timeout
|
|
76
|
+
|
|
77
|
+
# @return [Integer] Number of retry attempts for failed requests
|
|
42
78
|
attr_accessor :retry_attempts
|
|
79
|
+
|
|
80
|
+
# @return [Float] Delay between retry attempts in seconds
|
|
43
81
|
attr_accessor :retry_delay
|
|
82
|
+
|
|
83
|
+
# @return [Array<Symbol>] List of fallback providers to try when primary fails
|
|
84
|
+
attr_accessor :fallback_providers
|
|
85
|
+
|
|
86
|
+
# @return [Array<Symbol>] Additional keys removed from provider information
|
|
87
|
+
attr_reader :sensitive_keys
|
|
88
|
+
|
|
89
|
+
# @return [Hash] Registered providers configuration
|
|
44
90
|
attr_reader :providers
|
|
45
91
|
|
|
92
|
+
# Initialize configuration with default values
|
|
46
93
|
def initialize
|
|
47
94
|
@default_provider = :ollama
|
|
48
95
|
@timeout = 30
|
|
49
96
|
@retry_attempts = 3
|
|
50
97
|
@retry_delay = 1.0
|
|
98
|
+
@fallback_providers = []
|
|
99
|
+
@sensitive_keys = []
|
|
51
100
|
@providers = {}
|
|
101
|
+
@provider_instances = {} # : Hash[Symbol, untyped]
|
|
52
102
|
end
|
|
53
103
|
|
|
104
|
+
# Configure additional keys to remove from provider information.
|
|
105
|
+
# Built-in sensitive keys are always sanitized.
|
|
106
|
+
#
|
|
107
|
+
# @param keys [Array<Symbol, String>] Additional sensitive option keys
|
|
108
|
+
# @return [Array<Symbol>] Normalized additional keys
|
|
109
|
+
def sensitive_keys=(keys)
|
|
110
|
+
@sensitive_keys = Array(keys).map(&:to_sym).uniq
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Register a new AI provider
|
|
114
|
+
#
|
|
115
|
+
# @param name [Symbol] Unique identifier for the provider
|
|
116
|
+
# @param provider_class [Class] Provider class that inherits from Base
|
|
117
|
+
# @param options [Hash] Configuration options for the provider
|
|
118
|
+
# @option options [String] :api_key API key for authenticated providers
|
|
119
|
+
# @option options [String] :url Base URL for self-hosted providers
|
|
120
|
+
# @option options [String] :model, :chat_model Model name for text generation
|
|
121
|
+
# @option options [String] :embedding_model Model name for embeddings
|
|
122
|
+
# @return [void]
|
|
123
|
+
#
|
|
124
|
+
# @example Add OpenAI provider
|
|
125
|
+
# config.add_provider(:openai, Prescient::Provider::OpenAI,
|
|
126
|
+
# api_key: 'sk-...',
|
|
127
|
+
# chat_model: 'gpt-4.1-mini')
|
|
54
128
|
def add_provider(name, provider_class, **options)
|
|
55
|
-
|
|
129
|
+
provider_name = name.to_sym
|
|
130
|
+
@providers[provider_name] = {
|
|
56
131
|
class: provider_class,
|
|
57
132
|
options: options,
|
|
58
133
|
}
|
|
134
|
+
@provider_instances.delete(provider_name)
|
|
59
135
|
end
|
|
60
136
|
|
|
137
|
+
# Instantiate a provider by name
|
|
138
|
+
#
|
|
139
|
+
# @param name [Symbol] The provider name
|
|
140
|
+
# @return [Base, nil] Provider instance or nil if not found
|
|
61
141
|
def provider(name)
|
|
62
|
-
|
|
142
|
+
provider_name = name.to_sym
|
|
143
|
+
provider_config = @providers[provider_name]
|
|
63
144
|
return nil unless provider_config
|
|
64
145
|
|
|
65
|
-
provider_config[:
|
|
146
|
+
provider_options = provider_config[:options] # : Hash[Symbol, untyped]
|
|
147
|
+
@provider_instances[provider_name] ||= provider_config[:class].new(**provider_options)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Get list of providers that currently pass {Prescient::Base#available?}.
|
|
151
|
+
#
|
|
152
|
+
# Providers are included when their health check reports `reachable: true`,
|
|
153
|
+
# or, for legacy adapters, `status == "healthy"`.
|
|
154
|
+
#
|
|
155
|
+
# @return [Array<Symbol>] List of reachable provider names
|
|
156
|
+
def available_providers
|
|
157
|
+
@providers.keys.select do |name|
|
|
158
|
+
provider(name)&.available?
|
|
159
|
+
rescue StandardError
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class << self
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def configure_default_providers(config, env)
|
|
169
|
+
config.add_provider(:ollama,
|
|
170
|
+
Prescient::Provider::Ollama,
|
|
171
|
+
url: env.fetch('OLLAMA_URL', 'http://localhost:11434'),
|
|
172
|
+
embedding_model: env.fetch('OLLAMA_EMBEDDING_MODEL', 'nomic-embed-text'),
|
|
173
|
+
chat_model: env.fetch('OLLAMA_CHAT_MODEL', 'llama3.2:3b'))
|
|
174
|
+
if env['OPENAI_API_KEY']
|
|
175
|
+
config.add_provider(
|
|
176
|
+
:openai,
|
|
177
|
+
Prescient::Provider::OpenAI,
|
|
178
|
+
api_key: env['OPENAI_API_KEY'],
|
|
179
|
+
embedding_model: env.fetch('OPENAI_EMBEDDING_MODEL', 'text-embedding-3-small'),
|
|
180
|
+
chat_model: env.fetch('OPENAI_CHAT_MODEL', 'gpt-4.1-mini'),
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
if env['ANTHROPIC_API_KEY']
|
|
184
|
+
config.add_provider(
|
|
185
|
+
:anthropic,
|
|
186
|
+
Prescient::Provider::Anthropic,
|
|
187
|
+
api_key: env['ANTHROPIC_API_KEY'],
|
|
188
|
+
model: env.fetch('ANTHROPIC_MODEL', 'claude-sonnet-4-20250514'),
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
return unless env['HUGGINGFACE_API_KEY']
|
|
193
|
+
|
|
194
|
+
config.add_provider(
|
|
195
|
+
:huggingface,
|
|
196
|
+
Prescient::Provider::HuggingFace,
|
|
197
|
+
api_key: env['HUGGINGFACE_API_KEY'],
|
|
198
|
+
embedding_model: env.fetch(
|
|
199
|
+
'HUGGINGFACE_EMBEDDING_MODEL',
|
|
200
|
+
'sentence-transformers/all-MiniLM-L6-v2',
|
|
201
|
+
),
|
|
202
|
+
chat_model: env.fetch(
|
|
203
|
+
'HUGGINGFACE_CHAT_MODEL',
|
|
204
|
+
'google/gemma-2-2b-it',
|
|
205
|
+
),
|
|
206
|
+
)
|
|
66
207
|
end
|
|
67
208
|
end
|
|
68
209
|
|
|
69
210
|
# Default configuration
|
|
70
211
|
configure do |config|
|
|
71
|
-
config
|
|
72
|
-
url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
|
|
73
|
-
embedding_model: ENV.fetch('OLLAMA_EMBEDDING_MODEL', 'nomic-embed-text'),
|
|
74
|
-
chat_model: ENV.fetch('OLLAMA_CHAT_MODEL', 'llama3.1:8b'))
|
|
75
|
-
|
|
76
|
-
config.add_provider(:anthropic, Prescient::Provider::Anthropic,
|
|
77
|
-
api_key: ENV.fetch('ANTHROPIC_API_KEY', nil),
|
|
78
|
-
model: ENV.fetch('ANTHROPIC_MODEL', 'claude-3-haiku-20240307'))
|
|
79
|
-
|
|
80
|
-
config.add_provider(:openai, Prescient::Provider::OpenAI,
|
|
81
|
-
api_key: ENV.fetch('OPENAI_API_KEY', nil),
|
|
82
|
-
embedding_model: ENV.fetch('OPENAI_EMBEDDING_MODEL', 'text-embedding-3-small'),
|
|
83
|
-
chat_model: ENV.fetch('OPENAI_CHAT_MODEL', 'gpt-3.5-turbo'))
|
|
84
|
-
|
|
85
|
-
config.add_provider(:huggingface, Prescient::Provider::HuggingFace,
|
|
86
|
-
api_key: ENV.fetch('HUGGINGFACE_API_KEY', nil),
|
|
87
|
-
embedding_model: ENV.fetch('HUGGINGFACE_EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2'),
|
|
88
|
-
chat_model: ENV.fetch('HUGGINGFACE_CHAT_MODEL', 'microsoft/DialoGPT-medium'))
|
|
212
|
+
configure_default_providers(config, ENV)
|
|
89
213
|
end
|
|
90
214
|
end
|
|
@@ -5,7 +5,7 @@ set -e
|
|
|
5
5
|
|
|
6
6
|
OLLAMA_URL=${OLLAMA_URL:-"http://localhost:11434"}
|
|
7
7
|
EMBEDDING_MODEL=${OLLAMA_EMBEDDING_MODEL:-"nomic-embed-text"}
|
|
8
|
-
CHAT_MODEL=${OLLAMA_CHAT_MODEL:-"llama3.
|
|
8
|
+
CHAT_MODEL=${OLLAMA_CHAT_MODEL:-"llama3.2:3b"}
|
|
9
9
|
|
|
10
10
|
echo "🚀 Setting up Ollama models for Prescient gem..."
|
|
11
11
|
echo "Ollama URL: $OLLAMA_URL"
|
|
@@ -74,4 +74,4 @@ main() {
|
|
|
74
74
|
echo " OLLAMA_URL=$OLLAMA_URL ruby examples/custom_contexts.rb"
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
main "$@"
|
|
77
|
+
main "$@"
|