@mastra/pg 1.32.0-alpha.3 → 1.32.0-alpha.5

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.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.32.0-alpha.3"
6
+ version: "1.32.0-alpha.5"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.32.0-alpha.3",
2
+ "version": "1.32.0-alpha.5",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -405,6 +405,26 @@ Multilingual E5 produces 1024-dimensional vectors. Your vector index must be cre
405
405
 
406
406
  FastEmbed models embed at most 512 tokens per input (roughly 2,000 to 2,500 characters of English text). Longer inputs are truncated, so text past the limit doesn't affect the embedding and can't be found by semantic recall. The AI SDK v3 FastEmbed models return a warning when this happens. The legacy v1 and v2 models don't. If you embed long documents with FastEmbed, chunk them to fit the window, for example with `maxSize: 1500` in `MDocument.chunk()`.
407
407
 
408
+ ### Setting the embedding dimension
409
+
410
+ To name the vector index, Memory needs the embedder's output dimension. By default it finds the dimension by embedding a short probe string the first time semantic recall is set up, once per `Memory` instance. If you already know the dimension, set `embeddingDimension`. Memory then skips the probe, so startup makes no embedding call and doesn't depend on the embedding provider being reachable:
411
+
412
+ ```ts
413
+ import { Memory } from '@mastra/memory'
414
+
415
+ const memory = new Memory({
416
+ embedder: 'openai/text-embedding-3-small',
417
+ embeddingDimension: 1536,
418
+ options: {
419
+ semanticRecall: true,
420
+ },
421
+ })
422
+ ```
423
+
424
+ The index name is the same whether the dimension is configured or probed, so existing indexes keep working.
425
+
426
+ The value must match the vectors the embedder returns. Memory compares it with the first real embedding, and on a mismatch it throws a `MASTRA_MEMORY_EMBEDDING_DIMENSION_MISMATCH` error naming both values before creating or querying any index. Update `embeddingDimension` whenever you change the embedding model or its output dimension.
427
+
408
428
  ## PostgreSQL index optimization
409
429
 
410
430
  When using PostgreSQL as your vector store, you can optimize semantic recall performance by configuring the vector index. This is particularly important for large-scale deployments with thousands of messages.
@@ -37,6 +37,8 @@ export const agent = new Agent({
37
37
 
38
38
  **embedder** (`EmbeddingModel<string> | EmbeddingModelV2<string>`): Embedder instance for vector embeddings. Required when semantic recall is enabled.
39
39
 
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
+
40
42
  **options** (`MemoryConfig`): Memory configuration options.
41
43
 
42
44
  **options.lastMessages** (`number | false`): Number of most recent messages to include in context. Set to false to disable the message history feature entirely (messages are not loaded into context or saved). Use Number.MAX\_SAFE\_INTEGER to retrieve all messages with no limit. To load messages without saving new ones, use the readOnly option. The window slides forward on every request, so once a thread exceeds the limit, each turn invalidates the provider prompt cache. For long-running conversations, use Observational Memory instead.
package/dist/index.cjs CHANGED
@@ -19211,7 +19211,26 @@ async function getScorePercentiles(client, schema, args) {
19211
19211
  const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
19212
19212
  /** Core versions without the field reject it in strict query results, so it is only returned when core knows it. */
19213
19213
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in _mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY.trace;
19214
- const timeToFirstChunkFeatures = timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [];
19214
+ /** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
19215
+ const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
19216
+ const timeToFirstChunkFeatures = [...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [], ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : []];
19217
+ /**
19218
+ * Keyset bounds for a cursor's sort value: terms for rows strictly past it in `direction`, and the
19219
+ * condition for rows level with it. Empty values sort last in both directions, so they are past
19220
+ * every value and level only with each other.
19221
+ */
19222
+ function keysetSortBounds(field, direction, sortValue, values) {
19223
+ if (sortValue === null) return {
19224
+ past: [],
19225
+ level: `${field} IS NULL`
19226
+ };
19227
+ values.push(sortValue);
19228
+ const parameter = `$${values.length}::${typeof sortValue === "number" ? "float8" : "timestamptz"}`;
19229
+ return {
19230
+ past: [`${field} ${direction === "asc" ? ">" : "<"} ${parameter}`, `${field} IS NULL`],
19231
+ level: `${field} = ${parameter}`
19232
+ };
19233
+ }
19215
19234
  function durationMsSql(startedAt, endedAt) {
19216
19235
  return `EXTRACT(EPOCH FROM (${endedAt} - ${startedAt}))::numeric * 1000`;
19217
19236
  }
@@ -19227,6 +19246,7 @@ const TRACE_FIELDS = {
19227
19246
  endedAt: "r.\"endedAt\"",
19228
19247
  durationMs: durationMsSql("r.\"startedAt\"", "r.\"endedAt\""),
19229
19248
  timeToFirstChunkMs: "r.\"timeToFirstChunkMs\"",
19249
+ entityId: "r.\"entityId\"",
19230
19250
  entityName: "r.\"entityName\"",
19231
19251
  entityType: "r.\"entityType\"",
19232
19252
  name: "r.\"name\"",
@@ -19528,6 +19548,7 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
19528
19548
  "organizationId",
19529
19549
  "startedAt",
19530
19550
  "endedAt",
19551
+ "entityId",
19531
19552
  "entityName",
19532
19553
  "entityType",
19533
19554
  "name",
@@ -19803,8 +19824,8 @@ LIMIT $${values.length}`,
19803
19824
  values
19804
19825
  };
19805
19826
  }
19806
- const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
19807
- const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
19827
+ const orderField = `"${plan.orderBy.field}"`;
19828
+ const direction = `${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST`;
19808
19829
  if (plan.paginationMode === "page") {
19809
19830
  values.push(plan.perPage, plan.page * plan.perPage);
19810
19831
  return {
@@ -19818,10 +19839,9 @@ LIMIT $${values.length - 1} OFFSET $${values.length}`,
19818
19839
  }
19819
19840
  let pageCondition = "";
19820
19841
  if (plan.cursor) {
19821
- const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
19822
- const sortParameter = `$${values.length + 1}`;
19823
- pageCondition = `WHERE (${orderField} ${comparison} ${sortParameter} OR (${orderField} = ${sortParameter} AND "traceId" > ${`$${values.length + 2}`}))`;
19824
- values.push(plan.cursor.sortValue, plan.cursor.traceId);
19842
+ const { past, level } = keysetSortBounds(orderField, plan.orderBy.direction, plan.cursor.sortValue, values);
19843
+ values.push(plan.cursor.traceId);
19844
+ pageCondition = `WHERE (${[...past, `(${level} AND "traceId" > $${values.length})`].join(" OR ")})`;
19825
19845
  }
19826
19846
  values.push(plan.limit + 1);
19827
19847
  return {
@@ -20154,7 +20174,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
20154
20174
  traces,
20155
20175
  page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
20156
20176
  result: "traces",
20157
- sortValue: last[plan.orderBy.field],
20177
+ sortValue: last[plan.orderBy.field] ?? null,
20158
20178
  traceId: last.traceId
20159
20179
  }) : null }
20160
20180
  });
