valyu-js 2.2.0 → 2.2.2

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.mjs CHANGED
@@ -687,164 +687,269 @@ var Valyu = class {
687
687
  * @param options.startDate - Start date filter (YYYY-MM-DD format)
688
688
  * @param options.endDate - End date filter (YYYY-MM-DD format)
689
689
  * @param options.fastMode - Fast mode for quicker but shorter results (default: false)
690
- * @returns Promise resolving to answer response
690
+ * @param options.streaming - Enable streaming mode (default: false)
691
+ * @returns Promise resolving to answer response, or AsyncGenerator for streaming
691
692
  */
692
693
  async answer(query, options = {}) {
693
- try {
694
- const defaultSearchType = "all";
695
- if (!query || typeof query !== "string" || query.trim().length === 0) {
696
- return {
697
- success: false,
698
- error: "Query is required and must be a non-empty string"
699
- };
700
- }
701
- let finalSearchType = defaultSearchType;
702
- const providedSearchTypeString = options.searchType?.toLowerCase();
703
- if (providedSearchTypeString === "web" || providedSearchTypeString === "proprietary" || providedSearchTypeString === "all" || providedSearchTypeString === "news") {
704
- finalSearchType = providedSearchTypeString;
705
- } else if (options.searchType !== void 0) {
706
- return {
707
- success: false,
708
- error: "Invalid searchType provided. Must be one of: all, web, proprietary, news"
709
- };
694
+ const validationError = this.validateAnswerParams(query, options);
695
+ if (validationError) {
696
+ if (options.streaming) {
697
+ return this.createErrorGenerator(validationError);
710
698
  }
711
- if (options.systemInstructions !== void 0) {
712
- if (typeof options.systemInstructions !== "string") {
713
- return {
714
- success: false,
715
- error: "systemInstructions must be a string"
716
- };
717
- }
718
- const trimmed = options.systemInstructions.trim();
719
- if (trimmed.length === 0) {
720
- return {
721
- success: false,
722
- error: "systemInstructions cannot be empty when provided"
723
- };
724
- }
725
- if (trimmed.length > 2e3) {
726
- return {
727
- success: false,
728
- error: "systemInstructions must be 2000 characters or less"
729
- };
730
- }
731
- }
732
- if (options.dataMaxPrice !== void 0) {
733
- if (typeof options.dataMaxPrice !== "number" || options.dataMaxPrice <= 0) {
734
- return {
735
- success: false,
736
- error: "dataMaxPrice must be a positive number"
737
- };
738
- }
739
- }
740
- if (options.startDate && !this.validateDateFormat(options.startDate)) {
741
- return {
742
- success: false,
743
- error: "Invalid startDate format. Must be YYYY-MM-DD"
744
- };
745
- }
746
- if (options.endDate && !this.validateDateFormat(options.endDate)) {
747
- return {
748
- success: false,
749
- error: "Invalid endDate format. Must be YYYY-MM-DD"
750
- };
751
- }
752
- if (options.startDate && options.endDate) {
753
- const startDate = new Date(options.startDate);
754
- const endDate = new Date(options.endDate);
755
- if (startDate > endDate) {
756
- return {
757
- success: false,
758
- error: "startDate must be before endDate"
759
- };
760
- }
761
- }
762
- if (options.includedSources !== void 0) {
763
- if (!Array.isArray(options.includedSources)) {
764
- return {
765
- success: false,
766
- error: "includedSources must be an array"
767
- };
768
- }
769
- const includedSourcesValidation = this.validateSources(
770
- options.includedSources
771
- );
772
- if (!includedSourcesValidation.valid) {
773
- return {
774
- success: false,
775
- error: `Invalid includedSources format. Invalid sources: ${includedSourcesValidation.invalidSources.join(
776
- ", "
777
- )}. Sources must be valid URLs, domains (with optional paths), or dataset identifiers in 'provider/dataset' format.`
778
- };
779
- }
780
- }
781
- if (options.excludedSources !== void 0) {
782
- if (!Array.isArray(options.excludedSources)) {
783
- return {
784
- success: false,
785
- error: "excludedSources must be an array"
786
- };
787
- }
788
- const excludedSourcesValidation = this.validateSources(
789
- options.excludedSources
790
- );
791
- if (!excludedSourcesValidation.valid) {
792
- return {
793
- success: false,
794
- error: `Invalid excludedSources format. Invalid sources: ${excludedSourcesValidation.invalidSources.join(
795
- ", "
796
- )}. Sources must be valid URLs, domains (with optional paths), or dataset identifiers in 'provider/dataset' format.`
797
- };
798
- }
799
- }
800
- const payload = {
801
- query: query.trim(),
802
- search_type: finalSearchType
803
- };
804
- if (options.dataMaxPrice !== void 0) {
805
- payload.data_max_price = options.dataMaxPrice;
699
+ return { success: false, error: validationError };
700
+ }
701
+ const payload = this.buildAnswerPayload(query, options);
702
+ if (options.streaming) {
703
+ return this.streamAnswer(payload);
704
+ } else {
705
+ return this.fetchAnswer(payload);
706
+ }
707
+ }
708
+ /**
709
+ * Validate answer parameters
710
+ */
711
+ validateAnswerParams(query, options) {
712
+ if (!query || typeof query !== "string" || query.trim().length === 0) {
713
+ return "Query is required and must be a non-empty string";
714
+ }
715
+ const providedSearchTypeString = options.searchType?.toLowerCase();
716
+ if (providedSearchTypeString !== void 0 && providedSearchTypeString !== "web" && providedSearchTypeString !== "proprietary" && providedSearchTypeString !== "all" && providedSearchTypeString !== "news") {
717
+ return "Invalid searchType provided. Must be one of: all, web, proprietary, news";
718
+ }
719
+ if (options.systemInstructions !== void 0) {
720
+ if (typeof options.systemInstructions !== "string") {
721
+ return "systemInstructions must be a string";
806
722
  }
807
- if (options.structuredOutput !== void 0) {
808
- payload.structured_output = options.structuredOutput;
723
+ const trimmed = options.systemInstructions.trim();
724
+ if (trimmed.length === 0) {
725
+ return "systemInstructions cannot be empty when provided";
809
726
  }
810
- if (options.systemInstructions !== void 0) {
811
- payload.system_instructions = options.systemInstructions.trim();
727
+ if (trimmed.length > 2e3) {
728
+ return "systemInstructions must be 2000 characters or less";
812
729
  }
813
- if (options.countryCode !== void 0) {
814
- payload.country_code = options.countryCode;
730
+ }
731
+ if (options.dataMaxPrice !== void 0) {
732
+ if (typeof options.dataMaxPrice !== "number" || options.dataMaxPrice <= 0) {
733
+ return "dataMaxPrice must be a positive number";
815
734
  }
816
- if (options.includedSources !== void 0) {
817
- payload.included_sources = options.includedSources;
735
+ }
736
+ if (options.startDate && !this.validateDateFormat(options.startDate)) {
737
+ return "Invalid startDate format. Must be YYYY-MM-DD";
738
+ }
739
+ if (options.endDate && !this.validateDateFormat(options.endDate)) {
740
+ return "Invalid endDate format. Must be YYYY-MM-DD";
741
+ }
742
+ if (options.startDate && options.endDate) {
743
+ const startDate = new Date(options.startDate);
744
+ const endDate = new Date(options.endDate);
745
+ if (startDate > endDate) {
746
+ return "startDate must be before endDate";
818
747
  }
819
- if (options.excludedSources !== void 0) {
820
- payload.excluded_sources = options.excludedSources;
748
+ }
749
+ if (options.includedSources !== void 0) {
750
+ if (!Array.isArray(options.includedSources)) {
751
+ return "includedSources must be an array";
821
752
  }
822
- if (options.startDate !== void 0) {
823
- payload.start_date = options.startDate;
753
+ const validation = this.validateSources(options.includedSources);
754
+ if (!validation.valid) {
755
+ return `Invalid includedSources format. Invalid sources: ${validation.invalidSources.join(", ")}.`;
824
756
  }
825
- if (options.endDate !== void 0) {
826
- payload.end_date = options.endDate;
757
+ }
758
+ if (options.excludedSources !== void 0) {
759
+ if (!Array.isArray(options.excludedSources)) {
760
+ return "excludedSources must be an array";
827
761
  }
828
- if (options.fastMode !== void 0) {
829
- payload.fast_mode = options.fastMode;
762
+ const validation = this.validateSources(options.excludedSources);
763
+ if (!validation.valid) {
764
+ return `Invalid excludedSources format. Invalid sources: ${validation.invalidSources.join(", ")}.`;
830
765
  }
831
- const response = await axios.post(`${this.baseUrl}/answer`, payload, {
832
- headers: this.headers
766
+ }
767
+ return null;
768
+ }
769
+ /**
770
+ * Build payload for answer API
771
+ */
772
+ buildAnswerPayload(query, options) {
773
+ const defaultSearchType = "all";
774
+ const providedSearchTypeString = options.searchType?.toLowerCase();
775
+ let finalSearchType = defaultSearchType;
776
+ if (providedSearchTypeString === "web" || providedSearchTypeString === "proprietary" || providedSearchTypeString === "all" || providedSearchTypeString === "news") {
777
+ finalSearchType = providedSearchTypeString;
778
+ }
779
+ const payload = {
780
+ query: query.trim(),
781
+ search_type: finalSearchType
782
+ };
783
+ if (options.dataMaxPrice !== void 0) payload.data_max_price = options.dataMaxPrice;
784
+ if (options.structuredOutput !== void 0) payload.structured_output = options.structuredOutput;
785
+ if (options.systemInstructions !== void 0) payload.system_instructions = options.systemInstructions.trim();
786
+ if (options.countryCode !== void 0) payload.country_code = options.countryCode;
787
+ if (options.includedSources !== void 0) payload.included_sources = options.includedSources;
788
+ if (options.excludedSources !== void 0) payload.excluded_sources = options.excludedSources;
789
+ if (options.startDate !== void 0) payload.start_date = options.startDate;
790
+ if (options.endDate !== void 0) payload.end_date = options.endDate;
791
+ if (options.fastMode !== void 0) payload.fast_mode = options.fastMode;
792
+ return payload;
793
+ }
794
+ /**
795
+ * Fetch answer (non-streaming mode)
796
+ */
797
+ async fetchAnswer(payload) {
798
+ try {
799
+ const response = await fetch(`${this.baseUrl}/answer`, {
800
+ method: "POST",
801
+ headers: {
802
+ ...this.headers,
803
+ "Accept": "text/event-stream"
804
+ },
805
+ body: JSON.stringify(payload)
833
806
  });
834
- if (!response.status || response.status < 200 || response.status >= 300) {
807
+ if (!response.ok) {
808
+ const errorData = await response.json().catch(() => ({}));
835
809
  return {
836
810
  success: false,
837
- error: response.data?.error || "Request failed"
811
+ error: errorData.error || `HTTP Error: ${response.status}`
838
812
  };
839
813
  }
840
- return response.data;
814
+ let fullContent = "";
815
+ let searchResults = [];
816
+ let finalMetadata = {};
817
+ const reader = response.body?.getReader();
818
+ const decoder = new TextDecoder();
819
+ let buffer = "";
820
+ if (reader) {
821
+ while (true) {
822
+ const { done, value } = await reader.read();
823
+ if (done) break;
824
+ buffer += decoder.decode(value, { stream: true });
825
+ const lines = buffer.split("\n");
826
+ buffer = lines.pop() || "";
827
+ for (const line of lines) {
828
+ if (!line.startsWith("data: ")) continue;
829
+ const dataStr = line.slice(6);
830
+ if (dataStr === "[DONE]") continue;
831
+ try {
832
+ const parsed = JSON.parse(dataStr);
833
+ if (parsed.search_results && !parsed.success) {
834
+ searchResults = [...searchResults, ...parsed.search_results];
835
+ } else if (parsed.choices) {
836
+ const content = parsed.choices[0]?.delta?.content || "";
837
+ if (content) fullContent += content;
838
+ } else if (parsed.success !== void 0) {
839
+ finalMetadata = parsed;
840
+ }
841
+ } catch {
842
+ continue;
843
+ }
844
+ }
845
+ }
846
+ }
847
+ if (finalMetadata.success) {
848
+ const finalSearchResults = finalMetadata.search_results || searchResults;
849
+ const response2 = {
850
+ success: true,
851
+ tx_id: finalMetadata.tx_id || "",
852
+ original_query: finalMetadata.original_query || payload.query,
853
+ contents: fullContent || finalMetadata.contents || "",
854
+ data_type: finalMetadata.data_type || "unstructured",
855
+ search_results: finalSearchResults,
856
+ search_metadata: finalMetadata.search_metadata || { tx_ids: [], number_of_results: 0, total_characters: 0 },
857
+ ai_usage: finalMetadata.ai_usage || { input_tokens: 0, output_tokens: 0 },
858
+ cost: finalMetadata.cost || { total_deduction_dollars: 0, search_deduction_dollars: 0, ai_deduction_dollars: 0 }
859
+ };
860
+ if (finalMetadata.extraction_metadata) {
861
+ response2.extraction_metadata = finalMetadata.extraction_metadata;
862
+ }
863
+ return response2;
864
+ }
865
+ return {
866
+ success: false,
867
+ error: finalMetadata.error || "Unknown error occurred"
868
+ };
841
869
  } catch (e) {
842
870
  return {
843
871
  success: false,
844
- error: e.response?.data?.error || e.message
872
+ error: e.message || "Request failed"
845
873
  };
846
874
  }
847
875
  }
876
+ /**
877
+ * Stream answer using SSE
878
+ */
879
+ async *streamAnswer(payload) {
880
+ try {
881
+ const response = await fetch(`${this.baseUrl}/answer`, {
882
+ method: "POST",
883
+ headers: {
884
+ ...this.headers,
885
+ "Accept": "text/event-stream"
886
+ },
887
+ body: JSON.stringify(payload)
888
+ });
889
+ if (!response.ok) {
890
+ const errorData = await response.json().catch(() => ({}));
891
+ yield { type: "error", error: errorData.error || `HTTP Error: ${response.status}` };
892
+ return;
893
+ }
894
+ const reader = response.body?.getReader();
895
+ const decoder = new TextDecoder();
896
+ let buffer = "";
897
+ if (!reader) {
898
+ yield { type: "error", error: "No response body" };
899
+ return;
900
+ }
901
+ while (true) {
902
+ const { done, value } = await reader.read();
903
+ if (done) break;
904
+ buffer += decoder.decode(value, { stream: true });
905
+ const lines = buffer.split("\n");
906
+ buffer = lines.pop() || "";
907
+ for (const line of lines) {
908
+ if (!line.startsWith("data: ")) continue;
909
+ const dataStr = line.slice(6);
910
+ if (dataStr === "[DONE]") {
911
+ yield { type: "done" };
912
+ continue;
913
+ }
914
+ try {
915
+ const parsed = JSON.parse(dataStr);
916
+ if (parsed.search_results && parsed.success === void 0) {
917
+ yield { type: "search_results", search_results: parsed.search_results };
918
+ } else if (parsed.choices) {
919
+ const delta = parsed.choices[0]?.delta || {};
920
+ const content = delta.content || "";
921
+ const finishReason = parsed.choices[0]?.finish_reason;
922
+ if (content || finishReason) {
923
+ yield { type: "content", content, finish_reason: finishReason };
924
+ }
925
+ } else if (parsed.success !== void 0) {
926
+ yield {
927
+ type: "metadata",
928
+ tx_id: parsed.tx_id,
929
+ original_query: parsed.original_query,
930
+ data_type: parsed.data_type,
931
+ search_results: parsed.search_results,
932
+ search_metadata: parsed.search_metadata,
933
+ ai_usage: parsed.ai_usage,
934
+ cost: parsed.cost,
935
+ extraction_metadata: parsed.extraction_metadata
936
+ };
937
+ }
938
+ } catch {
939
+ continue;
940
+ }
941
+ }
942
+ }
943
+ } catch (e) {
944
+ yield { type: "error", error: e.message || "Stream failed" };
945
+ }
946
+ }
947
+ /**
948
+ * Create an error generator for streaming errors
949
+ */
950
+ async *createErrorGenerator(error) {
951
+ yield { type: "error", error };
952
+ }
848
953
  };
849
954
  export {
850
955
  Valyu