@mastra/libsql 1.27.1-alpha.0 → 1.27.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.
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.27.1-alpha.0"
6
+ version: "1.27.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.27.1-alpha.0",
2
+ "version": "1.27.1",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -456,8 +456,10 @@ const agent = new Agent({
456
456
  indexConfig: {
457
457
  type: 'hnsw', // Use HNSW for better performance
458
458
  metric: 'dotproduct', // Best for OpenAI embeddings
459
- m: 16, // Number of bi-directional links (default: 16)
460
- efConstruction: 64, // Size of candidate list during construction (default: 64)
459
+ hnsw: {
460
+ m: 16, // Number of bi-directional links (default: 16)
461
+ efConstruction: 64, // Size of candidate list during construction (default: 64)
462
+ },
461
463
  },
462
464
  },
463
465
  },
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Memory class
6
6
 
7
- The `Memory` class provides a reliable system for managing conversation history and thread-based message storage in Mastra. It enables persistent storage of conversations, semantic search capabilities, and efficient message retrieval. You must configure a storage provider for conversation history, and if you enable semantic recall you will also need to provide a vector store and embedder.
7
+ The `Memory` class provides a reliable system for managing conversation history and thread-based message storage in Mastra. It enables persistent storage of conversations, semantic search capabilities, and efficient message retrieval. You must configure a storage provider for conversation history, and if you enable semantic recall you will also need to provide a vector store and, unless that vector store embeds text itself, an embedder.
8
8
 
9
9
  ## Usage example
10
10
 
