@retrivora-ai/rag-engine 1.7.1 → 1.7.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/dist/{chunk-OHXRQTQH.mjs → chunk-M32ZXLTT.mjs} +399 -20
- package/dist/handlers/index.d.mts +1 -1
- package/dist/handlers/index.d.ts +1 -1
- package/dist/handlers/index.js +402 -22
- package/dist/handlers/index.mjs +5 -3
- package/dist/{index-kUXnRvuI.d.mts → index-BejNscWZ.d.mts} +4 -2
- package/dist/{index-B67KQ9NN.d.ts → index-CbkMJvu5.d.ts} +4 -2
- package/dist/index.js +37 -46
- package/dist/index.mjs +37 -46
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +400 -20
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +54 -80
- package/src/components/VisualizationRenderer.tsx +341 -0
- package/src/core/Pipeline.ts +39 -19
- package/src/handlers/index.ts +21 -3
- package/src/hooks/useUITransform.ts +54 -0
- package/src/utils/UITransformer.ts +535 -0
package/src/core/Pipeline.ts
CHANGED
|
@@ -101,16 +101,15 @@ export class Pipeline {
|
|
|
101
101
|
|
|
102
102
|
// Augment system prompt with a Universal Visualization Protocol
|
|
103
103
|
// We use a unique marker to ensure this is only injected once but is ALWAYS present.
|
|
104
|
-
const CHART_MARKER = '<!--
|
|
104
|
+
const CHART_MARKER = '<!-- UI_PROTOCOL_V6 -->';
|
|
105
105
|
const chartInstruction = `\n\n${CHART_MARKER}
|
|
106
|
-
### UNIVERSAL UI PROTOCOL
|
|
107
|
-
|
|
108
|
-
NEVER output naked JSON. If you provide a visualization, do not also provide a markdown table of the same data unless explicitly asked.
|
|
106
|
+
### UNIVERSAL UI PROTOCOL
|
|
107
|
+
When the user asks for a visual representation, comparison, distribution, breakdown, table, list, catalog, products, carousel, stock view, or category summary, you MUST include exactly one \`\`\`ui\`\`\` block.
|
|
109
108
|
|
|
110
109
|
1. VIEW SELECTION
|
|
111
|
-
- Use "carousel" for browsing products, items, or
|
|
112
|
-
- Use "chart" ONLY for numerical aggregations
|
|
113
|
-
- Use "table" for technical
|
|
110
|
+
- Use "carousel" for browsing specific products, items, or a catalog. This is the DEFAULT for "show me", "what are", or "find me" requests for a specific category (e.g. "beauty").
|
|
111
|
+
- Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
|
|
112
|
+
- Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
|
|
114
113
|
|
|
115
114
|
2. REQUIRED JSON SHAPE
|
|
116
115
|
{
|
|
@@ -118,31 +117,52 @@ NEVER output naked JSON. If you provide a visualization, do not also provide a m
|
|
|
118
117
|
"title": "Short heading",
|
|
119
118
|
"description": "One sentence describing the view",
|
|
120
119
|
"chartType": "pie" | "bar" | "line", // Required if view is "chart"
|
|
121
|
-
"
|
|
120
|
+
"xAxisKey": "label",
|
|
121
|
+
"dataKeys": ["value"],
|
|
122
|
+
"columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
|
|
123
|
+
"insights": ["Short takeaway 1", "Short takeaway 2"],
|
|
124
|
+
"data": []
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
3. DATA RULES
|
|
125
|
-
- Build the UI payload from retrieved context
|
|
126
|
-
- For CAROUSEL: "data"
|
|
127
|
-
-
|
|
128
|
+
- Build the UI payload from retrieved context only. Do not invent products or values.
|
|
129
|
+
- For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
|
|
130
|
+
- For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
|
|
131
|
+
- If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
|
|
128
132
|
|
|
129
133
|
4. FORMAT RULES
|
|
130
|
-
-
|
|
131
|
-
-
|
|
134
|
+
- Return valid JSON inside the \`\`\`ui\`\`\` block.
|
|
135
|
+
- Put natural language explanation before or after the block, not inside.
|
|
132
136
|
|
|
133
|
-
5.
|
|
134
|
-
User: "
|
|
135
|
-
Assistant:
|
|
137
|
+
5. EXAMPLES
|
|
138
|
+
User: "show me beauty products"
|
|
139
|
+
Assistant:
|
|
136
140
|
\`\`\`ui
|
|
137
141
|
{
|
|
138
142
|
"view": "carousel",
|
|
139
143
|
"title": "Beauty Products",
|
|
140
|
-
"description": "
|
|
144
|
+
"description": "Browse our latest skincare and makeup collection.",
|
|
141
145
|
"data": [
|
|
142
|
-
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "
|
|
146
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
|
|
147
|
+
{ "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
|
|
143
148
|
]
|
|
144
149
|
}
|
|
145
150
|
\`\`\`
|
|
151
|
+
|
|
152
|
+
User: "breakdown of products by category"
|
|
153
|
+
Assistant:
|
|
154
|
+
\`\`\`ui
|
|
155
|
+
{
|
|
156
|
+
"view": "chart",
|
|
157
|
+
"title": "Category Breakdown",
|
|
158
|
+
"chartType": "pie",
|
|
159
|
+
"xAxisKey": "label",
|
|
160
|
+
"dataKeys": ["value"],
|
|
161
|
+
"data": [
|
|
162
|
+
{ "label": "Beauty", "value": 15 },
|
|
163
|
+
{ "label": "Fragrances", "value": 8 }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
146
166
|
\`\`\``;
|
|
147
167
|
|
|
148
168
|
if (!this.config.llm.systemPrompt?.includes(CHART_MARKER)) {
|
|
@@ -387,7 +407,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
387
407
|
|
|
388
408
|
// 4. Context Augmentation
|
|
389
409
|
let context = sources.length
|
|
390
|
-
? sources.map((m, i) => `[Source ${i + 1}]\
|
|
410
|
+
? sources.map((m, i) => `[Source ${i + 1}]\n${m.content}`).join('\n\n---\n\n')
|
|
391
411
|
: 'No relevant context found.';
|
|
392
412
|
|
|
393
413
|
if (graphData && graphData.nodes.length > 0) {
|
package/src/handlers/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { VectorPlugin } from '../core/VectorPlugin';
|
|
3
3
|
import { RagConfig } from '../config/RagConfig';
|
|
4
|
-
import { ChatMessage } from '../types';
|
|
4
|
+
import { ChatMessage, ChatResponse } from '../types';
|
|
5
5
|
import { DocumentParser } from '../utils/DocumentParser';
|
|
6
|
+
import { UITransformer } from '../utils/UITransformer';
|
|
6
7
|
|
|
7
8
|
// ─── SSE helpers ──────────────────────────────────────────────────────────────
|
|
8
9
|
|
|
@@ -19,11 +20,16 @@ function sseTextFrame(text: string): string {
|
|
|
19
20
|
return `data: ${JSON.stringify({ type: 'text', text })}\n\n`;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
/** Encode the retrieval metadata as an SSE frame. */
|
|
23
|
+
/** Encode the retrieval metadata with UI transformation as an SSE frame. */
|
|
23
24
|
function sseMetaFrame(meta: unknown): string {
|
|
24
25
|
return `data: ${JSON.stringify({ type: 'metadata', ...((meta as object) ?? {}) })}\n\n`;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
/** Encode the UI transformation result as an SSE frame. */
|
|
29
|
+
function sseUIFrame(uiTransformation: unknown): string {
|
|
30
|
+
return `data: ${JSON.stringify({ type: 'ui_transformation', ...((uiTransformation as object) ?? {}) })}\n\n`;
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
/** Encode a stream error as an SSE frame. */
|
|
28
34
|
function sseErrorFrame(message: string): string {
|
|
29
35
|
return `data: ${JSON.stringify({ type: 'error', error: message })}\n\n`;
|
|
@@ -145,6 +151,18 @@ export function createStreamHandler(configOrPlugin?: Partial<RagConfig> | Vector
|
|
|
145
151
|
} else {
|
|
146
152
|
// Retrieval metadata object — always the final frame
|
|
147
153
|
enqueue(sseMetaFrame(chunk));
|
|
154
|
+
|
|
155
|
+
// Transform retrieved sources to UI representation
|
|
156
|
+
const sources = (chunk as ChatResponse)?.sources || [];
|
|
157
|
+
if (sources && sources.length > 0) {
|
|
158
|
+
try {
|
|
159
|
+
const uiTransformation = UITransformer.transform(message, sources);
|
|
160
|
+
enqueue(sseUIFrame(uiTransformation));
|
|
161
|
+
} catch (transformError) {
|
|
162
|
+
console.warn('[createStreamHandler] UI transformation warning:', transformError);
|
|
163
|
+
// Don't fail the stream if transformation fails, just skip it
|
|
164
|
+
}
|
|
165
|
+
}
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
168
|
} catch (streamError) {
|
|
@@ -292,4 +310,4 @@ export function createSuggestionsHandler(configOrPlugin?: Partial<RagConfig> | V
|
|
|
292
310
|
}
|
|
293
311
|
|
|
294
312
|
// Re-export SSE helper so host apps can use it in custom handlers
|
|
295
|
-
export { sseFrame, sseTextFrame, sseMetaFrame, sseErrorFrame };
|
|
313
|
+
export { sseFrame, sseTextFrame, sseMetaFrame, sseErrorFrame, sseUIFrame };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { UITransformer, UITransformationResponse } from '@/utils/UITransformer';
|
|
3
|
+
import { VectorMatch } from '@/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* useUITransform hook — Transforms vector database results into UI components
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const { transform, result, loading } = useUITransform();
|
|
10
|
+
*
|
|
11
|
+
* const handleSearch = async (query: string) => {
|
|
12
|
+
* const results = await fetchSearchResults(query);
|
|
13
|
+
* transform(query, results);
|
|
14
|
+
* };
|
|
15
|
+
*
|
|
16
|
+
* return (
|
|
17
|
+
* <>
|
|
18
|
+
* {result && <DynamicChart data={result} />}
|
|
19
|
+
* </>
|
|
20
|
+
* );
|
|
21
|
+
*/
|
|
22
|
+
export function useUITransform() {
|
|
23
|
+
const [result, setResult] = useState<UITransformationResponse | null>(null);
|
|
24
|
+
const [loading, setLoading] = useState(false);
|
|
25
|
+
const [error, setError] = useState<string | null>(null);
|
|
26
|
+
|
|
27
|
+
const transform = useCallback(
|
|
28
|
+
(query: string, data: VectorMatch[]) => {
|
|
29
|
+
try {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
setError(null);
|
|
32
|
+
|
|
33
|
+
const transformed = UITransformer.transform(query, data);
|
|
34
|
+
setResult(transformed);
|
|
35
|
+
|
|
36
|
+
return transformed;
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const message = err instanceof Error ? err.message : 'Transformation error';
|
|
39
|
+
setError(message);
|
|
40
|
+
return null;
|
|
41
|
+
} finally {
|
|
42
|
+
setLoading(false);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const reset = useCallback(() => {
|
|
49
|
+
setResult(null);
|
|
50
|
+
setError(null);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
return { transform, result, loading, error, reset };
|
|
54
|
+
}
|