@@ -20226,11 +20246,8 @@ function selectedKeys(rows, values) {
20226
20246
  function afterCursor(plan, values) {
20227
20247
  if (!plan.cursor) return "TRUE";
20228
20248
  const cursor = plan.cursor;
20229
- values.push(cursor.sortValue);
20230
- const timestamp = `$${values.length}::timestamptz`;
20231
- const field = `s."${plan.orderBy.field}"`;
20232
- const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${timestamp}`];
20233
- const equal = [`${field} = ${timestamp}`];
20249
+ const { past: terms, level } = keysetSortBounds(`s."${plan.orderBy.field}"`, plan.orderBy.direction, cursor.sortValue, values);
20250
+ const equal = [level];
20234
20251
  for (const key of identityFields) {
20235
20252
  const ref = `s."${key}" COLLATE "C"`;
20236
20253
  const value = cursor[key];
@@ -20296,7 +20313,7 @@ function compilePostgresSpanQuery(schema, plan) {
20296
20313
  "version"
20297
20314
  ].map((field) => `s."${field}"`).join(", ")}
20298
20315
  FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
20299
- ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityOrder}
20316
+ ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
20300
20317
  LIMIT $${values.length}`,
20301
20318
  values
20302
20319
  };
@@ -20617,6 +20634,7 @@ const DIMENSION_SQL = {
20617
20634
  entityName: "r.\"entityName\"",
20618
20635
  name: "r.\"name\"",
20619
20636
  spanType: "r.\"spanType\"",
20637
+ entityId: "r.\"entityId\"",
20620
20638
  environment: "r.\"environment\"",
20621
20639
  status: TRACE_STATUS_SQL,
20622
20640
  serviceName: "r.\"serviceName\"",
@@ -21851,7 +21869,8 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21851
21869
  "feedback",
21852
21870
  "trace-query-context-ids",
21853
21871
  ...timeToFirstChunkFeatures,
21854
- "trace-query-root-span-fields"
21872
+ "trace-query-root-span-fields",
21873
+ "trace-query-root-entity-id"
21855
21874
  ];
21856
21875
  return [
21857
21876
  "metrics",
@@ -21874,7 +21893,8 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21874
21893
  "feedback",
21875
21894
  "trace-query-context-ids",
21876
21895
  ...timeToFirstChunkFeatures,
21877
- "trace-query-root-span-fields"
21896
+ "trace-query-root-span-fields",
21897
+ "trace-query-root-entity-id"
21878
21898
  ];
21879
21899
  }
21880
21900
  async #run(op, fn, details) {