create-atsdc-stack 1.1.0 → 1.2.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/.claude/settings.local.json +3 -1
- package/CLAUDE.md +236 -215
- package/CONTRIBUTING.md +342 -342
- package/INSTALLATION.md +359 -359
- package/LICENSE +201 -201
- package/README.md +405 -405
- package/app/.env.example +17 -17
- package/app/.github/labeler.yml +61 -0
- package/app/.github/workflows/browser-tests.yml +101 -0
- package/app/.github/workflows/check.yml +24 -0
- package/app/.github/workflows/greetings.yml +16 -0
- package/app/.github/workflows/label.yml +22 -0
- package/app/.github/workflows/stale.yml +27 -0
- package/app/.github/workflows/summary.yml +34 -0
- package/app/.stylelintrc.json +8 -0
- package/app/README.md +251 -251
- package/app/astro.config.mjs +83 -83
- package/app/drizzle.config.ts +16 -16
- package/app/package.json +66 -52
- package/app/playwright.config.ts +27 -0
- package/app/public/manifest.webmanifest +36 -36
- package/app/pwa-assets.config.ts +8 -0
- package/app/src/components/Card.astro +36 -36
- package/app/src/db/initialize.ts +107 -107
- package/app/src/db/schema.ts +72 -72
- package/app/src/db/validations.ts +158 -158
- package/app/src/layouts/Layout.astro +63 -63
- package/app/src/lib/config.ts +36 -36
- package/app/src/lib/content-converter.ts +141 -141
- package/app/src/lib/dom-utils.ts +230 -230
- package/app/src/lib/exa-search.ts +269 -269
- package/app/src/pages/api/chat.ts +91 -91
- package/app/src/pages/api/posts.ts +350 -350
- package/app/src/pages/index.astro +87 -87
- package/app/src/styles/components/button.scss +152 -152
- package/app/src/styles/components/card.scss +180 -180
- package/app/src/styles/components/form.scss +240 -240
- package/app/src/styles/global.scss +141 -141
- package/app/src/styles/pages/index.scss +80 -80
- package/app/src/styles/reset.scss +83 -83
- package/app/src/styles/variables/globals.scss +96 -96
- package/app/src/styles/variables/mixins.scss +238 -238
- package/app/tests/browser.test.nopause.ts +10 -0
- package/app/tests/browser.test.ts +13 -0
- package/bin/cli.js +1151 -1151
- package/package.json +8 -6
- package/app/.astro/settings.json +0 -5
- package/app/.astro/types.d.ts +0 -1
|
@@ -1,269 +1,269 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exa Search Integration
|
|
3
|
-
* Provides AI-powered search capabilities using Exa
|
|
4
|
-
* Documentation: https://docs.exa.ai
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import Exa from 'exa-js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Initialize Exa client
|
|
11
|
-
* Requires EXA_API_KEY environment variable
|
|
12
|
-
*/
|
|
13
|
-
function getExaClient(): Exa {
|
|
14
|
-
const apiKey = import.meta.env.EXA_API_KEY || process.env.EXA_API_KEY;
|
|
15
|
-
|
|
16
|
-
if (!apiKey) {
|
|
17
|
-
throw new Error('EXA_API_KEY environment variable is not set');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return new Exa(apiKey);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Search options for Exa
|
|
25
|
-
*/
|
|
26
|
-
export interface ExaSearchOptions {
|
|
27
|
-
/** Number of results to return (1-10, default: 10) */
|
|
28
|
-
numResults?: number;
|
|
29
|
-
|
|
30
|
-
/** Type of search: 'neural', 'keyword', or 'auto' (default: 'auto') */
|
|
31
|
-
type?: 'neural' | 'keyword' | 'auto';
|
|
32
|
-
|
|
33
|
-
/** Whether to use autoprompt to expand query (default: false) */
|
|
34
|
-
useAutoprompt?: boolean;
|
|
35
|
-
|
|
36
|
-
/** Category of content to search */
|
|
37
|
-
category?: 'company' | 'research paper' | 'news' | 'github' | 'tweet' | 'movie' | 'song' | 'personal site' | 'pdf';
|
|
38
|
-
|
|
39
|
-
/** Start crawl date (YYYY-MM-DD) */
|
|
40
|
-
startCrawlDate?: string;
|
|
41
|
-
|
|
42
|
-
/** End crawl date (YYYY-MM-DD) */
|
|
43
|
-
endCrawlDate?: string;
|
|
44
|
-
|
|
45
|
-
/** Start published date (YYYY-MM-DD) */
|
|
46
|
-
startPublishedDate?: string;
|
|
47
|
-
|
|
48
|
-
/** End published date (YYYY-MM-DD) */
|
|
49
|
-
endPublishedDate?: string;
|
|
50
|
-
|
|
51
|
-
/** Include domains (whitelist) */
|
|
52
|
-
includeDomains?: string[];
|
|
53
|
-
|
|
54
|
-
/** Exclude domains (blacklist) */
|
|
55
|
-
excludeDomains?: string[];
|
|
56
|
-
|
|
57
|
-
/** Whether to include page content */
|
|
58
|
-
contents?: {
|
|
59
|
-
text?: boolean | { maxCharacters?: number };
|
|
60
|
-
highlights?: boolean | { numSentences?: number; highlightsPerUrl?: number };
|
|
61
|
-
summary?: boolean | { query?: string };
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Exa search result
|
|
67
|
-
*/
|
|
68
|
-
export interface ExaResult {
|
|
69
|
-
url: string;
|
|
70
|
-
title: string;
|
|
71
|
-
publishedDate?: string;
|
|
72
|
-
author?: string;
|
|
73
|
-
score?: number;
|
|
74
|
-
id: string;
|
|
75
|
-
text?: string;
|
|
76
|
-
highlights?: string[];
|
|
77
|
-
summary?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Search the web using Exa
|
|
82
|
-
* @param query - The search query
|
|
83
|
-
* @param options - Search options
|
|
84
|
-
* @returns Array of search results
|
|
85
|
-
*/
|
|
86
|
-
export async function searchWeb(
|
|
87
|
-
query: string,
|
|
88
|
-
options: ExaSearchOptions = {}
|
|
89
|
-
): Promise<ExaResult[]> {
|
|
90
|
-
try {
|
|
91
|
-
const exa = getExaClient();
|
|
92
|
-
|
|
93
|
-
const response = await exa.searchAndContents(query, {
|
|
94
|
-
numResults: options.numResults || 10,
|
|
95
|
-
type: options.type || 'auto',
|
|
96
|
-
useAutoprompt: options.useAutoprompt || false,
|
|
97
|
-
category: options.category,
|
|
98
|
-
startCrawlDate: options.startCrawlDate,
|
|
99
|
-
endCrawlDate: options.endCrawlDate,
|
|
100
|
-
startPublishedDate: options.startPublishedDate,
|
|
101
|
-
endPublishedDate: options.endPublishedDate,
|
|
102
|
-
includeDomains: options.includeDomains,
|
|
103
|
-
excludeDomains: options.excludeDomains,
|
|
104
|
-
text: options.contents?.text,
|
|
105
|
-
highlights: options.contents?.highlights,
|
|
106
|
-
summary: options.contents?.summary,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
return response.results.map((result: any) => ({
|
|
110
|
-
url: result.url,
|
|
111
|
-
title: result.title,
|
|
112
|
-
publishedDate: result.publishedDate,
|
|
113
|
-
author: result.author,
|
|
114
|
-
score: result.score,
|
|
115
|
-
id: result.id,
|
|
116
|
-
text: result.text,
|
|
117
|
-
highlights: result.highlights,
|
|
118
|
-
summary: result.summary,
|
|
119
|
-
}));
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.error('Exa search error:', error);
|
|
122
|
-
throw new Error('Failed to perform search');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Find similar content to a given URL
|
|
128
|
-
* @param url - The URL to find similar content for
|
|
129
|
-
* @param options - Search options
|
|
130
|
-
* @returns Array of similar results
|
|
131
|
-
*/
|
|
132
|
-
export async function findSimilar(
|
|
133
|
-
url: string,
|
|
134
|
-
options: Omit<ExaSearchOptions, 'type' | 'useAutoprompt'> = {}
|
|
135
|
-
): Promise<ExaResult[]> {
|
|
136
|
-
try {
|
|
137
|
-
const exa = getExaClient();
|
|
138
|
-
|
|
139
|
-
const response = await exa.findSimilarAndContents(url, {
|
|
140
|
-
numResults: options.numResults || 10,
|
|
141
|
-
category: options.category,
|
|
142
|
-
startCrawlDate: options.startCrawlDate,
|
|
143
|
-
endCrawlDate: options.endCrawlDate,
|
|
144
|
-
startPublishedDate: options.startPublishedDate,
|
|
145
|
-
endPublishedDate: options.endPublishedDate,
|
|
146
|
-
includeDomains: options.includeDomains,
|
|
147
|
-
excludeDomains: options.excludeDomains,
|
|
148
|
-
text: options.contents?.text,
|
|
149
|
-
highlights: options.contents?.highlights,
|
|
150
|
-
summary: options.contents?.summary,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
return response.results.map((result: any) => ({
|
|
154
|
-
url: result.url,
|
|
155
|
-
title: result.title,
|
|
156
|
-
publishedDate: result.publishedDate,
|
|
157
|
-
author: result.author,
|
|
158
|
-
score: result.score,
|
|
159
|
-
id: result.id,
|
|
160
|
-
text: result.text,
|
|
161
|
-
highlights: result.highlights,
|
|
162
|
-
summary: result.summary,
|
|
163
|
-
}));
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('Exa find similar error:', error);
|
|
166
|
-
throw new Error('Failed to find similar content');
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Get full page content for a list of URLs
|
|
172
|
-
* @param urls - Array of URLs to get content for
|
|
173
|
-
* @param options - Content options
|
|
174
|
-
* @returns Array of results with content
|
|
175
|
-
*/
|
|
176
|
-
export async function getContents(
|
|
177
|
-
urls: string[],
|
|
178
|
-
options: ExaSearchOptions['contents'] = {}
|
|
179
|
-
): Promise<ExaResult[]> {
|
|
180
|
-
try {
|
|
181
|
-
const exa = getExaClient();
|
|
182
|
-
|
|
183
|
-
const response = await exa.getContents(urls, {
|
|
184
|
-
text: options.text,
|
|
185
|
-
highlights: options.highlights,
|
|
186
|
-
summary: options.summary,
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
return response.results.map((result: any) => ({
|
|
190
|
-
url: result.url,
|
|
191
|
-
title: result.title,
|
|
192
|
-
publishedDate: result.publishedDate,
|
|
193
|
-
author: result.author,
|
|
194
|
-
id: result.id,
|
|
195
|
-
text: result.text,
|
|
196
|
-
highlights: result.highlights,
|
|
197
|
-
summary: result.summary,
|
|
198
|
-
}));
|
|
199
|
-
} catch (error) {
|
|
200
|
-
console.error('Exa get contents error:', error);
|
|
201
|
-
throw new Error('Failed to get contents');
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Search for recent news articles
|
|
207
|
-
* @param query - The search query
|
|
208
|
-
* @param daysBack - Number of days to look back (default: 7)
|
|
209
|
-
* @returns Array of news results
|
|
210
|
-
*/
|
|
211
|
-
export async function searchNews(query: string, daysBack: number = 7): Promise<ExaResult[]> {
|
|
212
|
-
const endDate = new Date();
|
|
213
|
-
const startDate = new Date();
|
|
214
|
-
startDate.setDate(startDate.getDate() - daysBack);
|
|
215
|
-
|
|
216
|
-
return searchWeb(query, {
|
|
217
|
-
category: 'news',
|
|
218
|
-
startPublishedDate: startDate.toISOString().split('T')[0],
|
|
219
|
-
endPublishedDate: endDate.toISOString().split('T')[0],
|
|
220
|
-
numResults: 10,
|
|
221
|
-
contents: {
|
|
222
|
-
text: { maxCharacters: 1000 },
|
|
223
|
-
highlights: { numSentences: 3 },
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Search for research papers
|
|
230
|
-
* @param query - The search query
|
|
231
|
-
* @param options - Additional search options
|
|
232
|
-
* @returns Array of research paper results
|
|
233
|
-
*/
|
|
234
|
-
export async function searchResearch(
|
|
235
|
-
query: string,
|
|
236
|
-
options: ExaSearchOptions = {}
|
|
237
|
-
): Promise<ExaResult[]> {
|
|
238
|
-
return searchWeb(query, {
|
|
239
|
-
...options,
|
|
240
|
-
category: 'research paper',
|
|
241
|
-
contents: {
|
|
242
|
-
text: { maxCharacters: 2000 },
|
|
243
|
-
summary: true,
|
|
244
|
-
...options.contents,
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Search GitHub repositories
|
|
251
|
-
* @param query - The search query
|
|
252
|
-
* @param options - Additional search options
|
|
253
|
-
* @returns Array of GitHub results
|
|
254
|
-
*/
|
|
255
|
-
export async function searchGitHub(
|
|
256
|
-
query: string,
|
|
257
|
-
options: ExaSearchOptions = {}
|
|
258
|
-
): Promise<ExaResult[]> {
|
|
259
|
-
return searchWeb(query, {
|
|
260
|
-
...options,
|
|
261
|
-
category: 'github',
|
|
262
|
-
includeDomains: ['github.com'],
|
|
263
|
-
contents: {
|
|
264
|
-
text: { maxCharacters: 1500 },
|
|
265
|
-
highlights: { numSentences: 5 },
|
|
266
|
-
...options.contents,
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Exa Search Integration
|
|
3
|
+
* Provides AI-powered search capabilities using Exa
|
|
4
|
+
* Documentation: https://docs.exa.ai
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Exa from 'exa-js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize Exa client
|
|
11
|
+
* Requires EXA_API_KEY environment variable
|
|
12
|
+
*/
|
|
13
|
+
function getExaClient(): Exa {
|
|
14
|
+
const apiKey = import.meta.env.EXA_API_KEY || process.env.EXA_API_KEY;
|
|
15
|
+
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
throw new Error('EXA_API_KEY environment variable is not set');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return new Exa(apiKey);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Search options for Exa
|
|
25
|
+
*/
|
|
26
|
+
export interface ExaSearchOptions {
|
|
27
|
+
/** Number of results to return (1-10, default: 10) */
|
|
28
|
+
numResults?: number;
|
|
29
|
+
|
|
30
|
+
/** Type of search: 'neural', 'keyword', or 'auto' (default: 'auto') */
|
|
31
|
+
type?: 'neural' | 'keyword' | 'auto';
|
|
32
|
+
|
|
33
|
+
/** Whether to use autoprompt to expand query (default: false) */
|
|
34
|
+
useAutoprompt?: boolean;
|
|
35
|
+
|
|
36
|
+
/** Category of content to search */
|
|
37
|
+
category?: 'company' | 'research paper' | 'news' | 'github' | 'tweet' | 'movie' | 'song' | 'personal site' | 'pdf';
|
|
38
|
+
|
|
39
|
+
/** Start crawl date (YYYY-MM-DD) */
|
|
40
|
+
startCrawlDate?: string;
|
|
41
|
+
|
|
42
|
+
/** End crawl date (YYYY-MM-DD) */
|
|
43
|
+
endCrawlDate?: string;
|
|
44
|
+
|
|
45
|
+
/** Start published date (YYYY-MM-DD) */
|
|
46
|
+
startPublishedDate?: string;
|
|
47
|
+
|
|
48
|
+
/** End published date (YYYY-MM-DD) */
|
|
49
|
+
endPublishedDate?: string;
|
|
50
|
+
|
|
51
|
+
/** Include domains (whitelist) */
|
|
52
|
+
includeDomains?: string[];
|
|
53
|
+
|
|
54
|
+
/** Exclude domains (blacklist) */
|
|
55
|
+
excludeDomains?: string[];
|
|
56
|
+
|
|
57
|
+
/** Whether to include page content */
|
|
58
|
+
contents?: {
|
|
59
|
+
text?: boolean | { maxCharacters?: number };
|
|
60
|
+
highlights?: boolean | { numSentences?: number; highlightsPerUrl?: number };
|
|
61
|
+
summary?: boolean | { query?: string };
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Exa search result
|
|
67
|
+
*/
|
|
68
|
+
export interface ExaResult {
|
|
69
|
+
url: string;
|
|
70
|
+
title: string;
|
|
71
|
+
publishedDate?: string;
|
|
72
|
+
author?: string;
|
|
73
|
+
score?: number;
|
|
74
|
+
id: string;
|
|
75
|
+
text?: string;
|
|
76
|
+
highlights?: string[];
|
|
77
|
+
summary?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Search the web using Exa
|
|
82
|
+
* @param query - The search query
|
|
83
|
+
* @param options - Search options
|
|
84
|
+
* @returns Array of search results
|
|
85
|
+
*/
|
|
86
|
+
export async function searchWeb(
|
|
87
|
+
query: string,
|
|
88
|
+
options: ExaSearchOptions = {}
|
|
89
|
+
): Promise<ExaResult[]> {
|
|
90
|
+
try {
|
|
91
|
+
const exa = getExaClient();
|
|
92
|
+
|
|
93
|
+
const response = await exa.searchAndContents(query, {
|
|
94
|
+
numResults: options.numResults || 10,
|
|
95
|
+
type: options.type || 'auto',
|
|
96
|
+
useAutoprompt: options.useAutoprompt || false,
|
|
97
|
+
category: options.category,
|
|
98
|
+
startCrawlDate: options.startCrawlDate,
|
|
99
|
+
endCrawlDate: options.endCrawlDate,
|
|
100
|
+
startPublishedDate: options.startPublishedDate,
|
|
101
|
+
endPublishedDate: options.endPublishedDate,
|
|
102
|
+
includeDomains: options.includeDomains,
|
|
103
|
+
excludeDomains: options.excludeDomains,
|
|
104
|
+
text: options.contents?.text,
|
|
105
|
+
highlights: options.contents?.highlights,
|
|
106
|
+
summary: options.contents?.summary,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return response.results.map((result: any) => ({
|
|
110
|
+
url: result.url,
|
|
111
|
+
title: result.title,
|
|
112
|
+
publishedDate: result.publishedDate,
|
|
113
|
+
author: result.author,
|
|
114
|
+
score: result.score,
|
|
115
|
+
id: result.id,
|
|
116
|
+
text: result.text,
|
|
117
|
+
highlights: result.highlights,
|
|
118
|
+
summary: result.summary,
|
|
119
|
+
}));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Exa search error:', error);
|
|
122
|
+
throw new Error('Failed to perform search');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Find similar content to a given URL
|
|
128
|
+
* @param url - The URL to find similar content for
|
|
129
|
+
* @param options - Search options
|
|
130
|
+
* @returns Array of similar results
|
|
131
|
+
*/
|
|
132
|
+
export async function findSimilar(
|
|
133
|
+
url: string,
|
|
134
|
+
options: Omit<ExaSearchOptions, 'type' | 'useAutoprompt'> = {}
|
|
135
|
+
): Promise<ExaResult[]> {
|
|
136
|
+
try {
|
|
137
|
+
const exa = getExaClient();
|
|
138
|
+
|
|
139
|
+
const response = await exa.findSimilarAndContents(url, {
|
|
140
|
+
numResults: options.numResults || 10,
|
|
141
|
+
category: options.category,
|
|
142
|
+
startCrawlDate: options.startCrawlDate,
|
|
143
|
+
endCrawlDate: options.endCrawlDate,
|
|
144
|
+
startPublishedDate: options.startPublishedDate,
|
|
145
|
+
endPublishedDate: options.endPublishedDate,
|
|
146
|
+
includeDomains: options.includeDomains,
|
|
147
|
+
excludeDomains: options.excludeDomains,
|
|
148
|
+
text: options.contents?.text,
|
|
149
|
+
highlights: options.contents?.highlights,
|
|
150
|
+
summary: options.contents?.summary,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return response.results.map((result: any) => ({
|
|
154
|
+
url: result.url,
|
|
155
|
+
title: result.title,
|
|
156
|
+
publishedDate: result.publishedDate,
|
|
157
|
+
author: result.author,
|
|
158
|
+
score: result.score,
|
|
159
|
+
id: result.id,
|
|
160
|
+
text: result.text,
|
|
161
|
+
highlights: result.highlights,
|
|
162
|
+
summary: result.summary,
|
|
163
|
+
}));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('Exa find similar error:', error);
|
|
166
|
+
throw new Error('Failed to find similar content');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get full page content for a list of URLs
|
|
172
|
+
* @param urls - Array of URLs to get content for
|
|
173
|
+
* @param options - Content options
|
|
174
|
+
* @returns Array of results with content
|
|
175
|
+
*/
|
|
176
|
+
export async function getContents(
|
|
177
|
+
urls: string[],
|
|
178
|
+
options: ExaSearchOptions['contents'] = {}
|
|
179
|
+
): Promise<ExaResult[]> {
|
|
180
|
+
try {
|
|
181
|
+
const exa = getExaClient();
|
|
182
|
+
|
|
183
|
+
const response = await exa.getContents(urls, {
|
|
184
|
+
text: options.text,
|
|
185
|
+
highlights: options.highlights,
|
|
186
|
+
summary: options.summary,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return response.results.map((result: any) => ({
|
|
190
|
+
url: result.url,
|
|
191
|
+
title: result.title,
|
|
192
|
+
publishedDate: result.publishedDate,
|
|
193
|
+
author: result.author,
|
|
194
|
+
id: result.id,
|
|
195
|
+
text: result.text,
|
|
196
|
+
highlights: result.highlights,
|
|
197
|
+
summary: result.summary,
|
|
198
|
+
}));
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Exa get contents error:', error);
|
|
201
|
+
throw new Error('Failed to get contents');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Search for recent news articles
|
|
207
|
+
* @param query - The search query
|
|
208
|
+
* @param daysBack - Number of days to look back (default: 7)
|
|
209
|
+
* @returns Array of news results
|
|
210
|
+
*/
|
|
211
|
+
export async function searchNews(query: string, daysBack: number = 7): Promise<ExaResult[]> {
|
|
212
|
+
const endDate = new Date();
|
|
213
|
+
const startDate = new Date();
|
|
214
|
+
startDate.setDate(startDate.getDate() - daysBack);
|
|
215
|
+
|
|
216
|
+
return searchWeb(query, {
|
|
217
|
+
category: 'news',
|
|
218
|
+
startPublishedDate: startDate.toISOString().split('T')[0],
|
|
219
|
+
endPublishedDate: endDate.toISOString().split('T')[0],
|
|
220
|
+
numResults: 10,
|
|
221
|
+
contents: {
|
|
222
|
+
text: { maxCharacters: 1000 },
|
|
223
|
+
highlights: { numSentences: 3 },
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Search for research papers
|
|
230
|
+
* @param query - The search query
|
|
231
|
+
* @param options - Additional search options
|
|
232
|
+
* @returns Array of research paper results
|
|
233
|
+
*/
|
|
234
|
+
export async function searchResearch(
|
|
235
|
+
query: string,
|
|
236
|
+
options: ExaSearchOptions = {}
|
|
237
|
+
): Promise<ExaResult[]> {
|
|
238
|
+
return searchWeb(query, {
|
|
239
|
+
...options,
|
|
240
|
+
category: 'research paper',
|
|
241
|
+
contents: {
|
|
242
|
+
text: { maxCharacters: 2000 },
|
|
243
|
+
summary: true,
|
|
244
|
+
...options.contents,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Search GitHub repositories
|
|
251
|
+
* @param query - The search query
|
|
252
|
+
* @param options - Additional search options
|
|
253
|
+
* @returns Array of GitHub results
|
|
254
|
+
*/
|
|
255
|
+
export async function searchGitHub(
|
|
256
|
+
query: string,
|
|
257
|
+
options: ExaSearchOptions = {}
|
|
258
|
+
): Promise<ExaResult[]> {
|
|
259
|
+
return searchWeb(query, {
|
|
260
|
+
...options,
|
|
261
|
+
category: 'github',
|
|
262
|
+
includeDomains: ['github.com'],
|
|
263
|
+
contents: {
|
|
264
|
+
text: { maxCharacters: 1500 },
|
|
265
|
+
highlights: { numSentences: 5 },
|
|
266
|
+
...options.contents,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|