exa-js 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  Exa: () => Exa2,
37
37
  ExaError: () => ExaError,
38
38
  HttpStatusCode: () => HttpStatusCode,
39
+ ResearchClient: () => ResearchClient,
39
40
  ResearchStatus: () => ResearchStatus,
40
41
  WebhookStatus: () => WebhookStatus,
41
42
  WebsetEnrichmentFormat: () => WebsetEnrichmentFormat,
@@ -690,14 +691,95 @@ var WebsetsClient = class extends WebsetsBaseClient {
690
691
  }
691
692
  };
692
693
 
693
- // src/index.ts
694
- var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : import_cross_fetch.default;
695
- var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : import_cross_fetch.Headers;
694
+ // src/research/base.ts
695
+ var ResearchBaseClient = class {
696
+ /**
697
+ * Initialize a new Research base client
698
+ * @param client The Exa client instance
699
+ */
700
+ constructor(client) {
701
+ this.client = client;
702
+ }
703
+ /**
704
+ * Make a request to the Research API (prefixes all paths with `/research`).
705
+ * @param endpoint The endpoint path, beginning with a slash (e.g. "/tasks").
706
+ * @param method The HTTP method. Defaults to "POST".
707
+ * @param data Optional request body
708
+ * @param params Optional query parameters
709
+ * @returns The parsed JSON response
710
+ */
711
+ async request(endpoint, method = "POST", data, params) {
712
+ return this.client.request(`/research${endpoint}`, method, data, params);
713
+ }
714
+ };
715
+
716
+ // src/research/client.ts
717
+ var ResearchClient = class extends ResearchBaseClient {
718
+ constructor(client) {
719
+ super(client);
720
+ }
721
+ /**
722
+ * Create a research task.
723
+ *
724
+ * Both parameters are required and have fixed shapes:
725
+ * 1. `input`
726
+ * `{ instructions: string }`
727
+ * • `instructions` – High-level guidance that tells the research agent what to do.
728
+ * 2. `output`
729
+ * defines the exact structure you expect back, and guides the research conducted by the agent.
730
+ * `{ schema: JSONSchema }`.
731
+ * The agent's response will be validated against this schema.
732
+ *
733
+ * @param input Object containing high-level research instructions.
734
+ * @param output Object containing the expected output schema.
735
+ * @returns The ResearchTaskResponse returned by the API.
736
+ */
737
+ async createTask(input, output) {
738
+ return this.request("/tasks", "POST", {
739
+ input,
740
+ output
741
+ });
742
+ }
743
+ /**
744
+ * Retrieve a research task by ID.
745
+ */
746
+ async getTask(id) {
747
+ return this.request(`/tasks/${id}`, "GET");
748
+ }
749
+ /**
750
+ * Poll a research task until completion or failure.
751
+ * Polls every 1 second with a maximum timeout of 10 minutes.
752
+ */
753
+ async pollTask(id) {
754
+ const pollingInterval = 1e3;
755
+ const maxPollingTime = 10 * 60 * 1e3;
756
+ const startTime = Date.now();
757
+ while (true) {
758
+ const task = await this.request(`/tasks/${id}`, "GET");
759
+ if (task.status === "completed" || task.status === "failed") {
760
+ return task;
761
+ }
762
+ if (Date.now() - startTime > maxPollingTime) {
763
+ throw new Error(
764
+ `Polling timeout: Task ${id} did not complete within 10 minutes`
765
+ );
766
+ }
767
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval));
768
+ }
769
+ }
770
+ };
771
+
772
+ // src/research/types.ts
696
773
  var ResearchStatus = /* @__PURE__ */ ((ResearchStatus2) => {
774
+ ResearchStatus2["in_progress"] = "in_progress";
697
775
  ResearchStatus2["completed"] = "completed";
698
776
  ResearchStatus2["failed"] = "failed";
699
777
  return ResearchStatus2;
700
778
  })(ResearchStatus || {});
