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/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Prescient
2
2
 
3
- Prescient provides a unified interface for AI providers including Ollama (local), Anthropic Claude, OpenAI GPT, and HuggingFace models. Built for prescient applications that need AI predictions with provider switching, error handling, and fallback mechanisms.
3
+ Prescient is a boring AI provider abstraction for Ruby. Configure your AI providers once, then use the same interface regardless of whether the request is handled by OpenAI, Anthropic, Ollama, or Hugging Face. Prescient handles provider selection, retries, health checks, and fallback.
4
+
5
+ For focused guidance, see the [examples guide](examples/README.md),
6
+ [Rails integration guide](INTEGRATION_GUIDE.md), and
7
+ [pgvector guide](VECTOR_SEARCH_GUIDE.md).
4
8
 
5
9
  ## Features
6
10
 
@@ -16,19 +20,19 @@ Prescient provides a unified interface for AI providers including Ollama (local)
16
20
 
17
21
  ### Ollama (Local)
18
22
 
19
- - **Models**: Any Ollama-compatible model (llama3.1, nomic-embed-text, etc.)
23
+ - **Models**: Any Ollama-compatible model (llama3.2, nomic-embed-text, etc.)
20
24
  - **Capabilities**: Embeddings, Text Generation, Model Management
21
25
  - **Use Case**: Privacy-focused, local deployments
22
26
 
23
27
  ### Anthropic Claude
24
28
 
25
- - **Models**: Claude 3 (Haiku, Sonnet, Opus)
29
+ - **Models**: Current Claude models selected through the Anthropic Models API
26
30
  - **Capabilities**: Text Generation only (no embeddings)
27
31
  - **Use Case**: High-quality conversational AI
28
32
 
29
33
  ### OpenAI
30
34
 
31
- - **Models**: GPT-3.5, GPT-4, text-embedding-3-small/large
35
+ - **Models**: Current GPT and text-embedding models selected through the OpenAI Models API
32
36
  - **Capabilities**: Embeddings, Text Generation
33
37
  - **Use Case**: Proven performance, wide model selection
34
38
 
@@ -66,21 +70,21 @@ gem install prescient
66
70
  # Ollama (Local)
67
71
  OLLAMA_URL=http://localhost:11434
68
72
  OLLAMA_EMBEDDING_MODEL=nomic-embed-text
69
- OLLAMA_CHAT_MODEL=llama3.1:8b
73
+ OLLAMA_CHAT_MODEL=llama3.2:3b
70
74
 
71
75
  # Anthropic
72
76
  ANTHROPIC_API_KEY=your_api_key
73
- ANTHROPIC_MODEL=claude-3-haiku-20240307
77
+ ANTHROPIC_MODEL=claude-sonnet-4-20250514
74
78
 
75
79
  # OpenAI
76
80
  OPENAI_API_KEY=your_api_key
77
81
  OPENAI_EMBEDDING_MODEL=text-embedding-3-small
78
- OPENAI_CHAT_MODEL=gpt-3.5-turbo
82
+ OPENAI_CHAT_MODEL=gpt-4.1-mini
79
83
 
80
84
  # HuggingFace
81
85
  HUGGINGFACE_API_KEY=your_api_key
82
86
  HUGGINGFACE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