@@ -31,11 +31,11 @@ export const agent = new Agent({
31
31
 
32
32
  ## Constructor parameters
33
33
 
34
- **storage** (`MastraCompositeStore`): Storage implementation for persisting memory data. Defaults to new DefaultStorage({ config: { url: "file:memory.db" } }) if not provided.
34
+ **storage** (`MastraCompositeStore`): Storage implementation for persisting memory data. Optional: when omitted, an agent uses the storage of the Mastra instance it belongs to. If neither is configured, accessing memory.storage throws Memory requires a storage provider to function.
35
35
 
36
36
  **vector** (`MastraVector | false`): Vector store for semantic search capabilities. Set to false to disable vector operations.
37
37
 
38
- **embedder** (`EmbeddingModel<string> | EmbeddingModelV2<string>`): Embedder instance for vector embeddings. Required when semantic recall is enabled.
38
+ **embedder** (`string | EmbeddingModel<string> | EmbeddingModelV2<string>`): Embedder for vector embeddings, as a model-router ID string (for example "openai/text-embedding-3-small"), an EmbeddingModel, or an EmbeddingModelV2. Required when semantic recall is enabled and the vector store doesn't generate embeddings itself (vector.isSelfEmbedding is false).
39
39
 
40
40
  **embeddingDimension** (`number`): Output dimension of the embedder, as a positive integer. When set, Memory uses it to name the semantic recall index instead of embedding a probe string on first use. It must match the vectors the embedder returns: a mismatch throws a MASTRA_MEMORY_EMBEDDING_DIMENSION_MISMATCH error before any index is created. See Setting the embedding dimension.
41
41
 
@@ -49,7 +49,7 @@ export const agent = new Agent({
49
49
 
50
50
  **options.retainFullInput** (`boolean`): When true, the request input is processed exactly as supplied instead of being filtered against stored history. Use this when you assemble the input yourself and need the message sequence preserved. Stored history is still loaded underneath, and every input message that isn't already stored is saved to the thread, including few-shot examples. Can be set per call on memory.options or agent-wide in the memory constructor options.
51
51
 
52
- **options.semanticRecall** (`boolean | { topK: number; messageRange: number | { before: number; after: number }; scope?: 'thread' | 'resource' }`): Enable semantic search in message history. Can be a boolean or an object with configuration options. When enabled, requires both vector store and embedder to be configured. Default topK is 4, default messageRange is {before: 1, after: 1}.
52
+ **options.semanticRecall** (`boolean | { topK: number; messageRange: number | { before: number; after: number }; scope?: 'thread' | 'resource' }`): Enable semantic search in message history. Can be a boolean or an object with configuration options. When enabled, requires a vector store, and an embedder unless the vector store embeds text itself (isSelfEmbedding). Default topK is 4, default messageRange is {before: 1, after: 1}.
53
53
 
54
54
  **options.workingMemory** (`WorkingMemory`): Configuration for working memory feature. Can be { enabled: boolean; template?: string; schema?: ZodObject\<any> | JSONSchema7; scope?: 'thread' | 'resource' } or { enabled: boolean } to disable.
55
55
 
@@ -128,8 +128,10 @@ export const agent = new Agent({
128
128
  indexConfig: {
129
129
  type: 'hnsw', // Use HNSW for better performance
130
130
  metric: 'dotproduct', // Optimal for OpenAI embeddings
131
- m: 16, // Number of bi-directional links
132
- efConstruction: 64, // Construction-time candidate list size
131
+ hnsw: {
132
+ m: 16, // Number of bi-directional links
133
+ efConstruction: 64, // Construction-time candidate list size
134
+ },
133
135
  },
134
136
  },
135
137
  workingMemory: {
package/dist/index.cjs CHANGED
@@ -501,7 +501,7 @@ const createNumericOperator = (symbol) => {
501
501
  sql: `CAST(json_extract(metadata, ${jsonPath}) AS NUMERIC) ${symbol} ?`,
502
502
  needsValue: true
503
503
  };
504
- else return {
504
+ return {
505
505
  sql: `CAST(json_extract(metadata, ${jsonPath}) AS TEXT) ${symbol} ?`,
506
506
  needsValue: true
507
507
  };
@@ -528,7 +528,8 @@ function buildElemMatchConditions(value) {
528
528
  sql: sql.replace(pattern, `json_extract(elem.value, '$.${jsonPath}')`),
529
529
  values
530
530
  };
531
- } else return {
531
+ }
532
+ return {
532
533
  sql: `json_extract(elem.value, '$.${parseJsonPathKey(field)}') = ?`,
533
534
  values: [fieldValue]
534
535
  };
@@ -2059,6 +2060,41 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
2059
2060
  });
2060
2061
  }
2061
2062
  /**
2063
+ * Updates multiple records in one write batch, with the same retry behavior as
2064
+ * {@link batchUpdate}. Returns, in input order, whether each update changed a row,
2065
+ * so callers can put extra conditions in `keys` (for example `{ endedAt: null }`).
2066
+ */
2067
+ async batchUpdateReportingChanges({ tableName, updates }) {
2068
+ return this.executeWriteOperationWithRetry(async () => {
2069
+ const changed = updates.map(() => false);
2070
+ const statements = [];
2071
+ const statementIndexes = [];
2072
+ for (const [index, { keys, data }] of updates.entries()) {
2073
+ const filteredData = await this.filterRecordToKnownColumns(tableName, data);
2074
+ if (Object.keys(filteredData).length === 0) continue;
2075
+ statements.push(prepareUpdateStatement({
2076
+ tableName,
2077
+ updates: filteredData,
2078
+ keys
2079
+ }));
2080
+ statementIndexes.push(index);
2081
+ }
2082
+ if (statements.length === 0) return changed;
2083
+ (await withClientWriteLock(this.client, () => this.client.batch(statements, "write"))).forEach((result, i) => {
2084
+ const index = statementIndexes[i];
2085
+ if (index !== void 0) changed[index] = result.rowsAffected > 0;
2086
+ });
2087
+ return changed;
2088
+ }, `batch update in table ${tableName}`).catch((error) => {
2089
+ throw new _mastra_core_error.MastraError({
2090
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "BATCH_UPDATE_REPORTING_CHANGES", "FAILED"),
2091
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
2092
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
2093
+ details: { tableName }
2094
+ }, error);
2095
+ });
2096
+ }
2097
+ /**
2062
2098
  * Internal batch delete implementation without retry logic.
2063
2099
  * Each record can be deleted based on single or composite keys.
2064
2100
  */
@@ -2191,7 +2227,7 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
2191
2227
  * @returns The total count of matching records
2192
2228
  */
2193
2229
  async selectTotalCount({ tableName, whereClause }) {
2194
- const statement = `SELECT COUNT(*) as count FROM ${(0, _mastra_core_utils.parseSqlIdentifier)(tableName, "table name")} ${whereClause ? `${whereClause.sql}` : ""}`;
2230
+ const statement = `SELECT COUNT(*) as count FROM ${(0, _mastra_core_utils.parseSqlIdentifier)(tableName, "table name")} ${whereClause ? whereClause.sql : ""}`;
2195
2231
  const result = await this.client.execute({
2196
2232
  sql: statement,
2197
2233
  args: whereClause?.args ?? []
@@ -12563,24 +12599,27 @@ var ObservabilityLibSQL = class ObservabilityLibSQL extends _mastra_core_storage
12563
12599
  }, error);
12564
12600
  }
12565
12601
  }
12566
- async batchUpdateSpans(args) {
12602
+ #toSpanUpdates(records) {
12567
12603
  const now = (/* @__PURE__ */ new Date()).toISOString();
12604
+ return records.map((record) => {
12605
+ const data = { ...record.updates };
12606
+ if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
12607
+ if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
12608
+ data.updatedAt = now;
12609
+ return {
12610
+ keys: {
12611
+ spanId: record.spanId,
12612
+ traceId: record.traceId
12613
+ },
12614
+ data
12615
+ };
12616
+ });
12617
+ }
12618
+ async batchUpdateSpans(args) {
12568
12619
  try {
12569
12620
  return this.#db.batchUpdate({
12570
12621
  tableName: _mastra_core_storage.TABLE_SPANS,
12571
- updates: args.records.map((record) => {
12572
- const data = { ...record.updates };
12573
- if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
12574
- if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
12575
- data.updatedAt = now;
12576
- return {
12577
- keys: {
12578
- spanId: record.spanId,
12579
- traceId: record.traceId
12580
- },
12581
- data
12582
- };
12583
- })
12622
+ updates: this.#toSpanUpdates(args.records)
12584
12623
  });
12585
12624
  } catch (error) {
12586
12625
  throw new _mastra_core_error.MastraError({
@@ -12590,6 +12629,30 @@ var ObservabilityLibSQL = class ObservabilityLibSQL extends _mastra_core_storage
12590
12629
  }, error);
12591
12630
  }