779
+
780
+ // src/index.ts
781
+ var fetchImpl = typeof global !== "undefined" && global.fetch ? global.fetch : import_cross_fetch.default;
782
+ var HeadersImpl = typeof global !== "undefined" && global.Headers ? global.Headers : import_cross_fetch.Headers;
701
783
  var Exa2 = class {
702
784
  /**
703
785
  * Helper method to separate out the contents-specific options from the rest.
@@ -755,6 +837,7 @@ var Exa2 = class {
755
837
  "User-Agent": "exa-node 1.4.0"
756
838
  });
757
839
  this.websets = new WebsetsClient(this);
840
+ this.research = new ResearchClient(this);
758
841
  }
759
842
  /**
760
843
  * Makes a request to the Exa API.
@@ -804,6 +887,10 @@ var Exa2 = class {
804
887
  errorData.path
805
888
  );
806
889
  }
890
+ const contentType = response.headers.get("content-type") || "";
891
+ if (contentType.includes("text/event-stream")) {
892
+ return await this.parseSSEStream(response);
893
+ }
807
894
  return await response.json();
808
895
  }
809
896
  /**
@@ -1027,47 +1114,76 @@ var Exa2 = class {
1027
1114
  }
1028
1115
  return { content, citations };
1029
1116
  }
1030
- /**
1031
- * Creates and runs a research task in a blocking manner.
1032
- *
1033
- * Both parameters are required and have fixed shapes:
1034
- * 1. `input`
1035
- * `{ instructions: string }`
1036
- * • `instructions` High-level guidance that tells the research agent what to do.
1037
- * 2. `output`
1038
- * defines the exact structure you expect back, and guides the research conducted by the agent.
1039
- * `{ schema: JSONSchema }`.
1040
- * The agent’s response will be validated against this schema.
1041
- *
1042
- * @param {{ instructions: string }} input The research prompt.
1043
- * @param {{ schema: JSONSchema }} output The desired output schema.
1044
- * @returns {Promise<ResearchTaskResponse>} The research response.
1045
- *
1046
- * @example
1047
- * const response = await exa.researchTask(
1048
- * { instructions: "I need a few key facts about honey pot ants." },
1049
- * {
1050
- * schema: {
1051
- * type: "object",
1052
- * required: ["scientificName", "primaryRegions"],
1053
- * properties: {
1054
- * scientificName: { type: "string" },
1055
- * primaryRegions: { type: "string" },
1056
- * },
1057
- * },
1058
- * },
1059
- * );
1060
- */
1061
- async researchTask(input, output) {
1062
- const body = {
1063
- input,
1064
- output
1065
- };
1066
- return await this.request(
1067
- "/research/tasks",
1068
- "POST",
1069
- body
1070
- );
1117
+ async parseSSEStream(response) {
1118
+ const reader = response.body?.getReader();
1119
+ if (!reader) {
1120
+ throw new ExaError(
1121
+ "No response body available for streaming.",
1122
+ 500,
1123
+ (/* @__PURE__ */ new Date()).toISOString()
1124
+ );
1125
+ }
1126
+ const decoder = new TextDecoder();
1127
+ let buffer = "";
1128
+ return new Promise(async (resolve, reject) => {
1129
+ try {
1130
+ while (true) {
1131
+ const { done, value } = await reader.read();
1132
+ if (done) break;
1133
+ buffer += decoder.decode(value, { stream: true });
1134
+ const lines = buffer.split("\n");
1135
+ buffer = lines.pop() || "";
1136
+ for (const line of lines) {
1137
+ if (!line.startsWith("data: ")) continue;
1138
+ const jsonStr = line.replace(/^data:\s*/, "").trim();
1139
+ if (!jsonStr || jsonStr === "[DONE]") {
1140
+ continue;
1141
+ }
1142
+ let chunk;
1143
+ try {
1144
+ chunk = JSON.parse(jsonStr);
1145
+ } catch {
1146
+ continue;
1147
+ }
1148
+ switch (chunk.tag) {
1149
+ case "complete":
1150
+ reader.releaseLock();
1151
+ resolve(chunk.data);
1152
+ return;
1153
+ case "error": {
1154
+ const message = chunk.error?.message || "Unknown error";
1155
+ reader.releaseLock();
1156
+ reject(
1157
+ new ExaError(
1158
+ message,
1159
+ 500 /* InternalServerError */,
1160
+ (/* @__PURE__ */ new Date()).toISOString()
1161
+ )
1162
+ );
1163
+ return;
1164
+ }
1165
+ // 'progress' and any other tags are ignored for the blocking variant
1166
+ default:
1167
+ break;
1168
+ }
1169
+ }
1170
+ }
1171
+ reject(
1172
+ new ExaError(
1173
+ "Stream ended without a completion event.",
1174
+ 500 /* InternalServerError */,
1175
+ (/* @__PURE__ */ new Date()).toISOString()
1176
+ )
1177
+ );
1178
+ } catch (err) {
1179
+ reject(err);
1180
+ } finally {
1181
+ try {
1182
+ reader.releaseLock();
1183
+ } catch {
1184
+ }
1185
+ }
1186
+ });
1071
1187
  }
1072
1188
  };
1073
1189
  var index_default = Exa2;
@@ -1079,6 +1195,7 @@ var index_default = Exa2;
1079
1195
  Exa,
1080
1196
  ExaError,
1081
1197
  HttpStatusCode,
1198
+ ResearchClient,
1082
1199
  ResearchStatus,
1083
1200
  WebhookStatus,
1084
1201
  WebsetEnrichmentFormat,