83
- HUGGINGFACE_CHAT_MODEL=microsoft/DialoGPT-medium
87
+ HUGGINGFACE_CHAT_MODEL=google/gemma-2-2b-it
84
88
  ```
85
89
 
86
90
  ### Programmatic Configuration
@@ -96,27 +100,74 @@ Prescient.configure do |config|
96
100
  config.retry_delay = 1.0
97
101
 
98
102
  # Add custom Ollama configuration
99
- config.add_provider(:ollama, Prescient::Ollama::Provider,
103
+ config.add_provider(:ollama, Prescient::Provider::Ollama,
100
104
  url: 'http://localhost:11434',
101
105
  embedding_model: 'nomic-embed-text',
102
- chat_model: 'llama3.1:8b'
106
+ chat_model: 'llama3.2:3b'
103
107
  )
104
108
 
105
109
  # Add Anthropic
106
- config.add_provider(:anthropic, Prescient::Anthropic::Provider,
110
+ config.add_provider(:anthropic, Prescient::Provider::Anthropic,
107
111
  api_key: ENV['ANTHROPIC_API_KEY'],
108
- model: 'claude-3-haiku-20240307'
112
+ model: 'claude-sonnet-4-20250514'
109
113
  )
110
114
 
111
115
  # Add OpenAI
112
- config.add_provider(:openai, Prescient::OpenAI::Provider,
116
+ config.add_provider(:openai, Prescient::Provider::OpenAI,
117
+ api_key: ENV['OPENAI_API_KEY'],
118
+ embedding_model: 'text-embedding-3-small',
119
+ chat_model: 'gpt-4.1-mini'
120
+ )
121
+ end
122
+ ```
123
+
124
+ ### Provider Fallback Configuration
125
+
126
+ Prescient supports automatic fallback to backup providers when the primary provider fails. This ensures high availability for your AI applications.
127
+
128
+ ```ruby
129
+ Prescient.configure do |config|
130
+ # Configure primary provider
131
+ config.add_provider(:primary, Prescient::Provider::OpenAI,
113
132
  api_key: ENV['OPENAI_API_KEY'],
114
133
  embedding_model: 'text-embedding-3-small',
115
- chat_model: 'gpt-3.5-turbo'
134
+ chat_model: 'gpt-4.1-mini'
116
135
  )
136
+
137
+ # Configure backup providers
138
+ config.add_provider(:backup1, Prescient::Provider::Anthropic,
139
+ api_key: ENV['ANTHROPIC_API_KEY'],
140
+ model: 'claude-sonnet-4-20250514'
141
+ )
142
+
143
+ config.add_provider(:backup2, Prescient::Provider::Ollama,
144
+ url: 'http://localhost:11434',
145
+ embedding_model: 'nomic-embed-text',
146
+ chat_model: 'llama3.2:3b'
147
+ )
148
+
149
+ # Configure fallback order
150
+ config.fallback_providers = [:backup1, :backup2]
117
151
  end
152
+
153
+ # Client with fallback enabled (default)
154
+ client = Prescient::Client.new(:primary, enable_fallback: true)
155
+
156
+ # Client without fallback
157
+ client_no_fallback = Prescient::Client.new(:primary, enable_fallback: false)
158
+
159
+ # Convenience methods also support fallback
160
+ response = Prescient.generate_response("Hello", provider: :primary, enable_fallback: true)
118
161
  ```
119
162
 
163
+ **Fallback Behavior:**
164
+ - When a provider fails with a persistent error, Prescient automatically tries the next available provider
165
+ - Configured fallback providers are tried in order; the provider operation determines availability
166
+ - If no fallback providers are configured, all configured providers are tried as fallbacks
167
+ - Transient errors (rate limits, timeouts) still use retry logic before fallback
168
+ - Provider-service failures, connection failures, rate limits, and unavailable models may trigger fallback; authentication and invalid-request errors are returned to the caller
169
+ - The fallback process preserves all method arguments and options
170
+
120
171
  ## Usage
121
172
 
122
173
  ### Quick Start
@@ -129,7 +180,7 @@ client = Prescient.client
129
180
 
130
181
  # Generate embeddings
131
182
  embedding = client.generate_embedding("Your text here")
132
- # => [0.1, 0.2, 0.3, ...] (768-dimensional vector)
183
+ # => [0.1, 0.2, 0.3, ...] (model-dependent vector dimensions)
133
184
 
134
185
  # Generate text responses
135
186
  response = client.generate_response("What is Ruby?")
@@ -170,8 +221,8 @@ response = Prescient.generate_response(query, context_items,
170
221
  )
171
222
 
172
223
  puts response[:response]
