search-web-api 1.0.13

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.
Files changed (111) hide show
  1. package/README.md +307 -0
  2. package/demo/index.ts +38 -0
  3. package/demo/openapi.ts +402 -0
  4. package/demo/routes/autocomplete.ts +124 -0
  5. package/demo/routes/search.ts +61 -0
  6. package/docs/AUTOCOMPLETE.md +296 -0
  7. package/docs/CATEGORY_SEARCH.md +265 -0
  8. package/package.json +32 -0
  9. package/src/autocomplete/autocomplete-ai-next-word-predictor.ts +47 -0
  10. package/src/autocomplete/autocomplete-search-engine-backends.ts +231 -0
  11. package/src/category-registry.ts +6 -0
  12. package/src/config/search-engine-constants.ts +216 -0
  13. package/src/constants.ts +6 -0
  14. package/src/engine-descriptions.ts +7 -0
  15. package/src/engine-status.ts +7 -0
  16. package/src/engine.ts +6 -0
  17. package/src/registry/search-engine-category-registry.ts +200 -0
  18. package/src/registry/search-engine-descriptions.ts +121 -0
  19. package/src/registry/search-engine-status-tracker.ts +240 -0
  20. package/src/result-container.ts +8 -0
  21. package/src/search/search-engines-registry-list.ts +205 -0
  22. package/src/search/search-query-executor.ts +168 -0
  23. package/src/search/search-result-container.ts +421 -0
  24. package/src/search-web-types.ts +7 -0
  25. package/src/search.ts +10 -0
  26. package/src/sources/academic/arxiv.ts +72 -0
  27. package/src/sources/academic/core.ts +65 -0
  28. package/src/sources/academic/crossref.ts +75 -0
  29. package/src/sources/academic/doaj.ts +63 -0
  30. package/src/sources/academic/google_scholar.ts +53 -0
  31. package/src/sources/academic/openalex.ts +75 -0
  32. package/src/sources/academic/pubmed.ts +96 -0
  33. package/src/sources/academic/semantic_scholar.ts +77 -0
  34. package/src/sources/academic/wikidata.ts +44 -0
  35. package/src/sources/general/baidu.ts +59 -0
  36. package/src/sources/general/bing.ts +30 -0
  37. package/src/sources/general/brave.ts +54 -0
  38. package/src/sources/general/duckduckgo.ts +49 -0
  39. package/src/sources/general/google.ts +68 -0
  40. package/src/sources/general/mojeek.ts +56 -0
  41. package/src/sources/general/qwant.ts +37 -0
  42. package/src/sources/general/startpage.ts +44 -0
  43. package/src/sources/general/yahoo.ts +41 -0
  44. package/src/sources/general/yandex.ts +56 -0
  45. package/src/sources/images/bing_images.ts +68 -0
  46. package/src/sources/images/deviantart.ts +71 -0
  47. package/src/sources/images/flickr.ts +132 -0
  48. package/src/sources/images/google_images.ts +101 -0
  49. package/src/sources/images/imgur.ts +58 -0
  50. package/src/sources/images/openclipart.ts +52 -0
  51. package/src/sources/images/pixabay.ts +56 -0
  52. package/src/sources/images/unsplash.ts +38 -0
  53. package/src/sources/images/wallhaven.ts +50 -0
  54. package/src/sources/it/crates.ts +41 -0
  55. package/src/sources/it/dockerhub.ts +47 -0
  56. package/src/sources/it/github.ts +37 -0
  57. package/src/sources/it/gitlab.ts +55 -0
  58. package/src/sources/it/npm.ts +36 -0
  59. package/src/sources/it/packagist.ts +43 -0
  60. package/src/sources/it/pypi.ts +30 -0
  61. package/src/sources/it/rubygems.ts +43 -0
  62. package/src/sources/it/stackoverflow.ts +39 -0
  63. package/src/sources/maps/apple_maps.ts +105 -0
  64. package/src/sources/maps/openstreetmap.ts +34 -0
  65. package/src/sources/maps/photon.ts +77 -0
  66. package/src/sources/news/bing_news.ts +95 -0
  67. package/src/sources/news/google_news.ts +80 -0
  68. package/src/sources/news/hackernews.ts +94 -0
  69. package/src/sources/news/yahoo_news.ts +77 -0
  70. package/src/sources/shopping/ebay.ts +96 -0
  71. package/src/sources/social/mastodon.ts +46 -0
  72. package/src/sources/social/medium.ts +52 -0
  73. package/src/sources/social/reddit.ts +48 -0
  74. package/src/sources/social/soundcloud.ts +64 -0
  75. package/src/sources/social/twitter.ts +56 -0
  76. package/src/sources/specialized/annas_archive.ts +97 -0
  77. package/src/sources/specialized/archive.ts +48 -0
  78. package/src/sources/specialized/genius.ts +43 -0
  79. package/src/sources/specialized/goodreads.ts +62 -0
  80. package/src/sources/specialized/imdb.ts +55 -0
  81. package/src/sources/specialized/openlibrary.ts +59 -0
  82. package/src/sources/specialized/wikipedia.ts +37 -0
  83. package/src/sources/specialized/wttr.ts +98 -0
  84. package/src/sources/torrents/1337x.ts +43 -0
  85. package/src/sources/torrents/eztv.ts +47 -0
  86. package/src/sources/torrents/kickass.ts +63 -0
  87. package/src/sources/torrents/nyaa.ts +53 -0
  88. package/src/sources/torrents/solidtorrents.ts +68 -0
  89. package/src/sources/torrents/thepiratebay.ts +57 -0
  90. package/src/sources/torrents/yts.ts +54 -0
  91. package/src/sources/videos/bing_videos.ts +91 -0
  92. package/src/sources/videos/dailymotion.ts +101 -0
  93. package/src/sources/videos/invidious.ts +86 -0
  94. package/src/sources/videos/peertube.ts +76 -0
  95. package/src/sources/videos/vimeo.ts +67 -0
  96. package/src/sources/videos/youtube.ts +78 -0
  97. package/src/suggest-next-words/autocomplete-ai.ts +38 -0
  98. package/src/suggest-next-words/autocomplete-search-engines.ts +384 -0
  99. package/src/suggest-next-words/misspelled-typos-8k.json +1 -0
  100. package/src/types/search-engine-interface.ts +27 -0
  101. package/src/types/search-result-types.ts +405 -0
  102. package/test/api.test.ts +128 -0
  103. package/test/autocomplete-ai.test.ts +20 -0
  104. package/test/autocomplete-engines.test.ts +131 -0
  105. package/test/engine-health-suite.test.ts +350 -0
  106. package/test/search.test.ts +69 -0
  107. package/test/sources-unit.test.ts +1152 -0
  108. package/test/sources.test.ts +182 -0
  109. package/test/test-utils.ts +81 -0
  110. package/tsconfig.json +16 -0
  111. package/vitest.config.ts +20 -0
