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.
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Prescient
2
2
 
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.
3
+ [![Gem Version](https://img.shields.io/gem/v/prescient?logo=rubygems&logoColor=white)](https://rubygems.org/gems/prescient)
4
+ [![Requires Ruby 3.1+](https://img.shields.io/badge/Requires-Ruby%203.1%2B-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org/)
5
+ [![CI](https://github.com/kanutocd/prescient/actions/workflows/ci.yml/badge.svg)](https://github.com/kanutocd/prescient/actions/workflows/ci.yml)
6
+ [![Security](https://img.shields.io/github/actions/workflow/status/kanutocd/prescient/security.yml?branch=main&event=push&label=Security)](https://github.com/kanutocd/prescient/actions/workflows/security.yml)
7
+ [![License](https://img.shields.io/badge/License-MIT-22C55E)](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**: Environment variable and programmatic 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
- Example JSON output:
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
- %{ system_prompt }
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
- %{ system_prompt } Use the company info below to help answer.
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 existing application-schema example below
538
- remains available for projects that need documents, chunks, and custom metadata.
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 setup creates these key tables:
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
- - Document embedding generation and storage
740
- - Similarity search with different distance functions
741
- - Metadata filtering and advanced queries
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`, and
1049
- `huggingface`. The corresponding provider environment variables and model
1050
- overrides are honored. These tests are never live unless both opt-in
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
@@ -32,7 +32,7 @@ namespace :yard do
32
32
  abort("Unable to determine YARD coverage") unless match
33
33
 
34
34
  coverage = match[1].to_f
35
- minimum = 99.0
35
+ minimum = 100.0
36
36
  if coverage < minimum
37
37
  message = format(
38
38
  "YARD coverage %<coverage>.2f%% is below %<minimum>.2f%%",
@@ -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
- ### Database Schema
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 setup automatically creates HNSW indexes for optimal performance:
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 and similarity search.
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:
@@ -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 = [:ollama, :anthropic, :openai, :huggingface]
70
+ providers = %i[ollama anthropic openai huggingface gemini mistral deepseek xai]
71
71
 
72
72
  providers.each do |provider_name|
73
73
  begin
@@ -228,8 +228,8 @@ rescue Prescient::Error => e
228
228
  puts "❌ Error: #{e.message}"
229
229
  end
230
230
 
231
- # Example 4: Embedding Text Extraction
232
- puts "\n--- Example 4: Embedding Text Extraction ---"
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
- # Test embedding text extraction
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
- # The extract_embedding_text method will only use title, content, and tags
266
- # This demonstrates how embedding generation can focus on specific fields
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(Notice how only title, content, and tags are included - not author, category, or date)"
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