@mastra/pg 0.10.2-alpha.1 → 0.10.2-alpha.3
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +16 -0
- package/dist/_tsup-dts-rollup.d.cts +3 -0
- package/dist/_tsup-dts-rollup.d.ts +3 -0
- package/dist/index.cjs +72 -44
- package/dist/index.js +72 -44
- package/package.json +2 -2
- package/src/storage/index.test.ts +107 -8
- package/src/storage/index.ts +74 -39
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/pg@0.10.2-alpha.
|
|
2
|
+
> @mastra/pg@0.10.2-alpha.3 build /home/runner/work/mastra/mastra/stores/pg
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.0
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 10698ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
14
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
15
15
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.cts[39m
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 10174ms
|
|
17
17
|
[34mCLI[39m Cleaning output folder
|
|
18
18
|
[34mESM[39m Build start
|
|
19
19
|
[34mCJS[39m Build start
|
|
20
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
21
|
-
[32mESM[39m ⚡️ Build success in
|
|
22
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
23
|
-
[32mCJS[39m ⚡️ Build success in
|
|
20
|
+
[32mESM[39m [1mdist/index.js [22m[32m67.23 KB[39m
|
|
21
|
+
[32mESM[39m ⚡️ Build success in 1908ms
|
|
22
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m67.79 KB[39m
|
|
23
|
+
[32mCJS[39m ⚡️ Build success in 1909ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @mastra/pg
|
|
2
2
|
|
|
3
|
+
## 0.10.2-alpha.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e0f9201: change how dedupe works for libsql and pg
|
|
8
|
+
- Updated dependencies [925ab94]
|
|
9
|
+
- @mastra/core@0.10.4-alpha.3
|
|
10
|
+
|
|
11
|
+
## 0.10.2-alpha.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 48eddb9: update filter logic in Memory class to support semantic recall search scope
|
|
16
|
+
- Updated dependencies [48eddb9]
|
|
17
|
+
- @mastra/core@0.10.4-alpha.2
|
|
18
|
+
|
|
3
19
|
## 0.10.2-alpha.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -272,6 +272,9 @@ declare class PostgresStore extends MastraStorage {
|
|
|
272
272
|
private setupSchemaPromise;
|
|
273
273
|
private schemaSetupComplete;
|
|
274
274
|
constructor(config: PostgresConfig);
|
|
275
|
+
get supports(): {
|
|
276
|
+
selectByIncludeResourceScope: boolean;
|
|
277
|
+
};
|
|
275
278
|
private getTableName;
|
|
276
279
|
/** @deprecated use getEvals instead */
|
|
277
280
|
getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
|
|
@@ -272,6 +272,9 @@ declare class PostgresStore extends MastraStorage {
|
|
|
272
272
|
private setupSchemaPromise;
|
|
273
273
|
private schemaSetupComplete;
|
|
274
274
|
constructor(config: PostgresConfig);
|
|
275
|
+
get supports(): {
|
|
276
|
+
selectByIncludeResourceScope: boolean;
|
|
277
|
+
};
|
|
275
278
|
private getTableName;
|
|
276
279
|
/** @deprecated use getEvals instead */
|
|
277
280
|
getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
|
package/dist/index.cjs
CHANGED
|
@@ -944,6 +944,11 @@ var PostgresStore = class extends storage.MastraStorage {
|
|
|
944
944
|
}
|
|
945
945
|
);
|
|
946
946
|
}
|
|
947
|
+
get supports() {
|
|
948
|
+
return {
|
|
949
|
+
selectByIncludeResourceScope: true
|
|
950
|
+
};
|
|
951
|
+
}
|
|
947
952
|
getTableName(indexName) {
|
|
948
953
|
const parsedIndexName = utils.parseSqlIdentifier(indexName, "table name");
|
|
949
954
|
const parsedSchemaName = this.schema ? utils.parseSqlIdentifier(this.schema, "schema name") : void 0;
|
|
@@ -1434,47 +1439,59 @@ var PostgresStore = class extends storage.MastraStorage {
|
|
|
1434
1439
|
let rows = [];
|
|
1435
1440
|
const include = selectBy?.include || [];
|
|
1436
1441
|
if (include.length) {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
m.thread_id AS "threadId"
|
|
1453
|
-
FROM ordered_messages m
|
|
1454
|
-
WHERE m.id = ANY($2)
|
|
1455
|
-
OR EXISTS (
|
|
1456
|
-
SELECT 1 FROM ordered_messages target
|
|
1457
|
-
WHERE target.id = ANY($2)
|
|
1458
|
-
AND (
|
|
1459
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
1460
|
-
(m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
|
|
1461
|
-
OR
|
|
1462
|
-
-- Get next messages based on the max withNextMessages
|
|
1463
|
-
(m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
|
|
1442
|
+
const unionQueries = [];
|
|
1443
|
+
const params = [];
|
|
1444
|
+
let paramIdx = 1;
|
|
1445
|
+
for (const inc of include) {
|
|
1446
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1447
|
+
const searchId = inc.threadId || threadId;
|
|
1448
|
+
unionQueries.push(
|
|
1449
|
+
`
|
|
1450
|
+
SELECT * FROM (
|
|
1451
|
+
WITH ordered_messages AS (
|
|
1452
|
+
SELECT
|
|
1453
|
+
*,
|
|
1454
|
+
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
1455
|
+
FROM ${this.getTableName(storage.TABLE_MESSAGES)}
|
|
1456
|
+
WHERE thread_id = $${paramIdx}
|
|
1464
1457
|
)
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1458
|
+
SELECT
|
|
1459
|
+
m.id,
|
|
1460
|
+
m.content,
|
|
1461
|
+
m.role,
|
|
1462
|
+
m.type,
|
|
1463
|
+
m."createdAt",
|
|
1464
|
+
m.thread_id AS "threadId",
|
|
1465
|
+
m."resourceId"
|
|
1466
|
+
FROM ordered_messages m
|
|
1467
|
+
WHERE m.id = $${paramIdx + 1}
|
|
1468
|
+
OR EXISTS (
|
|
1469
|
+
SELECT 1 FROM ordered_messages target
|
|
1470
|
+
WHERE target.id = $${paramIdx + 1}
|
|
1471
|
+
AND (
|
|
1472
|
+
-- Get previous messages based on the max withPreviousMessages
|
|
1473
|
+
(m.row_num <= target.row_num + $${paramIdx + 2} AND m.row_num > target.row_num)
|
|
1474
|
+
OR
|
|
1475
|
+
-- Get next messages based on the max withNextMessages
|
|
1476
|
+
(m.row_num >= target.row_num - $${paramIdx + 3} AND m.row_num < target.row_num)
|
|
1477
|
+
)
|
|
1478
|
+
)
|
|
1479
|
+
)
|
|
1480
|
+
`
|
|
1481
|
+
// Keep ASC for final sorting after fetching context
|
|
1482
|
+
);
|
|
1483
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1484
|
+
paramIdx += 4;
|
|
1485
|
+
}
|
|
1486
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1487
|
+
const includedRows = await this.db.manyOrNone(finalQuery, params);
|
|
1488
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1489
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
1490
|
+
if (seen.has(row.id)) return false;
|
|
1491
|
+
seen.add(row.id);
|
|
1492
|
+
return true;
|
|
1493
|
+
});
|
|
1494
|
+
rows = dedupedRows;
|
|
1478
1495
|
} else {
|
|
1479
1496
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1480
1497
|
if (limit === 0 && selectBy?.last !== false) ; else {
|
|
@@ -1578,16 +1595,27 @@ var PostgresStore = class extends storage.MastraStorage {
|
|
|
1578
1595
|
}
|
|
1579
1596
|
await this.db.tx(async (t) => {
|
|
1580
1597
|
for (const message of messages) {
|
|
1598
|
+
if (!message.threadId) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
if (!message.resourceId) {
|
|
1604
|
+
throw new Error(
|
|
1605
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1581
1608
|
await t.none(
|
|
1582
|
-
`INSERT INTO ${this.getTableName(storage.TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type)
|
|
1583
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1609
|
+
`INSERT INTO ${this.getTableName(storage.TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
1610
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
1584
1611
|
[
|
|
1585
1612
|
message.id,
|
|
1586
|
-
threadId,
|
|
1613
|
+
message.threadId,
|
|
1587
1614
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1588
1615
|
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1589
1616
|
message.role,
|
|
1590
|
-
message.type || "v2"
|
|
1617
|
+
message.type || "v2",
|
|
1618
|
+
message.resourceId
|
|
1591
1619
|
]
|
|
1592
1620
|
);
|
|
1593
1621
|
}
|
package/dist/index.js
CHANGED
|
@@ -936,6 +936,11 @@ var PostgresStore = class extends MastraStorage {
|
|
|
936
936
|
}
|
|
937
937
|
);
|
|
938
938
|
}
|
|
939
|
+
get supports() {
|
|
940
|
+
return {
|
|
941
|
+
selectByIncludeResourceScope: true
|
|
942
|
+
};
|
|
943
|
+
}
|
|
939
944
|
getTableName(indexName) {
|
|
940
945
|
const parsedIndexName = parseSqlIdentifier(indexName, "table name");
|
|
941
946
|
const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, "schema name") : void 0;
|
|
@@ -1426,47 +1431,59 @@ var PostgresStore = class extends MastraStorage {
|
|
|
1426
1431
|
let rows = [];
|
|
1427
1432
|
const include = selectBy?.include || [];
|
|
1428
1433
|
if (include.length) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
m.thread_id AS "threadId"
|
|
1445
|
-
FROM ordered_messages m
|
|
1446
|
-
WHERE m.id = ANY($2)
|
|
1447
|
-
OR EXISTS (
|
|
1448
|
-
SELECT 1 FROM ordered_messages target
|
|
1449
|
-
WHERE target.id = ANY($2)
|
|
1450
|
-
AND (
|
|
1451
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
1452
|
-
(m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
|
|
1453
|
-
OR
|
|
1454
|
-
-- Get next messages based on the max withNextMessages
|
|
1455
|
-
(m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
|
|
1434
|
+
const unionQueries = [];
|
|
1435
|
+
const params = [];
|
|
1436
|
+
let paramIdx = 1;
|
|
1437
|
+
for (const inc of include) {
|
|
1438
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1439
|
+
const searchId = inc.threadId || threadId;
|
|
1440
|
+
unionQueries.push(
|
|
1441
|
+
`
|
|
1442
|
+
SELECT * FROM (
|
|
1443
|
+
WITH ordered_messages AS (
|
|
1444
|
+
SELECT
|
|
1445
|
+
*,
|
|
1446
|
+
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
1447
|
+
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
1448
|
+
WHERE thread_id = $${paramIdx}
|
|
1456
1449
|
)
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1450
|
+
SELECT
|
|
1451
|
+
m.id,
|
|
1452
|
+
m.content,
|
|
1453
|
+
m.role,
|
|
1454
|
+
m.type,
|
|
1455
|
+
m."createdAt",
|
|
1456
|
+
m.thread_id AS "threadId",
|
|
1457
|
+
m."resourceId"
|
|
1458
|
+
FROM ordered_messages m
|
|
1459
|
+
WHERE m.id = $${paramIdx + 1}
|
|
1460
|
+
OR EXISTS (
|
|
1461
|
+
SELECT 1 FROM ordered_messages target
|
|
1462
|
+
WHERE target.id = $${paramIdx + 1}
|
|
1463
|
+
AND (
|
|
1464
|
+
-- Get previous messages based on the max withPreviousMessages
|
|
1465
|
+
(m.row_num <= target.row_num + $${paramIdx + 2} AND m.row_num > target.row_num)
|
|
1466
|
+
OR
|
|
1467
|
+
-- Get next messages based on the max withNextMessages
|
|
1468
|
+
(m.row_num >= target.row_num - $${paramIdx + 3} AND m.row_num < target.row_num)
|
|
1469
|
+
)
|
|
1470
|
+
)
|
|
1471
|
+
)
|
|
1472
|
+
`
|
|
1473
|
+
// Keep ASC for final sorting after fetching context
|
|
1474
|
+
);
|
|
1475
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1476
|
+
paramIdx += 4;
|
|
1477
|
+
}
|
|
1478
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1479
|
+
const includedRows = await this.db.manyOrNone(finalQuery, params);
|
|
1480
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1481
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
1482
|
+
if (seen.has(row.id)) return false;
|
|
1483
|
+
seen.add(row.id);
|
|
1484
|
+
return true;
|
|
1485
|
+
});
|
|
1486
|
+
rows = dedupedRows;
|
|
1470
1487
|
} else {
|
|
1471
1488
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1472
1489
|
if (limit === 0 && selectBy?.last !== false) ; else {
|
|
@@ -1570,16 +1587,27 @@ var PostgresStore = class extends MastraStorage {
|
|
|
1570
1587
|
}
|
|
1571
1588
|
await this.db.tx(async (t) => {
|
|
1572
1589
|
for (const message of messages) {
|
|
1590
|
+
if (!message.threadId) {
|
|
1591
|
+
throw new Error(
|
|
1592
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
if (!message.resourceId) {
|
|
1596
|
+
throw new Error(
|
|
1597
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1573
1600
|
await t.none(
|
|
1574
|
-
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type)
|
|
1575
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
1601
|
+
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
1602
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
1576
1603
|
[
|
|
1577
1604
|
message.id,
|
|
1578
|
-
threadId,
|
|
1605
|
+
message.threadId,
|
|
1579
1606
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1580
1607
|
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1581
1608
|
message.role,
|
|
1582
|
-
message.type || "v2"
|
|
1609
|
+
message.type || "v2",
|
|
1610
|
+
message.resourceId
|
|
1583
1611
|
]
|
|
1584
1612
|
);
|
|
1585
1613
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/pg",
|
|
3
|
-
"version": "0.10.2-alpha.
|
|
3
|
+
"version": "0.10.2-alpha.3",
|
|
4
4
|
"description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"vitest": "^3.2.2",
|
|
36
36
|
"@internal/lint": "0.0.10",
|
|
37
37
|
"@internal/storage-test-utils": "0.0.6",
|
|
38
|
-
"@mastra/core": "0.10.4-alpha.
|
|
38
|
+
"@mastra/core": "0.10.4-alpha.3"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@mastra/core": "^0.10.2-alpha.0"
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
createSampleMessageV2,
|
|
8
8
|
createSampleWorkflowSnapshot,
|
|
9
9
|
resetRole,
|
|
10
|
+
checkWorkflowSnapshot,
|
|
10
11
|
} from '@internal/storage-test-utils';
|
|
11
|
-
import type { MastraMessageV1, StorageThreadType } from '@mastra/core/memory';
|
|
12
|
+
import type { MastraMessageV1, MastraMessageV2, StorageThreadType } from '@mastra/core/memory';
|
|
12
13
|
import type { StorageColumn, TABLE_NAMES } from '@mastra/core/storage';
|
|
13
14
|
import {
|
|
14
15
|
TABLE_WORKFLOW_SNAPSHOT,
|
|
@@ -36,13 +37,6 @@ const connectionString = `postgresql://${TEST_CONFIG.user}:${TEST_CONFIG.passwor
|
|
|
36
37
|
|
|
37
38
|
vi.setConfig({ testTimeout: 60_000, hookTimeout: 60_000 });
|
|
38
39
|
|
|
39
|
-
const checkWorkflowSnapshot = (snapshot: WorkflowRunState | string, stepId: string, status: string) => {
|
|
40
|
-
if (typeof snapshot === 'string') {
|
|
41
|
-
throw new Error('Expected WorkflowRunState, got string');
|
|
42
|
-
}
|
|
43
|
-
expect(snapshot.context?.[stepId]?.status).toBe(status);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
40
|
describe('PostgresStore', () => {
|
|
47
41
|
let store: PostgresStore;
|
|
48
42
|
|
|
@@ -241,6 +235,110 @@ describe('PostgresStore', () => {
|
|
|
241
235
|
const savedMessages = await store.getMessages({ threadId: thread.id });
|
|
242
236
|
expect(savedMessages).toHaveLength(0);
|
|
243
237
|
});
|
|
238
|
+
|
|
239
|
+
it('should retrieve messages w/ next/prev messages by message id + resource id', async () => {
|
|
240
|
+
const thread = createSampleThread({ id: 'thread-one' });
|
|
241
|
+
await store.saveThread({ thread });
|
|
242
|
+
|
|
243
|
+
const thread2 = createSampleThread({ id: 'thread-two' });
|
|
244
|
+
await store.saveThread({ thread: thread2 });
|
|
245
|
+
|
|
246
|
+
const thread3 = createSampleThread({ id: 'thread-three' });
|
|
247
|
+
await store.saveThread({ thread: thread3 });
|
|
248
|
+
|
|
249
|
+
const messages: MastraMessageV2[] = [
|
|
250
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'First', resourceId: 'cross-thread-resource' }),
|
|
251
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'Second', resourceId: 'cross-thread-resource' }),
|
|
252
|
+
createSampleMessageV2({ threadId: 'thread-one', content: 'Third', resourceId: 'cross-thread-resource' }),
|
|
253
|
+
|
|
254
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Fourth', resourceId: 'cross-thread-resource' }),
|
|
255
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Fifth', resourceId: 'cross-thread-resource' }),
|
|
256
|
+
createSampleMessageV2({ threadId: 'thread-two', content: 'Sixth', resourceId: 'cross-thread-resource' }),
|
|
257
|
+
|
|
258
|
+
createSampleMessageV2({ threadId: 'thread-three', content: 'Seventh', resourceId: 'other-resource' }),
|
|
259
|
+
createSampleMessageV2({ threadId: 'thread-three', content: 'Eighth', resourceId: 'other-resource' }),
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
await store.saveMessages({ messages: messages, format: 'v2' });
|
|
263
|
+
|
|
264
|
+
const retrievedMessages = await store.getMessages({ threadId: 'thread-one', format: 'v2' });
|
|
265
|
+
expect(retrievedMessages).toHaveLength(3);
|
|
266
|
+
expect(retrievedMessages.map((m: any) => m.content.parts[0].text)).toEqual(['First', 'Second', 'Third']);
|
|
267
|
+
|
|
268
|
+
const retrievedMessages2 = await store.getMessages({ threadId: 'thread-two', format: 'v2' });
|
|
269
|
+
expect(retrievedMessages2).toHaveLength(3);
|
|
270
|
+
expect(retrievedMessages2.map((m: any) => m.content.parts[0].text)).toEqual(['Fourth', 'Fifth', 'Sixth']);
|
|
271
|
+
|
|
272
|
+
const retrievedMessages3 = await store.getMessages({ threadId: 'thread-three', format: 'v2' });
|
|
273
|
+
expect(retrievedMessages3).toHaveLength(2);
|
|
274
|
+
expect(retrievedMessages3.map((m: any) => m.content.parts[0].text)).toEqual(['Seventh', 'Eighth']);
|
|
275
|
+
|
|
276
|
+
const crossThreadMessages: MastraMessageV2[] = await store.getMessages({
|
|
277
|
+
threadId: 'thread-doesnt-exist',
|
|
278
|
+
format: 'v2',
|
|
279
|
+
selectBy: {
|
|
280
|
+
last: 0,
|
|
281
|
+
include: [
|
|
282
|
+
{
|
|
283
|
+
id: messages[1].id,
|
|
284
|
+
threadId: 'thread-one',
|
|
285
|
+
withNextMessages: 2,
|
|
286
|
+
withPreviousMessages: 2,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: messages[4].id,
|
|
290
|
+
threadId: 'thread-two',
|
|
291
|
+
withPreviousMessages: 2,
|
|
292
|
+
withNextMessages: 2,
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
expect(crossThreadMessages).toHaveLength(6);
|
|
299
|
+
expect(crossThreadMessages.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
|
|
300
|
+
expect(crossThreadMessages.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
|
|
301
|
+
|
|
302
|
+
const crossThreadMessages2: MastraMessageV2[] = await store.getMessages({
|
|
303
|
+
threadId: 'thread-one',
|
|
304
|
+
format: 'v2',
|
|
305
|
+
selectBy: {
|
|
306
|
+
last: 0,
|
|
307
|
+
include: [
|
|
308
|
+
{
|
|
309
|
+
id: messages[4].id,
|
|
310
|
+
threadId: 'thread-two',
|
|
311
|
+
withPreviousMessages: 1,
|
|
312
|
+
withNextMessages: 1,
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(crossThreadMessages2).toHaveLength(3);
|
|
319
|
+
expect(crossThreadMessages2.filter(m => m.threadId === `thread-one`)).toHaveLength(0);
|
|
320
|
+
expect(crossThreadMessages2.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
|
|
321
|
+
|
|
322
|
+
const crossThreadMessages3: MastraMessageV2[] = await store.getMessages({
|
|
323
|
+
threadId: 'thread-two',
|
|
324
|
+
format: 'v2',
|
|
325
|
+
selectBy: {
|
|
326
|
+
last: 0,
|
|
327
|
+
include: [
|
|
328
|
+
{
|
|
329
|
+
id: messages[1].id,
|
|
330
|
+
threadId: 'thread-one',
|
|
331
|
+
withNextMessages: 1,
|
|
332
|
+
withPreviousMessages: 1,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
expect(crossThreadMessages3).toHaveLength(3);
|
|
339
|
+
expect(crossThreadMessages3.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
|
|
340
|
+
expect(crossThreadMessages3.filter(m => m.threadId === `thread-two`)).toHaveLength(0);
|
|
341
|
+
});
|
|
244
342
|
});
|
|
245
343
|
|
|
246
344
|
describe('Edge Cases and Error Handling', () => {
|
|
@@ -1279,6 +1377,7 @@ describe('PostgresStore', () => {
|
|
|
1279
1377
|
});
|
|
1280
1378
|
|
|
1281
1379
|
it('should filter by date with pagination for getMessages', async () => {
|
|
1380
|
+
resetRole();
|
|
1282
1381
|
const threadData = createSampleThread();
|
|
1283
1382
|
const thread = await store.saveThread({ thread: threadData as StorageThreadType });
|
|
1284
1383
|
const now = new Date();
|
package/src/storage/index.ts
CHANGED
|
@@ -87,6 +87,14 @@ export class PostgresStore extends MastraStorage {
|
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
public get supports(): {
|
|
91
|
+
selectByIncludeResourceScope: boolean;
|
|
92
|
+
} {
|
|
93
|
+
return {
|
|
94
|
+
selectByIncludeResourceScope: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
private getTableName(indexName: string) {
|
|
91
99
|
const parsedIndexName = parseSqlIdentifier(indexName, 'table name');
|
|
92
100
|
const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, 'schema name') : undefined;
|
|
@@ -706,44 +714,60 @@ export class PostgresStore extends MastraStorage {
|
|
|
706
714
|
const include = selectBy?.include || [];
|
|
707
715
|
|
|
708
716
|
if (include.length) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
WHERE m.id = ANY($2)
|
|
727
|
-
OR EXISTS (
|
|
728
|
-
SELECT 1 FROM ordered_messages target
|
|
729
|
-
WHERE target.id = ANY($2)
|
|
730
|
-
AND (
|
|
731
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
732
|
-
(m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
|
|
733
|
-
OR
|
|
734
|
-
-- Get next messages based on the max withNextMessages
|
|
735
|
-
(m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
|
|
717
|
+
const unionQueries: string[] = [];
|
|
718
|
+
const params: any[] = [];
|
|
719
|
+
let paramIdx = 1;
|
|
720
|
+
|
|
721
|
+
for (const inc of include) {
|
|
722
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
723
|
+
// if threadId is provided, use it, otherwise use threadId from args
|
|
724
|
+
const searchId = inc.threadId || threadId;
|
|
725
|
+
unionQueries.push(
|
|
726
|
+
`
|
|
727
|
+
SELECT * FROM (
|
|
728
|
+
WITH ordered_messages AS (
|
|
729
|
+
SELECT
|
|
730
|
+
*,
|
|
731
|
+
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
732
|
+
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
733
|
+
WHERE thread_id = $${paramIdx}
|
|
736
734
|
)
|
|
737
|
-
|
|
738
|
-
|
|
735
|
+
SELECT
|
|
736
|
+
m.id,
|
|
737
|
+
m.content,
|
|
738
|
+
m.role,
|
|
739
|
+
m.type,
|
|
740
|
+
m."createdAt",
|
|
741
|
+
m.thread_id AS "threadId",
|
|
742
|
+
m."resourceId"
|
|
743
|
+
FROM ordered_messages m
|
|
744
|
+
WHERE m.id = $${paramIdx + 1}
|
|
745
|
+
OR EXISTS (
|
|
746
|
+
SELECT 1 FROM ordered_messages target
|
|
747
|
+
WHERE target.id = $${paramIdx + 1}
|
|
748
|
+
AND (
|
|
749
|
+
-- Get previous messages based on the max withPreviousMessages
|
|
750
|
+
(m.row_num <= target.row_num + $${paramIdx + 2} AND m.row_num > target.row_num)
|
|
751
|
+
OR
|
|
752
|
+
-- Get next messages based on the max withNextMessages
|
|
753
|
+
(m.row_num >= target.row_num - $${paramIdx + 3} AND m.row_num < target.row_num)
|
|
754
|
+
)
|
|
755
|
+
)
|
|
756
|
+
)
|
|
739
757
|
`, // Keep ASC for final sorting after fetching context
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
);
|
|
758
|
+
);
|
|
759
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
760
|
+
paramIdx += 4;
|
|
761
|
+
}
|
|
762
|
+
const finalQuery = unionQueries.join(' UNION ALL ') + ' ORDER BY "createdAt" ASC';
|
|
763
|
+
const includedRows = await this.db.manyOrNone(finalQuery, params);
|
|
764
|
+
const seen = new Set<string>();
|
|
765
|
+
const dedupedRows = includedRows.filter(row => {
|
|
766
|
+
if (seen.has(row.id)) return false;
|
|
767
|
+
seen.add(row.id);
|
|
768
|
+
return true;
|
|
769
|
+
});
|
|
770
|
+
rows = dedupedRows;
|
|
747
771
|
} else {
|
|
748
772
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
749
773
|
if (limit === 0 && selectBy?.last !== false) {
|
|
@@ -881,16 +905,27 @@ export class PostgresStore extends MastraStorage {
|
|
|
881
905
|
|
|
882
906
|
await this.db.tx(async t => {
|
|
883
907
|
for (const message of messages) {
|
|
908
|
+
if (!message.threadId) {
|
|
909
|
+
throw new Error(
|
|
910
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
if (!message.resourceId) {
|
|
914
|
+
throw new Error(
|
|
915
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
884
918
|
await t.none(
|
|
885
|
-
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type)
|
|
886
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
919
|
+
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
920
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
887
921
|
[
|
|
888
922
|
message.id,
|
|
889
|
-
threadId,
|
|
923
|
+
message.threadId,
|
|
890
924
|
typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
891
925
|
message.createdAt || new Date().toISOString(),
|
|
892
926
|
message.role,
|
|
893
927
|
message.type || 'v2',
|
|
928
|
+
message.resourceId,
|
|
894
929
|
],
|
|
895
930
|
);
|
|
896
931
|
}
|