@retrivora-ai/rag-engine 1.8.0 → 1.8.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/DocumentChunker-Dh9TvmGG.d.mts +45 -0
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
- package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
- package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
- package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
- package/dist/chunk-ICKRMZQK.mjs +76 -0
- package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
- package/dist/chunk-OZFBG4BA.mjs +291 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1269 -656
- package/dist/handlers/index.mjs +4 -1
- package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
- package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
- package/dist/index.d.mts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +1354 -826
- package/dist/index.mjs +1284 -795
- package/dist/server.d.mts +47 -27
- package/dist/server.d.ts +47 -27
- package/dist/server.js +1417 -829
- package/dist/server.mjs +164 -176
- package/package.json +6 -2
- package/src/app/api/upload/route.ts +4 -0
- package/src/app/constants.tsx +2 -2
- package/src/app/page.tsx +12 -322
- package/src/components/AmbientBackground.tsx +29 -0
- package/src/components/ArchitectureCard.tsx +17 -0
- package/src/components/ArchitectureCardsSection.tsx +15 -0
- package/src/components/ChatWindow.tsx +32 -0
- package/src/components/CodeViewer.tsx +51 -0
- package/src/components/ConfigProvider.tsx +1 -0
- package/src/components/DocViewer.tsx +37 -0
- package/src/components/DocumentUpload.tsx +44 -1
- package/src/components/Documentation.tsx +58 -0
- package/src/components/DynamicChart.tsx +27 -2
- package/src/components/Hero.tsx +59 -0
- package/src/components/HourglassLoader.tsx +87 -0
- package/src/components/Lifecycle.tsx +37 -0
- package/src/components/MarkdownComponents.tsx +140 -0
- package/src/components/MessageBubble.tsx +124 -904
- package/src/components/Navbar.tsx +55 -0
- package/src/components/ObservabilityPanel.tsx +374 -0
- package/src/components/ProductCard.tsx +5 -3
- package/src/components/UIDispatcher.tsx +344 -0
- package/src/components/VisualizationRenderer.tsx +372 -250
- package/src/config/RagConfig.ts +5 -0
- package/src/config/serverConfig.ts +3 -1
- package/src/core/Pipeline.ts +240 -271
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/VectorPlugin.ts +9 -0
- package/src/handlers/index.ts +91 -15
- package/src/hooks/useRagChat.ts +21 -11
- package/src/index.ts +9 -1
- package/src/llm/LLMFactory.ts +54 -2
- package/src/llm/providers/AnthropicProvider.ts +12 -8
- package/src/llm/providers/GeminiProvider.ts +188 -143
- package/src/llm/providers/OllamaProvider.ts +7 -3
- package/src/llm/providers/OpenAIProvider.ts +12 -8
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
- package/src/types/chat.ts +8 -0
- package/src/types/index.ts +132 -0
- package/src/types/props.ts +9 -1
- package/src/utils/ProductExtractor.ts +347 -0
- package/src/utils/SchemaMapper.ts +129 -0
- package/src/utils/UITransformer.ts +470 -209
- package/src/utils/synonyms.ts +78 -0
- package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
- package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
- package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
- package/dist/chunk-FLOSGE6A.mjs +0 -202
|
@@ -77,8 +77,8 @@ export class ProviderRegistry {
|
|
|
77
77
|
}
|
|
78
78
|
case 'pgvector':
|
|
79
79
|
case 'postgresql': {
|
|
80
|
-
const {
|
|
81
|
-
return
|
|
80
|
+
const { MultiTablePostgresProvider } = await import('../providers/vectordb/MultiTablePostgresProvider');
|
|
81
|
+
return MultiTablePostgresProvider as unknown as VectorProviderClass;
|
|
82
82
|
}
|
|
83
83
|
case 'mongodb': {
|
|
84
84
|
const { MongoDBProvider } = await import('../providers/vectordb/MongoDBProvider');
|
package/src/core/VectorPlugin.ts
CHANGED
|
@@ -38,6 +38,15 @@ export class VectorPlugin {
|
|
|
38
38
|
return this.config;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Get the initialized LLM provider (available after the first request).
|
|
43
|
+
* Used by handlers to pass to UITransformer.analyzeAndDecide() for
|
|
44
|
+
* LLM-driven visualization decisions.
|
|
45
|
+
*/
|
|
46
|
+
getLLMProvider() {
|
|
47
|
+
return this.pipeline.getLLMProvider();
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
/**
|
|
42
51
|
* Perform pre-flight health checks on all configured providers.
|
|
43
52
|
* Useful to verify connectivity before running operations.
|
package/src/handlers/index.ts
CHANGED
|
@@ -27,7 +27,12 @@ function sseMetaFrame(meta: unknown): string {
|
|
|
27
27
|
|
|
28
28
|
/** Encode the UI transformation result as an SSE frame. */
|
|
29
29
|
function sseUIFrame(uiTransformation: unknown): string {
|
|
30
|
-
return `data: ${JSON.stringify({ type: 'ui_transformation',
|
|
30
|
+
return `data: ${JSON.stringify({ type: 'ui_transformation', data: uiTransformation })}\n\n`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Encode the observability trace as an SSE frame. */
|
|
34
|
+
function sseObservabilityFrame(trace: unknown): string {
|
|
35
|
+
return `data: ${JSON.stringify({ type: 'observability', data: trace })}\n\n`;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
/** Encode a stream error as an SSE frame. */
|
|
@@ -152,15 +157,32 @@ export function createStreamHandler(configOrPlugin?: Partial<RagConfig> | Vector
|
|
|
152
157
|
// Retrieval metadata object — always the final frame
|
|
153
158
|
enqueue(sseMetaFrame(chunk));
|
|
154
159
|
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
|
|
160
|
+
// ── LLM-driven visualization decision ─────────────────────────
|
|
161
|
+
const responseChunk = chunk as ChatResponse;
|
|
162
|
+
const sources = responseChunk?.sources || [];
|
|
163
|
+
|
|
164
|
+
// Emit observability trace if present
|
|
165
|
+
if (responseChunk?.trace) {
|
|
166
|
+
enqueue(sseObservabilityFrame(responseChunk.trace));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (sources.length > 0) {
|
|
158
170
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
171
|
+
const llmProvider = plugin.getLLMProvider?.();
|
|
172
|
+
const uiTransformation = responseChunk?.ui_transformation ??
|
|
173
|
+
(llmProvider
|
|
174
|
+
? await UITransformer.analyzeAndDecide(message, sources, llmProvider)
|
|
175
|
+
: UITransformer.transform(message, sources, plugin.getConfig()));
|
|
176
|
+
|
|
177
|
+
if (uiTransformation) {
|
|
178
|
+
enqueue(sseUIFrame(uiTransformation));
|
|
179
|
+
}
|
|
161
180
|
} catch (transformError) {
|
|
162
181
|
console.warn('[createStreamHandler] UI transformation warning:', transformError);
|
|
163
|
-
|
|
182
|
+
try {
|
|
183
|
+
const fallback = UITransformer.transform(message, sources, plugin.getConfig());
|
|
184
|
+
if (fallback) enqueue(sseUIFrame(fallback));
|
|
185
|
+
} catch { /* silent */ }
|
|
164
186
|
}
|
|
165
187
|
}
|
|
166
188
|
}
|
|
@@ -249,15 +271,68 @@ export function createUploadHandler(configOrPlugin?: Partial<RagConfig> | Vector
|
|
|
249
271
|
const formData = await req.formData();
|
|
250
272
|
const files = formData.getAll('files') as File[];
|
|
251
273
|
const namespace = (formData.get('namespace') as string) || undefined;
|
|
274
|
+
const dimensionRaw = formData.get('dimension');
|
|
275
|
+
const dimension = dimensionRaw ? parseInt(dimensionRaw as string, 10) : undefined;
|
|
252
276
|
|
|
253
277
|
if (!files || files.length === 0) {
|
|
254
278
|
return NextResponse.json({ error: 'No files provided' }, { status: 400 });
|
|
255
279
|
}
|
|
256
280
|
|
|
257
|
-
const documents =
|
|
258
|
-
|
|
281
|
+
const documents: { docId: string; content: string; metadata?: Record<string, unknown> }[] = [];
|
|
282
|
+
|
|
283
|
+
for (const file of files) {
|
|
284
|
+
if (file.name.toLowerCase().endsWith('.csv') || file.type === 'text/csv') {
|
|
285
|
+
const text = await file.text();
|
|
286
|
+
const Papa = await import('papaparse');
|
|
287
|
+
const parsed = Papa.default.parse(text, { header: true, skipEmptyLines: true });
|
|
288
|
+
|
|
289
|
+
if (parsed.data && parsed.data.length > 0) {
|
|
290
|
+
let i = 0;
|
|
291
|
+
let lastRowData: Record<string, string> | null = null;
|
|
292
|
+
|
|
293
|
+
for (const row of parsed.data) {
|
|
294
|
+
i++;
|
|
295
|
+
const rowData = row as Record<string, string>;
|
|
296
|
+
|
|
297
|
+
// Dynamically determine the grouping key (typically the first column like ID or Handle)
|
|
298
|
+
const groupingKey = Object.keys(rowData)[0];
|
|
299
|
+
|
|
300
|
+
// Forward-fill ALL empty columns for variants belonging to the same group
|
|
301
|
+
if (lastRowData && groupingKey && rowData[groupingKey] && rowData[groupingKey] === lastRowData[groupingKey]) {
|
|
302
|
+
for (const key of Object.keys(rowData)) {
|
|
303
|
+
// Forward-fill if the current row lacks the field but the previous row has it
|
|
304
|
+
if (!rowData[key] && lastRowData[key]) {
|
|
305
|
+
rowData[key] = lastRowData[key];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Save a copy of the filled rowData for the next iteration
|
|
310
|
+
lastRowData = { ...rowData };
|
|
311
|
+
|
|
312
|
+
const contentParts: string[] = [];
|
|
313
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
314
|
+
if (key && val) {
|
|
315
|
+
contentParts.push(`${key}: ${val}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
documents.push({
|
|
320
|
+
docId: `${file.name}-row-${i}`,
|
|
321
|
+
content: contentParts.join(', '),
|
|
322
|
+
metadata: {
|
|
323
|
+
fileName: file.name,
|
|
324
|
+
fileSize: file.size,
|
|
325
|
+
fileType: file.type,
|
|
326
|
+
uploadedAt: new Date().toISOString(),
|
|
327
|
+
...(dimension ? { dimension } : {}),
|
|
328
|
+
...rowData
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
259
334
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
260
|
-
|
|
335
|
+
documents.push({
|
|
261
336
|
docId: file.name,
|
|
262
337
|
content,
|
|
263
338
|
metadata: {
|
|
@@ -265,10 +340,11 @@ export function createUploadHandler(configOrPlugin?: Partial<RagConfig> | Vector
|
|
|
265
340
|
fileSize: file.size,
|
|
266
341
|
fileType: file.type,
|
|
267
342
|
uploadedAt: new Date().toISOString(),
|
|
343
|
+
...(dimension ? { dimension } : {}),
|
|
268
344
|
},
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
272
348
|
|
|
273
349
|
const results = await plugin.ingest(documents, namespace);
|
|
274
350
|
return NextResponse.json({ message: 'Upload successful', results });
|
|
@@ -309,5 +385,5 @@ export function createSuggestionsHandler(configOrPlugin?: Partial<RagConfig> | V
|
|
|
309
385
|
};
|
|
310
386
|
}
|
|
311
387
|
|
|
312
|
-
// Re-export SSE
|
|
313
|
-
export { sseFrame, sseTextFrame, sseMetaFrame, sseErrorFrame, sseUIFrame };
|
|
388
|
+
// Re-export SSE helpers so host apps can use them in custom handlers
|
|
389
|
+
export { sseFrame, sseTextFrame, sseMetaFrame, sseErrorFrame, sseUIFrame, sseObservabilityFrame };
|
package/src/hooks/useRagChat.ts
CHANGED
|
@@ -19,16 +19,17 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
22
|
-
import { VectorMatch, RagMessage, UseRagChatOptions, UseRagChatReturn } from '../types';
|
|
22
|
+
import { VectorMatch, RagMessage, UseRagChatOptions, UseRagChatReturn, ObservabilityTrace } from '../types';
|
|
23
23
|
import { useStoredMessages } from './useStoredMessages';
|
|
24
24
|
|
|
25
25
|
// ─── SSE Frame Parser ──────────────────────────────────────────────────────────
|
|
26
26
|
|
|
27
|
-
interface SseTextFrame
|
|
28
|
-
interface SseMetaFrame
|
|
29
|
-
interface SseErrorFrame
|
|
30
|
-
interface SseUiFrame
|
|
31
|
-
|
|
27
|
+
interface SseTextFrame { type: 'text'; text: string }
|
|
28
|
+
interface SseMetaFrame { type: 'metadata'; sources?: VectorMatch[] }
|
|
29
|
+
interface SseErrorFrame { type: 'error'; error: string }
|
|
30
|
+
interface SseUiFrame { type: 'ui_transformation'; data: unknown }
|
|
31
|
+
interface SseObservabilityFrame { type: 'observability'; data: ObservabilityTrace }
|
|
32
|
+
type SseFrame = SseTextFrame | SseMetaFrame | SseErrorFrame | SseUiFrame | SseObservabilityFrame;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Parse all complete SSE frames from a raw chunk string.
|
|
@@ -126,6 +127,7 @@ export function useRagChat(
|
|
|
126
127
|
let assistantContent = '';
|
|
127
128
|
let sources: VectorMatch[] = [];
|
|
128
129
|
let uiTransformation: unknown = null;
|
|
130
|
+
let trace: ObservabilityTrace | undefined;
|
|
129
131
|
// Buffer for partial SSE frames that arrive split across reads
|
|
130
132
|
let buffer = '';
|
|
131
133
|
|
|
@@ -155,12 +157,14 @@ export function useRagChat(
|
|
|
155
157
|
buffer = buffer.slice(lastBoundary + 2);
|
|
156
158
|
|
|
157
159
|
for (const frame of parseSseChunk(complete)) {
|
|
158
|
-
if (frame.type === 'text') {
|
|
160
|
+
if (frame.type === 'text' && frame.text) {
|
|
159
161
|
assistantContent += frame.text;
|
|
160
162
|
} else if (frame.type === 'metadata') {
|
|
161
163
|
sources = frame.sources ?? [];
|
|
162
164
|
} else if (frame.type === 'ui_transformation') {
|
|
163
165
|
uiTransformation = frame.data;
|
|
166
|
+
} else if (frame.type === 'observability') {
|
|
167
|
+
trace = (frame as SseObservabilityFrame).data;
|
|
164
168
|
} else if (frame.type === 'error') {
|
|
165
169
|
setError(frame.error || 'Stream error');
|
|
166
170
|
}
|
|
@@ -184,20 +188,26 @@ export function useRagChat(
|
|
|
184
188
|
// Process any remaining buffered data
|
|
185
189
|
if (buffer.trim()) {
|
|
186
190
|
for (const frame of parseSseChunk(buffer)) {
|
|
187
|
-
if (frame.type === 'text') assistantContent += frame.text;
|
|
191
|
+
if (frame.type === 'text' && frame.text) assistantContent += frame.text;
|
|
188
192
|
else if (frame.type === 'metadata') sources = frame.sources ?? [];
|
|
193
|
+
else if (frame.type === 'observability') trace = (frame as SseObservabilityFrame).data;
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
|
|
192
|
-
|
|
197
|
+
// Final message with all accumulated data
|
|
198
|
+
const finalMsg: RagMessage = {
|
|
193
199
|
id: assistantMessageId,
|
|
194
200
|
role: 'assistant',
|
|
195
201
|
content: assistantContent,
|
|
196
202
|
sources: sources.length > 0 ? sources : undefined,
|
|
197
|
-
uiTransformation: uiTransformation
|
|
203
|
+
uiTransformation: uiTransformation ?? undefined,
|
|
204
|
+
trace,
|
|
198
205
|
createdAt: new Date().toISOString(),
|
|
199
206
|
};
|
|
200
|
-
|
|
207
|
+
setMessages((prev) =>
|
|
208
|
+
prev.map((msg) => (msg.id === assistantMessageId ? finalMsg : msg))
|
|
209
|
+
);
|
|
210
|
+
onReply?.(finalMsg);
|
|
201
211
|
} catch (err) {
|
|
202
212
|
const msg = err instanceof Error ? err.message : 'Something went wrong. Please try again.';
|
|
203
213
|
setError(msg);
|
package/src/index.ts
CHANGED
|
@@ -9,11 +9,15 @@ export { ChatWindow } from './components/ChatWindow';
|
|
|
9
9
|
export { DocumentUpload } from './components/DocumentUpload';
|
|
10
10
|
export { MessageBubble } from './components/MessageBubble';
|
|
11
11
|
export { SourceCard } from './components/SourceCard';
|
|
12
|
+
export { ObservabilityPanel } from './components/ObservabilityPanel';
|
|
12
13
|
export { ConfigProvider, useConfig } from './components/ConfigProvider';
|
|
13
14
|
|
|
14
15
|
// ── Headless Hooks ────────────────────────────────────────────
|
|
15
16
|
export { useRagChat } from './hooks/useRagChat';
|
|
16
17
|
|
|
18
|
+
// ── Utils ─────────────────────────────────────────────────────
|
|
19
|
+
export { addSynonyms } from './utils/synonyms';
|
|
20
|
+
|
|
17
21
|
// ── Types (Interfaces/Types only, no Node.js deps) ─────────────
|
|
18
22
|
export type { RagConfig, VectorDBConfig, VectorDBProvider, LLMConfig, LLMProvider, EmbeddingConfig, EmbeddingProvider, UIConfig, RAGConfig } from './config/RagConfig';
|
|
19
23
|
export type { ILLMProvider, EmbedOptions } from './llm/ILLMProvider';
|
|
@@ -35,5 +39,9 @@ export type {
|
|
|
35
39
|
ProductCardProps,
|
|
36
40
|
ProductCarouselProps,
|
|
37
41
|
DocumentUploadProps,
|
|
38
|
-
SourceCardProps
|
|
42
|
+
SourceCardProps,
|
|
43
|
+
ObservabilityTrace,
|
|
44
|
+
LatencyBreakdown,
|
|
45
|
+
TokenUsage,
|
|
46
|
+
RetrievedChunk
|
|
39
47
|
} from './types';
|
package/src/llm/LLMFactory.ts
CHANGED
|
@@ -12,10 +12,50 @@ interface LLMProviderStatic {
|
|
|
12
12
|
getHealthChecker?: () => IProviderHealthChecker;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/** Registry for custom provider factories registered at runtime. */
|
|
16
|
+
const customProviders = new Map<string, (config: LLMConfig) => ILLMProvider>();
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* LLMFactory — instantiates the correct ILLMProvider based on llmConfig.provider.
|
|
17
20
|
*/
|
|
18
21
|
export class LLMFactory {
|
|
22
|
+
/**
|
|
23
|
+
* Register a custom LLM provider factory at runtime.
|
|
24
|
+
*
|
|
25
|
+
* Use this to add support for any LLM backend (Azure OpenAI, Cohere, Mistral,
|
|
26
|
+
* Bedrock, etc.) without modifying this library's source code.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // In your Next.js app initialization:
|
|
30
|
+
* import { LLMFactory } from '@retrivora-ai/rag-engine/server';
|
|
31
|
+
* import { MyCustomProvider } from './providers/MyCustomProvider';
|
|
32
|
+
*
|
|
33
|
+
* LLMFactory.register('my-provider', (config) => new MyCustomProvider(config));
|
|
34
|
+
*
|
|
35
|
+
* // Then set in your .env.local:
|
|
36
|
+
* // LLM_PROVIDER=my-provider
|
|
37
|
+
*/
|
|
38
|
+
static register(name: string, factory: (config: LLMConfig) => ILLMProvider): void {
|
|
39
|
+
customProviders.set(name.toLowerCase(), factory);
|
|
40
|
+
console.log(`[LLMFactory] Registered custom provider: "${name}"`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Unregister a previously registered custom provider.
|
|
45
|
+
*/
|
|
46
|
+
static unregister(name: string): void {
|
|
47
|
+
customProviders.delete(name.toLowerCase());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* List all registered provider names (built-in + custom).
|
|
52
|
+
*/
|
|
53
|
+
static listProviders(): string[] {
|
|
54
|
+
return [
|
|
55
|
+
'openai', 'anthropic', 'ollama', 'gemini', 'rest', 'universal_rest', 'custom',
|
|
56
|
+
...Array.from(customProviders.keys()),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
19
59
|
static create(llmConfig: LLMConfig, embeddingConfig?: EmbeddingConfig): ILLMProvider {
|
|
20
60
|
switch (llmConfig.provider) {
|
|
21
61
|
case 'openai':
|
|
@@ -30,11 +70,23 @@ export class LLMFactory {
|
|
|
30
70
|
case 'universal_rest':
|
|
31
71
|
case 'custom':
|
|
32
72
|
return new UniversalLLMAdapter(llmConfig);
|
|
33
|
-
default:
|
|
73
|
+
default: {
|
|
74
|
+
// Check the runtime custom provider registry first
|
|
75
|
+
const providerName = String(llmConfig.provider ?? '').toLowerCase();
|
|
76
|
+
const customFactory = customProviders.get(providerName);
|
|
77
|
+
if (customFactory) {
|
|
78
|
+
return customFactory(llmConfig);
|
|
79
|
+
}
|
|
80
|
+
// Fall back to UniversalLLMAdapter if a baseUrl is provided
|
|
34
81
|
if (llmConfig.baseUrl || (llmConfig.options as Record<string, unknown>)?.baseUrl) {
|
|
35
82
|
return new UniversalLLMAdapter(llmConfig);
|
|
36
83
|
}
|
|
37
|
-
throw new Error(
|
|
84
|
+
throw new Error(
|
|
85
|
+
`[LLMFactory] Unknown provider "${llmConfig.provider}". ` +
|
|
86
|
+
`Built-in providers: ${LLMFactory.listProviders().join(', ')}. ` +
|
|
87
|
+
`Register a custom provider with LLMFactory.register().`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
38
90
|
}
|
|
39
91
|
}
|
|
40
92
|
|
|
@@ -75,13 +75,15 @@ export class AnthropicProvider implements ILLMProvider {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
async chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string> {
|
|
78
|
-
const systemPrompt
|
|
78
|
+
const resolvedPrompt = options?.systemPrompt ??
|
|
79
79
|
this.llmConfig.systemPrompt ??
|
|
80
80
|
`You are a helpful assistant. Use the context below to answer the user's question accurately.\n\nContext:\n${context}`;
|
|
81
81
|
|
|
82
|
-
const system =
|
|
83
|
-
?
|
|
84
|
-
:
|
|
82
|
+
const system = resolvedPrompt.includes('{{context}}')
|
|
83
|
+
? resolvedPrompt.replace('{{context}}', context)
|
|
84
|
+
: options?.systemPrompt
|
|
85
|
+
? resolvedPrompt // per-call: context is already in the user message
|
|
86
|
+
: `${resolvedPrompt}\n\nContext:\n${context}`;
|
|
85
87
|
|
|
86
88
|
const anthropicMessages: Anthropic.MessageParam[] = messages.map((m) => ({
|
|
87
89
|
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
@@ -100,13 +102,15 @@ export class AnthropicProvider implements ILLMProvider {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
async *chatStream(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string> {
|
|
103
|
-
const systemPrompt
|
|
105
|
+
const resolvedPrompt = options?.systemPrompt ??
|
|
104
106
|
this.llmConfig.systemPrompt ??
|
|
105
107
|
`You are a helpful assistant. Use the context below to answer the user's question accurately.\n\nContext:\n${context}`;
|
|
106
108
|
|
|
107
|
-
const system =
|
|
108
|
-
?
|
|
109
|
-
:
|
|
109
|
+
const system = resolvedPrompt.includes('{{context}}')
|
|
110
|
+
? resolvedPrompt.replace('{{context}}', context)
|
|
111
|
+
: options?.systemPrompt
|
|
112
|
+
? resolvedPrompt
|
|
113
|
+
: `${resolvedPrompt}\n\nContext:\n${context}`;
|
|
110
114
|
|
|
111
115
|
const anthropicMessages: Anthropic.MessageParam[] = messages.map((m) => ({
|
|
112
116
|
role: m.role === 'assistant' ? 'assistant' : 'user',
|