12592
12631
  }
12632
+ async batchUpdateUnendedSpans(args) {
12633
+ try {
12634
+ const changed = await this.#db.batchUpdateReportingChanges({
12635
+ tableName: _mastra_core_storage.TABLE_SPANS,
12636
+ updates: this.#toSpanUpdates(args.records).map((update) => ({
12637
+ ...update,
12638
+ keys: {
12639
+ ...update.keys,
12640
+ endedAt: null
12641
+ }
12642
+ }))
12643
+ });
12644
+ return { updated: args.records.filter((_, index) => changed[index]).map(({ traceId, spanId }) => ({
12645
+ traceId,
12646
+ spanId
12647
+ })) };
12648
+ } catch (error) {
12649
+ throw new _mastra_core_error.MastraError({
12650
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "BATCH_UPDATE_UNENDED_SPANS", "FAILED"),
12651
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12652
+ category: _mastra_core_error.ErrorCategory.USER
12653
+ }, error);
12654
+ }
12655
+ }
12593
12656
  async batchDeleteTraces(args) {
12594
12657
  this.assertUnscopedBatchDeleteTraces(args);
12595
12658
  try {
@@ -15278,6 +15341,9 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
15278
15341
  supportsConcurrentUpdates() {
15279
15342
  return true;
15280
15343
  }
15344
+ supportsGuardedSnapshotWrites() {
15345
+ return true;
15346
+ }
15281
15347
  parseWorkflowRun(row) {
15282
15348
  let parsedSnapshot = row.snapshot;
15283
15349
  if (typeof parsedSnapshot === "string") try {
@@ -15551,7 +15617,7 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
15551
15617
  }
15552
15618
  }), "updateWorkflowState");
15553
15619
  }
15554
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
15620
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence, expectedStatus }) {
15555
15621
  const now = /* @__PURE__ */ new Date();
15556
15622
  const createdAtValue = (createdAt ?? now).toISOString();
15557
15623
  const updatedAtValue = (updatedAt ?? now).toISOString();
@@ -15559,21 +15625,30 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
15559
15625
  workflowName,
15560
15626
  runId
15561
15627
  });
15628
+ const expected = expectedStatus === void 0 ? [] : Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus];
15629
+ const guard = expectedStatus === void 0 ? "" : expected.length ? ` WHERE json_extract(${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}.snapshot, '$.status') IN (${expected.map(() => "?").join(", ")})` : " WHERE 0";
15562
15630
  const check = this.#runFenceCheck(fence, runId, "persistWorkflowSnapshot");
15563
- await this.executeWithRetry(() => withRunFence(this.#client, check, (writer) => writer.execute({
15631
+ return (await this.executeWithRetry(() => withRunFence(this.#client, check, (writer) => writer.execute({
15564
15632
  sql: `INSERT INTO ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, resourceId, snapshot, createdAt, updatedAt)
15565
15633
  VALUES (?, ?, ?, jsonb(?), ?, ?)
15566
15634
  ON CONFLICT(workflow_name, run_id)
15567
- DO UPDATE SET resourceId = COALESCE(excluded.resourceId, ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}.resourceId), snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
15635
+ DO UPDATE SET resourceId = COALESCE(excluded.resourceId, ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}.resourceId), snapshot = excluded.snapshot, updatedAt = excluded.updatedAt${guard}`,
15568
15636
  args: [
15569
15637
  workflowName,
15570
15638
  runId,
15571
15639
  resourceId ?? null,
15572
15640
  safeStringify(snapshot),
15573
15641
  createdAtValue,
15574
- updatedAtValue
15642
+ updatedAtValue,
15643
+ ...expected
15575
15644
  ]
15576
- })), "persistWorkflowSnapshot");
15645
+ })), "persistWorkflowSnapshot")).rowsAffected > 0;
15646
+ }
15647
+ async loadWorkflowRunStatus({ workflowName, runId }) {
15648
+ return (await this.#client.execute({
15649
+ sql: `SELECT json_extract(snapshot, '$.status') AS status FROM ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
15650
+ args: [workflowName, runId]
15651
+ })).rows?.[0]?.status ?? null;
15577
15652
  }
15578
15653
  async loadWorkflowSnapshot({ workflowName, runId }) {
15579
15654
  this.logger.debug("Loading workflow snapshot", {