173
- puts "Model: #{response[:model]}"
174
- puts "Provider: #{response[:provider]}"
224
+ puts "Model: " + response[:model]
225
+ puts "Provider: " + response[:provider]
175
226
  ```
176
227
 
177
228
  ### Error Handling
@@ -192,12 +243,20 @@ end
192
243
 
193
244
  ### Health Monitoring
194
245
 
246
+ Health results separate transport reachability from configured-model readiness:
247
+ `reachable: true` means the provider answered, while `ready: true` means the
248
+ configured operation models were found or validated. Fallback uses the actual
249
+ operation and does not perform an additional health request.
250
+
195
251
  ```ruby
196
252
  # Check all providers
197
- [:ollama, :anthropic, :openai, :huggingface].each do |provider|
253
+ Prescient.configuration.providers.keys.each do |provider|
198
254
  health = Prescient.health_check(provider: provider)
199
255
  puts "#{provider}: #{health[:status]}"
200
- puts "Ready: #{health[:ready]}" if health[:ready]
256
+ puts "Reachable: #{health[:reachable]}"
257
+ puts "Ready: #{health[:ready]}"
258
+ rescue Prescient::Error => e
259
+ puts "#{provider}: unavailable (#{e.message})"
201
260
  end
202
261
  ```
203
262
 
@@ -210,18 +269,18 @@ Prescient.configure do |config|
210
269
  config.add_provider(:customer_service, Prescient::Provider::OpenAI,
211
270
  api_key: ENV['OPENAI_API_KEY'],
212
271
  embedding_model: 'text-embedding-3-small',
213
- chat_model: 'gpt-3.5-turbo',
272
+ chat_model: 'gpt-4.1-mini',
214
273
  prompt_templates: {
215
274
  system_prompt: 'You are a friendly customer service representative.',
216
275
  no_context_template: <<~TEMPLATE.strip,
217
- %{system_prompt}
276
+ %{ system_prompt }
218
277
 
219
278
  Customer Question: %{query}
220
279
 
221
280
  Please provide a helpful response.
222
281
  TEMPLATE
223
282
  with_context_template: <<~TEMPLATE.strip
224
- %{system_prompt} Use the company info below to help answer.
283
+ %{ system_prompt } Use the company info below to help answer.
225
284
 
226
285
  Company Information:
227
286
  %{context}
@@ -259,6 +318,7 @@ prompt_templates: {
259
318
  system_prompt: 'You are a technical documentation assistant. Provide detailed explanations with code examples.',
260
319
  # ... templates
261
320
  }
321
+
262
322
  ```
263
323
 
264
324
  #### Creative Writing
@@ -316,6 +376,14 @@ response = client.generate_response("I need a laptop for work", products)
316
376
  - **fields** - Array of field names available for this context type
317
377
  - **format** - Template string for displaying context items
318
378
  - **embedding_fields** - Specific fields to use when generating embeddings
379
+ - **context_excluded_fields** - Additional field names excluded from generic embedding text; built-in metadata exclusions remain active
380
+
381
+ ```ruby
382
+ config.add_provider(:openai, Prescient::Provider::OpenAI,
383
+ api_key: ENV['OPENAI_API_KEY'],
384
+ context_excluded_fields: %w[tenant_id internal_notes]
385
+ )
386
+ ```
319
387
 
320
388
  ### Automatic Context Detection
321
389
 
@@ -337,7 +405,7 @@ The system works perfectly without any context configuration - it will:
337
405
 
