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.
data/Rakefile CHANGED
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "rbconfig"
4
5
  require "rake/testtask"
5
6
  require "rubocop/rake_task"
7
+ require "yard"
8
+ require "yard/rake/yardoc_task"
6
9
 
7
10
  Rake::TestTask.new(:test) do |t|
8
11
  t.libs << "test"
@@ -10,21 +13,94 @@ Rake::TestTask.new(:test) do |t|
10
13
  t.test_files = FileList["test/**/*_test.rb"]
11
14
  end
12
15
 
13
- RuboCop::RakeTask.new
16
+ RuboCop::RakeTask.new(:rubocop) do |task|
17
+ task.options = ["--parallel"]
18
+ end
14
19
 
15
- desc "Run tests and linting"
16
- task default: %w[test rubocop]
20
+ YARD::Rake::YardocTask.new(:yard)
21
+ namespace :yard do
22
+ desc "Validate YARD documentation coverage"
23
+ task :validate do
24
+ require "open3"
25
+
26
+ stdout, stderr, status = Open3.capture3("bundle", "exec", "yard", "stats")
27
+ text = "#{stdout}\n#{stderr}"
28
+ puts text
29
+ abort("yard stats failed") unless status.success?
30
+
31
+ match = text.match(/([0-9]+(?:\.[0-9]+)?)%\s+documented/)
32
+ abort("Unable to determine YARD coverage") unless match
33
+
34
+ coverage = match[1].to_f
35
+ minimum = 99.0
36
+ if coverage < minimum
37
+ message = format(
38
+ "YARD coverage %<coverage>.2f%% is below %<minimum>.2f%%",
39
+ coverage: coverage,
40
+ minimum: minimum
41
+ )
42
+ abort(message)
43
+ end
17
44
 
18
- desc "Run tests with coverage"
19
- task :coverage do
20
- ENV['COVERAGE'] = 'true'
21
- Rake::Task[:test].execute
45
+ puts format("YARD coverage %.2f%%", coverage)
46
+ end
22
47
  end
23
48
 
49
+ namespace :rbs do
50
+ desc "Remove generated RBS prototype files"
51
+ task :clobber do
52
+ sh "rm -rf tmp/sig"
53
+ end
54
+
55
+ desc "Generate disposable RBS prototypes into tmp/sig"
56
+ task :prototype do
57
+ sh "rm -rf tmp/sig"
58
+ sh "mkdir -p tmp/sig"
59
+ sh "bundle exec rbs prototype rb --out-dir=tmp/sig --base-dir=lib lib"
60
+ end
61
+
62
+ desc "Validate curated RBS signatures with Steep"
63
+ task :validate do
64
+ sh "bundle exec steep check"
65
+ end
66
+
67
+ desc "Open diff between curated and generated signatures"
68
+ task :diff do
69
+ sh "diff -ru sig tmp/sig || true"
70
+ end
71
+
72
+ desc "Generate disposable RBS prototypes and validate curated signatures"
73
+ task check: %i[prototype validate]
74
+ end
75
+
76
+ namespace :examples do
77
+ desc "Validate Ruby example syntax without contacting providers"
78
+ task :syntax do
79
+ Dir["examples/**/*.rb"].sort.each do |file|
80
+ sh RbConfig.ruby, "-c", file
81
+ end
82
+ end
83
+ end
84
+
85
+ desc "Validate GitHub Actions workflows"
86
+ task :actionlint do
87
+ actionlint = if File.executable?(".tools/bin/actionlint")
88
+ ".tools/bin/actionlint"
89
+ else
90
+ ENV.fetch("ACTIONLINT", "actionlint")
91
+ end
92
+ sh actionlint
93
+ end
94
+
95
+
96
+ desc "Run tests and linting"
97
+ task default: %w[test rubocop yard yard:validate rbs:validate examples:syntax]
98
+
24
99
  desc "Console with gem loaded"
25
100
  task :console do
26
101
  require "bundler/setup"
27
102
  require "prescient"
28
103
  require "irb"
104
+ ARGV.clear
29
105
  IRB.start
