@reverbia/sdk 1.0.0-next.20251215193957 → 1.0.0-next.20251217134403

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.
@@ -1419,6 +1419,7 @@ Please inform the user about this issue and try to help them alternatively.`
1419
1419
  toolExecution: toolExecutionResult
1420
1420
  };
1421
1421
  }
1422
+ let sseError = null;
1422
1423
  const sseResult = await client.sse.post({
1423
1424
  baseUrl,
1424
1425
  url: "/api/v1/chat/completions",
@@ -1432,23 +1433,52 @@ Please inform the user about this issue and try to help them alternatively.`
1432
1433
  Authorization: `Bearer ${token}`,
1433
1434
  ...headers
1434
1435
  },
1435
- signal: abortController.signal
1436
+ signal: abortController.signal,
1437
+ sseMaxRetryAttempts: 1,
1438
+ onSseError: (error) => {
1439
+ sseError = error instanceof Error ? error : new Error(String(error));
1440
+ }
1436
1441
  });
1437
1442
  const accumulator = createStreamAccumulator();
1438
- for await (const chunk of sseResult.stream) {
1439
- if (isDoneMarker(chunk)) {
1440
- continue;
1441
- }
1442
- if (chunk && typeof chunk === "object") {
1443
- const contentDelta = processStreamingChunk(
1444
- chunk,
1445
- accumulator
1446
- );
1447
- if (contentDelta) {
1448
- if (onData) onData(contentDelta);
1449
- if (globalOnData) globalOnData(contentDelta);
1443
+ try {
1444
+ for await (const chunk of sseResult.stream) {
1445
+ if (isDoneMarker(chunk)) {
1446
+ continue;
1450
1447
  }
1448
+ if (chunk && typeof chunk === "object") {
1449
+ const contentDelta = processStreamingChunk(
1450
+ chunk,
1451
+ accumulator
1452
+ );
1453
+ if (contentDelta) {
1454
+ if (onData) onData(contentDelta);
1455
+ if (globalOnData) globalOnData(contentDelta);
1456
+ }
1457
+ }
1458
+ }
1459
+ } catch (streamErr) {
1460
+ if (isAbortError(streamErr) || abortController.signal.aborted) {
1461
+ setIsLoading(false);
1462
+ const partialCompletion = buildCompletionResponse(accumulator);
1463
+ return {
1464
+ data: partialCompletion,
1465
+ error: "Request aborted",
1466
+ toolExecution: toolExecutionResult
1467
+ };
1451
1468
  }
1469
+ throw streamErr;
1470
+ }
1471
+ if (abortController.signal.aborted) {
1472
+ setIsLoading(false);
1473
+ const partialCompletion = buildCompletionResponse(accumulator);
1474
+ return {
1475
+ data: partialCompletion,
1476
+ error: "Request aborted",
1477
+ toolExecution: toolExecutionResult
1478
+ };
1479
+ }
1480
+ if (sseError) {
1481
+ throw sseError;
1452
1482
  }
1453
1483
  const completion = buildCompletionResponse(accumulator);
1454
1484
  setIsLoading(false);
@@ -1670,7 +1700,8 @@ function messageToStored(message) {
1670
1700
  embeddingModel: message.embeddingModel,
1671
1701
  usage: message.usage,
1672
1702
  sources: message.sources,
1673
- responseDuration: message.responseDuration
1703
+ responseDuration: message.responseDuration,
1704
+ wasStopped: message.wasStopped
1674
1705
  };
1675
1706
  }
1676
1707
  function conversationToStored(conversation) {
@@ -1759,6 +1790,7 @@ async function createMessageOp(ctx, opts) {
1759
1790
  msg._setRaw("response_duration", opts.responseDuration);
1760
1791
  if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
1761
1792
  if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
1793
+ if (opts.wasStopped) msg._setRaw("was_stopped", opts.wasStopped);
1762
1794
  });
1763
1795
  });
1764
1796
  return messageToStored(created);
@@ -2049,6 +2081,45 @@ function useChatStorage(options) {
2049
2081
  });
