@mastra/libsql 1.0.0-beta.9 → 1.1.0-alpha.0
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/CHANGELOG.md +1241 -0
- package/dist/docs/README.md +39 -0
- package/dist/docs/SKILL.md +40 -0
- package/dist/docs/SOURCE_MAP.json +6 -0
- package/dist/docs/agents/01-agent-memory.md +166 -0
- package/dist/docs/agents/02-networks.md +292 -0
- package/dist/docs/agents/03-agent-approval.md +377 -0
- package/dist/docs/agents/04-network-approval.md +274 -0
- package/dist/docs/core/01-reference.md +151 -0
- package/dist/docs/guides/01-ai-sdk.md +120 -0
- package/dist/docs/memory/01-overview.md +45 -0
- package/dist/docs/memory/02-storage.md +261 -0
- package/dist/docs/memory/03-message-history.md +249 -0
- package/dist/docs/memory/04-working-memory.md +411 -0
- package/dist/docs/memory/05-semantic-recall.md +256 -0
- package/dist/docs/memory/06-memory-processors.md +318 -0
- package/dist/docs/memory/07-reference.md +133 -0
- package/dist/docs/observability/01-overview.md +73 -0
- package/dist/docs/observability/02-default.md +214 -0
- package/dist/docs/rag/01-retrieval.md +548 -0
- package/dist/docs/storage/01-reference.md +556 -0
- package/dist/docs/vectors/01-reference.md +213 -0
- package/dist/docs/workflows/01-snapshots.md +240 -0
- package/dist/index.cjs +817 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +814 -110
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +42 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/utils.d.ts +16 -1
- package/dist/storage/db/utils.d.ts.map +1 -1
- package/dist/storage/domains/agents/index.d.ts +10 -1
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +3 -2
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +25 -5
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +0 -1
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +1 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +10 -4
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts +6 -2
- package/dist/vector/index.d.ts.map +1 -1
- package/dist/vector/sql-builder.d.ts.map +1 -1
- package/package.json +12 -11
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createClient } from '@libsql/client';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { createVectorErrorId,
|
|
3
|
+
import { createVectorErrorId, AgentsStorage, AGENTS_SCHEMA, TABLE_AGENTS, AGENT_VERSIONS_SCHEMA, TABLE_AGENT_VERSIONS, createStorageErrorId, normalizePerPage, calculatePagination, MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, SPAN_SCHEMA, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, transformScoreRow, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraCompositeStore, TraceStatus, getSqlType, safelyParseJSON } from '@mastra/core/storage';
|
|
4
4
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
5
|
-
import { MastraVector } from '@mastra/core/vector';
|
|
5
|
+
import { MastraVector, validateTopK, validateUpsertInput } from '@mastra/core/vector';
|
|
6
6
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
7
7
|
import { MastraBase } from '@mastra/core/base';
|
|
8
8
|
import { MessageList } from '@mastra/core/agent';
|
|
@@ -242,10 +242,10 @@ var FILTER_OPERATORS = {
|
|
|
242
242
|
};
|
|
243
243
|
},
|
|
244
244
|
// Element Operators
|
|
245
|
-
$exists: (key) => {
|
|
245
|
+
$exists: (key, value) => {
|
|
246
246
|
const jsonPath = getJsonPath(key);
|
|
247
247
|
return {
|
|
248
|
-
sql: `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
|
|
248
|
+
sql: value === false ? `json_extract(metadata, ${jsonPath}) IS NULL` : `json_extract(metadata, ${jsonPath}) IS NOT NULL`,
|
|
249
249
|
needsValue: false
|
|
250
250
|
};
|
|
251
251
|
},
|
|
@@ -509,7 +509,7 @@ var LibSQLVector = class extends MastraVector {
|
|
|
509
509
|
maxRetries;
|
|
510
510
|
initialBackoffMs;
|
|
511
511
|
constructor({
|
|
512
|
-
|
|
512
|
+
url,
|
|
513
513
|
authToken,
|
|
514
514
|
syncUrl,
|
|
515
515
|
syncInterval,
|
|
@@ -519,14 +519,14 @@ var LibSQLVector = class extends MastraVector {
|
|
|
519
519
|
}) {
|
|
520
520
|
super({ id });
|
|
521
521
|
this.turso = createClient({
|
|
522
|
-
url
|
|
522
|
+
url,
|
|
523
523
|
syncUrl,
|
|
524
524
|
authToken,
|
|
525
525
|
syncInterval
|
|
526
526
|
});
|
|
527
527
|
this.maxRetries = maxRetries;
|
|
528
528
|
this.initialBackoffMs = initialBackoffMs;
|
|
529
|
-
if (
|
|
529
|
+
if (url.includes(`file:`) || url.includes(`:memory:`)) {
|
|
530
530
|
this.turso.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
|
|
531
531
|
this.turso.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout=5000.", err));
|
|
532
532
|
}
|
|
@@ -538,7 +538,7 @@ var LibSQLVector = class extends MastraVector {
|
|
|
538
538
|
try {
|
|
539
539
|
return await operation();
|
|
540
540
|
} catch (error) {
|
|
541
|
-
if (error.code === "SQLITE_BUSY" || error.message && error.message.toLowerCase().includes("database is locked")) {
|
|
541
|
+
if (error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.code === "SQLITE_LOCKED_SHAREDCACHE" || error.message && error.message.toLowerCase().includes("database is locked") || error.message && error.message.toLowerCase().includes("database table is locked")) {
|
|
542
542
|
attempts++;
|
|
543
543
|
if (attempts >= this.maxRetries) {
|
|
544
544
|
this.logger.error(
|
|
@@ -572,22 +572,14 @@ var LibSQLVector = class extends MastraVector {
|
|
|
572
572
|
minScore = -1
|
|
573
573
|
// Default to -1 to include all results (cosine similarity ranges from -1 to 1)
|
|
574
574
|
}) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
throw new MastraError(
|
|
584
|
-
{
|
|
585
|
-
id: createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
|
|
586
|
-
domain: ErrorDomain.STORAGE,
|
|
587
|
-
category: ErrorCategory.USER
|
|
588
|
-
},
|
|
589
|
-
error
|
|
590
|
-
);
|
|
575
|
+
validateTopK("LIBSQL", topK);
|
|
576
|
+
if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
|
|
577
|
+
throw new MastraError({
|
|
578
|
+
id: createVectorErrorId("LIBSQL", "QUERY", "INVALID_ARGS"),
|
|
579
|
+
domain: ErrorDomain.STORAGE,
|
|
580
|
+
category: ErrorCategory.USER,
|
|
581
|
+
details: { message: "queryVector must be an array of finite numbers" }
|
|
582
|
+
});
|
|
591
583
|
}
|
|
592
584
|
try {
|
|
593
585
|
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
@@ -647,6 +639,7 @@ var LibSQLVector = class extends MastraVector {
|
|
|
647
639
|
}
|
|
648
640
|
}
|
|
649
641
|
async doUpsert({ indexName, vectors, metadata, ids }) {
|
|
642
|
+
validateUpsertInput("LIBSQL", vectors, metadata, ids);
|
|
650
643
|
const tx = await this.turso.transaction("write");
|
|
651
644
|
try {
|
|
652
645
|
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
@@ -1093,6 +1086,14 @@ var LibSQLVector = class extends MastraVector {
|
|
|
1093
1086
|
});
|
|
1094
1087
|
}
|
|
1095
1088
|
};
|
|
1089
|
+
function buildSelectColumns(tableName) {
|
|
1090
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1091
|
+
return Object.keys(schema).map((col) => {
|
|
1092
|
+
const colDef = schema[col];
|
|
1093
|
+
const parsedCol = parseSqlIdentifier(col, "column name");
|
|
1094
|
+
return colDef?.type === "jsonb" ? `json(${parsedCol}) as ${parsedCol}` : parsedCol;
|
|
1095
|
+
}).join(", ");
|
|
1096
|
+
}
|
|
1096
1097
|
function isLockError(error) {
|
|
1097
1098
|
return error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
|
|
1098
1099
|
}
|
|
@@ -1141,17 +1142,27 @@ function createExecuteWriteOperationWithRetry({
|
|
|
1141
1142
|
}
|
|
1142
1143
|
function prepareStatement({ tableName, record }) {
|
|
1143
1144
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1144
|
-
const
|
|
1145
|
-
const
|
|
1145
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1146
|
+
const columnNames = Object.keys(record);
|
|
1147
|
+
const columns = columnNames.map((col) => parseSqlIdentifier(col, "column name"));
|
|
1148
|
+
const values = columnNames.map((col) => {
|
|
1149
|
+
const v = record[col];
|
|
1146
1150
|
if (typeof v === `undefined` || v === null) {
|
|
1147
1151
|
return null;
|
|
1148
1152
|
}
|
|
1153
|
+
const colDef = schema[col];
|
|
1154
|
+
if (colDef?.type === "jsonb") {
|
|
1155
|
+
return JSON.stringify(v);
|
|
1156
|
+
}
|
|
1149
1157
|
if (v instanceof Date) {
|
|
1150
1158
|
return v.toISOString();
|
|
1151
1159
|
}
|
|
1152
1160
|
return typeof v === "object" ? JSON.stringify(v) : v;
|
|
1153
1161
|
});
|
|
1154
|
-
const placeholders =
|
|
1162
|
+
const placeholders = columnNames.map((col) => {
|
|
1163
|
+
const colDef = schema[col];
|
|
1164
|
+
return colDef?.type === "jsonb" ? "jsonb(?)" : "?";
|
|
1165
|
+
}).join(", ");
|
|
1155
1166
|
return {
|
|
1156
1167
|
sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
|
|
1157
1168
|
args: values
|
|
@@ -1164,19 +1175,33 @@ function prepareUpdateStatement({
|
|
|
1164
1175
|
}) {
|
|
1165
1176
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1166
1177
|
const schema = TABLE_SCHEMAS[tableName];
|
|
1167
|
-
const
|
|
1168
|
-
const
|
|
1169
|
-
const
|
|
1178
|
+
const updateColumnNames = Object.keys(updates);
|
|
1179
|
+
const updateColumns = updateColumnNames.map((col) => parseSqlIdentifier(col, "column name"));
|
|
1180
|
+
const updateValues = updateColumnNames.map((col) => {
|
|
1181
|
+
const colDef = schema[col];
|
|
1182
|
+
const v = updates[col];
|
|
1183
|
+
if (colDef?.type === "jsonb") {
|
|
1184
|
+
return transformToSqlValue(v, true);
|
|
1185
|
+
}
|
|
1186
|
+
return transformToSqlValue(v, false);
|
|
1187
|
+
});
|
|
1188
|
+
const setClause = updateColumns.map((col, i) => {
|
|
1189
|
+
const colDef = schema[updateColumnNames[i]];
|
|
1190
|
+
return colDef?.type === "jsonb" ? `${col} = jsonb(?)` : `${col} = ?`;
|
|
1191
|
+
}).join(", ");
|
|
1170
1192
|
const whereClause = prepareWhereClause(keys, schema);
|
|
1171
1193
|
return {
|
|
1172
1194
|
sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
|
|
1173
1195
|
args: [...updateValues, ...whereClause.args]
|
|
1174
1196
|
};
|
|
1175
1197
|
}
|
|
1176
|
-
function transformToSqlValue(value) {
|
|
1198
|
+
function transformToSqlValue(value, forceJsonStringify = false) {
|
|
1177
1199
|
if (typeof value === "undefined" || value === null) {
|
|
1178
1200
|
return null;
|
|
1179
1201
|
}
|
|
1202
|
+
if (forceJsonStringify) {
|
|
1203
|
+
return JSON.stringify(value);
|
|
1204
|
+
}
|
|
1180
1205
|
if (value instanceof Date) {
|
|
1181
1206
|
return value.toISOString();
|
|
1182
1207
|
}
|
|
@@ -1533,11 +1558,12 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1533
1558
|
*/
|
|
1534
1559
|
async select({ tableName, keys }) {
|
|
1535
1560
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1561
|
+
const columns = buildSelectColumns(tableName);
|
|
1536
1562
|
const parsedKeys = Object.keys(keys).map((key) => parseSqlIdentifier(key, "column name"));
|
|
1537
1563
|
const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
|
|
1538
1564
|
const values = Object.values(keys);
|
|
1539
1565
|
const result = await this.client.execute({
|
|
1540
|
-
sql: `SELECT
|
|
1566
|
+
sql: `SELECT ${columns} FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
|
|
1541
1567
|
args: values
|
|
1542
1568
|
});
|
|
1543
1569
|
if (!result.rows || result.rows.length === 0) {
|
|
@@ -1577,7 +1603,8 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1577
1603
|
args
|
|
1578
1604
|
}) {
|
|
1579
1605
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1580
|
-
|
|
1606
|
+
const columns = buildSelectColumns(tableName);
|
|
1607
|
+
let statement = `SELECT ${columns} FROM ${parsedTableName}`;
|
|
1581
1608
|
if (whereClause?.sql) {
|
|
1582
1609
|
statement += ` ${whereClause.sql}`;
|
|
1583
1610
|
}
|
|
@@ -1594,7 +1621,17 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1594
1621
|
sql: statement,
|
|
1595
1622
|
args: [...whereClause?.args ?? [], ...args ?? []]
|
|
1596
1623
|
});
|
|
1597
|
-
return result.rows
|
|
1624
|
+
return (result.rows ?? []).map((row) => {
|
|
1625
|
+
return Object.fromEntries(
|
|
1626
|
+
Object.entries(row || {}).map(([k, v]) => {
|
|
1627
|
+
try {
|
|
1628
|
+
return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
|
|
1629
|
+
} catch {
|
|
1630
|
+
return [k, v];
|
|
1631
|
+
}
|
|
1632
|
+
})
|
|
1633
|
+
);
|
|
1634
|
+
});
|
|
1598
1635
|
}
|
|
1599
1636
|
/**
|
|
1600
1637
|
* Returns the total count of records matching the optional WHERE clause.
|
|
@@ -1638,7 +1675,7 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1638
1675
|
// SQLite uses 0/1 for booleans
|
|
1639
1676
|
case "jsonb":
|
|
1640
1677
|
return "TEXT";
|
|
1641
|
-
//
|
|
1678
|
+
// SQLite: column stores TEXT, we use jsonb()/json() functions for binary optimization
|
|
1642
1679
|
default:
|
|
1643
1680
|
return getSqlType(type);
|
|
1644
1681
|
}
|
|
@@ -1666,6 +1703,9 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1666
1703
|
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1667
1704
|
tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
1668
1705
|
}
|
|
1706
|
+
if (tableName === TABLE_SPANS) {
|
|
1707
|
+
tableConstraints.push("UNIQUE (spanId, traceId)");
|
|
1708
|
+
}
|
|
1669
1709
|
const allDefinitions = [...columnDefinitions, ...tableConstraints].join(",\n ");
|
|
1670
1710
|
const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
|
|
1671
1711
|
${allDefinitions}
|
|
@@ -1676,6 +1716,9 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1676
1716
|
await this.migrateSpansTable();
|
|
1677
1717
|
}
|
|
1678
1718
|
} catch (error) {
|
|
1719
|
+
if (error instanceof MastraError) {
|
|
1720
|
+
throw error;
|
|
1721
|
+
}
|
|
1679
1722
|
throw new MastraError(
|
|
1680
1723
|
{
|
|
1681
1724
|
id: createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
|
|
@@ -1689,7 +1732,7 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1689
1732
|
}
|
|
1690
1733
|
/**
|
|
1691
1734
|
* Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
|
|
1692
|
-
* This adds new columns that don't exist in old schema.
|
|
1735
|
+
* This adds new columns that don't exist in old schema and ensures required indexes exist.
|
|
1693
1736
|
*/
|
|
1694
1737
|
async migrateSpansTable() {
|
|
1695
1738
|
const schema = TABLE_SCHEMAS[TABLE_SPANS];
|
|
@@ -1703,11 +1746,205 @@ var LibSQLDB = class extends MastraBase {
|
|
|
1703
1746
|
this.logger.debug(`LibSQLDB: Added column '${columnName}' to ${TABLE_SPANS}`);
|
|
1704
1747
|
}
|
|
1705
1748
|
}
|
|
1749
|
+
const indexExists = await this.spansUniqueIndexExists();
|
|
1750
|
+
if (!indexExists) {
|
|
1751
|
+
const duplicateInfo = await this.checkForDuplicateSpans();
|
|
1752
|
+
if (duplicateInfo.hasDuplicates) {
|
|
1753
|
+
const errorMessage = `
|
|
1754
|
+
===========================================================================
|
|
1755
|
+
MIGRATION REQUIRED: Duplicate spans detected in ${TABLE_SPANS}
|
|
1756
|
+
===========================================================================
|
|
1757
|
+
|
|
1758
|
+
Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations.
|
|
1759
|
+
|
|
1760
|
+
The spans table requires a unique constraint on (traceId, spanId), but your
|
|
1761
|
+
database contains duplicate entries that must be resolved first.
|
|
1762
|
+
|
|
1763
|
+
To fix this, run the manual migration command:
|
|
1764
|
+
|
|
1765
|
+
npx mastra migrate
|
|
1766
|
+
|
|
1767
|
+
This command will:
|
|
1768
|
+
1. Remove duplicate spans (keeping the most complete/recent version)
|
|
1769
|
+
2. Add the required unique constraint
|
|
1770
|
+
|
|
1771
|
+
Note: This migration may take some time for large tables.
|
|
1772
|
+
===========================================================================
|
|
1773
|
+
`;
|
|
1774
|
+
throw new MastraError({
|
|
1775
|
+
id: createStorageErrorId("LIBSQL", "MIGRATION_REQUIRED", "DUPLICATE_SPANS"),
|
|
1776
|
+
domain: ErrorDomain.STORAGE,
|
|
1777
|
+
category: ErrorCategory.USER,
|
|
1778
|
+
text: errorMessage
|
|
1779
|
+
});
|
|
1780
|
+
} else {
|
|
1781
|
+
await this.client.execute(
|
|
1782
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${TABLE_SPANS}" ("spanId", "traceId")`
|
|
1783
|
+
);
|
|
1784
|
+
this.logger.debug(`LibSQLDB: Created unique index on (spanId, traceId) for ${TABLE_SPANS}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1706
1787
|
this.logger.info(`LibSQLDB: Migration completed for ${TABLE_SPANS}`);
|
|
1707
1788
|
} catch (error) {
|
|
1789
|
+
if (error instanceof MastraError) {
|
|
1790
|
+
throw error;
|
|
1791
|
+
}
|
|
1708
1792
|
this.logger.warn(`LibSQLDB: Failed to migrate spans table ${TABLE_SPANS}:`, error);
|
|
1709
1793
|
}
|
|
1710
1794
|
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Checks if the unique index on (spanId, traceId) already exists on the spans table.
|
|
1797
|
+
* Used to skip deduplication when the index already exists (migration already complete).
|
|
1798
|
+
*/
|
|
1799
|
+
async spansUniqueIndexExists() {
|
|
1800
|
+
try {
|
|
1801
|
+
const result = await this.client.execute(
|
|
1802
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = 'mastra_ai_spans_spanid_traceid_idx'`
|
|
1803
|
+
);
|
|
1804
|
+
return (result.rows?.length ?? 0) > 0;
|
|
1805
|
+
} catch {
|
|
1806
|
+
return false;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Checks for duplicate (traceId, spanId) combinations in the spans table.
|
|
1811
|
+
* Returns information about duplicates for logging/CLI purposes.
|
|
1812
|
+
*/
|
|
1813
|
+
async checkForDuplicateSpans() {
|
|
1814
|
+
try {
|
|
1815
|
+
const result = await this.client.execute(`
|
|
1816
|
+
SELECT COUNT(*) as duplicate_count FROM (
|
|
1817
|
+
SELECT "spanId", "traceId"
|
|
1818
|
+
FROM "${TABLE_SPANS}"
|
|
1819
|
+
GROUP BY "spanId", "traceId"
|
|
1820
|
+
HAVING COUNT(*) > 1
|
|
1821
|
+
)
|
|
1822
|
+
`);
|
|
1823
|
+
const duplicateCount = Number(result.rows?.[0]?.duplicate_count ?? 0);
|
|
1824
|
+
return {
|
|
1825
|
+
hasDuplicates: duplicateCount > 0,
|
|
1826
|
+
duplicateCount
|
|
1827
|
+
};
|
|
1828
|
+
} catch (error) {
|
|
1829
|
+
this.logger.debug(`LibSQLDB: Could not check for duplicates: ${error}`);
|
|
1830
|
+
return { hasDuplicates: false, duplicateCount: 0 };
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Manually run the spans migration to deduplicate and add the unique constraint.
|
|
1835
|
+
* This is intended to be called from the CLI when duplicates are detected.
|
|
1836
|
+
*
|
|
1837
|
+
* @returns Migration result with status and details
|
|
1838
|
+
*/
|
|
1839
|
+
async migrateSpans() {
|
|
1840
|
+
const indexExists = await this.spansUniqueIndexExists();
|
|
1841
|
+
if (indexExists) {
|
|
1842
|
+
return {
|
|
1843
|
+
success: true,
|
|
1844
|
+
alreadyMigrated: true,
|
|
1845
|
+
duplicatesRemoved: 0,
|
|
1846
|
+
message: `Migration already complete. Unique index exists on ${TABLE_SPANS}.`
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
const duplicateInfo = await this.checkForDuplicateSpans();
|
|
1850
|
+
if (duplicateInfo.hasDuplicates) {
|
|
1851
|
+
this.logger.info(
|
|
1852
|
+
`Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations. Starting deduplication...`
|
|
1853
|
+
);
|
|
1854
|
+
await this.deduplicateSpans();
|
|
1855
|
+
} else {
|
|
1856
|
+
this.logger.info(`No duplicate spans found.`);
|
|
1857
|
+
}
|
|
1858
|
+
await this.client.execute(
|
|
1859
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "mastra_ai_spans_spanid_traceid_idx" ON "${TABLE_SPANS}" ("spanId", "traceId")`
|
|
1860
|
+
);
|
|
1861
|
+
return {
|
|
1862
|
+
success: true,
|
|
1863
|
+
alreadyMigrated: false,
|
|
1864
|
+
duplicatesRemoved: duplicateInfo.duplicateCount,
|
|
1865
|
+
message: duplicateInfo.hasDuplicates ? `Migration complete. Removed duplicates and added unique index to ${TABLE_SPANS}.` : `Migration complete. Added unique index to ${TABLE_SPANS}.`
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Check migration status for the spans table.
|
|
1870
|
+
* Returns information about whether migration is needed.
|
|
1871
|
+
*/
|
|
1872
|
+
async checkSpansMigrationStatus() {
|
|
1873
|
+
const indexExists = await this.spansUniqueIndexExists();
|
|
1874
|
+
if (indexExists) {
|
|
1875
|
+
return {
|
|
1876
|
+
needsMigration: false,
|
|
1877
|
+
hasDuplicates: false,
|
|
1878
|
+
duplicateCount: 0,
|
|
1879
|
+
constraintExists: true,
|
|
1880
|
+
tableName: TABLE_SPANS
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
const duplicateInfo = await this.checkForDuplicateSpans();
|
|
1884
|
+
return {
|
|
1885
|
+
needsMigration: true,
|
|
1886
|
+
hasDuplicates: duplicateInfo.hasDuplicates,
|
|
1887
|
+
duplicateCount: duplicateInfo.duplicateCount,
|
|
1888
|
+
constraintExists: false,
|
|
1889
|
+
tableName: TABLE_SPANS
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* Deduplicates spans table by removing duplicate (spanId, traceId) combinations.
|
|
1894
|
+
* Keeps the "best" record for each duplicate group based on:
|
|
1895
|
+
* 1. Completed spans (endedAt IS NOT NULL) over incomplete ones
|
|
1896
|
+
* 2. Most recently updated (updatedAt DESC)
|
|
1897
|
+
* 3. Most recently created (createdAt DESC) as tiebreaker
|
|
1898
|
+
*/
|
|
1899
|
+
async deduplicateSpans() {
|
|
1900
|
+
try {
|
|
1901
|
+
const duplicateCheck = await this.client.execute(`
|
|
1902
|
+
SELECT COUNT(*) as duplicate_count FROM (
|
|
1903
|
+
SELECT "spanId", "traceId"
|
|
1904
|
+
FROM "${TABLE_SPANS}"
|
|
1905
|
+
GROUP BY "spanId", "traceId"
|
|
1906
|
+
HAVING COUNT(*) > 1
|
|
1907
|
+
)
|
|
1908
|
+
`);
|
|
1909
|
+
const duplicateCount = Number(duplicateCheck.rows?.[0]?.duplicate_count ?? 0);
|
|
1910
|
+
if (duplicateCount === 0) {
|
|
1911
|
+
this.logger.debug(`LibSQLDB: No duplicate spans found, skipping deduplication`);
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
this.logger.warn(`LibSQLDB: Found ${duplicateCount} duplicate (spanId, traceId) combinations, deduplicating...`);
|
|
1915
|
+
const deleteResult = await this.client.execute(`
|
|
1916
|
+
DELETE FROM "${TABLE_SPANS}"
|
|
1917
|
+
WHERE rowid NOT IN (
|
|
1918
|
+
SELECT MIN(best_rowid) FROM (
|
|
1919
|
+
SELECT
|
|
1920
|
+
rowid as best_rowid,
|
|
1921
|
+
"spanId",
|
|
1922
|
+
"traceId",
|
|
1923
|
+
ROW_NUMBER() OVER (
|
|
1924
|
+
PARTITION BY "spanId", "traceId"
|
|
1925
|
+
ORDER BY
|
|
1926
|
+
CASE WHEN "endedAt" IS NOT NULL THEN 0 ELSE 1 END,
|
|
1927
|
+
"updatedAt" DESC,
|
|
1928
|
+
"createdAt" DESC
|
|
1929
|
+
) as rn
|
|
1930
|
+
FROM "${TABLE_SPANS}"
|
|
1931
|
+
) ranked
|
|
1932
|
+
WHERE rn = 1
|
|
1933
|
+
GROUP BY "spanId", "traceId"
|
|
1934
|
+
)
|
|
1935
|
+
AND ("spanId", "traceId") IN (
|
|
1936
|
+
SELECT "spanId", "traceId"
|
|
1937
|
+
FROM "${TABLE_SPANS}"
|
|
1938
|
+
GROUP BY "spanId", "traceId"
|
|
1939
|
+
HAVING COUNT(*) > 1
|
|
1940
|
+
)
|
|
1941
|
+
`);
|
|
1942
|
+
const deletedCount = deleteResult.rowsAffected ?? 0;
|
|
1943
|
+
this.logger.warn(`LibSQLDB: Deleted ${deletedCount} duplicate span records`);
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
this.logger.warn(`LibSQLDB: Failed to deduplicate spans:`, error);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1711
1948
|
/**
|
|
1712
1949
|
* Gets a default value for a column type (used when adding NOT NULL columns).
|
|
1713
1950
|
*/
|
|
@@ -1811,8 +2048,10 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
1811
2048
|
}
|
|
1812
2049
|
async init() {
|
|
1813
2050
|
await this.#db.createTable({ tableName: TABLE_AGENTS, schema: AGENTS_SCHEMA });
|
|
2051
|
+
await this.#db.createTable({ tableName: TABLE_AGENT_VERSIONS, schema: AGENT_VERSIONS_SCHEMA });
|
|
1814
2052
|
}
|
|
1815
2053
|
async dangerouslyClearAll() {
|
|
2054
|
+
await this.#db.deleteData({ tableName: TABLE_AGENT_VERSIONS });
|
|
1816
2055
|
await this.#db.deleteData({ tableName: TABLE_AGENTS });
|
|
1817
2056
|
}
|
|
1818
2057
|
parseJson(value, fieldName) {
|
|
@@ -1854,6 +2093,9 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
1854
2093
|
outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
|
|
1855
2094
|
memory: this.parseJson(row.memory, "memory"),
|
|
1856
2095
|
scorers: this.parseJson(row.scorers, "scorers"),
|
|
2096
|
+
integrationTools: this.parseJson(row.integrationTools, "integrationTools"),
|
|
2097
|
+
ownerId: row.ownerId,
|
|
2098
|
+
activeVersionId: row.activeVersionId,
|
|
1857
2099
|
metadata: this.parseJson(row.metadata, "metadata"),
|
|
1858
2100
|
createdAt: new Date(row.createdAt),
|
|
1859
2101
|
updatedAt: new Date(row.updatedAt)
|
|
@@ -1897,6 +2139,9 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
1897
2139
|
outputProcessors: agent.outputProcessors ?? null,
|
|
1898
2140
|
memory: agent.memory ?? null,
|
|
1899
2141
|
scorers: agent.scorers ?? null,
|
|
2142
|
+
integrationTools: agent.integrationTools ?? null,
|
|
2143
|
+
ownerId: agent.ownerId ?? null,
|
|
2144
|
+
activeVersionId: agent.activeVersionId ?? null,
|
|
1900
2145
|
metadata: agent.metadata ?? null,
|
|
1901
2146
|
createdAt: now,
|
|
1902
2147
|
updatedAt: now
|
|
@@ -1946,6 +2191,9 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
1946
2191
|
if (updates.outputProcessors !== void 0) data.outputProcessors = updates.outputProcessors;
|
|
1947
2192
|
if (updates.memory !== void 0) data.memory = updates.memory;
|
|
1948
2193
|
if (updates.scorers !== void 0) data.scorers = updates.scorers;
|
|
2194
|
+
if (updates.integrationTools !== void 0) data.integrationTools = updates.integrationTools;
|
|
2195
|
+
if (updates.ownerId !== void 0) data.ownerId = updates.ownerId;
|
|
2196
|
+
if (updates.activeVersionId !== void 0) data.activeVersionId = updates.activeVersionId;
|
|
1949
2197
|
if (updates.metadata !== void 0) {
|
|
1950
2198
|
data.metadata = { ...existingAgent.metadata, ...updates.metadata };
|
|
1951
2199
|
}
|
|
@@ -1984,6 +2232,7 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
1984
2232
|
}
|
|
1985
2233
|
async deleteAgent({ id }) {
|
|
1986
2234
|
try {
|
|
2235
|
+
await this.deleteVersionsByAgentId(id);
|
|
1987
2236
|
await this.#db.delete({
|
|
1988
2237
|
tableName: TABLE_AGENTS,
|
|
1989
2238
|
keys: { id }
|
|
@@ -2053,6 +2302,262 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
2053
2302
|
);
|
|
2054
2303
|
}
|
|
2055
2304
|
}
|
|
2305
|
+
// ==========================================================================
|
|
2306
|
+
// Agent Version Methods
|
|
2307
|
+
// ==========================================================================
|
|
2308
|
+
async createVersion(input) {
|
|
2309
|
+
try {
|
|
2310
|
+
const now = /* @__PURE__ */ new Date();
|
|
2311
|
+
await this.#db.insert({
|
|
2312
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2313
|
+
record: {
|
|
2314
|
+
id: input.id,
|
|
2315
|
+
agentId: input.agentId,
|
|
2316
|
+
versionNumber: input.versionNumber,
|
|
2317
|
+
name: input.name ?? null,
|
|
2318
|
+
snapshot: input.snapshot,
|
|
2319
|
+
changedFields: input.changedFields ?? null,
|
|
2320
|
+
changeMessage: input.changeMessage ?? null,
|
|
2321
|
+
createdAt: now
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
return {
|
|
2325
|
+
...input,
|
|
2326
|
+
createdAt: now
|
|
2327
|
+
};
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
throw new MastraError(
|
|
2330
|
+
{
|
|
2331
|
+
id: createStorageErrorId("LIBSQL", "CREATE_VERSION", "FAILED"),
|
|
2332
|
+
domain: ErrorDomain.STORAGE,
|
|
2333
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2334
|
+
details: { versionId: input.id, agentId: input.agentId }
|
|
2335
|
+
},
|
|
2336
|
+
error
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
async getVersion(id) {
|
|
2341
|
+
try {
|
|
2342
|
+
const result = await this.#db.select({
|
|
2343
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2344
|
+
keys: { id }
|
|
2345
|
+
});
|
|
2346
|
+
if (!result) {
|
|
2347
|
+
return null;
|
|
2348
|
+
}
|
|
2349
|
+
return this.parseVersionRow(result);
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
throw new MastraError(
|
|
2352
|
+
{
|
|
2353
|
+
id: createStorageErrorId("LIBSQL", "GET_VERSION", "FAILED"),
|
|
2354
|
+
domain: ErrorDomain.STORAGE,
|
|
2355
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2356
|
+
details: { versionId: id }
|
|
2357
|
+
},
|
|
2358
|
+
error
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
async getVersionByNumber(agentId, versionNumber) {
|
|
2363
|
+
try {
|
|
2364
|
+
const rows = await this.#db.selectMany({
|
|
2365
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2366
|
+
whereClause: {
|
|
2367
|
+
sql: "WHERE agentId = ? AND versionNumber = ?",
|
|
2368
|
+
args: [agentId, versionNumber]
|
|
2369
|
+
},
|
|
2370
|
+
limit: 1
|
|
2371
|
+
});
|
|
2372
|
+
if (!rows || rows.length === 0) {
|
|
2373
|
+
return null;
|
|
2374
|
+
}
|
|
2375
|
+
return this.parseVersionRow(rows[0]);
|
|
2376
|
+
} catch (error) {
|
|
2377
|
+
throw new MastraError(
|
|
2378
|
+
{
|
|
2379
|
+
id: createStorageErrorId("LIBSQL", "GET_VERSION_BY_NUMBER", "FAILED"),
|
|
2380
|
+
domain: ErrorDomain.STORAGE,
|
|
2381
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2382
|
+
details: { agentId, versionNumber }
|
|
2383
|
+
},
|
|
2384
|
+
error
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
async getLatestVersion(agentId) {
|
|
2389
|
+
try {
|
|
2390
|
+
const rows = await this.#db.selectMany({
|
|
2391
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2392
|
+
whereClause: {
|
|
2393
|
+
sql: "WHERE agentId = ?",
|
|
2394
|
+
args: [agentId]
|
|
2395
|
+
},
|
|
2396
|
+
orderBy: "versionNumber DESC",
|
|
2397
|
+
limit: 1
|
|
2398
|
+
});
|
|
2399
|
+
if (!rows || rows.length === 0) {
|
|
2400
|
+
return null;
|
|
2401
|
+
}
|
|
2402
|
+
return this.parseVersionRow(rows[0]);
|
|
2403
|
+
} catch (error) {
|
|
2404
|
+
throw new MastraError(
|
|
2405
|
+
{
|
|
2406
|
+
id: createStorageErrorId("LIBSQL", "GET_LATEST_VERSION", "FAILED"),
|
|
2407
|
+
domain: ErrorDomain.STORAGE,
|
|
2408
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2409
|
+
details: { agentId }
|
|
2410
|
+
},
|
|
2411
|
+
error
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
async listVersions(input) {
|
|
2416
|
+
const { agentId, page = 0, perPage: perPageInput, orderBy } = input;
|
|
2417
|
+
if (page < 0) {
|
|
2418
|
+
throw new MastraError(
|
|
2419
|
+
{
|
|
2420
|
+
id: createStorageErrorId("LIBSQL", "LIST_VERSIONS", "INVALID_PAGE"),
|
|
2421
|
+
domain: ErrorDomain.STORAGE,
|
|
2422
|
+
category: ErrorCategory.USER,
|
|
2423
|
+
details: { page }
|
|
2424
|
+
},
|
|
2425
|
+
new Error("page must be >= 0")
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
const perPage = normalizePerPage(perPageInput, 20);
|
|
2429
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2430
|
+
try {
|
|
2431
|
+
const { field, direction } = this.parseVersionOrderBy(orderBy);
|
|
2432
|
+
const total = await this.#db.selectTotalCount({
|
|
2433
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2434
|
+
whereClause: {
|
|
2435
|
+
sql: "WHERE agentId = ?",
|
|
2436
|
+
args: [agentId]
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
if (total === 0) {
|
|
2440
|
+
return {
|
|
2441
|
+
versions: [],
|
|
2442
|
+
total: 0,
|
|
2443
|
+
page,
|
|
2444
|
+
perPage: perPageForResponse,
|
|
2445
|
+
hasMore: false
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2449
|
+
const rows = await this.#db.selectMany({
|
|
2450
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2451
|
+
whereClause: {
|
|
2452
|
+
sql: "WHERE agentId = ?",
|
|
2453
|
+
args: [agentId]
|
|
2454
|
+
},
|
|
2455
|
+
orderBy: `"${field}" ${direction}`,
|
|
2456
|
+
limit: limitValue,
|
|
2457
|
+
offset
|
|
2458
|
+
});
|
|
2459
|
+
const versions = rows.map((row) => this.parseVersionRow(row));
|
|
2460
|
+
return {
|
|
2461
|
+
versions,
|
|
2462
|
+
total,
|
|
2463
|
+
page,
|
|
2464
|
+
perPage: perPageForResponse,
|
|
2465
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
2466
|
+
};
|
|
2467
|
+
} catch (error) {
|
|
2468
|
+
throw new MastraError(
|
|
2469
|
+
{
|
|
2470
|
+
id: createStorageErrorId("LIBSQL", "LIST_VERSIONS", "FAILED"),
|
|
2471
|
+
domain: ErrorDomain.STORAGE,
|
|
2472
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2473
|
+
details: { agentId }
|
|
2474
|
+
},
|
|
2475
|
+
error
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
async deleteVersion(id) {
|
|
2480
|
+
try {
|
|
2481
|
+
await this.#db.delete({
|
|
2482
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2483
|
+
keys: { id }
|
|
2484
|
+
});
|
|
2485
|
+
} catch (error) {
|
|
2486
|
+
throw new MastraError(
|
|
2487
|
+
{
|
|
2488
|
+
id: createStorageErrorId("LIBSQL", "DELETE_VERSION", "FAILED"),
|
|
2489
|
+
domain: ErrorDomain.STORAGE,
|
|
2490
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2491
|
+
details: { versionId: id }
|
|
2492
|
+
},
|
|
2493
|
+
error
|
|
2494
|
+
);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
async deleteVersionsByAgentId(agentId) {
|
|
2498
|
+
try {
|
|
2499
|
+
const versions = await this.#db.selectMany({
|
|
2500
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2501
|
+
whereClause: {
|
|
2502
|
+
sql: "WHERE agentId = ?",
|
|
2503
|
+
args: [agentId]
|
|
2504
|
+
}
|
|
2505
|
+
});
|
|
2506
|
+
for (const version of versions) {
|
|
2507
|
+
await this.#db.delete({
|
|
2508
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2509
|
+
keys: { id: version.id }
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
throw new MastraError(
|
|
2514
|
+
{
|
|
2515
|
+
id: createStorageErrorId("LIBSQL", "DELETE_VERSIONS_BY_AGENT_ID", "FAILED"),
|
|
2516
|
+
domain: ErrorDomain.STORAGE,
|
|
2517
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2518
|
+
details: { agentId }
|
|
2519
|
+
},
|
|
2520
|
+
error
|
|
2521
|
+
);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
async countVersions(agentId) {
|
|
2525
|
+
try {
|
|
2526
|
+
const count = await this.#db.selectTotalCount({
|
|
2527
|
+
tableName: TABLE_AGENT_VERSIONS,
|
|
2528
|
+
whereClause: {
|
|
2529
|
+
sql: "WHERE agentId = ?",
|
|
2530
|
+
args: [agentId]
|
|
2531
|
+
}
|
|
2532
|
+
});
|
|
2533
|
+
return count;
|
|
2534
|
+
} catch (error) {
|
|
2535
|
+
throw new MastraError(
|
|
2536
|
+
{
|
|
2537
|
+
id: createStorageErrorId("LIBSQL", "COUNT_VERSIONS", "FAILED"),
|
|
2538
|
+
domain: ErrorDomain.STORAGE,
|
|
2539
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2540
|
+
details: { agentId }
|
|
2541
|
+
},
|
|
2542
|
+
error
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
// ==========================================================================
|
|
2547
|
+
// Private Helper Methods
|
|
2548
|
+
// ==========================================================================
|
|
2549
|
+
parseVersionRow(row) {
|
|
2550
|
+
return {
|
|
2551
|
+
id: row.id,
|
|
2552
|
+
agentId: row.agentId,
|
|
2553
|
+
versionNumber: row.versionNumber,
|
|
2554
|
+
name: row.name,
|
|
2555
|
+
snapshot: this.parseJson(row.snapshot, "snapshot"),
|
|
2556
|
+
changedFields: this.parseJson(row.changedFields, "changedFields"),
|
|
2557
|
+
changeMessage: row.changeMessage,
|
|
2558
|
+
createdAt: new Date(row.createdAt)
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2056
2561
|
};
|
|
2057
2562
|
var MemoryLibSQL = class extends MemoryStorage {
|
|
2058
2563
|
#client;
|
|
@@ -2210,13 +2715,15 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2210
2715
|
queryParams.push(resourceId);
|
|
2211
2716
|
}
|
|
2212
2717
|
if (filter?.dateRange?.start) {
|
|
2213
|
-
|
|
2718
|
+
const startOp = filter.dateRange.startExclusive ? ">" : ">=";
|
|
2719
|
+
conditions.push(`"createdAt" ${startOp} ?`);
|
|
2214
2720
|
queryParams.push(
|
|
2215
2721
|
filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
|
|
2216
2722
|
);
|
|
2217
2723
|
}
|
|
2218
2724
|
if (filter?.dateRange?.end) {
|
|
2219
|
-
|
|
2725
|
+
const endOp = filter.dateRange.endExclusive ? "<" : "<=";
|
|
2726
|
+
conditions.push(`"createdAt" ${endOp} ?`);
|
|
2220
2727
|
queryParams.push(
|
|
2221
2728
|
filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
|
|
2222
2729
|
);
|
|
@@ -2522,8 +3029,8 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2522
3029
|
await this.#db.insert({
|
|
2523
3030
|
tableName: TABLE_RESOURCES,
|
|
2524
3031
|
record: {
|
|
2525
|
-
...resource
|
|
2526
|
-
metadata
|
|
3032
|
+
...resource
|
|
3033
|
+
// metadata is handled by prepareStatement which stringifies jsonb columns
|
|
2527
3034
|
}
|
|
2528
3035
|
});
|
|
2529
3036
|
return resource;
|
|
@@ -2560,7 +3067,7 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2560
3067
|
values.push(workingMemory);
|
|
2561
3068
|
}
|
|
2562
3069
|
if (metadata) {
|
|
2563
|
-
updates.push("metadata = ?");
|
|
3070
|
+
updates.push("metadata = jsonb(?)");
|
|
2564
3071
|
values.push(JSON.stringify(updatedResource.metadata));
|
|
2565
3072
|
}
|
|
2566
3073
|
updates.push("updatedAt = ?");
|
|
@@ -2599,33 +3106,76 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2599
3106
|
);
|
|
2600
3107
|
}
|
|
2601
3108
|
}
|
|
2602
|
-
async
|
|
2603
|
-
const {
|
|
2604
|
-
|
|
3109
|
+
async listThreads(args) {
|
|
3110
|
+
const { page = 0, perPage: perPageInput, orderBy, filter } = args;
|
|
3111
|
+
try {
|
|
3112
|
+
this.validatePaginationInput(page, perPageInput ?? 100);
|
|
3113
|
+
} catch (error) {
|
|
2605
3114
|
throw new MastraError(
|
|
2606
3115
|
{
|
|
2607
|
-
id: createStorageErrorId("LIBSQL", "
|
|
3116
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_PAGE"),
|
|
2608
3117
|
domain: ErrorDomain.STORAGE,
|
|
2609
3118
|
category: ErrorCategory.USER,
|
|
2610
|
-
details: { page }
|
|
3119
|
+
details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
|
|
2611
3120
|
},
|
|
2612
|
-
new Error("
|
|
3121
|
+
error instanceof Error ? error : new Error("Invalid pagination parameters")
|
|
2613
3122
|
);
|
|
2614
3123
|
}
|
|
2615
3124
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
3125
|
+
try {
|
|
3126
|
+
this.validateMetadataKeys(filter?.metadata);
|
|
3127
|
+
} catch (error) {
|
|
3128
|
+
throw new MastraError(
|
|
3129
|
+
{
|
|
3130
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_KEY"),
|
|
3131
|
+
domain: ErrorDomain.STORAGE,
|
|
3132
|
+
category: ErrorCategory.USER,
|
|
3133
|
+
details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
|
|
3134
|
+
},
|
|
3135
|
+
error instanceof Error ? error : new Error("Invalid metadata key")
|
|
3136
|
+
);
|
|
3137
|
+
}
|
|
2616
3138
|
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2617
3139
|
const { field, direction } = this.parseOrderBy(orderBy);
|
|
2618
3140
|
try {
|
|
2619
|
-
const
|
|
2620
|
-
const queryParams = [
|
|
3141
|
+
const whereClauses = [];
|
|
3142
|
+
const queryParams = [];
|
|
3143
|
+
if (filter?.resourceId) {
|
|
3144
|
+
whereClauses.push("resourceId = ?");
|
|
3145
|
+
queryParams.push(filter.resourceId);
|
|
3146
|
+
}
|
|
3147
|
+
if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
|
|
3148
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
3149
|
+
if (value === null) {
|
|
3150
|
+
whereClauses.push(`json_extract(metadata, '$.${key}') IS NULL`);
|
|
3151
|
+
} else if (typeof value === "boolean") {
|
|
3152
|
+
whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
|
|
3153
|
+
queryParams.push(value ? 1 : 0);
|
|
3154
|
+
} else if (typeof value === "number") {
|
|
3155
|
+
whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
|
|
3156
|
+
queryParams.push(value);
|
|
3157
|
+
} else if (typeof value === "string") {
|
|
3158
|
+
whereClauses.push(`json_extract(metadata, '$.${key}') = ?`);
|
|
3159
|
+
queryParams.push(value);
|
|
3160
|
+
} else {
|
|
3161
|
+
throw new MastraError({
|
|
3162
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS", "INVALID_METADATA_VALUE"),
|
|
3163
|
+
domain: ErrorDomain.STORAGE,
|
|
3164
|
+
category: ErrorCategory.USER,
|
|
3165
|
+
text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${typeof value}`,
|
|
3166
|
+
details: { key, valueType: typeof value }
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
3172
|
+
const baseQuery = `FROM ${TABLE_THREADS} ${whereClause}`;
|
|
2621
3173
|
const mapRowToStorageThreadType = (row) => ({
|
|
2622
3174
|
id: row.id,
|
|
2623
3175
|
resourceId: row.resourceId,
|
|
2624
3176
|
title: row.title,
|
|
2625
3177
|
createdAt: new Date(row.createdAt),
|
|
2626
|
-
// Convert string to Date
|
|
2627
3178
|
updatedAt: new Date(row.updatedAt),
|
|
2628
|
-
// Convert string to Date
|
|
2629
3179
|
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
|
|
2630
3180
|
});
|
|
2631
3181
|
const countResult = await this.#client.execute({
|
|
@@ -2644,7 +3194,7 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2644
3194
|
}
|
|
2645
3195
|
const limitValue = perPageInput === false ? total : perPage;
|
|
2646
3196
|
const dataResult = await this.#client.execute({
|
|
2647
|
-
sql: `SELECT
|
|
3197
|
+
sql: `SELECT ${buildSelectColumns(TABLE_THREADS)} ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
|
|
2648
3198
|
args: [...queryParams, limitValue, offset]
|
|
2649
3199
|
});
|
|
2650
3200
|
const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
|
|
@@ -2656,12 +3206,18 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2656
3206
|
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
2657
3207
|
};
|
|
2658
3208
|
} catch (error) {
|
|
3209
|
+
if (error instanceof MastraError && error.category === ErrorCategory.USER) {
|
|
3210
|
+
throw error;
|
|
3211
|
+
}
|
|
2659
3212
|
const mastraError = new MastraError(
|
|
2660
3213
|
{
|
|
2661
|
-
id: createStorageErrorId("LIBSQL", "
|
|
3214
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS", "FAILED"),
|
|
2662
3215
|
domain: ErrorDomain.STORAGE,
|
|
2663
3216
|
category: ErrorCategory.THIRD_PARTY,
|
|
2664
|
-
details: {
|
|
3217
|
+
details: {
|
|
3218
|
+
...filter?.resourceId && { resourceId: filter.resourceId },
|
|
3219
|
+
hasMetadataFilter: !!filter?.metadata
|
|
3220
|
+
}
|
|
2665
3221
|
},
|
|
2666
3222
|
error
|
|
2667
3223
|
);
|
|
@@ -2681,8 +3237,8 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2681
3237
|
await this.#db.insert({
|
|
2682
3238
|
tableName: TABLE_THREADS,
|
|
2683
3239
|
record: {
|
|
2684
|
-
...thread
|
|
2685
|
-
metadata
|
|
3240
|
+
...thread
|
|
3241
|
+
// metadata is handled by prepareStatement which stringifies jsonb columns
|
|
2686
3242
|
}
|
|
2687
3243
|
});
|
|
2688
3244
|
return thread;
|
|
@@ -2729,7 +3285,7 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2729
3285
|
};
|
|
2730
3286
|
try {
|
|
2731
3287
|
await this.#client.execute({
|
|
2732
|
-
sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
|
|
3288
|
+
sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = jsonb(?) WHERE id = ?`,
|
|
2733
3289
|
args: [title, JSON.stringify(updatedThread.metadata), id]
|
|
2734
3290
|
});
|
|
2735
3291
|
return updatedThread;
|
|
@@ -2768,6 +3324,148 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
2768
3324
|
);
|
|
2769
3325
|
}
|
|
2770
3326
|
}
|
|
3327
|
+
async cloneThread(args) {
|
|
3328
|
+
const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
|
|
3329
|
+
const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
|
|
3330
|
+
if (!sourceThread) {
|
|
3331
|
+
throw new MastraError({
|
|
3332
|
+
id: createStorageErrorId("LIBSQL", "CLONE_THREAD", "SOURCE_NOT_FOUND"),
|
|
3333
|
+
domain: ErrorDomain.STORAGE,
|
|
3334
|
+
category: ErrorCategory.USER,
|
|
3335
|
+
text: `Source thread with id ${sourceThreadId} not found`,
|
|
3336
|
+
details: { sourceThreadId }
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3339
|
+
const newThreadId = providedThreadId || crypto.randomUUID();
|
|
3340
|
+
const existingThread = await this.getThreadById({ threadId: newThreadId });
|
|
3341
|
+
if (existingThread) {
|
|
3342
|
+
throw new MastraError({
|
|
3343
|
+
id: createStorageErrorId("LIBSQL", "CLONE_THREAD", "THREAD_EXISTS"),
|
|
3344
|
+
domain: ErrorDomain.STORAGE,
|
|
3345
|
+
category: ErrorCategory.USER,
|
|
3346
|
+
text: `Thread with id ${newThreadId} already exists`,
|
|
3347
|
+
details: { newThreadId }
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
try {
|
|
3351
|
+
let messageQuery = `SELECT id, content, role, type, "createdAt", thread_id, "resourceId"
|
|
3352
|
+
FROM "${TABLE_MESSAGES}" WHERE thread_id = ?`;
|
|
3353
|
+
const messageParams = [sourceThreadId];
|
|
3354
|
+
if (options?.messageFilter?.startDate) {
|
|
3355
|
+
messageQuery += ` AND "createdAt" >= ?`;
|
|
3356
|
+
messageParams.push(
|
|
3357
|
+
options.messageFilter.startDate instanceof Date ? options.messageFilter.startDate.toISOString() : options.messageFilter.startDate
|
|
3358
|
+
);
|
|
3359
|
+
}
|
|
3360
|
+
if (options?.messageFilter?.endDate) {
|
|
3361
|
+
messageQuery += ` AND "createdAt" <= ?`;
|
|
3362
|
+
messageParams.push(
|
|
3363
|
+
options.messageFilter.endDate instanceof Date ? options.messageFilter.endDate.toISOString() : options.messageFilter.endDate
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
if (options?.messageFilter?.messageIds && options.messageFilter.messageIds.length > 0) {
|
|
3367
|
+
messageQuery += ` AND id IN (${options.messageFilter.messageIds.map(() => "?").join(", ")})`;
|
|
3368
|
+
messageParams.push(...options.messageFilter.messageIds);
|
|
3369
|
+
}
|
|
3370
|
+
messageQuery += ` ORDER BY "createdAt" ASC`;
|
|
3371
|
+
if (options?.messageLimit && options.messageLimit > 0) {
|
|
3372
|
+
const limitQuery = `SELECT * FROM (${messageQuery.replace('ORDER BY "createdAt" ASC', 'ORDER BY "createdAt" DESC')} LIMIT ?) ORDER BY "createdAt" ASC`;
|
|
3373
|
+
messageParams.push(options.messageLimit);
|
|
3374
|
+
messageQuery = limitQuery;
|
|
3375
|
+
}
|
|
3376
|
+
const sourceMessagesResult = await this.#client.execute({ sql: messageQuery, args: messageParams });
|
|
3377
|
+
const sourceMessages = sourceMessagesResult.rows || [];
|
|
3378
|
+
const now = /* @__PURE__ */ new Date();
|
|
3379
|
+
const nowStr = now.toISOString();
|
|
3380
|
+
const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
|
|
3381
|
+
const cloneMetadata = {
|
|
3382
|
+
sourceThreadId,
|
|
3383
|
+
clonedAt: now,
|
|
3384
|
+
...lastMessageId && { lastMessageId }
|
|
3385
|
+
};
|
|
3386
|
+
const newThread = {
|
|
3387
|
+
id: newThreadId,
|
|
3388
|
+
resourceId: resourceId || sourceThread.resourceId,
|
|
3389
|
+
title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
|
|
3390
|
+
metadata: {
|
|
3391
|
+
...metadata,
|
|
3392
|
+
clone: cloneMetadata
|
|
3393
|
+
},
|
|
3394
|
+
createdAt: now,
|
|
3395
|
+
updatedAt: now
|
|
3396
|
+
};
|
|
3397
|
+
const tx = await this.#client.transaction("write");
|
|
3398
|
+
try {
|
|
3399
|
+
await tx.execute({
|
|
3400
|
+
sql: `INSERT INTO "${TABLE_THREADS}" (id, "resourceId", title, metadata, "createdAt", "updatedAt")
|
|
3401
|
+
VALUES (?, ?, ?, jsonb(?), ?, ?)`,
|
|
3402
|
+
args: [
|
|
3403
|
+
newThread.id,
|
|
3404
|
+
newThread.resourceId,
|
|
3405
|
+
newThread.title || null,
|
|
3406
|
+
JSON.stringify(newThread.metadata),
|
|
3407
|
+
nowStr,
|
|
3408
|
+
nowStr
|
|
3409
|
+
]
|
|
3410
|
+
});
|
|
3411
|
+
const clonedMessages = [];
|
|
3412
|
+
const targetResourceId = resourceId || sourceThread.resourceId;
|
|
3413
|
+
for (const sourceMsg of sourceMessages) {
|
|
3414
|
+
const newMessageId = crypto.randomUUID();
|
|
3415
|
+
const contentStr = sourceMsg.content;
|
|
3416
|
+
let parsedContent;
|
|
3417
|
+
try {
|
|
3418
|
+
parsedContent = JSON.parse(contentStr);
|
|
3419
|
+
} catch {
|
|
3420
|
+
parsedContent = { format: 2, parts: [{ type: "text", text: contentStr }] };
|
|
3421
|
+
}
|
|
3422
|
+
await tx.execute({
|
|
3423
|
+
sql: `INSERT INTO "${TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
|
|
3424
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3425
|
+
args: [
|
|
3426
|
+
newMessageId,
|
|
3427
|
+
newThreadId,
|
|
3428
|
+
contentStr,
|
|
3429
|
+
sourceMsg.role,
|
|
3430
|
+
sourceMsg.type || "v2",
|
|
3431
|
+
sourceMsg.createdAt,
|
|
3432
|
+
targetResourceId
|
|
3433
|
+
]
|
|
3434
|
+
});
|
|
3435
|
+
clonedMessages.push({
|
|
3436
|
+
id: newMessageId,
|
|
3437
|
+
threadId: newThreadId,
|
|
3438
|
+
content: parsedContent,
|
|
3439
|
+
role: sourceMsg.role,
|
|
3440
|
+
type: sourceMsg.type || void 0,
|
|
3441
|
+
createdAt: new Date(sourceMsg.createdAt),
|
|
3442
|
+
resourceId: targetResourceId
|
|
3443
|
+
});
|
|
3444
|
+
}
|
|
3445
|
+
await tx.commit();
|
|
3446
|
+
return {
|
|
3447
|
+
thread: newThread,
|
|
3448
|
+
clonedMessages
|
|
3449
|
+
};
|
|
3450
|
+
} catch (error) {
|
|
3451
|
+
await tx.rollback();
|
|
3452
|
+
throw error;
|
|
3453
|
+
}
|
|
3454
|
+
} catch (error) {
|
|
3455
|
+
if (error instanceof MastraError) {
|
|
3456
|
+
throw error;
|
|
3457
|
+
}
|
|
3458
|
+
throw new MastraError(
|
|
3459
|
+
{
|
|
3460
|
+
id: createStorageErrorId("LIBSQL", "CLONE_THREAD", "FAILED"),
|
|
3461
|
+
domain: ErrorDomain.STORAGE,
|
|
3462
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3463
|
+
details: { sourceThreadId, newThreadId }
|
|
3464
|
+
},
|
|
3465
|
+
error
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
2771
3469
|
};
|
|
2772
3470
|
var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
2773
3471
|
#db;
|
|
@@ -2782,6 +3480,22 @@ var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
|
2782
3480
|
async dangerouslyClearAll() {
|
|
2783
3481
|
await this.#db.deleteData({ tableName: TABLE_SPANS });
|
|
2784
3482
|
}
|
|
3483
|
+
/**
|
|
3484
|
+
* Manually run the spans migration to deduplicate and add the unique constraint.
|
|
3485
|
+
* This is intended to be called from the CLI when duplicates are detected.
|
|
3486
|
+
*
|
|
3487
|
+
* @returns Migration result with status and details
|
|
3488
|
+
*/
|
|
3489
|
+
async migrateSpans() {
|
|
3490
|
+
return this.#db.migrateSpans();
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Check migration status for the spans table.
|
|
3494
|
+
* Returns information about whether migration is needed.
|
|
3495
|
+
*/
|
|
3496
|
+
async checkSpansMigrationStatus() {
|
|
3497
|
+
return this.#db.checkSpansMigrationStatus();
|
|
3498
|
+
}
|
|
2785
3499
|
get tracingStrategy() {
|
|
2786
3500
|
return {
|
|
2787
3501
|
preferred: "batch-with-updates",
|
|
@@ -3110,7 +3824,9 @@ var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
|
3110
3824
|
perPage,
|
|
3111
3825
|
hasMore: (page + 1) * perPage < count
|
|
3112
3826
|
},
|
|
3113
|
-
spans:
|
|
3827
|
+
spans: toTraceSpans(
|
|
3828
|
+
spans.map((span) => transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: span }))
|
|
3829
|
+
)
|
|
3114
3830
|
};
|
|
3115
3831
|
} catch (error) {
|
|
3116
3832
|
throw new MastraError(
|
|
@@ -3249,7 +3965,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3249
3965
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3250
3966
|
const end = perPageInput === false ? total : start + perPage;
|
|
3251
3967
|
const result = await this.#client.execute({
|
|
3252
|
-
sql: `SELECT
|
|
3968
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3253
3969
|
args: [runId, limitValue, start]
|
|
3254
3970
|
});
|
|
3255
3971
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
@@ -3322,7 +4038,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3322
4038
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3323
4039
|
const end = perPageInput === false ? total : start + perPage;
|
|
3324
4040
|
const result = await this.#client.execute({
|
|
3325
|
-
sql: `SELECT
|
|
4041
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3326
4042
|
args: [...queryParams, limitValue, start]
|
|
3327
4043
|
});
|
|
3328
4044
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
@@ -3348,16 +4064,13 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3348
4064
|
}
|
|
3349
4065
|
/**
|
|
3350
4066
|
* LibSQL-specific score row transformation.
|
|
3351
|
-
* Maps additionalLLMContext column to additionalContext field.
|
|
3352
4067
|
*/
|
|
3353
4068
|
transformScoreRow(row) {
|
|
3354
|
-
return transformScoreRow(row
|
|
3355
|
-
fieldMappings: { additionalContext: "additionalLLMContext" }
|
|
3356
|
-
});
|
|
4069
|
+
return transformScoreRow(row);
|
|
3357
4070
|
}
|
|
3358
4071
|
async getScoreById({ id }) {
|
|
3359
4072
|
const result = await this.#client.execute({
|
|
3360
|
-
sql: `SELECT
|
|
4073
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} WHERE id = ?`,
|
|
3361
4074
|
args: [id]
|
|
3362
4075
|
});
|
|
3363
4076
|
return result.rows?.[0] ? this.transformScoreRow(result.rows[0]) : null;
|
|
@@ -3435,7 +4148,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3435
4148
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3436
4149
|
const end = perPageInput === false ? total : start + perPage;
|
|
3437
4150
|
const result = await this.#client.execute({
|
|
3438
|
-
sql: `SELECT
|
|
4151
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3439
4152
|
args: [entityId, entityType, limitValue, start]
|
|
3440
4153
|
});
|
|
3441
4154
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
@@ -3476,7 +4189,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3476
4189
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3477
4190
|
const end = perPageInput === false ? total : start + perPage;
|
|
3478
4191
|
const result = await this.#client.execute({
|
|
3479
|
-
sql: `SELECT
|
|
4192
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3480
4193
|
args: [traceId, spanId, limitValue, start]
|
|
3481
4194
|
});
|
|
3482
4195
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
@@ -3501,24 +4214,6 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3501
4214
|
}
|
|
3502
4215
|
}
|
|
3503
4216
|
};
|
|
3504
|
-
function parseWorkflowRun(row) {
|
|
3505
|
-
let parsedSnapshot = row.snapshot;
|
|
3506
|
-
if (typeof parsedSnapshot === "string") {
|
|
3507
|
-
try {
|
|
3508
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
3509
|
-
} catch (e) {
|
|
3510
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
return {
|
|
3514
|
-
workflowName: row.workflow_name,
|
|
3515
|
-
runId: row.run_id,
|
|
3516
|
-
snapshot: parsedSnapshot,
|
|
3517
|
-
resourceId: row.resourceId,
|
|
3518
|
-
createdAt: new Date(row.createdAt),
|
|
3519
|
-
updatedAt: new Date(row.updatedAt)
|
|
3520
|
-
};
|
|
3521
|
-
}
|
|
3522
4217
|
var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
3523
4218
|
#db;
|
|
3524
4219
|
#client;
|
|
@@ -3539,6 +4234,24 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3539
4234
|
(err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
|
|
3540
4235
|
);
|
|
3541
4236
|
}
|
|
4237
|
+
parseWorkflowRun(row) {
|
|
4238
|
+
let parsedSnapshot = row.snapshot;
|
|
4239
|
+
if (typeof parsedSnapshot === "string") {
|
|
4240
|
+
try {
|
|
4241
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
4242
|
+
} catch (e) {
|
|
4243
|
+
this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
return {
|
|
4247
|
+
workflowName: row.workflow_name,
|
|
4248
|
+
runId: row.run_id,
|
|
4249
|
+
snapshot: parsedSnapshot,
|
|
4250
|
+
resourceId: row.resourceId,
|
|
4251
|
+
createdAt: new Date(row.createdAt),
|
|
4252
|
+
updatedAt: new Date(row.updatedAt)
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
3542
4255
|
async init() {
|
|
3543
4256
|
const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
|
|
3544
4257
|
await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
|
|
@@ -3582,7 +4295,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3582
4295
|
const tx = await this.#client.transaction("write");
|
|
3583
4296
|
try {
|
|
3584
4297
|
const existingSnapshotResult = await tx.execute({
|
|
3585
|
-
sql: `SELECT snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
4298
|
+
sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
3586
4299
|
args: [workflowName, runId]
|
|
3587
4300
|
});
|
|
3588
4301
|
let snapshot;
|
|
@@ -3607,9 +4320,13 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3607
4320
|
}
|
|
3608
4321
|
snapshot.context[stepId] = result;
|
|
3609
4322
|
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
4323
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3610
4324
|
await tx.execute({
|
|
3611
|
-
sql: `
|
|
3612
|
-
|
|
4325
|
+
sql: `INSERT INTO ${TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, createdAt, updatedAt)
|
|
4326
|
+
VALUES (?, ?, jsonb(?), ?, ?)
|
|
4327
|
+
ON CONFLICT(workflow_name, run_id)
|
|
4328
|
+
DO UPDATE SET snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
|
|
4329
|
+
args: [workflowName, runId, JSON.stringify(snapshot), now, now]
|
|
3613
4330
|
});
|
|
3614
4331
|
await tx.commit();
|
|
3615
4332
|
return snapshot.context;
|
|
@@ -3630,7 +4347,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3630
4347
|
const tx = await this.#client.transaction("write");
|
|
3631
4348
|
try {
|
|
3632
4349
|
const existingSnapshotResult = await tx.execute({
|
|
3633
|
-
sql: `SELECT snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
4350
|
+
sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
3634
4351
|
args: [workflowName, runId]
|
|
3635
4352
|
});
|
|
3636
4353
|
if (!existingSnapshotResult.rows?.[0]) {
|
|
@@ -3645,7 +4362,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3645
4362
|
}
|
|
3646
4363
|
const updatedSnapshot = { ...snapshot, ...opts };
|
|
3647
4364
|
await tx.execute({
|
|
3648
|
-
sql: `UPDATE ${TABLE_WORKFLOW_SNAPSHOT} SET snapshot = ? WHERE workflow_name = ? AND run_id = ?`,
|
|
4365
|
+
sql: `UPDATE ${TABLE_WORKFLOW_SNAPSHOT} SET snapshot = jsonb(?) WHERE workflow_name = ? AND run_id = ?`,
|
|
3649
4366
|
args: [JSON.stringify(updatedSnapshot), workflowName, runId]
|
|
3650
4367
|
});
|
|
3651
4368
|
await tx.commit();
|
|
@@ -3709,13 +4426,13 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3709
4426
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3710
4427
|
try {
|
|
3711
4428
|
const result = await this.#client.execute({
|
|
3712
|
-
sql: `SELECT
|
|
4429
|
+
sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
|
|
3713
4430
|
args
|
|
3714
4431
|
});
|
|
3715
4432
|
if (!result.rows?.[0]) {
|
|
3716
4433
|
return null;
|
|
3717
4434
|
}
|
|
3718
|
-
return parseWorkflowRun(result.rows[0]);
|
|
4435
|
+
return this.parseWorkflowRun(result.rows[0]);
|
|
3719
4436
|
} catch (error) {
|
|
3720
4437
|
throw new MastraError(
|
|
3721
4438
|
{
|
|
@@ -3781,7 +4498,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3781
4498
|
conditions.push("resourceId = ?");
|
|
3782
4499
|
args.push(resourceId);
|
|
3783
4500
|
} else {
|
|
3784
|
-
|
|
4501
|
+
this.logger.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
3785
4502
|
}
|
|
3786
4503
|
}
|
|
3787
4504
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
@@ -3797,10 +4514,10 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3797
4514
|
const normalizedPerPage = usePagination ? normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
|
|
3798
4515
|
const offset = usePagination ? page * normalizedPerPage : 0;
|
|
3799
4516
|
const result = await this.#client.execute({
|
|
3800
|
-
sql: `SELECT
|
|
4517
|
+
sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
|
|
3801
4518
|
args: usePagination ? [...args, normalizedPerPage, offset] : args
|
|
3802
4519
|
});
|
|
3803
|
-
const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
|
|
4520
|
+
const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
|
|
3804
4521
|
return { runs, total: total || runs.length };
|
|
3805
4522
|
} catch (error) {
|
|
3806
4523
|
throw new MastraError(
|
|
@@ -3816,7 +4533,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3816
4533
|
};
|
|
3817
4534
|
|
|
3818
4535
|
// src/storage/index.ts
|
|
3819
|
-
var LibSQLStore = class extends
|
|
4536
|
+
var LibSQLStore = class extends MastraCompositeStore {
|
|
3820
4537
|
client;
|
|
3821
4538
|
maxRetries;
|
|
3822
4539
|
initialBackoffMs;
|
|
@@ -3861,19 +4578,6 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
3861
4578
|
agents
|
|
3862
4579
|
};
|
|
3863
4580
|
}
|
|
3864
|
-
get supports() {
|
|
3865
|
-
return {
|
|
3866
|
-
selectByIncludeResourceScope: true,
|
|
3867
|
-
resourceWorkingMemory: true,
|
|
3868
|
-
hasColumn: true,
|
|
3869
|
-
createTable: true,
|
|
3870
|
-
deleteMessages: true,
|
|
3871
|
-
observability: true,
|
|
3872
|
-
indexManagement: false,
|
|
3873
|
-
listScoresBySpan: true,
|
|
3874
|
-
agents: true
|
|
3875
|
-
};
|
|
3876
|
-
}
|
|
3877
4581
|
};
|
|
3878
4582
|
|
|
3879
4583
|
// src/vector/prompt.ts
|
|
@@ -3975,6 +4679,6 @@ Example Complex Query:
|
|
|
3975
4679
|
]
|
|
3976
4680
|
}`;
|
|
3977
4681
|
|
|
3978
|
-
export { LibSQLStore as DefaultStorage, LIBSQL_PROMPT, LibSQLStore, LibSQLVector };
|
|
4682
|
+
export { AgentsLibSQL, LibSQLStore as DefaultStorage, LIBSQL_PROMPT, LibSQLStore, LibSQLVector, MemoryLibSQL, ObservabilityLibSQL, ScoresLibSQL, WorkflowsLibSQL };
|
|
3979
4683
|
//# sourceMappingURL=index.js.map
|
|
3980
4684
|
//# sourceMappingURL=index.js.map
|