@mastra/pg 1.7.2 → 1.8.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 +124 -0
- package/dist/docs/SKILL.md +14 -14
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +9 -9
- package/dist/docs/references/docs-memory-storage.md +1 -1
- package/dist/docs/references/docs-memory-working-memory.md +20 -20
- package/dist/docs/references/docs-rag-overview.md +2 -2
- package/dist/docs/references/docs-rag-retrieval.md +4 -4
- package/dist/docs/references/docs-rag-vector-databases.md +13 -13
- package/dist/docs/references/reference-memory-memory-class.md +1 -1
- package/dist/docs/references/reference-processors-message-history-processor.md +1 -1
- package/dist/docs/references/reference-rag-metadata-filters.md +15 -15
- package/dist/docs/references/reference-storage-composite.md +1 -1
- package/dist/docs/references/reference-storage-dynamodb.md +7 -7
- package/dist/docs/references/reference-storage-postgresql.md +7 -7
- package/dist/docs/references/reference-tools-vector-query-tool.md +12 -12
- package/dist/docs/references/reference-vectors-pg.md +23 -21
- package/dist/index.cjs +379 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +379 -91
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +13 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +7 -2
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts +44 -10
- package/dist/vector/index.d.ts.map +1 -1
- package/dist/vector/types.d.ts +32 -2
- package/dist/vector/types.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -748,11 +748,22 @@ var PgVector = class extends vector.MastraVector {
|
|
|
748
748
|
}
|
|
749
749
|
return major > 0 || major === 0 && minor >= 7;
|
|
750
750
|
}
|
|
751
|
+
/** Checks if pgvector >= 0.7.0 (required for bit type). */
|
|
752
|
+
supportsBit() {
|
|
753
|
+
return this.supportsHalfvec();
|
|
754
|
+
}
|
|
755
|
+
/** Checks if pgvector >= 0.7.0 (required for sparsevec type). */
|
|
756
|
+
supportsSparsevec() {
|
|
757
|
+
return this.supportsHalfvec();
|
|
758
|
+
}
|
|
751
759
|
/**
|
|
752
760
|
* Gets the properly qualified vector type name
|
|
753
|
-
* @param vectorType - The type of vector storage
|
|
761
|
+
* @param vectorType - The type of vector storage
|
|
754
762
|
*/
|
|
755
|
-
getVectorTypeName(vectorType = "vector") {
|
|
763
|
+
getVectorTypeName(vectorType = "vector", dimension) {
|
|
764
|
+
if (vectorType === "bit") {
|
|
765
|
+
return dimension ? `bit(${dimension})` : "bit";
|
|
766
|
+
}
|
|
756
767
|
if (this.vectorExtensionSchema) {
|
|
757
768
|
if (this.vectorExtensionSchema === "pg_catalog") {
|
|
758
769
|
return vectorType;
|
|
@@ -763,20 +774,76 @@ var PgVector = class extends vector.MastraVector {
|
|
|
763
774
|
return vectorType;
|
|
764
775
|
}
|
|
765
776
|
/**
|
|
766
|
-
*
|
|
767
|
-
*
|
|
777
|
+
* Returns the operator class, distance operator, and score expression for a
|
|
778
|
+
* standard (non-bit) vector type prefix and metric.
|
|
768
779
|
*/
|
|
769
|
-
|
|
770
|
-
const prefix = vectorType === "halfvec" ? "halfvec" : "vector";
|
|
780
|
+
getMetricOps(prefix, metric) {
|
|
771
781
|
switch (metric) {
|
|
772
|
-
case "cosine":
|
|
773
|
-
return `${prefix}_cosine_ops`;
|
|
774
782
|
case "euclidean":
|
|
775
|
-
return
|
|
783
|
+
return {
|
|
784
|
+
operatorClass: `${prefix}_l2_ops`,
|
|
785
|
+
distanceOperator: "<->",
|
|
786
|
+
scoreExpr: (d) => `1.0 / (1.0 + (${d}))`
|
|
787
|
+
};
|
|
776
788
|
case "dotproduct":
|
|
777
|
-
return
|
|
789
|
+
return {
|
|
790
|
+
operatorClass: `${prefix}_ip_ops`,
|
|
791
|
+
distanceOperator: "<#>",
|
|
792
|
+
scoreExpr: (d) => `(${d}) * -1`
|
|
793
|
+
};
|
|
794
|
+
default:
|
|
795
|
+
return {
|
|
796
|
+
operatorClass: `${prefix}_cosine_ops`,
|
|
797
|
+
distanceOperator: "<=>",
|
|
798
|
+
scoreExpr: (d) => `1 - (${d})`
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Returns all vector-type-specific operations for the given vectorType and metric.
|
|
804
|
+
*/
|
|
805
|
+
getVectorOps(vectorType, metric) {
|
|
806
|
+
switch (vectorType) {
|
|
807
|
+
case "bit":
|
|
808
|
+
return {
|
|
809
|
+
operatorClass: metric === "jaccard" ? "bit_jaccard_ops" : "bit_hamming_ops",
|
|
810
|
+
distanceOperator: metric === "jaccard" ? "<%>" : "<~>",
|
|
811
|
+
scoreExpr: (d) => metric === "jaccard" ? `1 - (${d})` : `1 - ((${d})::float / bit_length(embedding))`,
|
|
812
|
+
formatVector: (v) => v.map((x) => x ? "1" : "0").join(""),
|
|
813
|
+
parseEmbedding: (e) => e.split("").map((c) => c === "1" ? 1 : 0)
|
|
814
|
+
};
|
|
815
|
+
case "sparsevec":
|
|
816
|
+
return {
|
|
817
|
+
...this.getMetricOps("sparsevec", metric),
|
|
818
|
+
formatVector: (v, dimension) => {
|
|
819
|
+
const dim = dimension ?? v.length;
|
|
820
|
+
const nonZero = v.map((val, i) => val !== 0 ? `${i + 1}:${val}` : null).filter(Boolean).join(",");
|
|
821
|
+
return `{${nonZero}}/${dim}`;
|
|
822
|
+
},
|
|
823
|
+
parseEmbedding: (e) => {
|
|
824
|
+
const match = e.match(/^\{([^}]*)\}\/(\d+)$/);
|
|
825
|
+
if (!match) return [];
|
|
826
|
+
const dim = parseInt(match[2], 10);
|
|
827
|
+
const result = new Array(dim).fill(0);
|
|
828
|
+
const entries = match[1];
|
|
829
|
+
if (entries.trim()) {
|
|
830
|
+
for (const entry of entries.split(",")) {
|
|
831
|
+
const [idxStr, valStr] = entry.split(":");
|
|
832
|
+
const idx = parseInt(idxStr.trim(), 10) - 1;
|
|
833
|
+
result[idx] = parseFloat(valStr.trim());
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return result;
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
case "halfvec":
|
|
840
|
+
case "vector":
|
|
778
841
|
default:
|
|
779
|
-
return
|
|
842
|
+
return {
|
|
843
|
+
...this.getMetricOps(vectorType === "halfvec" ? "halfvec" : "vector", metric),
|
|
844
|
+
formatVector: (v) => `[${v.join(",")}]`,
|
|
845
|
+
parseEmbedding: (e) => JSON.parse(e)
|
|
846
|
+
};
|
|
780
847
|
}
|
|
781
848
|
}
|
|
782
849
|
getTableName(indexName) {
|
|
@@ -879,10 +946,12 @@ var PgVector = class extends vector.MastraVector {
|
|
|
879
946
|
const client = await this.pool.connect();
|
|
880
947
|
try {
|
|
881
948
|
await client.query("BEGIN");
|
|
882
|
-
const vectorStr = `[${queryVector.join(",")}]`;
|
|
883
949
|
const translatedFilter = this.transformFilter(filter);
|
|
884
950
|
const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore, topK);
|
|
885
951
|
const indexInfo = await this.getIndexInfo({ indexName });
|
|
952
|
+
const metric = indexInfo.metric ?? "cosine";
|
|
953
|
+
const ops = this.getVectorOps(indexInfo.vectorType, metric);
|
|
954
|
+
const vectorStr = ops.formatVector(queryVector, indexInfo.dimension);
|
|
886
955
|
if (indexInfo.type === "hnsw") {
|
|
887
956
|
const calculatedEf = ef ?? Math.max(topK, (indexInfo?.config?.m ?? 16) * topK);
|
|
888
957
|
const searchEf = Math.min(1e3, Math.max(1, calculatedEf));
|
|
@@ -892,12 +961,14 @@ var PgVector = class extends vector.MastraVector {
|
|
|
892
961
|
await client.query(`SET LOCAL ivfflat.probes = ${probes}`);
|
|
893
962
|
}
|
|
894
963
|
const { tableName } = this.getTableName(indexName);
|
|
895
|
-
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
|
|
964
|
+
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
|
|
965
|
+
const distanceExpr = `embedding ${ops.distanceOperator} '${vectorStr}'::${qualifiedVectorType}`;
|
|
966
|
+
const scoreExpr = ops.scoreExpr(distanceExpr);
|
|
896
967
|
const query = `
|
|
897
968
|
WITH vector_scores AS (
|
|
898
969
|
SELECT
|
|
899
970
|
vector_id as id,
|
|
900
|
-
|
|
971
|
+
${scoreExpr} as score,
|
|
901
972
|
metadata
|
|
902
973
|
${includeVector ? ", embedding" : ""}
|
|
903
974
|
FROM ${tableName}
|
|
@@ -914,7 +985,7 @@ var PgVector = class extends vector.MastraVector {
|
|
|
914
985
|
id,
|
|
915
986
|
score,
|
|
916
987
|
metadata,
|
|
917
|
-
...includeVector && embedding && { vector:
|
|
988
|
+
...includeVector && embedding && { vector: ops.parseEmbedding(embedding) }
|
|
918
989
|
}));
|
|
919
990
|
} catch (error$1) {
|
|
920
991
|
await client.query("ROLLBACK");
|
|
@@ -963,8 +1034,10 @@ var PgVector = class extends vector.MastraVector {
|
|
|
963
1034
|
}
|
|
964
1035
|
const vectorIds = ids || vectors.map(() => crypto.randomUUID());
|
|
965
1036
|
const indexInfo = await this.getIndexInfo({ indexName });
|
|
966
|
-
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
|
|
1037
|
+
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
|
|
1038
|
+
const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
|
|
967
1039
|
for (let i = 0; i < vectors.length; i++) {
|
|
1040
|
+
const vectorStr = ops.formatVector(vectors[i], indexInfo.dimension);
|
|
968
1041
|
const query = `
|
|
969
1042
|
INSERT INTO ${tableName} (vector_id, embedding, metadata)
|
|
970
1043
|
VALUES ($1, $2::${qualifiedVectorType}, $3::jsonb)
|
|
@@ -974,7 +1047,7 @@ var PgVector = class extends vector.MastraVector {
|
|
|
974
1047
|
metadata = $3::jsonb
|
|
975
1048
|
RETURNING embedding::text
|
|
976
1049
|
`;
|
|
977
|
-
await client.query(query, [vectorIds[i],
|
|
1050
|
+
await client.query(query, [vectorIds[i], vectorStr, JSON.stringify(metadata?.[i] || {})]);
|
|
978
1051
|
}
|
|
979
1052
|
await client.query("COMMIT");
|
|
980
1053
|
this.logger?.debug(`Upserted ${vectors.length} vectors to ${indexName}`, {
|
|
@@ -1030,9 +1103,17 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1030
1103
|
dimension,
|
|
1031
1104
|
metric,
|
|
1032
1105
|
type,
|
|
1033
|
-
vectorType = "vector"
|
|
1106
|
+
vectorType = "vector",
|
|
1107
|
+
metadataIndexes
|
|
1034
1108
|
}) {
|
|
1035
|
-
const input =
|
|
1109
|
+
const input = JSON.stringify([
|
|
1110
|
+
indexName,
|
|
1111
|
+
dimension,
|
|
1112
|
+
metric,
|
|
1113
|
+
type || "ivfflat",
|
|
1114
|
+
vectorType,
|
|
1115
|
+
metadataIndexes?.toSorted() ?? []
|
|
1116
|
+
]);
|
|
1036
1117
|
return (await this.hasher).h32(input);
|
|
1037
1118
|
}
|
|
1038
1119
|
cachedIndexExists(indexName, newKey) {
|
|
@@ -1083,11 +1164,13 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1083
1164
|
async createIndex({
|
|
1084
1165
|
indexName,
|
|
1085
1166
|
dimension,
|
|
1086
|
-
metric = "cosine",
|
|
1167
|
+
metric: rawMetric = "cosine",
|
|
1087
1168
|
indexConfig = {},
|
|
1088
1169
|
buildIndex = true,
|
|
1089
|
-
vectorType = "vector"
|
|
1170
|
+
vectorType = "vector",
|
|
1171
|
+
metadataIndexes
|
|
1090
1172
|
}) {
|
|
1173
|
+
const metric = vectorType === "bit" && rawMetric !== "hamming" && rawMetric !== "jaccard" ? "hamming" : rawMetric;
|
|
1091
1174
|
const { tableName } = this.getTableName(indexName);
|
|
1092
1175
|
try {
|
|
1093
1176
|
if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
|
|
@@ -1096,8 +1179,17 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1096
1179
|
if (!Number.isInteger(dimension) || dimension <= 0) {
|
|
1097
1180
|
throw new Error("Dimension must be a positive integer");
|
|
1098
1181
|
}
|
|
1099
|
-
if (vectorType !== "vector" && vectorType !== "halfvec") {
|
|
1100
|
-
throw new Error('vectorType must be "vector" or "
|
|
1182
|
+
if (vectorType !== "vector" && vectorType !== "halfvec" && vectorType !== "bit" && vectorType !== "sparsevec") {
|
|
1183
|
+
throw new Error('vectorType must be "vector", "halfvec", "bit", or "sparsevec"');
|
|
1184
|
+
}
|
|
1185
|
+
if (vectorType === "bit" && dimension > 64e3) {
|
|
1186
|
+
throw new Error("bit vectors support up to 64,000 dimensions for indexes");
|
|
1187
|
+
}
|
|
1188
|
+
if ((metric === "hamming" || metric === "jaccard") && vectorType !== "bit") {
|
|
1189
|
+
throw new Error(`${metric} metric is only valid with vectorType 'bit'`);
|
|
1190
|
+
}
|
|
1191
|
+
if (indexConfig?.type === "ivfflat" && vectorType === "bit" && metric === "jaccard") {
|
|
1192
|
+
throw new Error("IVFFlat indexes do not support Jaccard distance for bit vectors. Use HNSW instead.");
|
|
1101
1193
|
}
|
|
1102
1194
|
} catch (error$1) {
|
|
1103
1195
|
const mastraError = new error.MastraError(
|
|
@@ -1119,7 +1211,8 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1119
1211
|
dimension,
|
|
1120
1212
|
type: indexConfig.type,
|
|
1121
1213
|
metric,
|
|
1122
|
-
vectorType
|
|
1214
|
+
vectorType,
|
|
1215
|
+
metadataIndexes
|
|
1123
1216
|
});
|
|
1124
1217
|
if (this.cachedIndexExists(indexName, indexCacheKey)) {
|
|
1125
1218
|
return;
|
|
@@ -1147,6 +1240,33 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1147
1240
|
}
|
|
1148
1241
|
});
|
|
1149
1242
|
}
|
|
1243
|
+
if (vectorType === "bit" && !this.supportsBit()) {
|
|
1244
|
+
throw new error.MastraError({
|
|
1245
|
+
id: storage.createVectorErrorId("PG", "CREATE_INDEX", "VECTOR_TYPE_NOT_SUPPORTED"),
|
|
1246
|
+
text: `${vectorType} type requires pgvector >= 0.7.0, but version ${this.vectorExtensionVersion || "unknown"} is installed. Either upgrade pgvector or use vectorType: 'vector'.`,
|
|
1247
|
+
domain: error.ErrorDomain.MASTRA_VECTOR,
|
|
1248
|
+
category: error.ErrorCategory.USER,
|
|
1249
|
+
details: {
|
|
1250
|
+
indexName,
|
|
1251
|
+
vectorType,
|
|
1252
|
+
installedVersion: this.vectorExtensionVersion
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
if (vectorType === "sparsevec" && !this.supportsSparsevec()) {
|
|
1257
|
+
throw new error.MastraError({
|
|
1258
|
+
id: storage.createVectorErrorId("PG", "CREATE_INDEX", "VECTOR_TYPE_NOT_SUPPORTED"),
|
|
1259
|
+
text: `${vectorType} type requires pgvector >= 0.7.0, but version ${this.vectorExtensionVersion || "unknown"} is installed. Either upgrade pgvector or use vectorType: 'vector'.`,
|
|
1260
|
+
domain: error.ErrorDomain.MASTRA_VECTOR,
|
|
1261
|
+
category: error.ErrorCategory.USER,
|
|
1262
|
+
details: {
|
|
1263
|
+
indexName,
|
|
1264
|
+
requestedVectorType: vectorType,
|
|
1265
|
+
pgvectorVersion: this.vectorExtensionVersion || "unknown",
|
|
1266
|
+
requiredVersion: "0.7.0"
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1150
1270
|
if (this.schema && this.vectorExtensionSchema && this.schema !== this.vectorExtensionSchema && this.vectorExtensionSchema !== "pg_catalog") {
|
|
1151
1271
|
await client.query(`SET search_path TO ${this.getSchemaName()}, "${this.vectorExtensionSchema}"`);
|
|
1152
1272
|
}
|
|
@@ -1164,6 +1284,9 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1164
1284
|
if (buildIndex) {
|
|
1165
1285
|
await this.setupIndex({ indexName, metric, indexConfig, vectorType }, client);
|
|
1166
1286
|
}
|
|
1287
|
+
if (metadataIndexes?.length) {
|
|
1288
|
+
await this.createMetadataIndexes(tableName, indexName, metadataIndexes);
|
|
1289
|
+
}
|
|
1167
1290
|
} catch (error) {
|
|
1168
1291
|
this.createdIndexes.delete(indexName);
|
|
1169
1292
|
this.indexVectorTypes.delete(indexName);
|
|
@@ -1187,10 +1310,10 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1187
1310
|
throw mastraError;
|
|
1188
1311
|
});
|
|
1189
1312
|
}
|
|
1190
|
-
async buildIndex({ indexName, metric = "cosine", indexConfig }) {
|
|
1313
|
+
async buildIndex({ indexName, metric = "cosine", indexConfig, vectorType }) {
|
|
1191
1314
|
const client = await this.pool.connect();
|
|
1192
1315
|
try {
|
|
1193
|
-
await this.setupIndex({ indexName, metric, indexConfig }, client);
|
|
1316
|
+
await this.setupIndex({ indexName, metric, indexConfig, vectorType }, client);
|
|
1194
1317
|
} catch (error$1) {
|
|
1195
1318
|
const mastraError = new error.MastraError(
|
|
1196
1319
|
{
|
|
@@ -1213,7 +1336,26 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1213
1336
|
const mutex = this.getMutexByName(`build-${indexName}`);
|
|
1214
1337
|
await mutex.runExclusive(async () => {
|
|
1215
1338
|
const isConfigEmpty = !indexConfig || Object.keys(indexConfig).length === 0 || !indexConfig.type && !indexConfig.ivf && !indexConfig.hnsw;
|
|
1216
|
-
const
|
|
1339
|
+
const defaultIndexType = vectorType === "sparsevec" ? "hnsw" : "ivfflat";
|
|
1340
|
+
const indexType = isConfigEmpty ? defaultIndexType : indexConfig.type || defaultIndexType;
|
|
1341
|
+
if (indexType === "ivfflat" && vectorType === "sparsevec") {
|
|
1342
|
+
throw new error.MastraError({
|
|
1343
|
+
id: storage.createVectorErrorId("PG", "BUILD_INDEX", "UNSUPPORTED_INDEX_TYPE"),
|
|
1344
|
+
text: `IVFFlat indexes do not support sparsevec type. Use HNSW instead.`,
|
|
1345
|
+
domain: error.ErrorDomain.MASTRA_VECTOR,
|
|
1346
|
+
category: error.ErrorCategory.USER,
|
|
1347
|
+
details: { indexName, vectorType, indexType }
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
if (indexType === "ivfflat" && vectorType === "bit" && metric === "jaccard") {
|
|
1351
|
+
throw new error.MastraError({
|
|
1352
|
+
id: storage.createVectorErrorId("PG", "BUILD_INDEX", "UNSUPPORTED_INDEX_TYPE"),
|
|
1353
|
+
text: `IVFFlat indexes do not support Jaccard distance for bit vectors. Use HNSW instead.`,
|
|
1354
|
+
domain: error.ErrorDomain.MASTRA_VECTOR,
|
|
1355
|
+
category: error.ErrorCategory.USER,
|
|
1356
|
+
details: { indexName, vectorType, indexType, metric }
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1217
1359
|
const { tableName, vectorIndexName } = this.getTableName(indexName);
|
|
1218
1360
|
let existingIndexInfo = null;
|
|
1219
1361
|
let dimension = 0;
|
|
@@ -1271,7 +1413,7 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1271
1413
|
return;
|
|
1272
1414
|
}
|
|
1273
1415
|
const effectiveVectorType = existingIndexInfo?.vectorType ?? vectorType;
|
|
1274
|
-
const metricOp = this.
|
|
1416
|
+
const metricOp = this.getVectorOps(effectiveVectorType, metric).operatorClass;
|
|
1275
1417
|
let indexSQL;
|
|
1276
1418
|
if (indexType === "hnsw") {
|
|
1277
1419
|
const m = indexConfig.hnsw?.m ?? 8;
|
|
@@ -1303,6 +1445,21 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1303
1445
|
await client.query(indexSQL);
|
|
1304
1446
|
});
|
|
1305
1447
|
}
|
|
1448
|
+
async createMetadataIndexes(tableName, indexName, metadataFields) {
|
|
1449
|
+
const hasher = await this.hasher;
|
|
1450
|
+
for (const field of metadataFields) {
|
|
1451
|
+
const fieldHash = hasher.h32(field).toString(16);
|
|
1452
|
+
const prefix = indexName.slice(0, 63 - "_md__idx".length - fieldHash.length);
|
|
1453
|
+
const metadataIdxName = `"${prefix}_md_${fieldHash}_idx"`;
|
|
1454
|
+
const escapedField = field.replace(/'/g, "''");
|
|
1455
|
+
await this.pool.query(
|
|
1456
|
+
`
|
|
1457
|
+
CREATE INDEX CONCURRENTLY IF NOT EXISTS ${metadataIdxName}
|
|
1458
|
+
ON ${tableName} ((metadata->>'${escapedField}'))
|
|
1459
|
+
`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1306
1463
|
async installVectorExtension(client) {
|
|
1307
1464
|
if (this.vectorExtensionInstalled) {
|
|
1308
1465
|
return;
|
|
@@ -1389,7 +1546,7 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1389
1546
|
WHERE c.table_schema = t.table_schema
|
|
1390
1547
|
AND c.table_name = t.table_name
|
|
1391
1548
|
AND c.column_name = 'embedding'
|
|
1392
|
-
AND c.udt_name IN ('vector', 'halfvec')
|
|
1549
|
+
AND c.udt_name IN ('vector', 'halfvec', 'bit', 'sparsevec')
|
|
1393
1550
|
)
|
|
1394
1551
|
AND EXISTS (
|
|
1395
1552
|
SELECT 1
|
|
@@ -1432,14 +1589,15 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1432
1589
|
FROM information_schema.columns
|
|
1433
1590
|
WHERE table_schema = $1
|
|
1434
1591
|
AND table_name = $2
|
|
1435
|
-
AND udt_name IN ('vector', 'halfvec')
|
|
1592
|
+
AND udt_name IN ('vector', 'halfvec', 'bit', 'sparsevec')
|
|
1436
1593
|
LIMIT 1;
|
|
1437
1594
|
`;
|
|
1438
1595
|
const tableExists = await client.query(tableExistsQuery, [this.schema || "public", indexName]);
|
|
1439
1596
|
if (tableExists.rows.length === 0) {
|
|
1440
1597
|
throw new Error(`Vector table ${tableName} does not exist`);
|
|
1441
1598
|
}
|
|
1442
|
-
const
|
|
1599
|
+
const udtName = tableExists.rows[0].udt_name;
|
|
1600
|
+
const vectorType = udtName === "halfvec" ? "halfvec" : udtName === "bit" ? "bit" : udtName === "sparsevec" ? "sparsevec" : "vector";
|
|
1443
1601
|
const dimensionQuery = `
|
|
1444
1602
|
SELECT atttypmod as dimension
|
|
1445
1603
|
FROM pg_attribute
|
|
@@ -1471,7 +1629,7 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1471
1629
|
index_def: "",
|
|
1472
1630
|
operator_class: "cosine"
|
|
1473
1631
|
};
|
|
1474
|
-
const metric = operator_class.includes("l2") ? "euclidean" : operator_class.includes("ip") ? "dotproduct" : "cosine";
|
|
1632
|
+
const metric = operator_class.includes("hamming") ? "hamming" : operator_class.includes("jaccard") ? "jaccard" : operator_class.includes("l2") ? "euclidean" : operator_class.includes("ip") ? "dotproduct" : "cosine";
|
|
1475
1633
|
const config = {};
|
|
1476
1634
|
if (index_method === "hnsw") {
|
|
1477
1635
|
const m = index_def.match(/m\s*=\s*'?(\d+)'?/)?.[1];
|
|
@@ -1606,13 +1764,14 @@ var PgVector = class extends vector.MastraVector {
|
|
|
1606
1764
|
client = await this.pool.connect();
|
|
1607
1765
|
const { tableName } = this.getTableName(indexName);
|
|
1608
1766
|
const indexInfo = await this.getIndexInfo({ indexName });
|
|
1609
|
-
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType);
|
|
1767
|
+
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
|
|
1768
|
+
const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
|
|
1610
1769
|
let updateParts = [];
|
|
1611
1770
|
let values = [];
|
|
1612
1771
|
let valueIndex = 1;
|
|
1613
1772
|
if (update.vector) {
|
|
1614
1773
|
updateParts.push(`embedding = $${valueIndex}::${qualifiedVectorType}`);
|
|
1615
|
-
values.push(
|
|
1774
|
+
values.push(ops.formatVector(update.vector, indexInfo.dimension));
|
|
1616
1775
|
valueIndex++;
|
|
1617
1776
|
}
|
|
1618
1777
|
if (update.metadata) {
|
|
@@ -2180,6 +2339,8 @@ var PgDB = class extends base.MastraBase {
|
|
|
2180
2339
|
client;
|
|
2181
2340
|
schemaName;
|
|
2182
2341
|
skipDefaultIndexes;
|
|
2342
|
+
/** Cache of actual table columns: tableName -> Set<columnName> */
|
|
2343
|
+
tableColumnsCache = /* @__PURE__ */ new Map();
|
|
2183
2344
|
constructor(config) {
|
|
2184
2345
|
super({
|
|
2185
2346
|
component: "STORAGE",
|
|
@@ -2189,6 +2350,40 @@ var PgDB = class extends base.MastraBase {
|
|
|
2189
2350
|
this.schemaName = config.schemaName;
|
|
2190
2351
|
this.skipDefaultIndexes = config.skipDefaultIndexes;
|
|
2191
2352
|
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Gets the set of column names that actually exist in the database table.
|
|
2355
|
+
* Results are cached; the cache is invalidated when alterTable() adds new columns.
|
|
2356
|
+
*/
|
|
2357
|
+
async getTableColumns(tableName) {
|
|
2358
|
+
const cached = this.tableColumnsCache.get(tableName);
|
|
2359
|
+
if (cached) return cached;
|
|
2360
|
+
const schema = this.schemaName || "public";
|
|
2361
|
+
const rows = await this.client.manyOrNone(
|
|
2362
|
+
`SELECT column_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2`,
|
|
2363
|
+
[schema, tableName]
|
|
2364
|
+
);
|
|
2365
|
+
const columns = new Set(rows.map((r) => r.column_name));
|
|
2366
|
+
if (columns.size > 0) {
|
|
2367
|
+
this.tableColumnsCache.set(tableName, columns);
|
|
2368
|
+
}
|
|
2369
|
+
return columns;
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Filters a record to only include columns that exist in the actual database table.
|
|
2373
|
+
* Unknown columns are silently dropped to ensure forward compatibility when newer
|
|
2374
|
+
* domain packages add fields that haven't been migrated yet.
|
|
2375
|
+
*/
|
|
2376
|
+
async filterRecordToKnownColumns(tableName, record) {
|
|
2377
|
+
const knownColumns = await this.getTableColumns(tableName);
|
|
2378
|
+
if (knownColumns.size === 0) return record;
|
|
2379
|
+
const filtered = {};
|
|
2380
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2381
|
+
if (knownColumns.has(key)) {
|
|
2382
|
+
filtered[key] = value;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
return filtered;
|
|
2386
|
+
}
|
|
2192
2387
|
async hasColumn(table, column) {
|
|
2193
2388
|
const schema = this.schemaName || "public";
|
|
2194
2389
|
const result = await this.client.oneOrNone(
|
|
@@ -2305,9 +2500,11 @@ var PgDB = class extends base.MastraBase {
|
|
|
2305
2500
|
async insert({ tableName, record }) {
|
|
2306
2501
|
try {
|
|
2307
2502
|
this.addTimestampZColumns(record);
|
|
2503
|
+
const filteredRecord = await this.filterRecordToKnownColumns(tableName, record);
|
|
2308
2504
|
const schemaName = getSchemaName(this.schemaName);
|
|
2309
|
-
const columns = Object.keys(
|
|
2310
|
-
|
|
2505
|
+
const columns = Object.keys(filteredRecord).map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
2506
|
+
if (columns.length === 0) return;
|
|
2507
|
+
const values = this.prepareValuesForInsert(filteredRecord, tableName);
|
|
2311
2508
|
const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
|
|
2312
2509
|
const fullTableName = getTableName({ indexName: tableName, schemaName });
|
|
2313
2510
|
const columnList = columns.map((c) => `"${c}"`).join(", ");
|
|
@@ -2443,6 +2640,8 @@ Note: This migration may take some time for large tables.
|
|
|
2443
2640
|
},
|
|
2444
2641
|
error$1
|
|
2445
2642
|
);
|
|
2643
|
+
} finally {
|
|
2644
|
+
this.tableColumnsCache.delete(tableName);
|
|
2446
2645
|
}
|
|
2447
2646
|
}
|
|
2448
2647
|
async setupTimestampTriggers(tableName) {
|
|
@@ -2762,6 +2961,8 @@ Note: This migration may take some time for large tables.
|
|
|
2762
2961
|
},
|
|
2763
2962
|
error$1
|
|
2764
2963
|
);
|
|
2964
|
+
} finally {
|
|
2965
|
+
this.tableColumnsCache.delete(tableName);
|
|
2765
2966
|
}
|
|
2766
2967
|
}
|
|
2767
2968
|
async load({ tableName, keys }) {
|
|
@@ -2838,6 +3039,8 @@ Note: This migration may take some time for large tables.
|
|
|
2838
3039
|
},
|
|
2839
3040
|
error$1
|
|
2840
3041
|
);
|
|
3042
|
+
} finally {
|
|
3043
|
+
this.tableColumnsCache.delete(tableName);
|
|
2841
3044
|
}
|
|
2842
3045
|
}
|
|
2843
3046
|
async createIndex(options) {
|
|
@@ -3082,10 +3285,12 @@ Note: This migration may take some time for large tables.
|
|
|
3082
3285
|
data
|
|
3083
3286
|
}) {
|
|
3084
3287
|
try {
|
|
3288
|
+
const filteredData = await this.filterRecordToKnownColumns(tableName, data);
|
|
3289
|
+
if (Object.keys(filteredData).length === 0) return;
|
|
3085
3290
|
const setColumns = [];
|
|
3086
3291
|
const setValues = [];
|
|
3087
3292
|
let paramIndex = 1;
|
|
3088
|
-
Object.entries(
|
|
3293
|
+
Object.entries(filteredData).forEach(([key, value]) => {
|
|
3089
3294
|
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
3090
3295
|
setColumns.push(`"${parsedKey}" = $${paramIndex++}`);
|
|
3091
3296
|
setValues.push(this.prepareValue(value, key, tableName));
|
|
@@ -4144,9 +4349,18 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4144
4349
|
compositePrimaryKey: storage.TABLE_CONFIGS[storage.TABLE_DATASET_ITEMS]?.compositePrimaryKey
|
|
4145
4350
|
});
|
|
4146
4351
|
await this.#db.createTable({ tableName: storage.TABLE_DATASET_VERSIONS, schema: storage.DATASET_VERSIONS_SCHEMA });
|
|
4352
|
+
await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "requestContextSchema", "JSONB");
|
|
4353
|
+
await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "requestContext", "JSONB");
|
|
4147
4354
|
await this.createDefaultIndexes();
|
|
4148
4355
|
await this.createCustomIndexes();
|
|
4149
4356
|
}
|
|
4357
|
+
async #addColumnIfNotExists(table, column, sqlType) {
|
|
4358
|
+
const exists = await this.#db.hasColumn(table, column);
|
|
4359
|
+
if (!exists) {
|
|
4360
|
+
const fullTableName = getTableName2({ indexName: table, schemaName: getSchemaName2(this.#schema) });
|
|
4361
|
+
await this.#db.client.none(`ALTER TABLE ${fullTableName} ADD COLUMN "${column}" ${sqlType}`);
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4150
4364
|
getDefaultIndexDefinitions() {
|
|
4151
4365
|
return [
|
|
4152
4366
|
{ name: "idx_dataset_items_dataset_validto", table: storage.TABLE_DATASET_ITEMS, columns: ["datasetId", "validTo"] },
|
|
@@ -4202,6 +4416,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4202
4416
|
metadata: row.metadata ? storage.safelyParseJSON(row.metadata) : void 0,
|
|
4203
4417
|
inputSchema: row.inputSchema ? storage.safelyParseJSON(row.inputSchema) : void 0,
|
|
4204
4418
|
groundTruthSchema: row.groundTruthSchema ? storage.safelyParseJSON(row.groundTruthSchema) : void 0,
|
|
4419
|
+
requestContextSchema: row.requestContextSchema ? storage.safelyParseJSON(row.requestContextSchema) : void 0,
|
|
4205
4420
|
version: row.version,
|
|
4206
4421
|
createdAt: storage.ensureDate(row.createdAtZ || row.createdAt),
|
|
4207
4422
|
updatedAt: storage.ensureDate(row.updatedAtZ || row.updatedAt)
|
|
@@ -4214,6 +4429,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4214
4429
|
datasetVersion: row.datasetVersion,
|
|
4215
4430
|
input: storage.safelyParseJSON(row.input),
|
|
4216
4431
|
groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : void 0,
|
|
4432
|
+
requestContext: row.requestContext ? storage.safelyParseJSON(row.requestContext) : void 0,
|
|
4217
4433
|
metadata: row.metadata ? storage.safelyParseJSON(row.metadata) : void 0,
|
|
4218
4434
|
createdAt: storage.ensureDate(row.createdAtZ || row.createdAt),
|
|
4219
4435
|
updatedAt: storage.ensureDate(row.updatedAtZ || row.updatedAt)
|
|
@@ -4228,6 +4444,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4228
4444
|
isDeleted: Boolean(row.isDeleted),
|
|
4229
4445
|
input: storage.safelyParseJSON(row.input),
|
|
4230
4446
|
groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : void 0,
|
|
4447
|
+
requestContext: row.requestContext ? storage.safelyParseJSON(row.requestContext) : void 0,
|
|
4231
4448
|
metadata: row.metadata ? storage.safelyParseJSON(row.metadata) : void 0,
|
|
4232
4449
|
createdAt: storage.ensureDate(row.createdAtZ || row.createdAt),
|
|
4233
4450
|
updatedAt: storage.ensureDate(row.updatedAtZ || row.updatedAt)
|
|
@@ -4256,6 +4473,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4256
4473
|
metadata: input.metadata ?? null,
|
|
4257
4474
|
inputSchema: input.inputSchema ?? null,
|
|
4258
4475
|
groundTruthSchema: input.groundTruthSchema ?? null,
|
|
4476
|
+
requestContextSchema: input.requestContextSchema ?? null,
|
|
4259
4477
|
version: 0,
|
|
4260
4478
|
createdAt: nowIso,
|
|
4261
4479
|
updatedAt: nowIso
|
|
@@ -4268,6 +4486,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4268
4486
|
metadata: input.metadata,
|
|
4269
4487
|
inputSchema: input.inputSchema ?? void 0,
|
|
4270
4488
|
groundTruthSchema: input.groundTruthSchema ?? void 0,
|
|
4489
|
+
requestContextSchema: input.requestContextSchema ?? void 0,
|
|
4271
4490
|
version: 0,
|
|
4272
4491
|
createdAt: now,
|
|
4273
4492
|
updatedAt: now
|
|
@@ -4335,6 +4554,10 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4335
4554
|
setClauses.push(`"groundTruthSchema" = $${paramIndex++}`);
|
|
4336
4555
|
values.push(args.groundTruthSchema === null ? null : JSON.stringify(args.groundTruthSchema));
|
|
4337
4556
|
}
|
|
4557
|
+
if (args.requestContextSchema !== void 0) {
|
|
4558
|
+
setClauses.push(`"requestContextSchema" = $${paramIndex++}`);
|
|
4559
|
+
values.push(args.requestContextSchema === null ? null : JSON.stringify(args.requestContextSchema));
|
|
4560
|
+
}
|
|
4338
4561
|
values.push(args.id);
|
|
4339
4562
|
await this.#db.client.none(
|
|
4340
4563
|
`UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE "id" = $${paramIndex}`,
|
|
@@ -4347,6 +4570,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4347
4570
|
metadata: args.metadata ?? existing.metadata,
|
|
4348
4571
|
inputSchema: (args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema) ?? void 0,
|
|
4349
4572
|
groundTruthSchema: (args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema) ?? void 0,
|
|
4573
|
+
requestContextSchema: (args.requestContextSchema !== void 0 ? args.requestContextSchema : existing.requestContextSchema) ?? void 0,
|
|
4350
4574
|
updatedAt: new Date(now)
|
|
4351
4575
|
};
|
|
4352
4576
|
} catch (error$1) {
|
|
@@ -4462,13 +4686,14 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4462
4686
|
);
|
|
4463
4687
|
newVersion = row.version;
|
|
4464
4688
|
await t.none(
|
|
4465
|
-
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
|
|
4689
|
+
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
4466
4690
|
[
|
|
4467
4691
|
id,
|
|
4468
4692
|
args.datasetId,
|
|
4469
4693
|
newVersion,
|
|
4470
4694
|
JSON.stringify(args.input),
|
|
4471
4695
|
jsonbArg(args.groundTruth),
|
|
4696
|
+
jsonbArg(args.requestContext),
|
|
4472
4697
|
jsonbArg(args.metadata),
|
|
4473
4698
|
nowIso,
|
|
4474
4699
|
nowIso,
|
|
@@ -4487,6 +4712,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4487
4712
|
datasetVersion: newVersion,
|
|
4488
4713
|
input: args.input,
|
|
4489
4714
|
groundTruth: args.groundTruth,
|
|
4715
|
+
requestContext: args.requestContext,
|
|
4490
4716
|
metadata: args.metadata,
|
|
4491
4717
|
createdAt: now,
|
|
4492
4718
|
updatedAt: now
|
|
@@ -4533,6 +4759,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4533
4759
|
const nowIso = now.toISOString();
|
|
4534
4760
|
const mergedInput = args.input ?? existing.input;
|
|
4535
4761
|
const mergedGroundTruth = args.groundTruth ?? existing.groundTruth;
|
|
4762
|
+
const mergedRequestContext = args.requestContext ?? existing.requestContext;
|
|
4536
4763
|
const mergedMetadata = args.metadata ?? existing.metadata;
|
|
4537
4764
|
let newVersion;
|
|
4538
4765
|
await this.#db.client.tx(async (t) => {
|
|
@@ -4546,13 +4773,14 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4546
4773
|
[newVersion, args.id]
|
|
4547
4774
|
);
|
|
4548
4775
|
await t.none(
|
|
4549
|
-
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
|
|
4776
|
+
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
4550
4777
|
[
|
|
4551
4778
|
args.id,
|
|
4552
4779
|
args.datasetId,
|
|
4553
4780
|
newVersion,
|
|
4554
4781
|
JSON.stringify(mergedInput),
|
|
4555
4782
|
jsonbArg(mergedGroundTruth),
|
|
4783
|
+
jsonbArg(mergedRequestContext),
|
|
4556
4784
|
jsonbArg(mergedMetadata),
|
|
4557
4785
|
existing.createdAt.toISOString(),
|
|
4558
4786
|
existing.createdAt.toISOString(),
|
|
@@ -4570,6 +4798,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4570
4798
|
datasetVersion: newVersion,
|
|
4571
4799
|
input: mergedInput,
|
|
4572
4800
|
groundTruth: mergedGroundTruth,
|
|
4801
|
+
requestContext: mergedRequestContext,
|
|
4573
4802
|
metadata: mergedMetadata,
|
|
4574
4803
|
updatedAt: now
|
|
4575
4804
|
};
|
|
@@ -4616,13 +4845,14 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4616
4845
|
[newVersion, id]
|
|
4617
4846
|
);
|
|
4618
4847
|
await t.none(
|
|
4619
|
-
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10)`,
|
|
4848
|
+
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
4620
4849
|
[
|
|
4621
4850
|
id,
|
|
4622
4851
|
datasetId,
|
|
4623
4852
|
newVersion,
|
|
4624
4853
|
JSON.stringify(existing.input),
|
|
4625
4854
|
jsonbArg(existing.groundTruth),
|
|
4855
|
+
jsonbArg(existing.requestContext),
|
|
4626
4856
|
jsonbArg(existing.metadata),
|
|
4627
4857
|
existing.createdAt.toISOString(),
|
|
4628
4858
|
existing.createdAt.toISOString(),
|
|
@@ -4677,13 +4907,14 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4677
4907
|
newVersion = row.version;
|
|
4678
4908
|
for (const { id, input: itemInput } of itemsWithIds) {
|
|
4679
4909
|
await t.none(
|
|
4680
|
-
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10)`,
|
|
4910
|
+
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
4681
4911
|
[
|
|
4682
4912
|
id,
|
|
4683
4913
|
input.datasetId,
|
|
4684
4914
|
newVersion,
|
|
4685
4915
|
JSON.stringify(itemInput.input),
|
|
4686
4916
|
jsonbArg(itemInput.groundTruth),
|
|
4917
|
+
jsonbArg(itemInput.requestContext),
|
|
4687
4918
|
jsonbArg(itemInput.metadata),
|
|
4688
4919
|
nowIso,
|
|
4689
4920
|
nowIso,
|
|
@@ -4703,6 +4934,7 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4703
4934
|
datasetVersion: newVersion,
|
|
4704
4935
|
input: itemInput.input,
|
|
4705
4936
|
groundTruth: itemInput.groundTruth,
|
|
4937
|
+
requestContext: itemInput.requestContext,
|
|
4706
4938
|
metadata: itemInput.metadata,
|
|
4707
4939
|
createdAt: now,
|
|
4708
4940
|
updatedAt: now
|
|
@@ -4758,13 +4990,14 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
|
|
|
4758
4990
|
[newVersion, item.id]
|
|
4759
4991
|
);
|
|
4760
4992
|
await t.none(
|
|
4761
|
-
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10)`,
|
|
4993
|
+
`INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","requestContext","metadata","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
4762
4994
|
[
|
|
4763
4995
|
item.id,
|
|
4764
4996
|
input.datasetId,
|
|
4765
4997
|
newVersion,
|
|
4766
4998
|
JSON.stringify(item.input),
|
|
4767
4999
|
jsonbArg(item.groundTruth),
|
|
5000
|
+
jsonbArg(item.requestContext),
|
|
4768
5001
|
jsonbArg(item.metadata),
|
|
4769
5002
|
item.createdAt.toISOString(),
|
|
4770
5003
|
item.createdAt.toISOString(),
|
|
@@ -7171,39 +7404,73 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7171
7404
|
* issues on large tables (see GitHub issue #11150). The old approach required
|
|
7172
7405
|
* scanning and sorting ALL messages in a thread to assign row numbers.
|
|
7173
7406
|
*
|
|
7174
|
-
* The
|
|
7175
|
-
* fetch
|
|
7407
|
+
* The current approach uses two phases for optimal performance:
|
|
7408
|
+
* 1. Batch-fetch all target messages' metadata (thread_id, createdAt) in one query
|
|
7409
|
+
* 2. Build cursor subqueries using "createdAt" directly (not COALESCE) so that
|
|
7410
|
+
* the existing (thread_id, createdAt DESC) index can be used for index scans
|
|
7411
|
+
* instead of sequential scans. This fixes GitHub issue #11702 where semantic
|
|
7412
|
+
* recall latency scaled linearly with message count (~30s for 7.4k messages).
|
|
7176
7413
|
*/
|
|
7414
|
+
_sortMessages(messages, field, direction) {
|
|
7415
|
+
return messages.sort((a, b) => {
|
|
7416
|
+
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
7417
|
+
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
7418
|
+
if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
|
|
7419
|
+
if (aValue == null) return 1;
|
|
7420
|
+
if (bValue == null) return -1;
|
|
7421
|
+
if (aValue === bValue) {
|
|
7422
|
+
return a.id.localeCompare(b.id);
|
|
7423
|
+
}
|
|
7424
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
7425
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
7426
|
+
}
|
|
7427
|
+
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
7428
|
+
});
|
|
7429
|
+
}
|
|
7177
7430
|
async _getIncludedMessages({ include }) {
|
|
7178
7431
|
if (!include || include.length === 0) return null;
|
|
7179
7432
|
const tableName = getTableName3({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName3(this.#schema) });
|
|
7180
7433
|
const selectColumns = `id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
|
|
7434
|
+
const targetIds = include.map((inc) => inc.id).filter(Boolean);
|
|
7435
|
+
if (targetIds.length === 0) return null;
|
|
7436
|
+
const idPlaceholders = targetIds.map((_, i) => "$" + (i + 1)).join(", ");
|
|
7437
|
+
const targetRows = await this.#db.client.manyOrNone(`SELECT id, thread_id, "createdAt" FROM ${tableName} WHERE id IN (${idPlaceholders})`, targetIds);
|
|
7438
|
+
if (targetRows.length === 0) return null;
|
|
7439
|
+
const targetMap = new Map(targetRows.map((r) => [r.id, { threadId: r.thread_id, createdAt: r.createdAt }]));
|
|
7181
7440
|
const unionQueries = [];
|
|
7182
7441
|
const params = [];
|
|
7183
7442
|
let paramIdx = 1;
|
|
7184
7443
|
for (const inc of include) {
|
|
7185
7444
|
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
7445
|
+
const target = targetMap.get(id);
|
|
7446
|
+
if (!target) continue;
|
|
7447
|
+
const p1 = "$" + paramIdx;
|
|
7448
|
+
const p2 = "$" + (paramIdx + 1);
|
|
7449
|
+
const p3 = "$" + (paramIdx + 2);
|
|
7186
7450
|
unionQueries.push(`(
|
|
7187
7451
|
SELECT ${selectColumns}
|
|
7188
7452
|
FROM ${tableName} m
|
|
7189
|
-
WHERE m.thread_id =
|
|
7190
|
-
AND
|
|
7191
|
-
ORDER BY
|
|
7192
|
-
LIMIT
|
|
7453
|
+
WHERE m.thread_id = ${p1}
|
|
7454
|
+
AND m."createdAt" <= ${p2}
|
|
7455
|
+
ORDER BY m."createdAt" DESC, m.id DESC
|
|
7456
|
+
LIMIT ${p3}
|
|
7193
7457
|
)`);
|
|
7194
|
-
params.push(
|
|
7195
|
-
paramIdx +=
|
|
7458
|
+
params.push(target.threadId, target.createdAt, withPreviousMessages + 1);
|
|
7459
|
+
paramIdx += 3;
|
|
7196
7460
|
if (withNextMessages > 0) {
|
|
7461
|
+
const p4 = "$" + paramIdx;
|
|
7462
|
+
const p5 = "$" + (paramIdx + 1);
|
|
7463
|
+
const p6 = "$" + (paramIdx + 2);
|
|
7197
7464
|
unionQueries.push(`(
|
|
7198
7465
|
SELECT ${selectColumns}
|
|
7199
7466
|
FROM ${tableName} m
|
|
7200
|
-
WHERE m.thread_id =
|
|
7201
|
-
AND
|
|
7202
|
-
ORDER BY
|
|
7203
|
-
LIMIT
|
|
7467
|
+
WHERE m.thread_id = ${p4}
|
|
7468
|
+
AND m."createdAt" > ${p5}
|
|
7469
|
+
ORDER BY m."createdAt" ASC, m.id ASC
|
|
7470
|
+
LIMIT ${p6}
|
|
7204
7471
|
)`);
|
|
7205
|
-
params.push(
|
|
7206
|
-
paramIdx +=
|
|
7472
|
+
params.push(target.threadId, target.createdAt, withNextMessages);
|
|
7473
|
+
paramIdx += 3;
|
|
7207
7474
|
}
|
|
7208
7475
|
}
|
|
7209
7476
|
if (unionQueries.length === 0) return null;
|
|
@@ -7211,7 +7478,7 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7211
7478
|
if (unionQueries.length === 1) {
|
|
7212
7479
|
finalQuery = unionQueries[0].slice(1, -1);
|
|
7213
7480
|
} else {
|
|
7214
|
-
finalQuery = `SELECT * FROM (${unionQueries.join(" UNION ALL ")}) AS combined ORDER BY "createdAt" ASC`;
|
|
7481
|
+
finalQuery = `SELECT * FROM (${unionQueries.join(" UNION ALL ")}) AS combined ORDER BY "createdAt" ASC, id ASC`;
|
|
7215
7482
|
}
|
|
7216
7483
|
const includedRows = await this.#db.client.manyOrNone(finalQuery, params);
|
|
7217
7484
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7325,6 +7592,24 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7325
7592
|
queryParams.push(filter.dateRange.end);
|
|
7326
7593
|
}
|
|
7327
7594
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
7595
|
+
if (perPage === 0 && (!include || include.length === 0)) {
|
|
7596
|
+
return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
|
|
7597
|
+
}
|
|
7598
|
+
if (perPage === 0 && include && include.length > 0) {
|
|
7599
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
7600
|
+
if (!includeMessages || includeMessages.length === 0) {
|
|
7601
|
+
return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
|
|
7602
|
+
}
|
|
7603
|
+
const messagesWithParsedContent2 = includeMessages.map((row) => this.parseRow(row));
|
|
7604
|
+
const list2 = new agent.MessageList().add(messagesWithParsedContent2, "memory");
|
|
7605
|
+
return {
|
|
7606
|
+
messages: this._sortMessages(list2.get.all.db(), field, direction),
|
|
7607
|
+
total: 0,
|
|
7608
|
+
page,
|
|
7609
|
+
perPage: perPageForResponse,
|
|
7610
|
+
hasMore: false
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7328
7613
|
const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
|
|
7329
7614
|
const countResult = await this.#db.client.one(countQuery, queryParams);
|
|
7330
7615
|
const total = parseInt(countResult.count, 10);
|
|
@@ -7355,21 +7640,7 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7355
7640
|
}
|
|
7356
7641
|
const messagesWithParsedContent = messages.map((row) => this.parseRow(row));
|
|
7357
7642
|
const list = new agent.MessageList().add(messagesWithParsedContent, "memory");
|
|
7358
|
-
|
|
7359
|
-
finalMessages = finalMessages.sort((a, b) => {
|
|
7360
|
-
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
7361
|
-
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
7362
|
-
if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
|
|
7363
|
-
if (aValue == null) return 1;
|
|
7364
|
-
if (bValue == null) return -1;
|
|
7365
|
-
if (aValue === bValue) {
|
|
7366
|
-
return a.id.localeCompare(b.id);
|
|
7367
|
-
}
|
|
7368
|
-
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
7369
|
-
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
7370
|
-
}
|
|
7371
|
-
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
7372
|
-
});
|
|
7643
|
+
const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
|
|
7373
7644
|
const threadIdSet = new Set(threadIds);
|
|
7374
7645
|
const returnedThreadMessageIds = new Set(
|
|
7375
7646
|
finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
|
|
@@ -7458,6 +7729,30 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7458
7729
|
queryParams.push(filter.dateRange.end);
|
|
7459
7730
|
}
|
|
7460
7731
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
7732
|
+
if (perPage === 0 && (!include || include.length === 0)) {
|
|
7733
|
+
return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
|
|
7734
|
+
}
|
|
7735
|
+
if (perPage === 0 && include && include.length > 0) {
|
|
7736
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
7737
|
+
if (!includeMessages || includeMessages.length === 0) {
|
|
7738
|
+
return {
|
|
7739
|
+
messages: [],
|
|
7740
|
+
total: 0,
|
|
7741
|
+
page,
|
|
7742
|
+
perPage: perPageForResponse,
|
|
7743
|
+
hasMore: false
|
|
7744
|
+
};
|
|
7745
|
+
}
|
|
7746
|
+
const messagesWithParsedContent2 = includeMessages.map((row) => this.parseRow(row));
|
|
7747
|
+
const list2 = new agent.MessageList().add(messagesWithParsedContent2, "memory");
|
|
7748
|
+
return {
|
|
7749
|
+
messages: this._sortMessages(list2.get.all.db(), field, direction),
|
|
7750
|
+
total: 0,
|
|
7751
|
+
page,
|
|
7752
|
+
perPage: perPageForResponse,
|
|
7753
|
+
hasMore: false
|
|
7754
|
+
};
|
|
7755
|
+
}
|
|
7461
7756
|
const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
|
|
7462
7757
|
const countResult = await this.#db.client.one(countQuery, queryParams);
|
|
7463
7758
|
const total = parseInt(countResult.count, 10);
|
|
@@ -7488,21 +7783,7 @@ var MemoryPG = class _MemoryPG extends storage.MemoryStorage {
|
|
|
7488
7783
|
}
|
|
7489
7784
|
const messagesWithParsedContent = messages.map((row) => this.parseRow(row));
|
|
7490
7785
|
const list = new agent.MessageList().add(messagesWithParsedContent, "memory");
|
|
7491
|
-
|
|
7492
|
-
finalMessages = finalMessages.sort((a, b) => {
|
|
7493
|
-
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
7494
|
-
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
7495
|
-
if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
|
|
7496
|
-
if (aValue == null) return 1;
|
|
7497
|
-
if (bValue == null) return -1;
|
|
7498
|
-
if (aValue === bValue) {
|
|
7499
|
-
return a.id.localeCompare(b.id);
|
|
7500
|
-
}
|
|
7501
|
-
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
7502
|
-
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
7503
|
-
}
|
|
7504
|
-
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
7505
|
-
});
|
|
7786
|
+
const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
|
|
7506
7787
|
const hasMore = perPageInput !== false && offset + perPage < total;
|
|
7507
7788
|
return {
|
|
7508
7789
|
messages: finalMessages,
|
|
@@ -8956,6 +9237,11 @@ var ObservabilityPG = class _ObservabilityPG extends storage.ObservabilityStorag
|
|
|
8956
9237
|
}
|
|
8957
9238
|
async init() {
|
|
8958
9239
|
await this.#db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.TABLE_SCHEMAS[storage.TABLE_SPANS] });
|
|
9240
|
+
await this.#db.alterTable({
|
|
9241
|
+
tableName: storage.TABLE_SPANS,
|
|
9242
|
+
schema: storage.TABLE_SCHEMAS[storage.TABLE_SPANS],
|
|
9243
|
+
ifNotExists: ["requestContext"]
|
|
9244
|
+
});
|
|
8959
9245
|
await this.createDefaultIndexes();
|
|
8960
9246
|
await this.createCustomIndexes();
|
|
8961
9247
|
}
|
|
@@ -9310,7 +9596,8 @@ var ObservabilityPG = class _ObservabilityPG extends storage.ObservabilityStorag
|
|
|
9310
9596
|
}
|
|
9311
9597
|
async listTraces(args) {
|
|
9312
9598
|
const { filters, pagination, orderBy } = storage.listTracesArgsSchema.parse(args);
|
|
9313
|
-
const
|
|
9599
|
+
const page = pagination?.page ?? 0;
|
|
9600
|
+
const perPage = pagination?.perPage ?? 10;
|
|
9314
9601
|
const tableName = getTableName2({
|
|
9315
9602
|
indexName: storage.TABLE_SPANS,
|
|
9316
9603
|
schemaName: getSchemaName2(this.#schema)
|
|
@@ -9432,10 +9719,11 @@ var ObservabilityPG = class _ObservabilityPG extends storage.ObservabilityStorag
|
|
|
9432
9719
|
}
|
|
9433
9720
|
}
|
|
9434
9721
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
9435
|
-
const
|
|
9436
|
-
const
|
|
9722
|
+
const orderField = orderBy?.field ?? "startedAt";
|
|
9723
|
+
const sortField = `${orderField}Z`;
|
|
9724
|
+
const sortDirection = orderBy?.direction ?? "DESC";
|
|
9437
9725
|
let orderClause;
|
|
9438
|
-
if (
|
|
9726
|
+
if (orderField === "endedAt") {
|
|
9439
9727
|
const nullsOrder = sortDirection === "DESC" ? "NULLS FIRST" : "NULLS LAST";
|
|
9440
9728
|
orderClause = `ORDER BY r."${sortField}" ${sortDirection} ${nullsOrder}`;
|
|
9441
9729
|
} else {
|