@@ -0,0 +1,296 @@
1
+ # Autocomplete API Documentation
2
+
3
+ The autocomplete module provides search query suggestions from multiple search engines, helping users quickly find what they're looking for.
4
+
5
+ ## Features
6
+
7
+ - **8 Search Engine Backends**: Google, DuckDuckGo, Brave, Wikipedia, Baidu, Qwant, Startpage, Yandex
8
+ - **Locale Support**: Most backends support different languages and regions
9
+ - **Multi-Backend Queries**: Combine suggestions from multiple engines
10
+ - **Fast Responses**: Optimized with 3-second timeout for quick results
11
+
12
+ ## API Endpoints
13
+
14
+ ### GET /autocomplete
15
+
16
+ Get autocomplete suggestions from a specific backend.
17
+
18
+ **Query Parameters:**
19
+
20
+ - `q` (required): Search query string
21
+ - `backend` (optional): Backend name (default: 'google')
22
+ - `locale` (optional): Language/locale code (default: 'en-US')
23
+
24
+ **Example:**
25
+
26
+ ```bash
27
+ curl "http://localhost:3000/autocomplete?q=typescript&backend=google&locale=en-US"
28
+ ```
29
+
30
+ **Response:**
31
+
32
+ ```json
33
+ {
34
+ "query": "typescript",
35
+ "backend": "google",
36
+ "locale": "en-US",
37
+ "suggestions": [
38
+ "typescript tutorial",
39
+ "typescript vs javascript",
40
+ "typescript generics",
41
+ "typescript interface",
42
+ "typescript types"
43
+ ]
44
+ }
45
+ ```
46
+
47
+ ### GET /autocomplete/multi
48
+
49
+ Get autocomplete suggestions from multiple backends and merge them.
50
+
51
+ **Query Parameters:**
52
+
53
+ - `q` (required): Search query string
54
+ - `backends` (optional): Comma-separated backend names (default: 'google,duckduckgo')
55
+ - `locale` (optional): Language/locale code (default: 'en-US')
56
+
57
+ **Example:**
58
+
59
+ ```bash
60
+ curl "http://localhost:3000/autocomplete/multi?q=python&backends=google,duckduckgo,wikipedia"
61
+ ```
62
+
63
+ **Response:**
64
+
65
+ ```json
66
+ {
67
+ "query": "python",
68
+ "backends": ["google", "duckduckgo", "wikipedia"],
69
+ "locale": "en-US",
70
+ "count": 12,
71
+ "suggestions": [
72
+ "python tutorial",
73
+ "python download",
74
+ "python programming",
75
+ "python (programming language)",
76
+ "python snake",
77
+ ...
78
+ ]
79
+ }
80
+ ```
81
+
82
+ ### GET /autocomplete/backends
83
+
84
+ List all available autocomplete backends.
85
+
86
+ **Example:**
87
+
88
+ ```bash
89
+ curl "http://localhost:3000/autocomplete/backends"
90
+ ```
91
+
92
+ **Response:**
93
+
94
+ ```json
95
+ {
96
+ "backends": [
97
+ {
98
+ "name": "baidu",
99
+ "description": "Baidu (Chinese search engine)"
100
+ },
101
+ {
102
+ "name": "brave",
103
+ "description": "Brave Search"
104
+ },
105
+ ...
106
+ ],
107
+ "count": 8
108
+ }
109
+ ```
110
+
111
+ ## Available Backends
112
+
113
+ | Backend | Description | Locale Support |
114
+ | -------------------- | ------------- | ---------------- |
115
+ | **google** | Google Search | Yes (subdomains) |
116
+ | **duckduckgo** | DuckDuckGo | Yes (regions) |
117
+ | **brave** | Brave Search | Limited |
118
+ | **wikipedia** | Wikipedia | Yes (languages) |
119
+ | **baidu** | Baidu | Chinese |
120
+ | **qwant** | Qwant | Yes (locales) |
121
+ | **startpage** | Startpage | Yes (languages) |
122
+ | **yandex** | Yandex | Russian |
123
+
124
+ ## Programmatic Usage
125
+
126
+ ### TypeScript/JavaScript
127
+
128
+ ```typescript
129
+ import { searchAutocomplete, searchAutocompleteMulti } from './app/lib/autocomplete';
130
+
131
+ // Single backend
132
+ const suggestions = await searchAutocomplete('google', 'typescript');
133
+ console.log(suggestions);
134
+
135
+ // Multiple backends
136
+ const merged = await searchAutocompleteMulti(
137
+ ['google', 'duckduckgo', 'wikipedia'],
138
+ 'javascript',
139
+ 'en-US'
140
+ );
141
+ console.log(merged);
142
+ ```
143
+
144
+ ### Individual Backend Functions
145
+
146
+ ```typescript
147
+ import { google, duckduckgo, wikipedia } from './app/lib/autocomplete';
148
+
149
+ // Google autocomplete
150
+ const googleSuggestions = await google('react', 'en-US');
151
+
152
+ // DuckDuckGo autocomplete
153
+ const ddgSuggestions = await duckduckgo('vue');
154
+
155
+ // Wikipedia autocomplete (German)
156
+ const wikiSuggestions = await wikipedia('quantum physics', 'de');
157
+ ```
158
+
159
+ ## Locale Support
160
+
161
+ ### Google
162
+
163
+ Google uses subdomains based on language:
164
+
165
+ ```typescript
166
+ // English (google.com)
167
+ await google('search query', 'en');
168
+
169
+ // German (google.de)
170
+ await google('Suchanfrage', 'de');
171
+
172
+ // French (google.fr)
173
+ await google('requête de recherche', 'fr');
174
+
175
+ // Japanese (google.co.jp)
176
+ await google('検索クエリ', 'ja');
177
+ ```
178
+
179
+ ### Wikipedia
180
+
181
+ Wikipedia uses language-specific domains:
182
+
183
+ ```typescript
184
+ // English Wikipedia
185
+ await wikipedia('physics', 'en');
186
+
187
+ // German Wikipedia
188
+ await wikipedia('Physik', 'de');
189
+
190
+ // French Wikipedia
191
+ await wikipedia('physique', 'fr');
192
+ ```
193
+
194
+ ### DuckDuckGo
195
+
196
+ DuckDuckGo uses region codes:
197
+
198
+ ```typescript
199
+ // US English
200
+ await duckduckgo('search', 'en-US');
201
+
202
+ // UK English
203
+ await duckduckgo('search', 'en-GB');
204
+
205
+ // German
206
+ await duckduckgo('Suche', 'de-DE');
207
+ ```
208
+
209
+ ## Implementation Details
210
+
211
+ ### Response Timeout
212
+
213
+ All autocomplete requests have a 3-second timeout to ensure fast responses. If a backend doesn't respond within this time, an empty array is returned.
214
+
215
+ ### Error Handling
216
+
217
+ Errors are caught and logged, but don't prevent the API from responding:
218
+
219
+ ```typescript
220
+ try {
221
+ return await backend(query, locale);
222
+ } catch (error) {
223
+ console.error('Autocomplete error:', error);
224
+ return [];
225
+ }
226
+ ```
227
+
228
+ ### Deduplication
229
+
230
+ When using multi-backend queries, results are automatically deduplicated:
231
+
232
+ ```typescript
233
+ const merged = new Set<string>();
234
+ for (const result of results) {
235
+ for (const suggestion of result) {
236
+ merged.add(suggestion);
237
+ }
238
+ }
239
+ return Array.from(merged);
240
+ ```
241
+
242
+ ## Examples
243
+
244
+ See `examples/autocomplete-example.ts` for comprehensive usage examples including:
245
+
246
+ 1. Single backend autocomplete
247
+ 2. Multi-backend queries
248
+ 3. Locale-specific suggestions
249
+ 4. Direct backend function calls
250
+ 5. Comparing results across backends
251
+ 6. Asian language autocomplete (Baidu, Yandex)
252
+
253
+ Run the examples:
254
+
255
+ ```bash
256
+ bun run examples/autocomplete-example.ts
257
+ ```
258
+
259
+ ## Performance Considerations
260
+
261
+ 1. **Parallel Requests**: Multi-backend queries run in parallel using `Promise.all()`
262
+ 2. **Fast Timeout**: 3-second timeout prevents slow backends from blocking
263
+ 3. **Lightweight Parsing**: Minimal HTML parsing using linkedom for Google
264
+ 4. **No Caching**: Suggestions are fresh on each request (can be added if needed)
265
+
266
+ ## Future Enhancements
267
+
268
+ Potential improvements:
269
+
270
+ - [ ] Add caching layer for frequent queries
271
+ - [ ] Support for more backends (Bing, Yahoo, etc.)
272
+ - [ ] Ranked suggestions based on popularity
273
+ - [ ] User query history integration
274
+ - [ ] Personalized suggestions
275
+ - [ ] A/B testing different backends
276
+
277
+ ## Troubleshooting
278
+
279
+ ### No suggestions returned
280
+
281
+ 1. Check if the backend is supported: `GET /autocomplete/backends`
282
+ 2. Verify the query is not empty
283
+ 3. Check network connectivity
284
+ 4. Try a different backend
285
+
286
+ ### Slow responses
287
+
288
+ 1. Use single backend instead of multi-backend
289
+ 2. Check backend-specific issues (rate limiting, etc.)
290
+ 3. Consider implementing caching
291
+
292
+ ### Locale not working
293
+
294
+ 1. Verify locale format (e.g., 'en-US', 'de-DE')
295
+ 2. Check if backend supports the locale
296
+ 3. Refer to backend-specific locale documentation above
@@ -0,0 +1,265 @@
1
+ # Category-Based Search with Weighted Ranking
2
+
3
+ This document explains how the TypeScript search API implements category-based search with weighted ranking, based on the Python SearXNG implementation.
4
+
5
+ ## Overview
6
+
7
+ The search API now supports:
8
+
9
+ - **Multi-category search**: Search across specific categories (general, news, academic, etc.)
10
+ - **Result combination**: Automatically combine and deduplicate results from multiple engines
11
+ - **Weighted ranking**: Score results based on engine quality, category importance, and position
12
+ - **Category grouping**: Results are grouped by category for better organization
13
+
14
+ ## Architecture
15
+
16
+ ### Key Components
17
+
18
+ 1. **CategoryRegistry** (`app/lib/category-registry.ts`)
19
+
20
+ - Manages engines organized by category
21
+ - Supports 13 categories: general, images, videos, news, maps, music, it, academic, social, files, torrents, shopping, specialized
22
+ - Each category has configurable default weights
23
+
24
+ 2. **ResultContainer** (`app/lib/result-container.ts`)
25
+
26
+ - Deduplicates results by URL hash
27
+ - Merges duplicate results from different engines
28
+ - Calculates weighted scores
29
+ - Groups results by category
30
+
31
+ 3. **Search Class** (`app/lib/search.ts`)
32
+ - Main search interface
33
+ - Queries multiple engines in parallel
34
+ - Applies category and engine filters
35
+ - Returns combined, scored, and sorted results
36
+
37
+ ## How Scoring Works
38
+
39
+ The scoring algorithm is based on SearXNG's Python implementation:
40
+
41
+ ```
42
+ score = Σ (weight / position) for each position
43
+ ```
44
+
45
+ Where:
46
+
47
+ - **weight** = `base_weight × engine_weight × category_weight × num_occurrences`
48
+ - **position**: The position of the result in the engine's results (1st, 2nd, 3rd, etc.)
49
+ - **num_occurrences**: How many engines found this result
50
+
51
+ ### Engine Weights
52
+
53
+ Higher quality engines get higher weights:
54
+
55
+ - Google: 1.5
56
+ - Bing: 1.3
57
+ - DuckDuckGo: 1.2
58
+ - Google Scholar: 1.4
59
+ - Semantic Scholar: 1.3
60
+ - Default: 1.0
61
+
62
+ ### Category Weights
63
+
64
+ Different categories can have different importance:
65
+
66
+ - Academic: 1.3 (higher credibility)
67
+ - IT: 1.2 (technical accuracy)
68
+ - News: 1.1
69
+ - Social: 0.9 (lower due to potential duplicates)
70
+ - Torrents: 0.8 (lower due to potential spam)
71
+ - Default: 1.0
72
+
73
+ ### Example Score Calculation
74
+
75
+ If a result appears at:
76
+
77
+ - Position 1 in Google (weight 1.5)
78
+ - Position 3 in Bing (weight 1.3)
79
+ - Category: general (weight 1.0)
80
+
81
+ ```
82
+ score = (1.5 × 1.0 × 2 / 1) + (1.3 × 1.0 × 2 / 3)
83
+ = 3.0 + 0.87
84
+ = 3.87
85
+ ```
86
+
87
+ The multiplier of 2 is because the result appeared in 2 engines.
88
+
89
+ ## Usage Examples
90
+
91
+ ### 1. Search a Single Category
92
+
93
+ ```typescript
94
+ import { Search } from "./app/lib/search.js";
95
+
96
+ const search = new Search();
97
+
98
+ // Search only general web engines
99
+ const results = await search.searchByCategories("typescript", ["general"]);
100
+ ```
101
+
102
+ ### 2. Search Multiple Categories
103
+
104
+ ```typescript
105
+ // Search across general, IT, and academic categories
106
+ const results = await search.searchByCategories("machine learning", [
107
+ "general",
108
+ "it",
109
+ "academic",
110
+ ]);
111
+ ```
112
+
113
+ ### 3. Use the Standard Search with Category Filter
114
+
115
+ ```typescript
116
+ // More explicit control
117
+ const results = await search.search(
118
+ "climate change",
119
+ 1, // page number
120
+ undefined, // engine names
121
+ ["news", "general"] // categories
122
+ );
123
+ ```
124
+
125
+ ### 4. Search Specific Engines
126
+
127
+ ```typescript
128
+ // Search only specific engines
129
+ const results = await search.search(
130
+ "nodejs",
131
+ 1,
132
+ ["google", "duckduckgo", "github"] // specific engines
133
+ );
134
+ ```
135
+
136
+ ## Available Categories
137
+
138
+ | Category | Description | Default Weight | Example Engines |
139
+ | ----------- | ------------------- | -------------- | ---------------------------------- |
140
+ | general | General web search | 1.0 | Google, Bing, DuckDuckGo |
141
+ | images | Image search | 1.0 | Unsplash, Bing Images, Flickr |
142
+ | videos | Video search | 1.0 | YouTube, Vimeo, Dailymotion |
143
+ | news | News search | 1.1 | Google News, HackerNews, Bing News |
144
+ | maps | Maps and locations | 1.0 | OpenStreetMap, Photon |
145
+ | it | Programming/IT | 1.2 | GitHub, StackOverflow, NPM |
146
+ | academic | Scientific papers | 1.3 | Google Scholar, arXiv, PubMed |
147
+ | social | Social media | 0.9 | Reddit, Twitter, Mastodon |
148
+ | torrents | Torrent search | 0.8 | 1337x, ThePirateBay, YTS |
149
+ | shopping | Shopping | 1.0 | eBay |
150
+ | specialized | Specialized engines | 1.1 | Wikipedia, IMDB, Genius |
151
+
152
+ ## Result Deduplication
153
+
154
+ Results with the same URL (normalized) are automatically merged:
155
+
156
+ 1. **URL normalization**: Removes protocol, www, trailing slashes
157
+ 2. **Title matching**: Includes partial title match for disambiguation
158
+ 3. **Result merging**:
159
+ - Combines engine names
160
+ - Tracks all positions
161
+ - Uses longer content/title
162
+ - Prefers HTTPS URLs
163
+ - Accumulates scores from all engines
164
+
165
+ Example:
166
+
167
+ ```
168
+ Result from Google at position 1
169
+ + Same URL from Bing at position 2
170
+ = Merged result with:
171
+ - engines: ['google', 'bing']
172
+ - positions: [1, 2]
173
+ - Higher combined score
174
+ ```
175
+
176
+ ## Category Grouping
177
+
178
+ Results are grouped by category in the final output:
179
+
180
+ 1. **First pass**: Sort all results by score (descending)
181
+ 2. **Second pass**: Group similar categories together
182
+ - Maximum 8 results per group
183
+ - Maximum distance of 20 positions to look back
184
+ - Groups by: category + template + has_image
185
+
186
+ This ensures related results appear together while maintaining score-based relevance.
187
+
188
+ ## API Reference
189
+
190
+ ### Search Class Methods
191
+
192
+ #### `search(query, pageno, engineNames?, categories?)`
193
+
194
+ Main search method with full control.
195
+
196
+ **Parameters:**
197
+
198
+ - `query`: Search query string
199
+ - `pageno`: Page number (default: 1)
200
+ - `engineNames`: Optional array of specific engine names
201
+ - `categories`: Optional array of category names
202
+
203
+ **Returns:** `Promise<MergedResult[]>`
204
+
205
+ #### `searchByCategories(query, categories, pageno?)`
206
+
207
+ Convenience method for multi-category search.
208
+
209
+ **Parameters:**
210
+
211
+ - `query`: Search query string
212
+ - `categories`: Array of category names
213
+ - `pageno`: Page number (default: 1)
214
+
215
+ **Returns:** `Promise<MergedResult[]>`
216
+
217
+ #### `getCategories()`
218
+
219
+ Get list of available categories.
220
+
221
+ **Returns:** `string[]`
222
+
223
+ #### `getEnginesByCategory(category)`
224
+
225
+ Get engines for a specific category.
226
+
227
+ **Parameters:**
228
+
229
+ - `category`: Category name
230
+
231
+ **Returns:** `Engine[]`
232
+
233
+ #### `getCategoryStats()`
234
+
235
+ Get statistics about engines and categories.
236
+
237
+ **Returns:** Object with category statistics
238
+
239
+ ### MergedResult Interface
240
+
241
+ ```typescript
242
+ interface MergedResult {
243
+ url?: string;
244
+ title: string;
245
+ content: string;
246
+ engines: string[]; // List of engines that found this result
247
+ positions: number[]; // Positions in each engine's results
248
+ score: number; // Calculated weighted score
249
+ priority: "low" | "normal" | "high";
250
+ category?: string; // Primary category
251
+ template?: string; // Result template type
252
+ thumbnail?: string;
253
+ publishedDate?: string;
254
+ author?: string;
255
+ // ... other fields
256
+ }
257
+ ```
258
+
259
+ ## Performance Considerations
260
+
261
+ 1. **Parallel execution**: All engines are queried in parallel using `Promise.all()`
262
+ 2. **Health tracking**: Unhealthy engines are automatically skipped
263
+ 3. **Timeout handling**: Individual engine timeouts don't block the overall search
264
+ 4. **Deduplication**: Hash-based deduplication is O(1) using Map
265
+ 5. **Sorting**: Results are sorted once using efficient native sort
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "search-web-api",
3
+ "description": "70+ search engines across 10 categories and Scrape Extract API",
4
+ "type": "module",
5
+ "version": "1.0.13",
6
+ "author": "vtempest",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/OpenSourceAGI/qwksearch-research-agent.git",
11
+ "directory": "packages/search-web-api"
12
+ },
13
+ "scripts": {
14
+ "dev": "bun --watch demo/index.ts",
15
+ "start": "bun demo/index.ts",
16
+ "test": "vitest run"
17
+ },
18
+ "dependencies": {
19
+ "@huggingface/transformers": "^3.8.1",
20
+ "@scalar/hono-api-reference": "^0.9.26",
21
+ "grab-url": "^1.0.8",
22
+ "hono": "^4.6.12",
23
+ "linkedom": "^0.18.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.9.3",
27
+ "@vitest/coverage-v8": "^4.0.18",
28
+ "tsx": "^4.7.1",
29
+ "typescript": "^5.9.3",
30
+ "vitest": "^4.0.18"
31
+ }
32
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Autocomplete AI Next-Word Predictor — Local, CPU-based next-word prediction
3
+ * using the Xenova/distilgpt2 model via the Hugging Face Transformers.js library.
4
+ * DistilGPT-2 is a distilled 82 M-parameter variant of GPT-2 (~80 MB) that
5
+ * runs efficiently on CPU with FP16 precision and is well-suited for lightweight
6
+ * inline query completion without any network calls.
7
+ *
8
+ * @see https://huggingface.co/Xenova/distilgpt2
9
+ */
10
+
11
+ import { pipeline, type TextGenerationSingle } from "@huggingface/transformers";
12
+
13
+ /**
14
+ * Predict the next words for a given text prompt using a small local language model.
15
+ *
16
+ * @param prompt - The input text to continue.
17
+ * @param options.maxTokens - Maximum number of new tokens to generate (default 16).
18
+ * @param options.model - Hugging Face model ID to use (default `"Xenova/distilgpt2"`).
19
+ * @param options.modelParams - Additional pipeline constructor options.
20
+ * @returns The generated continuation text with the original prompt removed.
21
+ */
22
+ export async function predictNextWordsWithSmallLocalModel(
23
+ prompt: string,
24
+ {
25
+ model = "Xenova/distilgpt2",
26
+ maxTokens = 16,
27
+ modelParams = {},
28
+ }: {
29
+ model?: string;
30
+ maxTokens?: number;
31
+ modelParams?: Record<string, unknown>;
32
+ } = {},
33
+ ): Promise<string> {
34
+ if (!prompt) throw new Error("Prompt is required");
35
+
36
+ const generator = await pipeline("text-generation", model, {
37
+ dtype: "fp16",
38
+ device: "cpu",
39
+ ...modelParams,
40
+ });
41
+
42
+ return (await generator(prompt, { max_new_tokens: maxTokens }))
43
+ .map((res) => (res as TextGenerationSingle).generated_text)
44
+ .join(" ")
45
+ .replace(prompt, "")
46
+ .trim();
47
+ }