30
- end
106
+ end
data/Steepfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :prescient do
4
+ signature 'sig'
5
+ library 'json'
6
+ library 'net-http'
7
+
8
+ check 'lib/prescient/version.rb'
9
+ check 'lib/prescient/errors.rb'
10
+ check 'lib/prescient.rb'
11
+ check 'lib/prescient/client.rb'
12
+ check 'lib/prescient/base.rb'
13
+ check 'lib/prescient/provider/openai.rb'
14
+ check 'lib/prescient/provider/ollama.rb'
15
+ check 'lib/prescient/provider/anthropic.rb'
16
+ check 'lib/prescient/provider/huggingface.rb'
17
+ end
@@ -2,23 +2,46 @@
2
2
 
3
3
  This guide provides a comprehensive overview of using Prescient with PostgreSQL's pgvector extension for semantic search and similarity matching.
4
4
 
5
+ The runnable companion is [`examples/vector_search.rb`](examples/vector_search.rb);
6
+ see the [examples guide](examples/README.md) for setup and the [main README](README.md)
7
+ for the provider API.
8
+
5
9
  ## Quick Start
6
10
 
11
+ For a reusable embedding store, use the library boundary rather than copying
12
+ the example's application-specific SQL:
13
+
14
+ ```ruby
15
+ require 'prescient'
16
+ require 'pg'
17
+
18
+ store = Prescient::Pgvector::Store.new(
19
+ connection: PG.connect(dbname: 'my_app'),
20
+ dimensions: 1536,
21
+ )
22
+ store.install!
23
+ store.create_index!(metric: :cosine)
24
+ ```
25
+
26
+ `Store` owns only its `prescient_embeddings` table. It does not manage document
27
+ or chunk tables, connections, migrations outside that table, or the `pg` gem.
28
+ Use `#upsert` and `#search` with embeddings of exactly the configured dimension.
29
+
7
30
  ### 1. Start Services
8
31
 
9
32
  ```bash
10
33
  # Start PostgreSQL with pgvector and Ollama
11
- docker-compose up -d postgres ollama
34
+ docker compose up -d postgres ollama
12
35
 
13
36
  # Wait for services to be ready
14
- docker-compose logs -f postgres ollama
37
+ docker compose logs -f postgres ollama
15
38
  ```
16
39
 
17
40
  ### 2. Initialize Models
18
41
 
19
42
  ```bash
20
43
  # Pull required Ollama models
21
- docker-compose up ollama-init
44
+ docker compose run --rm ollama-init
22
45
 
23
46
  # Or manually:
24
47
  ./scripts/setup-ollama-models.sh
@@ -117,7 +140,7 @@ vector_str = "[#{embedding.join(',')}]"
117
140
 
118
141
  db.exec_params(
119
142
  "INSERT INTO document_embeddings (document_id, embedding_provider, embedding_model, embedding_dimensions, embedding, embedding_text) VALUES ($1, $2, $3, $4, $5, $6)",
120
- [document_id, 'ollama', 'nomic-embed-text', 768, vector_str, content]
143
+ [document_id, 'ollama', 'nomic-embed-text', embedding.length, vector_str, content]
121
144
  )
122
145
  ```
123
146
 
@@ -130,17 +153,17 @@ query_embedding = client.generate_embedding(query_text)
130
153
  query_vector = "[#{query_embedding.join(',')}]"
131
154
 
132
155
  results = db.exec_params(
133
- "SELECT d.title, d.content, de.embedding <=> $1::vector AS distance
134
- FROM documents d
135
- JOIN document_embeddings de ON d.id = de.document_id
136
- ORDER BY de.embedding <=> $1::vector
156
+ "SELECT d.title, d.content, de.embedding <=> $1::vector AS distance
157
+ FROM documents d
158
+ JOIN document_embeddings de ON d.id = de.document_id
159
+ ORDER BY de.embedding <=> $1::vector
137
160
  LIMIT 5",
138
161
  [query_vector]
139
162
  )
140
163
 
141
164
  results.each do |row|
142
165
  similarity = 1 - row['distance'].to_f
143
- puts "#{row['title']} (#{(similarity * 100).round(1)}% similar)"
166
+ puts "#{ row['title']} (#{ (similarity * 100).round(1)}% similar)"
144
167
  end