338
406
  ```ruby
339
407
  # No context_configs needed - works with any data!
340
- client = Prescient.client(:default)
408
+ client = Prescient.client
341
409
  response = client.generate_response("Analyze this", [
342
410
  { 'title' => 'Issue', 'content' => 'Server down', 'created_at' => '2024-01-01' },
343
411
  { 'name' => 'Alert', 'message' => 'High CPU usage', 'timestamp' => 1234567 }
@@ -346,9 +414,44 @@ response = client.generate_response("Analyze this", [
346
414
 
347
415
  See `examples/custom_contexts.rb` for complete examples.
348
416
 
417
+ ### Sensitive Provider Options
418
+
419
+ `provider_info` always removes the built-in sensitive keys (`api_key`,
420
+ `password`, `token`, and `secret`). Add project-specific keys globally with
421
+ `sensitive_keys`; nested hashes and arrays are sanitized recursively:
422
+
423
+ ```ruby
424
+ Prescient.configure do |config|
425
+ config.sensitive_keys = %w[workspace_secret private_key]
426
+ end
427
+ ```
428
+
349
429
  ## Vector Database Integration (pgvector)
350
430
 
351
- Prescient integrates seamlessly with PostgreSQL's pgvector extension for storing and searching embeddings:
431
+ `Prescient::Pgvector::Store` is an opt-in PostgreSQL integration for storing
432
+ and searching embeddings. It accepts a PG-compatible connection, so `pg` stays
433
+ an application dependency rather than a required dependency of this gem.
434
+
435
+ ```ruby
436
+ require 'pg'
437
+
438
+ connection = PG.connect(dbname: 'my_app')
439
+ store = Prescient::Pgvector::Store.new(connection: connection, dimensions: 1536)
440
+ store.install!
441
+ store.create_index!
442
+
443
+ embedding = Prescient.generate_embedding('Semantic search', provider: :openai)
444
+ store.upsert(
445
+ id: 'document-42', embedding:, provider: 'openai', model: 'text-embedding-3-small',
446
+ content: 'Semantic search', metadata: { category: 'guide' }
447
+ )
448
+
449
+ results = store.search(embedding:, provider: :openai, model: 'text-embedding-3-small')
450
+ ```
451
+
452
+ Every vector must exactly match the store's configured dimensions; Prescient
453
+ never pads or truncates vectors. The existing application-schema example below
454
+ remains available for projects that need documents, chunks, and custom metadata.
352
455
 
353
456
  ### Setup with Docker
354
457
 
@@ -356,7 +459,7 @@ The included `docker-compose.yml` provides a complete setup with PostgreSQL + pg
356
459
 
357
460
  ```bash
358
461
  # Start PostgreSQL with pgvector
359
- docker-compose up -d postgres
462
+ docker compose up -d postgres
360
463
 
361
464
  # The database will automatically:
362
465
  # - Install pgvector extension
@@ -400,7 +503,7 @@ embedding = client.generate_embedding(text)
400
503
  vector_str = "[#{embedding.join(',')}]"
401
504
  db.exec_params(
402
505
  "INSERT INTO document_embeddings (document_id, embedding_provider, embedding_model, embedding_dimensions, embedding, embedding_text) VALUES ($1, $2, $3, $4, $5, $6)",
403
- [doc_id, 'ollama', 'nomic-embed-text', 768, vector_str, text]
506
+ [doc_id, 'ollama', 'nomic-embed-text', embedding.length, vector_str, text]
404
507
  )
405
508
 
406
509
  # Perform similarity search
@@ -409,10 +512,10 @@ query_embedding = client.generate_embedding(query_text)
409
512
  query_vector = "[#{query_embedding.join(',')}]"
410
513
 
411
514
  results = db.exec_params(
412
- "SELECT d.title, d.content, de.embedding <=> $1::vector AS distance
413
- FROM documents d
414
- JOIN document_embeddings de ON d.id = de.document_id
415
- ORDER BY de.embedding <=> $1::vector
515
+ "SELECT d.title, d.content, de.embedding <=> $1::vector AS distance
516
+ FROM documents d
517
+ JOIN document_embeddings de ON d.id = de.document_id
518
+ ORDER BY de.embedding <=> $1::vector
416
519
  LIMIT 5",
417
520
  [query_vector]
418
521
  )
