@mastra/pg 1.26.0-alpha.1 → 1.26.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-rag-vector-databases.md +34 -0
- package/dist/docs/references/reference-storage-retention.md +56 -4
- package/dist/index.cjs +194 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +195 -162
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/scores.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/scores.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +4 -2
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +5 -58
- package/dist/storage/retention.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "crypto";
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
3
3
|
import * as coreStorage from "@mastra/core/storage";
|
|
4
|
-
import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_ACTIVITY_SCHEMA, KNOWLEDGE_CURSORS_SCHEMA, KNOWLEDGE_MENTIONS_SCHEMA, KNOWLEDGE_NODES_SCHEMA, KNOWLEDGE_RECORDS_SCHEMA, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertKnowledgeCeilingRaised, assertKnowledgeScopeWithinCeiling, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
4
|
+
import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_ACTIVITY_SCHEMA, KNOWLEDGE_CURSORS_SCHEMA, KNOWLEDGE_MENTIONS_SCHEMA, KNOWLEDGE_NODES_SCHEMA, KNOWLEDGE_RECORDS_SCHEMA, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertKnowledgeCeilingRaised, assertKnowledgeScopeWithinCeiling, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveRetentionTargets, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
5
5
|
import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
|
|
6
6
|
import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
|
|
7
7
|
import { Mutex } from "async-mutex";
|
|
@@ -4903,137 +4903,31 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4903
4903
|
};
|
|
4904
4904
|
//#endregion
|
|
4905
4905
|
//#region src/storage/retention.ts
|
|
4906
|
-
const DEFAULT_BATCH_SIZE = 1e3;
|
|
4907
|
-
async function sleep(ms, signal) {
|
|
4908
|
-
if (ms <= 0 || signal?.aborted) return;
|
|
4909
|
-
await new Promise((resolve) => {
|
|
4910
|
-
const timer = setTimeout(() => {
|
|
4911
|
-
signal?.removeEventListener("abort", onAbort);
|
|
4912
|
-
resolve();
|
|
4913
|
-
}, ms);
|
|
4914
|
-
const onAbort = () => {
|
|
4915
|
-
clearTimeout(timer);
|
|
4916
|
-
resolve();
|
|
4917
|
-
};
|
|
4918
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4919
|
-
});
|
|
4920
|
-
}
|
|
4921
|
-
/**
|
|
4922
|
-
* Convert a policy's `maxAge` into a cutoff bound matching the anchor's storage
|
|
4923
|
-
* type: a `Date` for `timestamptz` columns (pg compares timezone-aware), or a
|
|
4924
|
-
* raw millisecond number for `bigint` epoch-ms columns.
|
|
4925
|
-
*/
|
|
4926
4906
|
function cutoffFor(policy, anchorType, now = Date.now()) {
|
|
4927
|
-
const cutoffMs =
|
|
4907
|
+
const cutoffMs = retentionCutoffMs(policy, now);
|
|
4928
4908
|
return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs);
|
|
4929
4909
|
}
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
};
|
|
4945
|
-
if (options?.maxBatches !== void 0 && batches >= options.maxBatches) return {
|
|
4946
|
-
deleted,
|
|
4947
|
-
done: false
|
|
4948
|
-
};
|
|
4949
|
-
let limit = batchSize;
|
|
4950
|
-
if (options?.maxRows !== void 0) {
|
|
4951
|
-
const remaining = options.maxRows - deleted;
|
|
4952
|
-
if (remaining <= 0) return {
|
|
4953
|
-
deleted,
|
|
4954
|
-
done: false
|
|
4955
|
-
};
|
|
4956
|
-
limit = Math.min(limit, remaining);
|
|
4957
|
-
}
|
|
4958
|
-
const affected = await deleteBatch(limit);
|
|
4959
|
-
deleted += affected;
|
|
4960
|
-
batches += 1;
|
|
4961
|
-
if (affected < limit) return {
|
|
4962
|
-
deleted,
|
|
4963
|
-
done: true
|
|
4964
|
-
};
|
|
4965
|
-
if (options?.pauseMs) await sleep(options.pauseMs, options.signal);
|
|
4966
|
-
}
|
|
4967
|
-
}
|
|
4968
|
-
/**
|
|
4969
|
-
* Runs the bounded, batched, cancellable delete loop for a set of tables in the
|
|
4970
|
-
* given order (callers pass children before parents for cascade-safe pruning),
|
|
4971
|
-
* and returns one {@link PruneResult} per table.
|
|
4972
|
-
*
|
|
4973
|
-
* The loop:
|
|
4974
|
-
* - deletes in chunks of `batchSize` (default 1000), each its own statement;
|
|
4975
|
-
* - stops a table's loop when a batch deletes fewer rows than requested (drained),
|
|
4976
|
-
* or when `maxBatches`/`maxRows` is hit, or the `signal` aborts — the latter
|
|
4977
|
-
* three leave `done: false` so the caller can resume;
|
|
4978
|
-
* - pauses `pauseMs` between batches when set, to avoid starving live traffic.
|
|
4979
|
-
*
|
|
4980
|
-
* `prune()` only deletes rows; it never reclaims disk. PostgreSQL reuses freed
|
|
4981
|
-
* space (dead tuples) via autovacuum on subsequent writes, so tables stop
|
|
4982
|
-
* growing. Returning disk to the OS (e.g. `VACUUM FULL`) is left to the operator.
|
|
4983
|
-
*/
|
|
4984
|
-
async function runPrune({ db, domain, targets, options }) {
|
|
4985
|
-
const results = [];
|
|
4986
|
-
const now = Date.now();
|
|
4987
|
-
for (const target of targets) {
|
|
4988
|
-
if (options?.signal?.aborted) {
|
|
4989
|
-
results.push({
|
|
4990
|
-
domain,
|
|
4991
|
-
table: target.table,
|
|
4992
|
-
deleted: 0,
|
|
4993
|
-
done: false
|
|
4994
|
-
});
|
|
4995
|
-
continue;
|
|
4996
|
-
}
|
|
4997
|
-
const cutoff = cutoffFor(target.policy, target.anchorType, now);
|
|
4998
|
-
const { deleted, done } = await runBatchedDelete({
|
|
4999
|
-
deleteBatch: (limit) => db.pruneBatch({
|
|
5000
|
-
tableName: target.table,
|
|
5001
|
-
column: target.column,
|
|
5002
|
-
cutoff,
|
|
5003
|
-
limit
|
|
5004
|
-
}),
|
|
5005
|
-
batchSize: target.policy.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
5006
|
-
options
|
|
5007
|
-
});
|
|
5008
|
-
results.push({
|
|
5009
|
-
domain,
|
|
5010
|
-
table: target.table,
|
|
5011
|
-
deleted,
|
|
5012
|
-
done
|
|
5013
|
-
});
|
|
5014
|
-
}
|
|
5015
|
-
return results;
|
|
4910
|
+
const runBatchedDelete = runRetentionBatches;
|
|
4911
|
+
function runPrune({ db, domain, targets, options }) {
|
|
4912
|
+
return executeRetentionPrune({
|
|
4913
|
+
domain,
|
|
4914
|
+
targets,
|
|
4915
|
+
options,
|
|
4916
|
+
cutoffFor: (target, now) => cutoffFor(target.policy, target.anchorType ?? "timestamp", now),
|
|
4917
|
+
deleteBatch: (target, cutoff, limit) => db.pruneBatch({
|
|
4918
|
+
tableName: target.table,
|
|
4919
|
+
column: target.column,
|
|
4920
|
+
cutoff,
|
|
4921
|
+
limit
|
|
4922
|
+
})
|
|
4923
|
+
});
|
|
5016
4924
|
}
|
|
5017
|
-
/**
|
|
5018
|
-
* Resolve a domain's `{ tableKey: policy }` map plus its descriptor into an
|
|
5019
|
-
* ordered list of {@link PruneTarget}s. `order` lists table keys children-first
|
|
5020
|
-
* so cascade-dependent rows are removed before their parents. Table keys not in
|
|
5021
|
-
* `policies` are skipped (unset = keep forever).
|
|
5022
|
-
*/
|
|
5023
4925
|
function resolveTargets({ policies, descriptor, order }) {
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
targets.push({
|
|
5030
|
-
table: entry.table,
|
|
5031
|
-
column: entry.column,
|
|
5032
|
-
anchorType: entry.anchorType ?? "timestamp",
|
|
5033
|
-
policy
|
|
5034
|
-
});
|
|
5035
|
-
}
|
|
5036
|
-
return targets;
|
|
4926
|
+
return resolveRetentionTargets({
|
|
4927
|
+
policies,
|
|
4928
|
+
descriptor,
|
|
4929
|
+
order
|
|
4930
|
+
});
|
|
5037
4931
|
}
|
|
5038
4932
|
//#endregion
|
|
5039
4933
|
//#region src/storage/domains/background-tasks/index.ts
|
|
@@ -10864,7 +10758,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10864
10758
|
hasMore: false
|
|
10865
10759
|
};
|
|
10866
10760
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10867
|
-
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
10761
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction}, "id" ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
10868
10762
|
return {
|
|
10869
10763
|
threads: (await this.#db.readClient.manyOrNone(dataQuery, [
|
|
10870
10764
|
...queryParams,
|
|
@@ -11057,10 +10951,11 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11057
10951
|
return messages.sort((a, b) => {
|
|
11058
10952
|
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
11059
10953
|
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
11060
|
-
|
|
10954
|
+
const idOrder = direction === "ASC" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
|
|
10955
|
+
if (aValue == null && bValue == null) return idOrder;
|
|
11061
10956
|
if (aValue == null) return 1;
|
|
11062
10957
|
if (bValue == null) return -1;
|
|
11063
|
-
if (aValue === bValue) return
|
|
10958
|
+
if (aValue === bValue) return idOrder;
|
|
11064
10959
|
if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
11065
10960
|
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
11066
10961
|
});
|
|
@@ -11263,7 +11158,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11263
11158
|
const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
|
|
11264
11159
|
try {
|
|
11265
11160
|
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
11266
|
-
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
11161
|
+
const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
|
|
11267
11162
|
const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
|
|
11268
11163
|
const tableName = getTableName$3({
|
|
11269
11164
|
indexName: TABLE_MESSAGES,
|
|
@@ -11418,7 +11313,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11418
11313
|
const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
|
|
11419
11314
|
try {
|
|
11420
11315
|
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
11421
|
-
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
11316
|
+
const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
|
|
11422
11317
|
const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
|
|
11423
11318
|
const tableName = getTableName$3({
|
|
11424
11319
|
indexName: TABLE_MESSAGES,
|
|
@@ -14913,6 +14808,11 @@ function tableIndexes() {
|
|
|
14913
14808
|
columns: "(\"tags\")",
|
|
14914
14809
|
using: "gin"
|
|
14915
14810
|
},
|
|
14811
|
+
{
|
|
14812
|
+
name: "mastra_score_events_scoreid_cursor_idx",
|
|
14813
|
+
table: TABLE_SCORE_EVENTS,
|
|
14814
|
+
columns: "(\"scoreId\", \"cursorId\" DESC)"
|
|
14815
|
+
},
|
|
14916
14816
|
{
|
|
14917
14817
|
name: "mastra_score_events_cursor_idx",
|
|
14918
14818
|
table: TABLE_SCORE_EVENTS,
|
|
@@ -17045,13 +16945,32 @@ function pushScoreIdentity(acc, scorerId, scoreSource) {
|
|
|
17045
16945
|
acc.params.push(scoreSource);
|
|
17046
16946
|
}
|
|
17047
16947
|
}
|
|
16948
|
+
function scoreRewriteConflict(row) {
|
|
16949
|
+
return `ON CONFLICT ("scoreId", "timestamp") DO UPDATE SET ${[
|
|
16950
|
+
...Object.keys(row).filter((column) => column !== "scoreId" && column !== "timestamp").map((column) => `"${column}" = EXCLUDED."${column}"`),
|
|
16951
|
+
"\"cursorId\" = EXCLUDED.\"cursorId\"",
|
|
16952
|
+
"\"xactId\" = EXCLUDED.\"xactId\""
|
|
16953
|
+
].join(", ")}`;
|
|
16954
|
+
}
|
|
16955
|
+
function collapseExactScoreConflicts(rows) {
|
|
16956
|
+
const records = /* @__PURE__ */ new Map();
|
|
16957
|
+
for (const row of rows) {
|
|
16958
|
+
const timestamp = new Date(row.timestamp).toISOString();
|
|
16959
|
+
const key = `${String(row.scoreId)}\u0000${timestamp}`;
|
|
16960
|
+
records.delete(key);
|
|
16961
|
+
records.set(key, row);
|
|
16962
|
+
}
|
|
16963
|
+
return [...records.values()];
|
|
16964
|
+
}
|
|
17048
16965
|
async function createScore(client, schema, args) {
|
|
17049
|
-
const
|
|
16966
|
+
const row = scoreRecordToRow(args.score);
|
|
16967
|
+
const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [row], scoreRewriteConflict(row));
|
|
17050
16968
|
if (insert) await client.query(insert.text, insert.values);
|
|
17051
16969
|
}
|
|
17052
16970
|
async function batchCreateScores(client, schema, args) {
|
|
17053
16971
|
if (args.scores.length === 0) return;
|
|
17054
|
-
const
|
|
16972
|
+
const rows = collapseExactScoreConflicts(args.scores.map(scoreRecordToRow));
|
|
16973
|
+
const insert = buildInsert(schema, TABLE_SCORE_EVENTS, rows, scoreRewriteConflict(rows[0]));
|
|
17055
16974
|
if (insert) await client.query(insert.text, insert.values);
|
|
17056
16975
|
}
|
|
17057
16976
|
/**
|
|
@@ -17074,6 +16993,16 @@ async function deleteScores(client, schema, args) {
|
|
|
17074
16993
|
}
|
|
17075
16994
|
await client.query(`DELETE FROM ${table} WHERE ${conditions.join(" AND ")}`, values);
|
|
17076
16995
|
}
|
|
16996
|
+
function latestScorePredicate(table, alias = "s") {
|
|
16997
|
+
return `NOT EXISTS (
|
|
16998
|
+
SELECT 1 FROM ${table} newer
|
|
16999
|
+
WHERE newer."scoreId" = ${alias}."scoreId"
|
|
17000
|
+
AND newer."cursorId" > ${alias}."cursorId"
|
|
17001
|
+
)`;
|
|
17002
|
+
}
|
|
17003
|
+
function applyLatestScorePredicate(acc, table) {
|
|
17004
|
+
acc.conditions.push(latestScorePredicate(table));
|
|
17005
|
+
}
|
|
17077
17006
|
async function listScores(client, schema, args) {
|
|
17078
17007
|
const { mode, filters, pagination, orderBy, after, limit } = listScoresArgsSchema.parse(args);
|
|
17079
17008
|
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
@@ -17084,28 +17013,50 @@ async function listScores(client, schema, args) {
|
|
|
17084
17013
|
return listScoresPage(client, table, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
|
|
17085
17014
|
}
|
|
17086
17015
|
async function getScoreById(client, schema, scoreId) {
|
|
17016
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17087
17017
|
const row = await client.oneOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
|
|
17088
|
-
FROM ${
|
|
17018
|
+
FROM ${table}
|
|
17089
17019
|
WHERE "scoreId" = $1
|
|
17090
|
-
ORDER BY "
|
|
17020
|
+
ORDER BY "cursorId" DESC
|
|
17091
17021
|
LIMIT 1`, [scoreId]);
|
|
17092
17022
|
return row ? rowToScoreRecord(row) : null;
|
|
17093
17023
|
}
|
|
17094
17024
|
async function listScoresPage(client, table, filters, page, perPage, orderField, orderDir) {
|
|
17095
|
-
|
|
17025
|
+
const acc = newFilterAccumulator();
|
|
17026
|
+
applyScoreFilters(acc, filters);
|
|
17027
|
+
applyLatestScorePredicate(acc, table);
|
|
17028
|
+
const whereClause = whereOrEmpty(acc);
|
|
17029
|
+
const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${table} s ${whereClause}`, acc.params);
|
|
17030
|
+
const total = Number(countRow?.count ?? 0);
|
|
17031
|
+
let scores = [];
|
|
17032
|
+
if (total > 0) {
|
|
17033
|
+
const safeOrderField = parseSqlIdentifier(orderField, "order field");
|
|
17034
|
+
scores = (await client.manyOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
|
|
17035
|
+
FROM ${table} s
|
|
17036
|
+
${whereClause}
|
|
17037
|
+
ORDER BY "${safeOrderField}" ${orderDir}, "cursorId" ${orderDir}
|
|
17038
|
+
LIMIT $${acc.next++} OFFSET $${acc.next++}`, [
|
|
17039
|
+
...acc.params,
|
|
17040
|
+
perPage,
|
|
17041
|
+
page * perPage
|
|
17042
|
+
])).map(rowToScoreRecord);
|
|
17043
|
+
}
|
|
17044
|
+
const deltaCursor = deltaPollingFeatureEnabled() ? await readSignalStreamHeadCursor({
|
|
17096
17045
|
client,
|
|
17097
17046
|
table,
|
|
17098
17047
|
filters,
|
|
17099
|
-
|
|
17100
|
-
|
|
17101
|
-
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
|
|
17105
|
-
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17048
|
+
applyFilters: applyScoreFilters
|
|
17049
|
+
}) : void 0;
|
|
17050
|
+
return {
|
|
17051
|
+
scores,
|
|
17052
|
+
pagination: {
|
|
17053
|
+
total,
|
|
17054
|
+
page,
|
|
17055
|
+
perPage,
|
|
17056
|
+
hasMore: (page + 1) * perPage < total
|
|
17057
|
+
},
|
|
17058
|
+
...deltaCursor !== void 0 ? { deltaCursor } : {}
|
|
17059
|
+
};
|
|
17109
17060
|
}
|
|
17110
17061
|
async function listScoresDelta(client, table, filters, after, limit) {
|
|
17111
17062
|
return listSignalDelta({
|
|
@@ -17121,12 +17072,14 @@ async function listScoresDelta(client, table, filters, after, limit) {
|
|
|
17121
17072
|
});
|
|
17122
17073
|
}
|
|
17123
17074
|
async function runScoreAggregateQuery(client, schema, args, filters) {
|
|
17075
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17124
17076
|
const acc = newFilterAccumulator();
|
|
17125
17077
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17126
17078
|
applyScoreFilters(acc, filters);
|
|
17079
|
+
applyLatestScorePredicate(acc, table);
|
|
17127
17080
|
const sql = `
|
|
17128
17081
|
SELECT ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17129
|
-
FROM ${
|
|
17082
|
+
FROM ${table} s
|
|
17130
17083
|
${whereOrEmpty(acc)}
|
|
17131
17084
|
`;
|
|
17132
17085
|
const row = await client.oneOrNone(sql, acc.params);
|
|
@@ -17158,10 +17111,12 @@ async function getScoreBreakdown(client, schema, args) {
|
|
|
17158
17111
|
});
|
|
17159
17112
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17160
17113
|
applyScoreFilters(acc, args.filters);
|
|
17114
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17115
|
+
applyLatestScorePredicate(acc, table);
|
|
17161
17116
|
const sql = `
|
|
17162
17117
|
SELECT ${resolved.map((e) => e.selectSql).join(", ")},
|
|
17163
17118
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17164
|
-
FROM ${
|
|
17119
|
+
FROM ${table} s
|
|
17165
17120
|
${whereOrEmpty(acc)}
|
|
17166
17121
|
GROUP BY ${resolved.map((e) => e.alias).join(", ")}
|
|
17167
17122
|
ORDER BY "value" DESC NULLS LAST
|
|
@@ -17181,11 +17136,13 @@ async function getScoreTimeSeries(client, schema, args) {
|
|
|
17181
17136
|
});
|
|
17182
17137
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17183
17138
|
applyScoreFilters(acc, args.filters);
|
|
17139
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17140
|
+
applyLatestScorePredicate(acc, table);
|
|
17184
17141
|
const sql = `
|
|
17185
17142
|
SELECT ${bucket} AS bucket,
|
|
17186
17143
|
${resolved.map((e) => e.selectSql).join(", ")},
|
|
17187
17144
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17188
|
-
FROM ${
|
|
17145
|
+
FROM ${table} s
|
|
17189
17146
|
${whereOrEmpty(acc)}
|
|
17190
17147
|
GROUP BY bucket, ${resolved.map((e) => e.alias).join(", ")}
|
|
17191
17148
|
ORDER BY bucket
|
|
@@ -17203,10 +17160,12 @@ async function getScoreTimeSeries(client, schema, args) {
|
|
|
17203
17160
|
const acc = newFilterAccumulator();
|
|
17204
17161
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17205
17162
|
applyScoreFilters(acc, args.filters);
|
|
17163
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17164
|
+
applyLatestScorePredicate(acc, table);
|
|
17206
17165
|
const sql = `
|
|
17207
17166
|
SELECT ${bucket} AS bucket,
|
|
17208
17167
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17209
|
-
FROM ${
|
|
17168
|
+
FROM ${table} s
|
|
17210
17169
|
${whereOrEmpty(acc)}
|
|
17211
17170
|
GROUP BY bucket
|
|
17212
17171
|
ORDER BY bucket
|
|
@@ -17226,9 +17185,11 @@ async function getScorePercentiles(client, schema, args) {
|
|
|
17226
17185
|
const acc = newFilterAccumulator();
|
|
17227
17186
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17228
17187
|
applyScoreFilters(acc, args.filters);
|
|
17188
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17189
|
+
applyLatestScorePredicate(acc, table);
|
|
17229
17190
|
const sql = `
|
|
17230
17191
|
SELECT ${bucket} AS bucket, ${percentileSelectSql(args.percentiles, "\"score\"")}
|
|
17231
|
-
FROM ${
|
|
17192
|
+
FROM ${table} s
|
|
17232
17193
|
${whereOrEmpty(acc)}
|
|
17233
17194
|
GROUP BY bucket
|
|
17234
17195
|
ORDER BY bucket
|
|
@@ -17291,6 +17252,11 @@ const FEEDBACK_FIELDS = {
|
|
|
17291
17252
|
const TRACE_SELECT = `
|
|
17292
17253
|
r."traceId" AS "traceId",
|
|
17293
17254
|
r."spanId" AS "rootSpanId",
|
|
17255
|
+
r."name" AS "name",
|
|
17256
|
+
r."entityId" AS "entityId",
|
|
17257
|
+
r."parentSpanId" AS "parentSpanId",
|
|
17258
|
+
r."metadata" AS "metadata",
|
|
17259
|
+
r."input" AS "input",
|
|
17294
17260
|
r."threadId" AS "threadId",
|
|
17295
17261
|
r."resourceId" AS "resourceId",
|
|
17296
17262
|
r."startedAt" AS "startedAt",
|
|
@@ -17422,13 +17388,6 @@ function latestSpanPredicate$1(spanTable) {
|
|
|
17422
17388
|
AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
|
|
17423
17389
|
)`;
|
|
17424
17390
|
}
|
|
17425
|
-
function latestScorePredicate(scoreTable) {
|
|
17426
|
-
return `NOT EXISTS (
|
|
17427
|
-
SELECT 1 FROM ${scoreTable} newer
|
|
17428
|
-
WHERE newer."scoreId" = s."scoreId"
|
|
17429
|
-
AND newer."cursorId" > s."cursorId"
|
|
17430
|
-
)`;
|
|
17431
|
-
}
|
|
17432
17391
|
function latestFeedbackPredicate(feedbackTable) {
|
|
17433
17392
|
return `NOT EXISTS (
|
|
17434
17393
|
SELECT 1 FROM ${feedbackTable} newer
|
|
@@ -17598,7 +17557,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections) {
|
|
|
17598
17557
|
values
|
|
17599
17558
|
};
|
|
17600
17559
|
}
|
|
17601
|
-
function compilePostgresTraceQuery(schema, plan) {
|
|
17560
|
+
function compilePostgresTraceQuery(schema, plan, mode = "data") {
|
|
17602
17561
|
const { ctes, values } = compilePostgresTraceScope(schema, plan, collectRelationCollections(plan.where));
|
|
17603
17562
|
let predicateSql = "TRUE";
|
|
17604
17563
|
if (plan.where) {
|
|
@@ -17612,6 +17571,12 @@ function compilePostgresTraceQuery(schema, plan) {
|
|
|
17612
17571
|
WHERE ${predicateSql}
|
|
17613
17572
|
)`);
|
|
17614
17573
|
const candidates = `WITH ${ctes.join(",\n")}`;
|
|
17574
|
+
if (plan.paginationMode === "page" && mode === "count") return {
|
|
17575
|
+
text: `${candidates}
|
|
17576
|
+
SELECT COUNT(*)::text AS count
|
|
17577
|
+
FROM candidates`,
|
|
17578
|
+
values
|
|
17579
|
+
};
|
|
17615
17580
|
if (plan.result === "groups") {
|
|
17616
17581
|
const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
|
|
17617
17582
|
if (plan.cursor) values.push(plan.cursor.threadId);
|
|
@@ -17629,6 +17594,17 @@ LIMIT $${values.length}`,
|
|
|
17629
17594
|
}
|
|
17630
17595
|
const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
|
|
17631
17596
|
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
17597
|
+
if (plan.paginationMode === "page") {
|
|
17598
|
+
values.push(plan.perPage, plan.page * plan.perPage);
|
|
17599
|
+
return {
|
|
17600
|
+
text: `${candidates}
|
|
17601
|
+
SELECT *
|
|
17602
|
+
FROM candidates
|
|
17603
|
+
ORDER BY ${orderField} ${direction}, "traceId" ASC
|
|
17604
|
+
LIMIT $${values.length - 1} OFFSET $${values.length}`,
|
|
17605
|
+
values
|
|
17606
|
+
};
|
|
17607
|
+
}
|
|
17632
17608
|
let pageCondition = "";
|
|
17633
17609
|
if (plan.cursor) {
|
|
17634
17610
|
const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
|
|
@@ -17770,15 +17746,22 @@ function isPostgresStatementTimeout(error) {
|
|
|
17770
17746
|
const candidate = error;
|
|
17771
17747
|
return candidate.code === "57014" && String(candidate.message ?? "").includes("statement timeout");
|
|
17772
17748
|
}
|
|
17773
|
-
|
|
17749
|
+
function isPostgresResourceLimit(error) {
|
|
17750
|
+
if (!error || typeof error !== "object") return false;
|
|
17751
|
+
const candidate = error;
|
|
17752
|
+
return candidate.code === "53200" || candidate.code === "53400";
|
|
17753
|
+
}
|
|
17754
|
+
async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute, options = {}) {
|
|
17774
17755
|
const resolvedTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
17775
17756
|
try {
|
|
17776
17757
|
return await client.tx(async (transaction) => {
|
|
17758
|
+
if (options.repeatableRead) await transaction.query("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
|
|
17777
17759
|
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
|
|
17778
17760
|
return execute(transaction);
|
|
17779
17761
|
});
|
|
17780
17762
|
} catch (error) {
|
|
17781
17763
|
if (isPostgresStatementTimeout(error)) throw new coreStorage.TraceQueryExecutionError();
|
|
17764
|
+
if (isPostgresResourceLimit(error)) throw new coreStorage.TraceQueryResourceLimitError();
|
|
17782
17765
|
throw error;
|
|
17783
17766
|
}
|
|
17784
17767
|
}
|
|
@@ -17806,6 +17789,50 @@ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
|
|
|
17806
17789
|
});
|
|
17807
17790
|
}
|
|
17808
17791
|
async function queryTraces(client, schema, plan, timeoutMs) {
|
|
17792
|
+
if (plan.paginationMode === "page") {
|
|
17793
|
+
const resolvedTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
17794
|
+
const deadline = performance.now() + resolvedTimeoutMs;
|
|
17795
|
+
const countQuery = compilePostgresTraceQuery(schema, plan, "count");
|
|
17796
|
+
const dataQuery = compilePostgresTraceQuery(schema, plan);
|
|
17797
|
+
const { total, rows } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
|
|
17798
|
+
const countRows = await transaction.any(countQuery.text, countQuery.values);
|
|
17799
|
+
const remainingTimeoutMs = Math.floor(deadline - performance.now());
|
|
17800
|
+
if (remainingTimeoutMs <= 0) throw new coreStorage.TraceQueryExecutionError();
|
|
17801
|
+
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
|
|
17802
|
+
const rows = await transaction.any(dataQuery.text, dataQuery.values);
|
|
17803
|
+
return {
|
|
17804
|
+
total: Number(countRows[0]?.count ?? 0),
|
|
17805
|
+
rows
|
|
17806
|
+
};
|
|
17807
|
+
}, { repeatableRead: true });
|
|
17808
|
+
const traces = rows.map((row) => ({
|
|
17809
|
+
traceId: String(row.traceId),
|
|
17810
|
+
rootSpanId: String(row.rootSpanId),
|
|
17811
|
+
name: row.name,
|
|
17812
|
+
entityId: row.entityId ?? null,
|
|
17813
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
17814
|
+
createdAt: asIsoTimestamp$1(row.startedAt),
|
|
17815
|
+
metadata: row.metadata ?? null,
|
|
17816
|
+
inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
|
|
17817
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17818
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17819
|
+
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
17820
|
+
endedAt: asIsoTimestamp$1(row.endedAt),
|
|
17821
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
17822
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
17823
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
17824
|
+
status: row.status
|
|
17825
|
+
}));
|
|
17826
|
+
return coreStorage.traceQueryResponseSchema.parse({
|
|
17827
|
+
traces,
|
|
17828
|
+
pagination: {
|
|
17829
|
+
total,
|
|
17830
|
+
page: plan.page,
|
|
17831
|
+
perPage: plan.perPage,
|
|
17832
|
+
hasMore: (plan.page + 1) * plan.perPage < total
|
|
17833
|
+
}
|
|
17834
|
+
});
|
|
17835
|
+
}
|
|
17809
17836
|
const query = compilePostgresTraceQuery(schema, plan);
|
|
17810
17837
|
const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
|
|
17811
17838
|
const visibleRows = rows.slice(0, plan.limit);
|
|
@@ -17823,6 +17850,12 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
17823
17850
|
const traces = visibleRows.map((row) => ({
|
|
17824
17851
|
traceId: String(row.traceId),
|
|
17825
17852
|
rootSpanId: String(row.rootSpanId),
|
|
17853
|
+
name: row.name,
|
|
17854
|
+
entityId: row.entityId ?? null,
|
|
17855
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
17856
|
+
createdAt: asIsoTimestamp$1(row.startedAt),
|
|
17857
|
+
metadata: row.metadata ?? null,
|
|
17858
|
+
inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
|
|
17826
17859
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17827
17860
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17828
17861
|
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
@@ -18570,7 +18603,7 @@ async function dangerouslyClearTracing(client, schema) {
|
|
|
18570
18603
|
* Use it through `MastraCompositeStore` with a dedicated Postgres connection.
|
|
18571
18604
|
*/
|
|
18572
18605
|
function wrapError(op, error, details) {
|
|
18573
|
-
if (error instanceof MastraError || error instanceof coreStorage.TraceQueryExecutionError) throw error;
|
|
18606
|
+
if (error instanceof MastraError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
|
|
18574
18607
|
throw new MastraError({
|
|
18575
18608
|
id: createStorageErrorId("PG", op, "FAILED"),
|
|
18576
18609
|
domain: ErrorDomain.STORAGE,
|