145
168
  ```
146
169
 
@@ -150,11 +173,11 @@ end
150
173
  # Search with metadata filtering
151
174
  results = db.exec_params(
152
175
  "SELECT d.title, de.embedding <=> $1::vector as distance
153
- FROM documents d
176
+ FROM documents d
154
177
  JOIN document_embeddings de ON d.id = de.document_id
155
178
  WHERE d.metadata->'tags' ? 'programming'
156
179
  AND d.metadata->>'difficulty' = 'beginner'
157
- ORDER BY de.embedding <=> $1::vector
180
+ ORDER BY de.embedding <=> $1::vector
158
181
  LIMIT 10",
159
182
  [query_vector]
160
183
  )
@@ -168,17 +191,17 @@ For large documents, split into chunks for better search granularity:
168
191
  def chunk_document(text, chunk_size: 500, overlap: 50)
169
192
  chunks = []
170
193
  start = 0
171
-
194
+
172
195
  while start < text.length
173
196
  end_pos = [start + chunk_size, text.length].min
174
-
197
+
175
198
  # Find word boundary to avoid cutting words
176
199
  if end_pos < text.length
177
200
  while end_pos > start && text[end_pos] != ' '
178
201
  end_pos -= 1
179
202
  end
180
203
  end
181
-
204
+
182
205
  chunk = text[start...end_pos].strip
183
206
  chunks << {
184
207
  text: chunk,
@@ -186,11 +209,11 @@ def chunk_document(text, chunk_size: 500, overlap: 50)
186
209
  end_pos: end_pos,
187
210
  index: chunks.length
188
211
  }
189
-
212
+
190
213
  start = end_pos - overlap
191
214
  break if start >= text.length
192
215
  end
193
-
216
+
194
217
  chunks
195
218
  end
196
219
 
@@ -200,18 +223,18 @@ chunks.each do |chunk|
200
223
  # Insert chunk
201
224
  chunk_result = db.exec_params(
202
225
  "INSERT INTO document_chunks (document_id, chunk_index, chunk_text, chunk_metadata) VALUES ($1, $2, $3, $4) RETURNING id",
203
- [document_id, chunk[:index], chunk[:text], {start_pos: chunk[:start_pos], end_pos: chunk[:end_pos]}.to_json]
226
+ [document_id, chunk[:index], chunk[:text], { start_pos: chunk[:start_pos], end_pos: chunk[:end_pos]}.to_json]
204
227
  )
205
228
  chunk_id = chunk_result[0]['id']
206
-
229
+
207
230
  # Generate embedding for chunk
208
231
  chunk_embedding = client.generate_embedding(chunk[:text])
209
232
  chunk_vector = "[#{chunk_embedding.join(',')}]"
210
-
233
+
211
234
  # Store chunk embedding
212
235
  db.exec_params(
213
236
  "INSERT INTO chunk_embeddings (chunk_id, document_id, embedding_provider, embedding_model, embedding_dimensions, embedding) VALUES ($1, $2, $3, $4, $5, $6)",
214
- [chunk_id, document_id, 'ollama', 'nomic-embed-text', 768, chunk_vector]
237
+ [chunk_id, document_id, 'ollama', 'nomic-embed-text', chunk_embedding.length, chunk_vector]
215
238
  )
216
239
  end
217
240
  ```
@@ -224,20 +247,20 @@ For different dataset sizes and performance requirements:
224
247
 
225
248
  ```sql
226
249
  -- Small datasets (< 100K vectors): Fast build, good accuracy
227
- CREATE INDEX idx_embeddings_small
228
- ON document_embeddings
250
+ CREATE INDEX idx_embeddings_small
251
+ ON document_embeddings
229
252
  USING hnsw (embedding vector_cosine_ops)
230
253
  WITH (m = 8, ef_construction = 32);
231
254
 
232
255
  -- Medium datasets (100K - 1M vectors): Balanced
233
- CREATE INDEX idx_embeddings_medium
234
- ON document_embeddings
256
+ CREATE INDEX idx_embeddings_medium
257
+ ON document_embeddings
235
258
  USING hnsw (embedding vector_cosine_ops)
236
259
  WITH (m = 16, ef_construction = 64);
237
260
 
238
261
  -- Large datasets (> 1M vectors): High accuracy
239
- CREATE INDEX idx_embeddings_large
240
- ON document_embeddings
262
+ CREATE INDEX idx_embeddings_large
263
+ ON document_embeddings
241
264
  USING hnsw (embedding vector_cosine_ops)
242
265
  WITH (m = 32, ef_construction = 128);
243
266
  ```
@@ -251,9 +274,9 @@ SET hnsw.ef_search = 100; -- Balanced (default)
251
274
  SET hnsw.ef_search = 200; -- High accuracy, slower
