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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -268
  3. data/CHANGELOG.md +37 -0
  4. data/INTEGRATION_GUIDE.md +7 -1
  5. data/README.md +210 -1
  6. data/Steepfile +12 -12
  7. data/db/migrate/001_create_prescient_tables.rb +15 -16
  8. data/examples/README.md +2 -1
  9. data/examples/custom_contexts.rb +4 -4
  10. data/exe/prescient +2 -2
  11. data/exe/prescient-mcp +7 -0
  12. data/lib/prescient/agent/audit_log.rb +37 -0
  13. data/lib/prescient/agent/cli_adapter.rb +29 -0
  14. data/lib/prescient/agent/configuration.rb +57 -0
  15. data/lib/prescient/agent/context.rb +56 -0
  16. data/lib/prescient/agent/error_serializer.rb +47 -0
  17. data/lib/prescient/agent/errors.rb +25 -0
  18. data/lib/prescient/agent/parser.rb +49 -0
  19. data/lib/prescient/agent/prompt_builder.rb +31 -0
  20. data/lib/prescient/agent/result.rb +36 -0
  21. data/lib/prescient/agent/runtime.rb +175 -0
  22. data/lib/prescient/agent/schema_validator.rb +215 -0
  23. data/lib/prescient/agent/tool_registry.rb +89 -0
  24. data/lib/prescient/agent.rb +22 -0
  25. data/lib/prescient/api.rb +337 -274
  26. data/lib/prescient/base.rb +370 -372
  27. data/lib/prescient/cli.rb +586 -526
  28. data/lib/prescient/client.rb +7 -6
  29. data/lib/prescient/configuration_loader.rb +492 -488
  30. data/lib/prescient/document_source.rb +114 -0
  31. data/lib/prescient/errors.rb +1 -3
  32. data/lib/prescient/mcp/authentication.rb +39 -0
  33. data/lib/prescient/mcp/configuration.rb +38 -0
  34. data/lib/prescient/mcp/rack.rb +243 -0
  35. data/lib/prescient/mcp/server.rb +202 -0
  36. data/lib/prescient/mcp/stdio.rb +42 -0
  37. data/lib/prescient/mcp.rb +8 -0
  38. data/lib/prescient/pgvector.rb +193 -189
  39. data/lib/prescient/provider/anthropic.rb +129 -125
  40. data/lib/prescient/provider/deepseek.rb +122 -118
  41. data/lib/prescient/provider/gemini.rb +153 -149
  42. data/lib/prescient/provider/huggingface.rb +191 -187
  43. data/lib/prescient/provider/mistral.rb +151 -147
  44. data/lib/prescient/provider/ollama.rb +168 -165
  45. data/lib/prescient/provider/openai.rb +174 -169
  46. data/lib/prescient/provider/xai.rb +122 -118
  47. data/lib/prescient/tool/search_api.rb +125 -121
  48. data/lib/prescient/tool/searxng.rb +123 -119
  49. data/lib/prescient/tool.rb +100 -98
  50. data/lib/prescient/version.rb +1 -1
  51. data/lib/prescient.rb +68 -62
  52. data/sig/prescient.rbs +176 -1
  53. metadata +23 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 176552789656cbda6e78951ab04eb0ecdfa9ffa53dcf88159bec5cdbf403cdef
4
- data.tar.gz: 5173a6bdd607dc6aca121079ee6e81243a550575f4702c57133f637a31aac798
3
+ metadata.gz: e272a00a9f7baaee44c4ad7b9827da6c1e5b930a98228e8d58718474d3090214
4
+ data.tar.gz: a7ac75b948c326d0829db56e0ab297561d178fa4643a6a4bbd5b4dc11cff150b
5
5
  SHA512:
6
- metadata.gz: fb309c19aa67f200e806b227f392ff5181e5ef7bc1ea00a6f9a0d4cf5ebbce989c0d9b472885a1c2795efaa7789a8ed61f912453fd20594fba029f8c7bc126d4
7
- data.tar.gz: 70187123148e3c0aad70f88fa469c91e3c55200c1c36b9452697109e2746cfe0fe70ef8358b766b3f99b967884aba4b846f3a8cb634c76fe9803ad45925d0e50
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
- # === LAYOUT & FORMATTING ===
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: single_quotes
43
+ EnforcedStyle: double_quotes
67
44
 
68
45
  Style/StringLiteralsInInterpolation:
69
- EnforcedStyle: single_quotes
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: false
171
- Max: 150
49
+ Enabled: true
50
+ Max: 145
172
51
  Exclude:
173
52
  - 'test/**/*'
174
53
 
175
54
  Metrics/MethodLength:
176
- Enabled: false
177
- Max: 25
55
+ Max: 37
178
56
  Exclude:
179
- - 'test/**/*'
180
- - 'db/**/*'
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
- Max: 20
61
+ Enabled: true
62
+ Max: 55
192
63
  Exclude:
193
- - 'test/**/*'
194
- - 'db/**/*'
195
- - 'lib/prescient/provider/*.rb'
64
+ - "test/**/*"
65
+ - "db/migrate/**/*"
196
66
 
197
- Metrics/CyclomaticComplexity:
198
- Max: 8
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
- Minitest/AssertTruthy:
316
- Enabled: true
317
-
318
- Minitest/AssertWithExpectedArgument:
319
- Enabled: true
73
+ Metrics/BlockLength:
74
+ Max: 35
320
75
 
321
- Minitest/RefuteFalse:
322
- Enabled: true
323
76
 
324
- Minitest/RefuteNil:
325
- Enabled: true
77
+ Metrics/CyclomaticComplexity:
78
+ Max: 10
326
79
 
327
- Minitest/TestMethodName:
328
- Enabled: true
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.7.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
- ## Usage
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 'sig'
5
- library 'json'
6
- library 'net-http'
4
+ signature "sig"
5
+ library "json"
6
+ library "net-http"
7
7
 
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'
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