valyu-js 2.1.7 → 2.2.1

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
@@ -13,6 +13,17 @@ var Valyu = class {
13
13
  "Content-Type": "application/json",
14
14
  "x-api-key": apiKey
15
15
  };
16
+ this.deepresearch = {
17
+ create: this._deepresearchCreate.bind(this),
18
+ status: this._deepresearchStatus.bind(this),
19
+ wait: this._deepresearchWait.bind(this),
20
+ stream: this._deepresearchStream.bind(this),
21
+ list: this._deepresearchList.bind(this),
22
+ update: this._deepresearchUpdate.bind(this),
23
+ cancel: this._deepresearchCancel.bind(this),
24
+ delete: this._deepresearchDelete.bind(this),
25
+ togglePublic: this._deepresearchTogglePublic.bind(this)
26
+ };
16
27
  }
17
28
  /**
18
29
  * Validates date format (YYYY-MM-DD)
@@ -432,177 +443,508 @@ var Valyu = class {
432
443
  }
433
444
  }
434
445
  /**
435
- * Get AI-powered answers using the Valyu Answer API
436
- * @param query - The question or query string
437
- * @param options - Answer configuration options
438
- * @param options.structuredOutput - JSON Schema object for structured responses
439
- * @param options.systemInstructions - Custom system-level instructions (max 2000 chars)
440
- * @param options.searchType - Type of search: "web", "proprietary", "all", or "news"
441
- * @param options.dataMaxPrice - Maximum spend (USD) for data retrieval
442
- * @param options.countryCode - Country code filter for search results
443
- * @param options.includedSources - List of specific sources to include
444
- * @param options.excludedSources - List of URLs/domains to exclude from search results
445
- * @param options.startDate - Start date filter (YYYY-MM-DD format)
446
- * @param options.endDate - End date filter (YYYY-MM-DD format)
447
- * @param options.fastMode - Fast mode for quicker but shorter results (default: false)
448
- * @returns Promise resolving to answer response
446
+ * DeepResearch: Create a new research task
449
447
  */