252
275
 
253
276
  -- Monitor query performance
254
- EXPLAIN (ANALYZE, BUFFERS)
255
- SELECT * FROM document_embeddings
256
- ORDER BY embedding <=> '[0.1,0.2,...]'::vector
277
+ EXPLAIN (ANALYZE, BUFFERS)
278
+ SELECT * FROM document_embeddings
279
+ ORDER BY embedding <=> '[0.1,0.2,...]'::vector
257
280
  LIMIT 10;
258
281
  ```
259
282
 
@@ -268,7 +291,7 @@ texts.each_slice(10) do |batch|
268
291
  batch.each do |text|
269
292
  embedding = client.generate_embedding(text)
270
293
  embeddings << embedding
271
-
294
+
272
295
  # Small delay to avoid rate limiting
273
296
  sleep(0.1)
274
297
  end
@@ -280,7 +303,7 @@ db.transaction do
280
303
  vector_str = "[#{embedding.join(',')}]"
281
304
  db.exec_params(
282
305
  "INSERT INTO document_embeddings (...) VALUES (...)",
283
- [documents[index].id, 'ollama', 'nomic-embed-text', 768, vector_str, texts[index]]
306
+ [documents[index].id, 'ollama', 'nomic-embed-text', embedding.length, vector_str, texts[index]]
284
307
  )
285
308
  end
286
309
  end
@@ -295,13 +318,13 @@ Combine vector similarity with traditional text search:
295
318
  ```sql
296
319
  WITH vector_results AS (
297
320
  SELECT document_id, embedding <=> $1::vector as distance
298
- FROM document_embeddings
299
- ORDER BY embedding <=> $1::vector
321
+ FROM document_embeddings
322
+ ORDER BY embedding <=> $1::vector
300
323
  LIMIT 20
301
324
  ),
302
325
  text_results AS (
303
326
  SELECT id as document_id, ts_rank(to_tsvector(content), plainto_tsquery($2)) as rank
304
- FROM documents
327
+ FROM documents
305
328
  WHERE to_tsvector(content) @@ plainto_tsquery($2)
306
329
  )
307
330
  SELECT d.title, d.content,
@@ -322,16 +345,17 @@ Store embeddings from multiple providers for comparison:
322
345
 
323
346
  ```ruby
324
347
  providers = [
325
- { client: Prescient.client(:ollama), name: 'ollama', model: 'nomic-embed-text', dims: 768 },
326
- { client: Prescient.client(:openai), name: 'openai', model: 'text-embedding-3-small', dims: 1536 }
348
+ { client: Prescient.client(:ollama), name: 'ollama', model: 'nomic-embed-text' },
349
+ { client: Prescient.client(:openai), name: 'openai', model: 'text-embedding-3-small' }
327
350
  ]
328
351
 
329
352
  providers.each do |provider|
330
353
  next unless provider[:client].available?
331
-
354
+
332
355
  embedding = provider[:client].generate_embedding(text)
356
+ provider[:dims] = embedding.length
333
357
  vector_str = "[#{embedding.join(',')}]"
334
-
358
+
335
359
  db.exec_params(
336
360
  "INSERT INTO document_embeddings (document_id, embedding_provider, embedding_model, embedding_dimensions, embedding, embedding_text) VALUES ($1, $2, $3, $4, $5, $6)",
337
361
  [document_id, provider[:name], provider[:model], provider[:dims], vector_str, text]
@@ -348,14 +372,14 @@ end
348
372
  def track_search(query_text, results, provider, model)
349
373
  query_embedding = client.generate_embedding(query_text)
350
374
  query_vector = "[#{query_embedding.join(',')}]"
351
-
375
+
352
376
  # Insert search query
353
377
  query_result = db.exec_params(
354
378
  "INSERT INTO search_queries (query_text, embedding_provider, embedding_model, query_embedding, result_count) VALUES ($1, $2, $3, $4, $5) RETURNING id",
355
379
  [query_text, provider, model, query_vector, results.length]
356
380
  )
357
381
  query_id = query_result[0]['id']
358
-
382
+
359
383
  # Insert query results
360
384
  results.each_with_index do |result, index|
361
385
  db.exec_params(
@@ -371,14 +395,14 @@ end
371
395
  ```sql
372
396
  -- Popular search terms
373
397
  SELECT query_text, COUNT(*) as search_count
