@mastra/pg 1.33.0-alpha.2 → 1.33.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +4 -2
- package/dist/docs/references/reference-memory-memory-class.md +8 -6
- package/dist/index.cjs +221 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +222 -71
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +14 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +2 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts +6 -0
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts +37 -1
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/thread-query.d.ts +0 -2
- package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +9 -3
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/vector/sql-builder.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/docs/SKILL.md
CHANGED
|
@@ -456,8 +456,10 @@ const agent = new Agent({
|
|
|
456
456
|
indexConfig: {
|
|
457
457
|
type: 'hnsw', // Use HNSW for better performance
|
|
458
458
|
metric: 'dotproduct', // Best for OpenAI embeddings
|
|
459
|
-
|
|
460
|
-
|
|
459
|
+
hnsw: {
|
|
460
|
+
m: 16, // Number of bi-directional links (default: 16)
|
|
461
|
+
efConstruction: 64, // Size of candidate list during construction (default: 64)
|
|
462
|
+
},
|
|
461
463
|
},
|
|
462
464
|
},
|
|
463
465
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Memory class
|
|
6
6
|
|
|
7
|
-
The `Memory` class provides a reliable system for managing conversation history and thread-based message storage in Mastra. It enables persistent storage of conversations, semantic search capabilities, and efficient message retrieval. You must configure a storage provider for conversation history, and if you enable semantic recall you will also need to provide a vector store and embedder.
|
|
7
|
+
The `Memory` class provides a reliable system for managing conversation history and thread-based message storage in Mastra. It enables persistent storage of conversations, semantic search capabilities, and efficient message retrieval. You must configure a storage provider for conversation history, and if you enable semantic recall you will also need to provide a vector store and, unless that vector store embeds text itself, an embedder.
|
|
8
8
|
|
|
9
9
|
## Usage example
|
|
10
10
|
|
|
@@ -31,11 +31,11 @@ export const agent = new Agent({
|
|
|
31
31
|
|
|
32
32
|
## Constructor parameters
|
|
33
33
|
|
|
34
|
-
**storage** (`MastraCompositeStore`): Storage implementation for persisting memory data.
|
|
34
|
+
**storage** (`MastraCompositeStore`): Storage implementation for persisting memory data. Optional: when omitted, an agent uses the storage of the Mastra instance it belongs to. If neither is configured, accessing memory.storage throws Memory requires a storage provider to function.
|
|
35
35
|
|
|
36
36
|
**vector** (`MastraVector | false`): Vector store for semantic search capabilities. Set to false to disable vector operations.
|
|
37
37
|
|
|
38
|
-
**embedder** (`EmbeddingModel<string> | EmbeddingModelV2<string>`): Embedder
|
|
38
|
+
**embedder** (`string | EmbeddingModel<string> | EmbeddingModelV2<string>`): Embedder for vector embeddings, as a model-router ID string (for example "openai/text-embedding-3-small"), an EmbeddingModel, or an EmbeddingModelV2. Required when semantic recall is enabled and the vector store doesn't generate embeddings itself (vector.isSelfEmbedding is false).
|
|
39
39
|
|
|
40
40
|
**embeddingDimension** (`number`): Output dimension of the embedder, as a positive integer. When set, Memory uses it to name the semantic recall index instead of embedding a probe string on first use. It must match the vectors the embedder returns: a mismatch throws a MASTRA_MEMORY_EMBEDDING_DIMENSION_MISMATCH error before any index is created. See Setting the embedding dimension.
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ export const agent = new Agent({
|
|
|
49
49
|
|
|
50
50
|
**options.retainFullInput** (`boolean`): When true, the request input is processed exactly as supplied instead of being filtered against stored history. Use this when you assemble the input yourself and need the message sequence preserved. Stored history is still loaded underneath, and every input message that isn't already stored is saved to the thread, including few-shot examples. Can be set per call on memory.options or agent-wide in the memory constructor options.
|
|
51
51
|
|
|
52
|
-
**options.semanticRecall** (`boolean | { topK: number; messageRange: number | { before: number; after: number }; scope?: 'thread' | 'resource' }`): Enable semantic search in message history. Can be a boolean or an object with configuration options. When enabled, requires
|
|
52
|
+
**options.semanticRecall** (`boolean | { topK: number; messageRange: number | { before: number; after: number }; scope?: 'thread' | 'resource' }`): Enable semantic search in message history. Can be a boolean or an object with configuration options. When enabled, requires a vector store, and an embedder unless the vector store embeds text itself (isSelfEmbedding). Default topK is 4, default messageRange is {before: 1, after: 1}.
|
|
53
53
|
|
|
54
54
|
**options.workingMemory** (`WorkingMemory`): Configuration for working memory feature. Can be { enabled: boolean; template?: string; schema?: ZodObject\<any> | JSONSchema7; scope?: 'thread' | 'resource' } or { enabled: boolean } to disable.
|
|
55
55
|
|
|
@@ -128,8 +128,10 @@ export const agent = new Agent({
|
|
|
128
128
|
indexConfig: {
|
|
129
129
|
type: 'hnsw', // Use HNSW for better performance
|
|
130
130
|
metric: 'dotproduct', // Optimal for OpenAI embeddings
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
hnsw: {
|
|
132
|
+
m: 16, // Number of bi-directional links
|
|
133
|
+
efConstruction: 64, // Construction-time candidate list size
|
|
134
|
+
},
|
|
133
135
|
},
|
|
134
136
|
},
|
|
135
137
|
workingMemory: {
|
package/dist/index.cjs
CHANGED
|
@@ -262,7 +262,7 @@ const createNumericOperator = (symbol) => {
|
|
|
262
262
|
sql: `(CASE WHEN jsonb_typeof(${jsonExtract}) = 'number' THEN (${textExtract})::numeric ${symbol} $${paramIndex}::numeric ELSE NULL END)`,
|
|
263
263
|
needsValue: true
|
|
264
264
|
};
|
|
265
|
-
|
|
265
|
+
return {
|
|
266
266
|
sql: `${textExtract} ${symbol} $${paramIndex}::text`,
|
|
267
267
|
needsValue: true
|
|
268
268
|
};
|
|
@@ -4019,6 +4019,42 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4019
4019
|
}, error);
|
|
4020
4020
|
}
|
|
4021
4021
|
}
|
|
4022
|
+
/**
|
|
4023
|
+
* Runs one UPDATE per record in a transaction, changing a row only while every
|
|
4024
|
+
* column in `nullColumns` is still NULL. Never inserts. Returns, in input order,
|
|
4025
|
+
* whether each update changed a row.
|
|
4026
|
+
*/
|
|
4027
|
+
async batchUpdateWhereNull({ tableName, updates, nullColumns }) {
|
|
4028
|
+
const nullConditions = nullColumns.map((column) => ` AND "${(0, _mastra_core_utils.parseSqlIdentifier)(column, "column name")}" IS NULL`).join("");
|
|
4029
|
+
try {
|
|
4030
|
+
return await this.client.tx(async (tx) => {
|
|
4031
|
+
const applied = [];
|
|
4032
|
+
for (const update of updates) {
|
|
4033
|
+
const query = await this.buildUpdateQuery({
|
|
4034
|
+
tableName,
|
|
4035
|
+
...update
|
|
4036
|
+
});
|
|
4037
|
+
if (!query) {
|
|
4038
|
+
applied.push(false);
|
|
4039
|
+
continue;
|
|
4040
|
+
}
|
|
4041
|
+
const rows = await tx.any(`${query.sql}${nullConditions} RETURNING 1`, query.values);
|
|
4042
|
+
applied.push(rows.length > 0);
|
|
4043
|
+
}
|
|
4044
|
+
return applied;
|
|
4045
|
+
});
|
|
4046
|
+
} catch (error) {
|
|
4047
|
+
throw new _mastra_core_error.MastraError({
|
|
4048
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "BATCH_UPDATE_WHERE_NULL", "FAILED"),
|
|
4049
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
4050
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
4051
|
+
details: {
|
|
4052
|
+
tableName,
|
|
4053
|
+
numberOfRecords: updates.length
|
|
4054
|
+
}
|
|
4055
|
+
}, error);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4022
4058
|
async batchDelete({ tableName, keys }) {
|
|
4023
4059
|
try {
|
|
4024
4060
|
if (keys.length === 0) return;
|
|
@@ -4139,9 +4175,14 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4139
4175
|
columns: [column]
|
|
4140
4176
|
});
|
|
4141
4177
|
}
|
|
4142
|
-
async executeUpdate(client,
|
|
4178
|
+
async executeUpdate(client, args) {
|
|
4179
|
+
const query = await this.buildUpdateQuery(args);
|
|
4180
|
+
if (!query) return;
|
|
4181
|
+
await client.none(query.sql, query.values);
|
|
4182
|
+
}
|
|
4183
|
+
async buildUpdateQuery({ tableName, keys, data }) {
|
|
4143
4184
|
const filteredData = await this.filterRecordToKnownColumns(tableName, data);
|
|
4144
|
-
if (Object.keys(filteredData).length === 0) return;
|
|
4185
|
+
if (Object.keys(filteredData).length === 0) return null;
|
|
4145
4186
|
const setColumns = [];
|
|
4146
4187
|
const setValues = [];
|
|
4147
4188
|
let paramIndex = 1;
|
|
@@ -4157,12 +4198,13 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4157
4198
|
whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
|
|
4158
4199
|
whereValues.push(this.prepareValue(value, key, tableName));
|
|
4159
4200
|
});
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4201
|
+
return {
|
|
4202
|
+
sql: `UPDATE ${getTableName$6({
|
|
4203
|
+
indexName: tableName,
|
|
4204
|
+
schemaName: getSchemaName$6(this.schemaName)
|
|
4205
|
+
})} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`,
|
|
4206
|
+
values: [...setValues, ...whereValues]
|
|
4207
|
+
};
|
|
4166
4208
|
}
|
|
4167
4209
|
};
|
|
4168
4210
|
// src/storage/domains/utils.ts
|
|
@@ -12038,7 +12080,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
12038
12080
|
indexName: OM_TABLE,
|
|
12039
12081
|
schemaName: quotedSchemaName
|
|
12040
12082
|
});
|
|
12041
|
-
const idxPrefix = schemaPrefix ?
|
|
12083
|
+
const idxPrefix = schemaPrefix ? schemaPrefix : "";
|
|
12042
12084
|
statements.push(`CREATE INDEX IF NOT EXISTS "${idxPrefix}idx_om_lookup_key" ON ${fullOmTableName} ("lookupKey");`);
|
|
12043
12085
|
}
|
|
12044
12086
|
for (const idx of MemoryPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
@@ -15712,30 +15754,33 @@ var ObservabilityPG = class ObservabilityPG extends _mastra_core_storage.Observa
|
|
|
15712
15754
|
}, error);
|
|
15713
15755
|
}
|
|
15714
15756
|
}
|
|
15757
|
+
#toSpanUpdates(records) {
|
|
15758
|
+
return records.map((record) => {
|
|
15759
|
+
const data = { ...record.updates };
|
|
15760
|
+
if (data.endedAt instanceof Date) {
|
|
15761
|
+
const endedAt = data.endedAt.toISOString();
|
|
15762
|
+
data.endedAt = endedAt;
|
|
15763
|
+
data.endedAtZ = endedAt;
|
|
15764
|
+
}
|
|
15765
|
+
if (data.startedAt instanceof Date) {
|
|
15766
|
+
const startedAt = data.startedAt.toISOString();
|
|
15767
|
+
data.startedAt = startedAt;
|
|
15768
|
+
data.startedAtZ = startedAt;
|
|
15769
|
+
}
|
|
15770
|
+
return {
|
|
15771
|
+
keys: {
|
|
15772
|
+
spanId: record.spanId,
|
|
15773
|
+
traceId: record.traceId
|
|
15774
|
+
},
|
|
15775
|
+
data
|
|
15776
|
+
};
|
|
15777
|
+
});
|
|
15778
|
+
}
|
|
15715
15779
|
async batchUpdateSpans(args) {
|
|
15716
15780
|
try {
|
|
15717
15781
|
return this.#db.batchUpdate({
|
|
15718
15782
|
tableName: _mastra_core_storage.TABLE_SPANS,
|
|
15719
|
-
updates: args.records
|
|
15720
|
-
const data = { ...record.updates };
|
|
15721
|
-
if (data.endedAt instanceof Date) {
|
|
15722
|
-
const endedAt = data.endedAt.toISOString();
|
|
15723
|
-
data.endedAt = endedAt;
|
|
15724
|
-
data.endedAtZ = endedAt;
|
|
15725
|
-
}
|
|
15726
|
-
if (data.startedAt instanceof Date) {
|
|
15727
|
-
const startedAt = data.startedAt.toISOString();
|
|
15728
|
-
data.startedAt = startedAt;
|
|
15729
|
-
data.startedAtZ = startedAt;
|
|
15730
|
-
}
|
|
15731
|
-
return {
|
|
15732
|
-
keys: {
|
|
15733
|
-
spanId: record.spanId,
|
|
15734
|
-
traceId: record.traceId
|
|
15735
|
-
},
|
|
15736
|
-
data
|
|
15737
|
-
};
|
|
15738
|
-
})
|
|
15783
|
+
updates: this.#toSpanUpdates(args.records)
|
|
15739
15784
|
});
|
|
15740
15785
|
} catch (error) {
|
|
15741
15786
|
throw new _mastra_core_error.MastraError({
|
|
@@ -15745,6 +15790,25 @@ var ObservabilityPG = class ObservabilityPG extends _mastra_core_storage.Observa
|
|
|
15745
15790
|
}, error);
|
|
15746
15791
|
}
|
|
15747
15792
|
}
|
|
15793
|
+
async batchUpdateUnendedSpans(args) {
|
|
15794
|
+
try {
|
|
15795
|
+
const applied = await this.#db.batchUpdateWhereNull({
|
|
15796
|
+
tableName: _mastra_core_storage.TABLE_SPANS,
|
|
15797
|
+
updates: this.#toSpanUpdates(args.records),
|
|
15798
|
+
nullColumns: ["endedAt"]
|
|
15799
|
+
});
|
|
15800
|
+
return { updated: args.records.filter((_, index) => applied[index]).map(({ traceId, spanId }) => ({
|
|
15801
|
+
traceId,
|
|
15802
|
+
spanId
|
|
15803
|
+
})) };
|
|
15804
|
+
} catch (error) {
|
|
15805
|
+
throw new _mastra_core_error.MastraError({
|
|
15806
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "BATCH_UPDATE_UNENDED_SPANS", "FAILED"),
|
|
15807
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
15808
|
+
category: _mastra_core_error.ErrorCategory.USER
|
|
15809
|
+
}, error);
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15748
15812
|
async batchDeleteTraces(args) {
|
|
15749
15813
|
this.assertUnscopedBatchDeleteTraces(args);
|
|
15750
15814
|
try {
|
|
@@ -16062,6 +16126,51 @@ const SPAN_EVENT_COLUMNS = [
|
|
|
16062
16126
|
type: "double precision",
|
|
16063
16127
|
nullable: true
|
|
16064
16128
|
},
|
|
16129
|
+
{
|
|
16130
|
+
name: "externalParentSpanId",
|
|
16131
|
+
type: "text",
|
|
16132
|
+
nullable: true
|
|
16133
|
+
},
|
|
16134
|
+
{
|
|
16135
|
+
name: "resumedFromSpanId",
|
|
16136
|
+
type: "text",
|
|
16137
|
+
nullable: true
|
|
16138
|
+
},
|
|
16139
|
+
{
|
|
16140
|
+
name: "model",
|
|
16141
|
+
type: "text",
|
|
16142
|
+
nullable: true
|
|
16143
|
+
},
|
|
16144
|
+
{
|
|
16145
|
+
name: "provider",
|
|
16146
|
+
type: "text",
|
|
16147
|
+
nullable: true
|
|
16148
|
+
},
|
|
16149
|
+
{
|
|
16150
|
+
name: "outcome",
|
|
16151
|
+
type: "text",
|
|
16152
|
+
nullable: true
|
|
16153
|
+
},
|
|
16154
|
+
{
|
|
16155
|
+
name: "stopReason",
|
|
16156
|
+
type: "text",
|
|
16157
|
+
nullable: true
|
|
16158
|
+
},
|
|
16159
|
+
{
|
|
16160
|
+
name: "timeToFirstTokenMs",
|
|
16161
|
+
type: "double precision",
|
|
16162
|
+
nullable: true
|
|
16163
|
+
},
|
|
16164
|
+
{
|
|
16165
|
+
name: "errorCount",
|
|
16166
|
+
type: "integer",
|
|
16167
|
+
nullable: true
|
|
16168
|
+
},
|
|
16169
|
+
{
|
|
16170
|
+
name: "stopReasonContext",
|
|
16171
|
+
type: "jsonb",
|
|
16172
|
+
nullable: true
|
|
16173
|
+
},
|
|
16065
16174
|
{
|
|
16066
16175
|
name: "tags",
|
|
16067
16176
|
type: "text[]",
|
|
@@ -16770,6 +16879,26 @@ function allTableDDL(schema, mode) {
|
|
|
16770
16879
|
];
|
|
16771
16880
|
}
|
|
16772
16881
|
/**
|
|
16882
|
+
* Span event columns added after the table first shipped, with their types. All
|
|
16883
|
+
* nullable with no default, so each ALTER is a catalog-only change; rows written
|
|
16884
|
+
* before it read NULL and readers fall back to the payload or attributes.
|
|
16885
|
+
*/
|
|
16886
|
+
const ADDED_SPAN_COLUMNS = [
|
|
16887
|
+
["inputPreview", "text"],
|
|
16888
|
+
["outputPreview", "text"],
|
|
16889
|
+
["errorPreview", "text"],
|
|
16890
|
+
["timeToFirstChunkMs", "double precision"],
|
|
16891
|
+
["externalParentSpanId", "text"],
|
|
16892
|
+
["resumedFromSpanId", "text"],
|
|
16893
|
+
["model", "text"],
|
|
16894
|
+
["provider", "text"],
|
|
16895
|
+
["outcome", "text"],
|
|
16896
|
+
["stopReason", "text"],
|
|
16897
|
+
["timeToFirstTokenMs", "double precision"],
|
|
16898
|
+
["errorCount", "integer"],
|
|
16899
|
+
["stopReasonContext", "jsonb"]
|
|
16900
|
+
];
|
|
16901
|
+
/**
|
|
16773
16902
|
* Additive column migrations for tables created by an older version of this
|
|
16774
16903
|
* schema. `CREATE TABLE IF NOT EXISTS` is a no-op on an existing table, so new
|
|
16775
16904
|
* columns have to be added out of band.
|
|
@@ -16786,20 +16915,11 @@ function additiveColumns(schema) {
|
|
|
16786
16915
|
column: "isPending",
|
|
16787
16916
|
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
|
|
16788
16917
|
},
|
|
16789
|
-
...[
|
|
16790
|
-
"inputPreview",
|
|
16791
|
-
"outputPreview",
|
|
16792
|
-
"errorPreview"
|
|
16793
|
-
].map((column) => ({
|
|
16918
|
+
...ADDED_SPAN_COLUMNS.map(([column, type]) => ({
|
|
16794
16919
|
table: TABLE_SPAN_EVENTS,
|
|
16795
16920
|
column,
|
|
16796
|
-
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "${column}"
|
|
16921
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "${column}" ${type}`
|
|
16797
16922
|
})),
|
|
16798
|
-
{
|
|
16799
|
-
table: TABLE_SPAN_EVENTS,
|
|
16800
|
-
column: "timeToFirstChunkMs",
|
|
16801
|
-
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "timeToFirstChunkMs" double precision`
|
|
16802
|
-
},
|
|
16803
16923
|
{
|
|
16804
16924
|
table: TABLE_FEEDBACK_EVENTS,
|
|
16805
16925
|
column: "reviewStatus",
|
|
@@ -17352,6 +17472,11 @@ const PROMOTED_KEYS = /* @__PURE__ */ new Set([
|
|
|
17352
17472
|
"executionSource",
|
|
17353
17473
|
"serviceName"
|
|
17354
17474
|
]);
|
|
17475
|
+
function nullableNumber$1(value) {
|
|
17476
|
+
if (value == null || value === "") return null;
|
|
17477
|
+
const n = Number(value);
|
|
17478
|
+
return Number.isNaN(n) ? null : n;
|
|
17479
|
+
}
|
|
17355
17480
|
function nullableString(value) {
|
|
17356
17481
|
if (typeof value === "string") return value === "" ? null : value;
|
|
17357
17482
|
if (value == null) return null;
|
|
@@ -17513,6 +17638,15 @@ function spanRecordToRow(span) {
|
|
|
17513
17638
|
startedAt: toIsoOrDate(span.startedAt),
|
|
17514
17639
|
endedAt: toIsoOrDate(endedAt),
|
|
17515
17640
|
timeToFirstChunkMs: span.timeToFirstChunkMs ?? null,
|
|
17641
|
+
externalParentSpanId: span.externalParentSpanId ?? null,
|
|
17642
|
+
resumedFromSpanId: span.resumedFromSpanId ?? null,
|
|
17643
|
+
model: span.model ?? null,
|
|
17644
|
+
provider: span.provider ?? null,
|
|
17645
|
+
outcome: span.outcome ?? null,
|
|
17646
|
+
stopReason: span.stopReason ?? null,
|
|
17647
|
+
stopReasonContext: jsonField(span.stopReasonContext),
|
|
17648
|
+
timeToFirstTokenMs: span.timeToFirstTokenMs ?? null,
|
|
17649
|
+
errorCount: span.errorCount ?? null,
|
|
17516
17650
|
tags: normalizeTags(span.tags),
|
|
17517
17651
|
metadataSearch: buildMetadataSearch(metadata),
|
|
17518
17652
|
metadataRaw: jsonField(metadata),
|
|
@@ -17542,6 +17676,7 @@ function rowToSpanRecord(row) {
|
|
|
17542
17676
|
const startedAt = toDate(row.startedAt);
|
|
17543
17677
|
const endedAt = spanRowEndedAt(row, startedAt);
|
|
17544
17678
|
const error = parsedJson(row.error);
|
|
17679
|
+
const attributes = parsedJson(row.attributes);
|
|
17545
17680
|
const { executionSource, ...ctx } = rowToCommonContext(row);
|
|
17546
17681
|
return {
|
|
17547
17682
|
...ctx,
|
|
@@ -17554,10 +17689,19 @@ function rowToSpanRecord(row) {
|
|
|
17554
17689
|
isEvent: Boolean(row.isEvent),
|
|
17555
17690
|
startedAt,
|
|
17556
17691
|
endedAt,
|
|
17692
|
+
externalParentSpanId: nullableString(row.externalParentSpanId),
|
|
17693
|
+
resumedFromSpanId: nullableString(row.resumedFromSpanId),
|
|
17694
|
+
model: nullableString(row.model) ?? (0, _mastra_core_storage.getStringAttribute)(attributes, "model"),
|
|
17695
|
+
provider: nullableString(row.provider) ?? (0, _mastra_core_storage.getStringAttribute)(attributes, "provider"),
|
|
17696
|
+
outcome: nullableString(row.outcome),
|
|
17697
|
+
stopReason: nullableString(row.stopReason),
|
|
17698
|
+
stopReasonContext: parsedJson(row.stopReasonContext) ?? null,
|
|
17699
|
+
timeToFirstTokenMs: nullableNumber$1(row.timeToFirstTokenMs),
|
|
17700
|
+
errorCount: nullableNumber$1(row.errorCount),
|
|
17557
17701
|
tags: normalizeTags(row.tags),
|
|
17558
17702
|
metadata: parsedJson(row.metadataRaw) ?? void 0,
|
|
17559
17703
|
scope: parsedJson(row.scope) ?? void 0,
|
|
17560
|
-
attributes:
|
|
17704
|
+
attributes: attributes ?? void 0,
|
|
17561
17705
|
links: parsedJson(row.links) ?? void 0,
|
|
17562
17706
|
input: parsedJson(row.input) ?? void 0,
|
|
17563
17707
|
output: parsedJson(row.output) ?? void 0,
|
|
@@ -20583,7 +20727,7 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20583
20727
|
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${values.length}`;
|
|
20584
20728
|
}).join(" AND ")})`;
|
|
20585
20729
|
}));
|
|
20586
|
-
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId",
|
|
20730
|
+
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", ${contextFields.map((field) => `"${field}"`).join(", ")},
|
|
20587
20731
|
CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs",
|
|
20588
20732
|
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
20589
20733
|
${[
|
|
@@ -20598,30 +20742,10 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20598
20742
|
String(version)
|
|
20599
20743
|
]);
|
|
20600
20744
|
const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
20601
|
-
const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
|
|
20602
|
-
const models = /* @__PURE__ */ new Map();
|
|
20603
|
-
if (agents.length) {
|
|
20604
|
-
const modelValues = [];
|
|
20605
|
-
const rows = await run(`SELECT DISTINCT ON ("organizationId", "traceId") "organizationId", "traceId",
|
|
20606
|
-
CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
|
|
20607
|
-
CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider
|
|
20608
|
-
FROM ${table} WHERE "spanType" = 'model_generation' AND NOT "isPending" AND (${agents.map((row) => `(${Object.entries({
|
|
20609
|
-
organizationId: row.organizationId,
|
|
20610
|
-
resourceId: row.resourceId,
|
|
20611
|
-
traceId: row.firstTraceId,
|
|
20612
|
-
parentSpanId: row.firstSpanId
|
|
20613
|
-
}).map(([field, value]) => {
|
|
20614
|
-
modelValues.push(value);
|
|
20615
|
-
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${modelValues.length}`;
|
|
20616
|
-
}).join(" AND ")})`).join(" OR ")})
|
|
20617
|
-
ORDER BY "organizationId", "traceId", "startedAt", "spanId" COLLATE "C", "cursorId" DESC`, modelValues);
|
|
20618
|
-
for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
|
|
20619
|
-
}
|
|
20620
20745
|
const threads = page.flatMap((row) => {
|
|
20621
20746
|
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
20622
20747
|
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
20623
20748
|
if (!first || !last) return [];
|
|
20624
|
-
const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
20625
20749
|
return [_mastra_core_storage.threadQueryRowSchema.parse({
|
|
20626
20750
|
organizationId: row.organizationId,
|
|
20627
20751
|
threadId: row.threadId,
|
|
@@ -20633,8 +20757,6 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20633
20757
|
endedAt: row.endedAt.toISOString(),
|
|
20634
20758
|
traceCount: Number(row.traceCount),
|
|
20635
20759
|
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
20636
|
-
model: model?.model ?? null,
|
|
20637
|
-
provider: model?.provider ?? null,
|
|
20638
20760
|
inputPreview: readPreview(first.inputPreview, first.input),
|
|
20639
20761
|
outputPreview: readPreview(last.outputPreview, last.output),
|
|
20640
20762
|
errorPreview: readPreview(last.errorPreview, last.error)
|
|
@@ -25618,6 +25740,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25618
25740
|
supportsConcurrentUpdates() {
|
|
25619
25741
|
return true;
|
|
25620
25742
|
}
|
|
25743
|
+
supportsGuardedSnapshotWrites() {
|
|
25744
|
+
return true;
|
|
25745
|
+
}
|
|
25746
|
+
/** SQL for a snapshot's status. Legacy json/text columns can hold escapes that fail a jsonb cast (#11563). */
|
|
25747
|
+
async #statusExpression(column) {
|
|
25748
|
+
return await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `${column} ->> 'status'` : `regexp_replace(${column}::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
|
|
25749
|
+
}
|
|
25621
25750
|
parseWorkflowRun(row) {
|
|
25622
25751
|
let parsedSnapshot = row.snapshot;
|
|
25623
25752
|
if (typeof parsedSnapshot === "string") try {
|
|
@@ -26013,20 +26142,22 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26013
26142
|
}, error);
|
|
26014
26143
|
}
|
|
26015
26144
|
}
|
|
26016
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
26145
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence, expectedStatus }) {
|
|
26017
26146
|
try {
|
|
26018
26147
|
const now = /* @__PURE__ */ new Date();
|
|
26019
26148
|
const createdAtValue = createdAt ? createdAt : now;
|
|
26020
26149
|
const updatedAtValue = updatedAt ? updatedAt : now;
|
|
26021
26150
|
const sanitizedSnapshot = toPgJson(snapshot);
|
|
26022
|
-
|
|
26151
|
+
const guard = expectedStatus === void 0 ? "" : ` WHERE ${await this.#statusExpression("t.snapshot")} = ANY($9::text[])`;
|
|
26152
|
+
return await withRunFence(this.#db.client, this.#runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, runId), "persistWorkflowSnapshot", (q) => q.oneOrNone(`INSERT INTO ${getTableName({
|
|
26023
26153
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26024
26154
|
schemaName: getSchemaName(this.#schema)
|
|
26025
26155
|
})} AS t
|
|
26026
26156
|
(workflow_name, run_id, "resourceId", snapshot, "createdAt", "updatedAt", "createdAtZ", "updatedAtZ")
|
|
26027
26157
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
26028
26158
|
ON CONFLICT (workflow_name, run_id) DO UPDATE
|
|
26029
|
-
SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8
|
|
26159
|
+
SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8${guard}
|
|
26160
|
+
RETURNING 1 AS written`, [
|
|
26030
26161
|
workflowName,
|
|
26031
26162
|
runId,
|
|
26032
26163
|
resourceId,
|
|
@@ -26034,8 +26165,9 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26034
26165
|
createdAtValue,
|
|
26035
26166
|
updatedAtValue,
|
|
26036
26167
|
createdAtValue,
|
|
26037
|
-
updatedAtValue
|
|
26038
|
-
|
|
26168
|
+
updatedAtValue,
|
|
26169
|
+
...expectedStatus === void 0 ? [] : [Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus]]
|
|
26170
|
+
])) !== null;
|
|
26039
26171
|
} catch (error) {
|
|
26040
26172
|
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26041
26173
|
throw new _mastra_core_error.MastraError({
|
|
@@ -26045,6 +26177,25 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26045
26177
|
}, error);
|
|
26046
26178
|
}
|
|
26047
26179
|
}
|
|
26180
|
+
async loadWorkflowRunStatus({ workflowName, runId }) {
|
|
26181
|
+
try {
|
|
26182
|
+
const statusExpr = await this.#statusExpression("snapshot");
|
|
26183
|
+
return (await this.#db.client.oneOrNone(`SELECT ${statusExpr} AS status FROM ${getTableName({
|
|
26184
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26185
|
+
schemaName: getSchemaName(this.#schema)
|
|
26186
|
+
})} WHERE "workflow_name" = $1 AND "run_id" = $2 ORDER BY "createdAt" DESC LIMIT 1`, [workflowName, runId]))?.status ?? null;
|
|
26187
|
+
} catch (error) {
|
|
26188
|
+
throw new _mastra_core_error.MastraError({
|
|
26189
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
|
|
26190
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
26191
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
26192
|
+
details: {
|
|
26193
|
+
workflowName,
|
|
26194
|
+
runId
|
|
26195
|
+
}
|
|
26196
|
+
}, error);
|
|
26197
|
+
}
|
|
26198
|
+
}
|
|
26048
26199
|
async loadWorkflowSnapshot({ workflowName, runId }) {
|
|
26049
26200
|
try {
|
|
26050
26201
|
const result = await this.#db.load({
|