@@ -423,14 +526,14 @@ results = db.exec_params(
423
526
  pgvector supports three distance functions:
424
527
 
425
528
  - **Cosine Distance** (`<=>`): Best for normalized embeddings
426
- - **L2 Distance** (`<->`): Euclidean distance, good general purpose
529
+ - **L2 Distance** (`<->`): Euclidean distance, good general purpose
427
530
  - **Inner Product** (`<#>`): Dot product, useful for specific cases
428
531
 
429
532
  ```sql
430
533
  -- Cosine similarity (most common)
431
534
  ORDER BY embedding <=> query_vector
432
535
 
433
- -- L2 distance
536
+ -- L2 distance
434
537
  ORDER BY embedding <-> query_vector
435
538
 
436
539
  -- Inner product
@@ -443,8 +546,8 @@ The setup automatically creates HNSW indexes for fast similarity search:
443
546
 
444
547
  ```sql
445
548
  -- Example index for cosine distance
446
- CREATE INDEX idx_embeddings_cosine
447
- ON document_embeddings
549
+ CREATE INDEX idx_embeddings_cosine
550
+ ON document_embeddings
448
551
  USING hnsw (embedding vector_cosine_ops)
449
552
  WITH (m = 16, ef_construction = 64);
450
553
  ```
@@ -457,22 +560,22 @@ Combine vector similarity with metadata filtering:
457
560
  # Search with tag filtering
458
561
  results = db.exec_params(
459
562
  "SELECT d.title, de.embedding <=> $1::vector as distance
460
- FROM documents d
563
+ FROM documents d
461
564
  JOIN document_embeddings de ON d.id = de.document_id
462
565
  WHERE d.metadata->'tags' ? 'programming'
463
- ORDER BY de.embedding <=> $1::vector
566
+ ORDER BY de.embedding <=> $1::vector
464
567
  LIMIT 5",
465
568
  [query_vector]
466
569
  )
467
570
 
468
- # Search with difficulty and tag filters
571
+ # Search with difficulty and tag filters
469
572
  results = db.exec_params(
470
573
  "SELECT d.title, de.embedding <=> $1::vector as distance
471
- FROM documents d
574
+ FROM documents d
472
575
  JOIN document_embeddings de ON d.id = de.document_id
473
576
  WHERE d.metadata->>'difficulty' = 'beginner'
474
577
  AND d.metadata->'tags' ?| $2::text[]
475
- ORDER BY de.embedding <=> $1::vector
578
+ ORDER BY de.embedding <=> $1::vector
476
579
  LIMIT 5",
477
580
  [query_vector, ['ruby', 'programming']]
478
581
  )
@@ -488,7 +591,7 @@ For large datasets, tune HNSW parameters:
488
591
  -- High accuracy (slower build, more memory)
489
592
  WITH (m = 32, ef_construction = 128)
490
593
 
491
- -- Fast build (lower accuracy, less memory)
594
+ -- Fast build (lower accuracy, less memory)
492
595
  WITH (m = 8, ef_construction = 32)
493
596
 
494
597
  -- Balanced (recommended default)
@@ -502,9 +605,9 @@ WITH (m = 16, ef_construction = 64)
502
605
  SET hnsw.ef_search = 100; -- Higher = more accurate, slower
503
606
 
504
607
  -- Use EXPLAIN ANALYZE to optimize queries
505
- EXPLAIN ANALYZE
506
- SELECT * FROM document_embeddings
507
- ORDER BY embedding <=> '[0.1,0.2,...]'::vector
608
+ EXPLAIN ANALYZE
609
+ SELECT * FROM document_embeddings
610
+ ORDER BY embedding <=> '[0.1,0.2,...]'::vector
508
611
  LIMIT 10;
509
612
  ```
510
613
 
@@ -516,14 +619,14 @@ For large documents, use chunking for better search granularity:
516
619
  def chunk_document(text, chunk_size: 500, overlap: 50)
517
620
  chunks = []
518
621
  start = 0
519
-
622
+
520
623
  while start < text.length
521
624
  end_pos = [start + chunk_size, text.length].min
522
625
  chunk = text[start...end_pos]
523
626
  chunks << chunk
524
627
  start += chunk_size - overlap
525
628
  end
526
-
629
+
527
630
  chunks
528
631
  end
529
632
 
@@ -541,13 +644,14 @@ Run the complete vector search example:
541
644
 
542
645
  ```bash
543
646
  # Start services
544
- docker-compose up -d postgres ollama
647
+ docker compose up -d postgres ollama
545
648
 
546
649
  # Run example
547
650
  DB_HOST=localhost ruby examples/vector_search.rb
548
651
  ```
549
652
 
550
653
  The example demonstrates:
654
+
551
655
  - Document embedding generation and storage
552
656
  - Similarity search with different distance functions
553
657
  - Metadata filtering and advanced queries
@@ -558,7 +662,7 @@ The example demonstrates:
558
662
  ### Custom Provider Implementation
559
663
 
560
664
  ```ruby
561
- class MyCustomProvider < Prescient::BaseProvider
665
+ class MyCustomProvider < Prescient::Base
562
666
  def generate_embedding(text, **options)
563
667
  # Your implementation
564
668
  end
@@ -594,16 +698,16 @@ client = Prescient.client(:ollama)
594
698
  info = client.provider_info
595
699
 
596
700
  puts info[:name] # => :ollama
597
- puts info[:class] # => "Prescient::Ollama::Provider"
701
+ puts info[:class] # => "Ollama"
598
702
  puts info[:available] # => true
599
- puts info[:options] # => {...} (excluding sensitive data)
703
+ puts info[:options] # => { ... } (excluding sensitive data)
600
704
  ```
601
705
 
602
706
  ## Provider-Specific Features
603
707
 
604
708
  ### Ollama
605
709
 
606
- - Model management: `pull_model`, `list_models`
710
+ - Model management: `pull_model`, `available_models`
607
711
  - Local deployment support
608
712
  - No API costs
609
713
 
@@ -617,6 +721,7 @@ puts info[:options] # => {...} (excluding sensitive data)
617
721
  - Multiple embedding model sizes
618
722
  - Latest GPT models
619
723
  - Reliable performance
724
+ - Uses the Chat Completions endpoint for the stable normalized response contract; the newer Responses API remains a future compatibility extension.
620
725
 
621
726
  ### HuggingFace
622
727
 
@@ -633,6 +738,7 @@ The easiest way to get started with Prescient and Ollama is using Docker Compose
633
738
  Before starting, ensure your system meets the minimum requirements for running Ollama:
634
739
 
635
740
  #### **Minimum Requirements:**
741
+
636
742
  - **CPU**: 4+ cores (x86_64 or ARM64)
637
743
  - **RAM**: 8GB+ (16GB recommended)
638
744
  - **Storage**: 10GB+ free space for models
@@ -640,48 +746,53 @@ Before starting, ensure your system meets the minimum requirements for running O
640
746
 
641
747
  #### **Model-Specific Requirements:**
642
748
 
643
- | Model | RAM Required | Storage | Notes |
644
- |-------|-------------|---------|-------|
645
- | `nomic-embed-text` | 1GB | 274MB | Embedding model |
646
- | `llama3.1:8b` | 8GB | 4.7GB | Chat model (8B parameters) |
647
- | `llama3.1:70b` | 64GB+ | 40GB | Large chat model (70B parameters) |
648
- | `codellama:7b` | 8GB | 3.8GB | Code generation model |
749
+ | Model | RAM Required | Storage | Notes |
750
+ | ------------------ | ------------ | ------- | --------------------------------- |
751
+ | `nomic-embed-text` | 1GB | 274MB | Embedding model |
752
+ | `llama3.2:3b` | 2GB | 2.0GB | Chat model (3B parameters) |
753
+ | `llama3.1:70b` | 64GB+ | 40GB | Large chat model (70B parameters) |
754
+ | `codellama:7b` | 8GB | 3.8GB | Code generation model |
649
755
 
650
756
  #### **Performance Recommendations:**
757
+
651
758
  - **SSD Storage**: Significantly faster model loading
652
759
  - **GPU (Optional)**: NVIDIA GPU with 8GB+ VRAM for acceleration
653
760
  - **Network**: Stable internet for initial model downloads
654
761
  - **Docker**: 4GB+ memory limit configured
655
762
 
656
763
  #### **GPU Acceleration (Optional):**
764
+
657
765
  - **NVIDIA GPU**: RTX 3060+ with 8GB+ VRAM recommended
658
766
  - **CUDA**: Version 11.8+ required
659
767
  - **Docker**: NVIDIA Container Toolkit installed
660
768
  - **Performance**: 3-10x faster inference with compatible models
661
769
 
662
- > **💡 Tip**: Start with smaller models like `llama3.1:8b` and upgrade based on your hardware capabilities and performance needs.
770
+ > **💡 Tip**: Start with smaller models like `llama3.2:3b` and upgrade based on your hardware capabilities and performance needs.
663
771
 
664
772
  ### Quick Start with Docker
665
773
 
666
774
  1. **Start Ollama service:**
775
+
667
776
  ```bash
668
- docker-compose up -d ollama
777
+ docker compose up -d ollama
669
778
  ```
670
779
 
671
780
  2. **Pull required models:**
781
+
672
782
  ```bash
673
783
  # Automatic setup
674
- docker-compose up ollama-init
675
-
784
+ docker compose run --rm ollama-init
785
+
676
786
  # Or manual setup
677
787
  ./scripts/setup-ollama-models.sh
678
788
  ```
679
789
 
680
790
  3. **Run examples:**
791
+
681
792
  ```bash
682
793
  # Set environment variable
683
794
  export OLLAMA_URL=http://localhost:11434
684
-
795
+
685
796
  # Run examples
686
797
  ruby examples/custom_contexts.rb
687
798
  ```
@@ -702,9 +813,9 @@ The included `docker-compose.yml` provides:
702
813
  services:
703
814
  ollama:
704
815
  ports:
705
- - "11434:11434" # Ollama API port
816
+ - "11434:11434" # Ollama API port
706
817
  volumes:
707
- - ollama_data:/root/.ollama # Persist models
818
+ - ollama_data:/root/.ollama # Persist models
708
819
  environment:
709
820
  - OLLAMA_HOST=0.0.0.0
710
821
  - OLLAMA_ORIGINS=*
@@ -732,7 +843,7 @@ services:
732
843
  # Ollama Configuration
733
844
  OLLAMA_URL=http://localhost:11434
734
845
  OLLAMA_EMBEDDING_MODEL=nomic-embed-text
735
- OLLAMA_CHAT_MODEL=llama3.1:8b
846
+ OLLAMA_CHAT_MODEL=llama3.2:3b
736
847
 
737
848
  # Optional: Other AI providers
738
849
  OPENAI_API_KEY=your_key_here
@@ -749,7 +860,7 @@ curl http://localhost:11434/api/tags
749
860
  # Pull a specific model
750
861
  curl -X POST http://localhost:11434/api/pull \
751
862
  -H "Content-Type: application/json" \
752
- -d '{"name": "llama3.1:8b"}'
863
+ -d '{ "name": "llama3.2:3b"}'
753
864
 
754
865
  # Health check
755
866
  curl http://localhost:11434/api/version
@@ -770,6 +881,7 @@ For production use:
770
881
  #### **Common Issues:**
771
882
 
772
883
  **Out of Memory Errors:**
884
+
773
885
  ```bash
774
886
  # Check available memory
775
887
  free -h
@@ -778,10 +890,11 @@ free -h
778
890
  # Settings > Resources > Memory: 8GB+
779
891
 
780
892
  # Use smaller models if hardware limited
781
- OLLAMA_CHAT_MODEL=llama3.1:7b ruby examples/custom_contexts.rb
893
+ OLLAMA_CHAT_MODEL=llama3.2:3b ruby examples/custom_contexts.rb
782
894
  ```
783
895
 
784
896
  **Slow Model Loading:**
897
+
785
898
  ```bash
786
899
  # Check disk I/O
787
900
  iostat -x 1
@@ -791,15 +904,17 @@ iostat -x 1
791
904
  ```
792
905
 
793
906
  **Model Download Failures:**
907
+
794
908
  ```bash
795
909
  # Check disk space
796
910
  df -h
797
911
 
798
912
  # Manually pull models with retry
799
- docker exec prescient-ollama ollama pull llama3.1:8b
913
+ docker exec prescient-ollama ollama pull llama3.2:3b
800
914
  ```
801
915
 
802
916
  **GPU Not Detected:**
917
+
803
918
  ```bash
804
919
  # Check NVIDIA Docker runtime
805
920
  docker run --rm --gpus all nvidia/cuda:11.8-base nvidia-smi
@@ -820,7 +935,7 @@ docker logs prescient-ollama
820
935
  # Test API response time
821
936
  time curl -X POST http://localhost:11434/api/generate \
822
937
  -H "Content-Type: application/json" \
823
- -d '{"model": "llama3.1:8b", "prompt": "Hello", "stream": false}'
938
+ -d '{ "model": "llama3.2:3b", "prompt": "Hello", "stream": false}'
824
939
  ```
825
940
 
826
941
  ## Testing
@@ -828,11 +943,44 @@ time curl -X POST http://localhost:11434/api/generate \
828
943
  The gem includes comprehensive test coverage:
829
944
 
830
945
  ```bash
831
- bundle exec rspec
946
+ bundle exec rake test
832
947
  ```
833
948
 
834
949
  ## Development
835
950
 
951
+ ### Opt-in live provider smoke tests
952
+
953
+ The default test suite uses mocked provider interactions. To exercise a live
954
+ provider explicitly, set `PRESCIENT_LIVE_SMOKE=1` and select one or more
955
+ providers with `PRESCIENT_LIVE_PROVIDERS`:
956
+
957
+ ```bash
958
+ PRESCIENT_LIVE_SMOKE=1 \
959
+ PRESCIENT_LIVE_PROVIDERS=openai \
960
+ OPENAI_API_KEY=... \
961
+ bundle exec ruby -Itest test/prescient/live_provider_smoke_test.rb
962
+ ```
963
+
964
+ Supported provider names are `ollama`, `anthropic`, `openai`, and
965
+ `huggingface`. The corresponding provider environment variables and model
966
+ overrides are honored. These tests are never live unless both opt-in
967
+ variables are set.
968
+
969
+ ### RBS and Steep
970
+
971
+ Validate the curated core API signatures with:
972
+
973
+ ```bash
974
+ bundle exec rake rbs:validate
975
+ ```
976
+
977
+ Generate disposable prototypes for comparison with:
978
+
979
+ ```bash
980
+ bundle exec rake rbs:prototype
981
+ bundle exec rake rbs:diff
982
+ ```
983
+
836
984
  After checking out the repo, run:
837
985
 
838
986
  ```bash
@@ -856,34 +1004,3 @@ bundle exec rake install
856
1004
  ## License
857
1005
 
858
1006
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
859
-
860
- ## Roadmap
861
-
862
- ### Version 0.2.0 (Planned)
863
-
864
- - **MariaDB Vector Support**: Integration with MariaDB using external vector databases
865
- - **Hybrid Database Architecture**: Support for MariaDB + Milvus/Qdrant combinations
866
- - **Vector Database Adapters**: Pluggable adapters for different vector storage backends
867
- - **Enhanced Chunking Strategies**: Smart document splitting with multiple algorithms
868
- - **Search Result Ranking**: Advanced scoring and re-ranking capabilities
869
-
870
- ### Version 0.3.0 (Future)
871
-
872
- - **Streaming Responses**: Real-time response streaming for chat applications
873
- - **Multi-Model Ensembles**: Combine responses from multiple AI providers
874
- - **Advanced Analytics**: Search performance insights and usage analytics
875
- - **Cloud Provider Integration**: Direct support for Pinecone, Weaviate, etc.
876
-
877
- ## Changelog
878
-
879
- ### Version 0.1.0
880
-
881
- - Initial release
882
- - Support for Ollama, Anthropic, OpenAI, and HuggingFace
883
- - Unified interface for embeddings and text generation
884
- - Comprehensive error handling and retry logic
885
- - Health monitoring capabilities
886
- - PostgreSQL pgvector integration with complete Docker setup
887
- - Vector similarity search with multiple distance functions
888
- - Document chunking and metadata filtering
889
- - Performance optimization guides and troubleshooting