@revealui/ai 0.5.2 → 0.6.2
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/README.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@revealui/ai"
|
|
3
|
+
description: "AI system for RevealUI - memory, LLM, orchestration, and tools."
|
|
4
|
+
visibility: public
|
|
5
|
+
status: verified
|
|
6
|
+
audience: user
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# @revealui/ai
|
|
2
10
|
|
|
3
11
|
> **Commercial package** - requires a [RevealUI Pro license](https://revealui.com/pro). Free to install and evaluate; a license key is required for production use.
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
* - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
|
|
12
12
|
* - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
|
|
13
13
|
* - abort(): cancels the stream
|
|
14
|
+
*
|
|
15
|
+
* CSRF: both POSTs echo the JS-readable `revealui-csrf` cookie (the signed
|
|
16
|
+
* double-submit token the RevealUI admin proxy issues) as an `X-CSRF-Token`
|
|
17
|
+
* header when the cookie is present — the api's csrfMiddleware requires it
|
|
18
|
+
* on session-cookie-authenticated unsafe requests. Sessionless callers
|
|
19
|
+
* (API-key / server-to-server) carry no session cookie and are exempt from
|
|
20
|
+
* the check, so omitting the header when the cookie is absent is correct.
|
|
14
21
|
*/
|
|
15
22
|
/**
|
|
16
23
|
* Mirror of `AgentStreamChunk` in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/client/hooks/useAgentStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
* - pendingElicitations: outstanding form-mode elicitation requests by id (A.2b)
|
|
12
12
|
* - submitElicitation: POST a response to /api/agent-stream/elicit (A.2b)
|
|
13
13
|
* - abort(): cancels the stream
|
|
14
|
+
*
|
|
15
|
+
* CSRF: both POSTs echo the JS-readable `revealui-csrf` cookie (the signed
|
|
16
|
+
* double-submit token the RevealUI admin proxy issues) as an `X-CSRF-Token`
|
|
17
|
+
* header when the cookie is present — the api's csrfMiddleware requires it
|
|
18
|
+
* on session-cookie-authenticated unsafe requests. Sessionless callers
|
|
19
|
+
* (API-key / server-to-server) carry no session cookie and are exempt from
|
|
20
|
+
* the check, so omitting the header when the cookie is absent is correct.
|
|
14
21
|
*/
|
|
15
22
|
'use client';
|
|
16
23
|
import { useCallback, useRef, useState } from 'react';
|
|
@@ -22,6 +29,35 @@ const INITIAL_STATE = {
|
|
|
22
29
|
sessionId: null,
|
|
23
30
|
pendingElicitations: [],
|
|
24
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Read the JS-readable `revealui-csrf` cookie. The api's csrfMiddleware
|
|
34
|
+
* (`apps/server/src/middleware/csrf.ts`) requires it as an `X-CSRF-Token`
|
|
35
|
+
* header on any session-cookie-bearing unsafe request; the admin proxy
|
|
36
|
+
* issues it on page load. Returns undefined outside the browser or when
|
|
37
|
+
* the cookie is absent/empty. Mirrors the cookie-read in `@revealui/core`'s
|
|
38
|
+
* admin APIClient (`request()`).
|
|
39
|
+
*/
|
|
40
|
+
function readCsrfToken() {
|
|
41
|
+
if (typeof document === 'undefined')
|
|
42
|
+
return undefined;
|
|
43
|
+
for (const part of document.cookie.split(';')) {
|
|
44
|
+
const eqIdx = part.indexOf('=');
|
|
45
|
+
if (eqIdx === -1)
|
|
46
|
+
continue;
|
|
47
|
+
if (part.slice(0, eqIdx).trim() === 'revealui-csrf') {
|
|
48
|
+
const raw = part.slice(eqIdx + 1).trim();
|
|
49
|
+
if (!raw)
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return raw;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
25
61
|
export function useAgentStream() {
|
|
26
62
|
const [state, setState] = useState(INITIAL_STATE);
|
|
27
63
|
const controllerRef = useRef(null);
|
|
@@ -39,9 +75,13 @@ export function useAgentStream() {
|
|
|
39
75
|
controllerRef.current = controller;
|
|
40
76
|
setState({ ...INITIAL_STATE, isStreaming: true });
|
|
41
77
|
try {
|
|
78
|
+
const csrfToken = readCsrfToken();
|
|
42
79
|
const response = await fetch(apiBase, {
|
|
43
80
|
method: 'POST',
|
|
44
|
-
headers: {
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
|
|
84
|
+
},
|
|
45
85
|
body: JSON.stringify(request),
|
|
46
86
|
credentials: 'include',
|
|
47
87
|
signal: controller.signal,
|
|
@@ -107,9 +147,13 @@ export function useAgentStream() {
|
|
|
107
147
|
const body = { sessionId, elicitationId, action };
|
|
108
148
|
if (action === 'accept' && content)
|
|
109
149
|
body.content = content;
|
|
150
|
+
const csrfToken = readCsrfToken();
|
|
110
151
|
const response = await fetch('/api/agent-stream/elicit', {
|
|
111
152
|
method: 'POST',
|
|
112
|
-
headers: {
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}),
|
|
156
|
+
},
|
|
113
157
|
body: JSON.stringify(body),
|
|
114
158
|
credentials: 'include',
|
|
115
159
|
});
|
package/dist/llm/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"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"}
|
package/dist/llm/client.js
CHANGED
|
@@ -250,20 +250,36 @@ export class LLMClient {
|
|
|
250
250
|
}
|
|
251
251
|
// Use dedicated embed provider if one was configured
|
|
252
252
|
const embedProvider = this.embedProviderOverride ?? this.provider;
|
|
253
|
+
// Only the primary-provider path maps to a known LLMProviderType the health
|
|
254
|
+
// monitor can key on. A dedicated embed override is an opaque provider, so we
|
|
255
|
+
// skip recording rather than attribute its latency to the primary provider.
|
|
256
|
+
const trackPrimary = !this.embedProviderOverride;
|
|
257
|
+
const callStart = Date.now();
|
|
253
258
|
try {
|
|
254
259
|
this.recordRequest();
|
|
255
|
-
|
|
260
|
+
const result = await this.circuitBreaker.execute(() => embedProvider.embed(text, options));
|
|
261
|
+
if (trackPrimary) {
|
|
262
|
+
this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
256
265
|
}
|
|
257
266
|
catch (error) {
|
|
267
|
+
if (trackPrimary) {
|
|
268
|
+
this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart, error instanceof Error ? error : new Error(String(error)));
|
|
269
|
+
}
|
|
258
270
|
// Try fallback if available (only when using the primary provider path)
|
|
259
|
-
if (!this.embedProviderOverride && this.fallbackProvider) {
|
|
271
|
+
if (!this.embedProviderOverride && this.fallbackProvider && this.config.fallbackProvider) {
|
|
260
272
|
const fp = this.fallbackProvider;
|
|
273
|
+
const fallbackStart = Date.now();
|
|
261
274
|
try {
|
|
262
|
-
|
|
275
|
+
const fb = this.fallbackCircuitBreaker
|
|
263
276
|
? await this.fallbackCircuitBreaker.execute(() => fp.embed(text, options))
|
|
264
277
|
: await fp.embed(text, options);
|
|
278
|
+
this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
|
|
279
|
+
return fb;
|
|
265
280
|
}
|
|
266
|
-
catch {
|
|
281
|
+
catch (fallbackError) {
|
|
282
|
+
this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart, fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
|
|
267
283
|
throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
268
284
|
}
|
|
269
285
|
}
|
|
@@ -276,6 +292,7 @@ export class LLMClient {
|
|
|
276
292
|
if (!this.checkRateLimit()) {
|
|
277
293
|
throw new Error('Rate limit exceeded');
|
|
278
294
|
}
|
|
295
|
+
const callStart = Date.now();
|
|
279
296
|
try {
|
|
280
297
|
// Circuit breaker check — streaming can't use execute() wrapper
|
|
281
298
|
// so we check state and record outcomes manually
|
|
@@ -283,18 +300,45 @@ export class LLMClient {
|
|
|
283
300
|
throw new CircuitBreakerOpenError(`llm-${this.config.provider}`);
|
|
284
301
|
}
|
|
285
302
|
this.recordRequest();
|
|
286
|
-
|
|
303
|
+
// Sample health latency at time-to-first-chunk, not full stream duration:
|
|
304
|
+
// a pull-based generator's total time includes downstream consumer speed,
|
|
305
|
+
// which would falsely mark a healthy provider degraded.
|
|
306
|
+
let sampled = false;
|
|
307
|
+
for await (const chunk of this.provider.stream(messages, options)) {
|
|
308
|
+
if (!sampled) {
|
|
309
|
+
this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
|
|
310
|
+
sampled = true;
|
|
311
|
+
}
|
|
312
|
+
yield chunk;
|
|
313
|
+
}
|
|
314
|
+
if (!sampled) {
|
|
315
|
+
// Stream produced zero chunks but the call still succeeded.
|
|
316
|
+
this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
|
|
317
|
+
}
|
|
287
318
|
}
|
|
288
319
|
catch (error) {
|
|
320
|
+
this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart, error instanceof Error ? error : new Error(String(error)));
|
|
289
321
|
// Try fallback if available
|
|
290
|
-
if (this.fallbackProvider) {
|
|
322
|
+
if (this.fallbackProvider && this.config.fallbackProvider) {
|
|
323
|
+
const fallbackStart = Date.now();
|
|
291
324
|
try {
|
|
292
325
|
if (this.fallbackCircuitBreaker?.isOpen()) {
|
|
293
326
|
throw new CircuitBreakerOpenError(`llm-${this.config.fallbackProvider}`);
|
|
294
327
|
}
|
|
295
|
-
|
|
328
|
+
let fallbackSampled = false;
|
|
329
|
+
for await (const chunk of this.fallbackProvider.stream(messages, options)) {
|
|
330
|
+
if (!fallbackSampled) {
|
|
331
|
+
this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
|
|
332
|
+
fallbackSampled = true;
|
|
333
|
+
}
|
|
334
|
+
yield chunk;
|
|
335
|
+
}
|
|
336
|
+
if (!fallbackSampled) {
|
|
337
|
+
this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
|
|
338
|
+
}
|
|
296
339
|
}
|
|
297
|
-
catch {
|
|
340
|
+
catch (fallbackError) {
|
|
341
|
+
this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart, fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
|
|
298
342
|
throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
299
343
|
}
|
|
300
344
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "AI runtime for agent-driven products — agents, memory, LLM providers (Inference Snaps, Ollama, OpenAI-compatible), tools, and orchestration. Anthropic-SDK-free.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -23,22 +23,22 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "FSL-1.1-MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"lru-cache": "^11.5.
|
|
26
|
+
"lru-cache": "^11.5.1",
|
|
27
27
|
"zod": "^4.4.3",
|
|
28
|
-
"@revealui/contracts": "0.6.
|
|
29
|
-
"@revealui/core": "0.
|
|
30
|
-
"@revealui/db": "0.7.
|
|
28
|
+
"@revealui/contracts": "0.6.1",
|
|
29
|
+
"@revealui/core": "0.10.1",
|
|
30
|
+
"@revealui/db": "0.7.1",
|
|
31
31
|
"@revealui/resilience": "0.2.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@electric-sql/pglite": "^0.
|
|
34
|
+
"@electric-sql/pglite": "^0.5.1",
|
|
35
35
|
"@testing-library/react": "^16.3.2",
|
|
36
|
-
"@vitest/coverage-v8": "^4.1.
|
|
36
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
37
37
|
"jsdom": "29.1.1",
|
|
38
|
-
"react": "^19.2.
|
|
39
|
-
"react-dom": "^19.2.
|
|
38
|
+
"react": "^19.2.7",
|
|
39
|
+
"react-dom": "^19.2.7",
|
|
40
40
|
"typescript": "^6.0.3",
|
|
41
|
-
"vitest": "^4.1.
|
|
41
|
+
"vitest": "^4.1.8",
|
|
42
42
|
"@revealui/dev": "0.1.0"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|