prescient 0.7.0 → 0.8.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/.rubocop.yml +21 -268
- data/CHANGELOG.md +37 -0
- data/INTEGRATION_GUIDE.md +7 -1
- data/README.md +210 -1
- data/Steepfile +12 -12
- data/db/migrate/001_create_prescient_tables.rb +15 -16
- data/examples/README.md +2 -1
- data/examples/custom_contexts.rb +4 -4
- data/exe/prescient +2 -2
- data/exe/prescient-mcp +7 -0
- data/lib/prescient/agent/audit_log.rb +37 -0
- data/lib/prescient/agent/cli_adapter.rb +29 -0
- data/lib/prescient/agent/configuration.rb +57 -0
- data/lib/prescient/agent/context.rb +56 -0
- data/lib/prescient/agent/error_serializer.rb +47 -0
- data/lib/prescient/agent/errors.rb +25 -0
- data/lib/prescient/agent/parser.rb +49 -0
- data/lib/prescient/agent/prompt_builder.rb +31 -0
- data/lib/prescient/agent/result.rb +36 -0
- data/lib/prescient/agent/runtime.rb +175 -0
- data/lib/prescient/agent/schema_validator.rb +215 -0
- data/lib/prescient/agent/tool_registry.rb +89 -0
- data/lib/prescient/agent.rb +22 -0
- data/lib/prescient/api.rb +337 -274
- data/lib/prescient/base.rb +370 -372
- data/lib/prescient/cli.rb +586 -526
- data/lib/prescient/client.rb +7 -6
- data/lib/prescient/configuration_loader.rb +492 -488
- data/lib/prescient/document_source.rb +114 -0
- data/lib/prescient/errors.rb +1 -3
- data/lib/prescient/mcp/authentication.rb +39 -0
- data/lib/prescient/mcp/configuration.rb +38 -0
- data/lib/prescient/mcp/rack.rb +243 -0
- data/lib/prescient/mcp/server.rb +202 -0
- data/lib/prescient/mcp/stdio.rb +42 -0
- data/lib/prescient/mcp.rb +8 -0
- data/lib/prescient/pgvector.rb +193 -189
- data/lib/prescient/provider/anthropic.rb +129 -125
- data/lib/prescient/provider/deepseek.rb +122 -118
- data/lib/prescient/provider/gemini.rb +153 -149
- data/lib/prescient/provider/huggingface.rb +191 -187
- data/lib/prescient/provider/mistral.rb +151 -147
- data/lib/prescient/provider/ollama.rb +168 -165
- data/lib/prescient/provider/openai.rb +174 -169
- data/lib/prescient/provider/xai.rb +122 -118
- data/lib/prescient/tool/search_api.rb +125 -121
- data/lib/prescient/tool/searxng.rb +123 -119
- data/lib/prescient/tool.rb +100 -98
- data/lib/prescient/version.rb +1 -1
- data/lib/prescient.rb +68 -62
- data/sig/prescient.rbs +176 -1
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e272a00a9f7baaee44c4ad7b9827da6c1e5b930a98228e8d58718474d3090214
|
|
4
|
+
data.tar.gz: a7ac75b948c326d0829db56e0ab297561d178fa4643a6a4bbd5b4dc11cff150b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e4ff77f5dd524e7bc3217b4f8fc1b8045f43d188b751f81dc2964dcf2d15dbfd3ed58efe8598ba40bd875e19a3597de1308e32c066466713b06af8e79fea55a
|
|
7
|
+
data.tar.gz: 3f5f8e5f78bc8db0566102f686ce1b66e752f9f81faba9dae09adfa49883aaa67c621b108b9a60cb573d695cae4874567e2ebf1c69b90a3971a6686f0316c288
|
data/.rubocop.yml
CHANGED
|
@@ -25,7 +25,8 @@ AllCops:
|
|
|
25
25
|
- 'Rakefile'
|
|
26
26
|
- 'examples/**/*'
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Metrics/ParameterLists:
|
|
29
|
+
CountKeywordArgs: false
|
|
29
30
|
|
|
30
31
|
Layout/LineLength:
|
|
31
32
|
Max: 120
|
|
@@ -38,291 +39,43 @@ Layout/LineLength:
|
|
|
38
39
|
- '^\s*context\s' # Test contexts
|
|
39
40
|
- '^\s*describe\s' # Test descriptions
|
|
40
41
|
|
|
41
|
-
Layout/MultilineMethodCallIndentation:
|
|
42
|
-
EnforcedStyle: indented
|
|
43
|
-
|
|
44
|
-
Layout/FirstHashElementIndentation:
|
|
45
|
-
EnforcedStyle: consistent
|
|
46
|
-
|
|
47
|
-
Layout/FirstArrayElementIndentation:
|
|
48
|
-
EnforcedStyle: consistent
|
|
49
|
-
|
|
50
|
-
Layout/HashAlignment:
|
|
51
|
-
EnforcedHashRocketStyle: table
|
|
52
|
-
EnforcedColonStyle: table
|
|
53
|
-
|
|
54
|
-
Layout/SpaceInsideHashLiteralBraces:
|
|
55
|
-
EnforcedStyle: space
|
|
56
|
-
|
|
57
|
-
Layout/EmptyLinesAroundModuleBody:
|
|
58
|
-
Enabled: false
|
|
59
|
-
|
|
60
|
-
Layout/EmptyLinesAroundClassBody:
|
|
61
|
-
Enabled: false
|
|
62
|
-
|
|
63
|
-
# === STYLE ===
|
|
64
|
-
|
|
65
42
|
Style/StringLiterals:
|
|
66
|
-
EnforcedStyle:
|
|
43
|
+
EnforcedStyle: double_quotes
|
|
67
44
|
|
|
68
45
|
Style/StringLiteralsInInterpolation:
|
|
69
|
-
EnforcedStyle:
|
|
70
|
-
|
|
71
|
-
Style/FrozenStringLiteralComment:
|
|
72
|
-
Enabled: true
|
|
73
|
-
EnforcedStyle: always
|
|
74
|
-
Exclude:
|
|
75
|
-
- 'examples/**/*'
|
|
76
|
-
|
|
77
|
-
Style/Documentation:
|
|
78
|
-
Enabled: false
|
|
79
|
-
|
|
80
|
-
Style/ClassAndModuleChildren:
|
|
81
|
-
EnforcedStyle: compact
|
|
82
|
-
|
|
83
|
-
Style/TrailingCommaInArguments:
|
|
84
|
-
EnforcedStyleForMultiline: comma
|
|
85
|
-
|
|
86
|
-
Style/TrailingCommaInArrayLiteral:
|
|
87
|
-
EnforcedStyleForMultiline: comma
|
|
88
|
-
|
|
89
|
-
Style/TrailingCommaInHashLiteral:
|
|
90
|
-
EnforcedStyleForMultiline: comma
|
|
91
|
-
|
|
92
|
-
Style/HashSyntax:
|
|
93
|
-
EnforcedStyle: ruby19_no_mixed_keys
|
|
94
|
-
|
|
95
|
-
Style/Lambda:
|
|
96
|
-
EnforcedStyle: literal
|
|
97
|
-
|
|
98
|
-
Style/NumericLiterals:
|
|
99
|
-
MinDigits: 6
|
|
100
|
-
|
|
101
|
-
Style/BlockDelimiters:
|
|
102
|
-
EnforcedStyle: semantic
|
|
103
|
-
AllowedMethods:
|
|
104
|
-
- expect
|
|
105
|
-
- let
|
|
106
|
-
- subject
|
|
107
|
-
|
|
108
|
-
Style/WordArray:
|
|
109
|
-
EnforcedStyle: brackets
|
|
110
|
-
|
|
111
|
-
Style/SymbolArray:
|
|
112
|
-
EnforcedStyle: brackets
|
|
113
|
-
|
|
114
|
-
Style/FormatString:
|
|
115
|
-
EnforcedStyle: percent
|
|
116
|
-
|
|
117
|
-
Style/CollectionMethods:
|
|
118
|
-
PreferredMethods:
|
|
119
|
-
collect: map
|
|
120
|
-
collect!: map!
|
|
121
|
-
detect: find
|
|
122
|
-
find_all: select
|
|
123
|
-
reduce: inject
|
|
124
|
-
|
|
125
|
-
Style/Alias:
|
|
126
|
-
EnforcedStyle: prefer_alias_method
|
|
127
|
-
|
|
128
|
-
Style/AccessorGrouping:
|
|
129
|
-
EnforcedStyle: separated
|
|
130
|
-
|
|
131
|
-
Style/BisectedAttrAccessor:
|
|
132
|
-
Enabled: true
|
|
133
|
-
|
|
134
|
-
Style/RedundantAssignment:
|
|
135
|
-
Enabled: true
|
|
136
|
-
|
|
137
|
-
Style/RedundantFetchBlock:
|
|
138
|
-
Enabled: true
|
|
139
|
-
|
|
140
|
-
Style/RedundantFileExtensionInRequire:
|
|
141
|
-
Enabled: true
|
|
142
|
-
|
|
143
|
-
Style/RedundantRegexpCharacterClass:
|
|
144
|
-
Enabled: true
|
|
145
|
-
|
|
146
|
-
Style/RedundantRegexpEscape:
|
|
147
|
-
Enabled: true
|
|
148
|
-
|
|
149
|
-
Style/SlicingWithRange:
|
|
150
|
-
Enabled: true
|
|
151
|
-
|
|
152
|
-
Style/ExponentialNotation:
|
|
153
|
-
Enabled: true
|
|
154
|
-
|
|
155
|
-
Style/HashTransformKeys:
|
|
156
|
-
Enabled: true
|
|
157
|
-
|
|
158
|
-
Style/HashTransformValues:
|
|
159
|
-
Enabled: true
|
|
160
|
-
|
|
161
|
-
Style/RedundantReturn:
|
|
162
|
-
AllowMultipleReturnValues: true
|
|
163
|
-
|
|
164
|
-
# === METRICS ===
|
|
165
|
-
|
|
166
|
-
Metrics/ModuleLength:
|
|
167
|
-
Max: 150
|
|
46
|
+
EnforcedStyle: double_quotes
|
|
168
47
|
|
|
169
48
|
Metrics/ClassLength:
|
|
170
|
-
Enabled:
|
|
171
|
-
Max:
|
|
49
|
+
Enabled: true
|
|
50
|
+
Max: 145
|
|
172
51
|
Exclude:
|
|
173
52
|
- 'test/**/*'
|
|
174
53
|
|
|
175
54
|
Metrics/MethodLength:
|
|
176
|
-
|
|
177
|
-
Max: 25
|
|
55
|
+
Max: 37
|
|
178
56
|
Exclude:
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
- 'lib/prescient/provider/*.rb'
|
|
182
|
-
|
|
183
|
-
Metrics/BlockLength:
|
|
184
|
-
Enabled: false
|
|
185
|
-
Max: 25
|
|
186
|
-
Exclude:
|
|
187
|
-
- 'test/**/*'
|
|
188
|
-
- 'lib/prescient.rb' # Configuration block
|
|
57
|
+
- "test/**/*"
|
|
58
|
+
- "db/migrate/**/*"
|
|
189
59
|
|
|
190
60
|
Metrics/AbcSize:
|
|
191
|
-
|
|
61
|
+
Enabled: true
|
|
62
|
+
Max: 55
|
|
192
63
|
Exclude:
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
- 'lib/prescient/provider/*.rb'
|
|
64
|
+
- "test/**/*"
|
|
65
|
+
- "db/migrate/**/*"
|
|
196
66
|
|
|
197
|
-
|
|
198
|
-
Max:
|
|
199
|
-
Exclude:
|
|
200
|
-
- 'lib/prescient/provider/*.rb'
|
|
67
|
+
Minitest/MultipleAssertions:
|
|
68
|
+
Max: 20
|
|
201
69
|
|
|
202
70
|
Metrics/PerceivedComplexity:
|
|
203
|
-
Enabled: false
|
|
204
|
-
Max: 8
|
|
205
|
-
Exclude:
|
|
206
|
-
- 'lib/prescient/provider/*.rb'
|
|
207
|
-
|
|
208
|
-
Metrics/ParameterLists:
|
|
209
|
-
Max: 5
|
|
210
|
-
CountKeywordArgs: false
|
|
211
|
-
|
|
212
|
-
# === LINT ===
|
|
213
|
-
|
|
214
|
-
Lint/RaiseException:
|
|
215
|
-
Enabled: true
|
|
216
|
-
|
|
217
|
-
Lint/StructNewOverride:
|
|
218
|
-
Enabled: true
|
|
219
|
-
|
|
220
|
-
Lint/DeprecatedOpenSSLConstant:
|
|
221
|
-
Enabled: true
|
|
222
|
-
|
|
223
|
-
Lint/MixedRegexpCaptureTypes:
|
|
224
|
-
Enabled: true
|
|
225
|
-
|
|
226
|
-
Lint/DuplicateElsifCondition:
|
|
227
|
-
Enabled: true
|
|
228
|
-
|
|
229
|
-
Lint/BinaryOperatorWithIdenticalOperands:
|
|
230
|
-
Enabled: true
|
|
231
|
-
|
|
232
|
-
Lint/DuplicateRescueException:
|
|
233
|
-
Enabled: true
|
|
234
|
-
|
|
235
|
-
Lint/EmptyConditionalBody:
|
|
236
|
-
Enabled: true
|
|
237
|
-
|
|
238
|
-
Lint/FloatComparison:
|
|
239
|
-
Enabled: true
|
|
240
|
-
|
|
241
|
-
Lint/MissingSuper:
|
|
242
|
-
Enabled: true
|
|
243
|
-
|
|
244
|
-
Lint/OutOfRangeRegexpRef:
|
|
245
|
-
Enabled: true
|
|
246
|
-
|
|
247
|
-
Lint/SelfAssignment:
|
|
248
|
-
Enabled: true
|
|
249
|
-
|
|
250
|
-
Lint/TopLevelReturnWithArgument:
|
|
251
|
-
Enabled: true
|
|
252
|
-
|
|
253
|
-
Lint/UnreachableLoop:
|
|
254
|
-
Enabled: true
|
|
255
|
-
|
|
256
|
-
# === NAMING ===
|
|
257
|
-
|
|
258
|
-
Naming/PredicatePrefix:
|
|
259
|
-
ForbiddenPrefixes:
|
|
260
|
-
- is_
|
|
261
|
-
AllowedMethods:
|
|
262
|
-
- is_a?
|
|
263
|
-
|
|
264
|
-
Naming/MemoizedInstanceVariableName:
|
|
265
|
-
EnforcedStyleForLeadingUnderscores: required
|
|
266
|
-
|
|
267
|
-
Naming/VariableNumber:
|
|
268
|
-
EnforcedStyle: snake_case
|
|
269
|
-
|
|
270
|
-
# === SECURITY ===
|
|
271
|
-
|
|
272
|
-
Security/Open:
|
|
273
|
-
Enabled: true
|
|
274
|
-
|
|
275
|
-
Security/YAMLLoad:
|
|
276
|
-
Enabled: true
|
|
277
|
-
|
|
278
|
-
Security/JSONLoad:
|
|
279
|
-
Enabled: true
|
|
280
|
-
|
|
281
|
-
# === PERFORMANCE ===
|
|
282
|
-
|
|
283
|
-
Performance/AncestorsInclude:
|
|
284
|
-
Enabled: true
|
|
285
|
-
|
|
286
|
-
Performance/BigDecimalWithNumericArgument:
|
|
287
|
-
Enabled: true
|
|
288
|
-
|
|
289
|
-
Performance/RedundantSortBlock:
|
|
290
|
-
Enabled: true
|
|
291
|
-
|
|
292
|
-
Performance/RedundantStringChars:
|
|
293
|
-
Enabled: true
|
|
294
|
-
|
|
295
|
-
Performance/ReverseFirst:
|
|
296
|
-
Enabled: true
|
|
297
|
-
|
|
298
|
-
Performance/SortReverse:
|
|
299
|
-
Enabled: true
|
|
300
|
-
|
|
301
|
-
Performance/Squeeze:
|
|
302
|
-
Enabled: true
|
|
303
|
-
|
|
304
|
-
Performance/StringInclude:
|
|
305
|
-
Enabled: true
|
|
306
|
-
|
|
307
|
-
Performance/Sum:
|
|
308
|
-
Enabled: true
|
|
309
|
-
|
|
310
|
-
# === MINITEST ===
|
|
311
|
-
|
|
312
|
-
Minitest/MultipleAssertions:
|
|
313
71
|
Max: 10
|
|
314
72
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
Minitest/AssertWithExpectedArgument:
|
|
319
|
-
Enabled: true
|
|
73
|
+
Metrics/BlockLength:
|
|
74
|
+
Max: 35
|
|
320
75
|
|
|
321
|
-
Minitest/RefuteFalse:
|
|
322
|
-
Enabled: true
|
|
323
76
|
|
|
324
|
-
|
|
325
|
-
|
|
77
|
+
Metrics/CyclomaticComplexity:
|
|
78
|
+
Max: 10
|
|
326
79
|
|
|
327
|
-
|
|
328
|
-
Enabled:
|
|
80
|
+
Style/DirectiveScope:
|
|
81
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## [0.8.0] - 2025-08-31
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added an optional, bounded Ruby agent runtime with strict single-action tool
|
|
10
|
+
execution, provider routing through `Prescient::Client`, and safe loop limits.
|
|
11
|
+
- Added deterministic agent context compaction and safe serialization of tool
|
|
12
|
+
failures before they are returned to the model.
|
|
13
|
+
- Added generic callable agent tools, request-scoped authorization and bounded
|
|
14
|
+
telemetry hooks, the `prescient agent` CLI command, and `POST /v1/agent`.
|
|
15
|
+
- Added a dedicated `Prescient::Agent::CLIAdapter`, schema validation for
|
|
16
|
+
generic callable tools, and independent task and response byte limits.
|
|
17
|
+
- Added structured failure telemetry, principal propagation, and an
|
|
18
|
+
authenticated, bounded MCP Rack transport backed by shared JSON-RPC dispatch.
|
|
19
|
+
- Added valid UTF-8, JSON-safe observation truncation for bounded Agent context.
|
|
20
|
+
- Extended the MCP Rack transport with authenticated sessions, lifecycle
|
|
21
|
+
termination, notifications, one-shot SSE responses, Origin checks, and a
|
|
22
|
+
reusable bearer-token authentication policy.
|
|
23
|
+
- Documented the host-owned tenant/principal authorization contract and aligned
|
|
24
|
+
the Agent implementation plan with the shipped REST and MCP surfaces.
|
|
25
|
+
- Added an opt-in thread-safe JSONL Agent audit sink for durable safe telemetry.
|
|
26
|
+
- Added an optional lazy MCP adapter with capability discovery, safe core
|
|
27
|
+
operations, agent invocation, resources, and newline-delimited JSON-RPC stdio.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fixed Agent tool validation to enforce richer JSON Schema constraints,
|
|
32
|
+
composition, and local references.
|
|
33
|
+
- Fixed request-scoped Agent authorization context handling for concurrent API
|
|
34
|
+
requests and added regression coverage for principal isolation.
|
|
35
|
+
- Tightened generic callable Agent tools to require explicit object schemas
|
|
36
|
+
instead of accepting unconstrained arguments.
|
|
37
|
+
- Added structured Agent failure telemetry for initialization failures and
|
|
38
|
+
propagated telemetry configuration through CLI and REST Agent execution.
|
|
39
|
+
|
|
5
40
|
## [0.7.0] - 2025-08-17
|
|
6
41
|
|
|
7
42
|
### Added
|
|
@@ -17,6 +52,8 @@
|
|
|
17
52
|
country, API-key, timeout, and result-limit configuration.
|
|
18
53
|
- Added capability groups for ordered tool-adapter fallback on transient
|
|
19
54
|
connection and rate-limit failures.
|
|
55
|
+
- Added bounded JSON document sources for Ruby, CLI, and REST generation context,
|
|
56
|
+
including local files, in-memory documents, and injected Redis clients.
|
|
20
57
|
- Documented SearXNG and SearchApi setup and CLI usage side by side.
|
|
21
58
|
- Added explicit `--generate` opt-in behavior to the web-search example.
|
|
22
59
|
- Added `SEARXNG_URL` environment defaults for automatic `web_search` registration.
|
data/INTEGRATION_GUIDE.md
CHANGED
|
@@ -11,7 +11,7 @@ and [examples guide](examples/README.md).
|
|
|
11
11
|
|
|
12
12
|
```ruby
|
|
13
13
|
# Add to your Gemfile
|
|
14
|
-
gem 'prescient', '~> 0.
|
|
14
|
+
gem 'prescient', '~> 0.8.0'
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### 2. Replace Existing AI Service
|
|
@@ -192,6 +192,7 @@ This makes the following routes available under `/prescient`:
|
|
|
192
192
|
| `POST` | `/v1/generate` | Text generation |
|
|
193
193
|
| `POST` | `/v1/search` | Normalized external-tool search |
|
|
194
194
|
| `POST` | `/v1/search/generate` | Search with opt-in AI generation |
|
|
195
|
+
| `POST` | `/v1/agent` | Bounded allowlisted tool-calling agent |
|
|
195
196
|
| `POST` | `/v1/embeddings` | Single embedding |
|
|
196
197
|
| `POST` | `/v1/embeddings/batch` | Bounded batch embeddings |
|
|
197
198
|
|
|
@@ -214,6 +215,11 @@ curl -X POST http://localhost:3000/prescient/v1/search \
|
|
|
214
215
|
-H "Authorization: Bearer ${PRESCIENT_API_TOKEN}" \
|
|
215
216
|
-H 'Content-Type: application/json' \
|
|
216
217
|
-d '{"query":"Ruby HTTP clients","limit":5}'
|
|
218
|
+
|
|
219
|
+
curl -X POST http://localhost:3000/prescient/v1/agent \
|
|
220
|
+
-H "Authorization: Bearer ${PRESCIENT_API_TOKEN}" \
|
|
221
|
+
-H 'Content-Type: application/json' \
|
|
222
|
+
-d '{"prompt":"Summarize account status","provider":"openai","tools":[]}'
|
|
217
223
|
```
|
|
218
224
|
|
|
219
225
|
Responses include a request ID. Request bodies are size-limited, batch inputs
|
data/README.md
CHANGED
|
@@ -13,6 +13,94 @@ For focused guidance, see the **[examples guide](https://github.com/kanutocd/pre
|
|
|
13
13
|
**[Rails integration guide](https://github.com/kanutocd/prescient/blob/main/INTEGRATION_GUIDE.md)**, and
|
|
14
14
|
**[pgvector guide](https://github.com/kanutocd/prescient/blob/main/VECTOR_SEARCH_GUIDE.md)**.
|
|
15
15
|
|
|
16
|
+
Prescient has three primary application entry points:
|
|
17
|
+
|
|
18
|
+
- **Ruby gem** — Use `Prescient` directly from a Ruby application.
|
|
19
|
+
- **CLI** — Run provider operations and configuration checks from scripts or a
|
|
20
|
+
terminal.
|
|
21
|
+
- **Rack-compatible REST API** — Mount `Prescient::API` in a Rack, Rails, or
|
|
22
|
+
other Rack-compatible application.
|
|
23
|
+
|
|
24
|
+
The Ruby entry point does not load the CLI, REST API, or MCP transport. Those
|
|
25
|
+
surfaces are loaded lazily when referenced or explicitly required.
|
|
26
|
+
MCP is an optional protocol integration surface available through explicit
|
|
27
|
+
`prescient/mcp` loading; it does not change the three primary entry points.
|
|
28
|
+
|
|
29
|
+
## Contents
|
|
30
|
+
|
|
31
|
+
- [Quick Start](#quick-start)
|
|
32
|
+
- [Features](#features)
|
|
33
|
+
- [Supported Providers](#supported-providers)
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Command-Line Interface](#command-line-interface)
|
|
36
|
+
- [REST API](#rest-api)
|
|
37
|
+
- [Configuration](#configuration)
|
|
38
|
+
- [Ruby API](#ruby-api)
|
|
39
|
+
- [Agent runtime](#agent-runtime)
|
|
40
|
+
- [Custom Prompt Templates](#custom-prompt-templates)
|
|
41
|
+
- [Custom Context Configurations](#custom-context-configurations)
|
|
42
|
+
- [Vector Database Integration](#vector-database-integration-pgvector)
|
|
43
|
+
- [Testing](#testing)
|
|
44
|
+
- [Development](#development)
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
Prescient reads provider credentials from the environment. The following
|
|
49
|
+
example uses OpenAI; replace it with a configured provider when appropriate.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export OPENAI_API_KEY=your_api_key
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Create `example.rb`:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
require "prescient"
|
|
59
|
+
|
|
60
|
+
Prescient.configure do |config|
|
|
61
|
+
config.default_provider = :openai
|
|
62
|
+
config.add_provider(
|
|
63
|
+
:openai,
|
|
64
|
+
Prescient::Provider::OpenAI,
|
|
65
|
+
api_key: ENV.fetch("OPENAI_API_KEY"),
|
|
66
|
+
chat_model: ENV.fetch("OPENAI_CHAT_MODEL", "gpt-4.1-mini")
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
result = Prescient.generate_response(
|
|
71
|
+
"Draft a concise maintenance notice for a scheduled database upgrade."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
puts result[:response]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Run it with:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bundle exec ruby example.rb
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`generate_response` returns a normalized hash containing the generated
|
|
84
|
+
response, provider, model, and provider metadata. Use the client directly when
|
|
85
|
+
you need repeated operations or provider-specific fallback control:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
client = Prescient.client(:openai)
|
|
89
|
+
response = client.generate_response("Summarize this release note.")
|
|
90
|
+
embedding = client.generate_embedding("A searchable release note.")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For local development, configure Ollama instead of a hosted provider:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
docker compose up -d ollama
|
|
97
|
+
docker compose run --rm ollama-init
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
See the [examples guide](examples/README.md) for complete scripts and the
|
|
101
|
+
[Configuration](#configuration) section for YAML, environment references,
|
|
102
|
+
fallbacks, prompt templates, and external tools.
|
|
103
|
+
|
|
16
104
|
## Features
|
|
17
105
|
|
|
18
106
|
- **Provider abstraction** — One consistent interface across supported AI providers
|
|
@@ -106,6 +194,8 @@ prescient config validate
|
|
|
106
194
|
prescient config example
|
|
107
195
|
prescient generate "Explain Ruby Ractors"
|
|
108
196
|
prescient embed "Ruby is a programming language"
|
|
197
|
+
prescient search "Ruby HTTP clients"
|
|
198
|
+
prescient agent "Summarize the account status" --tool accounts
|
|
109
199
|
```
|
|
110
200
|
|
|
111
201
|
Supported options include:
|
|
@@ -125,8 +215,15 @@ Supported options include:
|
|
|
125
215
|
--api-key KEY Use an API key for the operation
|
|
126
216
|
--api-key-env NAME Read the API key from an environment variable
|
|
127
217
|
--format FORMAT Select text or json output
|
|
218
|
+
--json-file PATH Load JSON documents as generation context
|
|
219
|
+
--no-fallback Disable provider fallback
|
|
128
220
|
```
|
|
129
221
|
|
|
222
|
+
Search-specific options are `--tool NAME`, `--generate`, and `--limit COUNT`.
|
|
223
|
+
Agent-specific options are repeatable `--tool NAME` and `--max-loops COUNT`.
|
|
224
|
+
The full command-specific help is available with `prescient search --help` or
|
|
225
|
+
`prescient agent --help`.
|
|
226
|
+
|
|
130
227
|
Use `--api-key-env` to source credentials from an environment variable. The
|
|
131
228
|
direct `--api-key` option is available for ephemeral automation but may be
|
|
132
229
|
visible in shell history or process listings. Use `--format json` for
|
|
@@ -247,6 +344,7 @@ Available endpoints include:
|
|
|
247
344
|
- **`POST /v1/generate`**
|
|
248
345
|
- **`POST /v1/search`**
|
|
249
346
|
- **`POST /v1/search/generate`**
|
|
347
|
+
- **`POST /v1/agent`**
|
|
250
348
|
- **`POST /v1/embeddings`**
|
|
251
349
|
- **`POST /v1/embeddings/batch`**
|
|
252
350
|
- **`GET /v1/providers`**
|
|
@@ -584,6 +682,117 @@ client_no_fallback = Prescient::Client.new(:primary, enable_fallback: false)
|
|
|
584
682
|
response = Prescient.generate_response("Hello", provider: :primary, enable_fallback: true)
|
|
585
683
|
```
|
|
586
684
|
|
|
685
|
+
### JSON document context
|
|
686
|
+
|
|
687
|
+
For small documentation sets, you can load JSON objects from a file instead of
|
|
688
|
+
creating embeddings:
|
|
689
|
+
|
|
690
|
+
```ruby
|
|
691
|
+
documents = Prescient::DocumentSource::JsonFile.new(path: "docs.json").fetch
|
|
692
|
+
response = Prescient.generate_response("How do I reset my password?", documents)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
The CLI supports the same flow with bounded document loading:
|
|
696
|
+
|
|
697
|
+
```bash
|
|
698
|
+
prescient generate --json-file docs.json "How do I reset my password?"
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Redis-backed documents use an injected Redis-compatible client, so Redis remains
|
|
702
|
+
optional for gem consumers:
|
|
703
|
+
|
|
704
|
+
```ruby
|
|
705
|
+
source = Prescient::DocumentSource::RedisJson.new(client: redis, key: "docs:acme")
|
|
706
|
+
response = Prescient.generate_response("Summarize the docs", source.fetch)
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
The REST API accepts the same documents inline as `documents` on
|
|
710
|
+
`POST /v1/generate`. Sources enforce document-count and serialized-byte limits;
|
|
711
|
+
use retrieval or embeddings when the documentation no longer fits comfortably
|
|
712
|
+
in the provider's context window.
|
|
713
|
+
|
|
714
|
+
## Agent runtime
|
|
715
|
+
|
|
716
|
+
Prescient also provides an optional, bounded agent runtime. It is loaded
|
|
717
|
+
explicitly and can execute allowlisted search tools through the existing
|
|
718
|
+
provider routing:
|
|
719
|
+
|
|
720
|
+
```ruby
|
|
721
|
+
require "prescient/agent"
|
|
722
|
+
|
|
723
|
+
agent = Prescient::Agent::Runtime.new(
|
|
724
|
+
provider: :openai,
|
|
725
|
+
tool_names: [:web_search],
|
|
726
|
+
configuration: Prescient::Agent::Configuration.new(max_loops: 5)
|
|
727
|
+
)
|
|
728
|
+
result = agent.run("Find and summarize the latest Ruby release")
|
|
729
|
+
puts result.response
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
The runtime supports one validated JSON action per iteration and stops at its
|
|
733
|
+
loop limit. It does not provide arbitrary Ruby, shell, filesystem, browser, or
|
|
734
|
+
database access. The agent namespace is not loaded by `require "prescient"`.
|
|
735
|
+
|
|
736
|
+
The same bounded runtime is available from the CLI:
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
prescient agent "Summarize the account status" --provider openai --tool accounts
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
It is also available through `POST /v1/agent`. Supply an explicit `tools` array;
|
|
743
|
+
the REST API does not grant access to configured tools implicitly:
|
|
744
|
+
|
|
745
|
+
```json
|
|
746
|
+
{"prompt":"Summarize the account status","provider":"openai","tools":[]}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
Applications can provide `authorization` and `request_context` hooks to enforce
|
|
750
|
+
tenant/principal policy, and a bounded `telemetry` hook receives only event
|
|
751
|
+
metadata such as loop count, action names, and success status. The authorization
|
|
752
|
+
hook receives `tool:`, copied `arguments:`, and copied request-scoped
|
|
753
|
+
`context:`; only an exact `true` result permits invocation. Prescient does not
|
|
754
|
+
guess roles, ownership, or organization boundaries, so the host application
|
|
755
|
+
must make the access decision.
|
|
756
|
+
|
|
757
|
+
For durable, privacy-preserving audit records, pass an opt-in JSONL sink:
|
|
758
|
+
|
|
759
|
+
```ruby
|
|
760
|
+
audit_log = Prescient::Agent::AuditLog.new(path: "tmp/prescient-agent.jsonl")
|
|
761
|
+
Prescient::Agent::Runtime.new(audit_log:).run("Summarize account status")
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
The sink persists event metadata and timestamps, never prompts, documents, or
|
|
765
|
+
raw observations. Audit storage failures are isolated from agent execution;
|
|
766
|
+
configure retention, access controls, and storage protection in the host.
|
|
767
|
+
|
|
768
|
+
For MCP hosts, load the optional dependency-free adapter explicitly with
|
|
769
|
+
`require "prescient/mcp"`, or run `prescient-mcp` for newline-delimited
|
|
770
|
+
JSON-RPC over stdio. MCP exposes only explicitly enabled capabilities and never
|
|
771
|
+
returns credentials or raw provider failure bodies.
|
|
772
|
+
|
|
773
|
+
The optional Rack adapter provides a bounded Streamable HTTP surface with
|
|
774
|
+
bearer-token authentication, Origin allowlisting, initialize-assigned sessions,
|
|
775
|
+
`Mcp-Session-Id` lifecycle management, notifications, and one-shot JSON or
|
|
776
|
+
server-sent-event responses:
|
|
777
|
+
|
|
778
|
+
```ruby
|
|
779
|
+
require "prescient/mcp"
|
|
780
|
+
|
|
781
|
+
app = Prescient::MCP::Rack.new(
|
|
782
|
+
authentication: Prescient::MCP::Authentication::BearerToken.new(
|
|
783
|
+
token: ENV.fetch("MCP_TOKEN"),
|
|
784
|
+
principal: { id: "mcp-client" }
|
|
785
|
+
),
|
|
786
|
+
allowed_origins: ["https://admin.example.com"]
|
|
787
|
+
)
|
|
788
|
+
run app
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
Clients must send `MCP-Protocol-Version: 2025-06-18` and the returned
|
|
792
|
+
`Mcp-Session-Id` on requests after `initialize`. `DELETE` terminates a session.
|
|
793
|
+
The adapter does not implement a long-lived notification queue or replace a
|
|
794
|
+
host application's token provisioning and authorization policy.
|
|
795
|
+
|
|
587
796
|
**Fallback Behavior:**
|
|
588
797
|
- When a provider fails with a persistent error, Prescient automatically tries the next available provider
|
|
589
798
|
- Configured fallback providers are tried in order; the provider operation determines availability
|
|
@@ -592,7 +801,7 @@ response = Prescient.generate_response("Hello", provider: :primary, enable_fallb
|
|
|
592
801
|
- Provider-service failures, connection failures, rate limits, and unavailable models may trigger fallback; authentication and invalid-request errors are returned to the caller
|
|
593
802
|
- The fallback process preserves all method arguments and options
|
|
594
803
|
|
|
595
|
-
##
|
|
804
|
+
## Ruby API
|
|
596
805
|
|
|
597
806
|
### Quick Start
|
|
598
807
|
|
data/Steepfile
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
target :prescient do
|
|
4
|
-
signature
|
|
5
|
-
library
|
|
6
|
-
library
|
|
4
|
+
signature "sig"
|
|
5
|
+
library "json"
|
|
6
|
+
library "net-http"
|
|
7
7
|
|
|
8
|
-
check
|
|
9
|
-
check
|
|
10
|
-
check
|
|
11
|
-
check
|
|
12
|
-
check
|
|
13
|
-
check
|
|
14
|
-
check
|
|
15
|
-
check
|
|
16
|
-
check
|
|
8
|
+
check "lib/prescient/version.rb"
|
|
9
|
+
check "lib/prescient/errors.rb"
|
|
10
|
+
check "lib/prescient.rb"
|
|
11
|
+
check "lib/prescient/client.rb"
|
|
12
|
+
check "lib/prescient/base.rb"
|
|
13
|
+
check "lib/prescient/provider/openai.rb"
|
|
14
|
+
check "lib/prescient/provider/ollama.rb"
|
|
15
|
+
check "lib/prescient/provider/anthropic.rb"
|
|
16
|
+
check "lib/prescient/provider/huggingface.rb"
|
|
17
17
|
end
|