450
- async answer(query, options = {}) {
448
+ async _deepresearchCreate(options) {
451
449
  try {
452
- const defaultSearchType = "all";
453
- if (!query || typeof query !== "string" || query.trim().length === 0) {
450
+ if (!options.input?.trim()) {
454
451
  return {
455
452
  success: false,
456
- error: "Query is required and must be a non-empty string"
453
+ error: "input is required and cannot be empty"
457
454
  };
458
455
  }
459
- let finalSearchType = defaultSearchType;
460
- const providedSearchTypeString = options.searchType?.toLowerCase();
461
- if (providedSearchTypeString === "web" || providedSearchTypeString === "proprietary" || providedSearchTypeString === "all" || providedSearchTypeString === "news") {
462
- finalSearchType = providedSearchTypeString;
463
- } else if (options.searchType !== void 0) {
464
- return {
465
- success: false,
466
- error: "Invalid searchType provided. Must be one of: all, web, proprietary, news"
456
+ const payload = {
457
+ input: options.input,
458
+ model: options.model || "lite",
459
+ output_formats: options.outputFormats || ["markdown"],
460
+ code_execution: options.codeExecution !== false
461
+ };
462
+ if (options.strategy) payload.strategy = options.strategy;
463
+ if (options.search) {
464
+ payload.search = {
465
+ search_type: options.search.searchType,
466
+ included_sources: options.search.includedSources
467
467
  };
468
468
  }
469
- if (options.systemInstructions !== void 0) {
470
- if (typeof options.systemInstructions !== "string") {
471
- return {
472
- success: false,
473
- error: "systemInstructions must be a string"
474
- };
475
- }
476
- const trimmed = options.systemInstructions.trim();
477
- if (trimmed.length === 0) {
478
- return {
479
- success: false,
480
- error: "systemInstructions cannot be empty when provided"
481
- };
482
- }
483
- if (trimmed.length > 2e3) {
484
- return {
485
- success: false,
486
- error: "systemInstructions must be 2000 characters or less"
487
- };
488
- }
469
+ if (options.urls) payload.urls = options.urls;
470
+ if (options.files) payload.files = options.files;
471
+ if (options.mcpServers) payload.mcp_servers = options.mcpServers;
472
+ if (options.previousReports) {
473
+ payload.previous_reports = options.previousReports;
474
+ }
475
+ if (options.webhookUrl) payload.webhook_url = options.webhookUrl;
476
+ if (options.metadata) payload.metadata = options.metadata;
477
+ const response = await axios.post(
478
+ `${this.baseUrl}/deepresearch/tasks`,
479
+ payload,
480
+ { headers: this.headers }
481
+ );
482
+ return { success: true, ...response.data };
483
+ } catch (e) {
484
+ return {
485
+ success: false,
486
+ error: e.response?.data?.error || e.message
487
+ };
488
+ }
489
+ }
490
+ /**
491
+ * DeepResearch: Get task status
492
+ */
493
+ async _deepresearchStatus(taskId) {
494
+ try {
495
+ const response = await axios.get(
496
+ `${this.baseUrl}/deepresearch/tasks/${taskId}/status`,
497
+ { headers: this.headers }
498
+ );
499
+ return { success: true, ...response.data };
500
+ } catch (e) {
501
+ return {
502
+ success: false,
503
+ error: e.response?.data?.error || e.message
504
+ };
505
+ }
506
+ }
507
+ /**
508
+ * DeepResearch: Wait for task completion with polling
509
+ */
510
+ async _deepresearchWait(taskId, options = {}) {
511
+ const pollInterval = options.pollInterval || 5e3;
512
+ const maxWaitTime = options.maxWaitTime || 36e5;
513
+ const startTime = Date.now();
514
+ while (true) {
515
+ const status = await this._deepresearchStatus(taskId);
516
+ if (!status.success) {
517
+ throw new Error(status.error);
489
518
  }
490
- if (options.dataMaxPrice !== void 0) {
491
- if (typeof options.dataMaxPrice !== "number" || options.dataMaxPrice <= 0) {
492
- return {
493
- success: false,
494
- error: "dataMaxPrice must be a positive number"
495
- };
496
- }
519
+ if (options.onProgress) {
520
+ options.onProgress(status);
497
521
  }
498
- if (options.startDate && !this.validateDateFormat(options.startDate)) {
499
- return {
500
- success: false,
501
- error: "Invalid startDate format. Must be YYYY-MM-DD"
502
- };
522
+ if (status.status === "completed" || status.status === "failed" || status.status === "cancelled") {
523
+ return status;
503
524
  }
504
- if (options.endDate && !this.validateDateFormat(options.endDate)) {
505
- return {
506
- success: false,
507
- error: "Invalid endDate format. Must be YYYY-MM-DD"
508
- };
525
+ if (Date.now() - startTime > maxWaitTime) {
526
+ throw new Error("Maximum wait time exceeded");
509
527
  }
510
- if (options.startDate && options.endDate) {
511
- const startDate = new Date(options.startDate);
512
- const endDate = new Date(options.endDate);
513
- if (startDate > endDate) {
514
- return {
515
- success: false,
516
- error: "startDate must be before endDate"
517
- };
528
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
529
+ }
530
+ }
531
+ /**
532
+ * DeepResearch: Stream real-time updates
533
+ */
534
+ async _deepresearchStream(taskId, callback) {
535
+ let isComplete = false;
536
+ let lastMessageCount = 0;
537
+ while (!isComplete) {
538
+ try {
539
+ const status = await this._deepresearchStatus(taskId);
540
+ if (!status.success) {
541
+ if (callback.onError) {
542
+ callback.onError(new Error(status.error));
543
+ }
544
+ return;
518
545
  }
519
- }
520
- if (options.includedSources !== void 0) {
521
- if (!Array.isArray(options.includedSources)) {
522
- return {
523
- success: false,
524
- error: "includedSources must be an array"
525
- };
546
+ if (status.progress && callback.onProgress) {
547
+ callback.onProgress(
548
+ status.progress.current_step,
549
+ status.progress.total_steps
550
+ );
526
551
  }
527
- const includedSourcesValidation = this.validateSources(
528
- options.includedSources
529
- );
530
- if (!includedSourcesValidation.valid) {
531
- return {
532
- success: false,
533
- error: `Invalid includedSources format. Invalid sources: ${includedSourcesValidation.invalidSources.join(
534
- ", "
535
- )}. Sources must be valid URLs, domains (with optional paths), or dataset identifiers in 'provider/dataset' format.`
536
- };
552
+ if (status.messages && callback.onMessage) {
553
+ const newMessages = status.messages.slice(lastMessageCount);
554
+ newMessages.forEach((msg) => callback.onMessage(msg));
555
+ lastMessageCount = status.messages.length;
537
556
  }
538
- }
539
- if (options.excludedSources !== void 0) {
540
- if (!Array.isArray(options.excludedSources)) {
541
- return {
542
- success: false,
543
- error: "excludedSources must be an array"
544
- };
557
+ if (status.status === "completed") {
558
+ if (callback.onComplete) {
559
+ callback.onComplete(status);
560
+ }
561
+ isComplete = true;
562
+ } else if (status.status === "failed" || status.status === "cancelled") {
563
+ if (callback.onError) {
564
+ callback.onError(
565
+ new Error(status.error || `Task ${status.status}`)
566
+ );
567
+ }
568
+ isComplete = true;
545
569
  }
546
- const excludedSourcesValidation = this.validateSources(
547
- options.excludedSources
548
- );
549
- if (!excludedSourcesValidation.valid) {
550
- return {
551
- success: false,
552
- error: `Invalid excludedSources format. Invalid sources: ${excludedSourcesValidation.invalidSources.join(
553
- ", "
554
- )}. Sources must be valid URLs, domains (with optional paths), or dataset identifiers in 'provider/dataset' format.`
555
- };
570
+ if (!isComplete) {
571
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
572
+ }
573
+ } catch (error) {
574
+ if (callback.onError) {
575
+ callback.onError(error);
556
576
  }
577
+ throw error;
557
578
  }
558
- const payload = {
559
- query: query.trim(),
560
- search_type: finalSearchType
579
+ }
580
+ }
581
+ /**
582
+ * DeepResearch: List all tasks
583
+ */
584
+ async _deepresearchList(options) {
585
+ try {
586
+ const limit = options.limit || 10;
587
+ const response = await axios.get(
588
+ `${this.baseUrl}/deepresearch/listtasks?api_key_id=${options.apiKeyId}&limit=${limit}`,
589
+ { headers: this.headers }
590
+ );
591
+ return { success: true, data: response.data };
592
+ } catch (e) {
593
+ return {
594
+ success: false,
595
+ error: e.response?.data?.error || e.message
596
+ };
597
+ }
598
+ }
599
+ /**
600
+ * DeepResearch: Add follow-up instruction
601
+ */
602
+ async _deepresearchUpdate(taskId, instruction) {
603
+ try {
604
+ if (!instruction?.trim()) {
605
+ return {
606
+ success: false,
607
+ error: "instruction is required and cannot be empty"
608
+ };
609
+ }
610
+ const response = await axios.post(
611
+ `${this.baseUrl}/deepresearch/tasks/${taskId}/update`,
612
+ { instruction },
613
+ { headers: this.headers }
614
+ );
615
+ return { success: true, ...response.data };
616
+ } catch (e) {
617
+ return {
618
+ success: false,
619
+ error: e.response?.data?.error || e.message
620
+ };
621
+ }
622
+ }
623
+ /**
624
+ * DeepResearch: Cancel task
625
+ */
626
+ async _deepresearchCancel(taskId) {
627
+ try {
628
+ const response = await axios.post(
629
+ `${this.baseUrl}/deepresearch/tasks/${taskId}/cancel`,
630
+ {},
631
+ { headers: this.headers }
632
+ );
633
+ return { success: true, ...response.data };
634
+ } catch (e) {
635
+ return {
636
+ success: false,
637
+ error: e.response?.data?.error || e.message
638
+ };
639
+ }
640
+ }
641
+ /**
642
+ * DeepResearch: Delete task
643
+ */
644
+ async _deepresearchDelete(taskId) {
645
+ try {
646
+ const response = await axios.delete(
647
+ `${this.baseUrl}/deepresearch/tasks/${taskId}/delete`,
648
+ { headers: this.headers }
649
+ );
650
+ return { success: true, ...response.data };
651
+ } catch (e) {
652
+ return {
653
+ success: false,
654
+ error: e.response?.data?.error || e.message
561
655
  };
562
- if (options.dataMaxPrice !== void 0) {
563
- payload.data_max_price = options.dataMaxPrice;
656
+ }
657
+ }
658
+ /**
659
+ * DeepResearch: Toggle public flag
660
+ */
661
+ async _deepresearchTogglePublic(taskId, isPublic) {
662
+ try {
663
+ const response = await axios.post(
664
+ `${this.baseUrl}/deepresearch/tasks/${taskId}/public`,
665
+ { public: isPublic },
666
+ { headers: this.headers }
667
+ );
668
+ return { success: true, ...response.data };
669
+ } catch (e) {
670
+ return {
671
+ success: false,
672
+ error: e.response?.data?.error || e.message
673
+ };
674
+ }
675
+ }
676
+ /**
677
+ * Get AI-powered answers using the Valyu Answer API
678
+ * @param query - The question or query string
679
+ * @param options - Answer configuration options
680
+ * @param options.structuredOutput - JSON Schema object for structured responses
681
+ * @param options.systemInstructions - Custom system-level instructions (max 2000 chars)
682
+ * @param options.searchType - Type of search: "web", "proprietary", "all", or "news"
683
+ * @param options.dataMaxPrice - Maximum spend (USD) for data retrieval
684
+ * @param options.countryCode - Country code filter for search results
685
+ * @param options.includedSources - List of specific sources to include
686
+ * @param options.excludedSources - List of URLs/domains to exclude from search results
687
+ * @param options.startDate - Start date filter (YYYY-MM-DD format)
688
+ * @param options.endDate - End date filter (YYYY-MM-DD format)
689
+ * @param options.fastMode - Fast mode for quicker but shorter results (default: false)
690
+ * @param options.streaming - Enable streaming mode (default: false)
691
+ * @returns Promise resolving to answer response, or AsyncGenerator for streaming
692
+ */
693
+ async answer(query, options = {}) {
694
+ const validationError = this.validateAnswerParams(query, options);
695
+ if (validationError) {
696
+ if (options.streaming) {
697
+ return this.createErrorGenerator(validationError);
564
698
  }
565
- if (options.structuredOutput !== void 0) {
566
- payload.structured_output = options.structuredOutput;
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";
567
722
  }
568
- if (options.systemInstructions !== void 0) {
569
- payload.system_instructions = options.systemInstructions.trim();
723
+ const trimmed = options.systemInstructions.trim();
724
+ if (trimmed.length === 0) {
725
+ return "systemInstructions cannot be empty when provided";
570
726
  }
571
- if (options.countryCode !== void 0) {
572
- payload.country_code = options.countryCode;
727
+ if (trimmed.length > 2e3) {
728
+ return "systemInstructions must be 2000 characters or less";
573
729
  }
574
- if (options.includedSources !== void 0) {
575
- payload.included_sources = options.includedSources;
730
+ }
731
+ if (options.dataMaxPrice !== void 0) {
732
+ if (typeof options.dataMaxPrice !== "number" || options.dataMaxPrice <= 0) {
733
+ return "dataMaxPrice must be a positive number";
576
734
  }
577
- if (options.excludedSources !== void 0) {
578
- payload.excluded_sources = options.excludedSources;
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";
579
747
  }
580
- if (options.startDate !== void 0) {
581
- payload.start_date = options.startDate;
748
+ }
749
+ if (options.includedSources !== void 0) {
750
+ if (!Array.isArray(options.includedSources)) {
751
+ return "includedSources must be an array";
582
752
  }
583
- if (options.endDate !== void 0) {
584
- payload.end_date = options.endDate;
753
+ const validation = this.validateSources(options.includedSources);
754
+ if (!validation.valid) {
755
+ return `Invalid includedSources format. Invalid sources: ${validation.invalidSources.join(", ")}.`;
585
756
  }
586
- if (options.fastMode !== void 0) {
587
- payload.fast_mode = options.fastMode;
757
+ }
758
+ if (options.excludedSources !== void 0) {
759
+ if (!Array.isArray(options.excludedSources)) {
760
+ return "excludedSources must be an array";
588
761
  }
589
- const response = await axios.post(`${this.baseUrl}/answer`, payload, {
590
- headers: this.headers
762
+ const validation = this.validateSources(options.excludedSources);
763
+ if (!validation.valid) {
764
+ return `Invalid excludedSources format. Invalid sources: ${validation.invalidSources.join(", ")}.`;
765
+ }
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)
591
806
  });
592
- if (!response.status || response.status < 200 || response.status >= 300) {
807
+ if (!response.ok) {
808
+ const errorData = await response.json().catch(() => ({}));
593
809
  return {
594
810
  success: false,
595
- error: response.data?.error || "Request failed"
811
+ error: errorData.error || `HTTP Error: ${response.status}`
596
812
  };
597
813
  }
598
- 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
+ return {
850
+ success: true,
851
+ ai_tx_id: finalMetadata.ai_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
+ }
861
+ return {
862
+ success: false,
863
+ error: finalMetadata.error || "Unknown error occurred"
864
+ };
599
865
  } catch (e) {
600
866
  return {
601
867
  success: false,
602
- error: e.response?.data?.error || e.message
868
+ error: e.message || "Request failed"
603
869
  };
604
870
  }
605
871
  }
872
+ /**
873
+ * Stream answer using SSE
874
+ */
875
+ async *streamAnswer(payload) {
876
+ try {
877
+ const response = await fetch(`${this.baseUrl}/answer`, {
878
+ method: "POST",
879
+ headers: {
880
+ ...this.headers,
881
+ "Accept": "text/event-stream"
882
+ },
883
+ body: JSON.stringify(payload)
884
+ });
885
+ if (!response.ok) {
886
+ const errorData = await response.json().catch(() => ({}));
887
+ yield { type: "error", error: errorData.error || `HTTP Error: ${response.status}` };
888
+ return;
889
+ }
890
+ const reader = response.body?.getReader();
891
+ const decoder = new TextDecoder();
892
+ let buffer = "";
893
+ if (!reader) {
894
+ yield { type: "error", error: "No response body" };
895
+ return;
896
+ }
897
+ while (true) {
898
+ const { done, value } = await reader.read();
899
+ if (done) break;
900
+ buffer += decoder.decode(value, { stream: true });
901
+ const lines = buffer.split("\n");
902
+ buffer = lines.pop() || "";
903
+ for (const line of lines) {
904
+ if (!line.startsWith("data: ")) continue;
905
+ const dataStr = line.slice(6);
906
+ if (dataStr === "[DONE]") {
907
+ yield { type: "done" };
908
+ continue;
909
+ }
910
+ try {
911
+ const parsed = JSON.parse(dataStr);
912
+ if (parsed.search_results && parsed.success === void 0) {
913
+ yield { type: "search_results", search_results: parsed.search_results };
914
+ } else if (parsed.choices) {
915
+ const delta = parsed.choices[0]?.delta || {};
916
+ const content = delta.content || "";
917
+ const finishReason = parsed.choices[0]?.finish_reason;
918
+ if (content || finishReason) {
919
+ yield { type: "content", content, finish_reason: finishReason };
920
+ }
921
+ } else if (parsed.success !== void 0) {
922
+ yield {
923
+ type: "metadata",
924
+ ai_tx_id: parsed.ai_tx_id,
925
+ original_query: parsed.original_query,
926
+ data_type: parsed.data_type,
927
+ search_results: parsed.search_results,
928
+ search_metadata: parsed.search_metadata,
929
+ ai_usage: parsed.ai_usage,
930
+ cost: parsed.cost
931
+ };
932
+ }
933
+ } catch {
934
+ continue;
935
+ }
936
+ }
937
+ }
938
+ } catch (e) {
939
+ yield { type: "error", error: e.message || "Stream failed" };
940
+ }
941
+ }
942
+ /**
943
+ * Create an error generator for streaming errors
944
+ */
945
+ async *createErrorGenerator(error) {
946
+ yield { type: "error", error };
947
+ }
606
948
  };
607
949
  export {
608
950
  Valyu