extract-webpage 1.2.50 → 1.2.51
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/extract-webpage.cjs.js +1 -1
- package/dist/extract-webpage.cjs.js.map +1 -1
- package/dist/extract-webpage.es.js +3 -3
- package/dist/extract-webpage.es.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/package.json +4 -11
- package/src/html-to-content/html-utils.ts +8 -44
- package/src/index.ts +0 -3
- package/dist/search/index.d.ts +0 -14
- package/dist/search/meta-search-agent-reexport.d.ts +0 -8
- package/dist/search/public-searxng.d.ts +0 -47
- package/dist/search/search-web.d.ts +0 -33
- package/dist/search/tavily.d.ts +0 -20
- package/dist/search/url-to-html.d.ts +0 -62
- package/src/search/__tests__/public-searxng.test.ts +0 -529
- package/src/search/index.ts +0 -45
- package/src/search/meta-search-agent-reexport.ts +0 -38
- package/src/search/public-searxng.ts +0 -470
- package/src/search/search-web.ts +0 -668
- package/src/search/tavily.ts +0 -106
- package/src/search/url-to-html.ts +0 -278
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Unit tests for SearXNG search functionality
|
|
3
|
-
*/
|
|
4
|
-
import { searchWeb, searchSearxng } from "../public-searxng";
|
|
5
|
-
import grab from "../utils/grab";
|
|
6
|
-
|
|
7
|
-
// Mock grab-url
|
|
8
|
-
jest.mock("grab-url");
|
|
9
|
-
const mockGrab = grab as jest.MockedFunction<typeof grab>;
|
|
10
|
-
|
|
11
|
-
describe("searchWeb", () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
jest.clearAllMocks();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
describe("Private SearXNG instance (JSON)", () => {
|
|
17
|
-
it("should search with private SearXNG instance", async () => {
|
|
18
|
-
const mockResults = {
|
|
19
|
-
results: [
|
|
20
|
-
{
|
|
21
|
-
title: "Test Result 1",
|
|
22
|
-
url: "https://example.com/1",
|
|
23
|
-
content: "Test snippet 1",
|
|
24
|
-
score: 0.95,
|
|
25
|
-
metadata: "2024-01-01 | Example News",
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
title: "Test Result 2",
|
|
29
|
-
url: "https://example.com/2",
|
|
30
|
-
content: "Test snippet 2",
|
|
31
|
-
score: 0.85,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
suggestions: ["suggestion1", "suggestion2"],
|
|
35
|
-
infoboxes: [],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
39
|
-
|
|
40
|
-
const result = await searchWeb("test query", {
|
|
41
|
-
privateSearxng: "https://search.example.com",
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
45
|
-
"https://search.example.com/search",
|
|
46
|
-
expect.objectContaining({
|
|
47
|
-
q: expect.stringContaining("test"),
|
|
48
|
-
format: "json",
|
|
49
|
-
})
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(result).toHaveProperty("results");
|
|
53
|
-
expect(result).toHaveProperty("suggestions");
|
|
54
|
-
if (!Array.isArray(result)) {
|
|
55
|
-
expect(result.results).toHaveLength(2);
|
|
56
|
-
expect(result.results[0].title).toBe("Test Result 1");
|
|
57
|
-
expect(result.results[0].score).toBe(0.95);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should parse metadata for date and source", async () => {
|
|
62
|
-
const mockResults = {
|
|
63
|
-
results: [
|
|
64
|
-
{
|
|
65
|
-
title: "Article with Metadata",
|
|
66
|
-
url: "https://example.com/article",
|
|
67
|
-
content: "Content",
|
|
68
|
-
score: 0.9,
|
|
69
|
-
metadata: "Jan 15, 2024 | TechNews",
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
suggestions: [],
|
|
73
|
-
infoboxes: [],
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
77
|
-
|
|
78
|
-
const result = await searchWeb("test", {
|
|
79
|
-
privateSearxng: "https://search.example.com",
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (!Array.isArray(result) && result.results.length > 0) {
|
|
83
|
-
const firstResult = result.results[0];
|
|
84
|
-
expect(firstResult.date).toBeDefined();
|
|
85
|
-
expect(firstResult.source).toBeDefined();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("should clean HTML entities from titles", async () => {
|
|
90
|
-
const mockResults = {
|
|
91
|
-
results: [
|
|
92
|
-
{
|
|
93
|
-
title: "Test & Title with <HTML> Entities",
|
|
94
|
-
url: "https://example.com",
|
|
95
|
-
content: "Content",
|
|
96
|
-
score: 0.9,
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
suggestions: [],
|
|
100
|
-
infoboxes: [],
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
104
|
-
|
|
105
|
-
const result = await searchWeb("test", {
|
|
106
|
-
privateSearxng: "https://search.example.com",
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
if (!Array.isArray(result) && result.results.length > 0) {
|
|
110
|
-
expect(result.results[0].title).not.toContain("&");
|
|
111
|
-
expect(result.results[0].title).toContain("&");
|
|
112
|
-
expect(result.results[0].title).not.toContain("<");
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should handle breadcrumbed titles", async () => {
|
|
117
|
-
const mockResults = {
|
|
118
|
-
results: [
|
|
119
|
-
{
|
|
120
|
-
title: "Site Name | Very Long Article Title Here",
|
|
121
|
-
url: "https://example.com",
|
|
122
|
-
content: "Content",
|
|
123
|
-
score: 0.9,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
suggestions: [],
|
|
127
|
-
infoboxes: [],
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
131
|
-
|
|
132
|
-
const result = await searchWeb("test", {
|
|
133
|
-
privateSearxng: "https://search.example.com",
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (!Array.isArray(result) && result.results.length > 0) {
|
|
137
|
-
// Should extract the longest part
|
|
138
|
-
expect(result.results[0].title).toContain("Very Long Article Title Here");
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should add favicon URLs", async () => {
|
|
143
|
-
const mockResults = {
|
|
144
|
-
results: [
|
|
145
|
-
{
|
|
146
|
-
title: "Test",
|
|
147
|
-
url: "https://example.com/page",
|
|
148
|
-
content: "Content",
|
|
149
|
-
score: 0.9,
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
suggestions: [],
|
|
153
|
-
infoboxes: [],
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
157
|
-
|
|
158
|
-
const result = await searchWeb("test", {
|
|
159
|
-
privateSearxng: "https://search.example.com",
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
if (!Array.isArray(result) && result.results.length > 0) {
|
|
163
|
-
expect(result.results[0].favicon).toContain("googleusercontent.com");
|
|
164
|
-
expect(result.results[0].favicon).toContain("example.com");
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("should handle string response by parsing JSON", async () => {
|
|
169
|
-
const mockResultsString = JSON.stringify({
|
|
170
|
-
results: [
|
|
171
|
-
{
|
|
172
|
-
title: "Test",
|
|
173
|
-
url: "https://example.com",
|
|
174
|
-
content: "Content",
|
|
175
|
-
score: 0.9,
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
suggestions: [],
|
|
179
|
-
infoboxes: [],
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
mockGrab.mockResolvedValueOnce(mockResultsString);
|
|
183
|
-
|
|
184
|
-
const result = await searchWeb("test", {
|
|
185
|
-
privateSearxng: "https://search.example.com",
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
if (!Array.isArray(result)) {
|
|
189
|
-
expect(result.results).toHaveLength(1);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("should handle invalid JSON gracefully", async () => {
|
|
194
|
-
mockGrab.mockResolvedValueOnce("Not valid JSON");
|
|
195
|
-
|
|
196
|
-
const result = await searchWeb("test", {
|
|
197
|
-
privateSearxng: "https://search.example.com",
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
if (!Array.isArray(result)) {
|
|
201
|
-
expect(result.results).toEqual([]);
|
|
202
|
-
expect(result.suggestions).toEqual([]);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("should validate URL paths", async () => {
|
|
207
|
-
const mockResults = {
|
|
208
|
-
results: [
|
|
209
|
-
{
|
|
210
|
-
title: "Domain Only",
|
|
211
|
-
url: "https://example.com/", // Just domain
|
|
212
|
-
content: "Content",
|
|
213
|
-
score: 0.9,
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
title: "Full Path",
|
|
217
|
-
url: "https://example.com/article/page",
|
|
218
|
-
content: "Content",
|
|
219
|
-
score: 0.8,
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
suggestions: [],
|
|
223
|
-
infoboxes: [],
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
227
|
-
mockGrab.mockResolvedValueOnce(mockResults);
|
|
228
|
-
|
|
229
|
-
const result = await searchWeb("test", {
|
|
230
|
-
privateSearxng: "https://search.example.com",
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
234
|
-
expect.stringContaining("domain-only"),
|
|
235
|
-
expect.any(String)
|
|
236
|
-
);
|
|
237
|
-
consoleSpy.mockRestore();
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
describe("Public SearXNG instance (HTML scraping)", () => {
|
|
242
|
-
it("should scrape results from HTML", async () => {
|
|
243
|
-
const mockHtml = `
|
|
244
|
-
<article class="result">
|
|
245
|
-
<h3><a href="https://example.com/1">First Result</a></h3>
|
|
246
|
-
<p class="content">First snippet</p>
|
|
247
|
-
</article>
|
|
248
|
-
<article class="result">
|
|
249
|
-
<h3><a href="https://example.com/2">Second Result</a></h3>
|
|
250
|
-
<p class="content">Second snippet</p>
|
|
251
|
-
</article>
|
|
252
|
-
`;
|
|
253
|
-
|
|
254
|
-
mockGrab.mockResolvedValueOnce(mockHtml);
|
|
255
|
-
|
|
256
|
-
const result = await searchWeb("test", {
|
|
257
|
-
privateSearxng: false,
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
expect(Array.isArray(result)).toBe(true);
|
|
261
|
-
if (Array.isArray(result)) {
|
|
262
|
-
expect(result).toHaveLength(2);
|
|
263
|
-
expect(result[0].title).toBe("First Result");
|
|
264
|
-
expect(result[0].url).toBe("https://example.com/1");
|
|
265
|
-
expect(result[0].snippet).toBe("First snippet");
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it("should handle HTML entities in scraped content", async () => {
|
|
270
|
-
const mockHtml = `
|
|
271
|
-
<article class="result">
|
|
272
|
-
<h3><a href="https://example.com?param=value&other=test">Test & Title</a></h3>
|
|
273
|
-
<p class="content">Content with <entities></p>
|
|
274
|
-
</article>
|
|
275
|
-
`;
|
|
276
|
-
|
|
277
|
-
mockGrab.mockResolvedValueOnce(mockHtml);
|
|
278
|
-
|
|
279
|
-
const result = await searchWeb("test", {
|
|
280
|
-
privateSearxng: false,
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
284
|
-
expect(result[0].title).toContain("&");
|
|
285
|
-
expect(result[0].title).not.toContain("&");
|
|
286
|
-
expect(result[0].url).toContain("&");
|
|
287
|
-
expect(result[0].snippet).toContain("<entities>");
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("should add favicons to scraped results", async () => {
|
|
292
|
-
const mockHtml = `
|
|
293
|
-
<article class="result">
|
|
294
|
-
<h3><a href="https://example.com/page">Test</a></h3>
|
|
295
|
-
<p class="content">Content</p>
|
|
296
|
-
</article>
|
|
297
|
-
`;
|
|
298
|
-
|
|
299
|
-
mockGrab.mockResolvedValueOnce(mockHtml);
|
|
300
|
-
|
|
301
|
-
const result = await searchWeb("test", {
|
|
302
|
-
privateSearxng: false,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
if (Array.isArray(result) && result.length > 0) {
|
|
306
|
-
expect(result[0].favicon).toContain("google.com/s2/favicons");
|
|
307
|
-
expect(result[0].domain).toBe("example.com");
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
it("should validate URLs in scraped results", async () => {
|
|
312
|
-
const mockHtml = `
|
|
313
|
-
<article class="result">
|
|
314
|
-
<h3><a href="https://example.com/">Domain Only</a></h3>
|
|
315
|
-
<p class="content">Content</p>
|
|
316
|
-
</article>
|
|
317
|
-
`;
|
|
318
|
-
|
|
319
|
-
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
320
|
-
mockGrab.mockResolvedValueOnce(mockHtml);
|
|
321
|
-
|
|
322
|
-
await searchWeb("test", { privateSearxng: false });
|
|
323
|
-
|
|
324
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
325
|
-
expect.stringContaining("domain-only"),
|
|
326
|
-
expect.any(String)
|
|
327
|
-
);
|
|
328
|
-
consoleSpy.mockRestore();
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
describe("Retry logic", () => {
|
|
333
|
-
it("should retry on fetch failure", async () => {
|
|
334
|
-
mockGrab
|
|
335
|
-
.mockRejectedValueOnce(new Error("Network error"))
|
|
336
|
-
.mockResolvedValueOnce({
|
|
337
|
-
results: [
|
|
338
|
-
{ title: "Retry Success", url: "https://example.com", content: "Content", score: 0.9 },
|
|
339
|
-
],
|
|
340
|
-
suggestions: [],
|
|
341
|
-
infoboxes: [],
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
const result = await searchWeb("test", {
|
|
345
|
-
privateSearxng: "https://search.example.com",
|
|
346
|
-
maxRetries: 3,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
expect(mockGrab).toHaveBeenCalledTimes(2);
|
|
350
|
-
if (!Array.isArray(result)) {
|
|
351
|
-
expect(result.results[0].title).toBe("Retry Success");
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it("should retry on empty results", async () => {
|
|
356
|
-
mockGrab
|
|
357
|
-
.mockResolvedValueOnce("") // Empty HTML
|
|
358
|
-
.mockResolvedValueOnce(`
|
|
359
|
-
<article class="result">
|
|
360
|
-
<h3><a href="https://example.com">Retry Result</a></h3>
|
|
361
|
-
<p class="content">Found on retry</p>
|
|
362
|
-
</article>
|
|
363
|
-
`);
|
|
364
|
-
|
|
365
|
-
const result = await searchWeb("test", {
|
|
366
|
-
privateSearxng: false,
|
|
367
|
-
maxRetries: 2,
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
expect(mockGrab).toHaveBeenCalledTimes(2);
|
|
371
|
-
if (Array.isArray(result)) {
|
|
372
|
-
expect(result[0].title).toBe("Retry Result");
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
it("should return empty array after exhausting retries", async () => {
|
|
377
|
-
mockGrab.mockRejectedValue(new Error("Network error"));
|
|
378
|
-
|
|
379
|
-
const result = await searchWeb("test", {
|
|
380
|
-
privateSearxng: "https://search.example.com",
|
|
381
|
-
maxRetries: 2,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
expect(mockGrab).toHaveBeenCalledTimes(3); // Initial + 2 retries
|
|
385
|
-
expect(result).toEqual([]);
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
describe("Search parameters", () => {
|
|
390
|
-
it("should apply category filter", async () => {
|
|
391
|
-
mockGrab.mockResolvedValueOnce({
|
|
392
|
-
results: [],
|
|
393
|
-
suggestions: [],
|
|
394
|
-
infoboxes: [],
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
await searchWeb("test", {
|
|
398
|
-
privateSearxng: "https://search.example.com",
|
|
399
|
-
category: "news",
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
403
|
-
expect.any(String),
|
|
404
|
-
expect.objectContaining({
|
|
405
|
-
category_news: 1,
|
|
406
|
-
})
|
|
407
|
-
);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("should apply recency filter", async () => {
|
|
411
|
-
mockGrab.mockResolvedValueOnce({
|
|
412
|
-
results: [],
|
|
413
|
-
suggestions: [],
|
|
414
|
-
infoboxes: [],
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
await searchWeb("test", {
|
|
418
|
-
privateSearxng: "https://search.example.com",
|
|
419
|
-
recency: "week",
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
423
|
-
expect.any(String),
|
|
424
|
-
expect.objectContaining({
|
|
425
|
-
time_range: "week",
|
|
426
|
-
})
|
|
427
|
-
);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
it("should handle pagination", async () => {
|
|
431
|
-
mockGrab.mockResolvedValueOnce({
|
|
432
|
-
results: [],
|
|
433
|
-
suggestions: [],
|
|
434
|
-
infoboxes: [],
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
await searchWeb("test", {
|
|
438
|
-
privateSearxng: "https://search.example.com",
|
|
439
|
-
page: 3,
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
443
|
-
expect.any(String),
|
|
444
|
-
expect.objectContaining({
|
|
445
|
-
pageno: 3,
|
|
446
|
-
})
|
|
447
|
-
);
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
it("should apply safesearch", async () => {
|
|
451
|
-
mockGrab.mockResolvedValueOnce({
|
|
452
|
-
results: [],
|
|
453
|
-
suggestions: [],
|
|
454
|
-
infoboxes: [],
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
await searchWeb("test", {
|
|
458
|
-
privateSearxng: "https://search.example.com",
|
|
459
|
-
safesearch: true,
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
463
|
-
expect.any(String),
|
|
464
|
-
expect.objectContaining({
|
|
465
|
-
safesearch: "1",
|
|
466
|
-
})
|
|
467
|
-
);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it("should use custom language", async () => {
|
|
471
|
-
mockGrab.mockResolvedValueOnce({
|
|
472
|
-
results: [],
|
|
473
|
-
suggestions: [],
|
|
474
|
-
infoboxes: [],
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
await searchWeb("test", {
|
|
478
|
-
privateSearxng: "https://search.example.com",
|
|
479
|
-
lang: "es",
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
expect(mockGrab).toHaveBeenCalledWith(
|
|
483
|
-
expect.any(String),
|
|
484
|
-
expect.objectContaining({
|
|
485
|
-
language: "es",
|
|
486
|
-
})
|
|
487
|
-
);
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
describe("searchSearxng", () => {
|
|
493
|
-
beforeEach(() => {
|
|
494
|
-
jest.clearAllMocks();
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
it("should adapt options to searchWeb", async () => {
|
|
498
|
-
mockGrab.mockResolvedValueOnce({
|
|
499
|
-
results: [{ title: "Test", url: "https://example.com", content: "Content", score: 0.9 }],
|
|
500
|
-
suggestions: ["test1"],
|
|
501
|
-
infoboxes: [],
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
const result = await searchSearxng("test query", {
|
|
505
|
-
categories: ["news"],
|
|
506
|
-
pageno: 2,
|
|
507
|
-
language: "fr",
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
expect(result).toHaveProperty("results");
|
|
511
|
-
expect(result).toHaveProperty("suggestions");
|
|
512
|
-
expect(result.results).toHaveLength(1);
|
|
513
|
-
expect(result.suggestions).toHaveLength(1);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
it("should handle array results from searchWeb", async () => {
|
|
517
|
-
mockGrab.mockResolvedValueOnce(`
|
|
518
|
-
<article class="result">
|
|
519
|
-
<h3><a href="https://example.com">Test</a></h3>
|
|
520
|
-
<p class="content">Content</p>
|
|
521
|
-
</article>
|
|
522
|
-
`);
|
|
523
|
-
|
|
524
|
-
const result = await searchSearxng("test", { categories: ["general"] });
|
|
525
|
-
|
|
526
|
-
expect(result.results).toHaveLength(1);
|
|
527
|
-
expect(result.suggestions).toEqual([]);
|
|
528
|
-
});
|
|
529
|
-
});
|
package/src/search/index.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module extract-webpage/search
|
|
3
|
-
* @description Re-exports MetaSearchAgent from agent-toolkit with search functions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createSearchHandlers } from "chat-agent-toolkit";
|
|
7
|
-
import { searchSearxng } from "./public-searxng";
|
|
8
|
-
import { searchTavily, isTavilyConfigured } from "./tavily";
|
|
9
|
-
import { scrapeURL } from "./url-to-html";
|
|
10
|
-
import { getDocumentsFromLinks } from "../utils/documents";
|
|
11
|
-
|
|
12
|
-
// Export search handlers with actual search functions
|
|
13
|
-
export const searchHandlers = createSearchHandlers({
|
|
14
|
-
searchSearxng,
|
|
15
|
-
searchTavily,
|
|
16
|
-
isTavilyConfigured,
|
|
17
|
-
scrapeURL,
|
|
18
|
-
getDocumentsFromLinks,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Re-export everything from agent-toolkit for convenience
|
|
22
|
-
export {
|
|
23
|
-
MetaSearchAgent,
|
|
24
|
-
generateSuggestions,
|
|
25
|
-
groupAndSummarizeDocs,
|
|
26
|
-
buildFallbackDocs,
|
|
27
|
-
rerankDocs,
|
|
28
|
-
processDocs,
|
|
29
|
-
normalizeSourcesOutput,
|
|
30
|
-
registerUploadFileLoader,
|
|
31
|
-
splitTextIntoChunks,
|
|
32
|
-
LineOutputParser,
|
|
33
|
-
LineListOutputParser,
|
|
34
|
-
formatChatHistoryAsString,
|
|
35
|
-
} from "chat-agent-toolkit";
|
|
36
|
-
|
|
37
|
-
export type {
|
|
38
|
-
UploadFileLoader,
|
|
39
|
-
MetaSearchAgentType,
|
|
40
|
-
Config,
|
|
41
|
-
ChatTurnMessage,
|
|
42
|
-
SearchingEvent,
|
|
43
|
-
FewShotExample,
|
|
44
|
-
Document,
|
|
45
|
-
} from "chat-agent-toolkit";
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Re-exports MetaSearchAgent from chat-agent-toolkit
|
|
3
|
-
* @deprecated Import from 'chat-agent-toolkit' instead
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
MetaSearchAgent as default,
|
|
8
|
-
searchHandlers,
|
|
9
|
-
generateSuggestions,
|
|
10
|
-
groupAndSummarizeDocs,
|
|
11
|
-
buildFallbackDocs,
|
|
12
|
-
rerankDocs,
|
|
13
|
-
processDocs,
|
|
14
|
-
normalizeSourcesOutput,
|
|
15
|
-
splitTextIntoChunks,
|
|
16
|
-
} from "chat-agent-toolkit";
|
|
17
|
-
|
|
18
|
-
export type {
|
|
19
|
-
MetaSearchAgentType,
|
|
20
|
-
Config,
|
|
21
|
-
ChatTurnMessage,
|
|
22
|
-
SearchingEvent,
|
|
23
|
-
FewShotExample,
|
|
24
|
-
Document,
|
|
25
|
-
} from "chat-agent-toolkit";
|
|
26
|
-
|
|
27
|
-
export {
|
|
28
|
-
LineOutputParser,
|
|
29
|
-
LineListOutputParser,
|
|
30
|
-
formatChatHistoryAsString,
|
|
31
|
-
} from "chat-agent-toolkit";
|
|
32
|
-
|
|
33
|
-
export {
|
|
34
|
-
webSearchResponsePrompt,
|
|
35
|
-
webSearchRetrieverPrompt,
|
|
36
|
-
webSearchRetrieverFewShots,
|
|
37
|
-
writingAssistantPrompt,
|
|
38
|
-
} from "chat-agent-toolkit";
|