@retrivora-ai/rag-engine 1.0.4 → 1.0.5
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/{RagConfig-DRJO4hGU.d.mts → RagConfig-BEmz4hVP.d.mts} +58 -1
- package/dist/{RagConfig-DRJO4hGU.d.ts → RagConfig-BEmz4hVP.d.ts} +58 -1
- package/dist/{chunk-6MLZHQZT.mjs → chunk-MWL4AGQI.mjs} +189 -98
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +191 -100
- package/dist/handlers/index.mjs +11 -3
- package/dist/index-CSfaCeux.d.ts +206 -0
- package/dist/index-DFSy0CG6.d.mts +206 -0
- package/dist/index.d.mts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +103 -89
- package/dist/index.mjs +91 -95
- package/dist/server.d.mts +45 -97
- package/dist/server.d.ts +45 -97
- package/dist/server.js +271 -215
- package/dist/server.mjs +78 -167
- package/package.json +12 -10
- package/src/components/ChatWindow.tsx +2 -2
- package/src/components/ConfigProvider.tsx +2 -2
- package/src/components/MessageBubble.tsx +2 -2
- package/src/components/SourceCard.tsx +1 -1
- package/src/components/ThemeToggle.tsx +1 -1
- package/src/config/ConfigBuilder.ts +86 -211
- package/src/config/uiConstants.ts +23 -0
- package/src/core/ConfigResolver.ts +57 -29
- package/src/core/LangChainAgent.ts +73 -40
- package/src/core/Pipeline.ts +63 -7
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/QueryProcessor.ts +9 -8
- package/src/handlers/index.ts +138 -49
- package/src/hooks/useRagChat.ts +71 -32
- package/src/server.ts +12 -2
- package/dist/DocumentChunker-C-sCZPhi.d.mts +0 -102
- package/dist/DocumentChunker-C-sCZPhi.d.ts +0 -102
- package/dist/index-B2mutkgp.d.ts +0 -116
- package/dist/index-Bjy0es5a.d.mts +0 -116
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocumentChunker — splits long text into overlapping chunks
|
|
3
|
+
* suitable for vector embedding and retrieval.
|
|
4
|
+
*
|
|
5
|
+
* Strategy: sentence-aware sliding window
|
|
6
|
+
* 1. Split on sentence boundaries
|
|
7
|
+
* 2. Accumulate sentences into chunks up to `chunkSize` characters
|
|
8
|
+
* 3. Carry over `chunkOverlap` characters from the previous chunk
|
|
9
|
+
*/
|
|
10
|
+
interface Chunk {
|
|
11
|
+
id: string;
|
|
12
|
+
content: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface ChunkOptions {
|
|
16
|
+
/** Target chunk size in characters (default 1000) */
|
|
17
|
+
chunkSize?: number;
|
|
18
|
+
/** Overlap between consecutive chunks in characters (default 200) */
|
|
19
|
+
chunkOverlap?: number;
|
|
20
|
+
/** Source document identifier used as ID prefix */
|
|
21
|
+
docId?: string | number;
|
|
22
|
+
/** Extra metadata to attach to every chunk */
|
|
23
|
+
metadata?: Record<string, unknown>;
|
|
24
|
+
/** Characters used to split text, in order of priority */
|
|
25
|
+
separators?: string[];
|
|
26
|
+
}
|
|
27
|
+
declare class DocumentChunker {
|
|
28
|
+
private readonly chunkSize;
|
|
29
|
+
private readonly chunkOverlap;
|
|
30
|
+
private readonly separators;
|
|
31
|
+
constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
|
|
32
|
+
/**
|
|
33
|
+
* Split a single text string into overlapping chunks using a recursive strategy.
|
|
34
|
+
* Preserves structural boundaries (Markdown headers) where possible.
|
|
35
|
+
*/
|
|
36
|
+
chunk(text: string, options?: ChunkOptions): Chunk[];
|
|
37
|
+
private recursiveSplit;
|
|
38
|
+
chunkMany(documents: Array<{
|
|
39
|
+
content: string;
|
|
40
|
+
docId?: string | number;
|
|
41
|
+
metadata?: Record<string, unknown>;
|
|
42
|
+
}>): Chunk[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { type Chunk as C, DocumentChunker as D, type ChunkOptions as a };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocumentChunker — splits long text into overlapping chunks
|
|
3
|
+
* suitable for vector embedding and retrieval.
|
|
4
|
+
*
|
|
5
|
+
* Strategy: sentence-aware sliding window
|
|
6
|
+
* 1. Split on sentence boundaries
|
|
7
|
+
* 2. Accumulate sentences into chunks up to `chunkSize` characters
|
|
8
|
+
* 3. Carry over `chunkOverlap` characters from the previous chunk
|
|
9
|
+
*/
|
|
10
|
+
interface Chunk {
|
|
11
|
+
id: string;
|
|
12
|
+
content: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface ChunkOptions {
|
|
16
|
+
/** Target chunk size in characters (default 1000) */
|
|
17
|
+
chunkSize?: number;
|
|
18
|
+
/** Overlap between consecutive chunks in characters (default 200) */
|
|
19
|
+
chunkOverlap?: number;
|
|
20
|
+
/** Source document identifier used as ID prefix */
|
|
21
|
+
docId?: string | number;
|
|
22
|
+
/** Extra metadata to attach to every chunk */
|
|
23
|
+
metadata?: Record<string, unknown>;
|
|
24
|
+
/** Characters used to split text, in order of priority */
|
|
25
|
+
separators?: string[];
|
|
26
|
+
}
|
|
27
|
+
declare class DocumentChunker {
|
|
28
|
+
private readonly chunkSize;
|
|
29
|
+
private readonly chunkOverlap;
|
|
30
|
+
private readonly separators;
|
|
31
|
+
constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
|
|
32
|
+
/**
|
|
33
|
+
* Split a single text string into overlapping chunks using a recursive strategy.
|
|
34
|
+
* Preserves structural boundaries (Markdown headers) where possible.
|
|
35
|
+
*/
|
|
36
|
+
chunk(text: string, options?: ChunkOptions): Chunk[];
|
|
37
|
+
private recursiveSplit;
|
|
38
|
+
chunkMany(documents: Array<{
|
|
39
|
+
content: string;
|
|
40
|
+
docId?: string | number;
|
|
41
|
+
metadata?: Record<string, unknown>;
|
|
42
|
+
}>): Chunk[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { type Chunk as C, DocumentChunker as D, type ChunkOptions as a };
|
|
@@ -1,3 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic LLM Provider interface.
|
|
3
|
+
* Covers both chat completion and embedding generation so a single
|
|
4
|
+
* provider can handle both responsibilities when appropriate.
|
|
5
|
+
*/
|
|
6
|
+
interface ChatMessage {
|
|
7
|
+
role: 'user' | 'assistant' | 'system';
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
interface ChatOptions {
|
|
11
|
+
/** Override max tokens for this specific call */
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
/** Override temperature for this specific call */
|
|
14
|
+
temperature?: number;
|
|
15
|
+
/** Stop sequences */
|
|
16
|
+
stop?: string[];
|
|
17
|
+
}
|
|
18
|
+
interface EmbedOptions {
|
|
19
|
+
/** Override model for this specific embed call */
|
|
20
|
+
model?: string;
|
|
21
|
+
/** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
|
|
22
|
+
taskType?: 'query' | 'document';
|
|
23
|
+
}
|
|
24
|
+
interface ILLMProvider {
|
|
25
|
+
/**
|
|
26
|
+
* Send a chat completion request.
|
|
27
|
+
* @param messages – the full conversation history
|
|
28
|
+
* @param context – retrieved RAG context to inject
|
|
29
|
+
* @param options – optional per-call overrides
|
|
30
|
+
* @returns – the assistant's reply text
|
|
31
|
+
*/
|
|
32
|
+
chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Send a streaming chat completion request.
|
|
35
|
+
* @returns – an async iterable of text chunks
|
|
36
|
+
*/
|
|
37
|
+
chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Generate an embedding vector for the given text.
|
|
40
|
+
* @param text – text to embed
|
|
41
|
+
* @param options – optional overrides
|
|
42
|
+
* @returns – float array (the embedding)
|
|
43
|
+
*/
|
|
44
|
+
embed(text: string, options?: EmbedOptions): Promise<number[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Generate embedding vectors for multiple texts in a single batch.
|
|
47
|
+
* @param texts – array of texts to embed
|
|
48
|
+
* @param options – optional overrides
|
|
49
|
+
* @returns – array of float arrays
|
|
50
|
+
*/
|
|
51
|
+
batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
|
|
52
|
+
/**
|
|
53
|
+
* Check if the provider endpoint is reachable.
|
|
54
|
+
*/
|
|
55
|
+
ping(): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
|
|
1
58
|
interface VectorMatch {
|
|
2
59
|
id: string | number;
|
|
3
60
|
score: number;
|
|
@@ -208,4 +265,4 @@ interface RagConfig {
|
|
|
208
265
|
graphDb?: GraphDBConfig;
|
|
209
266
|
}
|
|
210
267
|
|
|
211
|
-
export type {
|
|
268
|
+
export type { ChatMessage as C, EmbedOptions as E, GraphDBConfig as G, ILLMProvider as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, ChatOptions as a, ChatResponse as b, EmbeddingConfig as c, EmbeddingProvider as d, IngestDocument as e, LLMProvider as f, RagConfig as g, UpsertDocument as h, VectorDBConfig as i, VectorDBProvider as j, RetrievalResult as k, GraphNode as l, Edge as m, GraphSearchResult as n };
|
|
@@ -1,3 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic LLM Provider interface.
|
|
3
|
+
* Covers both chat completion and embedding generation so a single
|
|
4
|
+
* provider can handle both responsibilities when appropriate.
|
|
5
|
+
*/
|
|
6
|
+
interface ChatMessage {
|
|
7
|
+
role: 'user' | 'assistant' | 'system';
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
interface ChatOptions {
|
|
11
|
+
/** Override max tokens for this specific call */
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
/** Override temperature for this specific call */
|
|
14
|
+
temperature?: number;
|
|
15
|
+
/** Stop sequences */
|
|
16
|
+
stop?: string[];
|
|
17
|
+
}
|
|
18
|
+
interface EmbedOptions {
|
|
19
|
+
/** Override model for this specific embed call */
|
|
20
|
+
model?: string;
|
|
21
|
+
/** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
|
|
22
|
+
taskType?: 'query' | 'document';
|
|
23
|
+
}
|
|
24
|
+
interface ILLMProvider {
|
|
25
|
+
/**
|
|
26
|
+
* Send a chat completion request.
|
|
27
|
+
* @param messages – the full conversation history
|
|
28
|
+
* @param context – retrieved RAG context to inject
|
|
29
|
+
* @param options – optional per-call overrides
|
|
30
|
+
* @returns – the assistant's reply text
|
|
31
|
+
*/
|
|
32
|
+
chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Send a streaming chat completion request.
|
|
35
|
+
* @returns – an async iterable of text chunks
|
|
36
|
+
*/
|
|
37
|
+
chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Generate an embedding vector for the given text.
|
|
40
|
+
* @param text – text to embed
|
|
41
|
+
* @param options – optional overrides
|
|
42
|
+
* @returns – float array (the embedding)
|
|
43
|
+
*/
|
|
44
|
+
embed(text: string, options?: EmbedOptions): Promise<number[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Generate embedding vectors for multiple texts in a single batch.
|
|
47
|
+
* @param texts – array of texts to embed
|
|
48
|
+
* @param options – optional overrides
|
|
49
|
+
* @returns – array of float arrays
|
|
50
|
+
*/
|
|
51
|
+
batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
|
|
52
|
+
/**
|
|
53
|
+
* Check if the provider endpoint is reachable.
|
|
54
|
+
*/
|
|
55
|
+
ping(): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
|
|
1
58
|
interface VectorMatch {
|
|
2
59
|
id: string | number;
|
|
3
60
|
score: number;
|
|
@@ -208,4 +265,4 @@ interface RagConfig {
|
|
|
208
265
|
graphDb?: GraphDBConfig;
|
|
209
266
|
}
|
|
210
267
|
|
|
211
|
-
export type {
|
|
268
|
+
export type { ChatMessage as C, EmbedOptions as E, GraphDBConfig as G, ILLMProvider as I, LLMConfig as L, RAGConfig as R, UIConfig as U, VectorMatch as V, ChatOptions as a, ChatResponse as b, EmbeddingConfig as c, EmbeddingProvider as d, IngestDocument as e, LLMProvider as f, RagConfig as g, UpsertDocument as h, VectorDBConfig as i, VectorDBProvider as j, RetrievalResult as k, GraphNode as l, Edge as m, GraphSearchResult as n };
|
|
@@ -190,13 +190,23 @@ function getRagConfig(env = process.env) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
// src/core/ConfigResolver.ts
|
|
193
|
+
var _cachedEnvConfig = null;
|
|
194
|
+
function getCachedEnvConfig() {
|
|
195
|
+
if (!_cachedEnvConfig) {
|
|
196
|
+
_cachedEnvConfig = getRagConfig();
|
|
197
|
+
}
|
|
198
|
+
return _cachedEnvConfig;
|
|
199
|
+
}
|
|
200
|
+
function resetConfigCache() {
|
|
201
|
+
_cachedEnvConfig = null;
|
|
202
|
+
}
|
|
193
203
|
var ConfigResolver = class {
|
|
194
204
|
/**
|
|
195
|
-
* Resolves the final configuration by merging host-provided config with defaults.
|
|
205
|
+
* Resolves the final configuration by merging host-provided config with environment defaults.
|
|
196
206
|
* @param hostConfig - Partial configuration passed from the host application.
|
|
197
207
|
*/
|
|
198
208
|
static resolve(hostConfig) {
|
|
199
|
-
const envConfig =
|
|
209
|
+
const envConfig = getCachedEnvConfig();
|
|
200
210
|
if (!hostConfig) {
|
|
201
211
|
return envConfig;
|
|
202
212
|
}
|
|
@@ -1508,14 +1518,16 @@ var LangChainAgent = class {
|
|
|
1508
1518
|
this.config = config;
|
|
1509
1519
|
}
|
|
1510
1520
|
/**
|
|
1511
|
-
* Initializes the agent with the RAG pipeline as a
|
|
1512
|
-
* Dynamically imports
|
|
1521
|
+
* Initializes the LangChain ReAct agent with the RAG pipeline as a tool.
|
|
1522
|
+
* Dynamically imports langchain so the package doesn't crash if it isn't installed.
|
|
1513
1523
|
*/
|
|
1514
1524
|
async initialize(chatModel) {
|
|
1515
1525
|
try {
|
|
1516
|
-
const { DynamicTool } = await
|
|
1517
|
-
|
|
1518
|
-
|
|
1526
|
+
const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
|
|
1527
|
+
import("@langchain/core/tools"),
|
|
1528
|
+
import("@langchain/core/messages"),
|
|
1529
|
+
import("langchain")
|
|
1530
|
+
]);
|
|
1519
1531
|
const searchTool = new DynamicTool({
|
|
1520
1532
|
name: "document_search",
|
|
1521
1533
|
description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
|
|
@@ -1524,42 +1536,53 @@ var LangChainAgent = class {
|
|
|
1524
1536
|
return `Search Results:
|
|
1525
1537
|
${response.reply}
|
|
1526
1538
|
|
|
1527
|
-
Sources Used: ${JSON.stringify(
|
|
1539
|
+
Sources Used: ${JSON.stringify(
|
|
1540
|
+
response.sources.map((s) => s.id)
|
|
1541
|
+
)}`;
|
|
1528
1542
|
}
|
|
1529
1543
|
});
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
new MessagesPlaceholder("agent_scratchpad")
|
|
1536
|
-
]);
|
|
1537
|
-
const agent = await createOpenAIFunctionsAgent({
|
|
1538
|
-
llm: chatModel,
|
|
1539
|
-
tools,
|
|
1540
|
-
prompt
|
|
1541
|
-
});
|
|
1542
|
-
this.executor = new AgentExecutor({
|
|
1543
|
-
agent,
|
|
1544
|
-
tools
|
|
1544
|
+
this.agent = createAgent({
|
|
1545
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1546
|
+
model: chatModel,
|
|
1547
|
+
tools: [searchTool],
|
|
1548
|
+
systemPrompt: this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."
|
|
1545
1549
|
});
|
|
1550
|
+
void HumanMessage;
|
|
1546
1551
|
} catch (error) {
|
|
1547
|
-
|
|
1548
|
-
|
|
1552
|
+
const isMissing = error instanceof Error && error.message.includes("Cannot find module");
|
|
1553
|
+
const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
|
|
1554
|
+
throw new Error(
|
|
1555
|
+
`[LangChainAgent] Failed to initialize.${hint}
|
|
1556
|
+
${error instanceof Error ? error.message : String(error)}`
|
|
1557
|
+
);
|
|
1549
1558
|
}
|
|
1550
1559
|
}
|
|
1551
1560
|
/**
|
|
1552
1561
|
* Run the agentic flow.
|
|
1562
|
+
*
|
|
1563
|
+
* LangChain v1.x: invoke takes `{ messages: [...] }` instead of `{ input, chat_history }`.
|
|
1564
|
+
* The agent returns `{ messages: [...] }` — the last message is the final answer.
|
|
1553
1565
|
*/
|
|
1554
1566
|
async run(input, chatHistory = []) {
|
|
1555
|
-
|
|
1567
|
+
var _a, _b, _c;
|
|
1568
|
+
if (!this.agent) {
|
|
1556
1569
|
throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
|
|
1557
1570
|
}
|
|
1558
|
-
const
|
|
1559
|
-
|
|
1560
|
-
|
|
1571
|
+
const { HumanMessage, AIMessage } = await import("@langchain/core/messages");
|
|
1572
|
+
const historyMessages = chatHistory.map(
|
|
1573
|
+
(m) => m.role === "user" ? new HumanMessage(m.content) : new AIMessage(m.content)
|
|
1574
|
+
);
|
|
1575
|
+
const response = await this.agent.invoke({
|
|
1576
|
+
messages: [...historyMessages, new HumanMessage(input)]
|
|
1561
1577
|
});
|
|
1562
|
-
|
|
1578
|
+
const lastMessage = (_a = response == null ? void 0 : response.messages) == null ? void 0 : _a.at(-1);
|
|
1579
|
+
if (lastMessage && typeof lastMessage.content === "string") {
|
|
1580
|
+
return lastMessage.content;
|
|
1581
|
+
}
|
|
1582
|
+
if (Array.isArray(lastMessage == null ? void 0 : lastMessage.content)) {
|
|
1583
|
+
return lastMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
1584
|
+
}
|
|
1585
|
+
return String((_c = (_b = response == null ? void 0 : response.output) != null ? _b : response) != null ? _c : "");
|
|
1563
1586
|
}
|
|
1564
1587
|
};
|
|
1565
1588
|
|
|
@@ -1968,6 +1991,10 @@ var QueryProcessor = class {
|
|
|
1968
1991
|
}
|
|
1969
1992
|
/**
|
|
1970
1993
|
* Constructs a QueryFilter object from extracted hints.
|
|
1994
|
+
*
|
|
1995
|
+
* Note: proper nouns are extracted inside `extractQueryFieldHints` and surfaced
|
|
1996
|
+
* as field-less hints; `buildQueryFilter` adds them to `keywords` without
|
|
1997
|
+
* re-running the proper noun regex to avoid duplication.
|
|
1971
1998
|
*/
|
|
1972
1999
|
static buildQueryFilter(question, hints) {
|
|
1973
2000
|
const filter = { metadata: {}, keywords: [], queryText: question };
|
|
@@ -1975,13 +2002,11 @@ var QueryProcessor = class {
|
|
|
1975
2002
|
if (hint.field) {
|
|
1976
2003
|
filter.metadata[hint.field] = hint.value;
|
|
1977
2004
|
} else {
|
|
1978
|
-
filter.keywords.
|
|
2005
|
+
if (!filter.keywords.includes(hint.value)) {
|
|
2006
|
+
filter.keywords.push(hint.value);
|
|
2007
|
+
}
|
|
1979
2008
|
}
|
|
1980
2009
|
}
|
|
1981
|
-
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
|
|
1982
|
-
const term = this.normalizeHintValue(match[0]);
|
|
1983
|
-
if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
|
|
1984
|
-
}
|
|
1985
2010
|
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
1986
2011
|
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
1987
2012
|
return filter;
|
|
@@ -1989,10 +2014,40 @@ var QueryProcessor = class {
|
|
|
1989
2014
|
};
|
|
1990
2015
|
|
|
1991
2016
|
// src/core/Pipeline.ts
|
|
2017
|
+
var LRUEmbeddingCache = class {
|
|
2018
|
+
constructor(maxSize = 500) {
|
|
2019
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
2020
|
+
this.maxSize = maxSize;
|
|
2021
|
+
}
|
|
2022
|
+
get(key) {
|
|
2023
|
+
const value = this.cache.get(key);
|
|
2024
|
+
if (value !== void 0) {
|
|
2025
|
+
this.cache.delete(key);
|
|
2026
|
+
this.cache.set(key, value);
|
|
2027
|
+
}
|
|
2028
|
+
return value;
|
|
2029
|
+
}
|
|
2030
|
+
set(key, value) {
|
|
2031
|
+
if (this.cache.has(key)) {
|
|
2032
|
+
this.cache.delete(key);
|
|
2033
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
2034
|
+
const oldestKey = this.cache.keys().next().value;
|
|
2035
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
2036
|
+
}
|
|
2037
|
+
this.cache.set(key, value);
|
|
2038
|
+
}
|
|
2039
|
+
clear() {
|
|
2040
|
+
this.cache.clear();
|
|
2041
|
+
}
|
|
2042
|
+
get size() {
|
|
2043
|
+
return this.cache.size;
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
1992
2046
|
var Pipeline = class {
|
|
1993
2047
|
constructor(config) {
|
|
1994
2048
|
this.config = config;
|
|
1995
|
-
|
|
2049
|
+
/** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
|
|
2050
|
+
this.embeddingCache = new LRUEmbeddingCache(500);
|
|
1996
2051
|
this.initialised = false;
|
|
1997
2052
|
var _a, _b, _c, _d, _e;
|
|
1998
2053
|
this.chunker = new DocumentChunker(
|
|
@@ -2080,6 +2135,7 @@ var Pipeline = class {
|
|
|
2080
2135
|
}
|
|
2081
2136
|
/**
|
|
2082
2137
|
* Step 2: Generate embeddings for chunks with retry logic.
|
|
2138
|
+
* Uses batchEmbed when available for efficiency; falls back to sequential embedding.
|
|
2083
2139
|
*/
|
|
2084
2140
|
async processEmbeddings(chunks) {
|
|
2085
2141
|
const embedBatchOptions = {
|
|
@@ -2093,7 +2149,9 @@ var Pipeline = class {
|
|
|
2093
2149
|
embedBatchOptions
|
|
2094
2150
|
);
|
|
2095
2151
|
if (vectors.length !== chunks.length) {
|
|
2096
|
-
throw new Error(
|
|
2152
|
+
throw new Error(
|
|
2153
|
+
`[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
|
|
2154
|
+
);
|
|
2097
2155
|
}
|
|
2098
2156
|
return vectors;
|
|
2099
2157
|
}
|
|
@@ -2247,6 +2305,7 @@ ${context}`;
|
|
|
2247
2305
|
}
|
|
2248
2306
|
/**
|
|
2249
2307
|
* Universal retrieval method combining all enabled providers.
|
|
2308
|
+
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
2250
2309
|
*/
|
|
2251
2310
|
async retrieve(query, options) {
|
|
2252
2311
|
var _a, _b, _c;
|
|
@@ -2279,10 +2338,13 @@ ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
|
|
|
2279
2338
|
New Question: ${question}
|
|
2280
2339
|
|
|
2281
2340
|
Optimized Search Query:`;
|
|
2282
|
-
const rewrite = await this.llmProvider.chat(
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2341
|
+
const rewrite = await this.llmProvider.chat(
|
|
2342
|
+
[
|
|
2343
|
+
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
2344
|
+
{ role: "user", content: prompt }
|
|
2345
|
+
],
|
|
2346
|
+
""
|
|
2347
|
+
);
|
|
2286
2348
|
return rewrite.trim() || question;
|
|
2287
2349
|
}
|
|
2288
2350
|
};
|
|
@@ -2501,8 +2563,35 @@ var DocumentParser = class {
|
|
|
2501
2563
|
};
|
|
2502
2564
|
|
|
2503
2565
|
// src/handlers/index.ts
|
|
2504
|
-
function
|
|
2505
|
-
|
|
2566
|
+
function sseFrame(payload) {
|
|
2567
|
+
return `data: ${JSON.stringify(payload)}
|
|
2568
|
+
|
|
2569
|
+
`;
|
|
2570
|
+
}
|
|
2571
|
+
function sseTextFrame(text) {
|
|
2572
|
+
return `data: ${JSON.stringify({ type: "text", text })}
|
|
2573
|
+
|
|
2574
|
+
`;
|
|
2575
|
+
}
|
|
2576
|
+
function sseMetaFrame(meta) {
|
|
2577
|
+
return `data: ${JSON.stringify(__spreadValues({ type: "metadata" }, meta != null ? meta : {}))}
|
|
2578
|
+
|
|
2579
|
+
`;
|
|
2580
|
+
}
|
|
2581
|
+
function sseErrorFrame(message) {
|
|
2582
|
+
return `data: ${JSON.stringify({ type: "error", error: message })}
|
|
2583
|
+
|
|
2584
|
+
`;
|
|
2585
|
+
}
|
|
2586
|
+
var SSE_HEADERS = {
|
|
2587
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2588
|
+
"Cache-Control": "no-cache, no-transform",
|
|
2589
|
+
Connection: "keep-alive",
|
|
2590
|
+
"X-Accel-Buffering": "no"
|
|
2591
|
+
// Disable Nginx buffering for streaming
|
|
2592
|
+
};
|
|
2593
|
+
function createChatHandler(configOrPlugin) {
|
|
2594
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
2506
2595
|
return async function POST(req) {
|
|
2507
2596
|
try {
|
|
2508
2597
|
const body = await req.json();
|
|
@@ -2518,67 +2607,64 @@ function createChatHandler(config) {
|
|
|
2518
2607
|
}
|
|
2519
2608
|
};
|
|
2520
2609
|
}
|
|
2521
|
-
function createStreamHandler(
|
|
2522
|
-
const plugin = new VectorPlugin(
|
|
2610
|
+
function createStreamHandler(configOrPlugin) {
|
|
2611
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
2523
2612
|
return async function POST(req) {
|
|
2613
|
+
let body;
|
|
2524
2614
|
try {
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2615
|
+
body = await req.json();
|
|
2616
|
+
} catch (e) {
|
|
2617
|
+
return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
|
|
2618
|
+
status: 400,
|
|
2619
|
+
headers: { "Content-Type": "application/json" }
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
const { message, history = [], namespace } = body;
|
|
2623
|
+
if (!(message == null ? void 0 : message.trim())) {
|
|
2624
|
+
return new Response(JSON.stringify({ error: "message is required" }), {
|
|
2625
|
+
status: 400,
|
|
2626
|
+
headers: { "Content-Type": "application/json" }
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
const encoder = new TextEncoder();
|
|
2630
|
+
const stream = new ReadableStream({
|
|
2631
|
+
async start(controller) {
|
|
2632
|
+
const enqueue = (text) => controller.enqueue(encoder.encode(text));
|
|
2633
|
+
try {
|
|
2634
|
+
const pipelineStream = plugin.chatStream(message, history, namespace);
|
|
2530
2635
|
try {
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
} else {
|
|
2538
|
-
controller.enqueue(encoder.encode(`
|
|
2539
|
-
|
|
2540
|
-
__METADATA__${JSON.stringify(chunk)}`));
|
|
2541
|
-
}
|
|
2636
|
+
for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
2637
|
+
const chunk = temp.value;
|
|
2638
|
+
if (typeof chunk === "string") {
|
|
2639
|
+
enqueue(sseTextFrame(chunk));
|
|
2640
|
+
} else {
|
|
2641
|
+
enqueue(sseMetaFrame(chunk));
|
|
2542
2642
|
}
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2643
|
+
}
|
|
2644
|
+
} catch (temp) {
|
|
2645
|
+
error = [temp];
|
|
2646
|
+
} finally {
|
|
2647
|
+
try {
|
|
2648
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
2545
2649
|
} finally {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
} finally {
|
|
2549
|
-
if (error)
|
|
2550
|
-
throw error[0];
|
|
2551
|
-
}
|
|
2650
|
+
if (error)
|
|
2651
|
+
throw error[0];
|
|
2552
2652
|
}
|
|
2553
|
-
controller.close();
|
|
2554
|
-
} catch (streamError) {
|
|
2555
|
-
console.error("[createStreamHandler] Stream processing error:", streamError);
|
|
2556
|
-
const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
|
|
2557
|
-
controller.enqueue(encoder.encode(`
|
|
2558
|
-
|
|
2559
|
-
__ERROR__${JSON.stringify({ error: errorMessage })}`));
|
|
2560
|
-
controller.close();
|
|
2561
2653
|
}
|
|
2654
|
+
} catch (streamError) {
|
|
2655
|
+
const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
|
|
2656
|
+
console.error("[createStreamHandler] Stream error:", streamError);
|
|
2657
|
+
enqueue(sseErrorFrame(errorMessage));
|
|
2658
|
+
} finally {
|
|
2659
|
+
controller.close();
|
|
2562
2660
|
}
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
"Content-Type": "text/event-stream",
|
|
2567
|
-
"Cache-Control": "no-cache",
|
|
2568
|
-
"Connection": "keep-alive"
|
|
2569
|
-
}
|
|
2570
|
-
});
|
|
2571
|
-
} catch (err) {
|
|
2572
|
-
const message = err instanceof Error ? err.message : "Internal server error";
|
|
2573
|
-
return new Response(JSON.stringify({ error: message }), {
|
|
2574
|
-
status: 500,
|
|
2575
|
-
headers: { "Content-Type": "application/json" }
|
|
2576
|
-
});
|
|
2577
|
-
}
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
return new Response(stream, { headers: SSE_HEADERS });
|
|
2578
2664
|
};
|
|
2579
2665
|
}
|
|
2580
|
-
function createIngestHandler(
|
|
2581
|
-
const plugin = new VectorPlugin(
|
|
2666
|
+
function createIngestHandler(configOrPlugin) {
|
|
2667
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
2582
2668
|
return async function POST(req) {
|
|
2583
2669
|
try {
|
|
2584
2670
|
const body = await req.json();
|
|
@@ -2594,8 +2680,8 @@ function createIngestHandler(config) {
|
|
|
2594
2680
|
}
|
|
2595
2681
|
};
|
|
2596
2682
|
}
|
|
2597
|
-
function createHealthHandler(
|
|
2598
|
-
const plugin = new VectorPlugin(
|
|
2683
|
+
function createHealthHandler(configOrPlugin) {
|
|
2684
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
2599
2685
|
return async function GET() {
|
|
2600
2686
|
try {
|
|
2601
2687
|
const health = await plugin.checkHealth();
|
|
@@ -2611,8 +2697,8 @@ function createHealthHandler(config) {
|
|
|
2611
2697
|
}
|
|
2612
2698
|
};
|
|
2613
2699
|
}
|
|
2614
|
-
function createUploadHandler(
|
|
2615
|
-
const plugin = new VectorPlugin(
|
|
2700
|
+
function createUploadHandler(configOrPlugin) {
|
|
2701
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
2616
2702
|
return async function POST(req) {
|
|
2617
2703
|
try {
|
|
2618
2704
|
const formData = await req.formData();
|
|
@@ -2647,6 +2733,7 @@ function createUploadHandler(config) {
|
|
|
2647
2733
|
|
|
2648
2734
|
export {
|
|
2649
2735
|
getRagConfig,
|
|
2736
|
+
resetConfigCache,
|
|
2650
2737
|
ConfigResolver,
|
|
2651
2738
|
OpenAIProvider,
|
|
2652
2739
|
AnthropicProvider,
|
|
@@ -2665,6 +2752,10 @@ export {
|
|
|
2665
2752
|
ProviderHealthCheck,
|
|
2666
2753
|
VectorPlugin,
|
|
2667
2754
|
DocumentParser,
|
|
2755
|
+
sseFrame,
|
|
2756
|
+
sseTextFrame,
|
|
2757
|
+
sseMetaFrame,
|
|
2758
|
+
sseErrorFrame,
|
|
2668
2759
|
createChatHandler,
|
|
2669
2760
|
createStreamHandler,
|
|
2670
2761
|
createIngestHandler,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import '../RagConfig-
|
|
2
|
-
export { c as createChatHandler,
|
|
1
|
+
import '../RagConfig-BEmz4hVP.mjs';
|
|
2
|
+
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-DFSy0CG6.mjs';
|
|
3
3
|
import 'next/server';
|
package/dist/handlers/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import '../RagConfig-
|
|
2
|
-
export { c as createChatHandler,
|
|
1
|
+
import '../RagConfig-BEmz4hVP.js';
|
|
2
|
+
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-CSfaCeux.js';
|
|
3
3
|
import 'next/server';
|