prescient 0.4.0 → 0.6.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/.dockerignore +18 -0
- data/CHANGELOG.md +42 -0
- data/Dockerfile +45 -0
- data/INTEGRATION_GUIDE.md +121 -11
- data/README.md +261 -16
- data/Rakefile +1 -1
- data/VECTOR_SEARCH_GUIDE.md +7 -3
- data/docker-compose.api.yml +21 -0
- data/examples/README.md +19 -1
- data/examples/basic_usage.rb +1 -1
- data/examples/custom_contexts.rb +6 -21
- data/examples/rest_api.ru +30 -0
- data/examples/vector_search.rb +69 -305
- data/lib/prescient/api.rb +285 -0
- data/lib/prescient/cli.rb +197 -4
- data/lib/prescient/configuration_loader.rb +437 -0
- data/lib/prescient/provider/deepseek.rb +139 -0
- data/lib/prescient/provider/gemini.rb +173 -0
- data/lib/prescient/provider/huggingface.rb +1 -0
- data/lib/prescient/provider/mistral.rb +171 -0
- data/lib/prescient/provider/openai.rb +3 -0
- data/lib/prescient/provider/xai.rb +139 -0
- data/lib/prescient/version.rb +1 -1
- data/lib/prescient.rb +119 -23
- data/schema/prescient.configuration.schema.json +153 -0
- data/sig/prescient.rbs +115 -0
- metadata +12 -1
data/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Prescient
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://rubygems.org/gems/prescient)
|
|
4
|
+
[](https://www.ruby-lang.org/)
|
|
5
|
+
[](https://github.com/kanutocd/prescient/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/kanutocd/prescient/actions/workflows/security.yml)
|
|
7
|
+
[](LICENSE.txt)
|
|
8
|
+
|
|
9
|
+
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, Hugging Face, Google Gemini, Mistral, DeepSeek, or xAI. Prescient handles provider selection, retries, health checks, and fallback.
|
|
4
10
|
|
|
5
11
|
For focused guidance, see the **[examples guide](https://github.com/kanutocd/prescient/tree/main/examples)**,
|
|
6
12
|
**[Rails integration guide](https://github.com/kanutocd/prescient/blob/main/INTEGRATION_GUIDE.md)**, and
|
|
@@ -14,7 +20,7 @@ For focused guidance, see the **[examples guide](https://github.com/kanutocd/pre
|
|
|
14
20
|
- **Text Completion**: Chat completions with context support
|
|
15
21
|
- **Error Handling**: Robust error handling with automatic retries
|
|
16
22
|
- **Health Monitoring**: Built-in health checks for all providers
|
|
17
|
-
- **Flexible Configuration**:
|
|
23
|
+
- **Flexible Configuration**: YAML, environment variable, and programmatic configuration
|
|
18
24
|
|
|
19
25
|
## Supported Providers
|
|
20
26
|
|
|
@@ -42,6 +48,30 @@ For focused guidance, see the **[examples guide](https://github.com/kanutocd/pre
|
|
|
42
48
|
- **Capabilities**: Embeddings, Text Generation
|
|
43
49
|
- **Use Case**: Open-source models, research
|
|
44
50
|
|
|
51
|
+
### Google Gemini
|
|
52
|
+
|
|
53
|
+
- **Models**: Gemini generation and embedding models
|
|
54
|
+
- **Capabilities**: Embeddings, Text Generation, Model Listing
|
|
55
|
+
- **Use Case**: Google AI hosted models
|
|
56
|
+
|
|
57
|
+
### Mistral
|
|
58
|
+
|
|
59
|
+
- **Models**: Mistral chat and embedding models
|
|
60
|
+
- **Capabilities**: Embeddings, Text Generation, Model Listing
|
|
61
|
+
- **Use Case**: Mistral AI hosted models
|
|
62
|
+
|
|
63
|
+
### DeepSeek
|
|
64
|
+
|
|
65
|
+
- **Models**: DeepSeek chat models
|
|
66
|
+
- **Capabilities**: Text Generation, Model Listing (no embeddings)
|
|
67
|
+
- **Use Case**: DeepSeek hosted reasoning and chat models
|
|
68
|
+
|
|
69
|
+
### xAI
|
|
70
|
+
|
|
71
|
+
- **Models**: Grok chat models
|
|
72
|
+
- **Capabilities**: Text Generation, Model Listing (no embeddings)
|
|
73
|
+
- **Use Case**: xAI hosted reasoning and chat models
|
|
74
|
+
|
|
45
75
|
## Installation
|
|
46
76
|
|
|
47
77
|
Add this line to your application's Gemfile:
|
|
@@ -70,6 +100,7 @@ Prescient includes a thin CLI for provider inspection and common operations:
|
|
|
70
100
|
prescient providers
|
|
71
101
|
prescient health
|
|
72
102
|
prescient config validate
|
|
103
|
+
prescient config example
|
|
73
104
|
prescient generate "Explain Ruby Ractors"
|
|
74
105
|
prescient embed "Ruby is a programming language"
|
|
75
106
|
```
|
|
@@ -81,6 +112,13 @@ Supported options include:
|
|
|
81
112
|
--model NAME Override the selected operation's model
|
|
82
113
|
--chat-model NAME Override the chat model for generation
|
|
83
114
|
--embedding-model NAME Override the embedding model
|
|
115
|
+
--system-prompt TEXT Override the system prompt
|
|
116
|
+
--no-context-template TEXT
|
|
117
|
+
Override the no-context prompt template
|
|
118
|
+
--with-context-template TEXT
|
|
119
|
+
Override the with-context prompt template
|
|
120
|
+
--prompt-templates-file PATH
|
|
121
|
+
Load prompt templates from a YAML file
|
|
84
122
|
--api-key KEY Use an API key for the operation
|
|
85
123
|
--api-key-env NAME Read the API key from an environment variable
|
|
86
124
|
--format FORMAT Select text or json output
|
|
@@ -96,7 +134,7 @@ printf '%s' "Explain PostgreSQL logical replication" | \
|
|
|
96
134
|
prescient generate --provider openai --format json
|
|
97
135
|
```
|
|
98
136
|
|
|
99
|
-
|
|
137
|
+
OpenAI example JSON output:
|
|
100
138
|
|
|
101
139
|
```json
|
|
102
140
|
{
|
|
@@ -125,6 +163,35 @@ Example JSON output:
|
|
|
125
163
|
}
|
|
126
164
|
```
|
|
127
165
|
|
|
166
|
+
Anthropic example JSON output:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"response": "PostgreSQL logical replication is a method of replicating data between PostgreSQL databases that allows fine-grained control over which data is replicated and how it is applied. Unlike physical replication, which copies the entire database cluster at the storage level, logical replication works at the level of individual database changes, such as INSERT, UPDATE, and DELETE operations.\n\n### Key Features of PostgreSQL Logical Replication:\n\n1. **Row-Level Changes:** Logical replication replicates changes at the row level, meaning only the actual data changes are sent to the subscriber.\n\n2. **Selective Replication:** You can replicate specific tables rather than the entire database. This allows partial replication tailored to your needs.\n\n3. **Asynchronous Replication:** Logical replication is asynchronous, so there might be a slight delay between the publisher and subscriber.\n\n4. **Bidirectional Replication (with care):** While PostgreSQL does not natively support multi-master replication, logical replication can be configured to allow bidirectional replication setups with caution to avoid conflicts.\n\n5. **Decoupling of Replication:** Logical replication decouples the replication from the physical storage, enabling replication across different PostgreSQL versions (within compatibility limits).\n\n### How Logical Replication Works:\n\n- **Publisher:** The source database that sends changes. It publishes a set of changes based on one or more publications.\n- **Publication:** A set of changes (typically from specific tables) that the publisher makes available to subscribers.\n- **Subscriber:** The target database that receives changes and applies them.\n- **Subscription:** A configuration on the subscriber that connects to a publication and applies changes.\n\n### Use Cases:\n\n- Replicating specific tables or subsets of data.\n- Migrating data between PostgreSQL versions or clusters.\n- Distributing data geographically.\n- Implementing data warehousing or reporting solutions with up-to-date data.\n- Supporting microservices architectures where different services own different parts of the data.\n\n### Basic Setup Example:\n\n1. **On the Publisher:**\n\n```sql\nCREATE PUBLICATION my_publication FOR TABLE my_table;\n```\n\n2. **On the Subscriber:**\n\n```sql\nCREATE SUBSCRIPTION my_subscription\nCONNECTION 'host=publisher_host dbname=mydb user=replicator password=secret'\nPUBLICATION my_publication;\n```\n\nOnce set up, changes to `my_table` on the publisher will be sent and applied to the subscriber.\n\n### Important Notes:\n\n- Logical replication requires WAL (Write-Ahead Logging) to be configured properly with `wal_level = logical`.\n- Some DDL changes (like adding columns) need careful handling as logical replication primarily replicates DML changes.\n- Logical replication does not replicate sequences, large objects, or certain system catalogs automatically.\n- Conflict resolution is mostly manual; the subscriber applies changes as received.\n\n---\n\nIn summary, PostgreSQL logical replication provides a flexible, table-level replication mechanism that supports selective and version-independent replication of data changes, suitable for many modern replication and data distribution scenarios.",
|
|
171
|
+
"model": "gpt-4.1-mini",
|
|
172
|
+
"provider": "openai",
|
|
173
|
+
"processing_time": null,
|
|
174
|
+
"metadata": {
|
|
175
|
+
"usage": {
|
|
176
|
+
"prompt_tokens": 38,
|
|
177
|
+
"completion_tokens": 597,
|
|
178
|
+
"total_tokens": 635,
|
|
179
|
+
"prompt_tokens_details": {
|
|
180
|
+
"cached_tokens": 0,
|
|
181
|
+
"audio_tokens": 0
|
|
182
|
+
},
|
|
183
|
+
"completion_tokens_details": {
|
|
184
|
+
"reasoning_tokens": 0,
|
|
185
|
+
"audio_tokens": 0,
|
|
186
|
+
"accepted_prediction_tokens": 0,
|
|
187
|
+
"rejected_prediction_tokens": 0
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"finish_reason": "stop"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
128
195
|
For automated model and credential overrides:
|
|
129
196
|
|
|
130
197
|
```bash
|
|
@@ -146,6 +213,72 @@ The CLI writes results to stdout, diagnostics to stderr, and returns a
|
|
|
146
213
|
non-zero status for invalid usage, provider errors, or unreachable health
|
|
147
214
|
checks. It uses the same `Prescient::Client` execution path as Ruby callers.
|
|
148
215
|
|
|
216
|
+
Generate a schema-backed, annotated starter configuration with:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
prescient config example > prescient.yml
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The generated file points YAML language servers at the latest schema on the
|
|
223
|
+
main branch. Pin the schema URL to a release tag when reproducible tooling is
|
|
224
|
+
required.
|
|
225
|
+
|
|
226
|
+
## REST API
|
|
227
|
+
|
|
228
|
+
`Prescient::API` is a dependency-free Rack-compatible application. Mount it in
|
|
229
|
+
the web server of your choice without making HTTP a requirement for library
|
|
230
|
+
users. The API and CLI files are loaded lazily; `Prescient::API` autoloads on
|
|
231
|
+
first reference, or you can require it explicitly:
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
require 'prescient'
|
|
235
|
+
require 'prescient/api'
|
|
236
|
+
|
|
237
|
+
run Prescient::API.new(
|
|
238
|
+
authentication: ->(env) { env['HTTP_AUTHORIZATION'] == "Bearer #{ENV['PRESCIENT_API_TOKEN']}" }
|
|
239
|
+
)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Available endpoints include:
|
|
243
|
+
|
|
244
|
+
- **`POST /v1/generate`**
|
|
245
|
+
- **`POST /v1/embeddings`**
|
|
246
|
+
- **`POST /v1/embeddings/batch`**
|
|
247
|
+
- **`GET /v1/providers`**
|
|
248
|
+
- **`GET /v1/models`**
|
|
249
|
+
- **`GET /v1/capabilities`**
|
|
250
|
+
- **`GET /v1/health`**
|
|
251
|
+
- **`GET /v1/version`**
|
|
252
|
+
- **`GET /healthz`**
|
|
253
|
+
- **`GET /readyz`**
|
|
254
|
+
|
|
255
|
+
Responses include:
|
|
256
|
+
|
|
257
|
+
- a request ID
|
|
258
|
+
- a generic JSON error envelope that never exposes raw provider response bodies.
|
|
259
|
+
|
|
260
|
+
## Docker
|
|
261
|
+
|
|
262
|
+
Build and run the REST API image as a non-root container:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
docker build -t prescient:local .
|
|
266
|
+
docker run --rm -p 9292:9292 \
|
|
267
|
+
-e PRESCIENT_API_TOKEN=change-me \
|
|
268
|
+
prescient:local
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The image exposes port `9292`, includes a `/healthz` healthcheck, supports a
|
|
272
|
+
read-only filesystem, and does not bundle PostgreSQL, Redis, or a worker. The
|
|
273
|
+
Compose example provides the same setup:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
PRESCIENT_API_TOKEN=change-me docker compose -f docker-compose.api.yml up --build
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Tagged releases publish to GHCR as:
|
|
280
|
+
`ghcr.io/kanutocd/prescient:<version>`.
|
|
281
|
+
|
|
149
282
|
## Configuration
|
|
150
283
|
|
|
151
284
|
### Environment Variables
|
|
@@ -169,6 +302,68 @@ OPENAI_CHAT_MODEL=gpt-4.1-mini
|
|
|
169
302
|
HUGGINGFACE_API_KEY=your_api_key
|
|
170
303
|
HUGGINGFACE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
171
304
|
HUGGINGFACE_CHAT_MODEL=google/gemma-2-2b-it
|
|
305
|
+
|
|
306
|
+
# Google Gemini
|
|
307
|
+
GEMINI_API_KEY=your_api_key
|
|
308
|
+
GEMINI_EMBEDDING_MODEL=gemini-embedding-001
|
|
309
|
+
GEMINI_CHAT_MODEL=gemini-2.5-flash
|
|
310
|
+
|
|
311
|
+
# Mistral
|
|
312
|
+
MISTRAL_API_KEY=your_api_key
|
|
313
|
+
MISTRAL_EMBEDDING_MODEL=mistral-embed
|
|
314
|
+
MISTRAL_CHAT_MODEL=mistral-large-latest
|
|
315
|
+
|
|
316
|
+
# DeepSeek
|
|
317
|
+
DEEPSEEK_API_KEY=your_api_key
|
|
318
|
+
DEEPSEEK_CHAT_MODEL=deepseek-v4-flash
|
|
319
|
+
|
|
320
|
+
# xAI
|
|
321
|
+
XAI_API_KEY=your_api_key
|
|
322
|
+
XAI_CHAT_MODEL=grok-4.5
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### YAML Configuration
|
|
326
|
+
|
|
327
|
+
Load a versioned YAML configuration with environment-backed credentials:
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/kanutocd/prescient/refs/heads/main/schema/prescient.configuration.schema.json
|
|
331
|
+
version: 1
|
|
332
|
+
default_provider: openai
|
|
333
|
+
providers:
|
|
334
|
+
openai:
|
|
335
|
+
type: openai
|
|
336
|
+
api_key_env: OPENAI_API_KEY
|
|
337
|
+
embedding_model: text-embedding-3-small
|
|
338
|
+
chat_model: gpt-4.1-mini
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
Prescient.load_configuration('prescient.yml')
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Configuration precedence is CLI overrides, environment defaults and references,
|
|
346
|
+
YAML values, then built-in defaults. Use `prescient config validate` to check a
|
|
347
|
+
configuration before running an operation, or `prescient config example` to
|
|
348
|
+
generate an annotated starter file.
|
|
349
|
+
|
|
350
|
+
Prompt templates can also be configured per provider. Use the YAML mapping for
|
|
351
|
+
multiline templates, or pass a template file to a single CLI operation:
|
|
352
|
+
|
|
353
|
+
```yaml
|
|
354
|
+
providers:
|
|
355
|
+
openai:
|
|
356
|
+
type: openai
|
|
357
|
+
api_key_env: OPENAI_API_KEY
|
|
358
|
+
chat_model: gpt-4.1-mini
|
|
359
|
+
prompt_templates:
|
|
360
|
+
system_prompt: You are a concise assistant.
|
|
361
|
+
no_context_template: "%{system_prompt}\n\nUser: %{query}"
|
|
362
|
+
with_context_template: "%{system_prompt}\n\nContext:\n%{context}\n\nUser: %{query}"
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
prescient generate --prompt-templates-file prompts.yml "Summarize this"
|
|
172
367
|
```
|
|
173
368
|
|
|
174
369
|
### Programmatic Configuration
|
|
@@ -202,6 +397,32 @@ Prescient.configure do |config|
|
|
|
202
397
|
embedding_model: 'text-embedding-3-small',
|
|
203
398
|
chat_model: 'gpt-4.1-mini'
|
|
204
399
|
)
|
|
400
|
+
|
|
401
|
+
# Add Google Gemini
|
|
402
|
+
config.add_provider(:gemini, Prescient::Provider::Gemini,
|
|
403
|
+
api_key: ENV['GEMINI_API_KEY'],
|
|
404
|
+
embedding_model: 'gemini-embedding-001',
|
|
405
|
+
chat_model: 'gemini-2.5-flash'
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Add Mistral
|
|
409
|
+
config.add_provider(:mistral, Prescient::Provider::Mistral,
|
|
410
|
+
api_key: ENV['MISTRAL_API_KEY'],
|
|
411
|
+
embedding_model: 'mistral-embed',
|
|
412
|
+
chat_model: 'mistral-large-latest'
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Add DeepSeek
|
|
416
|
+
config.add_provider(:deepseek, Prescient::Provider::DeepSeek,
|
|
417
|
+
api_key: ENV['DEEPSEEK_API_KEY'],
|
|
418
|
+
chat_model: 'deepseek-v4-flash'
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Add xAI
|
|
422
|
+
config.add_provider(:xai, Prescient::Provider::XAI,
|
|
423
|
+
api_key: ENV['XAI_API_KEY'],
|
|
424
|
+
chat_model: 'grok-4.5'
|
|
425
|
+
)
|
|
205
426
|
end
|
|
206
427
|
```
|
|
207
428
|
|
|
@@ -357,14 +578,14 @@ Prescient.configure do |config|
|
|
|
357
578
|
prompt_templates: {
|
|
358
579
|
system_prompt: 'You are a friendly customer service representative.',
|
|
359
580
|
no_context_template: <<~TEMPLATE.strip,
|
|
360
|
-
%{
|
|
581
|
+
%{system_prompt}
|
|
361
582
|
|
|
362
583
|
Customer Question: %{query}
|
|
363
584
|
|
|
364
585
|
Please provide a helpful response.
|
|
365
586
|
TEMPLATE
|
|
366
587
|
with_context_template: <<~TEMPLATE.strip
|
|
367
|
-
%{
|
|
588
|
+
%{system_prompt} Use the company info below to help answer.
|
|
368
589
|
|
|
369
590
|
Company Information:
|
|
370
591
|
%{context}
|
|
@@ -534,8 +755,9 @@ results = store.search(embedding:, provider: :openai, model: 'text-embedding-3-s
|
|
|
534
755
|
```
|
|
535
756
|
|
|
536
757
|
Every vector must exactly match the store's configured dimensions; Prescient
|
|
537
|
-
never pads or truncates vectors. The
|
|
538
|
-
|
|
758
|
+
never pads or truncates vectors. The application-schema example below is
|
|
759
|
+
optional integration material for projects that need documents, chunks, and
|
|
760
|
+
custom metadata; it is not managed by `Prescient::Pgvector::Store`.
|
|
539
761
|
|
|
540
762
|
### Setup with Docker
|
|
541
763
|
|
|
@@ -554,7 +776,7 @@ docker compose up -d postgres
|
|
|
554
776
|
|
|
555
777
|
### Database Schema
|
|
556
778
|
|
|
557
|
-
The
|
|
779
|
+
The optional Docker demo creates these application-owned tables:
|
|
558
780
|
|
|
559
781
|
- **`documents`** - Store original content and metadata
|
|
560
782
|
- **`document_embeddings`** - Store vector embeddings for documents
|
|
@@ -736,10 +958,9 @@ DB_HOST=localhost ruby examples/vector_search.rb
|
|
|
736
958
|
|
|
737
959
|
The example demonstrates:
|
|
738
960
|
|
|
739
|
-
-
|
|
740
|
-
-
|
|
741
|
-
-
|
|
742
|
-
- Performance comparison between approaches
|
|
961
|
+
- Embedding generation through `Prescient::Client`
|
|
962
|
+
- Dimension-validated storage through `Prescient::Pgvector::Store`
|
|
963
|
+
- Similarity search with provider and model filters
|
|
743
964
|
|
|
744
965
|
## Advanced Usage
|
|
745
966
|
|
|
@@ -813,6 +1034,30 @@ puts info[:options] # => { ... } (excluding sensitive data)
|
|
|
813
1034
|
- Research-friendly
|
|
814
1035
|
- Free tier available
|
|
815
1036
|
|
|
1037
|
+
### Google Gemini
|
|
1038
|
+
|
|
1039
|
+
- Text generation and embeddings
|
|
1040
|
+
- Google AI API integration
|
|
1041
|
+
- Model discovery through the Gemini models endpoint
|
|
1042
|
+
|
|
1043
|
+
### Mistral
|
|
1044
|
+
|
|
1045
|
+
- Text generation and embeddings
|
|
1046
|
+
- OpenAI-compatible API style
|
|
1047
|
+
- Model discovery through the Mistral models endpoint
|
|
1048
|
+
|
|
1049
|
+
### DeepSeek
|
|
1050
|
+
|
|
1051
|
+
- Text generation
|
|
1052
|
+
- OpenAI-compatible API style
|
|
1053
|
+
- No embedding support
|
|
1054
|
+
|
|
1055
|
+
### xAI
|
|
1056
|
+
|
|
1057
|
+
- Text generation
|
|
1058
|
+
- OpenAI-compatible API style
|
|
1059
|
+
- No embedding support
|
|
1060
|
+
|
|
816
1061
|
## Docker Setup (Recommended for Ollama)
|
|
817
1062
|
|
|
818
1063
|
The easiest way to get started with Prescient and Ollama is using Docker Compose:
|
|
@@ -1045,10 +1290,10 @@ OPENAI_API_KEY=... \
|
|
|
1045
1290
|
bundle exec ruby -Itest test/prescient/live_provider_smoke_test.rb
|
|
1046
1291
|
```
|
|
1047
1292
|
|
|
1048
|
-
Supported provider names are `ollama`, `anthropic`, `openai`,
|
|
1049
|
-
`
|
|
1050
|
-
overrides are honored. These tests are never
|
|
1051
|
-
variables are set.
|
|
1293
|
+
Supported provider names are `ollama`, `anthropic`, `openai`, `huggingface`,
|
|
1294
|
+
`gemini`, `mistral`, `deepseek`, and `xai`. The corresponding provider
|
|
1295
|
+
environment variables and model overrides are honored. These tests are never
|
|
1296
|
+
live unless both opt-in variables are set.
|
|
1052
1297
|
|
|
1053
1298
|
### RBS and Steep
|
|
1054
1299
|
|
data/Rakefile
CHANGED
data/VECTOR_SEARCH_GUIDE.md
CHANGED
|
@@ -27,6 +27,10 @@ store.create_index!(metric: :cosine)
|
|
|
27
27
|
or chunk tables, connections, migrations outside that table, or the `pg` gem.
|
|
28
28
|
Use `#upsert` and `#search` with embeddings of exactly the configured dimension.
|
|
29
29
|
|
|
30
|
+
The remaining sections describe an optional application-owned document schema
|
|
31
|
+
used by the repository's Docker demo. They are not tables created or managed
|
|
32
|
+
by `Prescient::Pgvector::Store`.
|
|
33
|
+
|
|
30
34
|
### 1. Start Services
|
|
31
35
|
|
|
32
36
|
```bash
|
|
@@ -55,12 +59,12 @@ export DB_HOST=localhost
|
|
|
55
59
|
export OLLAMA_URL=http://localhost:11434
|
|
56
60
|
|
|
57
61
|
# Run the example
|
|
58
|
-
ruby examples/vector_search.rb
|
|
62
|
+
bundle exec ruby examples/vector_search.rb
|
|
59
63
|
```
|
|
60
64
|
|
|
61
65
|
## Architecture Overview
|
|
62
66
|
|
|
63
|
-
###
|
|
67
|
+
### Application-Owned Example Schema
|
|
64
68
|
|
|
65
69
|
```
|
|
66
70
|
documents
|
|
@@ -103,7 +107,7 @@ chunk_embeddings
|
|
|
103
107
|
|
|
104
108
|
### Vector Indexes
|
|
105
109
|
|
|
106
|
-
The
|
|
110
|
+
The optional Docker demo creates HNSW indexes for the application-owned tables:
|
|
107
111
|
|
|
108
112
|
- **Cosine Distance**: `embedding <=> query_vector`
|
|
109
113
|
- **L2 Distance**: `embedding <-> query_vector`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
services:
|
|
2
|
+
prescient-api:
|
|
3
|
+
build:
|
|
4
|
+
context: .
|
|
5
|
+
dockerfile: Dockerfile
|
|
6
|
+
image: prescient:local
|
|
7
|
+
ports:
|
|
8
|
+
- "9292:9292"
|
|
9
|
+
environment:
|
|
10
|
+
PRESCIENT_API_TOKEN: ${PRESCIENT_API_TOKEN:?set PRESCIENT_API_TOKEN}
|
|
11
|
+
OLLAMA_URL: ${OLLAMA_URL:-http://host.docker.internal:11434}
|
|
12
|
+
read_only: true
|
|
13
|
+
tmpfs:
|
|
14
|
+
- /tmp
|
|
15
|
+
restart: unless-stopped
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:9292/healthz"]
|
|
18
|
+
interval: 30s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 3
|
|
21
|
+
start_period: 10s
|
data/examples/README.md
CHANGED
|
@@ -15,7 +15,25 @@ bundle install
|
|
|
15
15
|
- `custom_prompts.rb` — system prompts and no-context/with-context templates.
|
|
16
16
|
- `custom_contexts.rb` — explicit context types, field matching, formatting,
|
|
17
17
|
and embedding field selection.
|
|
18
|
-
- `vector_search.rb` — PostgreSQL/pgvector storage
|
|
18
|
+
- `vector_search.rb` — `Prescient::Pgvector::Store` PostgreSQL/pgvector storage
|
|
19
|
+
and similarity search.
|
|
20
|
+
- `rest_api.ru` — a tiny Rack-compatible application that mounts
|
|
21
|
+
`Prescient::API` and lists its endpoints at `/`.
|
|
22
|
+
|
|
23
|
+
Run the REST API example with a Rack server such as `rackup`:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
BUNDLE_WITH=rack_example bundle install
|
|
27
|
+
PRESCIENT_API_TOKEN=change-me BUNDLE_WITH=rack_example \
|
|
28
|
+
bundle exec rackup -s puma examples/rest_api.ru
|
|
29
|
+
curl http://localhost:9292/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Running `bundle exec ruby examples/rest_api.ru` directly prints the same
|
|
33
|
+
endpoint catalog without starting a server.
|
|
34
|
+
|
|
35
|
+
The example does not add Rack as a Prescient runtime dependency; it only uses
|
|
36
|
+
the Rack-compatible `call` interface provided by `Prescient::API`.
|
|
19
37
|
|
|
20
38
|
The first three examples use Ollama by default. Start Ollama and pull the
|
|
21
39
|
current local models before running them:
|
data/examples/basic_usage.rb
CHANGED
|
@@ -67,7 +67,7 @@ end
|
|
|
67
67
|
# Example 3: Provider comparison (if multiple providers configured)
|
|
68
68
|
puts "\n=== Example 3: Provider Health Check ==="
|
|
69
69
|
|
|
70
|
-
providers = [
|
|
70
|
+
providers = %i[ollama anthropic openai huggingface gemini mistral deepseek xai]
|
|
71
71
|
|
|
72
72
|
providers.each do |provider_name|
|
|
73
73
|
begin
|
data/examples/custom_contexts.rb
CHANGED
|
@@ -228,8 +228,8 @@ rescue Prescient::Error => e
|
|
|
228
228
|
puts "❌ Error: #{e.message}"
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
-
# Example 4: Embedding Text
|
|
232
|
-
puts "\n--- Example 4: Embedding Text
|
|
231
|
+
# Example 4: Embedding Text Selection
|
|
232
|
+
puts "\n--- Example 4: Embedding Text Selection ---"
|
|
233
233
|
|
|
234
234
|
begin
|
|
235
235
|
# Configure a provider with context configs
|
|
@@ -251,7 +251,7 @@ begin
|
|
|
251
251
|
client = Prescient.client(:embedding_demo)
|
|
252
252
|
|
|
253
253
|
if client.available?
|
|
254
|
-
#
|
|
254
|
+
# The configured embedding_fields select title, content, and tags.
|
|
255
255
|
blog_post = {
|
|
256
256
|
'type' => 'blog_post',
|
|
257
257
|
'title' => 'Getting Started with AI',
|
|
@@ -262,12 +262,10 @@ begin
|
|
|
262
262
|
'publish_date' => '2024-01-15'
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
embedding_text = client.provider.send(:extract_embedding_text, blog_post)
|
|
268
|
-
puts "📊 Embedding Text Extracted:"
|
|
265
|
+
embedding_text = [blog_post['title'], blog_post['content'], blog_post['tags']].join(' ')
|
|
266
|
+
puts "📊 Embedding Text Selected:"
|
|
269
267
|
puts "\"#{embedding_text}\""
|
|
270
|
-
puts "\n(
|
|
268
|
+
puts "\n(Only title, content, and tags are included in the embedding input)"
|
|
271
269
|
|
|
272
270
|
# Generate actual embedding
|
|
273
271
|
puts "\n🔢 Generating embedding..."
|
|
@@ -317,19 +315,6 @@ begin
|
|
|
317
315
|
puts "🔧 Raw data (no context config):"
|
|
318
316
|
random_data.each { |item| puts " #{item}" }
|
|
319
317
|
|
|
320
|
-
puts "\n📄 How items are formatted without context config:"
|
|
321
|
-
random_data.each do |item|
|
|
322
|
-
formatted = client.provider.send(:format_context_item, item)
|
|
323
|
-
puts " #{formatted}"
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
puts "\n🔤 Embedding text extraction (automatic field filtering):"
|
|
327
|
-
random_data.each do |item|
|
|
328
|
-
embedding_text = client.provider.send(:extract_embedding_text, item)
|
|
329
|
-
puts " \"#{embedding_text}\""
|
|
330
|
-
puts " (Notice: excludes 'created_at', 'timestamp' - common metadata fields)"
|
|
331
|
-
end
|
|
332
|
-
|
|
333
318
|
response = client.generate_response("Summarize the key issues", random_data)
|
|
334
319
|
puts "\n🤖 AI Response (using default formatting):"
|
|
335
320
|
puts response[:response]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative '../lib/prescient'
|
|
5
|
+
|
|
6
|
+
api = Prescient::API.new(
|
|
7
|
+
authentication: ->(env) {
|
|
8
|
+
expected = ENV.fetch('PRESCIENT_API_TOKEN', nil)
|
|
9
|
+
expected && env['HTTP_AUTHORIZATION'] == "Bearer #{expected}"
|
|
10
|
+
},
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
endpoints = Prescient::API::ROUTES.keys.map { |method, path|
|
|
14
|
+
{ method: method, path: path }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
app = ->(env) {
|
|
18
|
+
if env['REQUEST_METHOD'] == 'GET' && env['PATH_INFO'] == '/'
|
|
19
|
+
payload = JSON.generate({ name: 'Prescient API', endpoints: endpoints })
|
|
20
|
+
[200, { 'content-type' => 'application/json', 'content-length' => payload.bytesize.to_s }, [payload]]
|
|
21
|
+
else
|
|
22
|
+
api.call(env)
|
|
23
|
+
end
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if respond_to?(:run, true)
|
|
27
|
+
run app
|
|
28
|
+
else
|
|
29
|
+
puts JSON.pretty_generate({ name: 'Prescient API', endpoints: endpoints })
|
|
30
|
+
end
|