374
- FROM search_queries
398
+ FROM search_queries
375
399
  WHERE created_at > NOW() - INTERVAL '7 days'
376
400
  GROUP BY query_text
377
401
  ORDER BY search_count DESC
378
402
  LIMIT 10;
379
403
 
380
404
  -- Average similarity scores
381
- SELECT embedding_provider, embedding_model,
405
+ SELECT embedding_provider, embedding_model,
382
406
  AVG(similarity_score) as avg_similarity,
383
407
  COUNT(*) as result_count
384
408
  FROM query_results qr
@@ -400,11 +424,12 @@ ORDER BY hour;
400
424
  ### Common Issues
401
425
 
402
426
  **Slow queries:**
427
+
403
428
  ```sql
404
429
  -- Check if indexes are being used
405
- EXPLAIN (ANALYZE, BUFFERS)
406
- SELECT * FROM document_embeddings
407
- ORDER BY embedding <=> '[...]'::vector
430
+ EXPLAIN (ANALYZE, BUFFERS)
431
+ SELECT * FROM document_embeddings
432
+ ORDER BY embedding <=> '[...]'::vector
408
433
  LIMIT 10;
409
434
 
410
435
  -- Rebuild indexes if needed
@@ -412,10 +437,11 @@ REINDEX INDEX idx_document_embeddings_cosine;
412
437
  ```
413
438
 
414
439
  **Memory issues:**
440
+
415
441
  ```sql
416
442
  -- Check index sizes
417
443
  SELECT schemaname, tablename, indexname, pg_size_pretty(pg_relation_size(indexrelid)) as size
418
- FROM pg_stat_user_indexes
444
+ FROM pg_stat_user_indexes
419
445
  WHERE tablename LIKE '%embedding%'
420
446
  ORDER BY pg_relation_size(indexrelid) DESC;
421
447
 
@@ -424,6 +450,7 @@ SET work_mem = '256MB';
424
450
  ```
425
451
 
426
452
  **Dimension mismatches:**
