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/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(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/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
+ });