@mastra/pg 0.10.2-alpha.1 → 0.10.2-alpha.2
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 +8 -0
- package/dist/_tsup-dts-rollup.d.cts +3 -0
- package/dist/_tsup-dts-rollup.d.ts +3 -0
- package/dist/index.cjs +74 -43
- package/dist/index.js +74 -43
- package/package.json +3 -3
- package/src/storage/index.test.ts +107 -8
- package/src/storage/index.ts +76 -38
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.2 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 10152ms
|
|
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 12410ms
|
|
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 1748ms
|
|
22
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m67.79 KB[39m
|
|
23
|
+
[32mCJS[39m ⚡️ Build success in 1748ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @mastra/pg
|
|
2
2
|
|
|
3
|
+
## 0.10.2-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 48eddb9: update filter logic in Memory class to support semantic recall search scope
|
|
8
|
+
- Updated dependencies [48eddb9]
|
|
9
|
+
- @mastra/core@0.10.4-alpha.2
|
|
10
|
+
|
|
3
11
|
## 0.10.2-alpha.1
|
|
4
12
|
|
|
5
13
|
### 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,62 @@ 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
|
-
|
|
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 dedupedRows = Object.values(
|
|
1489
|
+
includedRows.reduce(
|
|
1490
|
+
(acc, row) => {
|
|
1491
|
+
acc[row.id] = row;
|
|
1492
|
+
return acc;
|
|
1493
|
+
},
|
|
1494
|
+
{}
|
|
1495
|
+
)
|
|
1477
1496
|
);
|
|
1497
|
+
rows = dedupedRows;
|
|
1478
1498
|
} else {
|
|
1479
1499
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1480
1500
|
if (limit === 0 && selectBy?.last !== false) ; else {
|
|
@@ -1578,16 +1598,27 @@ var PostgresStore = class extends storage.MastraStorage {
|
|
|
1578
1598
|
}
|
|
1579
1599
|
await this.db.tx(async (t) => {
|
|
1580
1600
|
for (const message of messages) {
|
|
1601
|
+
if (!message.threadId) {
|
|
1602
|
+
throw new Error(
|
|
1603
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
if (!message.resourceId) {
|
|
1607
|
+
throw new Error(
|
|
1608
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1581
1611
|
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)`,
|
|
1612
|
+
`INSERT INTO ${this.getTableName(storage.TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
1613
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
1584
1614
|
[
|
|
1585
1615
|
message.id,
|
|
1586
|
-
threadId,
|
|
1616
|
+
message.threadId,
|
|
1587
1617
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1588
1618
|
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1589
1619
|
message.role,
|
|
1590
|
-
message.type || "v2"
|
|
1620
|
+
message.type || "v2",
|
|
1621
|
+
message.resourceId
|
|
1591
1622
|
]
|
|
1592
1623
|
);
|
|
1593
1624
|
}
|
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,62 @@ 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
|
-
|
|
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 dedupedRows = Object.values(
|
|
1481
|
+
includedRows.reduce(
|
|
1482
|
+
(acc, row) => {
|
|
1483
|
+
acc[row.id] = row;
|
|
1484
|
+
return acc;
|
|
1485
|
+
},
|
|
1486
|
+
{}
|
|
1487
|
+
)
|
|
1469
1488
|
);
|
|
1489
|
+
rows = dedupedRows;
|
|
1470
1490
|
} else {
|
|
1471
1491
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1472
1492
|
if (limit === 0 && selectBy?.last !== false) ; else {
|
|
@@ -1570,16 +1590,27 @@ var PostgresStore = class extends MastraStorage {
|
|
|
1570
1590
|
}
|
|
1571
1591
|
await this.db.tx(async (t) => {
|
|
1572
1592
|
for (const message of messages) {
|
|
1593
|
+
if (!message.threadId) {
|
|
1594
|
+
throw new Error(
|
|
1595
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
if (!message.resourceId) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1573
1603
|
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)`,
|
|
1604
|
+
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
1605
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
1576
1606
|
[
|
|
1577
1607
|
message.id,
|
|
1578
|
-
threadId,
|
|
1608
|
+
message.threadId,
|
|
1579
1609
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1580
1610
|
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1581
1611
|
message.role,
|
|
1582
|
-
message.type || "v2"
|
|
1612
|
+
message.type || "v2",
|
|
1613
|
+
message.resourceId
|
|
1583
1614
|
]
|
|
1584
1615
|
);
|
|
1585
1616
|
}
|
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.2",
|
|
4
4
|
"description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"typescript": "^5.8.2",
|
|
35
35
|
"vitest": "^3.2.2",
|
|
36
36
|
"@internal/lint": "0.0.10",
|
|
37
|
-
"@
|
|
38
|
-
"@
|
|
37
|
+
"@mastra/core": "0.10.4-alpha.2",
|
|
38
|
+
"@internal/storage-test-utils": "0.0.6"
|
|
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,63 @@ 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
|
-
|
|
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 dedupedRows = Object.values(
|
|
765
|
+
includedRows.reduce(
|
|
766
|
+
(acc, row) => {
|
|
767
|
+
acc[row.id] = row;
|
|
768
|
+
return acc;
|
|
769
|
+
},
|
|
770
|
+
{} as Record<string, (typeof includedRows)[0]>,
|
|
771
|
+
),
|
|
746
772
|
);
|
|
773
|
+
rows = dedupedRows;
|
|
747
774
|
} else {
|
|
748
775
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
749
776
|
if (limit === 0 && selectBy?.last !== false) {
|
|
@@ -881,16 +908,27 @@ export class PostgresStore extends MastraStorage {
|
|
|
881
908
|
|
|
882
909
|
await this.db.tx(async t => {
|
|
883
910
|
for (const message of messages) {
|
|
911
|
+
if (!message.threadId) {
|
|
912
|
+
throw new Error(
|
|
913
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
if (!message.resourceId) {
|
|
917
|
+
throw new Error(
|
|
918
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
919
|
+
);
|
|
920
|
+
}
|
|
884
921
|
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)`,
|
|
922
|
+
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
923
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
887
924
|
[
|
|
888
925
|
message.id,
|
|
889
|
-
threadId,
|
|
926
|
+
message.threadId,
|
|
890
927
|
typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
891
928
|
message.createdAt || new Date().toISOString(),
|
|
892
929
|
message.role,
|
|
893
930
|
message.type || 'v2',
|
|
931
|
+
message.resourceId,
|
|
894
932
|
],
|
|
895
933
|
);
|
|
896
934
|
}
|