@voltagent/core 0.1.71 → 0.1.72

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.d.ts CHANGED
@@ -344,10 +344,13 @@ type InternalAnyWorkflowStep<INPUT, DATA = DangerouslyAllowAny, RESULT = Dangero
344
344
  * Infer the result type from a list of steps
345
345
  * @private - INTERNAL USE ONLY
346
346
  */
347
- type InternalInferWorkflowStepsResult<STEPS extends ReadonlyArray<InternalAnyWorkflowStep<DangerouslyAllowAny, DangerouslyAllowAny, DangerouslyAllowAny>>> = {
348
- [K in keyof STEPS]: Awaited<ReturnType<STEPS[K]["execute"]>>;
347
+ type InternalInferWorkflowStepsResult<STEPS extends ReadonlyArray<InternalAnyWorkflowStep<any, any, any, any, any>>> = {
348
+ [K in keyof STEPS]: ExtractExecuteResult<STEPS[K]>;
349
349
  };
350
350
  type InternalExtractWorkflowInputData<T> = TF.IsUnknown<T> extends true ? BaseMessage | BaseMessage[] | string : TF.IsAny<T> extends true ? BaseMessage | BaseMessage[] | string : T extends z.ZodType ? z.infer<T> : T;
351
+ type ExtractExecuteResult<T> = T extends {
352
+ execute: (...args: any[]) => infer R;
353
+ } ? R extends Promise<infer U> ? U : R : never;
351
354
 
352
355
  type WorkflowStateStatus = "pending" | "running" | "completed" | "failed" | "suspended";
353
356
  type WorkflowState<INPUT, RESULT> = {
@@ -3545,6 +3548,17 @@ interface LibSQLStorageOptions extends MemoryOptions {
3545
3548
  * @default 100
3546
3549
  */
3547
3550
  storageLimit?: number;
3551
+ /**
3552
+ * Number of retry attempts for database operations when encountering busy/locked errors
3553
+ * @default 3
3554
+ */
3555
+ retryAttempts?: number;
3556
+ /**
3557
+ * Base delay in milliseconds before retrying a failed operation
3558
+ * Uses a jittered exponential backoff strategy for better load distribution
3559
+ * @default 50
3560
+ */
3561
+ baseDelayMs?: number;
3548
3562
  }
3549
3563
  /**
3550
3564
  * A LibSQL storage implementation of the Memory and WorkflowMemory interfaces
@@ -3560,6 +3574,8 @@ declare class LibSQLStorage implements Memory {
3560
3574
  private initialized;
3561
3575
  private workflowExtension;
3562
3576
  private logger;
3577
+ private retryAttempts;
3578
+ private baseDelayMs;
3563
3579
  /**
3564
3580
  * Create a new LibSQL storage
3565
3581
  * @param options Configuration options
@@ -3577,6 +3593,20 @@ declare class LibSQLStorage implements Memory {
3577
3593
  * @param data Additional data to log
3578
3594
  */
3579
3595
  private debug;
3596
+ /**
3597
+ * Calculate delay with jitter for better load distribution
3598
+ * @param attempt Current retry attempt number
3599
+ * @returns Delay in milliseconds
3600
+ */
3601
+ private calculateRetryDelay;
3602
+ /**
3603
+ * Execute a database operation with retry strategy
3604
+ * Implements jittered exponential backoff
3605
+ * @param operationFn The operation function to execute
3606
+ * @param operationName Operation name for logging
3607
+ * @returns The result of the operation
3608
+ */
3609
+ private executeWithRetryStrategy;
3580
3610
  /**
3581
3611
  * Initialize workflow tables
3582
3612
  */
@@ -3619,7 +3649,7 @@ declare class LibSQLStorage implements Memory {
3619
3649
  /**
3620
3650
  * Close the database connection
3621
3651
  */
3622
- close(): void;
3652
+ close(): Promise<void>;
3623
3653
  /**
3624
3654
  * Add or update a history entry
3625
3655
  * @param key Entry ID
@@ -4989,10 +5019,10 @@ declare function andWhen<INPUT, DATA, RESULT>({ condition, step, inputSchema, ou
4989
5019
  * @param config - Configuration object with steps array and metadata
4990
5020
  * @returns A workflow step that executes all steps simultaneously and returns their results as an array
4991
5021
  */
4992
- declare function andAll<INPUT, DATA, RESULT, STEPS extends ReadonlyArray<InternalAnyWorkflowStep<INPUT, DATA, RESULT>>, INFERRED_RESULT = InternalInferWorkflowStepsResult<STEPS>>({ steps, ...config }: WorkflowStepParallelAllConfig<STEPS>): {
5022
+ declare function andAll<INPUT, DATA, RESULT, STEPS extends ReadonlyArray<InternalAnyWorkflowStep<INPUT, DATA, RESULT>>>({ steps, ...config }: WorkflowStepParallelAllConfig<STEPS>): {
4993
5023
  type: "parallel-all";
4994
- steps: InternalAnyWorkflowStep<INPUT, DATA, INFERRED_RESULT>[];
4995
- execute: (context: WorkflowExecuteContext<INPUT, DATA, any, any>) => Promise<INFERRED_RESULT>;
5024
+ steps: InternalAnyWorkflowStep<INPUT, DATA, InternalInferWorkflowStepsResult<STEPS>>[];
5025
+ execute: (context: WorkflowExecuteContext<INPUT, DATA, any, any>) => Promise<InternalInferWorkflowStepsResult<STEPS>>;
4996
5026
  id: string;
4997
5027
  name: string;
4998
5028
  purpose: string;
@@ -5043,12 +5073,12 @@ declare function andAll<INPUT, DATA, RESULT, STEPS extends ReadonlyArray<Interna
5043
5073
  * @param config - Configuration object with steps array and metadata
5044
5074
  * @returns A workflow step that executes all steps simultaneously and returns the result from the first step to complete
5045
5075
  */
5046
- declare function andRace<INPUT, DATA, RESULT, STEPS extends ReadonlyArray<InternalAnyWorkflowStep<INPUT, DATA, RESULT>>, INFERRED_RESULT = InternalInferWorkflowStepsResult<STEPS>[number]>({ steps, ...config }: InternalWorkflowStepConfig<{
5076
+ declare function andRace<INPUT, DATA, RESULT, STEPS extends ReadonlyArray<InternalAnyWorkflowStep<INPUT, DATA, RESULT>>>({ steps, ...config }: InternalWorkflowStepConfig<{
5047
5077
  steps: STEPS;
5048
5078
  }>): {
5049
5079
  type: "parallel-race";
5050
- steps: InternalAnyWorkflowStep<INPUT, DATA, INFERRED_RESULT>[];
5051
- execute: (context: WorkflowExecuteContext<INPUT, DATA, any, any>) => Promise<INFERRED_RESULT>;
5080
+ steps: InternalAnyWorkflowStep<INPUT, DATA, InternalInferWorkflowStepsResult<STEPS>[number]>[];
5081
+ execute: (context: WorkflowExecuteContext<INPUT, DATA, any, any>) => Promise<InternalInferWorkflowStepsResult<STEPS>[number]>;
5052
5082
  id: string;
5053
5083
  name: string;
5054
5084
  purpose: string;
package/dist/index.js CHANGED
@@ -4669,18 +4669,24 @@ var LibSQLStorage = class {
4669
4669
  initialized;
4670
4670
  workflowExtension;
4671
4671
  logger;
4672
+ retryAttempts;
4673
+ baseDelayMs;
4672
4674
  /**
4673
4675
  * Create a new LibSQL storage
4674
4676
  * @param options Configuration options
4675
4677
  */
4676
4678
  constructor(options) {
4677
4679
  this.logger = new LoggerProxy({ component: "libsql-storage" });
4680
+ this.retryAttempts = options.retryAttempts ?? 3;
4681
+ this.baseDelayMs = options.baseDelayMs ?? 50;
4678
4682
  this.options = {
4679
4683
  storageLimit: options.storageLimit || 100,
4680
4684
  tablePrefix: options.tablePrefix || "voltagent_memory",
4681
4685
  debug: options.debug || false,
4682
4686
  url: this.normalizeUrl(options.url),
4683
- authToken: options.authToken
4687
+ authToken: options.authToken,
4688
+ retryAttempts: this.retryAttempts,
4689
+ baseDelayMs: this.baseDelayMs
4684
4690
  };
4685
4691
  this.client = (0, import_client.createClient)({
4686
4692
  url: this.options.url,
@@ -4726,6 +4732,50 @@ var LibSQLStorage = class {
4726
4732
  this.logger.debug(`${message}`, data || "");
4727
4733
  }
4728
4734
  }
4735
+ /**
4736
+ * Calculate delay with jitter for better load distribution
4737
+ * @param attempt Current retry attempt number
4738
+ * @returns Delay in milliseconds
4739
+ */
4740
+ calculateRetryDelay(attempt) {
4741
+ const exponentialDelay = this.baseDelayMs * 2 ** (attempt - 1);
4742
+ const jitterFactor = 0.2 + Math.random() * 0.2;
4743
+ const delayWithJitter = exponentialDelay * (1 + jitterFactor);
4744
+ return Math.min(delayWithJitter, 2e3);
4745
+ }
4746
+ /**
4747
+ * Execute a database operation with retry strategy
4748
+ * Implements jittered exponential backoff
4749
+ * @param operationFn The operation function to execute
4750
+ * @param operationName Operation name for logging
4751
+ * @returns The result of the operation
4752
+ */
4753
+ async executeWithRetryStrategy(operationFn, operationName) {
4754
+ let attempt = 0;
4755
+ while (attempt < this.retryAttempts) {
4756
+ attempt++;
4757
+ try {
4758
+ return await operationFn();
4759
+ } catch (error) {
4760
+ const isBusyError = error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked") || error.code === "SQLITE_BUSY");
4761
+ if (!isBusyError || attempt >= this.retryAttempts) {
4762
+ this.debug(`Operation failed: ${operationName}`, {
4763
+ attempt,
4764
+ error: error.message
4765
+ });
4766
+ throw error;
4767
+ }
4768
+ const delay = this.calculateRetryDelay(attempt);
4769
+ this.debug(`Retrying ${operationName}`, {
4770
+ attempt,
4771
+ remainingAttempts: this.retryAttempts - attempt,
4772
+ delay
4773
+ });
4774
+ await new Promise((resolve) => setTimeout(resolve, delay));
4775
+ }
4776
+ }
4777
+ throw new Error(`Max retry attempts (${this.retryAttempts}) exceeded for ${operationName}`);
4778
+ }
4729
4779
  /**
4730
4780
  * Initialize workflow tables
4731
4781
  */
@@ -4744,6 +4794,20 @@ var LibSQLStorage = class {
4744
4794
  * @returns Promise that resolves when initialization is complete
4745
4795
  */
4746
4796
  async initializeDatabase() {
4797
+ if (this.options.url.startsWith("file:") || this.options.url.includes(":memory:")) {
4798
+ try {
4799
+ await this.client.execute("PRAGMA journal_mode=WAL;");
4800
+ this.debug("PRAGMA journal_mode=WAL set.");
4801
+ } catch (err) {
4802
+ this.debug("Failed to set PRAGMA journal_mode=WAL.", err);
4803
+ }
4804
+ try {
4805
+ await this.client.execute("PRAGMA busy_timeout = 5000;");
4806
+ this.debug("PRAGMA busy_timeout=5000 set.");
4807
+ } catch (err) {
4808
+ this.debug("Failed to set PRAGMA busy_timeout.", err);
4809
+ }
4810
+ }
4747
4811
  const conversationsTableName = `${this.options.tablePrefix}_conversations`;
4748
4812
  await this.client.execute(`
4749
4813
  CREATE TABLE IF NOT EXISTS ${conversationsTableName} (
@@ -5015,7 +5079,7 @@ var LibSQLStorage = class {
5015
5079
  await debugDelay();
5016
5080
  const tableName = `${this.options.tablePrefix}_messages`;
5017
5081
  const contentString = JSON.stringify(message.content);
5018
- try {
5082
+ await this.executeWithRetryStrategy(async () => {
5019
5083
  await this.client.execute({
5020
5084
  sql: `INSERT INTO ${tableName} (conversation_id, message_id, role, content, type, created_at)
5021
5085
  VALUES (?, ?, ?, ?, ?, ?)`,
@@ -5034,10 +5098,7 @@ var LibSQLStorage = class {
5034
5098
  } catch (pruneError) {
5035
5099
  this.debug("Error pruning old messages:", pruneError);
5036
5100
  }
5037
- } catch (error) {
5038
- this.debug("Error adding message:", error);
5039
- throw new Error("Failed to add message to LibSQL database");
5040
- }
5101
+ }, `addMessage[${message.id}]`);
5041
5102
  }
5042
5103
  /**
5043
5104
  * Prune old messages to respect storage limit
@@ -5110,7 +5171,11 @@ var LibSQLStorage = class {
5110
5171
  /**
5111
5172
  * Close the database connection
5112
5173
  */
5113
- close() {
5174
+ async close() {
5175
+ try {
5176
+ await this.initialized;
5177
+ } catch {
5178
+ }
5114
5179
  this.client.close();
5115
5180
  }
5116
5181
  /**
@@ -5370,7 +5435,7 @@ var LibSQLStorage = class {
5370
5435
  const now = (/* @__PURE__ */ new Date()).toISOString();
5371
5436
  const metadataString = JSON.stringify(conversation.metadata);
5372
5437
  const tableName = `${this.options.tablePrefix}_conversations`;
5373
- try {
5438
+ return await this.executeWithRetryStrategy(async () => {
5374
5439
  await this.client.execute({
5375
5440
  sql: `INSERT INTO ${tableName} (id, resource_id, user_id, title, metadata, created_at, updated_at)
5376
5441
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
@@ -5393,10 +5458,7 @@ var LibSQLStorage = class {
5393
5458
  createdAt: now,
5394
5459
  updatedAt: now
5395
5460
  };
5396
- } catch (error) {
5397
- this.debug("Error creating conversation:", error);
5398
- throw new Error("Failed to create conversation in LibSQL database");
5399
- }
5461
+ }, `createConversation[${conversation.id}]`);
5400
5462
  }
5401
5463
  async getConversation(id) {
5402
5464
  await this.initialized;