@absolutejs/rag 0.17.0 → 0.18.1
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.
- package/CHANGELOG.md +12 -0
- package/README.md +4 -0
- package/RESEARCH.md +268 -0
- package/changelog.json +262 -235
- package/dist/angular/index.js +174 -2
- package/dist/angular/index.js.map +6 -4
- package/dist/client/index.js +149 -1
- package/dist/client/index.js.map +5 -4
- package/dist/client/research.js +223 -0
- package/dist/client/research.js.map +10 -0
- package/dist/manifest.js +3722 -269
- package/dist/manifest.js.map +29 -3
- package/dist/react/index.js +158 -2
- package/dist/react/index.js.map +6 -4
- package/dist/research/index.js +125286 -0
- package/dist/research/index.js.map +369 -0
- package/dist/src/angular/index.d.ts +1 -0
- package/dist/src/angular/research.service.d.ts +12 -0
- package/dist/src/client/index.d.ts +1 -0
- package/dist/src/client/research.d.ts +30 -0
- package/dist/src/react/index.d.ts +1 -0
- package/dist/src/react/useResearch.d.ts +9 -0
- package/dist/src/research/budget.d.ts +26 -0
- package/dist/src/research/evaluation.d.ts +42 -0
- package/dist/src/research/evidence.d.ts +21 -0
- package/dist/src/research/index.d.ts +9 -0
- package/dist/src/research/manifest.d.ts +18 -0
- package/dist/src/research/plugin.d.ts +230 -0
- package/dist/src/research/runtime.d.ts +2 -0
- package/dist/src/research/schema.d.ts +8 -0
- package/dist/src/research/store.d.ts +29 -0
- package/dist/src/research/types.d.ts +96 -0
- package/dist/src/research/workflows.d.ts +96 -0
- package/dist/src/svelte/createResearch.d.ts +9 -0
- package/dist/src/svelte/index.d.ts +1 -0
- package/dist/src/vue/index.d.ts +1 -0
- package/dist/src/vue/useResearch.d.ts +9 -0
- package/dist/svelte/index.js +164 -2
- package/dist/svelte/index.js.map +6 -4
- package/dist/vue/index.js +164 -2
- package/dist/vue/index.js.map +6 -4
- package/package.json +24 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.18.1 — 2026-09-24
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Accept Elysia 2 and TypeBox 1 research schemas with inferred result types and matching runtime validation while preserving TypeBox 0.34 compatibility.** (`ResearchTask`, `ResearchRuntime`, `ResearchSchema`, `ResearchStatic`, `createResearch`)
|
|
14
|
+
|
|
15
|
+
## 0.18.0 — 2026-09-24
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Add configurable web research, bounded multistep planning, field-level evidence review, a server plugin and six framework flows, company discovery, resumable batches, fenced PostgreSQL monitoring, billing admission, agent tools and evaluation adapters.**
|
|
20
|
+
|
|
9
21
|
## 0.17.0 — 2026-09-24
|
|
10
22
|
|
|
11
23
|
### Security
|
package/README.md
CHANGED
|
@@ -230,3 +230,7 @@ provider to cancel its in-flight request. `onProgress` reports completed pages.
|
|
|
230
230
|
Blank pages are skipped; an entirely unreadable document is rejected.
|
|
231
231
|
|
|
232
232
|
`createWebsiteReaderClient` from `@absolutejs/rag/web` connects to a trusted host-managed reader service and validates its status, text, source URLs and limitations. Configure the service endpoint and inject a static fallback. The client reserves fallback time within one total deadline, propagates caller cancellation, and keeps service saturation retryable. RAG's public HTTP/HTTPS reader shares the low-level pinned transport with `@absolutejs/egress/transport`; egress still enforces its own allowlisted HTTPS policy.
|
|
233
|
+
|
|
234
|
+
## Web research workflows
|
|
235
|
+
|
|
236
|
+
See [RESEARCH.md](./RESEARCH.md) for the research plugin, six framework flows, schema extraction, field evidence review, company discovery, durable batches, monitoring, spending policy, agent tools, and evaluation.
|
package/RESEARCH.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Web research workflows
|
|
2
|
+
|
|
3
|
+
`@absolutejs/rag/research` composes Search, the RAG web reader, AI structured generation, field evidence review, and optional billing admission. The runtime lives on the server. React, Vue, Svelte, Angular, HTML and HTMX share the same authorized endpoints.
|
|
4
|
+
|
|
5
|
+
## Configure once
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Elysia, t } from "elysia";
|
|
9
|
+
import { anthropic } from "@absolutejs/ai/anthropic";
|
|
10
|
+
import { createBraveSearch } from "@absolutejs/search/brave";
|
|
11
|
+
import {
|
|
12
|
+
createResearch,
|
|
13
|
+
researchPlugin,
|
|
14
|
+
researchMonitorTask,
|
|
15
|
+
} from "@absolutejs/rag/research";
|
|
16
|
+
|
|
17
|
+
const research = createResearch({
|
|
18
|
+
search: createBraveSearch({ apiKey: process.env.BRAVE_SEARCH_API_KEY! }),
|
|
19
|
+
provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
|
|
20
|
+
model: process.env.RESEARCH_MODEL!,
|
|
21
|
+
tasks: {
|
|
22
|
+
company: {
|
|
23
|
+
schema: t.Object({ name: t.String(), description: t.String() }),
|
|
24
|
+
},
|
|
25
|
+
signals: researchMonitorTask,
|
|
26
|
+
},
|
|
27
|
+
limits: { searches: 4, reads: 4, rounds: 2, timeoutMs: 90_000 },
|
|
28
|
+
// Optional: explicitly scoped public evidence, never implicit tenant sharing.
|
|
29
|
+
cache: { scope: "public-company-evidence", ttlMs: 300_000 },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// In an existing app, use its actual session/agent authorization here.
|
|
33
|
+
const plugin = researchPlugin({
|
|
34
|
+
runtime: research,
|
|
35
|
+
authorize: authorizeResearchRequest,
|
|
36
|
+
});
|
|
37
|
+
const app = new Elysia().use(plugin);
|
|
38
|
+
|
|
39
|
+
const result = await research.run({
|
|
40
|
+
query: "Example company",
|
|
41
|
+
task: "company",
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`authorizeResearchRequest` is your application's authorization function, not an export of this package. It is required. For multiple tenants, pass `runtime: request => runtimeForAuthorizedTenant(request)`; authorization runs first. Scope tenant caches, workflows and budget ledgers to the same resolved identity. The plugin rejects cross-origin browser POSTs and sends `Cache-Control: no-store`. Do not treat a caller-supplied tenant header as identity.
|
|
46
|
+
|
|
47
|
+
`run` selects server-registered schemas; browsers cannot override models, limits, schemas or credentials. `extract({schema, instructions}, input)` accepts JSON-compatible Elysia 2 `t`, TypeBox 1 (`typebox`), or legacy TypeBox 0.34 (`@sinclair/typebox`) schemas directly, preserving result type inference and runtime validation for trusted server code. The same configured AI provider handles planning, extraction and independent review, with transport retries and schema repair retries disabled.
|
|
48
|
+
|
|
49
|
+
Default limits are four searches, four page reads, two planning rounds, 90 seconds, 32,000 evidence characters, 4,096 output tokens per model call and 128 primitive output fields. Limits are per research call, not per entire batch. Page reading uses the existing hardened public web transport; optional `render` enables your existing browser service. A browser process is not provisioned automatically. Cancellation must also be honored by custom providers and readers.
|
|
50
|
+
|
|
51
|
+
Quoted entity phrases are required in accepted source text by default. Trusted server callers may supply `requiredPhrases` explicitly. This conservative check can miss legitimate aliases; semantic matching and recall still require evaluation.
|
|
52
|
+
|
|
53
|
+
## Read the result
|
|
54
|
+
|
|
55
|
+
- `data` contains the schema-shaped result only when all primitive fields pass review; otherwise it is `null`.
|
|
56
|
+
- `fields` always preserves reviewed JSON-pointer paths, values, verdicts, reasons and source-bound quotations. Inspect verdicts before consuming individual values from a partial result.
|
|
57
|
+
- Verdicts are `supported`, `unsupported`, `conflicting`, or `unknown`. Null values, missing reviews, duplicate review paths, and invalid quotations cannot become supported facts.
|
|
58
|
+
- `sources`, `searches`, `operations`, and `limitations` retain provenance and execution information. Search/page/model failures are not successful empty results.
|
|
59
|
+
- `reviewed` describes a model review with deterministic citation checks. It does not establish independent truth, exhaustive recall, current employment, email deliverability or buying intent.
|
|
60
|
+
|
|
61
|
+
## Framework flows
|
|
62
|
+
|
|
63
|
+
All bindings call the same streaming endpoint, expose progress and cancellation, and fence late results from an earlier request. Use `state.result.fields` for citations and field verdicts.
|
|
64
|
+
|
|
65
|
+
### React
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { useResearch } from "@absolutejs/rag/react";
|
|
69
|
+
function ResearchButton() {
|
|
70
|
+
const { run, cancel, state } = useResearch("/research");
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<button
|
|
74
|
+
onClick={() =>
|
|
75
|
+
void run({ query: "Example company", task: "company" }).catch(
|
|
76
|
+
() => {},
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
>
|
|
80
|
+
Research
|
|
81
|
+
</button>
|
|
82
|
+
<button onClick={cancel}>Cancel</button>
|
|
83
|
+
<pre>{JSON.stringify(state.result ?? state.progress ?? state.error)}</pre>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Vue
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { useResearch } from "@absolutejs/rag/vue";
|
|
93
|
+
// Within setup/effect scope; state is a shallow ref. Scope disposal cancels work.
|
|
94
|
+
const { run, cancel, state } = useResearch("/research");
|
|
95
|
+
await run({ query: "Example company", task: "company" });
|
|
96
|
+
console.log(state.value.result);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Svelte
|
|
100
|
+
|
|
101
|
+
```svelte
|
|
102
|
+
<script lang="ts">
|
|
103
|
+
import { onDestroy } from 'svelte';
|
|
104
|
+
import { createResearchStore } from '@absolutejs/rag/svelte';
|
|
105
|
+
const research = createResearchStore('/research');
|
|
106
|
+
const { state } = research;
|
|
107
|
+
onDestroy(research.dispose);
|
|
108
|
+
</script>
|
|
109
|
+
<button onclick={() => research.run({ query: 'Example company' }).catch(() => {})}>Research</button>
|
|
110
|
+
<button onclick={research.cancel}>Cancel</button>
|
|
111
|
+
<pre>{JSON.stringify($state.result ?? $state.progress ?? $state.error)}</pre>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Angular
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { DestroyRef, inject } from "@angular/core";
|
|
118
|
+
import { ResearchService } from "@absolutejs/rag/angular";
|
|
119
|
+
// Component field initializers; DestroyRef binds cancellation to this component.
|
|
120
|
+
research = inject(ResearchService).connect("/research", inject(DestroyRef));
|
|
121
|
+
// this.research.run({ query: 'Example company', task: 'company' });
|
|
122
|
+
// this.research.state().result; this.research.cancel();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### HTML
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<form id="research">
|
|
129
|
+
<input name="query" required /><button>Research</button>
|
|
130
|
+
</form>
|
|
131
|
+
<pre id="result" aria-live="polite"></pre>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { bindResearchForm } from "@absolutejs/rag/research/client";
|
|
136
|
+
const research = bindResearchForm(
|
|
137
|
+
document.querySelector<HTMLFormElement>("#research")!,
|
|
138
|
+
document.querySelector<HTMLElement>("#result")!,
|
|
139
|
+
);
|
|
140
|
+
// research.cancel(); research.dispose() when removing the form.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For custom UI use `createResearchClient({path, fetch, headers})`. HTML output uses `textContent`. HTTP credentials are same-origin; framework hooks work with your existing session cookies.
|
|
144
|
+
|
|
145
|
+
### HTMX
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<form
|
|
149
|
+
hx-post="/research/html"
|
|
150
|
+
hx-target="#result"
|
|
151
|
+
hx-indicator="#researching"
|
|
152
|
+
hx-sync="this:replace"
|
|
153
|
+
>
|
|
154
|
+
<input name="query" required />
|
|
155
|
+
<button>Research</button>
|
|
156
|
+
</form>
|
|
157
|
+
<span id="researching" class="htmx-indicator">Researching…</span>
|
|
158
|
+
<section id="result" aria-live="polite"></section>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This path accepts form-urlencoded input and returns an escaped HTML fragment with verdicts and source links. Request aborts propagate through the runtime. HTMX and client JavaScript assets are installed/served by your application as usual.
|
|
162
|
+
|
|
163
|
+
## Company discovery
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { discoverResearchCompanies } from "@absolutejs/rag/research";
|
|
167
|
+
const found = await discoverResearchCompanies(research, {
|
|
168
|
+
query: "Companies manufacturing grid-scale battery storage in Germany",
|
|
169
|
+
criteria: [
|
|
170
|
+
{
|
|
171
|
+
id: "manufactures",
|
|
172
|
+
description: "Manufactures grid-scale battery storage equipment",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
limit: 5,
|
|
176
|
+
});
|
|
177
|
+
// found.candidates includes evidence per criterion; found.accepted requires all to match.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Discovery verifies candidate name/domain fields, deduplicates exact hostnames, and researches each criterion separately. Missing evidence produces `unknown`, not a negative claim. Corporate aliases and subsidiaries are not silently merged. Contact discovery remains available through `@absolutejs/discover`.
|
|
181
|
+
|
|
182
|
+
## Durable batches and monitoring
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { SQL } from "bun";
|
|
186
|
+
import {
|
|
187
|
+
createPostgresResearchWorkflowStore,
|
|
188
|
+
researchWorkflowPostgresSchemaSql,
|
|
189
|
+
createResearchWorkflows,
|
|
190
|
+
selectResearchMonitorEvents,
|
|
191
|
+
} from "@absolutejs/rag/research";
|
|
192
|
+
|
|
193
|
+
const sql = new SQL(process.env.DATABASE_URL!);
|
|
194
|
+
// Apply researchWorkflowPostgresSchemaSql() using your normal migration system.
|
|
195
|
+
const store = createPostgresResearchWorkflowStore({
|
|
196
|
+
unsafe: (query, parameters) => sql.unsafe(query, parameters),
|
|
197
|
+
});
|
|
198
|
+
const workflows = createResearchWorkflows({
|
|
199
|
+
runtime: research,
|
|
200
|
+
store,
|
|
201
|
+
scope: "tenant:resolved-id",
|
|
202
|
+
version: "company-research-v1",
|
|
203
|
+
leaseMs: 120_000, // must exceed the configured runtime deadline
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await workflows.batch({
|
|
207
|
+
id: "import-2026-09",
|
|
208
|
+
items: [{ id: "example", query: "Example company", task: "company" }],
|
|
209
|
+
});
|
|
210
|
+
await workflows.monitor({
|
|
211
|
+
id: "example-signals",
|
|
212
|
+
query: "New events at Example company",
|
|
213
|
+
task: "signals",
|
|
214
|
+
select: selectResearchMonitorEvents,
|
|
215
|
+
});
|
|
216
|
+
await workflows.deliverMonitor("example-signals", async (event) => {
|
|
217
|
+
// Deliver through your existing Dispatch/Execution adapter.
|
|
218
|
+
// Receiver MUST deduplicate with event.id: delivery is at-least-once.
|
|
219
|
+
await deliverYourNotification(event.id, event.payload);
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
These are background worker functions. Register them with the existing `@absolutejs/queue` job registry and use your established cron/control-plane trigger; no new scheduler or process is installed. Job payloads should contain authorized tenant/workflow IDs, with the worker resolving runtime and configuration server-side. Large batches should be enqueued, not held in an HTTP request.
|
|
224
|
+
|
|
225
|
+
A batch retains each completed research result (including partial/unavailable outcomes); repeating the same item reuses it. Use a new batch/item ID for an intentional rerun. Reusing an ID with changed input or task version fails. The first successful monitor run, including a successful empty result, establishes a silent baseline. Incomplete research retains the previous baseline. Verified new events update state and durable delivery records in one fenced database statement. Expired workers cannot commit over successors.
|
|
226
|
+
|
|
227
|
+
The event helper deduplicates normalized entity/type/summary/date. It does not semantically cluster differently worded reports of the same event. For stable domain event IDs, configure your own `select`. At 10,000 seen IDs a monitor stops rather than silently forgetting and resending history. Persisted research contains source excerpts; apply your normal tenant data retention policy.
|
|
228
|
+
|
|
229
|
+
`createMemoryResearchWorkflowStore()` is for tests and local demos only. PostgreSQL is the durable implementation. Scheduled cadence, subscriptions, recipient selection, retention and delivery policy belong to the host.
|
|
230
|
+
|
|
231
|
+
## Spending policy
|
|
232
|
+
|
|
233
|
+
Operation limits work without billing configuration. For a shared monetary allowance, configure `budget` using the existing `createProviderBudget` ledger from `@absolutejs/billing/provider-budget`:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
const budget = {
|
|
237
|
+
ledger: createProviderBudget(creditSqlClient),
|
|
238
|
+
scope: "tenant:resolved-id:research",
|
|
239
|
+
period: () => new Date().toISOString().slice(0, 10),
|
|
240
|
+
maxMicros: dailyAllowanceMicros,
|
|
241
|
+
maxRequests: dailyOperationAllowance,
|
|
242
|
+
reserveMicros: configuredOperationPriceCeilings, // search/read/plan/extract/review
|
|
243
|
+
actualMicros: priceObservedOperation, // returns integer micros or null if unknown
|
|
244
|
+
};
|
|
245
|
+
// createResearch({ ...providerConfiguration, budget });
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The price policy must match the configured models, token ceilings and reader/search services. Unknown outcomes retain the reservation. A measured cost exceeding its configured ceiling stops the research after settlement. No model prices are hardcoded, and unknown costs are never treated as zero. The billing table is installed through the billing package's existing migration contract. `admit` remains available for custom policy, such as combining this ledger with existing rate-limit admission; do not supply both `budget` and `admit`.
|
|
249
|
+
|
|
250
|
+
## Agent tools and configuration recipe
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import { researchManifest } from "@absolutejs/rag/research";
|
|
254
|
+
import { toAIToolMap, toMcpToolRegistry } from "@absolutejs/manifest";
|
|
255
|
+
const aiTools = toAIToolMap(researchManifest, { runtime: research, enforce });
|
|
256
|
+
const mcpTools = toMcpToolRegistry(researchManifest, {
|
|
257
|
+
runtime: research,
|
|
258
|
+
enforce,
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`enforce` is the host's existing policy/Agency binding. Tools require `research:run`; unguarded bridges omit them. The manifest's Brave/Anthropic recipe configures credentials from environment references and a user-selected model. Direct runtime configuration supports any compatible search/model provider. The search package supplies its own `search_web` tool and Brave recipe.
|
|
263
|
+
|
|
264
|
+
## Evaluate before claiming equivalence
|
|
265
|
+
|
|
266
|
+
Use `evaluateResearch` to run representative cases with optional independent human review and total measured cost. Unreviewed acceptance, unknown cost, and cost per accepted finding without accepted findings remain `null`. Publication freshness measures source publication dates; it is not event freshness.
|
|
267
|
+
|
|
268
|
+
Each result includes `groundingCase`, accepted by the existing `evaluateRAGAnswerGrounding({cases})` function from `@absolutejs/rag/quality`. Citation resolution metrics do not establish factual correctness or discovery recall. Keep approved ground truth and reviewer identity with comparisons, and hold task/model/budget policy constant when switching search providers.
|