453
+
427
454
  ```ruby
428
455
  # Validate embedding dimensions before storing
429
456
  expected_dims = 768
@@ -447,4 +474,4 @@ end
447
474
  - [pgvector Documentation](https://github.com/pgvector/pgvector)
448
475
  - [HNSW Algorithm](https://arxiv.org/abs/1603.09320)
449
476
  - [Vector Database Concepts](https://www.pinecone.io/learn/vector-database/)
450
- - [Embedding Best Practices](https://platform.openai.com/docs/guides/embeddings/what-are-embeddings)
477
+ - [Embedding Best Practices](https://platform.openai.com/docs/guides/embeddings/what-are-embeddings)
data/docker-compose.yml CHANGED
@@ -1,8 +1,6 @@
1
1
  # Docker Compose configuration for running Ollama with Prescient gem
2
2
  # This provides a local AI environment for development and testing
3
3
 
4
- version: '3.8'
5
-
6
4
  services:
7
5
  ollama:
8
6
  image: ollama/ollama:latest
@@ -65,7 +63,7 @@ services:
65
63
  # Pull chat model
66
64
  curl -X POST http://ollama:11434/api/pull \
67
65
  -H "Content-Type: application/json" \
68
- -d "{\"name\": \"llama3.1:8b\"}"
66
+ -d "{\"name\": \"llama3.2:3b\"}"
69
67
 
70
68
  echo "Models pulled successfully!"
71
69
  '
@@ -122,7 +120,7 @@ services:
122
120
  # Ollama configuration
123
121
  - OLLAMA_URL=http://ollama:11434
124
122
  - OLLAMA_EMBEDDING_MODEL=nomic-embed-text
125
- - OLLAMA_CHAT_MODEL=llama3.1:8b
123
+ - OLLAMA_CHAT_MODEL=llama3.2:3b
126
124
 
127
125
  # Optional: Other AI provider configurations
128
126
  - OPENAI_API_KEY=${OPENAI_API_KEY:-}
@@ -150,4 +148,4 @@ volumes:
150
148
 
151
149
  networks:
152
150
  default:
153
- name: prescient-network
151
+ name: prescient-network
@@ -0,0 +1,45 @@
1
+ # Prescient examples
2
+
3
+ These scripts demonstrate the supported public API. They use the local
4
+ library checkout, so run them from the repository root after installing the
5
+ development dependencies:
6
+
7
+ ```bash
8
+ bundle install
9
+ ```
10
+
11
+ ## Examples
12
+
13
+ - `basic_usage.rb` — default-provider generation, embeddings, health checks,
14
+ provider selection, and custom configuration.
15
+ - `custom_prompts.rb` — system prompts and no-context/with-context templates.
16
+ - `custom_contexts.rb` — explicit context types, field matching, formatting,
17
+ and embedding field selection.
18
+ - `vector_search.rb` — PostgreSQL/pgvector storage and similarity search.
19
+
20
+ The first three examples use Ollama by default. Start Ollama and pull the
21
+ current local models before running them:
22
+
23
+ ```bash
24
+ docker compose up -d ollama
25
+ docker compose run --rm ollama-init
26
+ bundle exec ruby examples/basic_usage.rb
27
+ ```
28
+
29
+ The vector-search example additionally requires PostgreSQL with pgvector:
30
+
31
+ ```bash
32
+ docker compose up -d postgres ollama
33
+ docker compose run --rm ollama-init
34
+ DB_HOST=localhost bundle exec ruby examples/vector_search.rb
35
+ ```
36
+
37
+ Cloud-provider examples require the corresponding credentials and provider
38
+ configuration. The scripts are demonstrations rather than isolated test
39
+ fixtures; they may make real provider requests when the configured service is
40
+ available.
41
+
42
+ See the [main README](../README.md) for configuration, fallback behavior,
43
+ prompt templates, context exclusions, embeddings, and the public API. Rails
44
+ applications can also use the [integration guide](../INTEGRATION_GUIDE.md),
45
+ and PostgreSQL users should read the [pgvector guide](../VECTOR_SEARCH_GUIDE.md).
@@ -110,7 +110,7 @@ Prescient.configure do |config|
110
110
  config.add_provider(:custom_ollama, Prescient::Provider::Ollama,
111
111
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
112
112
  embedding_model: 'nomic-embed-text',
113
- chat_model: 'llama3.1:8b',
113
+ chat_model: 'llama3.2:3b',
114
114
  timeout: 60
115
115
  )
116
116
  end
@@ -120,4 +120,4 @@ puts " Timeout: #{Prescient.configuration.timeout}s"
120
120
  puts " Retry attempts: #{Prescient.configuration.retry_attempts}"
121
121
  puts " Providers: #{Prescient.configuration.providers.keys.join(', ')}"
122
122
 
123
- puts "\nšŸŽ‰ Examples completed!"
123
+ puts "\nšŸŽ‰ Examples completed!"
@@ -16,7 +16,7 @@ Prescient.configure do |config|
16
16
  config.add_provider(:ecommerce, Prescient::Provider::Ollama,
17
17
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
18
18
  embedding_model: 'nomic-embed-text',
19
- chat_model: 'llama3.1:8b',
19
+ chat_model: 'llama3.2:3b',
20
20
  # Define your own context types - no hardcoded assumptions!
21
21
  context_configs: {
22
22
  'product' => {
@@ -88,7 +88,7 @@ Prescient.configure do |config|
88
88
  config.add_provider(:healthcare, Prescient::Provider::Ollama,
89
89
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
90
90
  embedding_model: 'nomic-embed-text',
91
- chat_model: 'llama3.1:8b',
91
+ chat_model: 'llama3.2:3b',
92
92
  context_configs: {
93
93
  'patient' => {
94
94
  fields: %w[name age gender medical_conditions medications],
@@ -158,7 +158,7 @@ Prescient.configure do |config|
158
158
  config.add_provider(:project_mgmt, Prescient::Provider::Ollama,
159
159
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
160
160
  embedding_model: 'nomic-embed-text',
161
- chat_model: 'llama3.1:8b',
161
+ chat_model: 'llama3.2:3b',
162
162
  context_configs: {
163
163
  'issue' => {
164
164
  fields: %w[title description status priority assignee labels created_date],
@@ -237,7 +237,7 @@ begin
237
237
  config.add_provider(:embedding_demo, Prescient::Provider::Ollama,
238
238
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
239
239
  embedding_model: 'nomic-embed-text',
240
- chat_model: 'llama3.1:8b',
240
+ chat_model: 'llama3.2:3b',
241
241
  context_configs: {
242
242
  'blog_post' => {
243
243
  fields: %w[title content author tags category publish_date],
@@ -288,7 +288,7 @@ Prescient.configure do |config|
288
288
  config.add_provider(:no_config, Prescient::Provider::Ollama,
289
289
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
290
290
  embedding_model: 'nomic-embed-text',
291
- chat_model: 'llama3.1:8b'
291
+ chat_model: 'llama3.2:3b'
292
292
  # No context_configs defined - uses pure default behavior
293
293
  )
294
294
  end
@@ -352,4 +352,4 @@ puts "\nšŸŽÆ Best Practices:"
352
352
  puts " - Define context_configs for your specific domain"
353
353
  puts " - Use explicit 'type' field when context detection isn't reliable"
354
354
  puts " - Exclude sensitive/metadata fields from embedding_fields"
355
- puts " - Test with and without context configs to see the difference"
355
+ puts " - Test with and without context configs to see the difference"
@@ -15,7 +15,7 @@ Prescient.configure do |config|
15
15
  config.add_provider(:customer_service, Prescient::Provider::Ollama,
16
16
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
17
17
  embedding_model: 'nomic-embed-text',
18
- chat_model: 'llama3.1:8b',
18
+ chat_model: 'llama3.2:3b',
19
19
  prompt_templates: {
20
20
  system_prompt: 'You are a friendly customer service representative. Be helpful, empathetic, and professional.',
21
21
  no_context_template: <<~TEMPLATE.strip,
@@ -73,7 +73,7 @@ Prescient.configure do |config|
73
73
  config.add_provider(:tech_docs, Prescient::Provider::Ollama,
74
74
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
75
75
  embedding_model: 'nomic-embed-text',
76
- chat_model: 'llama3.1:8b',
76
+ chat_model: 'llama3.2:3b',
77
77
  prompt_templates: {
78
78
  system_prompt: 'You are a technical documentation assistant. Provide clear, accurate, and detailed technical explanations with code examples when relevant.',
79
79
  no_context_template: <<~TEMPLATE.strip,
@@ -125,7 +125,7 @@ Prescient.configure do |config|
125
125
  config.add_provider(:creative, Prescient::Provider::Ollama,
126
126
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
127
127
  embedding_model: 'nomic-embed-text',
128
- chat_model: 'llama3.1:8b',
128
+ chat_model: 'llama3.2:3b',
129
129
  prompt_templates: {
130
130
  system_prompt: 'You are a creative writing assistant. Help with storytelling, character development, and creative inspiration. Be imaginative and encouraging.',
131
131
  no_context_template: <<~TEMPLATE.strip,
@@ -182,7 +182,7 @@ Prescient.configure do |config|
182
182
  config.add_provider(:custom_default, Prescient::Provider::Ollama,
183
183
  url: ENV.fetch('OLLAMA_URL', 'http://localhost:11434'),
184
184
  embedding_model: 'nomic-embed-text',
185
- chat_model: 'llama3.1:8b',
185
+ chat_model: 'llama3.2:3b',
186
186
  prompt_templates: {
187
187
  # Only override the system prompt, keep default templates
188
188
  system_prompt: 'You are Sherlock Holmes. Approach every question with deductive reasoning and attention to detail.'
@@ -209,4 +209,4 @@ puts "\nšŸ’” Tips:"
209
209
  puts " - Use %{system_prompt}, %{query}, and %{context} placeholders in templates"
210
210
  puts " - Templates use Ruby's % string formatting"
211
211
  puts " - Override any or all template parts (system_prompt, no_context_template, with_context_template)"
212
- puts " - Each provider can have completely different prompt behavior"
212
+ puts " - Each provider can have completely different prompt behavior"
@@ -31,7 +31,7 @@ class VectorSearchExample
31
31
 
32
32
  # Check if services are available
33
33
  unless check_services_available
34
- puts "āŒ Required services not available. Please start with: docker-compose up -d"
34
+ puts "āŒ Required services not available. Please start with: docker compose up -d"
35
35
  return
36
36
  end
37
37
 
@@ -327,4 +327,4 @@ puts " - Try different embedding models (OpenAI, HuggingFace)"
327
327
  puts " - Implement hybrid search (vector + keyword)"
328
328
  puts " - Add document chunking for large texts"
329
329
  puts " - Experiment with different similarity thresholds"
330
- puts " - Add result re-ranking and filtering"
330
+ puts " - Add result re-ranking and filtering"