prescient 0.5.0 → 0.7.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 +122 -11
- data/README.md +243 -24
- data/docker-compose.api.yml +21 -0
- data/docker-compose.yml +22 -0
- data/examples/README.md +51 -0
- data/examples/rest_api.ru +30 -0
- data/examples/web_search.rb +34 -0
- data/lib/prescient/api.rb +337 -0
- data/lib/prescient/cli.rb +136 -6
- data/lib/prescient/client.rb +26 -0
- data/lib/prescient/configuration_loader.rb +171 -0
- data/lib/prescient/errors.rb +12 -0
- data/lib/prescient/provider/openai.rb +0 -2
- data/lib/prescient/tool/search_api.rb +126 -0
- data/lib/prescient/tool/searxng.rb +124 -0
- data/lib/prescient/tool.rb +123 -0
- data/lib/prescient/version.rb +1 -1
- data/lib/prescient.rb +71 -1
- data/schema/prescient.configuration.schema.json +119 -0
- data/searxng/settings.yml +18 -0
- data/sig/prescient.rbs +62 -0
- metadata +16 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 176552789656cbda6e78951ab04eb0ecdfa9ffa53dcf88159bec5cdbf403cdef
|
|
4
|
+
data.tar.gz: 5173a6bdd607dc6aca121079ee6e81243a550575f4702c57133f637a31aac798
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb309c19aa67f200e806b227f392ff5181e5ef7bc1ea00a6f9a0d4cf5ebbce989c0d9b472885a1c2795efaa7789a8ed61f912453fd20594fba029f8c7bc126d4
|
|
7
|
+
data.tar.gz: 70187123148e3c0aad70f88fa469c91e3c55200c1c36b9452697109e2746cfe0fe70ef8358b766b3f99b967884aba4b846f3a8cb634c76fe9803ad45925d0e50
|
data/.dockerignore
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## [0.7.0] - 2025-08-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added an explicit external-tool contract with a configurable SearXNG web-search adapter.
|
|
10
|
+
- Added YAML/schema configuration for tool registration, environment references, bounded requests, and normalized search results.
|
|
11
|
+
- Added `prescient search` and an annotated `web_search` configuration example.
|
|
12
|
+
- Added an optional development SearXNG service to `docker-compose.yml` for runnable web-search examples.
|
|
13
|
+
- Added opt-in search-to-provider context assembly through `Prescient.search_and_generate` and `prescient search --generate`.
|
|
14
|
+
- Added `POST /v1/search/generate` for REST API consumers to opt in to search-result context.
|
|
15
|
+
- Added `POST /v1/search` for normalized raw external-tool results without AI generation.
|
|
16
|
+
- Added SearchApi as a hosted web-search adapter with engine, location, language,
|
|
17
|
+
country, API-key, timeout, and result-limit configuration.
|
|
18
|
+
- Added capability groups for ordered tool-adapter fallback on transient
|
|
19
|
+
connection and rate-limit failures.
|
|
20
|
+
- Documented SearXNG and SearchApi setup and CLI usage side by side.
|
|
21
|
+
- Added explicit `--generate` opt-in behavior to the web-search example.
|
|
22
|
+
- Added `SEARXNG_URL` environment defaults for automatic `web_search` registration.
|
|
23
|
+
- Expanded `prescient config example` with documented SearXNG settings,
|
|
24
|
+
environment references, custom tool names, and direct-versus-generated usage.
|
|
25
|
+
- Organized CLI help into global and search-specific option sections.
|
|
26
|
+
|
|
27
|
+
## [0.6.0] - 2025-08-15
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- Added a dependency-free Rack-compatible REST API with generation, embeddings,
|
|
32
|
+
bounded batch embeddings, provider/model discovery, capabilities, health,
|
|
33
|
+
liveness, readiness, version, request IDs, authentication hooks, and JSON
|
|
34
|
+
error envelopes.
|
|
35
|
+
- Added a small Rack example that lists the REST API endpoints and delegates
|
|
36
|
+
requests to `Prescient::API`.
|
|
37
|
+
- Added an optional `rack_example` bundle group with Rack, Rackup, and Puma for
|
|
38
|
+
running the example application without adding web-server dependencies to
|
|
39
|
+
the library.
|
|
40
|
+
- Added a non-root, healthchecked Docker image and Compose example for the REST
|
|
41
|
+
API, with optional GHCR publication on version tags.
|
|
42
|
+
- Documented mounting `Prescient::API` in Rails routes with its endpoint
|
|
43
|
+
catalog and authentication example.
|
|
44
|
+
- Made the CLI and REST API optional lazy-loaded entry points so library users
|
|
45
|
+
requiring only `prescient` do not load either interface eagerly.
|
|
46
|
+
|
|
5
47
|
## [0.5.0] - 2025-08-14
|
|
6
48
|
|
|
7
49
|
### Added
|
data/Dockerfile
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
FROM ruby:3.3-alpine AS builder
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
RUN apk add --no-cache build-base git
|
|
8
|
+
|
|
9
|
+
ENV BUNDLE_WITH=rack_example \
|
|
10
|
+
BUNDLE_WITHOUT=development:test \
|
|
11
|
+
BUNDLE_PATH=/usr/local/bundle
|
|
12
|
+
|
|
13
|
+
COPY Gemfile prescient.gemspec ./
|
|
14
|
+
COPY lib ./lib
|
|
15
|
+
|
|
16
|
+
RUN bundle install --jobs 4 --retry 3
|
|
17
|
+
|
|
18
|
+
FROM ruby:3.3-alpine
|
|
19
|
+
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
|
|
22
|
+
RUN apk add --no-cache curl tzdata && \
|
|
23
|
+
addgroup -S -g 1000 prescient && \
|
|
24
|
+
adduser -S -u 1000 -G prescient prescient
|
|
25
|
+
|
|
26
|
+
ENV BUNDLE_WITH=rack_example \
|
|
27
|
+
BUNDLE_WITHOUT=development:test \
|
|
28
|
+
BUNDLE_PATH=/usr/local/bundle \
|
|
29
|
+
GEM_HOME=/usr/local/bundle/ruby/3.3.0 \
|
|
30
|
+
GEM_PATH=/usr/local/bundle/ruby/3.3.0 \
|
|
31
|
+
PATH=/usr/local/bundle/ruby/3.3.0/bin:/usr/local/bundle/bin:$PATH \
|
|
32
|
+
RACK_ENV=production
|
|
33
|
+
|
|
34
|
+
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
|
35
|
+
COPY --chown=prescient:prescient lib ./lib
|
|
36
|
+
COPY --chown=prescient:prescient examples/rest_api.ru ./examples/rest_api.ru
|
|
37
|
+
|
|
38
|
+
USER prescient
|
|
39
|
+
|
|
40
|
+
EXPOSE 9292
|
|
41
|
+
|
|
42
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
43
|
+
CMD curl --fail --silent http://127.0.0.1:9292/healthz || exit 1
|
|
44
|
+
|
|
45
|
+
CMD ["rackup", "-s", "puma", "-o", "0.0.0.0", "-p", "9292", "/app/examples/rest_api.ru"]
|
data/INTEGRATION_GUIDE.md
CHANGED
|
@@ -11,9 +11,7 @@ and [examples guide](examples/README.md).
|
|
|
11
11
|
|
|
12
12
|
```ruby
|
|
13
13
|
# Add to your Gemfile
|
|
14
|
-
gem 'prescient',
|
|
15
|
-
# OR when published:
|
|
16
|
-
# gem 'prescient', '~> 0.5.0'
|
|
14
|
+
gem 'prescient', '~> 0.7.0'
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
### 2. Replace Existing AI Service
|
|
@@ -111,6 +109,40 @@ Prescient.configure do |config|
|
|
|
111
109
|
chat_model: ENV.fetch('HUGGINGFACE_CHAT_MODEL', 'google/gemma-2-2b-it')
|
|
112
110
|
)
|
|
113
111
|
end
|
|
112
|
+
|
|
113
|
+
# Google Gemini
|
|
114
|
+
if ENV['GEMINI_API_KEY'].present?
|
|
115
|
+
config.add_provider(:gemini, Prescient::Provider::Gemini,
|
|
116
|
+
api_key: ENV['GEMINI_API_KEY'],
|
|
117
|
+
embedding_model: ENV.fetch('GEMINI_EMBEDDING_MODEL', 'gemini-embedding-001'),
|
|
118
|
+
chat_model: ENV.fetch('GEMINI_CHAT_MODEL', 'gemini-2.5-flash')
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Mistral
|
|
123
|
+
if ENV['MISTRAL_API_KEY'].present?
|
|
124
|
+
config.add_provider(:mistral, Prescient::Provider::Mistral,
|
|
125
|
+
api_key: ENV['MISTRAL_API_KEY'],
|
|
126
|
+
embedding_model: ENV.fetch('MISTRAL_EMBEDDING_MODEL', 'mistral-embed'),
|
|
127
|
+
chat_model: ENV.fetch('MISTRAL_CHAT_MODEL', 'mistral-large-latest')
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# DeepSeek supports generation, but not embeddings.
|
|
132
|
+
if ENV['DEEPSEEK_API_KEY'].present?
|
|
133
|
+
config.add_provider(:deepseek, Prescient::Provider::DeepSeek,
|
|
134
|
+
api_key: ENV['DEEPSEEK_API_KEY'],
|
|
135
|
+
chat_model: ENV.fetch('DEEPSEEK_CHAT_MODEL', 'deepseek-v4-flash')
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# xAI supports generation, but not embeddings.
|
|
140
|
+
if ENV['XAI_API_KEY'].present?
|
|
141
|
+
config.add_provider(:xai, Prescient::Provider::XAI,
|
|
142
|
+
api_key: ENV['XAI_API_KEY'],
|
|
143
|
+
chat_model: ENV.fetch('XAI_CHAT_MODEL', 'grok-4.5')
|
|
144
|
+
)
|
|
145
|
+
end
|
|
114
146
|
end
|
|
115
147
|
|
|
116
148
|
# Set default provider for Rails
|
|
@@ -128,7 +160,68 @@ Configuration precedence is CLI overrides, environment defaults and
|
|
|
128
160
|
references, YAML values, then built-in defaults. The generated
|
|
129
161
|
`prescient config example` file includes the current JSON Schema URL.
|
|
130
162
|
|
|
131
|
-
### 4.
|
|
163
|
+
### 4. Mount the REST API
|
|
164
|
+
|
|
165
|
+
`Prescient::API` is Rack-compatible and can be mounted directly in a Rails
|
|
166
|
+
route set. The API keeps provider execution on `Prescient::Client` and exposes
|
|
167
|
+
only generic operations:
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# config/routes.rb
|
|
171
|
+
prescient_api = Prescient::API.new(
|
|
172
|
+
authentication: lambda { |env|
|
|
173
|
+
expected_token = ENV.fetch('PRESCIENT_API_TOKEN', nil)
|
|
174
|
+
expected_token && env['HTTP_AUTHORIZATION'] == "Bearer #{expected_token}"
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
mount prescient_api => '/prescient', as: :prescient_api
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
This makes the following routes available under `/prescient`:
|
|
182
|
+
|
|
183
|
+
| Method | Path | Purpose |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `GET` | `/healthz` | Liveness check |
|
|
186
|
+
| `GET` | `/readyz` | Readiness check |
|
|
187
|
+
| `GET` | `/v1/version` | Library and API versions |
|
|
188
|
+
| `GET` | `/v1/providers` | Configured providers |
|
|
189
|
+
| `GET` | `/v1/models` | Available models, optionally filtered by provider |
|
|
190
|
+
| `GET` | `/v1/capabilities` | Provider capabilities |
|
|
191
|
+
| `GET` | `/v1/health` | Provider health |
|
|
192
|
+
| `POST` | `/v1/generate` | Text generation |
|
|
193
|
+
| `POST` | `/v1/search` | Normalized external-tool search |
|
|
194
|
+
| `POST` | `/v1/search/generate` | Search with opt-in AI generation |
|
|
195
|
+
| `POST` | `/v1/embeddings` | Single embedding |
|
|
196
|
+
| `POST` | `/v1/embeddings/batch` | Bounded batch embeddings |
|
|
197
|
+
|
|
198
|
+
For example:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
curl http://localhost:3000/prescient/healthz
|
|
202
|
+
|
|
203
|
+
curl -X POST http://localhost:3000/prescient/v1/generate \
|
|
204
|
+
-H "Authorization: Bearer ${PRESCIENT_API_TOKEN}" \
|
|
205
|
+
-H 'Content-Type: application/json' \
|
|
206
|
+
-d '{"prompt":"Explain Ruby fibers"}'
|
|
207
|
+
|
|
208
|
+
curl -X POST http://localhost:3000/prescient/v1/search/generate \
|
|
209
|
+
-H "Authorization: Bearer ${PRESCIENT_API_TOKEN}" \
|
|
210
|
+
-H 'Content-Type: application/json' \
|
|
211
|
+
-d '{"query":"Ruby HTTP clients","provider":"openai","limit":5}'
|
|
212
|
+
|
|
213
|
+
curl -X POST http://localhost:3000/prescient/v1/search \
|
|
214
|
+
-H "Authorization: Bearer ${PRESCIENT_API_TOKEN}" \
|
|
215
|
+
-H 'Content-Type: application/json' \
|
|
216
|
+
-d '{"query":"Ruby HTTP clients","limit":5}'
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Responses include a request ID. Request bodies are size-limited, batch inputs
|
|
220
|
+
are bounded, and JSON errors do not expose raw provider response bodies. Keep
|
|
221
|
+
the API behind the application’s normal TLS, authentication, rate-limiting,
|
|
222
|
+
and observability controls.
|
|
223
|
+
|
|
224
|
+
### 5. Update Environment Variables
|
|
132
225
|
|
|
133
226
|
```bash
|
|
134
227
|
# .env or environment configuration
|
|
@@ -151,9 +244,27 @@ ANTHROPIC_MODEL=claude-sonnet-4-20250514
|
|
|
151
244
|
HUGGINGFACE_API_KEY=your_huggingface_api_key
|
|
152
245
|
HUGGINGFACE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
153
246
|
HUGGINGFACE_CHAT_MODEL=google/gemma-2-2b-it
|
|
247
|
+
|
|
248
|
+
# Google Gemini
|
|
249
|
+
GEMINI_API_KEY=your_gemini_api_key
|
|
250
|
+
GEMINI_EMBEDDING_MODEL=gemini-embedding-001
|
|
251
|
+
GEMINI_CHAT_MODEL=gemini-2.5-flash
|
|
252
|
+
|
|
253
|
+
# Mistral
|
|
254
|
+
MISTRAL_API_KEY=your_mistral_api_key
|
|
255
|
+
MISTRAL_EMBEDDING_MODEL=mistral-embed
|
|
256
|
+
MISTRAL_CHAT_MODEL=mistral-large-latest
|
|
257
|
+
|
|
258
|
+
# DeepSeek (generation only)
|
|
259
|
+
DEEPSEEK_API_KEY=your_deepseek_api_key
|
|
260
|
+
DEEPSEEK_CHAT_MODEL=deepseek-v4-flash
|
|
261
|
+
|
|
262
|
+
# xAI (generation only)
|
|
263
|
+
XAI_API_KEY=your_xai_api_key
|
|
264
|
+
XAI_CHAT_MODEL=grok-4.5
|
|
154
265
|
```
|
|
155
266
|
|
|
156
|
-
###
|
|
267
|
+
### 6. Update Controllers
|
|
157
268
|
|
|
158
269
|
**Before:**
|
|
159
270
|
|
|
@@ -199,7 +310,7 @@ class Api::V1::AiQueriesController < ApplicationController
|
|
|
199
310
|
end
|
|
200
311
|
```
|
|
201
312
|
|
|
202
|
-
###
|
|
313
|
+
### 7. Health Check Integration
|
|
203
314
|
|
|
204
315
|
```ruby
|
|
205
316
|
# app/controllers/api/v1/system/health_controller.rb
|
|
@@ -232,7 +343,7 @@ class Api::V1::System::HealthController < ApplicationController
|
|
|
232
343
|
}
|
|
233
344
|
|
|
234
345
|
# Check backup providers
|
|
235
|
-
backup_providers = [
|
|
346
|
+
backup_providers = %i[openai anthropic huggingface gemini mistral deepseek xai] - [primary_provider]
|
|
236
347
|
providers[:backups] = backup_providers.map do |provider|
|
|
237
348
|
{
|
|
238
349
|
name: provider,
|
|
@@ -252,7 +363,7 @@ class Api::V1::System::HealthController < ApplicationController
|
|
|
252
363
|
end
|
|
253
364
|
```
|
|
254
365
|
|
|
255
|
-
###
|
|
366
|
+
### 8. Migration Strategy
|
|
256
367
|
|
|
257
368
|
1. **Phase 1: Side-by-side deployment**
|
|
258
369
|
|
|
@@ -271,7 +382,7 @@ end
|
|
|
271
382
|
- Update all controllers to use AIService
|
|
272
383
|
- Clean up unused code
|
|
273
384
|
|
|
274
|
-
###
|
|
385
|
+
### 9. Testing Updates
|
|
275
386
|
|
|
276
387
|
```ruby
|
|
277
388
|
# test/services/ai_service_test.rb
|
|
@@ -296,7 +407,7 @@ class AIServiceTest < ActiveSupport::TestCase
|
|
|
296
407
|
end
|
|
297
408
|
```
|
|
298
409
|
|
|
299
|
-
###
|
|
410
|
+
### 10. Monitoring and Logging
|
|
300
411
|
|
|
301
412
|
```ruby
|
|
302
413
|
# config/initializers/prescient_monitoring.rb
|
|
@@ -320,7 +431,7 @@ end
|
|
|
320
431
|
PrescientMonitoring.setup! if Rails.env.production?
|
|
321
432
|
```
|
|
322
433
|
|
|
323
|
-
###
|
|
434
|
+
### 11. Performance Optimization
|
|
324
435
|
|
|
325
436
|
```ruby
|
|
326
437
|
# app/services/ai_service.rb (enhanced)
|
data/README.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/prescient)
|
|
4
4
|
[](https://www.ruby-lang.org/)
|
|
5
|
+
[](https://github.com/kanutocd/prescient/pkgs/container/prescient)
|
|
5
6
|
[](https://github.com/kanutocd/prescient/actions/workflows/ci.yml)
|
|
6
7
|
[](https://github.com/kanutocd/prescient/actions/workflows/security.yml)
|
|
7
8
|
[](LICENSE.txt)
|
|
8
9
|
|
|
9
|
-
Prescient is a boring AI provider
|
|
10
|
+
Prescient is a boring AI provider gateway implemented in Ruby. Configure your AI providers once, then use them through a consistent Ruby API, CLI, or REST API. Prescient handles provider selection, retries, health checks, and fallback across configured providers, including OpenAI, Anthropic, Ollama, Hugging Face, Google Gemini, Mistral, DeepSeek, and xAI.
|
|
10
11
|
|
|
11
12
|
For focused guidance, see the **[examples guide](https://github.com/kanutocd/prescient/tree/main/examples)**,
|
|
12
13
|
**[Rails integration guide](https://github.com/kanutocd/prescient/blob/main/INTEGRATION_GUIDE.md)**, and
|
|
@@ -14,13 +15,15 @@ For focused guidance, see the **[examples guide](https://github.com/kanutocd/pre
|
|
|
14
15
|
|
|
15
16
|
## Features
|
|
16
17
|
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
18
|
+
- **Provider abstraction** — One consistent interface across supported AI providers
|
|
19
|
+
- **Multiple interfaces** — Ruby API, CLI, and Rack-compatible REST API
|
|
20
|
+
- **Text and embeddings** — Generate responses and embeddings with provider/model selection
|
|
21
|
+
- **Reliability controls** — Retries, health checks, and fallback across configured providers
|
|
22
|
+
- **Declarative configuration** — Versioned YAML, environment references, and JSON Schema validation
|
|
23
|
+
- **Prompt and context customization** — Configurable prompt templates and context formatting
|
|
24
|
+
- **External tools** — Explicit, normalized web-search integration with SearXNG and SearchApi
|
|
25
|
+
- **Local and cloud support** — Ollama alongside hosted providers
|
|
26
|
+
- **Optional integrations** — Docker deployment and pgvector support without making either mandatory
|
|
24
27
|
|
|
25
28
|
## Supported Providers
|
|
26
29
|
|
|
@@ -223,6 +226,64 @@ The generated file points YAML language servers at the latest schema on the
|
|
|
223
226
|
main branch. Pin the schema URL to a release tag when reproducible tooling is
|
|
224
227
|
required.
|
|
225
228
|
|
|
229
|
+
## REST API
|
|
230
|
+
|
|
231
|
+
`Prescient::API` is a dependency-free Rack-compatible application. Mount it in
|
|
232
|
+
the web server of your choice without making HTTP a requirement for library
|
|
233
|
+
users. The API and CLI files are loaded lazily; `Prescient::API` autoloads on
|
|
234
|
+
first reference, or you can require it explicitly:
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
require 'prescient'
|
|
238
|
+
require 'prescient/api'
|
|
239
|
+
|
|
240
|
+
run Prescient::API.new(
|
|
241
|
+
authentication: ->(env) { env['HTTP_AUTHORIZATION'] == "Bearer #{ENV['PRESCIENT_API_TOKEN']}" }
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Available endpoints include:
|
|
246
|
+
|
|
247
|
+
- **`POST /v1/generate`**
|
|
248
|
+
- **`POST /v1/search`**
|
|
249
|
+
- **`POST /v1/search/generate`**
|
|
250
|
+
- **`POST /v1/embeddings`**
|
|
251
|
+
- **`POST /v1/embeddings/batch`**
|
|
252
|
+
- **`GET /v1/providers`**
|
|
253
|
+
- **`GET /v1/models`**
|
|
254
|
+
- **`GET /v1/capabilities`**
|
|
255
|
+
- **`GET /v1/health`**
|
|
256
|
+
- **`GET /v1/version`**
|
|
257
|
+
- **`GET /healthz`**
|
|
258
|
+
- **`GET /readyz`**
|
|
259
|
+
|
|
260
|
+
Responses include:
|
|
261
|
+
|
|
262
|
+
- a request ID
|
|
263
|
+
- a generic JSON error envelope that never exposes raw provider response bodies.
|
|
264
|
+
|
|
265
|
+
## Docker
|
|
266
|
+
|
|
267
|
+
Build and run the REST API image as a non-root container:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
docker build -t prescient:local .
|
|
271
|
+
docker run --rm -p 9292:9292 \
|
|
272
|
+
-e PRESCIENT_API_TOKEN=change-me \
|
|
273
|
+
prescient:local
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The image exposes port `9292`, includes a `/healthz` healthcheck, supports a
|
|
277
|
+
read-only filesystem, and does not bundle PostgreSQL, Redis, or a worker. The
|
|
278
|
+
Compose example provides the same setup:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
PRESCIENT_API_TOKEN=change-me docker compose -f docker-compose.api.yml up --build
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Tagged releases publish to GHCR as:
|
|
285
|
+
`ghcr.io/kanutocd/prescient:<version>`.
|
|
286
|
+
|
|
226
287
|
## Configuration
|
|
227
288
|
|
|
228
289
|
### Environment Variables
|
|
@@ -291,25 +352,139 @@ YAML values, then built-in defaults. Use `prescient config validate` to check a
|
|
|
291
352
|
configuration before running an operation, or `prescient config example` to
|
|
292
353
|
generate an annotated starter file.
|
|
293
354
|
|
|
294
|
-
|
|
295
|
-
|
|
355
|
+
### External Tools
|
|
356
|
+
|
|
357
|
+
External tools are opt-in capability adapters, separate from AI providers.
|
|
358
|
+
Supported web-search adapters are [SearXNG](https://searxng.org/) and
|
|
359
|
+
[SearchApi](https://www.searchapi.io/):
|
|
360
|
+
|
|
361
|
+
#### SearXNG
|
|
362
|
+
|
|
363
|
+
Setting `SEARXNG_URL` registers the default `web_search` tool for CLI and Ruby
|
|
364
|
+
environment-based configuration. YAML or programmatic configuration can be
|
|
365
|
+
used when more control is needed.
|
|
296
366
|
|
|
297
367
|
```yaml
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
type:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
with_context_template: "%{system_prompt}\n\nContext:\n%{context}\n\nUser: %{query}"
|
|
368
|
+
tools:
|
|
369
|
+
web_search:
|
|
370
|
+
type: searxng
|
|
371
|
+
url_env: SEARXNG_URL
|
|
372
|
+
language: en
|
|
373
|
+
categories: [general, news]
|
|
374
|
+
timeout: 5
|
|
375
|
+
max_results: 5
|
|
307
376
|
```
|
|
308
377
|
|
|
378
|
+
Run the SearXNG-backed tool:
|
|
379
|
+
|
|
309
380
|
```bash
|
|
310
|
-
|
|
381
|
+
docker compose up -d searxng
|
|
382
|
+
SEARXNG_URL=http://localhost:8080 bundle exec prescient search \
|
|
383
|
+
--format json "Ruby HTTP clients"
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### SearchApi
|
|
387
|
+
|
|
388
|
+
[SearchApi](https://www.searchapi.io/) uses a hosted Google search engine and
|
|
389
|
+
requires an API key:
|
|
390
|
+
|
|
391
|
+
```yaml
|
|
392
|
+
tools:
|
|
393
|
+
web_search:
|
|
394
|
+
type: searchapi
|
|
395
|
+
api_key_env: SEARCHAPI_API_KEY
|
|
396
|
+
engine: google
|
|
397
|
+
location: New York
|
|
398
|
+
hl: en
|
|
399
|
+
gl: us
|
|
400
|
+
timeout: 10
|
|
401
|
+
max_results: 5
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
The `engine` value can select another SearchApi web or product engine when its
|
|
405
|
+
response uses `organic_results`, such as `bing`, `yahoo`, `yandex`,
|
|
406
|
+
`amazon_search`, or `walmart_search`.
|
|
407
|
+
|
|
408
|
+
Adapters can be grouped under one logical capability for ordered fallback:
|
|
409
|
+
|
|
410
|
+
```yaml
|
|
411
|
+
tools:
|
|
412
|
+
web_search:
|
|
413
|
+
adapters:
|
|
414
|
+
- type: searxng
|
|
415
|
+
url_env: SEARXNG_URL
|
|
416
|
+
- type: searchapi
|
|
417
|
+
api_key_env: SEARCHAPI_API_KEY
|
|
418
|
+
engine: google
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Adapters are tried in order. Fallback is limited to transient connection and
|
|
422
|
+
rate-limit failures; invalid configuration, authentication failures, and
|
|
423
|
+
malformed responses are not retried with another adapter.
|
|
424
|
+
|
|
425
|
+
Run the SearchApi-backed tool:
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
SEARCHAPI_API_KEY=your-key bundle exec prescient search \
|
|
429
|
+
--config prescient.yml --format json "Ruby HTTP clients"
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Invoke a configured tool explicitly from Ruby or the CLI:
|
|
433
|
+
|
|
434
|
+
```ruby
|
|
435
|
+
result = Prescient.tool(:web_search).search('Ruby HTTP clients', limit: 3)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
bundle exec prescient search --config prescient.yml \
|
|
440
|
+
--tool web_search --format json "Ruby HTTP clients"
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Results use a normalized envelope containing `tool`, `query`, `source`, and
|
|
444
|
+
`results` entries with `title`, `url`, `snippet`, and `source`. Requests have
|
|
445
|
+
bounded query length, timeout, result count, and response size. Tool execution
|
|
446
|
+
is explicit; Prescient does not autonomously invoke tools, and tool endpoints
|
|
447
|
+
are not exposed as raw tool endpoints through `Prescient::API`; the API exposes
|
|
448
|
+
the explicit combined search-and-generation operation below. Other adapters can
|
|
449
|
+
implement the same contract without changing provider integrations.
|
|
450
|
+
|
|
451
|
+
Search results are not sent to an AI provider by default. Opt in when you want
|
|
452
|
+
the normalized results assembled as generation context:
|
|
453
|
+
|
|
454
|
+
```ruby
|
|
455
|
+
response = Prescient.search_and_generate(
|
|
456
|
+
'Ruby HTTP clients',
|
|
457
|
+
tool: :web_search,
|
|
458
|
+
provider: :openai,
|
|
459
|
+
)
|
|
311
460
|
```
|
|
312
461
|
|
|
462
|
+
The CLI exposes the same opt-in behavior with `--generate`:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
prescient search --generate --provider openai "Ruby HTTP clients"
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
The REST API exposes the same opt-in behavior:
|
|
469
|
+
|
|
470
|
+
Raw normalized search results are available without generation:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
curl -X POST http://localhost:9292/v1/search \
|
|
474
|
+
-H 'Content-Type: application/json' \
|
|
475
|
+
-d '{"query":"Ruby HTTP clients","limit":5}'
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
curl -X POST http://localhost:9292/v1/search/generate \
|
|
480
|
+
-H 'Content-Type: application/json' \
|
|
481
|
+
-d '{"query":"Ruby HTTP clients","provider":"openai","limit":5}'
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Use `fallback: false` to disable provider fallback for the request. The
|
|
485
|
+
response is the normalized AI provider response; omit this endpoint and use
|
|
486
|
+
`POST /v1/generate` when search context is not wanted.
|
|
487
|
+
|
|
313
488
|
### Programmatic Configuration
|
|
314
489
|
|
|
315
490
|
```ruby
|
|
@@ -546,6 +721,24 @@ client = Prescient.client(:customer_service)
|
|
|
546
721
|
response = client.generate_response("What's your return policy?")
|
|
547
722
|
```
|
|
548
723
|
|
|
724
|
+
Templates can also be configured in YAML or overridden for one CLI operation:
|
|
725
|
+
|
|
726
|
+
```yaml
|
|
727
|
+
providers:
|
|
728
|
+
openai:
|
|
729
|
+
type: openai
|
|
730
|
+
api_key_env: OPENAI_API_KEY
|
|
731
|
+
chat_model: gpt-4.1-mini
|
|
732
|
+
prompt_templates:
|
|
733
|
+
system_prompt: You are a concise assistant.
|
|
734
|
+
no_context_template: "%{system_prompt}\n\nUser: %{query}"
|
|
735
|
+
with_context_template: "%{system_prompt}\n\nContext:\n%{context}\n\nUser: %{query}"
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
prescient generate --prompt-templates-file prompts.yml "Summarize this"
|
|
740
|
+
```
|
|
741
|
+
|
|
549
742
|
### Template Placeholders
|
|
550
743
|
|
|
551
744
|
- `%{system_prompt}` - The system/role instruction
|
|
@@ -978,6 +1171,30 @@ puts info[:options] # => { ... } (excluding sensitive data)
|
|
|
978
1171
|
- Research-friendly
|
|
979
1172
|
- Free tier available
|
|
980
1173
|
|
|
1174
|
+
### Google Gemini
|
|
1175
|
+
|
|
1176
|
+
- Text generation and embeddings
|
|
1177
|
+
- Google AI API integration
|
|
1178
|
+
- Model discovery through the Gemini models endpoint
|
|
1179
|
+
|
|
1180
|
+
### Mistral
|
|
1181
|
+
|
|
1182
|
+
- Text generation and embeddings
|
|
1183
|
+
- OpenAI-compatible API style
|
|
1184
|
+
- Model discovery through the Mistral models endpoint
|
|
1185
|
+
|
|
1186
|
+
### DeepSeek
|
|
1187
|
+
|
|
1188
|
+
- Text generation
|
|
1189
|
+
- OpenAI-compatible API style
|
|
1190
|
+
- No embedding support
|
|
1191
|
+
|
|
1192
|
+
### xAI
|
|
1193
|
+
|
|
1194
|
+
- Text generation
|
|
1195
|
+
- OpenAI-compatible API style
|
|
1196
|
+
- No embedding support
|
|
1197
|
+
|
|
981
1198
|
## Docker Setup (Recommended for Ollama)
|
|
982
1199
|
|
|
983
1200
|
The easiest way to get started with Prescient and Ollama is using Docker Compose:
|
|
@@ -1052,6 +1269,8 @@ The included `docker-compose.yml` provides:
|
|
|
1052
1269
|
|
|
1053
1270
|
- **ollama**: Ollama AI service with persistent model storage
|
|
1054
1271
|
- **ollama-init**: Automatically pulls required models on startup
|
|
1272
|
+
- **searxng**: Optional SearXNG web-search service with JSON output enabled
|
|
1273
|
+
- **postgres**: Optional PostgreSQL database with pgvector support
|
|
1055
1274
|
- **redis**: Optional caching layer for embeddings
|
|
1056
1275
|
- **prescient-app**: Example Ruby application container
|
|
1057
1276
|
|
|
@@ -1210,10 +1429,10 @@ OPENAI_API_KEY=... \
|
|
|
1210
1429
|
bundle exec ruby -Itest test/prescient/live_provider_smoke_test.rb
|
|
1211
1430
|
```
|
|
1212
1431
|
|
|
1213
|
-
Supported provider names are `ollama`, `anthropic`, `openai`,
|
|
1214
|
-
`
|
|
1215
|
-
overrides are honored. These tests are never
|
|
1216
|
-
variables are set.
|
|
1432
|
+
Supported provider names are `ollama`, `anthropic`, `openai`, `huggingface`,
|
|
1433
|
+
`gemini`, `mistral`, `deepseek`, and `xai`. The corresponding provider
|
|
1434
|
+
environment variables and model overrides are honored. These tests are never
|
|
1435
|
+
live unless both opt-in variables are set.
|
|
1217
1436
|
|
|
1218
1437
|
### RBS and Steep
|
|
1219
1438
|
|
|
@@ -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
|