2050
2082
  const responseDuration = (Date.now() - startTime) / 1e3;
2051
2083
  if (result.error || !result.data) {
2084
+ const abortedResult = result;
2085
+ if (abortedResult.error === "Request aborted") {
2086
+ const assistantContent2 = abortedResult.data?.choices?.[0]?.message?.content?.map((part) => part.text || "").join("") || "";
2087
+ const responseModel = abortedResult.data?.model || model || "";
2088
+ let storedAssistantMessage2;
2089
+ try {
2090
+ storedAssistantMessage2 = await createMessageOp(storageCtx, {
2091
+ conversationId: convId,
2092
+ role: "assistant",
2093
+ content: assistantContent2,
2094
+ model: responseModel,
2095
+ usage: convertUsageToStored(abortedResult.data?.usage),
2096
+ responseDuration,
2097
+ wasStopped: true
2098
+ });
2099
+ const completionData = abortedResult.data || {
2100
+ id: `aborted-${Date.now()}`,
2101
+ model: responseModel,
2102
+ choices: [{
2103
+ index: 0,
2104
+ message: {
2105
+ role: "assistant",
2106
+ content: [{ type: "text", text: assistantContent2 }]
2107
+ },
2108
+ finish_reason: "stop"
2109
+ }],
2110
+ usage: void 0
2111
+ };
2112
+ return {
2113
+ data: completionData,
2114
+ error: null,
2115
+ // Treat as success to the caller
2116
+ toolExecution: abortedResult.toolExecution,
2117
+ userMessage: storedUserMessage,
2118
+ assistantMessage: storedAssistantMessage2
2119
+ };
2120
+ } catch (err) {
2121
+ }
2122
+ }
2052
2123
  return {
2053
2124
  data: null,
2054
2125
  error: result.error || "No response data received",
@@ -2120,8 +2191,9 @@ function useChatStorage(options) {
2120
2191
 
2121
2192
  // src/lib/chatStorage/schema.ts
2122
2193
  import { appSchema, tableSchema } from "@nozbe/watermelondb";
2194
+ import { schemaMigrations, addColumns } from "@nozbe/watermelondb/Schema/migrations";
2123
2195
  var chatStorageSchema = appSchema({
2124
- version: 1,
2196
+ version: 2,
2125
2197
  tables: [
2126
2198
  tableSchema({
2127
2199
  name: "history",
@@ -2144,7 +2216,8 @@ var chatStorageSchema = appSchema({
2144
2216
  // JSON stringified ChatCompletionUsage
2145
2217
  { name: "sources", type: "string", isOptional: true },
2146
2218
  // JSON stringified SearchSource[]
2147
- { name: "response_duration", type: "number", isOptional: true }
2219
+ { name: "response_duration", type: "number", isOptional: true },
2220
+ { name: "was_stopped", type: "boolean", isOptional: true }
2148
2221
  ]
2149
2222
  }),
2150
2223
  tableSchema({
@@ -2159,6 +2232,21 @@ var chatStorageSchema = appSchema({
2159
2232
  })
2160
2233
  ]
2161
2234
  });
2235
+ var chatStorageMigrations = schemaMigrations({
2236
+ migrations: [
2237
+ {
2238
+ toVersion: 2,
2239
+ steps: [
2240
+ addColumns({
2241
+ table: "history",
2242
+ columns: [
2243
+ { name: "was_stopped", type: "boolean", isOptional: true }
2244
+ ]
2245
+ })
2246
+ ]
2247
+ }
2248
+ ]
2249
+ });
2162
2250
 
2163
2251
  // src/lib/chatStorage/models.ts
2164
2252
  import { Model } from "@nozbe/watermelondb";
@@ -2242,6 +2330,10 @@ var Message = class extends Model {
2242
2330
  const value = this._getRaw("response_duration");
2243
2331
  return value !== null && value !== void 0 ? value : void 0;
2244
2332
  }
2333
+ /** Whether the message generation was stopped by the user */
2334
+ get wasStopped() {
2335
+ return this._getRaw("was_stopped");
2336
+ }
2245
2337
  };
2246
2338
  Message.table = "history";
2247
2339
  Message.associations = {
@@ -3444,8 +3536,180 @@ var Memory = class extends Model2 {
3444
3536
  };
3445
3537
  Memory.table = "memories";
3446
3538
 
3539
+ // src/react/useSettings.ts
3540
+ import { useCallback as useCallback4, useState as useState4, useMemo as useMemo3, useEffect as useEffect2 } from "react";
3541
+
3542
+ // src/lib/settingsStorage/operations.ts
3543
+ import { Q as Q3 } from "@nozbe/watermelondb";
3544
+ function modelPreferenceToStored(preference) {
3545
+ return {
3546
+ uniqueId: preference.id,
3547
+ walletAddress: preference.walletAddress,
3548
+ models: preference.models
3549
+ };
3550
+ }
3551
+ async function getModelPreferenceOp(ctx, walletAddress) {
3552
+ const results = await ctx.modelPreferencesCollection.query(Q3.where("wallet_address", walletAddress)).fetch();
3553
+ return results.length > 0 ? modelPreferenceToStored(results[0]) : null;
3554
+ }
3555
+ async function setModelPreferenceOp(ctx, walletAddress, models) {
3556
+ const result = await ctx.database.write(async () => {
3557
+ const results = await ctx.modelPreferencesCollection.query(Q3.where("wallet_address", walletAddress)).fetch();
3558
+ if (results.length > 0) {
3559
+ const preference = results[0];
3560
+ await preference.update((pref) => {
3561
+ if (models !== void 0) {
3562
+ pref._setRaw("models", models || null);
3563
+ }
3564
+ });
3565
+ return preference;
3566
+ }
3567
+ return await ctx.modelPreferencesCollection.create((pref) => {
3568
+ pref._setRaw("wallet_address", walletAddress);
3569
+ if (models) pref._setRaw("models", models);
3570
+ });
3571
+ });
3572
+ return modelPreferenceToStored(result);
3573
+ }
3574
+ async function deleteModelPreferenceOp(ctx, walletAddress) {
3575
+ const results = await ctx.modelPreferencesCollection.query(Q3.where("wallet_address", walletAddress)).fetch();
3576
+ if (results.length === 0) return false;
3577
+ await ctx.database.write(async () => {
3578
+ await results[0].destroyPermanently();
3579
+ });
3580
+ return true;
3581
+ }
3582
+
3583
+ // src/react/useSettings.ts
3584
+ function useSettings(options) {
3585
+ const { database, walletAddress } = options;
3586
+ const [modelPreference, setModelPreferenceState] = useState4(null);
3587
+ const [isLoading, setIsLoading] = useState4(false);
3588
+ const modelPreferencesCollection = useMemo3(
3589
+ () => database.get("modelPreferences"),
3590
+ [database]
3591
+ );
3592
+ const storageCtx = useMemo3(
3593
+ () => ({
3594
+ database,
3595
+ modelPreferencesCollection
3596
+ }),
3597
+ [database, modelPreferencesCollection]
3598
+ );
3599
+ const getModelPreference = useCallback4(
3600
+ async (address) => {
3601
+ try {
3602
+ if (!address) throw new Error("Wallet address is required");
3603
+ const result = await getModelPreferenceOp(storageCtx, address);
3604
+ return result;
3605
+ } catch (error) {
3606
+ throw new Error(
3607
+ error instanceof Error ? error.message : "An unknown error occurred"
3608
+ );
3609
+ }
3610
+ },
3611
+ [storageCtx]
3612
+ );
3613
+ const setModelPreference = useCallback4(
3614
+ async (address, models) => {
3615
+ try {
3616
+ if (!address) throw new Error("Wallet address is required");
3617
+ const result = await setModelPreferenceOp(storageCtx, address, models);
3618
+ if (walletAddress && address === walletAddress) {
3619
+ setModelPreferenceState(result);
3620
+ }
3621
+ return result;
3622
+ } catch (error) {
3623
+ throw new Error(
3624
+ error instanceof Error ? error.message : "An unknown error occurred"
3625
+ );
3626
+ }
3627
+ },
3628
+ [storageCtx, walletAddress]
3629
+ );
3630
+ const deleteModelPreference = useCallback4(
3631
+ async (address) => {
3632
+ try {
3633
+ if (!address) throw new Error("Wallet address is required");
3634
+ const deleted = await deleteModelPreferenceOp(storageCtx, address);
3635
+ if (deleted && walletAddress && address === walletAddress) {
3636
+ setModelPreferenceState(null);
3637
+ }
3638
+ return deleted;
3639
+ } catch (error) {
3640
+ throw new Error(
3641
+ error instanceof Error ? error.message : "An unknown error occurred"
3642
+ );
3643
+ }
3644
+ },
3645
+ [storageCtx, walletAddress]
3646
+ );
3647
+ useEffect2(() => {
3648
+ if (!walletAddress) {
3649
+ setModelPreferenceState(null);
3650
+ return;
3651
+ }
3652
+ let cancelled = false;
3653
+ const loadPreference = async () => {
3654
+ setIsLoading(true);
3655
+ try {
3656
+ const preference = await getModelPreference(walletAddress);
3657
+ if (!cancelled) {
3658
+ setModelPreferenceState(preference);
3659
+ }
3660
+ } finally {
3661
+ if (!cancelled) {
3662
+ setIsLoading(false);
3663
+ }
3664
+ }
3665
+ };
3666
+ loadPreference();
3667
+ return () => {
3668
+ cancelled = true;
3669
+ };
3670
+ }, [walletAddress, getModelPreference]);
3671
+ return {
3672
+ modelPreference,
3673
+ isLoading,
3674
+ getModelPreference,
3675
+ setModelPreference,
3676
+ deleteModelPreference
3677
+ };
3678
+ }
3679
+
3680
+ // src/lib/settingsStorage/schema.ts
3681
+ import { appSchema as appSchema3, tableSchema as tableSchema3 } from "@nozbe/watermelondb";
3682
+ var settingsStorageSchema = appSchema3({
3683
+ version: 1,
3684
+ tables: [
3685
+ tableSchema3({
3686
+ name: "modelPreferences",
3687
+ columns: [
3688
+ { name: "wallet_address", type: "string", isIndexed: true },
3689
+ { name: "models", type: "string", isOptional: true }
3690
+ // stored as JSON stringified ModelPreference[]
3691
+ ]
3692
+ })
3693
+ ]
3694
+ });
3695
+
3696
+ // src/lib/settingsStorage/models.ts
3697
+ import { Model as Model3 } from "@nozbe/watermelondb";
3698
+ var ModelPreference = class extends Model3 {
3699
+ /** User's wallet address */
3700
+ get walletAddress() {
3701
+ return this._getRaw("wallet_address");
3702
+ }
3703
+ /** Preferred model identifier */
3704
+ get models() {
3705
+ const value = this._getRaw("models");
3706
+ return value ? value : void 0;
3707
+ }
3708
+ };
3709
+ ModelPreference.table = "modelPreferences";
3710
+
3447
3711
  // src/react/usePdf.ts
3448
- import { useCallback as useCallback4, useState as useState4 } from "react";
3712
+ import { useCallback as useCallback5, useState as useState5 } from "react";
3449
3713
 
3450
3714
  // src/lib/pdf.ts
3451
3715
  import * as pdfjs from "pdfjs-dist";
@@ -3498,9 +3762,9 @@ async function convertPdfToImages(pdfDataUrl) {
3498
3762
  // src/react/usePdf.ts
3499
3763
  var PDF_MIME_TYPE = "application/pdf";
3500
3764
  function usePdf() {
3501
- const [isProcessing, setIsProcessing] = useState4(false);
3502
- const [error, setError] = useState4(null);
3503
- const extractPdfContext = useCallback4(
3765
+ const [isProcessing, setIsProcessing] = useState5(false);
3766
+ const [error, setError] = useState5(null);
3767
+ const extractPdfContext = useCallback5(
3504
3768
  async (files) => {
3505
3769
  setIsProcessing(true);
3506
3770
  setError(null);
@@ -3547,12 +3811,12 @@ ${text}`;
3547
3811
  }
3548
3812
 
3549
3813
  // src/react/useOCR.ts
3550
- import { useCallback as useCallback5, useState as useState5 } from "react";
3814
+ import { useCallback as useCallback6, useState as useState6 } from "react";
3551
3815
  import Tesseract from "tesseract.js";
3552
3816
  function useOCR() {
3553
- const [isProcessing, setIsProcessing] = useState5(false);
3554
- const [error, setError] = useState5(null);
3555
- const extractOCRContext = useCallback5(
3817
+ const [isProcessing, setIsProcessing] = useState6(false);
3818
+ const [error, setError] = useState6(null);
3819
+ const extractOCRContext = useCallback6(
3556
3820
  async (files) => {
3557
3821
  setIsProcessing(true);
3558
3822
  setError(null);
@@ -3638,22 +3902,22 @@ ${text}`;
3638
3902
  }
3639
3903
 
3640
3904
  // src/react/useModels.ts
3641
- import { useCallback as useCallback6, useEffect as useEffect2, useRef as useRef3, useState as useState6 } from "react";
3905
+ import { useCallback as useCallback7, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
3642
3906
  function useModels(options = {}) {
3643
3907
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
3644
- const [models, setModels] = useState6([]);
3645
- const [isLoading, setIsLoading] = useState6(false);
3646
- const [error, setError] = useState6(null);
3908
+ const [models, setModels] = useState7([]);
3909
+ const [isLoading, setIsLoading] = useState7(false);
3910
+ const [error, setError] = useState7(null);
3647
3911
  const getTokenRef = useRef3(getToken);
3648
3912
  const baseUrlRef = useRef3(baseUrl);
3649
3913
  const providerRef = useRef3(provider);
3650
3914
  const abortControllerRef = useRef3(null);
3651
- useEffect2(() => {
3915
+ useEffect3(() => {
3652
3916
  getTokenRef.current = getToken;
3653
3917
  baseUrlRef.current = baseUrl;
3654
3918
  providerRef.current = provider;
3655
3919
  });
3656
- useEffect2(() => {
3920
+ useEffect3(() => {
3657
3921
  return () => {
3658
3922
  if (abortControllerRef.current) {
3659
3923
  abortControllerRef.current.abort();
@@ -3661,7 +3925,7 @@ function useModels(options = {}) {
3661
3925
  }
3662
3926
  };
3663
3927
  }, []);
3664
- const fetchModels = useCallback6(async () => {
3928
+ const fetchModels = useCallback7(async () => {
3665
3929
  if (abortControllerRef.current) {
3666
3930
  abortControllerRef.current.abort();
3667
3931
  }
@@ -3719,12 +3983,12 @@ function useModels(options = {}) {
3719
3983
  }
3720
3984
  }
3721
3985
  }, []);
3722
- const refetch = useCallback6(async () => {
3986
+ const refetch = useCallback7(async () => {
3723
3987
  setModels([]);
3724
3988
  await fetchModels();
3725
3989
  }, [fetchModels]);
3726
3990
  const hasFetchedRef = useRef3(false);
3727
- useEffect2(() => {
3991
+ useEffect3(() => {
3728
3992
  if (autoFetch && !hasFetchedRef.current) {
3729
3993
  hasFetchedRef.current = true;
3730
3994
  fetchModels();
@@ -3742,15 +4006,15 @@ function useModels(options = {}) {
3742
4006
  }
3743
4007
 
3744
4008
  // src/react/useSearch.ts
3745
- import { useCallback as useCallback7, useEffect as useEffect3, useRef as useRef4, useState as useState7 } from "react";
4009
+ import { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef4, useState as useState8 } from "react";
3746
4010
  function useSearch(options = {}) {
3747
4011
  const { getToken, baseUrl = BASE_URL, onError } = options;
3748
- const [isLoading, setIsLoading] = useState7(false);
3749
- const [results, setResults] = useState7(null);
3750
- const [response, setResponse] = useState7(null);
3751
- const [error, setError] = useState7(null);
4012
+ const [isLoading, setIsLoading] = useState8(false);
4013
+ const [results, setResults] = useState8(null);
4014
+ const [response, setResponse] = useState8(null);
4015
+ const [error, setError] = useState8(null);
3752
4016
  const abortControllerRef = useRef4(null);
3753
- useEffect3(() => {
4017
+ useEffect4(() => {
3754
4018
  return () => {
3755
4019
  if (abortControllerRef.current) {
3756
4020
  abortControllerRef.current.abort();
@@ -3758,7 +4022,7 @@ function useSearch(options = {}) {
3758
4022
  }
3759
4023
  };
3760
4024
  }, []);
3761
- const search = useCallback7(
4025
+ const search = useCallback8(
3762
4026
  async (query, searchOptions = {}) => {
3763
4027
  if (abortControllerRef.current) {
3764
4028
  abortControllerRef.current.abort();
@@ -3826,12 +4090,12 @@ function useSearch(options = {}) {
3826
4090
  }
3827
4091
 
3828
4092
  // src/react/useImageGeneration.ts
3829
- import { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef5, useState as useState8 } from "react";
4093
+ import { useCallback as useCallback9, useEffect as useEffect5, useRef as useRef5, useState as useState9 } from "react";
3830
4094
  function useImageGeneration(options = {}) {
3831
4095
  const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
3832
- const [isLoading, setIsLoading] = useState8(false);
4096
+ const [isLoading, setIsLoading] = useState9(false);
3833
4097
  const abortControllerRef = useRef5(null);
3834
- useEffect4(() => {
4098
+ useEffect5(() => {
3835
4099
  return () => {
3836
4100
  if (abortControllerRef.current) {
3837
4101
  abortControllerRef.current.abort();
@@ -3839,13 +4103,13 @@ function useImageGeneration(options = {}) {
3839
4103
  }
3840
4104
  };
3841
4105
  }, []);
3842
- const stop = useCallback8(() => {
4106
+ const stop = useCallback9(() => {
3843
4107
  if (abortControllerRef.current) {
3844
4108
  abortControllerRef.current.abort();
3845
4109
  abortControllerRef.current = null;
3846
4110
  }
3847
4111
  }, []);
3848
- const generateImage = useCallback8(
4112
+ const generateImage = useCallback9(
3849
4113
  async (args) => {
3850
4114
  if (abortControllerRef.current) {
3851
4115
  abortControllerRef.current.abort();
@@ -3966,6 +4230,8 @@ export {
3966
4230
  Message as ChatMessage,
3967
4231
  DEFAULT_TOOL_SELECTOR_MODEL,
3968
4232
  Memory as StoredMemoryModel,
4233
+ ModelPreference as StoredModelPreferenceModel,
4234
+ chatStorageMigrations,
3969
4235
  chatStorageSchema,
3970
4236
  createMemoryContextSystemMessage,
3971
4237
  decryptData,
@@ -3981,6 +4247,7 @@ export {
3981
4247
  memoryStorageSchema,
3982
4248
  requestEncryptionKey,
3983
4249
  selectTool,
4250
+ settingsStorageSchema,
3984
4251
  useChat,
3985
4252
  useChatStorage,
3986
4253
  useEncryption,
@@ -3989,5 +4256,6 @@ export {
3989
4256
  useModels,
3990
4257
  useOCR,
3991
4258
  usePdf,
3992
- useSearch
4259
+ useSearch,
4260
+ useSettings
3993
4261
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reverbia/sdk",
3
- "version": "1.0.0-next.20251215193957",
3
+ "version": "1.0.0-next.20251217134403",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",