exa-js 1.4.7 → 1.4.9
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/README.md +30 -25
- package/dist/index.d.ts +74 -26
- package/dist/index.js +136 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -74,21 +74,22 @@ const customContentsResults = await exa.getContents(["urls"], {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
// Get an answer to a question
|
|
77
|
-
const answerResult = await exa.answer("What is the population of New York City?"
|
|
78
|
-
expandedQueriesLimit: 2,
|
|
79
|
-
includeText: false
|
|
80
|
-
});
|
|
77
|
+
const answerResult = await exa.answer("What is the population of New York City?");
|
|
81
78
|
|
|
82
|
-
// Get answer with
|
|
79
|
+
// Get answer with citation contents
|
|
83
80
|
const answerWithTextResults = await exa.answer("What is the population of New York City?", {
|
|
84
|
-
|
|
85
|
-
expandedQueriesLimit: 2
|
|
81
|
+
text: true
|
|
86
82
|
});
|
|
87
83
|
|
|
88
|
-
//
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
// Get an answer with streaming
|
|
85
|
+
for await (const chunk of exa.streamAnswer("What is the population of New York City?")) {
|
|
86
|
+
if (chunk.content) {
|
|
87
|
+
process.stdout.write(chunk.content);
|
|
88
|
+
}
|
|
89
|
+
if (chunk.citations) {
|
|
90
|
+
console.log("\nCitations:", chunk.citations);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
92
93
|
```
|
|
93
94
|
|
|
94
95
|
### `exa.search(query: string, options?: SearchOptions): Promise<SearchResponse>`
|
|
@@ -123,27 +124,31 @@ Generates an answer to a query using search results as context.
|
|
|
123
124
|
|
|
124
125
|
```javascript
|
|
125
126
|
const response = await exa.answer('What is the population of New York City?', {
|
|
126
|
-
|
|
127
|
+
text: true
|
|
127
128
|
});
|
|
128
129
|
```
|
|
129
130
|
|
|
130
|
-
###
|
|
131
|
-
|
|
131
|
+
### `exa.streamAnswer(query: string, options?: { text?: boolean }): AsyncGenerator<AnswerStreamChunk>`
|
|
132
|
+
Streams an answer as it's being generated, yielding chunks of text and citations. This is useful for providing real-time updates in chat interfaces or displaying partial results as they become available.
|
|
132
133
|
|
|
133
134
|
```javascript
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
stream: true,
|
|
139
|
-
includeText: false
|
|
140
|
-
},
|
|
141
|
-
(chunk) => {
|
|
142
|
-
// Process each chunk as it arrives
|
|
143
|
-
console.log(chunk.answer);
|
|
135
|
+
// Basic streaming example
|
|
136
|
+
for await (const chunk of exa.streamAnswer("What is quantum computing?")) {
|
|
137
|
+
if (chunk.content) {
|
|
138
|
+
process.stdout.write(chunk.content);
|
|
144
139
|
}
|
|
145
|
-
)
|
|
140
|
+
if (chunk.citations) {
|
|
141
|
+
console.log("\nCitations:", chunk.citations);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for await (const chunk of exa.streamAnswer("What is quantum computing?", { text: true })) {
|
|
146
|
+
}
|
|
146
147
|
```
|
|
147
148
|
|
|
149
|
+
Each chunk contains:
|
|
150
|
+
- `content`: A string containing the next piece of generated text
|
|
151
|
+
- `citations`: An array of citation objects containing source information
|
|
152
|
+
|
|
148
153
|
# Contributing
|
|
149
154
|
Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ type BaseSearchOptions = {
|
|
|
32
32
|
* @typedef {Object} RegularSearchOptions
|
|
33
33
|
*/
|
|
34
34
|
type RegularSearchOptions = BaseSearchOptions & {
|
|
35
|
+
/**
|
|
36
|
+
* If true, the search results are moderated for safety.
|
|
37
|
+
*/
|
|
38
|
+
moderation?: boolean;
|
|
35
39
|
useAutoprompt?: boolean;
|
|
36
40
|
type?: "keyword" | "neural" | "auto";
|
|
37
41
|
};
|
|
@@ -58,7 +62,7 @@ type ExtrasOptions = {
|
|
|
58
62
|
* @property {boolean} [filterEmptyResults] - If true, filters out results with no contents. Default is true.
|
|
59
63
|
* @property {number} [subpages] - The number of subpages to return for each result, where each subpage is derived from an internal link for the result.
|
|
60
64
|
* @property {string | string[]} [subpageTarget] - Text used to match/rank subpages in the returned subpage list. You could use "about" to get *about* page for websites. Note that this is a fuzzy matcher.
|
|
61
|
-
* @property {ExtrasOptions} [extras] - Miscelleneous data
|
|
65
|
+
* @property {ExtrasOptions} [extras] - Miscelleneous data derived from results
|
|
62
66
|
*/
|
|
63
67
|
type ContentsOptions = {
|
|
64
68
|
text?: TextContentsOptions | true;
|
|
@@ -142,7 +146,7 @@ type ExtrasResponse = {
|
|
|
142
146
|
};
|
|
143
147
|
/**
|
|
144
148
|
* @typedef {Object} SubpagesResponse
|
|
145
|
-
* @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The
|
|
149
|
+
* @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The subpages for a result
|
|
146
150
|
*/
|
|
147
151
|
type SubpagesResponse<T extends ContentsOptions> = {
|
|
148
152
|
subpages: ContentsResultComponent<T>[];
|
|
@@ -164,6 +168,8 @@ type ContentsResultComponent<T extends ContentsOptions> = Default<(T["text"] ext
|
|
|
164
168
|
* @property {string} [author] - The author of the content, if available.
|
|
165
169
|
* @property {number} [score] - Similarity score between the query/url and the result.
|
|
166
170
|
* @property {string} id - The temporary ID for the document.
|
|
171
|
+
* @property {string} [image] - A representative image for the content, if any.
|
|
172
|
+
* @property {string} [favicon] - A favicon for the site, if any.
|
|
167
173
|
*/
|
|
168
174
|
type SearchResult<T extends ContentsOptions> = {
|
|
169
175
|
title: string | null;
|
|
@@ -192,38 +198,51 @@ type SearchResponse<T extends ContentsOptions> = {
|
|
|
192
198
|
/**
|
|
193
199
|
* Options for the answer endpoint
|
|
194
200
|
* @typedef {Object} AnswerOptions
|
|
195
|
-
* @property {number} [expandedQueriesLimit] - Maximum number of query variations (0-4). Default 1.
|
|
196
201
|
* @property {boolean} [stream] - Whether to stream the response. Default false.
|
|
197
|
-
* @property {boolean} [
|
|
202
|
+
* @property {boolean} [text] - Whether to include text in the source results. Default false.
|
|
198
203
|
*/
|
|
199
204
|
type AnswerOptions = {
|
|
200
|
-
expandedQueriesLimit?: number;
|
|
201
205
|
stream?: boolean;
|
|
202
|
-
|
|
206
|
+
text?: boolean;
|
|
203
207
|
};
|
|
204
208
|
/**
|
|
205
209
|
* Represents an answer response object from the /answer endpoint.
|
|
206
210
|
* @typedef {Object} AnswerResponse
|
|
207
211
|
* @property {string} answer - The generated answer text.
|
|
208
|
-
* @property {SearchResult<{}>[]
|
|
212
|
+
* @property {SearchResult<{}>[]} citations - The sources used to generate the answer.
|
|
209
213
|
* @property {string} [requestId] - Optional request ID for the answer.
|
|
210
214
|
*/
|
|
211
215
|
type AnswerResponse = {
|
|
212
216
|
answer: string;
|
|
213
|
-
|
|
217
|
+
citations: SearchResult<{}>[];
|
|
214
218
|
requestId?: string;
|
|
215
219
|
};
|
|
220
|
+
type AnswerStreamChunk = {
|
|
221
|
+
/**
|
|
222
|
+
* The partial text content of the answer (if present in this chunk).
|
|
223
|
+
*/
|
|
224
|
+
content?: string;
|
|
225
|
+
/**
|
|
226
|
+
* Citations associated with the current chunk of text (if present).
|
|
227
|
+
*/
|
|
228
|
+
citations?: Array<{
|
|
229
|
+
id: string;
|
|
230
|
+
url: string;
|
|
231
|
+
title?: string;
|
|
232
|
+
publishedDate?: string;
|
|
233
|
+
author?: string;
|
|
234
|
+
text?: string;
|
|
235
|
+
}>;
|
|
236
|
+
};
|
|
216
237
|
/**
|
|
217
238
|
* Represents a streaming answer response chunk from the /answer endpoint.
|
|
218
239
|
* @typedef {Object} AnswerStreamResponse
|
|
219
240
|
* @property {string} [answer] - A chunk of the generated answer text.
|
|
220
|
-
* @property {SearchResult<{}>[]]} [
|
|
221
|
-
* @property {string} [error] - Error message if something went wrong.
|
|
241
|
+
* @property {SearchResult<{}>[]]} [citations] - The sources used to generate the answer.
|
|
222
242
|
*/
|
|
223
243
|
type AnswerStreamResponse = {
|
|
224
244
|
answer?: string;
|
|
225
|
-
|
|
226
|
-
error?: string;
|
|
245
|
+
citations?: SearchResult<{}>[];
|
|
227
246
|
};
|
|
228
247
|
/**
|
|
229
248
|
* The Exa class encapsulates the API's endpoints.
|
|
@@ -231,6 +250,9 @@ type AnswerStreamResponse = {
|
|
|
231
250
|
declare class Exa {
|
|
232
251
|
private baseURL;
|
|
233
252
|
private headers;
|
|
253
|
+
/**
|
|
254
|
+
* Helper method to separate out the contents-specific options from the rest.
|
|
255
|
+
*/
|
|
234
256
|
private extractContentsOptions;
|
|
235
257
|
/**
|
|
236
258
|
* Constructs the Exa API client.
|
|
@@ -243,23 +265,23 @@ declare class Exa {
|
|
|
243
265
|
* @param {string} endpoint - The API endpoint to call.
|
|
244
266
|
* @param {string} method - The HTTP method to use.
|
|
245
267
|
* @param {any} [body] - The request body for POST requests.
|
|
246
|
-
* @param {boolean} [stream] - Whether to stream the response.
|
|
247
|
-
* @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
|
|
248
268
|
* @returns {Promise<any>} The response from the API.
|
|
249
269
|
*/
|
|
250
270
|
private request;
|
|
251
271
|
/**
|
|
252
272
|
* Performs a search with an Exa prompt-engineered query.
|
|
273
|
+
*
|
|
253
274
|
* @param {string} query - The query string.
|
|
254
|
-
* @param {RegularSearchOptions} [options] - Additional search options
|
|
275
|
+
* @param {RegularSearchOptions} [options] - Additional search options
|
|
255
276
|
* @returns {Promise<SearchResponse<{}>>} A list of relevant search results.
|
|
256
277
|
*/
|
|
257
278
|
search(query: string, options?: RegularSearchOptions): Promise<SearchResponse<{}>>;
|
|
258
279
|
/**
|
|
259
280
|
* Performs a search with an Exa prompt-engineered query and returns the contents of the documents.
|
|
281
|
+
*
|
|
260
282
|
* @param {string} query - The query string.
|
|
261
|
-
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
262
|
-
* @returns {Promise<SearchResponse<T>>} A list of relevant search results.
|
|
283
|
+
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
284
|
+
* @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.
|
|
263
285
|
*/
|
|
264
286
|
searchAndContents<T extends ContentsOptions>(query: string, options?: RegularSearchOptions & T): Promise<SearchResponse<T>>;
|
|
265
287
|
/**
|
|
@@ -273,24 +295,50 @@ declare class Exa {
|
|
|
273
295
|
* Finds similar links to the provided URL and returns the contents of the documents.
|
|
274
296
|
* @param {string} url - The URL for which to find similar links.
|
|
275
297
|
* @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.
|
|
276
|
-
* @returns {Promise<SearchResponse<T>>} A list of similar search results.
|
|
298
|
+
* @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.
|
|
277
299
|
*/
|
|
278
300
|
findSimilarAndContents<T extends ContentsOptions>(url: string, options?: FindSimilarOptions & T): Promise<SearchResponse<T>>;
|
|
279
301
|
/**
|
|
280
|
-
* Retrieves contents of documents based on URLs
|
|
281
|
-
* @param {string | string[] | SearchResult[]} urls -
|
|
302
|
+
* Retrieves contents of documents based on URLs.
|
|
303
|
+
* @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.
|
|
282
304
|
* @param {ContentsOptions} [options] - Additional options for retrieving document contents.
|
|
283
|
-
* @returns {Promise<SearchResponse<T>>} A list of document contents.
|
|
305
|
+
* @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.
|
|
284
306
|
*/
|
|
285
307
|
getContents<T extends ContentsOptions>(urls: string | string[] | SearchResult<T>[], options?: T): Promise<SearchResponse<T>>;
|
|
286
308
|
/**
|
|
287
|
-
*
|
|
309
|
+
* Generate an answer to a query.
|
|
288
310
|
* @param {string} query - The question or query to answer.
|
|
289
311
|
* @param {AnswerOptions} [options] - Additional options for answer generation.
|
|
290
|
-
* @
|
|
291
|
-
*
|
|
312
|
+
* @returns {Promise<AnswerResponse>} The generated answer and source references.
|
|
313
|
+
*
|
|
314
|
+
* Note: For streaming responses, use the `streamAnswer` method:
|
|
315
|
+
* ```ts
|
|
316
|
+
* for await (const chunk of exa.streamAnswer(query)) {
|
|
317
|
+
* // Handle chunks
|
|
318
|
+
* }
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
answer(query: string, options?: AnswerOptions): Promise<AnswerResponse>;
|
|
322
|
+
/**
|
|
323
|
+
* Stream an answer as an async generator
|
|
324
|
+
*
|
|
325
|
+
* Each iteration yields a chunk with partial text (`content`) or new citations.
|
|
326
|
+
* Use this if you'd like to read the answer incrementally, e.g. in a chat UI.
|
|
327
|
+
*
|
|
328
|
+
* Example usage:
|
|
329
|
+
* ```ts
|
|
330
|
+
* for await (const chunk of exa.streamAnswer("What is quantum computing?", { text: false })) {
|
|
331
|
+
* if (chunk.content) process.stdout.write(chunk.content);
|
|
332
|
+
* if (chunk.citations) {
|
|
333
|
+
* console.log("\nCitations: ", chunk.citations);
|
|
334
|
+
* }
|
|
335
|
+
* }
|
|
336
|
+
* ```
|
|
292
337
|
*/
|
|
293
|
-
|
|
338
|
+
streamAnswer(query: string, options?: {
|
|
339
|
+
text?: boolean;
|
|
340
|
+
}): AsyncGenerator<AnswerStreamChunk>;
|
|
341
|
+
private processChunk;
|
|
294
342
|
}
|
|
295
343
|
|
|
296
|
-
export { AnswerOptions, AnswerResponse, AnswerStreamResponse, BaseSearchOptions, ContentsOptions, ContentsResultComponent, Default, ExtrasOptions, ExtrasResponse, FindSimilarOptions, HighlightsContentsOptions, HighlightsResponse, LivecrawlOptions, RegularSearchOptions, SearchResponse, SearchResult, SubpagesResponse, SummaryContentsOptions, SummaryResponse, TextContentsOptions, TextResponse, Exa as default };
|
|
344
|
+
export { AnswerOptions, AnswerResponse, AnswerStreamChunk, AnswerStreamResponse, BaseSearchOptions, ContentsOptions, ContentsResultComponent, Default, ExtrasOptions, ExtrasResponse, FindSimilarOptions, HighlightsContentsOptions, HighlightsResponse, LivecrawlOptions, RegularSearchOptions, SearchResponse, SearchResult, SubpagesResponse, SummaryContentsOptions, SummaryResponse, TextContentsOptions, TextResponse, Exa as default };
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,9 @@ var import_cross_fetch = __toESM(require("cross-fetch"));
|
|
|
37
37
|
var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : import_cross_fetch.default;
|
|
38
38
|
var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : import_cross_fetch.Headers;
|
|
39
39
|
var Exa = class {
|
|
40
|
+
/**
|
|
41
|
+
* Helper method to separate out the contents-specific options from the rest.
|
|
42
|
+
*/
|
|
40
43
|
extractContentsOptions(options) {
|
|
41
44
|
const {
|
|
42
45
|
text,
|
|
@@ -50,8 +53,9 @@ var Exa = class {
|
|
|
50
53
|
...rest
|
|
51
54
|
} = options;
|
|
52
55
|
const contentsOptions = {};
|
|
53
|
-
if (text === void 0 && summary === void 0 && highlights === void 0 && extras === void 0)
|
|
56
|
+
if (text === void 0 && summary === void 0 && highlights === void 0 && extras === void 0) {
|
|
54
57
|
contentsOptions.text = true;
|
|
58
|
+
}
|
|
55
59
|
if (text !== void 0)
|
|
56
60
|
contentsOptions.text = text;
|
|
57
61
|
if (summary !== void 0)
|
|
@@ -99,11 +103,9 @@ var Exa = class {
|
|
|
99
103
|
* @param {string} endpoint - The API endpoint to call.
|
|
100
104
|
* @param {string} method - The HTTP method to use.
|
|
101
105
|
* @param {any} [body] - The request body for POST requests.
|
|
102
|
-
* @param {boolean} [stream] - Whether to stream the response.
|
|
103
|
-
* @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
|
|
104
106
|
* @returns {Promise<any>} The response from the API.
|
|
105
107
|
*/
|
|
106
|
-
async request(endpoint, method, body
|
|
108
|
+
async request(endpoint, method, body) {
|
|
107
109
|
const response = await fetchImpl(this.baseURL + endpoint, {
|
|
108
110
|
method,
|
|
109
111
|
headers: this.headers,
|
|
@@ -115,48 +117,13 @@ var Exa = class {
|
|
|
115
117
|
`Request failed with status ${response.status}. ${message}`
|
|
116
118
|
);
|
|
117
119
|
}
|
|
118
|
-
if (stream && response.body) {
|
|
119
|
-
const reader = response.body.getReader();
|
|
120
|
-
const decoder = new TextDecoder();
|
|
121
|
-
let buffer = "";
|
|
122
|
-
try {
|
|
123
|
-
while (true) {
|
|
124
|
-
const { done, value } = await reader.read();
|
|
125
|
-
if (done)
|
|
126
|
-
break;
|
|
127
|
-
buffer += decoder.decode(value, { stream: true });
|
|
128
|
-
const lines = buffer.split("\n");
|
|
129
|
-
buffer = lines.pop() || "";
|
|
130
|
-
for (const line of lines) {
|
|
131
|
-
if (line.startsWith("data: ")) {
|
|
132
|
-
try {
|
|
133
|
-
const data = JSON.parse(line.slice(6));
|
|
134
|
-
onChunk?.(data);
|
|
135
|
-
} catch (e) {
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (buffer && buffer.startsWith("data: ")) {
|
|
141
|
-
try {
|
|
142
|
-
const data = JSON.parse(buffer.slice(6));
|
|
143
|
-
onChunk?.(data);
|
|
144
|
-
} catch (e) {
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
throw new Error(`Streaming error: ${error?.message || "Unknown error"}`);
|
|
149
|
-
} finally {
|
|
150
|
-
reader.releaseLock();
|
|
151
|
-
}
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
120
|
return await response.json();
|
|
155
121
|
}
|
|
156
122
|
/**
|
|
157
123
|
* Performs a search with an Exa prompt-engineered query.
|
|
124
|
+
*
|
|
158
125
|
* @param {string} query - The query string.
|
|
159
|
-
* @param {RegularSearchOptions} [options] - Additional search options
|
|
126
|
+
* @param {RegularSearchOptions} [options] - Additional search options
|
|
160
127
|
* @returns {Promise<SearchResponse<{}>>} A list of relevant search results.
|
|
161
128
|
*/
|
|
162
129
|
async search(query, options) {
|
|
@@ -164,9 +131,10 @@ var Exa = class {
|
|
|
164
131
|
}
|
|
165
132
|
/**
|
|
166
133
|
* Performs a search with an Exa prompt-engineered query and returns the contents of the documents.
|
|
134
|
+
*
|
|
167
135
|
* @param {string} query - The query string.
|
|
168
|
-
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
169
|
-
* @returns {Promise<SearchResponse<T>>} A list of relevant search results.
|
|
136
|
+
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
137
|
+
* @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.
|
|
170
138
|
*/
|
|
171
139
|
async searchAndContents(query, options) {
|
|
172
140
|
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
|
|
@@ -189,7 +157,7 @@ var Exa = class {
|
|
|
189
157
|
* Finds similar links to the provided URL and returns the contents of the documents.
|
|
190
158
|
* @param {string} url - The URL for which to find similar links.
|
|
191
159
|
* @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.
|
|
192
|
-
* @returns {Promise<SearchResponse<T>>} A list of similar search results.
|
|
160
|
+
* @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.
|
|
193
161
|
*/
|
|
194
162
|
async findSimilarAndContents(url, options) {
|
|
195
163
|
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
|
|
@@ -200,13 +168,13 @@ var Exa = class {
|
|
|
200
168
|
});
|
|
201
169
|
}
|
|
202
170
|
/**
|
|
203
|
-
* Retrieves contents of documents based on URLs
|
|
204
|
-
* @param {string | string[] | SearchResult[]} urls -
|
|
171
|
+
* Retrieves contents of documents based on URLs.
|
|
172
|
+
* @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.
|
|
205
173
|
* @param {ContentsOptions} [options] - Additional options for retrieving document contents.
|
|
206
|
-
* @returns {Promise<SearchResponse<T>>} A list of document contents.
|
|
174
|
+
* @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.
|
|
207
175
|
*/
|
|
208
176
|
async getContents(urls, options) {
|
|
209
|
-
if (urls.length === 0) {
|
|
177
|
+
if (!urls || Array.isArray(urls) && urls.length === 0) {
|
|
210
178
|
throw new Error("Must provide at least one URL");
|
|
211
179
|
}
|
|
212
180
|
let requestUrls;
|
|
@@ -221,23 +189,134 @@ var Exa = class {
|
|
|
221
189
|
urls: requestUrls,
|
|
222
190
|
...options
|
|
223
191
|
};
|
|
224
|
-
return await this.request(
|
|
192
|
+
return await this.request("/contents", "POST", payload);
|
|
225
193
|
}
|
|
226
194
|
/**
|
|
227
|
-
*
|
|
195
|
+
* Generate an answer to a query.
|
|
228
196
|
* @param {string} query - The question or query to answer.
|
|
229
197
|
* @param {AnswerOptions} [options] - Additional options for answer generation.
|
|
230
|
-
* @
|
|
231
|
-
*
|
|
198
|
+
* @returns {Promise<AnswerResponse>} The generated answer and source references.
|
|
199
|
+
*
|
|
200
|
+
* Note: For streaming responses, use the `streamAnswer` method:
|
|
201
|
+
* ```ts
|
|
202
|
+
* for await (const chunk of exa.streamAnswer(query)) {
|
|
203
|
+
* // Handle chunks
|
|
204
|
+
* }
|
|
205
|
+
* ```
|
|
232
206
|
*/
|
|
233
|
-
async answer(query, options
|
|
207
|
+
async answer(query, options) {
|
|
208
|
+
if (options?.stream) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"For streaming responses, please use streamAnswer() instead:\n\nfor await (const chunk of exa.streamAnswer(query)) {\n // Handle chunks\n}"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
234
213
|
const requestBody = {
|
|
235
214
|
query,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
includeText: options?.includeText ?? false
|
|
215
|
+
stream: false,
|
|
216
|
+
text: options?.text ?? false
|
|
239
217
|
};
|
|
240
|
-
return await this.request("/answer", "POST", requestBody
|
|
218
|
+
return await this.request("/answer", "POST", requestBody);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Stream an answer as an async generator
|
|
222
|
+
*
|
|
223
|
+
* Each iteration yields a chunk with partial text (`content`) or new citations.
|
|
224
|
+
* Use this if you'd like to read the answer incrementally, e.g. in a chat UI.
|
|
225
|
+
*
|
|
226
|
+
* Example usage:
|
|
227
|
+
* ```ts
|
|
228
|
+
* for await (const chunk of exa.streamAnswer("What is quantum computing?", { text: false })) {
|
|
229
|
+
* if (chunk.content) process.stdout.write(chunk.content);
|
|
230
|
+
* if (chunk.citations) {
|
|
231
|
+
* console.log("\nCitations: ", chunk.citations);
|
|
232
|
+
* }
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
async *streamAnswer(query, options) {
|
|
237
|
+
const body = {
|
|
238
|
+
query,
|
|
239
|
+
text: options?.text ?? false,
|
|
240
|
+
stream: true
|
|
241
|
+
};
|
|
242
|
+
const response = await fetchImpl(this.baseURL + "/answer", {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: this.headers,
|
|
245
|
+
body: JSON.stringify(body)
|
|
246
|
+
});
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
const message = await response.text();
|
|
249
|
+
throw new Error(
|
|
250
|
+
`Request failed with status ${response.status}. ${message}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
const reader = response.body?.getReader();
|
|
254
|
+
if (!reader) {
|
|
255
|
+
throw new Error("No response body available for streaming.");
|
|
256
|
+
}
|
|
257
|
+
const decoder = new TextDecoder();
|
|
258
|
+
let buffer = "";
|
|
259
|
+
try {
|
|
260
|
+
while (true) {
|
|
261
|
+
const { done, value } = await reader.read();
|
|
262
|
+
if (done)
|
|
263
|
+
break;
|
|
264
|
+
buffer += decoder.decode(value, { stream: true });
|
|
265
|
+
const lines = buffer.split("\n");
|
|
266
|
+
buffer = lines.pop() || "";
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
if (!line.startsWith("data: "))
|
|
269
|
+
continue;
|
|
270
|
+
const jsonStr = line.replace(/^data:\s*/, "").trim();
|
|
271
|
+
if (!jsonStr || jsonStr === "[DONE]") {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
let chunkData;
|
|
275
|
+
try {
|
|
276
|
+
chunkData = JSON.parse(jsonStr);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const chunk = this.processChunk(chunkData);
|
|
281
|
+
if (chunk.content || chunk.citations) {
|
|
282
|
+
yield chunk;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (buffer.startsWith("data: ")) {
|
|
287
|
+
const leftover = buffer.replace(/^data:\s*/, "").trim();
|
|
288
|
+
if (leftover && leftover !== "[DONE]") {
|
|
289
|
+
try {
|
|
290
|
+
const chunkData = JSON.parse(leftover);
|
|
291
|
+
const chunk = this.processChunk(chunkData);
|
|
292
|
+
if (chunk.content || chunk.citations) {
|
|
293
|
+
yield chunk;
|
|
294
|
+
}
|
|
295
|
+
} catch (e) {
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} finally {
|
|
300
|
+
reader.releaseLock();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
processChunk(chunkData) {
|
|
304
|
+
let content;
|
|
305
|
+
let citations;
|
|
306
|
+
if (chunkData.choices && chunkData.choices[0] && chunkData.choices[0].delta) {
|
|
307
|
+
content = chunkData.choices[0].delta.content;
|
|
308
|
+
}
|
|
309
|
+
if (chunkData.citations && chunkData.citations !== "null") {
|
|
310
|
+
citations = chunkData.citations.map((c) => ({
|
|
311
|
+
id: c.id,
|
|
312
|
+
url: c.url,
|
|
313
|
+
title: c.title,
|
|
314
|
+
publishedDate: c.publishedDate,
|
|
315
|
+
author: c.author,
|
|
316
|
+
text: c.text
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
return { content, citations };
|
|
241
320
|
}
|
|
242
321
|
};
|
|
243
322
|
var src_default = Exa;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fetch, { Headers } from \"cross-fetch\";\n\n// Use native fetch in Node.js environments\nconst fetchImpl = typeof global !== \"undefined\" && global.fetch ? global.fetch : fetch;\nconst HeadersImpl = typeof global !== \"undefined\" && global.Headers ? global.Headers : Headers;\n\nconst isBeta = false;\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} SearchOptions\n * @property {number} [numResults] - Number of search results to return. Default 10. Max 10 for basic plans.\n * @property {string[]} [includeDomains] - List of domains to include in the search.\n * @property {string[]} [excludeDomains] - List of domains to exclude in the search.\n * @property {string} [startCrawlDate] - Start date for results based on crawl date.\n * @property {string} [endCrawlDate] - End date for results based on crawl date.\n * @property {string} [startPublishedDate] - Start date for results based on published date.\n * @property {string} [endPublishedDate] - End date for results based on published date.\n * @property {string} [category] - A data category to focus on, with higher comprehensivity and data cleanliness. Currently, the only category is company.\n * @property {string[]} [includeText] - List of strings that must be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [excludeText] - List of strings that must not be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [flags] - Experimental flags\n */\nexport type BaseSearchOptions = {\n numResults?: number;\n includeDomains?: string[];\n excludeDomains?: string[];\n startCrawlDate?: string;\n endCrawlDate?: string;\n startPublishedDate?: string;\n endPublishedDate?: string;\n category?: \"company\" | \"research paper\" | \"news\" | \"pdf\" | \"github\" | \"tweet\" | \"personal site\" | \"linkedin profile\" | \"financial report\";\n includeText?: string[];\n excludeText?: string[];\n flags?: string[];\n};\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} RegularSearchOptions\n */\nexport type RegularSearchOptions = BaseSearchOptions & {\n useAutoprompt?: boolean;\n type?: \"keyword\" | \"neural\" | \"auto\";\n};\n\n/**\n * Options for finding similar links.\n * @typedef {Object} FindSimilarOptions\n * @property {boolean} [excludeSourceDomain] - If true, excludes links from the base domain of the input.\n */\nexport type FindSimilarOptions = BaseSearchOptions & {\n excludeSourceDomain?: boolean;\n};\n\nexport type ExtrasOptions = { links?: number; imageLinks?: number };\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} ContentsOptions\n * @property {TextContentsOptions | boolean} [text] - Options for retrieving text contents.\n * @property {HighlightsContentsOptions | boolean} [highlights] - Options for retrieving highlights.\n * @property {SummaryContentsOptions | boolean} [summary] - Options for retrieving summary.\n * @property {LivecrawlOptions} [livecrawl] - Options for livecrawling contents. Default is \"never\" for neural/auto search, \"fallback\" for keyword search.\n * @property {number} [livecrawlTimeout] - The timeout for livecrawling. Max and default is 10000ms.\n * @property {boolean} [filterEmptyResults] - If true, filters out results with no contents. Default is true.\n * @property {number} [subpages] - The number of subpages to return for each result, where each subpage is derived from an internal link for the result.\n * @property {string | string[]} [subpageTarget] - Text used to match/rank subpages in the returned subpage list. You could use \"about\" to get *about* page for websites. Note that this is a fuzzy matcher.\n * @property {ExtrasOptions} [extras] - Miscelleneous data for derived from results\n */\nexport type ContentsOptions = {\n text?: TextContentsOptions | true;\n highlights?: HighlightsContentsOptions | true;\n summary?: SummaryContentsOptions | true;\n livecrawl?: LivecrawlOptions;\n livecrawlTimeout?: number;\n filterEmptyResults?: boolean;\n subpages?: number;\n subpageTarget?: string | string[];\n extras?: ExtrasOptions;\n} & (typeof isBeta extends true ? {} : {});\n\n/**\n * Options for livecrawling contents\n * @typedef {string} LivecrawlOptions\n */\nexport type LivecrawlOptions = \"never\" | \"fallback\" | \"always\" | \"auto\";\n\n/**\n * Options for retrieving text from page.\n * @typedef {Object} TextContentsOptions\n * @property {number} [maxCharacters] - The maximum number of characters to return.\n * @property {boolean} [includeHtmlTags] - If true, includes HTML tags in the returned text. Default: false\n */\nexport type TextContentsOptions = {\n maxCharacters?: number;\n includeHtmlTags?: boolean;\n};\n\n/**\n * Options for retrieving highlights from page.\n * @typedef {Object} HighlightsContentsOptions\n * @property {string} [query] - The query string to use for highlights search.\n * @property {number} [numSentences] - The number of sentences to return for each highlight.\n * @property {number} [highlightsPerUrl] - The number of highlights to return for each URL.\n */\nexport type HighlightsContentsOptions = {\n query?: string;\n numSentences?: number;\n highlightsPerUrl?: number;\n};\n\n/**\n * Options for retrieving summary from page.\n * @typedef {Object} SummaryContentsOptions\n * @property {string} [query] - The query string to use for summary generation.\n */\nexport type SummaryContentsOptions = {\n query?: string;\n};\n\n/**\n * @typedef {Object} TextResponse\n * @property {string} text - Text from page\n */\nexport type TextResponse = { text: string };\n\n/**\n * @typedef {Object} HighlightsResponse\n * @property {string[]} highlights - The highlights as an array of strings.\n * @property {number[]} highlightScores - The corresponding scores as an array of floats, 0 to 1\n */\nexport type HighlightsResponse = {\n highlights: string[];\n highlightScores: number[];\n};\n\n/**\n * @typedef {Object} SummaryResponse\n * @property {string} summary - The generated summary of the page content.\n */\nexport type SummaryResponse = { summary: string };\n\n/**\n * @typedef {Object} ExtrasResponse\n * @property {string[]} links - The links on the page of a result\n * @property {string[]} imageLinks - The image links on the page of a result\n */\nexport type ExtrasResponse = { extras: { links?: string[]; imageLinks?: string[] } };\n\n/**\n * @typedef {Object} SubpagesResponse\n * @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The links on the page of a result\n */\nexport type SubpagesResponse<T extends ContentsOptions> = {\n subpages: ContentsResultComponent<T>[];\n};\n\nexport type Default<T extends {}, U> = [keyof T] extends [never] ? U : T;\n\n/**\n * @typedef {Object} ContentsResultComponent\n * Depending on 'ContentsOptions', this yields a combination of 'TextResponse', 'HighlightsResponse', 'SummaryResponse', or an empty object.\n *\n * @template T - A type extending from 'ContentsOptions'.\n */\nexport type ContentsResultComponent<T extends ContentsOptions> = Default<\n (T[\"text\"] extends object | true ? TextResponse : {}) &\n (T[\"highlights\"] extends object | true ? HighlightsResponse : {}) &\n (T[\"summary\"] extends object | true ? SummaryResponse : {}) &\n (T[\"subpages\"] extends number ? SubpagesResponse<T> : {}) &\n (T[\"extras\"] extends object ? ExtrasResponse : {}),\n TextResponse\n>;\n\n/**\n * Represents a search result object.\n * @typedef {Object} SearchResult\n * @property {string} title - The title of the search result.\n * @property {string} url - The URL of the search result.\n * @property {string} [publishedDate] - The estimated creation date of the content.\n * @property {string} [author] - The author of the content, if available.\n * @property {number} [score] - Similarity score between the query/url and the result.\n * @property {string} id - The temporary ID for the document.\n */\nexport type SearchResult<T extends ContentsOptions> = {\n title: string | null;\n url: string;\n publishedDate?: string;\n author?: string;\n score?: number;\n id: string;\n image?: string;\n favicon?: string;\n} & ContentsResultComponent<T>;\n\n/**\n * Represents a search response object.\n * @typedef {Object} SearchResponse\n * @property {Result[]} results - The list of search results.\n * @property {string} [autopromptString] - The autoprompt string, if applicable.\n * @property {string} [autoDate] - The autoprompt date, if applicable.\n * @property {string} requestId - The request ID for the search.\n */\nexport type SearchResponse<T extends ContentsOptions> = {\n results: SearchResult<T>[];\n autopromptString?: string;\n autoDate?: string;\n requestId: string;\n};\n\n/**\n * Options for the answer endpoint\n * @typedef {Object} AnswerOptions\n * @property {number} [expandedQueriesLimit] - Maximum number of query variations (0-4). Default 1.\n * @property {boolean} [stream] - Whether to stream the response. Default false.\n * @property {boolean} [includeText] - Whether to include text in the source results. Default false.\n */\nexport type AnswerOptions = {\n expandedQueriesLimit?: number;\n stream?: boolean;\n includeText?: boolean;\n};\n\n/**\n * Represents an answer response object from the /answer endpoint.\n * @typedef {Object} AnswerResponse\n * @property {string} answer - The generated answer text.\n * @property {SearchResult<{}>[]]} sources - The sources used to generate the answer.\n * @property {string} [requestId] - Optional request ID for the answer.\n */\nexport type AnswerResponse = {\n answer: string;\n sources: SearchResult<{}>[];\n requestId?: string;\n};\n\n/**\n * Represents a streaming answer response chunk from the /answer endpoint.\n * @typedef {Object} AnswerStreamResponse\n * @property {string} [answer] - A chunk of the generated answer text.\n * @property {SearchResult<{}>[]]} [sources] - The sources used to generate the answer.\n * @property {string} [error] - Error message if something went wrong.\n */\nexport type AnswerStreamResponse = {\n answer?: string;\n sources?: SearchResult<{}>[];\n error?: string;\n};\n\n/**\n * The Exa class encapsulates the API's endpoints.\n */\nclass Exa {\n private baseURL: string;\n private headers: Headers;\n\n private extractContentsOptions<T extends ContentsOptions>(options: T): {\n contentsOptions: ContentsOptions;\n restOptions: Omit<T, keyof ContentsOptions>;\n } {\n const {\n text,\n highlights,\n summary,\n subpages,\n subpageTarget,\n extras,\n livecrawl,\n livecrawlTimeout,\n ...rest\n } = options;\n\n const contentsOptions: ContentsOptions = {};\n // don't send text if it's explicitly false\n if (\n text === undefined &&\n summary === undefined &&\n highlights === undefined &&\n extras === undefined\n )\n contentsOptions.text = true;\n if (text !== undefined) contentsOptions.text = text;\n\n if (summary !== undefined) contentsOptions.summary = summary;\n if (highlights !== undefined) contentsOptions.highlights = highlights;\n\n if (subpages !== undefined) contentsOptions.subpages = subpages;\n if (subpageTarget !== undefined)\n contentsOptions.subpageTarget = subpageTarget;\n\n if (extras !== undefined) contentsOptions.extras = extras;\n if (livecrawl !== undefined) contentsOptions.livecrawl = livecrawl;\n if (livecrawlTimeout !== undefined)\n contentsOptions.livecrawlTimeout = livecrawlTimeout;\n\n return {\n contentsOptions: contentsOptions,\n restOptions: rest as Omit<T, keyof ContentsOptions>,\n };\n }\n\n /**\n * Constructs the Exa API client.\n * @param {string} apiKey - The API key for authentication.\n * @param {string} [baseURL] - The base URL of the Exa API.\n */\n constructor(apiKey?: string, baseURL: string = \"https://api.exa.ai\") {\n this.baseURL = baseURL;\n if (!apiKey) {\n apiKey = process.env.EXASEARCH_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"API key must be provided as an argument or as an environment variable (EXASEARCH_API_KEY)\",\n );\n }\n }\n this.headers = new HeadersImpl({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.4.0\",\n });\n }\n\n /**\n * Makes a request to the Exa API.\n * @param {string} endpoint - The API endpoint to call.\n * @param {string} method - The HTTP method to use.\n * @param {any} [body] - The request body for POST requests.\n * @param {boolean} [stream] - Whether to stream the response.\n * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.\n * @returns {Promise<any>} The response from the API.\n */\n private async request(\n endpoint: string,\n method: string,\n body?: any,\n stream?: boolean,\n onChunk?: (chunk: AnswerStreamResponse) => void,\n ): Promise<any> {\n const response = await fetchImpl(this.baseURL + endpoint, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const message = (await response.json()).error;\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n /**\n * Handle streaming responses from the API. This processes Server-Sent Events (SSE)\n * where data is sent in chunks with the format \"data: {...}\". Each chunk is decoded,\n * parsed as JSON, and passed to the provided callback function.\n */\n if (stream && response.body) {\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const data = JSON.parse(line.slice(6));\n onChunk?.(data);\n } catch (e) {\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const data = JSON.parse(buffer.slice(6));\n onChunk?.(data);\n } catch (e) {\n }\n }\n } catch (error: any) {\n throw new Error(`Streaming error: ${error?.message || 'Unknown error'}`);\n } finally {\n reader.releaseLock();\n }\n\n return null;\n }\n return await response.json();\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query.\n * @param {string} query - The query string.\n * @param {RegularSearchOptions} [options] - Additional search options.\n * @returns {Promise<SearchResponse<{}>>} A list of relevant search results.\n */\n async search(\n query: string,\n options?: RegularSearchOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/search\", \"POST\", { query, ...options });\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query and returns the contents of the documents.\n * @param {string} query - The query string.\n * @param {RegularSearchOptions & T} [options] - Additional search + contents options.\n * @returns {Promise<SearchResponse<T>>} A list of relevant search results.\n */\n async searchAndContents<T extends ContentsOptions>(\n query: string,\n options?: RegularSearchOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/search\", \"POST\", {\n query,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Finds similar links to the provided URL.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions} [options] - Additional options for finding similar links.\n * @returns {Promise<SearchResponse<{}>>} A list of similar search results.\n */\n async findSimilar(\n url: string,\n options?: FindSimilarOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/findSimilar\", \"POST\", { url, ...options });\n }\n\n /**\n * Finds similar links to the provided URL and returns the contents of the documents.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.\n * @returns {Promise<SearchResponse<T>>} A list of similar search results.\n */\n async findSimilarAndContents<T extends ContentsOptions>(\n url: string,\n options?: FindSimilarOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/findSimilar\", \"POST\", {\n url,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Retrieves contents of documents based on URLs\n * @param {string | string[] | SearchResult[]} urls - An array of URLs.\n * @param {ContentsOptions} [options] - Additional options for retrieving document contents.\n * @returns {Promise<SearchResponse<T>>} A list of document contents.\n */\n async getContents<T extends ContentsOptions>(\n urls: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (urls.length === 0) {\n throw new Error(\"Must provide at least one URL\");\n }\n let requestUrls: string[];\n if (typeof urls === \"string\") {\n requestUrls = [urls];\n } else if (typeof urls[0] === \"string\") {\n requestUrls = urls as string[];\n } else {\n requestUrls = (urls as SearchResult<T>[]).map((result) => result.url);\n }\n const payload = {\n urls: requestUrls,\n ...options,\n };\n\n return await this.request(`/contents`, \"POST\", payload);\n }\n\n /**\n * Generates an answer to a query using search results as context.\n * @param {string} query - The question or query to answer.\n * @param {AnswerOptions} [options] - Additional options for answer generation.\n * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.\n * @returns {Promise<AnswerResponse | null>} The generated answer and source references, or null if streaming.\n */\n async answer(\n query: string,\n options?: AnswerOptions,\n onChunk?: (chunk: AnswerStreamResponse) => void,\n ): Promise<AnswerResponse | null> {\n const requestBody = {\n query,\n expandedQueriesLimit: options?.expandedQueriesLimit ?? 1,\n stream: options?.stream ?? false,\n includeText: options?.includeText ?? false\n };\n return await this.request(\"/answer\", \"POST\", requestBody, options?.stream, onChunk);\n }\n}\n\nexport default Exa;\n"],"mappings":"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","names":["fetch"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fetch, { Headers } from \"cross-fetch\";\n\n// Use native fetch in Node.js environments\nconst fetchImpl = typeof global !== \"undefined\" && global.fetch ? global.fetch : fetch;\nconst HeadersImpl = typeof global !== \"undefined\" && global.Headers ? global.Headers : Headers;\n\nconst isBeta = false;\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} SearchOptions\n * @property {number} [numResults] - Number of search results to return. Default 10. Max 10 for basic plans.\n * @property {string[]} [includeDomains] - List of domains to include in the search.\n * @property {string[]} [excludeDomains] - List of domains to exclude in the search.\n * @property {string} [startCrawlDate] - Start date for results based on crawl date.\n * @property {string} [endCrawlDate] - End date for results based on crawl date.\n * @property {string} [startPublishedDate] - Start date for results based on published date.\n * @property {string} [endPublishedDate] - End date for results based on published date.\n * @property {string} [category] - A data category to focus on, with higher comprehensivity and data cleanliness. Currently, the only category is company.\n * @property {string[]} [includeText] - List of strings that must be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [excludeText] - List of strings that must not be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [flags] - Experimental flags\n */\nexport type BaseSearchOptions = {\n numResults?: number;\n includeDomains?: string[];\n excludeDomains?: string[];\n startCrawlDate?: string;\n endCrawlDate?: string;\n startPublishedDate?: string;\n endPublishedDate?: string;\n category?:\n | \"company\"\n | \"research paper\"\n | \"news\"\n | \"pdf\"\n | \"github\"\n | \"tweet\"\n | \"personal site\"\n | \"linkedin profile\"\n | \"financial report\";\n includeText?: string[];\n excludeText?: string[];\n flags?: string[];\n};\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} RegularSearchOptions\n */\nexport type RegularSearchOptions = BaseSearchOptions & {\n /**\n * If true, the search results are moderated for safety.\n */\n moderation?: boolean;\n\n useAutoprompt?: boolean;\n type?: \"keyword\" | \"neural\" | \"auto\";\n};\n\n/**\n * Options for finding similar links.\n * @typedef {Object} FindSimilarOptions\n * @property {boolean} [excludeSourceDomain] - If true, excludes links from the base domain of the input.\n */\nexport type FindSimilarOptions = BaseSearchOptions & {\n excludeSourceDomain?: boolean;\n};\n\nexport type ExtrasOptions = { links?: number; imageLinks?: number };\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} ContentsOptions\n * @property {TextContentsOptions | boolean} [text] - Options for retrieving text contents.\n * @property {HighlightsContentsOptions | boolean} [highlights] - Options for retrieving highlights.\n * @property {SummaryContentsOptions | boolean} [summary] - Options for retrieving summary.\n * @property {LivecrawlOptions} [livecrawl] - Options for livecrawling contents. Default is \"never\" for neural/auto search, \"fallback\" for keyword search.\n * @property {number} [livecrawlTimeout] - The timeout for livecrawling. Max and default is 10000ms.\n * @property {boolean} [filterEmptyResults] - If true, filters out results with no contents. Default is true.\n * @property {number} [subpages] - The number of subpages to return for each result, where each subpage is derived from an internal link for the result.\n * @property {string | string[]} [subpageTarget] - Text used to match/rank subpages in the returned subpage list. You could use \"about\" to get *about* page for websites. Note that this is a fuzzy matcher.\n * @property {ExtrasOptions} [extras] - Miscelleneous data derived from results\n */\nexport type ContentsOptions = {\n text?: TextContentsOptions | true;\n highlights?: HighlightsContentsOptions | true;\n summary?: SummaryContentsOptions | true;\n livecrawl?: LivecrawlOptions;\n livecrawlTimeout?: number;\n filterEmptyResults?: boolean;\n subpages?: number;\n subpageTarget?: string | string[];\n extras?: ExtrasOptions;\n} & (typeof isBeta extends true ? {} : {});\n\n/**\n * Options for livecrawling contents\n * @typedef {string} LivecrawlOptions\n */\nexport type LivecrawlOptions = \"never\" | \"fallback\" | \"always\" | \"auto\";\n\n/**\n * Options for retrieving text from page.\n * @typedef {Object} TextContentsOptions\n * @property {number} [maxCharacters] - The maximum number of characters to return.\n * @property {boolean} [includeHtmlTags] - If true, includes HTML tags in the returned text. Default: false\n */\nexport type TextContentsOptions = {\n maxCharacters?: number;\n includeHtmlTags?: boolean;\n};\n\n/**\n * Options for retrieving highlights from page.\n * @typedef {Object} HighlightsContentsOptions\n * @property {string} [query] - The query string to use for highlights search.\n * @property {number} [numSentences] - The number of sentences to return for each highlight.\n * @property {number} [highlightsPerUrl] - The number of highlights to return for each URL.\n */\nexport type HighlightsContentsOptions = {\n query?: string;\n numSentences?: number;\n highlightsPerUrl?: number;\n};\n\n/**\n * Options for retrieving summary from page.\n * @typedef {Object} SummaryContentsOptions\n * @property {string} [query] - The query string to use for summary generation.\n */\nexport type SummaryContentsOptions = {\n query?: string;\n};\n\n/**\n * @typedef {Object} TextResponse\n * @property {string} text - Text from page\n */\nexport type TextResponse = { text: string };\n\n/**\n * @typedef {Object} HighlightsResponse\n * @property {string[]} highlights - The highlights as an array of strings.\n * @property {number[]} highlightScores - The corresponding scores as an array of floats, 0 to 1\n */\nexport type HighlightsResponse = {\n highlights: string[];\n highlightScores: number[];\n};\n\n/**\n * @typedef {Object} SummaryResponse\n * @property {string} summary - The generated summary of the page content.\n */\nexport type SummaryResponse = { summary: string };\n\n/**\n * @typedef {Object} ExtrasResponse\n * @property {string[]} links - The links on the page of a result\n * @property {string[]} imageLinks - The image links on the page of a result\n */\nexport type ExtrasResponse = { extras: { links?: string[]; imageLinks?: string[] } };\n\n/**\n * @typedef {Object} SubpagesResponse\n * @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The subpages for a result\n */\nexport type SubpagesResponse<T extends ContentsOptions> = {\n subpages: ContentsResultComponent<T>[];\n};\n\nexport type Default<T extends {}, U> = [keyof T] extends [never] ? U : T;\n\n/**\n * @typedef {Object} ContentsResultComponent\n * Depending on 'ContentsOptions', this yields a combination of 'TextResponse', 'HighlightsResponse', 'SummaryResponse', or an empty object.\n *\n * @template T - A type extending from 'ContentsOptions'.\n */\nexport type ContentsResultComponent<T extends ContentsOptions> = Default<\n (T[\"text\"] extends object | true ? TextResponse : {}) &\n (T[\"highlights\"] extends object | true ? HighlightsResponse : {}) &\n (T[\"summary\"] extends object | true ? SummaryResponse : {}) &\n (T[\"subpages\"] extends number ? SubpagesResponse<T> : {}) &\n (T[\"extras\"] extends object ? ExtrasResponse : {}),\n TextResponse\n>;\n\n/**\n * Represents a search result object.\n * @typedef {Object} SearchResult\n * @property {string} title - The title of the search result.\n * @property {string} url - The URL of the search result.\n * @property {string} [publishedDate] - The estimated creation date of the content.\n * @property {string} [author] - The author of the content, if available.\n * @property {number} [score] - Similarity score between the query/url and the result.\n * @property {string} id - The temporary ID for the document.\n * @property {string} [image] - A representative image for the content, if any.\n * @property {string} [favicon] - A favicon for the site, if any.\n */\nexport type SearchResult<T extends ContentsOptions> = {\n title: string | null;\n url: string;\n publishedDate?: string;\n author?: string;\n score?: number;\n id: string;\n image?: string;\n favicon?: string;\n} & ContentsResultComponent<T>;\n\n/**\n * Represents a search response object.\n * @typedef {Object} SearchResponse\n * @property {Result[]} results - The list of search results.\n * @property {string} [autopromptString] - The autoprompt string, if applicable.\n * @property {string} [autoDate] - The autoprompt date, if applicable.\n * @property {string} requestId - The request ID for the search.\n */\nexport type SearchResponse<T extends ContentsOptions> = {\n results: SearchResult<T>[];\n autopromptString?: string;\n autoDate?: string;\n requestId: string;\n};\n\n/**\n * Options for the answer endpoint\n * @typedef {Object} AnswerOptions\n * @property {boolean} [stream] - Whether to stream the response. Default false.\n * @property {boolean} [text] - Whether to include text in the source results. Default false.\n */\nexport type AnswerOptions = {\n stream?: boolean;\n text?: boolean;\n};\n\n/**\n * Represents an answer response object from the /answer endpoint.\n * @typedef {Object} AnswerResponse\n * @property {string} answer - The generated answer text.\n * @property {SearchResult<{}>[]} citations - The sources used to generate the answer.\n * @property {string} [requestId] - Optional request ID for the answer.\n */\nexport type AnswerResponse = {\n answer: string;\n citations: SearchResult<{}>[];\n requestId?: string;\n};\n\nexport type AnswerStreamChunk = {\n /**\n * The partial text content of the answer (if present in this chunk).\n */\n content?: string;\n /**\n * Citations associated with the current chunk of text (if present).\n */\n citations?: Array<{\n id: string;\n url: string;\n title?: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n }>;\n};\n\n/**\n * Represents a streaming answer response chunk from the /answer endpoint.\n * @typedef {Object} AnswerStreamResponse\n * @property {string} [answer] - A chunk of the generated answer text.\n * @property {SearchResult<{}>[]]} [citations] - The sources used to generate the answer.\n */\nexport type AnswerStreamResponse = {\n answer?: string;\n citations?: SearchResult<{}>[];\n};\n\n/**\n * The Exa class encapsulates the API's endpoints.\n */\nclass Exa {\n private baseURL: string;\n private headers: Headers;\n\n /**\n * Helper method to separate out the contents-specific options from the rest.\n */\n private extractContentsOptions<T extends ContentsOptions>(options: T): {\n contentsOptions: ContentsOptions;\n restOptions: Omit<T, keyof ContentsOptions>;\n } {\n const {\n text,\n highlights,\n summary,\n subpages,\n subpageTarget,\n extras,\n livecrawl,\n livecrawlTimeout,\n ...rest\n } = options;\n\n const contentsOptions: ContentsOptions = {};\n\n // Default: if none of text, summary, or highlights is provided, we retrieve text\n if (\n text === undefined &&\n summary === undefined &&\n highlights === undefined &&\n extras === undefined\n ) {\n contentsOptions.text = true;\n }\n\n if (text !== undefined) contentsOptions.text = text;\n if (summary !== undefined) contentsOptions.summary = summary;\n if (highlights !== undefined) contentsOptions.highlights = highlights;\n if (subpages !== undefined) contentsOptions.subpages = subpages;\n if (subpageTarget !== undefined) contentsOptions.subpageTarget = subpageTarget;\n if (extras !== undefined) contentsOptions.extras = extras;\n if (livecrawl !== undefined) contentsOptions.livecrawl = livecrawl;\n if (livecrawlTimeout !== undefined) contentsOptions.livecrawlTimeout = livecrawlTimeout;\n\n return {\n contentsOptions,\n restOptions: rest as Omit<T, keyof ContentsOptions>,\n };\n }\n\n /**\n * Constructs the Exa API client.\n * @param {string} apiKey - The API key for authentication.\n * @param {string} [baseURL] - The base URL of the Exa API.\n */\n constructor(apiKey?: string, baseURL: string = \"https://api.exa.ai\") {\n this.baseURL = baseURL;\n if (!apiKey) {\n apiKey = process.env.EXASEARCH_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"API key must be provided as an argument or as an environment variable (EXASEARCH_API_KEY)\",\n );\n }\n }\n this.headers = new HeadersImpl({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.4.0\",\n });\n }\n\n /**\n * Makes a request to the Exa API.\n * @param {string} endpoint - The API endpoint to call.\n * @param {string} method - The HTTP method to use.\n * @param {any} [body] - The request body for POST requests.\n * @returns {Promise<any>} The response from the API.\n */\n private async request(\n endpoint: string,\n method: string,\n body?: any,\n ): Promise<any> {\n const response = await fetchImpl(this.baseURL + endpoint, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const message = (await response.json()).error;\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n return await response.json();\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query.\n * \n * @param {string} query - The query string.\n * @param {RegularSearchOptions} [options] - Additional search options\n * @returns {Promise<SearchResponse<{}>>} A list of relevant search results.\n */\n async search(\n query: string,\n options?: RegularSearchOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/search\", \"POST\", { query, ...options });\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query and returns the contents of the documents.\n * \n * @param {string} query - The query string.\n * @param {RegularSearchOptions & T} [options] - Additional search + contents options\n * @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.\n */\n async searchAndContents<T extends ContentsOptions>(\n query: string,\n options?: RegularSearchOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/search\", \"POST\", {\n query,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Finds similar links to the provided URL.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions} [options] - Additional options for finding similar links.\n * @returns {Promise<SearchResponse<{}>>} A list of similar search results.\n */\n async findSimilar(\n url: string,\n options?: FindSimilarOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/findSimilar\", \"POST\", { url, ...options });\n }\n\n /**\n * Finds similar links to the provided URL and returns the contents of the documents.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.\n * @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.\n */\n async findSimilarAndContents<T extends ContentsOptions>(\n url: string,\n options?: FindSimilarOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/findSimilar\", \"POST\", {\n url,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Retrieves contents of documents based on URLs.\n * @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.\n * @param {ContentsOptions} [options] - Additional options for retrieving document contents.\n * @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.\n */\n async getContents<T extends ContentsOptions>(\n urls: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (!urls || (Array.isArray(urls) && urls.length === 0)) {\n throw new Error(\"Must provide at least one URL\");\n }\n\n let requestUrls: string[];\n\n if (typeof urls === \"string\") {\n requestUrls = [urls];\n } else if (typeof urls[0] === \"string\") {\n requestUrls = urls as string[];\n } else {\n requestUrls = (urls as SearchResult<T>[]).map((result) => result.url);\n }\n\n const payload = {\n urls: requestUrls,\n ...options,\n };\n\n return await this.request(\"/contents\", \"POST\", payload);\n }\n\n /**\n * Generate an answer to a query.\n * @param {string} query - The question or query to answer.\n * @param {AnswerOptions} [options] - Additional options for answer generation.\n * @returns {Promise<AnswerResponse>} The generated answer and source references.\n * \n * Note: For streaming responses, use the `streamAnswer` method:\n * ```ts\n * for await (const chunk of exa.streamAnswer(query)) {\n * // Handle chunks\n * }\n * ```\n */\n async answer(\n query: string,\n options?: AnswerOptions,\n ): Promise<AnswerResponse> {\n if (options?.stream) {\n throw new Error(\n \"For streaming responses, please use streamAnswer() instead:\\n\\n\" +\n \"for await (const chunk of exa.streamAnswer(query)) {\\n\" +\n \" // Handle chunks\\n\" +\n \"}\"\n );\n }\n\n // For non-streaming requests, make a regular API call\n const requestBody = {\n query,\n stream: false,\n text: options?.text ?? false\n };\n\n return await this.request(\"/answer\", \"POST\", requestBody);\n }\n\n /**\n * Stream an answer as an async generator\n *\n * Each iteration yields a chunk with partial text (`content`) or new citations.\n * Use this if you'd like to read the answer incrementally, e.g. in a chat UI.\n *\n * Example usage:\n * ```ts\n * for await (const chunk of exa.streamAnswer(\"What is quantum computing?\", { text: false })) {\n * if (chunk.content) process.stdout.write(chunk.content);\n * if (chunk.citations) {\n * console.log(\"\\nCitations: \", chunk.citations);\n * }\n * }\n * ```\n */\n async *streamAnswer(\n query: string,\n options?: { text?: boolean }\n ): AsyncGenerator<AnswerStreamChunk> {\n // Build the POST body and fetch the streaming response.\n const body = {\n query,\n text: options?.text ?? false,\n stream: true,\n };\n\n const response = await fetchImpl(this.baseURL + \"/answer\", {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"No response body available for streaming.\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n\n const jsonStr = line.replace(/^data:\\s*/, \"\").trim();\n if (!jsonStr || jsonStr === \"[DONE]\") {\n continue;\n }\n\n let chunkData: any;\n try {\n chunkData = JSON.parse(jsonStr);\n } catch (err) {\n continue;\n }\n\n const chunk = this.processChunk(chunkData);\n if (chunk.content || chunk.citations) {\n yield chunk;\n }\n }\n }\n\n if (buffer.startsWith(\"data: \")) {\n const leftover = buffer.replace(/^data:\\s*/, \"\").trim();\n if (leftover && leftover !== \"[DONE]\") {\n try {\n const chunkData = JSON.parse(leftover);\n const chunk = this.processChunk(chunkData);\n if (chunk.content || chunk.citations) {\n yield chunk;\n }\n } catch (e) {\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n private processChunk(chunkData: any): AnswerStreamChunk {\n let content: string | undefined;\n let citations:\n | Array<{\n id: string;\n url: string;\n title?: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n }>\n | undefined;\n\n if (chunkData.choices && chunkData.choices[0] && chunkData.choices[0].delta) {\n content = chunkData.choices[0].delta.content;\n }\n\n if (chunkData.citations && chunkData.citations !== \"null\") {\n citations = chunkData.citations.map((c: any) => ({\n id: c.id,\n url: c.url,\n title: c.title,\n publishedDate: c.publishedDate,\n author: c.author,\n text: c.text,\n }));\n }\n\n return { content, citations };\n }\n}\n\nexport default Exa;\n"],"mappings":"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","names":["fetch"]}
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,9 @@ import fetch, { Headers } from "cross-fetch";
|
|
|
3
3
|
var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : fetch;
|
|
4
4
|
var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : Headers;
|
|
5
5
|
var Exa = class {
|
|
6
|
+
/**
|
|
7
|
+
* Helper method to separate out the contents-specific options from the rest.
|
|
8
|
+
*/
|
|
6
9
|
extractContentsOptions(options) {
|
|
7
10
|
const {
|
|
8
11
|
text,
|
|
@@ -16,8 +19,9 @@ var Exa = class {
|
|
|
16
19
|
...rest
|
|
17
20
|
} = options;
|
|
18
21
|
const contentsOptions = {};
|
|
19
|
-
if (text === void 0 && summary === void 0 && highlights === void 0 && extras === void 0)
|
|
22
|
+
if (text === void 0 && summary === void 0 && highlights === void 0 && extras === void 0) {
|
|
20
23
|
contentsOptions.text = true;
|
|
24
|
+
}
|
|
21
25
|
if (text !== void 0)
|
|
22
26
|
contentsOptions.text = text;
|
|
23
27
|
if (summary !== void 0)
|
|
@@ -65,11 +69,9 @@ var Exa = class {
|
|
|
65
69
|
* @param {string} endpoint - The API endpoint to call.
|
|
66
70
|
* @param {string} method - The HTTP method to use.
|
|
67
71
|
* @param {any} [body] - The request body for POST requests.
|
|
68
|
-
* @param {boolean} [stream] - Whether to stream the response.
|
|
69
|
-
* @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
|
|
70
72
|
* @returns {Promise<any>} The response from the API.
|
|
71
73
|
*/
|
|
72
|
-
async request(endpoint, method, body
|
|
74
|
+
async request(endpoint, method, body) {
|
|
73
75
|
const response = await fetchImpl(this.baseURL + endpoint, {
|
|
74
76
|
method,
|
|
75
77
|
headers: this.headers,
|
|
@@ -81,48 +83,13 @@ var Exa = class {
|
|
|
81
83
|
`Request failed with status ${response.status}. ${message}`
|
|
82
84
|
);
|
|
83
85
|
}
|
|
84
|
-
if (stream && response.body) {
|
|
85
|
-
const reader = response.body.getReader();
|
|
86
|
-
const decoder = new TextDecoder();
|
|
87
|
-
let buffer = "";
|
|
88
|
-
try {
|
|
89
|
-
while (true) {
|
|
90
|
-
const { done, value } = await reader.read();
|
|
91
|
-
if (done)
|
|
92
|
-
break;
|
|
93
|
-
buffer += decoder.decode(value, { stream: true });
|
|
94
|
-
const lines = buffer.split("\n");
|
|
95
|
-
buffer = lines.pop() || "";
|
|
96
|
-
for (const line of lines) {
|
|
97
|
-
if (line.startsWith("data: ")) {
|
|
98
|
-
try {
|
|
99
|
-
const data = JSON.parse(line.slice(6));
|
|
100
|
-
onChunk?.(data);
|
|
101
|
-
} catch (e) {
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (buffer && buffer.startsWith("data: ")) {
|
|
107
|
-
try {
|
|
108
|
-
const data = JSON.parse(buffer.slice(6));
|
|
109
|
-
onChunk?.(data);
|
|
110
|
-
} catch (e) {
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
} catch (error) {
|
|
114
|
-
throw new Error(`Streaming error: ${error?.message || "Unknown error"}`);
|
|
115
|
-
} finally {
|
|
116
|
-
reader.releaseLock();
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
86
|
return await response.json();
|
|
121
87
|
}
|
|
122
88
|
/**
|
|
123
89
|
* Performs a search with an Exa prompt-engineered query.
|
|
90
|
+
*
|
|
124
91
|
* @param {string} query - The query string.
|
|
125
|
-
* @param {RegularSearchOptions} [options] - Additional search options
|
|
92
|
+
* @param {RegularSearchOptions} [options] - Additional search options
|
|
126
93
|
* @returns {Promise<SearchResponse<{}>>} A list of relevant search results.
|
|
127
94
|
*/
|
|
128
95
|
async search(query, options) {
|
|
@@ -130,9 +97,10 @@ var Exa = class {
|
|
|
130
97
|
}
|
|
131
98
|
/**
|
|
132
99
|
* Performs a search with an Exa prompt-engineered query and returns the contents of the documents.
|
|
100
|
+
*
|
|
133
101
|
* @param {string} query - The query string.
|
|
134
|
-
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
135
|
-
* @returns {Promise<SearchResponse<T>>} A list of relevant search results.
|
|
102
|
+
* @param {RegularSearchOptions & T} [options] - Additional search + contents options
|
|
103
|
+
* @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.
|
|
136
104
|
*/
|
|
137
105
|
async searchAndContents(query, options) {
|
|
138
106
|
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
|
|
@@ -155,7 +123,7 @@ var Exa = class {
|
|
|
155
123
|
* Finds similar links to the provided URL and returns the contents of the documents.
|
|
156
124
|
* @param {string} url - The URL for which to find similar links.
|
|
157
125
|
* @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.
|
|
158
|
-
* @returns {Promise<SearchResponse<T>>} A list of similar search results.
|
|
126
|
+
* @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.
|
|
159
127
|
*/
|
|
160
128
|
async findSimilarAndContents(url, options) {
|
|
161
129
|
const { contentsOptions, restOptions } = options === void 0 ? { contentsOptions: { text: true }, restOptions: {} } : this.extractContentsOptions(options);
|
|
@@ -166,13 +134,13 @@ var Exa = class {
|
|
|
166
134
|
});
|
|
167
135
|
}
|
|
168
136
|
/**
|
|
169
|
-
* Retrieves contents of documents based on URLs
|
|
170
|
-
* @param {string | string[] | SearchResult[]} urls -
|
|
137
|
+
* Retrieves contents of documents based on URLs.
|
|
138
|
+
* @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.
|
|
171
139
|
* @param {ContentsOptions} [options] - Additional options for retrieving document contents.
|
|
172
|
-
* @returns {Promise<SearchResponse<T>>} A list of document contents.
|
|
140
|
+
* @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.
|
|
173
141
|
*/
|
|
174
142
|
async getContents(urls, options) {
|
|
175
|
-
if (urls.length === 0) {
|
|
143
|
+
if (!urls || Array.isArray(urls) && urls.length === 0) {
|
|
176
144
|
throw new Error("Must provide at least one URL");
|
|
177
145
|
}
|
|
178
146
|
let requestUrls;
|
|
@@ -187,23 +155,134 @@ var Exa = class {
|
|
|
187
155
|
urls: requestUrls,
|
|
188
156
|
...options
|
|
189
157
|
};
|
|
190
|
-
return await this.request(
|
|
158
|
+
return await this.request("/contents", "POST", payload);
|
|
191
159
|
}
|
|
192
160
|
/**
|
|
193
|
-
*
|
|
161
|
+
* Generate an answer to a query.
|
|
194
162
|
* @param {string} query - The question or query to answer.
|
|
195
163
|
* @param {AnswerOptions} [options] - Additional options for answer generation.
|
|
196
|
-
* @
|
|
197
|
-
*
|
|
164
|
+
* @returns {Promise<AnswerResponse>} The generated answer and source references.
|
|
165
|
+
*
|
|
166
|
+
* Note: For streaming responses, use the `streamAnswer` method:
|
|
167
|
+
* ```ts
|
|
168
|
+
* for await (const chunk of exa.streamAnswer(query)) {
|
|
169
|
+
* // Handle chunks
|
|
170
|
+
* }
|
|
171
|
+
* ```
|
|
198
172
|
*/
|
|
199
|
-
async answer(query, options
|
|
173
|
+
async answer(query, options) {
|
|
174
|
+
if (options?.stream) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"For streaming responses, please use streamAnswer() instead:\n\nfor await (const chunk of exa.streamAnswer(query)) {\n // Handle chunks\n}"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
200
179
|
const requestBody = {
|
|
201
180
|
query,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
includeText: options?.includeText ?? false
|
|
181
|
+
stream: false,
|
|
182
|
+
text: options?.text ?? false
|
|
205
183
|
};
|
|
206
|
-
return await this.request("/answer", "POST", requestBody
|
|
184
|
+
return await this.request("/answer", "POST", requestBody);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Stream an answer as an async generator
|
|
188
|
+
*
|
|
189
|
+
* Each iteration yields a chunk with partial text (`content`) or new citations.
|
|
190
|
+
* Use this if you'd like to read the answer incrementally, e.g. in a chat UI.
|
|
191
|
+
*
|
|
192
|
+
* Example usage:
|
|
193
|
+
* ```ts
|
|
194
|
+
* for await (const chunk of exa.streamAnswer("What is quantum computing?", { text: false })) {
|
|
195
|
+
* if (chunk.content) process.stdout.write(chunk.content);
|
|
196
|
+
* if (chunk.citations) {
|
|
197
|
+
* console.log("\nCitations: ", chunk.citations);
|
|
198
|
+
* }
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
async *streamAnswer(query, options) {
|
|
203
|
+
const body = {
|
|
204
|
+
query,
|
|
205
|
+
text: options?.text ?? false,
|
|
206
|
+
stream: true
|
|
207
|
+
};
|
|
208
|
+
const response = await fetchImpl(this.baseURL + "/answer", {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: this.headers,
|
|
211
|
+
body: JSON.stringify(body)
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
const message = await response.text();
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Request failed with status ${response.status}. ${message}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const reader = response.body?.getReader();
|
|
220
|
+
if (!reader) {
|
|
221
|
+
throw new Error("No response body available for streaming.");
|
|
222
|
+
}
|
|
223
|
+
const decoder = new TextDecoder();
|
|
224
|
+
let buffer = "";
|
|
225
|
+
try {
|
|
226
|
+
while (true) {
|
|
227
|
+
const { done, value } = await reader.read();
|
|
228
|
+
if (done)
|
|
229
|
+
break;
|
|
230
|
+
buffer += decoder.decode(value, { stream: true });
|
|
231
|
+
const lines = buffer.split("\n");
|
|
232
|
+
buffer = lines.pop() || "";
|
|
233
|
+
for (const line of lines) {
|
|
234
|
+
if (!line.startsWith("data: "))
|
|
235
|
+
continue;
|
|
236
|
+
const jsonStr = line.replace(/^data:\s*/, "").trim();
|
|
237
|
+
if (!jsonStr || jsonStr === "[DONE]") {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
let chunkData;
|
|
241
|
+
try {
|
|
242
|
+
chunkData = JSON.parse(jsonStr);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const chunk = this.processChunk(chunkData);
|
|
247
|
+
if (chunk.content || chunk.citations) {
|
|
248
|
+
yield chunk;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (buffer.startsWith("data: ")) {
|
|
253
|
+
const leftover = buffer.replace(/^data:\s*/, "").trim();
|
|
254
|
+
if (leftover && leftover !== "[DONE]") {
|
|
255
|
+
try {
|
|
256
|
+
const chunkData = JSON.parse(leftover);
|
|
257
|
+
const chunk = this.processChunk(chunkData);
|
|
258
|
+
if (chunk.content || chunk.citations) {
|
|
259
|
+
yield chunk;
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} finally {
|
|
266
|
+
reader.releaseLock();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
processChunk(chunkData) {
|
|
270
|
+
let content;
|
|
271
|
+
let citations;
|
|
272
|
+
if (chunkData.choices && chunkData.choices[0] && chunkData.choices[0].delta) {
|
|
273
|
+
content = chunkData.choices[0].delta.content;
|
|
274
|
+
}
|
|
275
|
+
if (chunkData.citations && chunkData.citations !== "null") {
|
|
276
|
+
citations = chunkData.citations.map((c) => ({
|
|
277
|
+
id: c.id,
|
|
278
|
+
url: c.url,
|
|
279
|
+
title: c.title,
|
|
280
|
+
publishedDate: c.publishedDate,
|
|
281
|
+
author: c.author,
|
|
282
|
+
text: c.text
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
return { content, citations };
|
|
207
286
|
}
|
|
208
287
|
};
|
|
209
288
|
var src_default = Exa;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fetch, { Headers } from \"cross-fetch\";\n\n// Use native fetch in Node.js environments\nconst fetchImpl = typeof global !== \"undefined\" && global.fetch ? global.fetch : fetch;\nconst HeadersImpl = typeof global !== \"undefined\" && global.Headers ? global.Headers : Headers;\n\nconst isBeta = false;\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} SearchOptions\n * @property {number} [numResults] - Number of search results to return. Default 10. Max 10 for basic plans.\n * @property {string[]} [includeDomains] - List of domains to include in the search.\n * @property {string[]} [excludeDomains] - List of domains to exclude in the search.\n * @property {string} [startCrawlDate] - Start date for results based on crawl date.\n * @property {string} [endCrawlDate] - End date for results based on crawl date.\n * @property {string} [startPublishedDate] - Start date for results based on published date.\n * @property {string} [endPublishedDate] - End date for results based on published date.\n * @property {string} [category] - A data category to focus on, with higher comprehensivity and data cleanliness. Currently, the only category is company.\n * @property {string[]} [includeText] - List of strings that must be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [excludeText] - List of strings that must not be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [flags] - Experimental flags\n */\nexport type BaseSearchOptions = {\n numResults?: number;\n includeDomains?: string[];\n excludeDomains?: string[];\n startCrawlDate?: string;\n endCrawlDate?: string;\n startPublishedDate?: string;\n endPublishedDate?: string;\n category?: \"company\" | \"research paper\" | \"news\" | \"pdf\" | \"github\" | \"tweet\" | \"personal site\" | \"linkedin profile\" | \"financial report\";\n includeText?: string[];\n excludeText?: string[];\n flags?: string[];\n};\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} RegularSearchOptions\n */\nexport type RegularSearchOptions = BaseSearchOptions & {\n useAutoprompt?: boolean;\n type?: \"keyword\" | \"neural\" | \"auto\";\n};\n\n/**\n * Options for finding similar links.\n * @typedef {Object} FindSimilarOptions\n * @property {boolean} [excludeSourceDomain] - If true, excludes links from the base domain of the input.\n */\nexport type FindSimilarOptions = BaseSearchOptions & {\n excludeSourceDomain?: boolean;\n};\n\nexport type ExtrasOptions = { links?: number; imageLinks?: number };\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} ContentsOptions\n * @property {TextContentsOptions | boolean} [text] - Options for retrieving text contents.\n * @property {HighlightsContentsOptions | boolean} [highlights] - Options for retrieving highlights.\n * @property {SummaryContentsOptions | boolean} [summary] - Options for retrieving summary.\n * @property {LivecrawlOptions} [livecrawl] - Options for livecrawling contents. Default is \"never\" for neural/auto search, \"fallback\" for keyword search.\n * @property {number} [livecrawlTimeout] - The timeout for livecrawling. Max and default is 10000ms.\n * @property {boolean} [filterEmptyResults] - If true, filters out results with no contents. Default is true.\n * @property {number} [subpages] - The number of subpages to return for each result, where each subpage is derived from an internal link for the result.\n * @property {string | string[]} [subpageTarget] - Text used to match/rank subpages in the returned subpage list. You could use \"about\" to get *about* page for websites. Note that this is a fuzzy matcher.\n * @property {ExtrasOptions} [extras] - Miscelleneous data for derived from results\n */\nexport type ContentsOptions = {\n text?: TextContentsOptions | true;\n highlights?: HighlightsContentsOptions | true;\n summary?: SummaryContentsOptions | true;\n livecrawl?: LivecrawlOptions;\n livecrawlTimeout?: number;\n filterEmptyResults?: boolean;\n subpages?: number;\n subpageTarget?: string | string[];\n extras?: ExtrasOptions;\n} & (typeof isBeta extends true ? {} : {});\n\n/**\n * Options for livecrawling contents\n * @typedef {string} LivecrawlOptions\n */\nexport type LivecrawlOptions = \"never\" | \"fallback\" | \"always\" | \"auto\";\n\n/**\n * Options for retrieving text from page.\n * @typedef {Object} TextContentsOptions\n * @property {number} [maxCharacters] - The maximum number of characters to return.\n * @property {boolean} [includeHtmlTags] - If true, includes HTML tags in the returned text. Default: false\n */\nexport type TextContentsOptions = {\n maxCharacters?: number;\n includeHtmlTags?: boolean;\n};\n\n/**\n * Options for retrieving highlights from page.\n * @typedef {Object} HighlightsContentsOptions\n * @property {string} [query] - The query string to use for highlights search.\n * @property {number} [numSentences] - The number of sentences to return for each highlight.\n * @property {number} [highlightsPerUrl] - The number of highlights to return for each URL.\n */\nexport type HighlightsContentsOptions = {\n query?: string;\n numSentences?: number;\n highlightsPerUrl?: number;\n};\n\n/**\n * Options for retrieving summary from page.\n * @typedef {Object} SummaryContentsOptions\n * @property {string} [query] - The query string to use for summary generation.\n */\nexport type SummaryContentsOptions = {\n query?: string;\n};\n\n/**\n * @typedef {Object} TextResponse\n * @property {string} text - Text from page\n */\nexport type TextResponse = { text: string };\n\n/**\n * @typedef {Object} HighlightsResponse\n * @property {string[]} highlights - The highlights as an array of strings.\n * @property {number[]} highlightScores - The corresponding scores as an array of floats, 0 to 1\n */\nexport type HighlightsResponse = {\n highlights: string[];\n highlightScores: number[];\n};\n\n/**\n * @typedef {Object} SummaryResponse\n * @property {string} summary - The generated summary of the page content.\n */\nexport type SummaryResponse = { summary: string };\n\n/**\n * @typedef {Object} ExtrasResponse\n * @property {string[]} links - The links on the page of a result\n * @property {string[]} imageLinks - The image links on the page of a result\n */\nexport type ExtrasResponse = { extras: { links?: string[]; imageLinks?: string[] } };\n\n/**\n * @typedef {Object} SubpagesResponse\n * @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The links on the page of a result\n */\nexport type SubpagesResponse<T extends ContentsOptions> = {\n subpages: ContentsResultComponent<T>[];\n};\n\nexport type Default<T extends {}, U> = [keyof T] extends [never] ? U : T;\n\n/**\n * @typedef {Object} ContentsResultComponent\n * Depending on 'ContentsOptions', this yields a combination of 'TextResponse', 'HighlightsResponse', 'SummaryResponse', or an empty object.\n *\n * @template T - A type extending from 'ContentsOptions'.\n */\nexport type ContentsResultComponent<T extends ContentsOptions> = Default<\n (T[\"text\"] extends object | true ? TextResponse : {}) &\n (T[\"highlights\"] extends object | true ? HighlightsResponse : {}) &\n (T[\"summary\"] extends object | true ? SummaryResponse : {}) &\n (T[\"subpages\"] extends number ? SubpagesResponse<T> : {}) &\n (T[\"extras\"] extends object ? ExtrasResponse : {}),\n TextResponse\n>;\n\n/**\n * Represents a search result object.\n * @typedef {Object} SearchResult\n * @property {string} title - The title of the search result.\n * @property {string} url - The URL of the search result.\n * @property {string} [publishedDate] - The estimated creation date of the content.\n * @property {string} [author] - The author of the content, if available.\n * @property {number} [score] - Similarity score between the query/url and the result.\n * @property {string} id - The temporary ID for the document.\n */\nexport type SearchResult<T extends ContentsOptions> = {\n title: string | null;\n url: string;\n publishedDate?: string;\n author?: string;\n score?: number;\n id: string;\n image?: string;\n favicon?: string;\n} & ContentsResultComponent<T>;\n\n/**\n * Represents a search response object.\n * @typedef {Object} SearchResponse\n * @property {Result[]} results - The list of search results.\n * @property {string} [autopromptString] - The autoprompt string, if applicable.\n * @property {string} [autoDate] - The autoprompt date, if applicable.\n * @property {string} requestId - The request ID for the search.\n */\nexport type SearchResponse<T extends ContentsOptions> = {\n results: SearchResult<T>[];\n autopromptString?: string;\n autoDate?: string;\n requestId: string;\n};\n\n/**\n * Options for the answer endpoint\n * @typedef {Object} AnswerOptions\n * @property {number} [expandedQueriesLimit] - Maximum number of query variations (0-4). Default 1.\n * @property {boolean} [stream] - Whether to stream the response. Default false.\n * @property {boolean} [includeText] - Whether to include text in the source results. Default false.\n */\nexport type AnswerOptions = {\n expandedQueriesLimit?: number;\n stream?: boolean;\n includeText?: boolean;\n};\n\n/**\n * Represents an answer response object from the /answer endpoint.\n * @typedef {Object} AnswerResponse\n * @property {string} answer - The generated answer text.\n * @property {SearchResult<{}>[]]} sources - The sources used to generate the answer.\n * @property {string} [requestId] - Optional request ID for the answer.\n */\nexport type AnswerResponse = {\n answer: string;\n sources: SearchResult<{}>[];\n requestId?: string;\n};\n\n/**\n * Represents a streaming answer response chunk from the /answer endpoint.\n * @typedef {Object} AnswerStreamResponse\n * @property {string} [answer] - A chunk of the generated answer text.\n * @property {SearchResult<{}>[]]} [sources] - The sources used to generate the answer.\n * @property {string} [error] - Error message if something went wrong.\n */\nexport type AnswerStreamResponse = {\n answer?: string;\n sources?: SearchResult<{}>[];\n error?: string;\n};\n\n/**\n * The Exa class encapsulates the API's endpoints.\n */\nclass Exa {\n private baseURL: string;\n private headers: Headers;\n\n private extractContentsOptions<T extends ContentsOptions>(options: T): {\n contentsOptions: ContentsOptions;\n restOptions: Omit<T, keyof ContentsOptions>;\n } {\n const {\n text,\n highlights,\n summary,\n subpages,\n subpageTarget,\n extras,\n livecrawl,\n livecrawlTimeout,\n ...rest\n } = options;\n\n const contentsOptions: ContentsOptions = {};\n // don't send text if it's explicitly false\n if (\n text === undefined &&\n summary === undefined &&\n highlights === undefined &&\n extras === undefined\n )\n contentsOptions.text = true;\n if (text !== undefined) contentsOptions.text = text;\n\n if (summary !== undefined) contentsOptions.summary = summary;\n if (highlights !== undefined) contentsOptions.highlights = highlights;\n\n if (subpages !== undefined) contentsOptions.subpages = subpages;\n if (subpageTarget !== undefined)\n contentsOptions.subpageTarget = subpageTarget;\n\n if (extras !== undefined) contentsOptions.extras = extras;\n if (livecrawl !== undefined) contentsOptions.livecrawl = livecrawl;\n if (livecrawlTimeout !== undefined)\n contentsOptions.livecrawlTimeout = livecrawlTimeout;\n\n return {\n contentsOptions: contentsOptions,\n restOptions: rest as Omit<T, keyof ContentsOptions>,\n };\n }\n\n /**\n * Constructs the Exa API client.\n * @param {string} apiKey - The API key for authentication.\n * @param {string} [baseURL] - The base URL of the Exa API.\n */\n constructor(apiKey?: string, baseURL: string = \"https://api.exa.ai\") {\n this.baseURL = baseURL;\n if (!apiKey) {\n apiKey = process.env.EXASEARCH_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"API key must be provided as an argument or as an environment variable (EXASEARCH_API_KEY)\",\n );\n }\n }\n this.headers = new HeadersImpl({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.4.0\",\n });\n }\n\n /**\n * Makes a request to the Exa API.\n * @param {string} endpoint - The API endpoint to call.\n * @param {string} method - The HTTP method to use.\n * @param {any} [body] - The request body for POST requests.\n * @param {boolean} [stream] - Whether to stream the response.\n * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.\n * @returns {Promise<any>} The response from the API.\n */\n private async request(\n endpoint: string,\n method: string,\n body?: any,\n stream?: boolean,\n onChunk?: (chunk: AnswerStreamResponse) => void,\n ): Promise<any> {\n const response = await fetchImpl(this.baseURL + endpoint, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const message = (await response.json()).error;\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n /**\n * Handle streaming responses from the API. This processes Server-Sent Events (SSE)\n * where data is sent in chunks with the format \"data: {...}\". Each chunk is decoded,\n * parsed as JSON, and passed to the provided callback function.\n */\n if (stream && response.body) {\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const data = JSON.parse(line.slice(6));\n onChunk?.(data);\n } catch (e) {\n }\n }\n }\n }\n\n if (buffer && buffer.startsWith('data: ')) {\n try {\n const data = JSON.parse(buffer.slice(6));\n onChunk?.(data);\n } catch (e) {\n }\n }\n } catch (error: any) {\n throw new Error(`Streaming error: ${error?.message || 'Unknown error'}`);\n } finally {\n reader.releaseLock();\n }\n\n return null;\n }\n return await response.json();\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query.\n * @param {string} query - The query string.\n * @param {RegularSearchOptions} [options] - Additional search options.\n * @returns {Promise<SearchResponse<{}>>} A list of relevant search results.\n */\n async search(\n query: string,\n options?: RegularSearchOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/search\", \"POST\", { query, ...options });\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query and returns the contents of the documents.\n * @param {string} query - The query string.\n * @param {RegularSearchOptions & T} [options] - Additional search + contents options.\n * @returns {Promise<SearchResponse<T>>} A list of relevant search results.\n */\n async searchAndContents<T extends ContentsOptions>(\n query: string,\n options?: RegularSearchOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/search\", \"POST\", {\n query,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Finds similar links to the provided URL.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions} [options] - Additional options for finding similar links.\n * @returns {Promise<SearchResponse<{}>>} A list of similar search results.\n */\n async findSimilar(\n url: string,\n options?: FindSimilarOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/findSimilar\", \"POST\", { url, ...options });\n }\n\n /**\n * Finds similar links to the provided URL and returns the contents of the documents.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.\n * @returns {Promise<SearchResponse<T>>} A list of similar search results.\n */\n async findSimilarAndContents<T extends ContentsOptions>(\n url: string,\n options?: FindSimilarOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/findSimilar\", \"POST\", {\n url,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Retrieves contents of documents based on URLs\n * @param {string | string[] | SearchResult[]} urls - An array of URLs.\n * @param {ContentsOptions} [options] - Additional options for retrieving document contents.\n * @returns {Promise<SearchResponse<T>>} A list of document contents.\n */\n async getContents<T extends ContentsOptions>(\n urls: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (urls.length === 0) {\n throw new Error(\"Must provide at least one URL\");\n }\n let requestUrls: string[];\n if (typeof urls === \"string\") {\n requestUrls = [urls];\n } else if (typeof urls[0] === \"string\") {\n requestUrls = urls as string[];\n } else {\n requestUrls = (urls as SearchResult<T>[]).map((result) => result.url);\n }\n const payload = {\n urls: requestUrls,\n ...options,\n };\n\n return await this.request(`/contents`, \"POST\", payload);\n }\n\n /**\n * Generates an answer to a query using search results as context.\n * @param {string} query - The question or query to answer.\n * @param {AnswerOptions} [options] - Additional options for answer generation.\n * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.\n * @returns {Promise<AnswerResponse | null>} The generated answer and source references, or null if streaming.\n */\n async answer(\n query: string,\n options?: AnswerOptions,\n onChunk?: (chunk: AnswerStreamResponse) => void,\n ): Promise<AnswerResponse | null> {\n const requestBody = {\n query,\n expandedQueriesLimit: options?.expandedQueriesLimit ?? 1,\n stream: options?.stream ?? false,\n includeText: options?.includeText ?? false\n };\n return await this.request(\"/answer\", \"POST\", requestBody, options?.stream, onChunk);\n }\n}\n\nexport default Exa;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fetch, { Headers } from \"cross-fetch\";\n\n// Use native fetch in Node.js environments\nconst fetchImpl = typeof global !== \"undefined\" && global.fetch ? global.fetch : fetch;\nconst HeadersImpl = typeof global !== \"undefined\" && global.Headers ? global.Headers : Headers;\n\nconst isBeta = false;\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} SearchOptions\n * @property {number} [numResults] - Number of search results to return. Default 10. Max 10 for basic plans.\n * @property {string[]} [includeDomains] - List of domains to include in the search.\n * @property {string[]} [excludeDomains] - List of domains to exclude in the search.\n * @property {string} [startCrawlDate] - Start date for results based on crawl date.\n * @property {string} [endCrawlDate] - End date for results based on crawl date.\n * @property {string} [startPublishedDate] - Start date for results based on published date.\n * @property {string} [endPublishedDate] - End date for results based on published date.\n * @property {string} [category] - A data category to focus on, with higher comprehensivity and data cleanliness. Currently, the only category is company.\n * @property {string[]} [includeText] - List of strings that must be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [excludeText] - List of strings that must not be present in webpage text of results. Currently only supports 1 string of up to 5 words.\n * @property {string[]} [flags] - Experimental flags\n */\nexport type BaseSearchOptions = {\n numResults?: number;\n includeDomains?: string[];\n excludeDomains?: string[];\n startCrawlDate?: string;\n endCrawlDate?: string;\n startPublishedDate?: string;\n endPublishedDate?: string;\n category?:\n | \"company\"\n | \"research paper\"\n | \"news\"\n | \"pdf\"\n | \"github\"\n | \"tweet\"\n | \"personal site\"\n | \"linkedin profile\"\n | \"financial report\";\n includeText?: string[];\n excludeText?: string[];\n flags?: string[];\n};\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} RegularSearchOptions\n */\nexport type RegularSearchOptions = BaseSearchOptions & {\n /**\n * If true, the search results are moderated for safety.\n */\n moderation?: boolean;\n\n useAutoprompt?: boolean;\n type?: \"keyword\" | \"neural\" | \"auto\";\n};\n\n/**\n * Options for finding similar links.\n * @typedef {Object} FindSimilarOptions\n * @property {boolean} [excludeSourceDomain] - If true, excludes links from the base domain of the input.\n */\nexport type FindSimilarOptions = BaseSearchOptions & {\n excludeSourceDomain?: boolean;\n};\n\nexport type ExtrasOptions = { links?: number; imageLinks?: number };\n\n/**\n * Search options for performing a search query.\n * @typedef {Object} ContentsOptions\n * @property {TextContentsOptions | boolean} [text] - Options for retrieving text contents.\n * @property {HighlightsContentsOptions | boolean} [highlights] - Options for retrieving highlights.\n * @property {SummaryContentsOptions | boolean} [summary] - Options for retrieving summary.\n * @property {LivecrawlOptions} [livecrawl] - Options for livecrawling contents. Default is \"never\" for neural/auto search, \"fallback\" for keyword search.\n * @property {number} [livecrawlTimeout] - The timeout for livecrawling. Max and default is 10000ms.\n * @property {boolean} [filterEmptyResults] - If true, filters out results with no contents. Default is true.\n * @property {number} [subpages] - The number of subpages to return for each result, where each subpage is derived from an internal link for the result.\n * @property {string | string[]} [subpageTarget] - Text used to match/rank subpages in the returned subpage list. You could use \"about\" to get *about* page for websites. Note that this is a fuzzy matcher.\n * @property {ExtrasOptions} [extras] - Miscelleneous data derived from results\n */\nexport type ContentsOptions = {\n text?: TextContentsOptions | true;\n highlights?: HighlightsContentsOptions | true;\n summary?: SummaryContentsOptions | true;\n livecrawl?: LivecrawlOptions;\n livecrawlTimeout?: number;\n filterEmptyResults?: boolean;\n subpages?: number;\n subpageTarget?: string | string[];\n extras?: ExtrasOptions;\n} & (typeof isBeta extends true ? {} : {});\n\n/**\n * Options for livecrawling contents\n * @typedef {string} LivecrawlOptions\n */\nexport type LivecrawlOptions = \"never\" | \"fallback\" | \"always\" | \"auto\";\n\n/**\n * Options for retrieving text from page.\n * @typedef {Object} TextContentsOptions\n * @property {number} [maxCharacters] - The maximum number of characters to return.\n * @property {boolean} [includeHtmlTags] - If true, includes HTML tags in the returned text. Default: false\n */\nexport type TextContentsOptions = {\n maxCharacters?: number;\n includeHtmlTags?: boolean;\n};\n\n/**\n * Options for retrieving highlights from page.\n * @typedef {Object} HighlightsContentsOptions\n * @property {string} [query] - The query string to use for highlights search.\n * @property {number} [numSentences] - The number of sentences to return for each highlight.\n * @property {number} [highlightsPerUrl] - The number of highlights to return for each URL.\n */\nexport type HighlightsContentsOptions = {\n query?: string;\n numSentences?: number;\n highlightsPerUrl?: number;\n};\n\n/**\n * Options for retrieving summary from page.\n * @typedef {Object} SummaryContentsOptions\n * @property {string} [query] - The query string to use for summary generation.\n */\nexport type SummaryContentsOptions = {\n query?: string;\n};\n\n/**\n * @typedef {Object} TextResponse\n * @property {string} text - Text from page\n */\nexport type TextResponse = { text: string };\n\n/**\n * @typedef {Object} HighlightsResponse\n * @property {string[]} highlights - The highlights as an array of strings.\n * @property {number[]} highlightScores - The corresponding scores as an array of floats, 0 to 1\n */\nexport type HighlightsResponse = {\n highlights: string[];\n highlightScores: number[];\n};\n\n/**\n * @typedef {Object} SummaryResponse\n * @property {string} summary - The generated summary of the page content.\n */\nexport type SummaryResponse = { summary: string };\n\n/**\n * @typedef {Object} ExtrasResponse\n * @property {string[]} links - The links on the page of a result\n * @property {string[]} imageLinks - The image links on the page of a result\n */\nexport type ExtrasResponse = { extras: { links?: string[]; imageLinks?: string[] } };\n\n/**\n * @typedef {Object} SubpagesResponse\n * @property {ContentsResultComponent<T extends ContentsOptions>} subpages - The subpages for a result\n */\nexport type SubpagesResponse<T extends ContentsOptions> = {\n subpages: ContentsResultComponent<T>[];\n};\n\nexport type Default<T extends {}, U> = [keyof T] extends [never] ? U : T;\n\n/**\n * @typedef {Object} ContentsResultComponent\n * Depending on 'ContentsOptions', this yields a combination of 'TextResponse', 'HighlightsResponse', 'SummaryResponse', or an empty object.\n *\n * @template T - A type extending from 'ContentsOptions'.\n */\nexport type ContentsResultComponent<T extends ContentsOptions> = Default<\n (T[\"text\"] extends object | true ? TextResponse : {}) &\n (T[\"highlights\"] extends object | true ? HighlightsResponse : {}) &\n (T[\"summary\"] extends object | true ? SummaryResponse : {}) &\n (T[\"subpages\"] extends number ? SubpagesResponse<T> : {}) &\n (T[\"extras\"] extends object ? ExtrasResponse : {}),\n TextResponse\n>;\n\n/**\n * Represents a search result object.\n * @typedef {Object} SearchResult\n * @property {string} title - The title of the search result.\n * @property {string} url - The URL of the search result.\n * @property {string} [publishedDate] - The estimated creation date of the content.\n * @property {string} [author] - The author of the content, if available.\n * @property {number} [score] - Similarity score between the query/url and the result.\n * @property {string} id - The temporary ID for the document.\n * @property {string} [image] - A representative image for the content, if any.\n * @property {string} [favicon] - A favicon for the site, if any.\n */\nexport type SearchResult<T extends ContentsOptions> = {\n title: string | null;\n url: string;\n publishedDate?: string;\n author?: string;\n score?: number;\n id: string;\n image?: string;\n favicon?: string;\n} & ContentsResultComponent<T>;\n\n/**\n * Represents a search response object.\n * @typedef {Object} SearchResponse\n * @property {Result[]} results - The list of search results.\n * @property {string} [autopromptString] - The autoprompt string, if applicable.\n * @property {string} [autoDate] - The autoprompt date, if applicable.\n * @property {string} requestId - The request ID for the search.\n */\nexport type SearchResponse<T extends ContentsOptions> = {\n results: SearchResult<T>[];\n autopromptString?: string;\n autoDate?: string;\n requestId: string;\n};\n\n/**\n * Options for the answer endpoint\n * @typedef {Object} AnswerOptions\n * @property {boolean} [stream] - Whether to stream the response. Default false.\n * @property {boolean} [text] - Whether to include text in the source results. Default false.\n */\nexport type AnswerOptions = {\n stream?: boolean;\n text?: boolean;\n};\n\n/**\n * Represents an answer response object from the /answer endpoint.\n * @typedef {Object} AnswerResponse\n * @property {string} answer - The generated answer text.\n * @property {SearchResult<{}>[]} citations - The sources used to generate the answer.\n * @property {string} [requestId] - Optional request ID for the answer.\n */\nexport type AnswerResponse = {\n answer: string;\n citations: SearchResult<{}>[];\n requestId?: string;\n};\n\nexport type AnswerStreamChunk = {\n /**\n * The partial text content of the answer (if present in this chunk).\n */\n content?: string;\n /**\n * Citations associated with the current chunk of text (if present).\n */\n citations?: Array<{\n id: string;\n url: string;\n title?: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n }>;\n};\n\n/**\n * Represents a streaming answer response chunk from the /answer endpoint.\n * @typedef {Object} AnswerStreamResponse\n * @property {string} [answer] - A chunk of the generated answer text.\n * @property {SearchResult<{}>[]]} [citations] - The sources used to generate the answer.\n */\nexport type AnswerStreamResponse = {\n answer?: string;\n citations?: SearchResult<{}>[];\n};\n\n/**\n * The Exa class encapsulates the API's endpoints.\n */\nclass Exa {\n private baseURL: string;\n private headers: Headers;\n\n /**\n * Helper method to separate out the contents-specific options from the rest.\n */\n private extractContentsOptions<T extends ContentsOptions>(options: T): {\n contentsOptions: ContentsOptions;\n restOptions: Omit<T, keyof ContentsOptions>;\n } {\n const {\n text,\n highlights,\n summary,\n subpages,\n subpageTarget,\n extras,\n livecrawl,\n livecrawlTimeout,\n ...rest\n } = options;\n\n const contentsOptions: ContentsOptions = {};\n\n // Default: if none of text, summary, or highlights is provided, we retrieve text\n if (\n text === undefined &&\n summary === undefined &&\n highlights === undefined &&\n extras === undefined\n ) {\n contentsOptions.text = true;\n }\n\n if (text !== undefined) contentsOptions.text = text;\n if (summary !== undefined) contentsOptions.summary = summary;\n if (highlights !== undefined) contentsOptions.highlights = highlights;\n if (subpages !== undefined) contentsOptions.subpages = subpages;\n if (subpageTarget !== undefined) contentsOptions.subpageTarget = subpageTarget;\n if (extras !== undefined) contentsOptions.extras = extras;\n if (livecrawl !== undefined) contentsOptions.livecrawl = livecrawl;\n if (livecrawlTimeout !== undefined) contentsOptions.livecrawlTimeout = livecrawlTimeout;\n\n return {\n contentsOptions,\n restOptions: rest as Omit<T, keyof ContentsOptions>,\n };\n }\n\n /**\n * Constructs the Exa API client.\n * @param {string} apiKey - The API key for authentication.\n * @param {string} [baseURL] - The base URL of the Exa API.\n */\n constructor(apiKey?: string, baseURL: string = \"https://api.exa.ai\") {\n this.baseURL = baseURL;\n if (!apiKey) {\n apiKey = process.env.EXASEARCH_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"API key must be provided as an argument or as an environment variable (EXASEARCH_API_KEY)\",\n );\n }\n }\n this.headers = new HeadersImpl({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.4.0\",\n });\n }\n\n /**\n * Makes a request to the Exa API.\n * @param {string} endpoint - The API endpoint to call.\n * @param {string} method - The HTTP method to use.\n * @param {any} [body] - The request body for POST requests.\n * @returns {Promise<any>} The response from the API.\n */\n private async request(\n endpoint: string,\n method: string,\n body?: any,\n ): Promise<any> {\n const response = await fetchImpl(this.baseURL + endpoint, {\n method,\n headers: this.headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const message = (await response.json()).error;\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n return await response.json();\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query.\n * \n * @param {string} query - The query string.\n * @param {RegularSearchOptions} [options] - Additional search options\n * @returns {Promise<SearchResponse<{}>>} A list of relevant search results.\n */\n async search(\n query: string,\n options?: RegularSearchOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/search\", \"POST\", { query, ...options });\n }\n\n /**\n * Performs a search with an Exa prompt-engineered query and returns the contents of the documents.\n * \n * @param {string} query - The query string.\n * @param {RegularSearchOptions & T} [options] - Additional search + contents options\n * @returns {Promise<SearchResponse<T>>} A list of relevant search results with requested contents.\n */\n async searchAndContents<T extends ContentsOptions>(\n query: string,\n options?: RegularSearchOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/search\", \"POST\", {\n query,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Finds similar links to the provided URL.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions} [options] - Additional options for finding similar links.\n * @returns {Promise<SearchResponse<{}>>} A list of similar search results.\n */\n async findSimilar(\n url: string,\n options?: FindSimilarOptions,\n ): Promise<SearchResponse<{}>> {\n return await this.request(\"/findSimilar\", \"POST\", { url, ...options });\n }\n\n /**\n * Finds similar links to the provided URL and returns the contents of the documents.\n * @param {string} url - The URL for which to find similar links.\n * @param {FindSimilarOptions & T} [options] - Additional options for finding similar links + contents.\n * @returns {Promise<SearchResponse<T>>} A list of similar search results, including requested contents.\n */\n async findSimilarAndContents<T extends ContentsOptions>(\n url: string,\n options?: FindSimilarOptions & T,\n ): Promise<SearchResponse<T>> {\n const { contentsOptions, restOptions } =\n options === undefined\n ? { contentsOptions: { text: true }, restOptions: {} }\n : this.extractContentsOptions(options);\n\n return await this.request(\"/findSimilar\", \"POST\", {\n url,\n contents: contentsOptions,\n ...restOptions,\n });\n }\n\n /**\n * Retrieves contents of documents based on URLs.\n * @param {string | string[] | SearchResult[]} urls - A URL or array of URLs, or an array of SearchResult objects.\n * @param {ContentsOptions} [options] - Additional options for retrieving document contents.\n * @returns {Promise<SearchResponse<T>>} A list of document contents for the requested URLs.\n */\n async getContents<T extends ContentsOptions>(\n urls: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (!urls || (Array.isArray(urls) && urls.length === 0)) {\n throw new Error(\"Must provide at least one URL\");\n }\n\n let requestUrls: string[];\n\n if (typeof urls === \"string\") {\n requestUrls = [urls];\n } else if (typeof urls[0] === \"string\") {\n requestUrls = urls as string[];\n } else {\n requestUrls = (urls as SearchResult<T>[]).map((result) => result.url);\n }\n\n const payload = {\n urls: requestUrls,\n ...options,\n };\n\n return await this.request(\"/contents\", \"POST\", payload);\n }\n\n /**\n * Generate an answer to a query.\n * @param {string} query - The question or query to answer.\n * @param {AnswerOptions} [options] - Additional options for answer generation.\n * @returns {Promise<AnswerResponse>} The generated answer and source references.\n * \n * Note: For streaming responses, use the `streamAnswer` method:\n * ```ts\n * for await (const chunk of exa.streamAnswer(query)) {\n * // Handle chunks\n * }\n * ```\n */\n async answer(\n query: string,\n options?: AnswerOptions,\n ): Promise<AnswerResponse> {\n if (options?.stream) {\n throw new Error(\n \"For streaming responses, please use streamAnswer() instead:\\n\\n\" +\n \"for await (const chunk of exa.streamAnswer(query)) {\\n\" +\n \" // Handle chunks\\n\" +\n \"}\"\n );\n }\n\n // For non-streaming requests, make a regular API call\n const requestBody = {\n query,\n stream: false,\n text: options?.text ?? false\n };\n\n return await this.request(\"/answer\", \"POST\", requestBody);\n }\n\n /**\n * Stream an answer as an async generator\n *\n * Each iteration yields a chunk with partial text (`content`) or new citations.\n * Use this if you'd like to read the answer incrementally, e.g. in a chat UI.\n *\n * Example usage:\n * ```ts\n * for await (const chunk of exa.streamAnswer(\"What is quantum computing?\", { text: false })) {\n * if (chunk.content) process.stdout.write(chunk.content);\n * if (chunk.citations) {\n * console.log(\"\\nCitations: \", chunk.citations);\n * }\n * }\n * ```\n */\n async *streamAnswer(\n query: string,\n options?: { text?: boolean }\n ): AsyncGenerator<AnswerStreamChunk> {\n // Build the POST body and fetch the streaming response.\n const body = {\n query,\n text: options?.text ?? false,\n stream: true,\n };\n\n const response = await fetchImpl(this.baseURL + \"/answer\", {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Request failed with status ${response.status}. ${message}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"No response body available for streaming.\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n\n const jsonStr = line.replace(/^data:\\s*/, \"\").trim();\n if (!jsonStr || jsonStr === \"[DONE]\") {\n continue;\n }\n\n let chunkData: any;\n try {\n chunkData = JSON.parse(jsonStr);\n } catch (err) {\n continue;\n }\n\n const chunk = this.processChunk(chunkData);\n if (chunk.content || chunk.citations) {\n yield chunk;\n }\n }\n }\n\n if (buffer.startsWith(\"data: \")) {\n const leftover = buffer.replace(/^data:\\s*/, \"\").trim();\n if (leftover && leftover !== \"[DONE]\") {\n try {\n const chunkData = JSON.parse(leftover);\n const chunk = this.processChunk(chunkData);\n if (chunk.content || chunk.citations) {\n yield chunk;\n }\n } catch (e) {\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n private processChunk(chunkData: any): AnswerStreamChunk {\n let content: string | undefined;\n let citations:\n | Array<{\n id: string;\n url: string;\n title?: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n }>\n | undefined;\n\n if (chunkData.choices && chunkData.choices[0] && chunkData.choices[0].delta) {\n content = chunkData.choices[0].delta.content;\n }\n\n if (chunkData.citations && chunkData.citations !== \"null\") {\n citations = chunkData.citations.map((c: any) => ({\n id: c.id,\n url: c.url,\n title: c.title,\n publishedDate: c.publishedDate,\n author: c.author,\n text: c.text,\n }));\n }\n\n return { content, citations };\n }\n}\n\nexport default Exa;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exa-js",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.9",
|
|
4
4
|
"description": "Exa SDK for Node.js and the browser",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -37,14 +37,16 @@
|
|
|
37
37
|
"@types/node": "^22.10.2",
|
|
38
38
|
"cross-env": "^7.0.3",
|
|
39
39
|
"prettier": "2.8.4",
|
|
40
|
+
"ts-node": "^10.9.2",
|
|
40
41
|
"tsup": "6.6.3",
|
|
41
42
|
"typedoc": "^0.25.4",
|
|
42
43
|
"typedoc-plugin-markdown": "^3.17.1",
|
|
43
|
-
"typescript": "4.9.5",
|
|
44
|
+
"typescript": "^4.9.5",
|
|
44
45
|
"vitest": "0.28.5"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"cross-fetch": "^4.0.0"
|
|
48
|
+
"cross-fetch": "^4.0.0",
|
|
49
|
+
"dotenv": "^16.4.7"
|
|
48
50
|
},
|
|
49
51
|
"directories": {
|
|
50
52
|
"test": "test"
|