exa-js 1.3.5 → 1.4.6

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 CHANGED
@@ -62,16 +62,33 @@ const similarExcludingSourceResults = await exa.findSimilar("https://example.com
62
62
  const similarWithContentsResults = await exa.findSimilarAndContents("https://example.com", { text: true, highlights: true });
63
63
 
64
64
  // Get text contents
65
- const textContentsResults = await exa.getContents(["ids"], { text: true });
65
+ const textContentsResults = await exa.getContents(["urls"], { text: true });
66
66
 
67
67
  // Get highlights
68
- const highlightsContentsResults = await exa.getContents(["ids"], { highlights: true });
68
+ const highlightsContentsResults = await exa.getContents(["urls"], { highlights: true });
69
69
 
70
70
  // Get contents with contents options
71
- const customContentsResults = await exa.getContents(["ids"], {
71
+ const customContentsResults = await exa.getContents(["urls"], {
72
72
  text: { includeHtmlTags: true, maxCharacters: 1000 },
73
73
  highlights: { highlightsPerUrl: 2, numSentences: 1, query: "This is the highlight query:" }
74
74
  });
75
+
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
+ });
81
+
82
+ // Get answer with source contents
83
+ const answerWithTextResults = await exa.answer("What is the population of New York City?", {
84
+ includeText: true,
85
+ expandedQueriesLimit: 2
86
+ });
87
+
88
+ // Stream answer response
89
+ const streamingResults = await exa.answer("What is the population of New York City?", {
90
+ stream: true
91
+ });
75
92
  ```
76
93
 
77
94
  ### `exa.search(query: string, options?: SearchOptions): Promise<SearchResponse>`
@@ -94,12 +111,39 @@ const response = await exa.findSimilar('https://waitbutwhy.com/2014/05/fermi-par
94
111
  });
95
112
  ```
96
113
 
97
- ### `exa.getContents(ids: string[] | Result[]): Promise<GetContentsResponse>`
114
+ ### `exa.getContents(urls: string[] | Result[]): Promise<GetContentsResponse>`
98
115
  Retrieves the contents of the specified documents.
99
116
 
100
117
  ```javascript
101
118
  const response = await exa.getContents(['8U71IlQ5DUTdsZFherhhYA', 'X3wd0PbJmAvhu_DQjDKA7A']);
102
119
  ```
103
120
 
121
+ ### `exa.answer(query: string, options?: AnswerOptions): Promise<AnswerResponse>`
122
+ Generates an answer to a query using search results as context.
123
+
124
+ ```javascript
125
+ const response = await exa.answer('What is the population of New York City?', {
126
+ expandedQueriesLimit: 2,
127
+ });
128
+ ```
129
+
130
+ ### Streaming Responses
131
+ The answer endpoint supports streaming responses, where the answer is returned in chunks as it's being generated. This is useful if you'd like to provide users with tokens as they are generated.
132
+
133
+ ```javascript
134
+ await exa.answer(
135
+ 'What are the latest developments in AI?',
136
+ {
137
+ expandedQueriesLimit: 2,
138
+ stream: true,
139
+ includeText: false
140
+ },
141
+ (chunk) => {
142
+ // Process each chunk as it arrives
143
+ console.log(chunk.answer);
144
+ }
145
+ );
146
+ ```
147
+
104
148
  # Contributing
105
149
  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
@@ -22,7 +22,7 @@ type BaseSearchOptions = {
22
22
  endCrawlDate?: string;
23
23
  startPublishedDate?: string;
24
24
  endPublishedDate?: string;
25
- category?: string;
25
+ category?: "company" | "research paper" | "news" | "pdf" | "github" | "tweet" | "personal site" | "linkedin profile" | "financial report";
26
26
  includeText?: string[];
27
27
  excludeText?: string[];
28
28
  flags?: string[];
@@ -33,7 +33,7 @@ type BaseSearchOptions = {
33
33
  */
34
34
  type RegularSearchOptions = BaseSearchOptions & {
35
35
  useAutoprompt?: boolean;
36
- type?: string;
36
+ type?: "keyword" | "neural" | "auto";
37
37
  };
38
38
  /**
39
39
  * Options for finding similar links.
@@ -189,6 +189,42 @@ type SearchResponse<T extends ContentsOptions> = {
189
189
  autoDate?: string;
190
190
  requestId: string;
191
191
  };
192
+ /**
193
+ * Options for the answer endpoint
194
+ * @typedef {Object} AnswerOptions
195
+ * @property {number} [expandedQueriesLimit] - Maximum number of query variations (0-4). Default 1.
196
+ * @property {boolean} [stream] - Whether to stream the response. Default false.
197
+ * @property {boolean} [includeText] - Whether to include text in the source results. Default false.
198
+ */
199
+ type AnswerOptions = {
200
+ expandedQueriesLimit?: number;
201
+ stream?: boolean;
202
+ includeText?: boolean;
203
+ };
204
+ /**
205
+ * Represents an answer response object from the /answer endpoint.
206
+ * @typedef {Object} AnswerResponse
207
+ * @property {string} answer - The generated answer text.
208
+ * @property {SearchResult<{}>[]]} sources - The sources used to generate the answer.
209
+ * @property {string} [requestId] - Optional request ID for the answer.
210
+ */
211
+ type AnswerResponse = {
212
+ answer: string;
213
+ sources: SearchResult<{}>[];
214
+ requestId?: string;
215
+ };
216
+ /**
217
+ * Represents a streaming answer response chunk from the /answer endpoint.
218
+ * @typedef {Object} AnswerStreamResponse
219
+ * @property {string} [answer] - A chunk of the generated answer text.
220
+ * @property {SearchResult<{}>[]]} [sources] - The sources used to generate the answer.
221
+ * @property {string} [error] - Error message if something went wrong.
222
+ */
223
+ type AnswerStreamResponse = {
224
+ answer?: string;
225
+ sources?: SearchResult<{}>[];
226
+ error?: string;
227
+ };
192
228
  /**
193
229
  * The Exa class encapsulates the API's endpoints.
194
230
  */
@@ -207,6 +243,8 @@ declare class Exa {
207
243
  * @param {string} endpoint - The API endpoint to call.
208
244
  * @param {string} method - The HTTP method to use.
209
245
  * @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.
210
248
  * @returns {Promise<any>} The response from the API.
211
249
  */
212
250
  private request;
@@ -239,12 +277,20 @@ declare class Exa {
239
277
  */
240
278
  findSimilarAndContents<T extends ContentsOptions>(url: string, options?: FindSimilarOptions & T): Promise<SearchResponse<T>>;
241
279
  /**
242
- * Retrieves contents of documents based on a list of document IDs.
243
- * @param {string | string[] | SearchResult[]} ids - An array of document IDs (or `SearchResult` objects).
280
+ * Retrieves contents of documents based on URLs
281
+ * @param {string | string[] | SearchResult[]} urls - An array of URLs.
244
282
  * @param {ContentsOptions} [options] - Additional options for retrieving document contents.
245
283
  * @returns {Promise<SearchResponse<T>>} A list of document contents.
246
284
  */
247
- getContents<T extends ContentsOptions>(ids: string | string[] | SearchResult<T>[], options?: T): Promise<SearchResponse<T>>;
285
+ getContents<T extends ContentsOptions>(urls: string | string[] | SearchResult<T>[], options?: T): Promise<SearchResponse<T>>;
286
+ /**
287
+ * Generates an answer to a query using search results as context.
288
+ * @param {string} query - The question or query to answer.
289
+ * @param {AnswerOptions} [options] - Additional options for answer generation.
290
+ * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
291
+ * @returns {Promise<AnswerResponse | null>} The generated answer and source references, or null if streaming.
292
+ */
293
+ answer(query: string, options?: AnswerOptions, onChunk?: (chunk: AnswerStreamResponse) => void): Promise<AnswerResponse | null>;
248
294
  }
249
295
 
250
- export { BaseSearchOptions, ContentsOptions, ContentsResultComponent, Default, ExtrasOptions, ExtrasResponse, FindSimilarOptions, HighlightsContentsOptions, HighlightsResponse, LivecrawlOptions, RegularSearchOptions, SearchResponse, SearchResult, SubpagesResponse, SummaryContentsOptions, SummaryResponse, TextContentsOptions, TextResponse, Exa as default };
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 };
package/dist/index.js CHANGED
@@ -34,6 +34,8 @@ __export(src_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(src_exports);
36
36
  var import_cross_fetch = __toESM(require("cross-fetch"));
37
+ var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : import_cross_fetch.default;
38
+ var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : import_cross_fetch.Headers;
37
39
  var Exa = class {
38
40
  extractContentsOptions(options) {
39
41
  const {
@@ -86,10 +88,10 @@ var Exa = class {
86
88
  );
87
89
  }
88
90
  }
89
- this.headers = new import_cross_fetch.Headers({
91
+ this.headers = new HeadersImpl({
90
92
  "x-api-key": apiKey,
91
93
  "Content-Type": "application/json",
92
- "User-Agent": "exa-node 1.3.2"
94
+ "User-Agent": "exa-node 1.4.0"
93
95
  });
94
96
  }
95
97
  /**
@@ -97,10 +99,12 @@ var Exa = class {
97
99
  * @param {string} endpoint - The API endpoint to call.
98
100
  * @param {string} method - The HTTP method to use.
99
101
  * @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.
100
104
  * @returns {Promise<any>} The response from the API.
101
105
  */
102
- async request(endpoint, method, body) {
103
- const response = await (0, import_cross_fetch.default)(this.baseURL + endpoint, {
106
+ async request(endpoint, method, body, stream, onChunk) {
107
+ const response = await fetchImpl(this.baseURL + endpoint, {
104
108
  method,
105
109
  headers: this.headers,
106
110
  body: body ? JSON.stringify(body) : void 0
@@ -111,6 +115,42 @@ var Exa = class {
111
115
  `Request failed with status ${response.status}. ${message}`
112
116
  );
113
117
  }
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
+ }
114
154
  return await response.json();
115
155
  }
116
156
  /**
@@ -160,27 +200,44 @@ var Exa = class {
160
200
  });
161
201
  }
162
202
  /**
163
- * Retrieves contents of documents based on a list of document IDs.
164
- * @param {string | string[] | SearchResult[]} ids - An array of document IDs (or `SearchResult` objects).
203
+ * Retrieves contents of documents based on URLs
204
+ * @param {string | string[] | SearchResult[]} urls - An array of URLs.
165
205
  * @param {ContentsOptions} [options] - Additional options for retrieving document contents.
166
206
  * @returns {Promise<SearchResponse<T>>} A list of document contents.
167
207
  */
168
- async getContents(ids, options) {
169
- if (ids.length === 0) {
170
- throw new Error("Must provide at least one ID");
208
+ async getContents(urls, options) {
209
+ if (urls.length === 0) {
210
+ throw new Error("Must provide at least one URL");
171
211
  }
172
- let requestIds;
173
- if (typeof ids === "string") {
174
- requestIds = [ids];
175
- } else if (typeof ids[0] === "string") {
176
- requestIds = ids;
212
+ let requestUrls;
213
+ if (typeof urls === "string") {
214
+ requestUrls = [urls];
215
+ } else if (typeof urls[0] === "string") {
216
+ requestUrls = urls;
177
217
  } else {
178
- requestIds = ids.map((result) => result.id);
218
+ requestUrls = urls.map((result) => result.url);
179
219
  }
180
- return await this.request(`/contents`, "POST", {
181
- ids: requestIds,
220
+ const payload = {
221
+ urls: requestUrls,
182
222
  ...options
183
- });
223
+ };
224
+ return await this.request(`/contents`, "POST", payload);
225
+ }
226
+ /**
227
+ * Generates an answer to a query using search results as context.
228
+ * @param {string} query - The question or query to answer.
229
+ * @param {AnswerOptions} [options] - Additional options for answer generation.
230
+ * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
231
+ * @returns {Promise<AnswerResponse | null>} The generated answer and source references, or null if streaming.
232
+ */
233
+ async answer(query, options, onChunk) {
234
+ const requestBody = {
235
+ query,
236
+ expandedQueriesLimit: options?.expandedQueriesLimit ?? 1,
237
+ stream: options?.stream ?? false,
238
+ includeText: options?.includeText ?? false
239
+ };
240
+ return await this.request("/answer", "POST", requestBody, options?.stream, onChunk);
184
241
  }
185
242
  };
186
243
  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\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?: string;\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?: string;\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 * 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 Headers({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.3.2\",\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 fetch(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 * @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 a list of document IDs.\n * @param {string | string[] | SearchResult[]} ids - An array of document IDs (or `SearchResult` objects).\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 ids: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (ids.length === 0) {\n throw new Error(\"Must provide at least one ID\");\n }\n let requestIds: string[];\n if (typeof ids === \"string\") {\n requestIds = [ids];\n } else if (typeof ids[0] === \"string\") {\n requestIds = ids as string[];\n } else {\n requestIds = (ids as SearchResult<T>[]).map((result) => result.id);\n }\n return await this.request(`/contents`, \"POST\", {\n ids: requestIds,\n ...options,\n });\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?: \"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"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/index.ts
2
2
  import fetch, { Headers } from "cross-fetch";
3
+ var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : fetch;
4
+ var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : Headers;
3
5
  var Exa = class {
4
6
  extractContentsOptions(options) {
5
7
  const {
@@ -52,10 +54,10 @@ var Exa = class {
52
54
  );
53
55
  }
54
56
  }
55
- this.headers = new Headers({
57
+ this.headers = new HeadersImpl({
56
58
  "x-api-key": apiKey,
57
59
  "Content-Type": "application/json",
58
- "User-Agent": "exa-node 1.3.2"
60
+ "User-Agent": "exa-node 1.4.0"
59
61
  });
60
62
  }
61
63
  /**
@@ -63,10 +65,12 @@ var Exa = class {
63
65
  * @param {string} endpoint - The API endpoint to call.
64
66
  * @param {string} method - The HTTP method to use.
65
67
  * @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.
66
70
  * @returns {Promise<any>} The response from the API.
67
71
  */
68
- async request(endpoint, method, body) {
69
- const response = await fetch(this.baseURL + endpoint, {
72
+ async request(endpoint, method, body, stream, onChunk) {
73
+ const response = await fetchImpl(this.baseURL + endpoint, {
70
74
  method,
71
75
  headers: this.headers,
72
76
  body: body ? JSON.stringify(body) : void 0
@@ -77,6 +81,42 @@ var Exa = class {
77
81
  `Request failed with status ${response.status}. ${message}`
78
82
  );
79
83
  }
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
+ }
80
120
  return await response.json();
81
121
  }
82
122
  /**
@@ -126,27 +166,44 @@ var Exa = class {
126
166
  });
127
167
  }
128
168
  /**
129
- * Retrieves contents of documents based on a list of document IDs.
130
- * @param {string | string[] | SearchResult[]} ids - An array of document IDs (or `SearchResult` objects).
169
+ * Retrieves contents of documents based on URLs
170
+ * @param {string | string[] | SearchResult[]} urls - An array of URLs.
131
171
  * @param {ContentsOptions} [options] - Additional options for retrieving document contents.
132
172
  * @returns {Promise<SearchResponse<T>>} A list of document contents.
133
173
  */
134
- async getContents(ids, options) {
135
- if (ids.length === 0) {
136
- throw new Error("Must provide at least one ID");
174
+ async getContents(urls, options) {
175
+ if (urls.length === 0) {
176
+ throw new Error("Must provide at least one URL");
137
177
  }
138
- let requestIds;
139
- if (typeof ids === "string") {
140
- requestIds = [ids];
141
- } else if (typeof ids[0] === "string") {
142
- requestIds = ids;
178
+ let requestUrls;
179
+ if (typeof urls === "string") {
180
+ requestUrls = [urls];
181
+ } else if (typeof urls[0] === "string") {
182
+ requestUrls = urls;
143
183
  } else {
144
- requestIds = ids.map((result) => result.id);
184
+ requestUrls = urls.map((result) => result.url);
145
185
  }
146
- return await this.request(`/contents`, "POST", {
147
- ids: requestIds,
186
+ const payload = {
187
+ urls: requestUrls,
148
188
  ...options
149
- });
189
+ };
190
+ return await this.request(`/contents`, "POST", payload);
191
+ }
192
+ /**
193
+ * Generates an answer to a query using search results as context.
194
+ * @param {string} query - The question or query to answer.
195
+ * @param {AnswerOptions} [options] - Additional options for answer generation.
196
+ * @param {(chunk: AnswerStreamResponse) => void} [onChunk] - Callback for handling stream chunks.
197
+ * @returns {Promise<AnswerResponse | null>} The generated answer and source references, or null if streaming.
198
+ */
199
+ async answer(query, options, onChunk) {
200
+ const requestBody = {
201
+ query,
202
+ expandedQueriesLimit: options?.expandedQueriesLimit ?? 1,
203
+ stream: options?.stream ?? false,
204
+ includeText: options?.includeText ?? false
205
+ };
206
+ return await this.request("/answer", "POST", requestBody, options?.stream, onChunk);
150
207
  }
151
208
  };
152
209
  var src_default = Exa;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import fetch, { Headers } from \"cross-fetch\";\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?: string;\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?: string;\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 * 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 Headers({\n \"x-api-key\": apiKey,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"exa-node 1.3.2\",\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 fetch(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 * @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 a list of document IDs.\n * @param {string | string[] | SearchResult[]} ids - An array of document IDs (or `SearchResult` objects).\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 ids: string | string[] | SearchResult<T>[],\n options?: T,\n ): Promise<SearchResponse<T>> {\n if (ids.length === 0) {\n throw new Error(\"Must provide at least one ID\");\n }\n let requestIds: string[];\n if (typeof ids === \"string\") {\n requestIds = [ids];\n } else if (typeof ids[0] === \"string\") {\n requestIds = ids as string[];\n } else {\n requestIds = (ids as SearchResult<T>[]).map((result) => result.id);\n }\n return await this.request(`/contents`, \"POST\", {\n ids: requestIds,\n ...options,\n });\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?: \"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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exa-js",
3
- "version": "1.3.5",
3
+ "version": "1.4.6",
4
4
  "description": "Exa SDK for Node.js and the browser",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -34,14 +34,13 @@
34
34
  },
35
35
  "license": "MIT",
36
36
  "devDependencies": {
37
- "@types/node": "^22.10.7",
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",
41
40
  "tsup": "6.6.3",
42
41
  "typedoc": "^0.25.4",
43
42
  "typedoc-plugin-markdown": "^3.17.1",
44
- "typescript": "^4.9.5",
43
+ "typescript": "4.9.5",
45
44
  "vitest": "0.28.5"
46
45
  },
47
46
  "dependencies": {