groq-rag 0.1.0
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/LICENSE +21 -0
- package/README.md +456 -0
- package/dist/index.cjs +1977 -0
- package/dist/index.d.cts +771 -0
- package/dist/index.d.ts +771 -0
- package/dist/index.js +1903 -0
- package/package.json +74 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1977 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
Agent: () => Agent,
|
|
34
|
+
BraveSearch: () => BraveSearch,
|
|
35
|
+
ChromaVectorStore: () => ChromaVectorStore,
|
|
36
|
+
DuckDuckGoSearch: () => DuckDuckGoSearch,
|
|
37
|
+
GroqEmbeddings: () => GroqEmbeddings,
|
|
38
|
+
GroqRAG: () => GroqRAG,
|
|
39
|
+
MemoryVectorStore: () => MemoryVectorStore,
|
|
40
|
+
OpenAIEmbeddings: () => OpenAIEmbeddings,
|
|
41
|
+
Retriever: () => Retriever,
|
|
42
|
+
SerperSearch: () => SerperSearch,
|
|
43
|
+
TextChunker: () => TextChunker,
|
|
44
|
+
ToolExecutor: () => ToolExecutor,
|
|
45
|
+
WebFetcher: () => WebFetcher,
|
|
46
|
+
batch: () => batch,
|
|
47
|
+
chunkText: () => chunkText,
|
|
48
|
+
cleanText: () => cleanText,
|
|
49
|
+
cosineSimilarity: () => cosineSimilarity,
|
|
50
|
+
createAgent: () => createAgent,
|
|
51
|
+
createCalculatorTool: () => createCalculatorTool,
|
|
52
|
+
createDateTimeTool: () => createDateTimeTool,
|
|
53
|
+
createEmbeddingProvider: () => createEmbeddingProvider,
|
|
54
|
+
createFetchUrlTool: () => createFetchUrlTool,
|
|
55
|
+
createFetcher: () => createFetcher,
|
|
56
|
+
createRAGQueryTool: () => createRAGQueryTool,
|
|
57
|
+
createRetriever: () => createRetriever,
|
|
58
|
+
createSearchProvider: () => createSearchProvider,
|
|
59
|
+
createToolExecutor: () => createToolExecutor,
|
|
60
|
+
createVectorStore: () => createVectorStore,
|
|
61
|
+
createWebSearchTool: () => createWebSearchTool,
|
|
62
|
+
default: () => index_default,
|
|
63
|
+
estimateTokens: () => estimateTokens,
|
|
64
|
+
extractUrls: () => extractUrls,
|
|
65
|
+
formatContext: () => formatContext,
|
|
66
|
+
generateId: () => generateId,
|
|
67
|
+
getBuiltinTools: () => getBuiltinTools,
|
|
68
|
+
retry: () => retry,
|
|
69
|
+
safeJsonParse: () => safeJsonParse,
|
|
70
|
+
sleep: () => sleep,
|
|
71
|
+
truncateToTokens: () => truncateToTokens
|
|
72
|
+
});
|
|
73
|
+
module.exports = __toCommonJS(index_exports);
|
|
74
|
+
|
|
75
|
+
// src/client.ts
|
|
76
|
+
var import_groq_sdk2 = __toESM(require("groq-sdk"), 1);
|
|
77
|
+
|
|
78
|
+
// src/utils/chunker.ts
|
|
79
|
+
var TextChunker = class {
|
|
80
|
+
options;
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
this.options = {
|
|
83
|
+
strategy: options.strategy || "recursive",
|
|
84
|
+
chunkSize: options.chunkSize || 1e3,
|
|
85
|
+
chunkOverlap: options.chunkOverlap || 200,
|
|
86
|
+
separators: options.separators || ["\n\n", "\n", ". ", " ", ""]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Chunk text into smaller pieces
|
|
91
|
+
*/
|
|
92
|
+
chunk(text, documentId) {
|
|
93
|
+
switch (this.options.strategy) {
|
|
94
|
+
case "fixed":
|
|
95
|
+
return this.fixedChunk(text, documentId);
|
|
96
|
+
case "sentence":
|
|
97
|
+
return this.sentenceChunk(text, documentId);
|
|
98
|
+
case "paragraph":
|
|
99
|
+
return this.paragraphChunk(text, documentId);
|
|
100
|
+
case "recursive":
|
|
101
|
+
return this.recursiveChunk(text, documentId);
|
|
102
|
+
case "semantic":
|
|
103
|
+
return this.semanticChunk(text, documentId);
|
|
104
|
+
default:
|
|
105
|
+
return this.recursiveChunk(text, documentId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Fixed-size chunking with overlap
|
|
110
|
+
*/
|
|
111
|
+
fixedChunk(text, documentId) {
|
|
112
|
+
const chunks = [];
|
|
113
|
+
let start = 0;
|
|
114
|
+
while (start < text.length) {
|
|
115
|
+
const end = Math.min(start + this.options.chunkSize, text.length);
|
|
116
|
+
const content = text.slice(start, end).trim();
|
|
117
|
+
if (content) {
|
|
118
|
+
chunks.push({
|
|
119
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
120
|
+
documentId,
|
|
121
|
+
content,
|
|
122
|
+
startIndex: start,
|
|
123
|
+
endIndex: end
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
start += this.options.chunkSize - this.options.chunkOverlap;
|
|
127
|
+
}
|
|
128
|
+
return chunks;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Sentence-based chunking
|
|
132
|
+
*/
|
|
133
|
+
sentenceChunk(text, documentId) {
|
|
134
|
+
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
|
|
135
|
+
const chunks = [];
|
|
136
|
+
let currentChunk = "";
|
|
137
|
+
let startIndex = 0;
|
|
138
|
+
for (const sentence of sentences) {
|
|
139
|
+
if ((currentChunk + sentence).length > this.options.chunkSize && currentChunk) {
|
|
140
|
+
chunks.push({
|
|
141
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
142
|
+
documentId,
|
|
143
|
+
content: currentChunk.trim(),
|
|
144
|
+
startIndex,
|
|
145
|
+
endIndex: startIndex + currentChunk.length
|
|
146
|
+
});
|
|
147
|
+
startIndex += currentChunk.length;
|
|
148
|
+
currentChunk = sentence;
|
|
149
|
+
} else {
|
|
150
|
+
currentChunk += sentence;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (currentChunk.trim()) {
|
|
154
|
+
chunks.push({
|
|
155
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
156
|
+
documentId,
|
|
157
|
+
content: currentChunk.trim(),
|
|
158
|
+
startIndex,
|
|
159
|
+
endIndex: startIndex + currentChunk.length
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return chunks;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Paragraph-based chunking
|
|
166
|
+
*/
|
|
167
|
+
paragraphChunk(text, documentId) {
|
|
168
|
+
const paragraphs = text.split(/\n\n+/);
|
|
169
|
+
const chunks = [];
|
|
170
|
+
let currentChunk = "";
|
|
171
|
+
let startIndex = 0;
|
|
172
|
+
for (const paragraph of paragraphs) {
|
|
173
|
+
const trimmed = paragraph.trim();
|
|
174
|
+
if (!trimmed) continue;
|
|
175
|
+
if ((currentChunk + "\n\n" + trimmed).length > this.options.chunkSize && currentChunk) {
|
|
176
|
+
chunks.push({
|
|
177
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
178
|
+
documentId,
|
|
179
|
+
content: currentChunk.trim(),
|
|
180
|
+
startIndex,
|
|
181
|
+
endIndex: startIndex + currentChunk.length
|
|
182
|
+
});
|
|
183
|
+
startIndex += currentChunk.length;
|
|
184
|
+
currentChunk = trimmed;
|
|
185
|
+
} else {
|
|
186
|
+
currentChunk = currentChunk ? currentChunk + "\n\n" + trimmed : trimmed;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (currentChunk.trim()) {
|
|
190
|
+
chunks.push({
|
|
191
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
192
|
+
documentId,
|
|
193
|
+
content: currentChunk.trim(),
|
|
194
|
+
startIndex,
|
|
195
|
+
endIndex: startIndex + currentChunk.length
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return chunks;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Recursive character text splitter (LangChain-style)
|
|
202
|
+
*/
|
|
203
|
+
recursiveChunk(text, documentId) {
|
|
204
|
+
return this.recursiveSplit(text, this.options.separators, documentId);
|
|
205
|
+
}
|
|
206
|
+
recursiveSplit(text, separators, documentId, chunks = []) {
|
|
207
|
+
const separator = separators[0];
|
|
208
|
+
const remainingSeparators = separators.slice(1);
|
|
209
|
+
let splits;
|
|
210
|
+
if (separator === "") {
|
|
211
|
+
splits = text.split("");
|
|
212
|
+
} else {
|
|
213
|
+
splits = text.split(separator);
|
|
214
|
+
}
|
|
215
|
+
let currentChunk = "";
|
|
216
|
+
for (const split of splits) {
|
|
217
|
+
const piece = separator === "" ? split : split + separator;
|
|
218
|
+
if ((currentChunk + piece).length > this.options.chunkSize) {
|
|
219
|
+
if (currentChunk) {
|
|
220
|
+
if (currentChunk.length > this.options.chunkSize && remainingSeparators.length > 0) {
|
|
221
|
+
this.recursiveSplit(currentChunk, remainingSeparators, documentId, chunks);
|
|
222
|
+
} else {
|
|
223
|
+
chunks.push({
|
|
224
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
225
|
+
documentId,
|
|
226
|
+
content: currentChunk.trim()
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
currentChunk = piece;
|
|
231
|
+
} else {
|
|
232
|
+
currentChunk += piece;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (currentChunk.trim()) {
|
|
236
|
+
if (currentChunk.length > this.options.chunkSize && remainingSeparators.length > 0) {
|
|
237
|
+
this.recursiveSplit(currentChunk, remainingSeparators, documentId, chunks);
|
|
238
|
+
} else {
|
|
239
|
+
chunks.push({
|
|
240
|
+
id: `${documentId}-chunk-${chunks.length}`,
|
|
241
|
+
documentId,
|
|
242
|
+
content: currentChunk.trim()
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return chunks;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Semantic chunking (placeholder - would use embeddings in production)
|
|
250
|
+
*/
|
|
251
|
+
semanticChunk(text, documentId) {
|
|
252
|
+
return this.paragraphChunk(text, documentId);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function chunkText(text, documentId, options) {
|
|
256
|
+
const chunker = new TextChunker(options);
|
|
257
|
+
return chunker.chunk(text, documentId);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/utils/helpers.ts
|
|
261
|
+
function generateId() {
|
|
262
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
263
|
+
}
|
|
264
|
+
function cosineSimilarity(a, b) {
|
|
265
|
+
if (a.length !== b.length) {
|
|
266
|
+
throw new Error("Vectors must have the same length");
|
|
267
|
+
}
|
|
268
|
+
let dotProduct = 0;
|
|
269
|
+
let normA = 0;
|
|
270
|
+
let normB = 0;
|
|
271
|
+
for (let i = 0; i < a.length; i++) {
|
|
272
|
+
dotProduct += a[i] * b[i];
|
|
273
|
+
normA += a[i] * a[i];
|
|
274
|
+
normB += b[i] * b[i];
|
|
275
|
+
}
|
|
276
|
+
normA = Math.sqrt(normA);
|
|
277
|
+
normB = Math.sqrt(normB);
|
|
278
|
+
if (normA === 0 || normB === 0) {
|
|
279
|
+
return 0;
|
|
280
|
+
}
|
|
281
|
+
return dotProduct / (normA * normB);
|
|
282
|
+
}
|
|
283
|
+
function estimateTokens(text) {
|
|
284
|
+
return Math.ceil(text.length / 4);
|
|
285
|
+
}
|
|
286
|
+
function truncateToTokens(text, maxTokens) {
|
|
287
|
+
const estimatedChars = maxTokens * 4;
|
|
288
|
+
if (text.length <= estimatedChars) {
|
|
289
|
+
return text;
|
|
290
|
+
}
|
|
291
|
+
return text.slice(0, estimatedChars) + "...";
|
|
292
|
+
}
|
|
293
|
+
function cleanText(text) {
|
|
294
|
+
return text.replace(/\n{3,}/g, "\n\n").replace(/[^\S\n]+/g, " ").replace(/ ?\n ?/g, "\n").trim();
|
|
295
|
+
}
|
|
296
|
+
function extractUrls(text) {
|
|
297
|
+
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/g;
|
|
298
|
+
return text.match(urlRegex) || [];
|
|
299
|
+
}
|
|
300
|
+
function sleep(ms) {
|
|
301
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
302
|
+
}
|
|
303
|
+
async function retry(fn, options = {}) {
|
|
304
|
+
const { maxRetries = 3, baseDelay = 1e3, maxDelay = 1e4 } = options;
|
|
305
|
+
let lastError;
|
|
306
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
307
|
+
try {
|
|
308
|
+
return await fn();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
lastError = error;
|
|
311
|
+
if (attempt < maxRetries) {
|
|
312
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
|
|
313
|
+
await sleep(delay);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
throw lastError;
|
|
318
|
+
}
|
|
319
|
+
function formatContext(results, options = {}) {
|
|
320
|
+
const { includeMetadata = false, separator = "\n\n---\n\n" } = options;
|
|
321
|
+
return results.map((result, index) => {
|
|
322
|
+
let text = `[Source ${index + 1}]
|
|
323
|
+
${result.content}`;
|
|
324
|
+
if (includeMetadata && result.metadata) {
|
|
325
|
+
const meta = Object.entries(result.metadata).filter(([_, v]) => v !== void 0).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
326
|
+
if (meta) {
|
|
327
|
+
text = `[Source ${index + 1} | ${meta}]
|
|
328
|
+
${result.content}`;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return text;
|
|
332
|
+
}).join(separator);
|
|
333
|
+
}
|
|
334
|
+
function safeJsonParse(text, defaultValue) {
|
|
335
|
+
try {
|
|
336
|
+
return JSON.parse(text);
|
|
337
|
+
} catch {
|
|
338
|
+
return defaultValue;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function batch(array, size) {
|
|
342
|
+
const batches = [];
|
|
343
|
+
for (let i = 0; i < array.length; i += size) {
|
|
344
|
+
batches.push(array.slice(i, i + size));
|
|
345
|
+
}
|
|
346
|
+
return batches;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/rag/retriever.ts
|
|
350
|
+
var Retriever = class {
|
|
351
|
+
vectorStore;
|
|
352
|
+
embeddings;
|
|
353
|
+
chunker;
|
|
354
|
+
constructor(vectorStore, embeddings, chunkingOptions) {
|
|
355
|
+
this.vectorStore = vectorStore;
|
|
356
|
+
this.embeddings = embeddings;
|
|
357
|
+
this.chunker = new TextChunker(chunkingOptions);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Add a document to the retriever
|
|
361
|
+
*/
|
|
362
|
+
async addDocument(content, metadata) {
|
|
363
|
+
const documentId = generateId();
|
|
364
|
+
const chunks = this.chunker.chunk(content, documentId);
|
|
365
|
+
const chunksWithMetadata = chunks.map((chunk) => ({
|
|
366
|
+
...chunk,
|
|
367
|
+
metadata: { ...metadata, documentId }
|
|
368
|
+
}));
|
|
369
|
+
const texts = chunksWithMetadata.map((c) => c.content);
|
|
370
|
+
const embedResults = await this.embeddings.embedBatch(texts);
|
|
371
|
+
const chunksWithEmbeddings = chunksWithMetadata.map(
|
|
372
|
+
(chunk, i) => ({
|
|
373
|
+
...chunk,
|
|
374
|
+
embedding: embedResults[i].embedding
|
|
375
|
+
})
|
|
376
|
+
);
|
|
377
|
+
await this.vectorStore.add(chunksWithEmbeddings);
|
|
378
|
+
return documentId;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Add multiple documents
|
|
382
|
+
*/
|
|
383
|
+
async addDocuments(documents) {
|
|
384
|
+
const ids = [];
|
|
385
|
+
for (const doc of documents) {
|
|
386
|
+
const id = await this.addDocument(doc.content, doc.metadata);
|
|
387
|
+
ids.push(id);
|
|
388
|
+
}
|
|
389
|
+
return ids;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Add raw text directly (without chunking)
|
|
393
|
+
*/
|
|
394
|
+
async addChunk(content, metadata) {
|
|
395
|
+
const id = generateId();
|
|
396
|
+
const [embedResult] = await this.embeddings.embedBatch([content]);
|
|
397
|
+
const chunk = {
|
|
398
|
+
id,
|
|
399
|
+
documentId: id,
|
|
400
|
+
content,
|
|
401
|
+
metadata,
|
|
402
|
+
embedding: embedResult.embedding
|
|
403
|
+
};
|
|
404
|
+
await this.vectorStore.add([chunk]);
|
|
405
|
+
return id;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Retrieve relevant documents for a query
|
|
409
|
+
*/
|
|
410
|
+
async retrieve(query, options = {}) {
|
|
411
|
+
const { topK = 5, minScore = 0 } = options;
|
|
412
|
+
const { embedding } = await this.embeddings.embed(query);
|
|
413
|
+
const results = await this.vectorStore.search(embedding, { topK: topK * 2 });
|
|
414
|
+
return results.filter((r) => r.score >= minScore).slice(0, topK);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Retrieve and format context for LLM
|
|
418
|
+
*/
|
|
419
|
+
async getContext(query, options = {}) {
|
|
420
|
+
const results = await this.retrieve(query, options);
|
|
421
|
+
return formatContext(
|
|
422
|
+
results.map((r) => ({
|
|
423
|
+
content: r.document.content,
|
|
424
|
+
metadata: r.document.metadata
|
|
425
|
+
})),
|
|
426
|
+
{ includeMetadata: options.includeMetadata }
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Delete documents by ID
|
|
431
|
+
*/
|
|
432
|
+
async deleteDocuments(ids) {
|
|
433
|
+
await this.vectorStore.delete(ids);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Clear all documents
|
|
437
|
+
*/
|
|
438
|
+
async clear() {
|
|
439
|
+
await this.vectorStore.clear();
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Get document count
|
|
443
|
+
*/
|
|
444
|
+
async count() {
|
|
445
|
+
return this.vectorStore.count();
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
function createRetriever(vectorStore, embeddings, options) {
|
|
449
|
+
return new Retriever(vectorStore, embeddings, options);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/rag/vectorStore.ts
|
|
453
|
+
var MemoryVectorStore = class {
|
|
454
|
+
documents = /* @__PURE__ */ new Map();
|
|
455
|
+
async add(documents) {
|
|
456
|
+
for (const doc of documents) {
|
|
457
|
+
this.documents.set(doc.id, doc);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async search(queryEmbedding, options = {}) {
|
|
461
|
+
const { topK = 5, filter } = options;
|
|
462
|
+
const results = [];
|
|
463
|
+
for (const doc of this.documents.values()) {
|
|
464
|
+
if (!doc.embedding) continue;
|
|
465
|
+
if (filter && doc.metadata) {
|
|
466
|
+
let matches = true;
|
|
467
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
468
|
+
if (doc.metadata[key] !== value) {
|
|
469
|
+
matches = false;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (!matches) continue;
|
|
474
|
+
}
|
|
475
|
+
const score = cosineSimilarity(queryEmbedding, doc.embedding);
|
|
476
|
+
results.push({
|
|
477
|
+
document: doc,
|
|
478
|
+
score,
|
|
479
|
+
relevance: score > 0.8 ? "high" : score > 0.5 ? "medium" : "low"
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return results.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
483
|
+
}
|
|
484
|
+
async delete(ids) {
|
|
485
|
+
for (const id of ids) {
|
|
486
|
+
this.documents.delete(id);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async clear() {
|
|
490
|
+
this.documents.clear();
|
|
491
|
+
}
|
|
492
|
+
async count() {
|
|
493
|
+
return this.documents.size;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Get all documents (useful for debugging)
|
|
497
|
+
*/
|
|
498
|
+
getAll() {
|
|
499
|
+
return Array.from(this.documents.values());
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
var ChromaVectorStore = class {
|
|
503
|
+
baseURL;
|
|
504
|
+
collectionName;
|
|
505
|
+
collectionId;
|
|
506
|
+
constructor(config) {
|
|
507
|
+
this.baseURL = config.connectionString || "http://localhost:8000";
|
|
508
|
+
this.collectionName = config.indexName || "groq-rag";
|
|
509
|
+
}
|
|
510
|
+
async ensureCollection() {
|
|
511
|
+
if (this.collectionId) return;
|
|
512
|
+
const response = await fetch(`${this.baseURL}/api/v1/collections`, {
|
|
513
|
+
method: "POST",
|
|
514
|
+
headers: { "Content-Type": "application/json" },
|
|
515
|
+
body: JSON.stringify({ name: this.collectionName })
|
|
516
|
+
});
|
|
517
|
+
if (response.ok) {
|
|
518
|
+
const data = await response.json();
|
|
519
|
+
this.collectionId = data.id;
|
|
520
|
+
} else {
|
|
521
|
+
const getResponse = await fetch(
|
|
522
|
+
`${this.baseURL}/api/v1/collections/${this.collectionName}`
|
|
523
|
+
);
|
|
524
|
+
if (getResponse.ok) {
|
|
525
|
+
const data = await getResponse.json();
|
|
526
|
+
this.collectionId = data.id;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async add(documents) {
|
|
531
|
+
await this.ensureCollection();
|
|
532
|
+
const ids = documents.map((d) => d.id);
|
|
533
|
+
const embeddings = documents.map((d) => d.embedding || []);
|
|
534
|
+
const metadatas = documents.map((d) => d.metadata || {});
|
|
535
|
+
const contents = documents.map((d) => d.content);
|
|
536
|
+
await fetch(`${this.baseURL}/api/v1/collections/${this.collectionId}/add`, {
|
|
537
|
+
method: "POST",
|
|
538
|
+
headers: { "Content-Type": "application/json" },
|
|
539
|
+
body: JSON.stringify({
|
|
540
|
+
ids,
|
|
541
|
+
embeddings,
|
|
542
|
+
metadatas,
|
|
543
|
+
documents: contents
|
|
544
|
+
})
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
async search(queryEmbedding, options = {}) {
|
|
548
|
+
await this.ensureCollection();
|
|
549
|
+
const { topK = 5, filter } = options;
|
|
550
|
+
const response = await fetch(
|
|
551
|
+
`${this.baseURL}/api/v1/collections/${this.collectionId}/query`,
|
|
552
|
+
{
|
|
553
|
+
method: "POST",
|
|
554
|
+
headers: { "Content-Type": "application/json" },
|
|
555
|
+
body: JSON.stringify({
|
|
556
|
+
query_embeddings: [queryEmbedding],
|
|
557
|
+
n_results: topK,
|
|
558
|
+
where: filter
|
|
559
|
+
})
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
if (!response.ok) {
|
|
563
|
+
throw new Error(`Chroma query failed: ${response.statusText}`);
|
|
564
|
+
}
|
|
565
|
+
const data = await response.json();
|
|
566
|
+
const results = [];
|
|
567
|
+
const ids = data.ids[0] || [];
|
|
568
|
+
const distances = data.distances[0] || [];
|
|
569
|
+
const docs = data.documents[0] || [];
|
|
570
|
+
const metas = data.metadatas[0] || [];
|
|
571
|
+
for (let i = 0; i < ids.length; i++) {
|
|
572
|
+
const score = 1 - (distances[i] || 0);
|
|
573
|
+
results.push({
|
|
574
|
+
document: {
|
|
575
|
+
id: ids[i],
|
|
576
|
+
documentId: ids[i],
|
|
577
|
+
content: docs[i],
|
|
578
|
+
metadata: metas[i]
|
|
579
|
+
},
|
|
580
|
+
score,
|
|
581
|
+
relevance: score > 0.8 ? "high" : score > 0.5 ? "medium" : "low"
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return results;
|
|
585
|
+
}
|
|
586
|
+
async delete(ids) {
|
|
587
|
+
await this.ensureCollection();
|
|
588
|
+
await fetch(`${this.baseURL}/api/v1/collections/${this.collectionId}/delete`, {
|
|
589
|
+
method: "POST",
|
|
590
|
+
headers: { "Content-Type": "application/json" },
|
|
591
|
+
body: JSON.stringify({ ids })
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
async clear() {
|
|
595
|
+
await fetch(`${this.baseURL}/api/v1/collections/${this.collectionName}`, {
|
|
596
|
+
method: "DELETE"
|
|
597
|
+
});
|
|
598
|
+
this.collectionId = void 0;
|
|
599
|
+
await this.ensureCollection();
|
|
600
|
+
}
|
|
601
|
+
async count() {
|
|
602
|
+
await this.ensureCollection();
|
|
603
|
+
const response = await fetch(
|
|
604
|
+
`${this.baseURL}/api/v1/collections/${this.collectionId}/count`
|
|
605
|
+
);
|
|
606
|
+
const data = await response.json();
|
|
607
|
+
return data;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
function createVectorStore(config) {
|
|
611
|
+
switch (config.provider) {
|
|
612
|
+
case "chroma":
|
|
613
|
+
return new ChromaVectorStore(config);
|
|
614
|
+
case "memory":
|
|
615
|
+
default:
|
|
616
|
+
return new MemoryVectorStore();
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/rag/embeddings.ts
|
|
621
|
+
var import_groq_sdk = __toESM(require("groq-sdk"), 1);
|
|
622
|
+
var GroqEmbeddings = class {
|
|
623
|
+
client;
|
|
624
|
+
model;
|
|
625
|
+
dimensions;
|
|
626
|
+
constructor(config) {
|
|
627
|
+
this.client = config.groqClient || new import_groq_sdk.default({ apiKey: config.apiKey });
|
|
628
|
+
this.model = config.model || "llama-3.3-70b-versatile";
|
|
629
|
+
this.dimensions = config.dimensions || 1536;
|
|
630
|
+
}
|
|
631
|
+
async embed(text) {
|
|
632
|
+
const embedding = await this.generatePseudoEmbedding(text);
|
|
633
|
+
return { embedding, tokenCount: Math.ceil(text.length / 4) };
|
|
634
|
+
}
|
|
635
|
+
async embedBatch(texts) {
|
|
636
|
+
const results = [];
|
|
637
|
+
for (const text of texts) {
|
|
638
|
+
results.push(await this.embed(text));
|
|
639
|
+
}
|
|
640
|
+
return results;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Generate a pseudo-embedding (for demo purposes)
|
|
644
|
+
* In production, replace with actual embedding API
|
|
645
|
+
*/
|
|
646
|
+
async generatePseudoEmbedding(text) {
|
|
647
|
+
const embedding = new Array(this.dimensions).fill(0);
|
|
648
|
+
for (let i = 0; i < text.length; i++) {
|
|
649
|
+
const charCode = text.charCodeAt(i);
|
|
650
|
+
const index = charCode * (i + 1) % this.dimensions;
|
|
651
|
+
embedding[index] += 1 / Math.sqrt(text.length);
|
|
652
|
+
}
|
|
653
|
+
const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
|
|
654
|
+
if (norm > 0) {
|
|
655
|
+
for (let i = 0; i < embedding.length; i++) {
|
|
656
|
+
embedding[i] /= norm;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return embedding;
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
var OpenAIEmbeddings = class {
|
|
663
|
+
apiKey;
|
|
664
|
+
baseURL;
|
|
665
|
+
model;
|
|
666
|
+
dimensions;
|
|
667
|
+
constructor(config) {
|
|
668
|
+
this.apiKey = config.apiKey || process.env.OPENAI_API_KEY || "";
|
|
669
|
+
this.baseURL = config.baseURL || "https://api.openai.com/v1";
|
|
670
|
+
this.model = config.model || "text-embedding-3-small";
|
|
671
|
+
this.dimensions = config.dimensions || 1536;
|
|
672
|
+
}
|
|
673
|
+
async embed(text) {
|
|
674
|
+
const response = await fetch(`${this.baseURL}/embeddings`, {
|
|
675
|
+
method: "POST",
|
|
676
|
+
headers: {
|
|
677
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
678
|
+
"Content-Type": "application/json"
|
|
679
|
+
},
|
|
680
|
+
body: JSON.stringify({
|
|
681
|
+
input: text,
|
|
682
|
+
model: this.model,
|
|
683
|
+
dimensions: this.dimensions
|
|
684
|
+
})
|
|
685
|
+
});
|
|
686
|
+
if (!response.ok) {
|
|
687
|
+
throw new Error(`Embedding request failed: ${response.statusText}`);
|
|
688
|
+
}
|
|
689
|
+
const data = await response.json();
|
|
690
|
+
return {
|
|
691
|
+
embedding: data.data[0].embedding,
|
|
692
|
+
tokenCount: data.usage?.total_tokens
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
async embedBatch(texts) {
|
|
696
|
+
const batches = batch(texts, 100);
|
|
697
|
+
const results = [];
|
|
698
|
+
for (const batchTexts of batches) {
|
|
699
|
+
const response = await fetch(`${this.baseURL}/embeddings`, {
|
|
700
|
+
method: "POST",
|
|
701
|
+
headers: {
|
|
702
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
703
|
+
"Content-Type": "application/json"
|
|
704
|
+
},
|
|
705
|
+
body: JSON.stringify({
|
|
706
|
+
input: batchTexts,
|
|
707
|
+
model: this.model,
|
|
708
|
+
dimensions: this.dimensions
|
|
709
|
+
})
|
|
710
|
+
});
|
|
711
|
+
if (!response.ok) {
|
|
712
|
+
throw new Error(`Embedding request failed: ${response.statusText}`);
|
|
713
|
+
}
|
|
714
|
+
const data = await response.json();
|
|
715
|
+
for (const item of data.data) {
|
|
716
|
+
results.push({
|
|
717
|
+
embedding: item.embedding,
|
|
718
|
+
tokenCount: data.usage?.total_tokens ? Math.floor(data.usage.total_tokens / batchTexts.length) : void 0
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return results;
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
function createEmbeddingProvider(config, groqClient) {
|
|
726
|
+
switch (config.provider) {
|
|
727
|
+
case "openai":
|
|
728
|
+
return new OpenAIEmbeddings(config);
|
|
729
|
+
case "groq":
|
|
730
|
+
case "local":
|
|
731
|
+
default:
|
|
732
|
+
return new GroqEmbeddings({ ...config, groqClient });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/web/fetcher.ts
|
|
737
|
+
var cheerio = __toESM(require("cheerio"), 1);
|
|
738
|
+
var import_turndown = __toESM(require("turndown"), 1);
|
|
739
|
+
var WebFetcher = class {
|
|
740
|
+
config;
|
|
741
|
+
turndown;
|
|
742
|
+
constructor(config = {}) {
|
|
743
|
+
this.config = {
|
|
744
|
+
userAgent: config.userAgent || "Mozilla/5.0 (compatible; GroqRAG/1.0; +https://github.com/mithun50/groq-rag)",
|
|
745
|
+
timeout: config.timeout || 3e4,
|
|
746
|
+
maxContentLength: config.maxContentLength || 1e6,
|
|
747
|
+
// 1MB
|
|
748
|
+
followRedirects: config.followRedirects ?? true,
|
|
749
|
+
proxy: config.proxy
|
|
750
|
+
};
|
|
751
|
+
this.turndown = new import_turndown.default({
|
|
752
|
+
headingStyle: "atx",
|
|
753
|
+
codeBlockStyle: "fenced",
|
|
754
|
+
bulletListMarker: "-"
|
|
755
|
+
});
|
|
756
|
+
this.turndown.addRule("removeScripts", {
|
|
757
|
+
filter: ["script", "style", "noscript", "iframe"],
|
|
758
|
+
replacement: () => ""
|
|
759
|
+
});
|
|
760
|
+
this.turndown.addRule("preserveCode", {
|
|
761
|
+
filter: "pre",
|
|
762
|
+
replacement: (content, node) => {
|
|
763
|
+
const code = node.textContent || content;
|
|
764
|
+
return `
|
|
765
|
+
\`\`\`
|
|
766
|
+
${code}
|
|
767
|
+
\`\`\`
|
|
768
|
+
`;
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Fetch a URL and extract content
|
|
774
|
+
*/
|
|
775
|
+
async fetch(url, options = {}) {
|
|
776
|
+
const {
|
|
777
|
+
headers = {},
|
|
778
|
+
timeout = this.config.timeout,
|
|
779
|
+
extractText = true,
|
|
780
|
+
includeLinks = false,
|
|
781
|
+
includeImages = false
|
|
782
|
+
} = options;
|
|
783
|
+
const controller = new AbortController();
|
|
784
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
785
|
+
try {
|
|
786
|
+
const response = await fetch(url, {
|
|
787
|
+
headers: {
|
|
788
|
+
"User-Agent": this.config.userAgent,
|
|
789
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
790
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
791
|
+
...headers
|
|
792
|
+
},
|
|
793
|
+
redirect: this.config.followRedirects ? "follow" : "manual",
|
|
794
|
+
signal: controller.signal
|
|
795
|
+
});
|
|
796
|
+
if (!response.ok) {
|
|
797
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
798
|
+
}
|
|
799
|
+
const html = await response.text();
|
|
800
|
+
clearTimeout(timeoutId);
|
|
801
|
+
const $ = cheerio.load(html);
|
|
802
|
+
$("script, style, noscript, iframe, nav, footer, aside, .ads, .advertisement").remove();
|
|
803
|
+
const title = $("title").text().trim() || $("h1").first().text().trim() || $('meta[property="og:title"]').attr("content") || void 0;
|
|
804
|
+
const metadata = {
|
|
805
|
+
description: $('meta[name="description"]').attr("content") || $('meta[property="og:description"]').attr("content") || void 0,
|
|
806
|
+
author: $('meta[name="author"]').attr("content") || $('meta[property="article:author"]').attr("content") || void 0,
|
|
807
|
+
publishedDate: $('meta[property="article:published_time"]').attr("content") || $("time").attr("datetime") || void 0
|
|
808
|
+
};
|
|
809
|
+
let content = "";
|
|
810
|
+
let markdown = "";
|
|
811
|
+
if (extractText) {
|
|
812
|
+
const mainSelectors = [
|
|
813
|
+
"main",
|
|
814
|
+
"article",
|
|
815
|
+
'[role="main"]',
|
|
816
|
+
".content",
|
|
817
|
+
".post-content",
|
|
818
|
+
".article-content",
|
|
819
|
+
".entry-content",
|
|
820
|
+
"#content",
|
|
821
|
+
"#main"
|
|
822
|
+
];
|
|
823
|
+
let mainHtml = $("body").html() || "";
|
|
824
|
+
let mainText = $("body").text();
|
|
825
|
+
for (const selector of mainSelectors) {
|
|
826
|
+
const selected = $(selector);
|
|
827
|
+
if (selected.length > 0) {
|
|
828
|
+
mainHtml = selected.first().html() || mainHtml;
|
|
829
|
+
mainText = selected.first().text();
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
markdown = this.turndown.turndown(mainHtml);
|
|
834
|
+
content = mainText.replace(/\s+/g, " ").trim();
|
|
835
|
+
}
|
|
836
|
+
let links;
|
|
837
|
+
if (includeLinks) {
|
|
838
|
+
links = [];
|
|
839
|
+
$("a[href]").each((_, el) => {
|
|
840
|
+
const $el = $(el);
|
|
841
|
+
const href = $el.attr("href");
|
|
842
|
+
const text = $el.text().trim();
|
|
843
|
+
if (href && text && !href.startsWith("#") && !href.startsWith("javascript:")) {
|
|
844
|
+
try {
|
|
845
|
+
const absoluteUrl = new URL(href, url).toString();
|
|
846
|
+
links.push({ text, href: absoluteUrl });
|
|
847
|
+
} catch {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
links = [...new Map(links.map((l) => [l.href, l])).values()];
|
|
852
|
+
}
|
|
853
|
+
let images;
|
|
854
|
+
if (includeImages) {
|
|
855
|
+
images = [];
|
|
856
|
+
$("img[src]").each((_, el) => {
|
|
857
|
+
const $el = $(el);
|
|
858
|
+
const src = $el.attr("src");
|
|
859
|
+
const alt = $el.attr("alt") || "";
|
|
860
|
+
if (src) {
|
|
861
|
+
try {
|
|
862
|
+
const absoluteUrl = new URL(src, url).toString();
|
|
863
|
+
images.push({ alt, src: absoluteUrl });
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
return {
|
|
870
|
+
url,
|
|
871
|
+
title,
|
|
872
|
+
content,
|
|
873
|
+
markdown,
|
|
874
|
+
links,
|
|
875
|
+
images,
|
|
876
|
+
metadata,
|
|
877
|
+
fetchedAt: /* @__PURE__ */ new Date()
|
|
878
|
+
};
|
|
879
|
+
} catch (error) {
|
|
880
|
+
clearTimeout(timeoutId);
|
|
881
|
+
throw error;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Fetch multiple URLs in parallel
|
|
886
|
+
*/
|
|
887
|
+
async fetchMany(urls, options = {}) {
|
|
888
|
+
const results = await Promise.allSettled(
|
|
889
|
+
urls.map((url) => this.fetch(url, options))
|
|
890
|
+
);
|
|
891
|
+
return results.map((result, i) => {
|
|
892
|
+
if (result.status === "fulfilled") {
|
|
893
|
+
return result.value;
|
|
894
|
+
} else {
|
|
895
|
+
return new Error(`Failed to fetch ${urls[i]}: ${result.reason}`);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Fetch and extract just the text content
|
|
901
|
+
*/
|
|
902
|
+
async fetchText(url) {
|
|
903
|
+
const result = await this.fetch(url, { extractText: true });
|
|
904
|
+
return result.content;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Fetch and convert to markdown
|
|
908
|
+
*/
|
|
909
|
+
async fetchMarkdown(url) {
|
|
910
|
+
const result = await this.fetch(url, { extractText: true });
|
|
911
|
+
return result.markdown || result.content;
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
function createFetcher(config) {
|
|
915
|
+
return new WebFetcher(config);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/web/search.ts
|
|
919
|
+
var DuckDuckGoSearch = class {
|
|
920
|
+
baseUrl = "https://html.duckduckgo.com/html/";
|
|
921
|
+
async search(query, options = {}) {
|
|
922
|
+
const { maxResults = 10 } = options;
|
|
923
|
+
try {
|
|
924
|
+
const response = await fetch(this.baseUrl, {
|
|
925
|
+
method: "POST",
|
|
926
|
+
headers: {
|
|
927
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
928
|
+
"User-Agent": "Mozilla/5.0 (compatible; GroqRAG/1.0)"
|
|
929
|
+
},
|
|
930
|
+
body: `q=${encodeURIComponent(query)}`
|
|
931
|
+
});
|
|
932
|
+
if (!response.ok) {
|
|
933
|
+
throw new Error(`Search failed: ${response.statusText}`);
|
|
934
|
+
}
|
|
935
|
+
const html = await response.text();
|
|
936
|
+
const results = this.parseResults(html, maxResults);
|
|
937
|
+
return results;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
console.error("DuckDuckGo search error:", error);
|
|
940
|
+
return [];
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
parseResults(html, maxResults) {
|
|
944
|
+
const results = [];
|
|
945
|
+
const resultRegex = /<a[^>]+class="result__a"[^>]*href="([^"]+)"[^>]*>([^<]+)<\/a>/gi;
|
|
946
|
+
const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([^<]+(?:<[^>]+>[^<]+<\/[^>]+>)*[^<]*)<\/a>/gi;
|
|
947
|
+
let match;
|
|
948
|
+
let position = 1;
|
|
949
|
+
while ((match = resultRegex.exec(html)) !== null && results.length < maxResults) {
|
|
950
|
+
const url = this.decodeUrl(match[1]);
|
|
951
|
+
const title = this.decodeHtml(match[2]);
|
|
952
|
+
const snippetMatch = snippetRegex.exec(html);
|
|
953
|
+
const snippet = snippetMatch ? this.decodeHtml(snippetMatch[1].replace(/<[^>]+>/g, "")) : "";
|
|
954
|
+
if (url && title && !url.includes("duckduckgo.com")) {
|
|
955
|
+
results.push({
|
|
956
|
+
title,
|
|
957
|
+
url,
|
|
958
|
+
snippet,
|
|
959
|
+
position: position++
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return results;
|
|
964
|
+
}
|
|
965
|
+
decodeUrl(url) {
|
|
966
|
+
try {
|
|
967
|
+
const decoded = decodeURIComponent(url);
|
|
968
|
+
const match = decoded.match(/uddg=([^&]+)/);
|
|
969
|
+
if (match) {
|
|
970
|
+
return decodeURIComponent(match[1]);
|
|
971
|
+
}
|
|
972
|
+
return decoded;
|
|
973
|
+
} catch {
|
|
974
|
+
return url;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
decodeHtml(html) {
|
|
978
|
+
return html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").trim();
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
var BraveSearch = class {
|
|
982
|
+
apiKey;
|
|
983
|
+
baseUrl = "https://api.search.brave.com/res/v1/web/search";
|
|
984
|
+
constructor(apiKey) {
|
|
985
|
+
this.apiKey = apiKey;
|
|
986
|
+
}
|
|
987
|
+
async search(query, options = {}) {
|
|
988
|
+
const { maxResults = 10, safeSearch = true } = options;
|
|
989
|
+
const params = new URLSearchParams({
|
|
990
|
+
q: query,
|
|
991
|
+
count: String(maxResults),
|
|
992
|
+
safesearch: safeSearch ? "moderate" : "off"
|
|
993
|
+
});
|
|
994
|
+
const response = await fetch(`${this.baseUrl}?${params}`, {
|
|
995
|
+
headers: {
|
|
996
|
+
"Accept": "application/json",
|
|
997
|
+
"X-Subscription-Token": this.apiKey
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
if (!response.ok) {
|
|
1001
|
+
throw new Error(`Brave search failed: ${response.statusText}`);
|
|
1002
|
+
}
|
|
1003
|
+
const data = await response.json();
|
|
1004
|
+
return (data.web?.results || []).map((result, index) => ({
|
|
1005
|
+
title: result.title,
|
|
1006
|
+
url: result.url,
|
|
1007
|
+
snippet: result.description,
|
|
1008
|
+
position: index + 1
|
|
1009
|
+
}));
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
var SerperSearch = class {
|
|
1013
|
+
apiKey;
|
|
1014
|
+
baseUrl = "https://google.serper.dev/search";
|
|
1015
|
+
constructor(apiKey) {
|
|
1016
|
+
this.apiKey = apiKey;
|
|
1017
|
+
}
|
|
1018
|
+
async search(query, options = {}) {
|
|
1019
|
+
const { maxResults = 10 } = options;
|
|
1020
|
+
const response = await fetch(this.baseUrl, {
|
|
1021
|
+
method: "POST",
|
|
1022
|
+
headers: {
|
|
1023
|
+
"Content-Type": "application/json",
|
|
1024
|
+
"X-API-KEY": this.apiKey
|
|
1025
|
+
},
|
|
1026
|
+
body: JSON.stringify({
|
|
1027
|
+
q: query,
|
|
1028
|
+
num: maxResults
|
|
1029
|
+
})
|
|
1030
|
+
});
|
|
1031
|
+
if (!response.ok) {
|
|
1032
|
+
throw new Error(`Serper search failed: ${response.statusText}`);
|
|
1033
|
+
}
|
|
1034
|
+
const data = await response.json();
|
|
1035
|
+
return (data.organic || []).map((result) => ({
|
|
1036
|
+
title: result.title,
|
|
1037
|
+
url: result.link,
|
|
1038
|
+
snippet: result.snippet,
|
|
1039
|
+
position: result.position
|
|
1040
|
+
}));
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
function createSearchProvider(config) {
|
|
1044
|
+
const { provider = "duckduckgo", apiKey } = config || {};
|
|
1045
|
+
switch (provider) {
|
|
1046
|
+
case "brave":
|
|
1047
|
+
if (!apiKey) throw new Error("Brave Search requires an API key");
|
|
1048
|
+
return new BraveSearch(apiKey);
|
|
1049
|
+
case "serper":
|
|
1050
|
+
if (!apiKey) throw new Error("Serper Search requires an API key");
|
|
1051
|
+
return new SerperSearch(apiKey);
|
|
1052
|
+
case "duckduckgo":
|
|
1053
|
+
default:
|
|
1054
|
+
return new DuckDuckGoSearch();
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/tools/executor.ts
|
|
1059
|
+
var ToolExecutor = class {
|
|
1060
|
+
tools = /* @__PURE__ */ new Map();
|
|
1061
|
+
constructor(tools = []) {
|
|
1062
|
+
for (const tool of tools) {
|
|
1063
|
+
this.register(tool);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Register a tool
|
|
1068
|
+
*/
|
|
1069
|
+
register(tool) {
|
|
1070
|
+
this.tools.set(tool.name, tool);
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Unregister a tool
|
|
1074
|
+
*/
|
|
1075
|
+
unregister(name) {
|
|
1076
|
+
return this.tools.delete(name);
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Get all registered tools
|
|
1080
|
+
*/
|
|
1081
|
+
getTools() {
|
|
1082
|
+
return Array.from(this.tools.values());
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Get tools in Groq API format
|
|
1086
|
+
*/
|
|
1087
|
+
getToolsForAPI() {
|
|
1088
|
+
return this.getTools().map((tool) => ({
|
|
1089
|
+
type: "function",
|
|
1090
|
+
function: {
|
|
1091
|
+
name: tool.name,
|
|
1092
|
+
description: tool.description,
|
|
1093
|
+
parameters: tool.parameters
|
|
1094
|
+
}
|
|
1095
|
+
}));
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Check if a tool exists
|
|
1099
|
+
*/
|
|
1100
|
+
has(name) {
|
|
1101
|
+
return this.tools.has(name);
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Execute a tool by name
|
|
1105
|
+
*/
|
|
1106
|
+
async execute(name, params) {
|
|
1107
|
+
const startTime = Date.now();
|
|
1108
|
+
const tool = this.tools.get(name);
|
|
1109
|
+
if (!tool) {
|
|
1110
|
+
return {
|
|
1111
|
+
name,
|
|
1112
|
+
result: null,
|
|
1113
|
+
error: `Tool "${name}" not found`,
|
|
1114
|
+
executionTime: Date.now() - startTime
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
try {
|
|
1118
|
+
const result = await tool.execute(params);
|
|
1119
|
+
return {
|
|
1120
|
+
name,
|
|
1121
|
+
result,
|
|
1122
|
+
executionTime: Date.now() - startTime
|
|
1123
|
+
};
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
return {
|
|
1126
|
+
name,
|
|
1127
|
+
result: null,
|
|
1128
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1129
|
+
executionTime: Date.now() - startTime
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Execute multiple tool calls in parallel
|
|
1135
|
+
*/
|
|
1136
|
+
async executeMany(calls) {
|
|
1137
|
+
return Promise.all(
|
|
1138
|
+
calls.map((call) => this.execute(call.name, call.params))
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Process tool calls from Groq API response
|
|
1143
|
+
*/
|
|
1144
|
+
async processToolCalls(toolCalls) {
|
|
1145
|
+
const results = await Promise.all(
|
|
1146
|
+
toolCalls.map(async (toolCall) => {
|
|
1147
|
+
let params = {};
|
|
1148
|
+
try {
|
|
1149
|
+
params = JSON.parse(toolCall.function.arguments);
|
|
1150
|
+
} catch {
|
|
1151
|
+
}
|
|
1152
|
+
const result = await this.execute(toolCall.function.name, params);
|
|
1153
|
+
return {
|
|
1154
|
+
toolCallId: toolCall.id,
|
|
1155
|
+
result
|
|
1156
|
+
};
|
|
1157
|
+
})
|
|
1158
|
+
);
|
|
1159
|
+
return results;
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Format tool results as messages for Groq API
|
|
1163
|
+
*/
|
|
1164
|
+
formatToolResultsAsMessages(results) {
|
|
1165
|
+
return results.map(({ toolCallId, result }) => ({
|
|
1166
|
+
role: "tool",
|
|
1167
|
+
tool_call_id: toolCallId,
|
|
1168
|
+
content: result.error ? `Error: ${result.error}` : JSON.stringify(result.result)
|
|
1169
|
+
}));
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
function createToolExecutor(tools) {
|
|
1173
|
+
return new ToolExecutor(tools);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// src/agents/agent.ts
|
|
1177
|
+
function parseTextFunctionCall(text) {
|
|
1178
|
+
const match = text.match(/<function=(\w+)(\{[\s\S]*?\})<\/function>/);
|
|
1179
|
+
if (match) {
|
|
1180
|
+
try {
|
|
1181
|
+
const name = match[1];
|
|
1182
|
+
const args = JSON.parse(match[2]);
|
|
1183
|
+
return { name, args };
|
|
1184
|
+
} catch {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
function extractFailedGeneration(error) {
|
|
1191
|
+
if (error instanceof Error) {
|
|
1192
|
+
const jsonMatch = error.message.match(/\{[\s\S]*\}/);
|
|
1193
|
+
if (jsonMatch) {
|
|
1194
|
+
try {
|
|
1195
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
1196
|
+
return parsed.error?.failed_generation || null;
|
|
1197
|
+
} catch {
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const err = error;
|
|
1203
|
+
return err.error?.failed_generation || null;
|
|
1204
|
+
}
|
|
1205
|
+
var Agent = class {
|
|
1206
|
+
client;
|
|
1207
|
+
config;
|
|
1208
|
+
executor;
|
|
1209
|
+
messages;
|
|
1210
|
+
constructor(client, config = {}) {
|
|
1211
|
+
this.client = client;
|
|
1212
|
+
this.config = {
|
|
1213
|
+
name: config.name || "Agent",
|
|
1214
|
+
model: config.model || "llama-3.3-70b-versatile",
|
|
1215
|
+
systemPrompt: config.systemPrompt || this.getDefaultSystemPrompt(),
|
|
1216
|
+
tools: config.tools || [],
|
|
1217
|
+
maxIterations: config.maxIterations || 10,
|
|
1218
|
+
verbose: config.verbose ?? false,
|
|
1219
|
+
memory: config.memory || { messages: [] }
|
|
1220
|
+
};
|
|
1221
|
+
this.executor = new ToolExecutor(this.config.tools);
|
|
1222
|
+
this.messages = [
|
|
1223
|
+
{ role: "system", content: this.config.systemPrompt },
|
|
1224
|
+
...this.config.memory.messages
|
|
1225
|
+
];
|
|
1226
|
+
}
|
|
1227
|
+
getDefaultSystemPrompt() {
|
|
1228
|
+
return `You are a helpful AI assistant. You have access to tools that you can use to help answer questions.
|
|
1229
|
+
|
|
1230
|
+
IMPORTANT: When you need to use a tool, the system will automatically handle the tool calling for you. Just respond naturally and the tools will be invoked through the API's function calling mechanism. Do NOT write out function calls as text like "<function=..." - that format is not supported.
|
|
1231
|
+
|
|
1232
|
+
Available capabilities:
|
|
1233
|
+
- Search the web for current information
|
|
1234
|
+
- Fetch and read content from URLs
|
|
1235
|
+
- Query the knowledge base for indexed documents
|
|
1236
|
+
- Perform mathematical calculations
|
|
1237
|
+
- Get current date and time
|
|
1238
|
+
|
|
1239
|
+
When you don't know something or need current information, use the appropriate tool. Provide clear, helpful responses based on the information you gather.`;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Add a tool to the agent
|
|
1243
|
+
*/
|
|
1244
|
+
addTool(tool) {
|
|
1245
|
+
this.config.tools.push(tool);
|
|
1246
|
+
this.executor.register(tool);
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Run the agent with a user message
|
|
1250
|
+
*/
|
|
1251
|
+
async run(input) {
|
|
1252
|
+
const steps = [];
|
|
1253
|
+
const toolCalls = [];
|
|
1254
|
+
this.messages.push({ role: "user", content: input });
|
|
1255
|
+
let iteration = 0;
|
|
1256
|
+
let totalTokens = 0;
|
|
1257
|
+
while (iteration < this.config.maxIterations) {
|
|
1258
|
+
iteration++;
|
|
1259
|
+
if (this.config.verbose) {
|
|
1260
|
+
console.log(`
|
|
1261
|
+
[Agent] Iteration ${iteration}`);
|
|
1262
|
+
}
|
|
1263
|
+
let response;
|
|
1264
|
+
let textFunctionCall = null;
|
|
1265
|
+
try {
|
|
1266
|
+
response = await this.client.chat.completions.create({
|
|
1267
|
+
model: this.config.model,
|
|
1268
|
+
messages: this.messages,
|
|
1269
|
+
tools: this.executor.getToolsForAPI(),
|
|
1270
|
+
tool_choice: "auto"
|
|
1271
|
+
});
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
const failedGen = extractFailedGeneration(error);
|
|
1274
|
+
if (failedGen) {
|
|
1275
|
+
textFunctionCall = parseTextFunctionCall(failedGen);
|
|
1276
|
+
if (textFunctionCall) {
|
|
1277
|
+
const result = await this.executor.execute(textFunctionCall.name, textFunctionCall.args);
|
|
1278
|
+
toolCalls.push(result);
|
|
1279
|
+
steps.push({
|
|
1280
|
+
action: textFunctionCall.name,
|
|
1281
|
+
actionInput: textFunctionCall.args,
|
|
1282
|
+
observation: result.error || JSON.stringify(result.result)
|
|
1283
|
+
});
|
|
1284
|
+
this.messages.push({
|
|
1285
|
+
role: "assistant",
|
|
1286
|
+
content: `I'll use the ${textFunctionCall.name} tool to help with this.`
|
|
1287
|
+
});
|
|
1288
|
+
this.messages.push({
|
|
1289
|
+
role: "user",
|
|
1290
|
+
content: `Tool result: ${result.error || JSON.stringify(result.result)}`
|
|
1291
|
+
});
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
throw error;
|
|
1296
|
+
}
|
|
1297
|
+
const choice = response.choices[0];
|
|
1298
|
+
const message = choice.message;
|
|
1299
|
+
totalTokens += response.usage?.total_tokens || 0;
|
|
1300
|
+
this.messages.push(message);
|
|
1301
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
1302
|
+
if (this.config.verbose) {
|
|
1303
|
+
console.log(`[Agent] Tool calls:`, message.tool_calls.map((tc) => tc.function.name));
|
|
1304
|
+
}
|
|
1305
|
+
const results = await this.executor.processToolCalls(message.tool_calls);
|
|
1306
|
+
for (const { toolCallId, result } of results) {
|
|
1307
|
+
const toolCall = message.tool_calls.find((tc) => tc.id === toolCallId);
|
|
1308
|
+
steps.push({
|
|
1309
|
+
action: toolCall?.function.name,
|
|
1310
|
+
actionInput: toolCall ? JSON.parse(toolCall.function.arguments) : {},
|
|
1311
|
+
observation: result.error || JSON.stringify(result.result)
|
|
1312
|
+
});
|
|
1313
|
+
toolCalls.push(result);
|
|
1314
|
+
}
|
|
1315
|
+
const toolMessages = this.executor.formatToolResultsAsMessages(results);
|
|
1316
|
+
this.messages.push(...toolMessages);
|
|
1317
|
+
if (this.config.verbose) {
|
|
1318
|
+
console.log(`[Agent] Tool results:`, results.map((r) => r.result));
|
|
1319
|
+
}
|
|
1320
|
+
} else {
|
|
1321
|
+
steps.push({
|
|
1322
|
+
thought: message.content || "",
|
|
1323
|
+
isFinal: true
|
|
1324
|
+
});
|
|
1325
|
+
if (this.config.verbose) {
|
|
1326
|
+
console.log(`[Agent] Final response:`, message.content?.substring(0, 100));
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
output: message.content || "",
|
|
1330
|
+
steps,
|
|
1331
|
+
toolCalls,
|
|
1332
|
+
totalTokens
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
if (choice.finish_reason === "stop" && !message.tool_calls) {
|
|
1336
|
+
steps.push({
|
|
1337
|
+
thought: message.content || "",
|
|
1338
|
+
isFinal: true
|
|
1339
|
+
});
|
|
1340
|
+
return {
|
|
1341
|
+
output: message.content || "",
|
|
1342
|
+
steps,
|
|
1343
|
+
toolCalls,
|
|
1344
|
+
totalTokens
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
return {
|
|
1349
|
+
output: "Agent reached maximum iterations without completing the task.",
|
|
1350
|
+
steps,
|
|
1351
|
+
toolCalls,
|
|
1352
|
+
totalTokens
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Run with streaming output
|
|
1357
|
+
*/
|
|
1358
|
+
async *runStream(input) {
|
|
1359
|
+
this.messages.push({ role: "user", content: input });
|
|
1360
|
+
let iteration = 0;
|
|
1361
|
+
const steps = [];
|
|
1362
|
+
const toolCalls = [];
|
|
1363
|
+
while (iteration < this.config.maxIterations) {
|
|
1364
|
+
iteration++;
|
|
1365
|
+
let response;
|
|
1366
|
+
try {
|
|
1367
|
+
response = await this.client.chat.completions.create({
|
|
1368
|
+
model: this.config.model,
|
|
1369
|
+
messages: this.messages,
|
|
1370
|
+
tools: this.executor.getToolsForAPI(),
|
|
1371
|
+
tool_choice: "auto",
|
|
1372
|
+
stream: true
|
|
1373
|
+
});
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
const failedGen = extractFailedGeneration(error);
|
|
1376
|
+
if (failedGen) {
|
|
1377
|
+
const textFunctionCall = parseTextFunctionCall(failedGen);
|
|
1378
|
+
if (textFunctionCall) {
|
|
1379
|
+
yield { type: "tool_call", data: { name: textFunctionCall.name, arguments: JSON.stringify(textFunctionCall.args) } };
|
|
1380
|
+
const result = await this.executor.execute(textFunctionCall.name, textFunctionCall.args);
|
|
1381
|
+
yield { type: "tool_result", data: result };
|
|
1382
|
+
toolCalls.push(result);
|
|
1383
|
+
this.messages.push({
|
|
1384
|
+
role: "assistant",
|
|
1385
|
+
content: `Using ${textFunctionCall.name} tool.`
|
|
1386
|
+
});
|
|
1387
|
+
this.messages.push({
|
|
1388
|
+
role: "user",
|
|
1389
|
+
content: `Tool result: ${result.error || JSON.stringify(result.result)}`
|
|
1390
|
+
});
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
throw error;
|
|
1395
|
+
}
|
|
1396
|
+
let content = "";
|
|
1397
|
+
const currentToolCalls = [];
|
|
1398
|
+
for await (const chunk of response) {
|
|
1399
|
+
const delta = chunk.choices[0]?.delta;
|
|
1400
|
+
if (delta?.content) {
|
|
1401
|
+
content += delta.content;
|
|
1402
|
+
yield { type: "content", data: delta.content };
|
|
1403
|
+
}
|
|
1404
|
+
if (delta?.tool_calls) {
|
|
1405
|
+
for (const tc of delta.tool_calls) {
|
|
1406
|
+
if (tc.index !== void 0) {
|
|
1407
|
+
if (!currentToolCalls[tc.index]) {
|
|
1408
|
+
currentToolCalls[tc.index] = {
|
|
1409
|
+
id: tc.id || "",
|
|
1410
|
+
type: "function",
|
|
1411
|
+
function: { name: tc.function?.name || "", arguments: "" }
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
if (tc.id) currentToolCalls[tc.index].id = tc.id;
|
|
1415
|
+
if (tc.function?.name) currentToolCalls[tc.index].function.name = tc.function.name;
|
|
1416
|
+
if (tc.function?.arguments) {
|
|
1417
|
+
currentToolCalls[tc.index].function.arguments += tc.function.arguments;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (currentToolCalls.length > 0) {
|
|
1424
|
+
this.messages.push({
|
|
1425
|
+
role: "assistant",
|
|
1426
|
+
content: content || null,
|
|
1427
|
+
tool_calls: currentToolCalls
|
|
1428
|
+
});
|
|
1429
|
+
for (const tc of currentToolCalls) {
|
|
1430
|
+
yield { type: "tool_call", data: { name: tc.function.name, arguments: tc.function.arguments } };
|
|
1431
|
+
const result = await this.executor.execute(
|
|
1432
|
+
tc.function.name,
|
|
1433
|
+
JSON.parse(tc.function.arguments || "{}")
|
|
1434
|
+
);
|
|
1435
|
+
yield { type: "tool_result", data: result };
|
|
1436
|
+
toolCalls.push(result);
|
|
1437
|
+
this.messages.push({
|
|
1438
|
+
role: "tool",
|
|
1439
|
+
tool_call_id: tc.id,
|
|
1440
|
+
content: result.error || JSON.stringify(result.result)
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
} else {
|
|
1444
|
+
this.messages.push({ role: "assistant", content });
|
|
1445
|
+
yield { type: "done", data: { output: content, steps, toolCalls } };
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
yield { type: "done", data: { output: "Max iterations reached", steps, toolCalls } };
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Clear conversation history
|
|
1453
|
+
*/
|
|
1454
|
+
clearHistory() {
|
|
1455
|
+
this.messages = [{ role: "system", content: this.config.systemPrompt }];
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Get conversation history
|
|
1459
|
+
*/
|
|
1460
|
+
getHistory() {
|
|
1461
|
+
return [...this.messages];
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
function createAgent(client, config) {
|
|
1465
|
+
return new Agent(client, config);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// src/tools/builtins.ts
|
|
1469
|
+
function createWebSearchTool(searchProvider = new DuckDuckGoSearch()) {
|
|
1470
|
+
return {
|
|
1471
|
+
name: "web_search",
|
|
1472
|
+
description: "Search the web for information. Use this to find current information, news, or research topics.",
|
|
1473
|
+
parameters: {
|
|
1474
|
+
type: "object",
|
|
1475
|
+
properties: {
|
|
1476
|
+
query: {
|
|
1477
|
+
type: "string",
|
|
1478
|
+
description: "The search query"
|
|
1479
|
+
},
|
|
1480
|
+
maxResults: {
|
|
1481
|
+
type: "number",
|
|
1482
|
+
description: "Maximum number of results to return (default: 5)"
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
required: ["query"]
|
|
1486
|
+
},
|
|
1487
|
+
execute: async (params) => {
|
|
1488
|
+
const { query, maxResults = 5 } = params;
|
|
1489
|
+
const results = await searchProvider.search(query, { maxResults });
|
|
1490
|
+
return results.map((r) => ({
|
|
1491
|
+
title: r.title,
|
|
1492
|
+
url: r.url,
|
|
1493
|
+
snippet: r.snippet
|
|
1494
|
+
}));
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function createFetchUrlTool(fetcher = new WebFetcher()) {
|
|
1499
|
+
return {
|
|
1500
|
+
name: "fetch_url",
|
|
1501
|
+
description: "Fetch and extract content from a URL. Use this to read web pages, articles, or documentation.",
|
|
1502
|
+
parameters: {
|
|
1503
|
+
type: "object",
|
|
1504
|
+
properties: {
|
|
1505
|
+
url: {
|
|
1506
|
+
type: "string",
|
|
1507
|
+
description: "The URL to fetch"
|
|
1508
|
+
},
|
|
1509
|
+
includeLinks: {
|
|
1510
|
+
type: "boolean",
|
|
1511
|
+
description: "Include links found on the page (default: false)"
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
required: ["url"]
|
|
1515
|
+
},
|
|
1516
|
+
execute: async (params) => {
|
|
1517
|
+
const { url, includeLinks = false } = params;
|
|
1518
|
+
const result = await fetcher.fetch(url, { includeLinks });
|
|
1519
|
+
return {
|
|
1520
|
+
title: result.title,
|
|
1521
|
+
content: result.markdown || result.content,
|
|
1522
|
+
links: result.links?.slice(0, 10),
|
|
1523
|
+
metadata: result.metadata
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function createRAGQueryTool(retriever) {
|
|
1529
|
+
return {
|
|
1530
|
+
name: "rag_query",
|
|
1531
|
+
description: "Search the knowledge base for relevant information. Use this to find information from indexed documents.",
|
|
1532
|
+
parameters: {
|
|
1533
|
+
type: "object",
|
|
1534
|
+
properties: {
|
|
1535
|
+
query: {
|
|
1536
|
+
type: "string",
|
|
1537
|
+
description: "The search query"
|
|
1538
|
+
},
|
|
1539
|
+
topK: {
|
|
1540
|
+
type: "number",
|
|
1541
|
+
description: "Number of results to return (default: 5)"
|
|
1542
|
+
}
|
|
1543
|
+
},
|
|
1544
|
+
required: ["query"]
|
|
1545
|
+
},
|
|
1546
|
+
execute: async (params) => {
|
|
1547
|
+
const { query, topK = 5 } = params;
|
|
1548
|
+
const results = await retriever.retrieve(query, { topK });
|
|
1549
|
+
return results.map((r) => ({
|
|
1550
|
+
content: r.document.content,
|
|
1551
|
+
score: r.score,
|
|
1552
|
+
metadata: r.document.metadata
|
|
1553
|
+
}));
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
function createCalculatorTool() {
|
|
1558
|
+
return {
|
|
1559
|
+
name: "calculator",
|
|
1560
|
+
description: "Perform mathematical calculations. Supports basic arithmetic, powers, and common math functions.",
|
|
1561
|
+
parameters: {
|
|
1562
|
+
type: "object",
|
|
1563
|
+
properties: {
|
|
1564
|
+
expression: {
|
|
1565
|
+
type: "string",
|
|
1566
|
+
description: 'The mathematical expression to evaluate (e.g., "2 + 2", "sqrt(16)", "pow(2, 8)")'
|
|
1567
|
+
}
|
|
1568
|
+
},
|
|
1569
|
+
required: ["expression"]
|
|
1570
|
+
},
|
|
1571
|
+
execute: async (params) => {
|
|
1572
|
+
const { expression } = params;
|
|
1573
|
+
const sanitized = expression.replace(/[^0-9+\-*/().,%\s]/gi, "").replace(/pow/gi, "Math.pow").replace(/sqrt/gi, "Math.sqrt").replace(/abs/gi, "Math.abs").replace(/sin/gi, "Math.sin").replace(/cos/gi, "Math.cos").replace(/tan/gi, "Math.tan").replace(/log/gi, "Math.log").replace(/exp/gi, "Math.exp").replace(/pi/gi, "Math.PI").replace(/e(?![xp])/gi, "Math.E");
|
|
1574
|
+
try {
|
|
1575
|
+
if (!sanitized.trim()) {
|
|
1576
|
+
return { expression, error: "Invalid expression" };
|
|
1577
|
+
}
|
|
1578
|
+
const result = new Function(`return ${sanitized}`)();
|
|
1579
|
+
if (result === void 0 || typeof result === "number" && isNaN(result)) {
|
|
1580
|
+
return { expression, error: "Invalid expression" };
|
|
1581
|
+
}
|
|
1582
|
+
return { expression, result };
|
|
1583
|
+
} catch {
|
|
1584
|
+
return { expression, error: "Invalid expression" };
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function createDateTimeTool() {
|
|
1590
|
+
return {
|
|
1591
|
+
name: "get_datetime",
|
|
1592
|
+
description: "Get the current date and time in a specific timezone.",
|
|
1593
|
+
parameters: {
|
|
1594
|
+
type: "object",
|
|
1595
|
+
properties: {
|
|
1596
|
+
timezone: {
|
|
1597
|
+
type: "string",
|
|
1598
|
+
description: 'Timezone (e.g., "UTC", "America/New_York", "Asia/Tokyo"). Defaults to UTC.'
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
required: []
|
|
1602
|
+
},
|
|
1603
|
+
execute: async (params) => {
|
|
1604
|
+
const { timezone = "UTC" } = params;
|
|
1605
|
+
const now = /* @__PURE__ */ new Date();
|
|
1606
|
+
try {
|
|
1607
|
+
const formatted = now.toLocaleString("en-US", { timeZone: timezone });
|
|
1608
|
+
return {
|
|
1609
|
+
datetime: formatted,
|
|
1610
|
+
timezone,
|
|
1611
|
+
timestamp: now.toISOString(),
|
|
1612
|
+
unix: Math.floor(now.getTime() / 1e3)
|
|
1613
|
+
};
|
|
1614
|
+
} catch {
|
|
1615
|
+
return {
|
|
1616
|
+
datetime: now.toISOString(),
|
|
1617
|
+
timezone: "UTC",
|
|
1618
|
+
timestamp: now.toISOString(),
|
|
1619
|
+
unix: Math.floor(now.getTime() / 1e3)
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
function getBuiltinTools() {
|
|
1626
|
+
return [
|
|
1627
|
+
createWebSearchTool(),
|
|
1628
|
+
createFetchUrlTool(),
|
|
1629
|
+
createCalculatorTool(),
|
|
1630
|
+
createDateTimeTool()
|
|
1631
|
+
];
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// src/client.ts
|
|
1635
|
+
var GroqRAG = class {
|
|
1636
|
+
groq;
|
|
1637
|
+
config;
|
|
1638
|
+
retriever;
|
|
1639
|
+
fetcher;
|
|
1640
|
+
searchProvider;
|
|
1641
|
+
chat;
|
|
1642
|
+
web;
|
|
1643
|
+
rag;
|
|
1644
|
+
constructor(config = {}) {
|
|
1645
|
+
this.config = config;
|
|
1646
|
+
this.groq = new import_groq_sdk2.default({
|
|
1647
|
+
apiKey: config.apiKey || process.env.GROQ_API_KEY,
|
|
1648
|
+
baseURL: config.baseURL,
|
|
1649
|
+
timeout: config.timeout,
|
|
1650
|
+
maxRetries: config.maxRetries
|
|
1651
|
+
});
|
|
1652
|
+
this.fetcher = createFetcher(config.web);
|
|
1653
|
+
this.searchProvider = createSearchProvider();
|
|
1654
|
+
this.chat = new ChatWithRAG(this);
|
|
1655
|
+
this.web = new WebModule(this);
|
|
1656
|
+
this.rag = new RAGModule(this);
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Get the underlying Groq client
|
|
1660
|
+
*/
|
|
1661
|
+
get client() {
|
|
1662
|
+
return this.groq;
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Initialize RAG with vector store and embeddings
|
|
1666
|
+
*/
|
|
1667
|
+
async initRAG(options) {
|
|
1668
|
+
const embeddingConfig = options?.embedding || {
|
|
1669
|
+
provider: "groq"
|
|
1670
|
+
};
|
|
1671
|
+
const vectorStoreConfig = options?.vectorStore || {
|
|
1672
|
+
provider: "memory"
|
|
1673
|
+
};
|
|
1674
|
+
const vectorStore = createVectorStore(vectorStoreConfig);
|
|
1675
|
+
const embeddings = createEmbeddingProvider(embeddingConfig, this.groq);
|
|
1676
|
+
this.retriever = createRetriever(vectorStore, embeddings, options?.chunking);
|
|
1677
|
+
return this.retriever;
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Get the retriever (initializes with defaults if not already initialized)
|
|
1681
|
+
*/
|
|
1682
|
+
async getRetriever() {
|
|
1683
|
+
if (!this.retriever) {
|
|
1684
|
+
await this.initRAG();
|
|
1685
|
+
}
|
|
1686
|
+
return this.retriever;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Get the web fetcher
|
|
1690
|
+
*/
|
|
1691
|
+
getFetcher() {
|
|
1692
|
+
return this.fetcher;
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Get the search provider
|
|
1696
|
+
*/
|
|
1697
|
+
getSearchProvider() {
|
|
1698
|
+
return this.searchProvider;
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Create an agent with tools
|
|
1702
|
+
*/
|
|
1703
|
+
createAgent(config) {
|
|
1704
|
+
return createAgent(this.groq, config);
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Create an agent with built-in tools (web search, fetch, RAG)
|
|
1708
|
+
*/
|
|
1709
|
+
async createAgentWithBuiltins(config) {
|
|
1710
|
+
const retriever = await this.getRetriever();
|
|
1711
|
+
const tools = [
|
|
1712
|
+
...getBuiltinTools(),
|
|
1713
|
+
createRAGQueryTool(retriever),
|
|
1714
|
+
...config?.tools || []
|
|
1715
|
+
];
|
|
1716
|
+
return createAgent(this.groq, { ...config, tools });
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Standard chat completions (passthrough to Groq)
|
|
1720
|
+
*/
|
|
1721
|
+
async complete(params) {
|
|
1722
|
+
return this.groq.chat.completions.create(params);
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Streaming chat completions (passthrough to Groq)
|
|
1726
|
+
*/
|
|
1727
|
+
stream(params) {
|
|
1728
|
+
return this.groq.chat.completions.create(params);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
var ChatWithRAG = class {
|
|
1732
|
+
constructor(parent) {
|
|
1733
|
+
this.parent = parent;
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Chat with RAG-augmented context
|
|
1737
|
+
*/
|
|
1738
|
+
async withRAG(options) {
|
|
1739
|
+
const {
|
|
1740
|
+
messages,
|
|
1741
|
+
model = "llama-3.3-70b-versatile",
|
|
1742
|
+
temperature = 0.7,
|
|
1743
|
+
maxTokens = 1024,
|
|
1744
|
+
topK = 5,
|
|
1745
|
+
minScore = 0,
|
|
1746
|
+
includeMetadata = false,
|
|
1747
|
+
systemPrompt
|
|
1748
|
+
} = options;
|
|
1749
|
+
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
1750
|
+
if (!lastUserMessage || typeof lastUserMessage.content !== "string") {
|
|
1751
|
+
throw new Error("No user message found for RAG retrieval");
|
|
1752
|
+
}
|
|
1753
|
+
const retriever = await this.parent.getRetriever();
|
|
1754
|
+
const results = await retriever.retrieve(lastUserMessage.content, {
|
|
1755
|
+
topK,
|
|
1756
|
+
minScore
|
|
1757
|
+
});
|
|
1758
|
+
const context = formatContext(
|
|
1759
|
+
results.map((r) => ({
|
|
1760
|
+
content: r.document.content,
|
|
1761
|
+
metadata: r.document.metadata
|
|
1762
|
+
})),
|
|
1763
|
+
{ includeMetadata }
|
|
1764
|
+
);
|
|
1765
|
+
const ragSystemPrompt = systemPrompt || `You are a helpful assistant. Use the following context to answer questions accurately.
|
|
1766
|
+
If the context doesn't contain relevant information, say so clearly.
|
|
1767
|
+
|
|
1768
|
+
Context:
|
|
1769
|
+
${context}`;
|
|
1770
|
+
const augmentedMessages = [
|
|
1771
|
+
{ role: "system", content: ragSystemPrompt },
|
|
1772
|
+
...messages
|
|
1773
|
+
];
|
|
1774
|
+
const response = await this.parent.client.chat.completions.create({
|
|
1775
|
+
model,
|
|
1776
|
+
messages: augmentedMessages,
|
|
1777
|
+
temperature,
|
|
1778
|
+
max_tokens: maxTokens
|
|
1779
|
+
});
|
|
1780
|
+
return {
|
|
1781
|
+
content: response.choices[0]?.message?.content || "",
|
|
1782
|
+
sources: results,
|
|
1783
|
+
usage: response.usage ? {
|
|
1784
|
+
promptTokens: response.usage.prompt_tokens,
|
|
1785
|
+
completionTokens: response.usage.completion_tokens,
|
|
1786
|
+
totalTokens: response.usage.total_tokens
|
|
1787
|
+
} : void 0
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Chat with web search augmentation
|
|
1792
|
+
*/
|
|
1793
|
+
async withWebSearch(options) {
|
|
1794
|
+
const {
|
|
1795
|
+
messages,
|
|
1796
|
+
model = "llama-3.3-70b-versatile",
|
|
1797
|
+
searchQuery,
|
|
1798
|
+
maxResults = 5
|
|
1799
|
+
} = options;
|
|
1800
|
+
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
1801
|
+
const query = searchQuery || (typeof lastUserMessage?.content === "string" ? lastUserMessage.content : "");
|
|
1802
|
+
const searchResults = await this.parent.getSearchProvider().search(query, { maxResults });
|
|
1803
|
+
const context = searchResults.map((r, i) => `[${i + 1}] ${r.title}
|
|
1804
|
+
${r.snippet}
|
|
1805
|
+
URL: ${r.url}`).join("\n\n");
|
|
1806
|
+
const systemPrompt = `You are a helpful assistant with access to web search results.
|
|
1807
|
+
Use the following search results to answer the question. Cite sources using [1], [2], etc.
|
|
1808
|
+
|
|
1809
|
+
Search Results:
|
|
1810
|
+
${context}`;
|
|
1811
|
+
const response = await this.parent.client.chat.completions.create({
|
|
1812
|
+
model,
|
|
1813
|
+
messages: [{ role: "system", content: systemPrompt }, ...messages]
|
|
1814
|
+
});
|
|
1815
|
+
return {
|
|
1816
|
+
content: response.choices[0]?.message?.content || "",
|
|
1817
|
+
sources: searchResults
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Chat with URL content
|
|
1822
|
+
*/
|
|
1823
|
+
async withUrl(options) {
|
|
1824
|
+
const { messages, url, model = "llama-3.3-70b-versatile" } = options;
|
|
1825
|
+
const fetchResult = await this.parent.getFetcher().fetch(url);
|
|
1826
|
+
const systemPrompt = `You are a helpful assistant. Use the following web page content to answer questions.
|
|
1827
|
+
|
|
1828
|
+
Title: ${fetchResult.title || "Unknown"}
|
|
1829
|
+
URL: ${url}
|
|
1830
|
+
|
|
1831
|
+
Content:
|
|
1832
|
+
${fetchResult.markdown || fetchResult.content}`;
|
|
1833
|
+
const response = await this.parent.client.chat.completions.create({
|
|
1834
|
+
model,
|
|
1835
|
+
messages: [{ role: "system", content: systemPrompt }, ...messages]
|
|
1836
|
+
});
|
|
1837
|
+
return {
|
|
1838
|
+
content: response.choices[0]?.message?.content || "",
|
|
1839
|
+
source: fetchResult
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
var WebModule = class {
|
|
1844
|
+
constructor(parent) {
|
|
1845
|
+
this.parent = parent;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Fetch a URL
|
|
1849
|
+
*/
|
|
1850
|
+
async fetch(url, options) {
|
|
1851
|
+
return this.parent.getFetcher().fetch(url, options);
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Fetch multiple URLs
|
|
1855
|
+
*/
|
|
1856
|
+
async fetchMany(urls, options) {
|
|
1857
|
+
return this.parent.getFetcher().fetchMany(urls, options);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Search the web
|
|
1861
|
+
*/
|
|
1862
|
+
async search(query, options) {
|
|
1863
|
+
return this.parent.getSearchProvider().search(query, options);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Fetch URL and convert to markdown
|
|
1867
|
+
*/
|
|
1868
|
+
async fetchMarkdown(url) {
|
|
1869
|
+
return this.parent.getFetcher().fetchMarkdown(url);
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
var RAGModule = class {
|
|
1873
|
+
constructor(parent) {
|
|
1874
|
+
this.parent = parent;
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Add a document to the knowledge base
|
|
1878
|
+
*/
|
|
1879
|
+
async addDocument(content, metadata) {
|
|
1880
|
+
const retriever = await this.parent.getRetriever();
|
|
1881
|
+
return retriever.addDocument(content, metadata);
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Add multiple documents
|
|
1885
|
+
*/
|
|
1886
|
+
async addDocuments(documents) {
|
|
1887
|
+
const retriever = await this.parent.getRetriever();
|
|
1888
|
+
return retriever.addDocuments(documents);
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Add a URL's content to the knowledge base
|
|
1892
|
+
*/
|
|
1893
|
+
async addUrl(url, metadata) {
|
|
1894
|
+
const fetcher = this.parent.getFetcher();
|
|
1895
|
+
const result = await fetcher.fetch(url);
|
|
1896
|
+
const content = result.markdown || result.content;
|
|
1897
|
+
const retriever = await this.parent.getRetriever();
|
|
1898
|
+
return retriever.addDocument(content, {
|
|
1899
|
+
...metadata,
|
|
1900
|
+
url,
|
|
1901
|
+
title: result.title,
|
|
1902
|
+
fetchedAt: result.fetchedAt.toISOString()
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Query the knowledge base
|
|
1907
|
+
*/
|
|
1908
|
+
async query(query, options) {
|
|
1909
|
+
const retriever = await this.parent.getRetriever();
|
|
1910
|
+
return retriever.retrieve(query, options);
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Get formatted context for a query
|
|
1914
|
+
*/
|
|
1915
|
+
async getContext(query, options) {
|
|
1916
|
+
const retriever = await this.parent.getRetriever();
|
|
1917
|
+
return retriever.getContext(query, options);
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Clear the knowledge base
|
|
1921
|
+
*/
|
|
1922
|
+
async clear() {
|
|
1923
|
+
const retriever = await this.parent.getRetriever();
|
|
1924
|
+
return retriever.clear();
|
|
1925
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* Get document count
|
|
1928
|
+
*/
|
|
1929
|
+
async count() {
|
|
1930
|
+
const retriever = await this.parent.getRetriever();
|
|
1931
|
+
return retriever.count();
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
// src/index.ts
|
|
1936
|
+
var index_default = GroqRAG;
|
|
1937
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1938
|
+
0 && (module.exports = {
|
|
1939
|
+
Agent,
|
|
1940
|
+
BraveSearch,
|
|
1941
|
+
ChromaVectorStore,
|
|
1942
|
+
DuckDuckGoSearch,
|
|
1943
|
+
GroqEmbeddings,
|
|
1944
|
+
GroqRAG,
|
|
1945
|
+
MemoryVectorStore,
|
|
1946
|
+
OpenAIEmbeddings,
|
|
1947
|
+
Retriever,
|
|
1948
|
+
SerperSearch,
|
|
1949
|
+
TextChunker,
|
|
1950
|
+
ToolExecutor,
|
|
1951
|
+
WebFetcher,
|
|
1952
|
+
batch,
|
|
1953
|
+
chunkText,
|
|
1954
|
+
cleanText,
|
|
1955
|
+
cosineSimilarity,
|
|
1956
|
+
createAgent,
|
|
1957
|
+
createCalculatorTool,
|
|
1958
|
+
createDateTimeTool,
|
|
1959
|
+
createEmbeddingProvider,
|
|
1960
|
+
createFetchUrlTool,
|
|
1961
|
+
createFetcher,
|
|
1962
|
+
createRAGQueryTool,
|
|
1963
|
+
createRetriever,
|
|
1964
|
+
createSearchProvider,
|
|
1965
|
+
createToolExecutor,
|
|
1966
|
+
createVectorStore,
|
|
1967
|
+
createWebSearchTool,
|
|
1968
|
+
estimateTokens,
|
|
1969
|
+
extractUrls,
|
|
1970
|
+
formatContext,
|
|
1971
|
+
generateId,
|
|
1972
|
+
getBuiltinTools,
|
|
1973
|
+
retry,
|
|
1974
|
+
safeJsonParse,
|
|
1975
|
+
sleep,
|
|
1976
|
+
truncateToTokens
|
|
1977
|
+
});
|