@mastra/pg 1.25.0 → 1.26.0-alpha.1

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.25.0"
6
+ version: "1.26.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.25.0",
2
+ "version": "1.26.0-alpha.1",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -41,6 +41,8 @@ export const agent = new Agent({
41
41
 
42
42
  **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.
43
43
 
44
+ **options.messageHistory** (`{ maxTokens: number; atMaxRemoveTokens?: number }`): Token budget for the complete prompt, including remembered history, system instructions, context, and the current turn. When the prompt exceeds maxTokens, the oldest remembered messages are dropped until the prompt is at most maxTokens - atMaxRemoveTokens (defaults to 25% of maxTokens). Protected content is never removed. Linked tool calls and results are removed together. During agent runs, trimmed history is excluded from later turns via a persisted per-thread boundary, but the messages themselves stay in storage. When set without an explicit lastMessages, the default 10-message cap is not applied. Set maxTokens to 0 to disable message history. Prefer this over lastMessages, since message count is a poor proxy for context size.
45
+
44
46
  **options.readOnly** (`boolean`): When true, prevents memory from saving new messages and provides working memory as read-only context (without the updateWorkingMemory tool). Useful for read-only operations like previews, internal routing agents, or sub agents that should reference but not modify memory.
45
47
 
46
48
  **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 both vector store and embedder to be configured. Default topK is 4, default messageRange is {before: 1, after: 1}.
@@ -248,6 +248,34 @@ await store.upsert({
248
248
  })
249
249
  ```
250
250
 
251
+ **Weaviate**:
252
+
253
+ ```ts
254
+ import { WeaviateVector } from '@mastra/weaviate'
255
+
256
+ const store = new WeaviateVector({
257
+ id: 'weaviate-vector',
258
+ httpHost: process.env.WEAVIATE_HOST,
259
+ httpPort: 443,
260
+ httpSecure: true,
261
+ grpcHost: process.env.WEAVIATE_GRPC_HOST,
262
+ grpcPort: 443,
263
+ grpcSecure: true,
264
+ apiKey: process.env.WEAVIATE_API_KEY,
265
+ })
266
+
267
+ await store.createIndex({
268
+ indexName: 'myCollection',
269
+ dimension: 1536,
270
+ })
271
+
272
+ await store.upsert({
273
+ indexName: 'myCollection',
274
+ vectors: embeddings,
275
+ metadata: chunks.map(chunk => ({ text: chunk.text })),
276
+ })
277
+ ```
278
+
251
279
  **Cloudflare**:
252
280
 
253
281
  ```ts
@@ -534,6 +562,17 @@ Namespace names must:
534
562
 
535
563
  - Example: `_namespace` isn't valid (starts with underscore)
536
564
 
565
+ **Weaviate**:
566
+
567
+ Index names map to Weaviate collections, which:
568
+
569
+ - Are capitalized by Weaviate (the first letter is upper-cased)
570
+ - Should contain only letters, numbers, and the `_` character
571
+ - Must start with a letter, since only the first character is upper-cased (a leading digit or symbol stays invalid)
572
+ - Preserve the original Mastra index name in the collection description, so `listIndexes()` and `describeIndex()` return the name you supplied
573
+ - Example: `my_collection` is stored as `My_collection` and returned as `my_collection`
574
+ - Example: `123_collection` isn't valid (starts with a digit)
575
+
537
576
  **Cloudflare**:
538
577
 
539
578
  Index names must:
package/dist/index.cjs CHANGED
@@ -2484,6 +2484,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2484
2484
  }
2485
2485
  //#endregion
2486
2486
  //#region src/storage/db/index.ts
2487
+ const POSTGRES_MAX_BIND_PARAMETERS$1 = 65535;
2487
2488
  /**
2488
2489
  * Resolves PgDomainConfig to a database client and schema.
2489
2490
  * Handles creating a new pool if config is provided.
@@ -3037,6 +3038,90 @@ var PgDB = class extends _mastra_core_base.MastraBase {
3037
3038
  ON CONFLICT ("traceId", "spanId") DO NOTHING`, values);
3038
3039
  } else await client.none(`INSERT INTO ${fullTableName} (${columnList}) VALUES (${placeholders})`, values);
3039
3040
  }
3041
+ getChunkRowLimit(columnCount) {
3042
+ if (columnCount === 0) return 0;
3043
+ return Math.max(1, Math.floor(POSTGRES_MAX_BIND_PARAMETERS$1 / columnCount));
3044
+ }
3045
+ getSpanConflictIdentifier(record) {
3046
+ const traceId = record.traceId;
3047
+ const spanId = record.spanId;
3048
+ if (traceId === void 0 || spanId === void 0) return;
3049
+ return `${String(traceId)}|${String(spanId)}`;
3050
+ }
3051
+ async normalizeForInsert(tableName, record) {
3052
+ this.addTimestampZColumns(record);
3053
+ const filteredRecord = await this.filterRecordToKnownColumns(tableName, record);
3054
+ return {
3055
+ columns: Object.keys(filteredRecord).map((column) => (0, _mastra_core_utils.parseSqlIdentifier)(column, "column name")),
3056
+ values: this.prepareValuesForInsert(filteredRecord, tableName),
3057
+ conflictKey: tableName === _mastra_core_storage.TABLE_SPANS ? this.getSpanConflictIdentifier(filteredRecord) : void 0
3058
+ };
3059
+ }
3060
+ buildMultiRowInsertStatement({ tableName, columns, rows }) {
3061
+ const fullTableName = getTableName$6({
3062
+ indexName: tableName,
3063
+ schemaName: getSchemaName$6(this.schemaName)
3064
+ });
3065
+ const columnList = columns.map((column) => `"${column}"`).join(", ");
3066
+ const bindParams = [];
3067
+ const values = [];
3068
+ let bindIndex = 1;
3069
+ for (const rowValues of rows) {
3070
+ const placeholders = rowValues.map(() => `$${bindIndex++}`);
3071
+ bindParams.push(`(${placeholders.join(", ")})`);
3072
+ values.push(...rowValues);
3073
+ }
3074
+ let query = `INSERT INTO ${fullTableName} (${columnList}) VALUES ${bindParams.join(", ")}`;
3075
+ if (tableName === _mastra_core_storage.TABLE_SPANS) {
3076
+ const updateColumns = columns.filter((column) => column !== "traceId" && column !== "spanId");
3077
+ if (updateColumns.length > 0) {
3078
+ const updateClause = updateColumns.map((column) => `"${column}" = EXCLUDED."${column}"`).join(", ");
3079
+ query += ` ON CONFLICT ("traceId", "spanId") DO UPDATE SET ${updateClause}`;
3080
+ } else query += ` ON CONFLICT ("traceId", "spanId") DO NOTHING`;
3081
+ }
3082
+ return {
3083
+ query,
3084
+ values
3085
+ };
3086
+ }
3087
+ async executeBatchInsert(client, { tableName, records }) {
3088
+ const preparedRecords = [];
3089
+ for (const record of records) preparedRecords.push(await this.normalizeForInsert(tableName, record));
3090
+ let pendingColumns;
3091
+ let pendingConflictKeys = /* @__PURE__ */ new Set();
3092
+ let pendingRows = [];
3093
+ let pendingLimit = 0;
3094
+ const flush = async () => {
3095
+ if (!pendingColumns || pendingRows.length === 0) return;
3096
+ const statement = this.buildMultiRowInsertStatement({
3097
+ tableName,
3098
+ columns: pendingColumns,
3099
+ rows: pendingRows
3100
+ });
3101
+ await client.none(statement.query, statement.values);
3102
+ pendingColumns = void 0;
3103
+ pendingRows = [];
3104
+ pendingConflictKeys = /* @__PURE__ */ new Set();
3105
+ pendingLimit = 0;
3106
+ };
3107
+ for (const { columns, values, conflictKey } of preparedRecords) {
3108
+ if (columns.length === 0) continue;
3109
+ const columnsSignature = columns.join("\0");
3110
+ const currentPendingColumns = pendingColumns;
3111
+ const isSpans = tableName === _mastra_core_storage.TABLE_SPANS;
3112
+ const conflictDuplicate = isSpans && conflictKey !== void 0 && pendingConflictKeys.has(conflictKey);
3113
+ const exceedsLimit = pendingRows.length >= pendingLimit;
3114
+ if (currentPendingColumns === void 0 || columnsSignature !== currentPendingColumns.join("\0") || conflictDuplicate || exceedsLimit) {
3115
+ await flush();
3116
+ pendingColumns = columns;
3117
+ pendingLimit = this.getChunkRowLimit(columns.length);
3118
+ pendingRows = [values];
3119
+ pendingConflictKeys = /* @__PURE__ */ new Set();
3120
+ } else pendingRows.push(values);
3121
+ if (isSpans && conflictKey !== void 0) pendingConflictKeys.add(conflictKey);
3122
+ }
3123
+ await flush();
3124
+ }
3040
3125
  async insert({ tableName, record }) {
3041
3126
  try {
3042
3127
  await this.executeInsert(this.client, {
@@ -3485,9 +3570,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3485
3570
  async batchInsert({ tableName, records }) {
3486
3571
  try {
3487
3572
  await this.client.tx(async (tx) => {
3488
- for (const record of records) await this.executeInsert(tx, {
3573
+ await this.executeBatchInsert(tx, {
3489
3574
  tableName,
3490
- record
3575
+ records
3491
3576
  });
3492
3577
  });
3493
3578
  } catch (error) {
@@ -17631,6 +17716,75 @@ LIMIT $${values.length}`,
17631
17716
  values
17632
17717
  };
17633
17718
  }
17719
+ function discoveryRegistry(scope) {
17720
+ if (scope === "trace") return TRACE_FIELDS;
17721
+ if (scope === "spans") return SPAN_FIELDS;
17722
+ if (scope === "scores") return SCORE_FIELDS;
17723
+ return FEEDBACK_FIELDS;
17724
+ }
17725
+ function discoverySource(scope) {
17726
+ if (scope === "trace") return "root_scope r";
17727
+ if (scope === "spans") return "current_spans s";
17728
+ if (scope === "scores") return "current_scores s";
17729
+ return "current_feedback s";
17730
+ }
17731
+ function discoveryCollections(scope) {
17732
+ return scope === "trace" ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([scope]);
17733
+ }
17734
+ function compilePostgresTraceQueryObservedFields(schema, plan) {
17735
+ const { ctes, values } = compilePostgresTraceScope(schema, plan, /* @__PURE__ */ new Set());
17736
+ const searchParameter = values.length + 1;
17737
+ const search = plan.search ? `AND strpos(lower('metadata.' || entry.key), lower($${searchParameter})) > 0` : "";
17738
+ if (plan.search) values.push(plan.search);
17739
+ values.push(plan.limit + 1);
17740
+ return {
17741
+ text: `WITH ${ctes.join(",\n")}
17742
+ SELECT 'metadata.' || entry.key AS path, count(*)::bigint AS occurrences
17743
+ FROM root_scope r
17744
+ CROSS JOIN LATERAL jsonb_each(CASE WHEN jsonb_typeof(r."metadataRaw") = 'object' THEN r."metadataRaw" ELSE '{}'::jsonb END) entry
17745
+ WHERE jsonb_typeof(entry.value) = 'string'
17746
+ AND btrim(entry.value #>> '{}') <> ''
17747
+ AND entry.key <> ''
17748
+ AND strpos(entry.key, '.') = 0
17749
+ AND octet_length('metadata.' || entry.key) <= ${_mastra_core_storage.TRACE_QUERY_MAX_PATH_BYTES}
17750
+ AND octet_length(entry.value #>> '{}') <= ${_mastra_core_storage.TRACE_QUERY_MAX_STRING_BYTES}
17751
+ ${search}
17752
+ GROUP BY entry.key
17753
+ ORDER BY occurrences DESC, ('metadata.' || entry.key) COLLATE "C" ASC
17754
+ LIMIT $${values.length}`,
17755
+ values
17756
+ };
17757
+ }
17758
+ function compilePostgresTraceQueryValues(schema, plan) {
17759
+ const { ctes, values } = compilePostgresTraceScope(schema, plan, discoveryCollections(plan.predicateScope));
17760
+ let field;
17761
+ if (plan.predicateScope === "trace" && plan.path.startsWith("metadata.")) {
17762
+ const keyParameter = `$${values.length + 1}`;
17763
+ field = `COALESCE(
17764
+ CASE WHEN jsonb_typeof(r."metadataSearch" -> ${keyParameter}) = 'string' THEN r."metadataSearch" ->> ${keyParameter} END,
17765
+ CASE WHEN jsonb_typeof(r."metadataRaw" -> ${keyParameter}) = 'string' THEN NULLIF(btrim(r."metadataRaw" ->> ${keyParameter}), '') END
17766
+ )`;
17767
+ values.push(plan.path.slice(9));
17768
+ } else field = fieldSql(discoveryRegistry(plan.predicateScope), plan.path);
17769
+ const searchParameter = values.length + 1;
17770
+ const search = plan.search ? `AND strpos(lower(value), lower($${searchParameter})) > 0` : "";
17771
+ if (plan.search) values.push(plan.search);
17772
+ values.push(plan.limit + 1);
17773
+ return {
17774
+ text: `WITH ${ctes.join(",\n")}, extracted AS (
17775
+ SELECT ${field}::text AS value FROM ${discoverySource(plan.predicateScope)}
17776
+ )
17777
+ SELECT value, count(*)::bigint AS count
17778
+ FROM extracted
17779
+ WHERE value IS NOT NULL
17780
+ AND octet_length(value) <= ${_mastra_core_storage.TRACE_QUERY_MAX_STRING_BYTES}
17781
+ ${search}
17782
+ GROUP BY value
17783
+ ORDER BY count DESC, value COLLATE "C" ASC
17784
+ LIMIT $${values.length}`,
17785
+ values
17786
+ };
17787
+ }
17634
17788
  function asIsoTimestamp$1(value) {
17635
17789
  if (value === null || value === void 0) throw new Error("Trace query returned a null timestamp");
17636
17790
  return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
@@ -17652,6 +17806,29 @@ async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute) {
17652
17806
  throw error;
17653
17807
  }
17654
17808
  }
17809
+ async function getTraceQueryObservedFields(client, schema, plan, timeoutMs) {
17810
+ if (plan.predicateScope !== "trace") return {
17811
+ observedFields: [],
17812
+ observedFieldsTruncated: false
17813
+ };
17814
+ const query = compilePostgresTraceQueryObservedFields(schema, plan);
17815
+ const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
17816
+ return {
17817
+ observedFields: rows.slice(0, plan.limit).map((row) => _mastra_core_storage.createTraceQueryObservedFieldDescriptor(String(row.path), Number(row.occurrences))),
17818
+ observedFieldsTruncated: rows.length > plan.limit
17819
+ };
17820
+ }
17821
+ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
17822
+ const query = compilePostgresTraceQueryValues(schema, plan);
17823
+ const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
17824
+ return _mastra_core_storage.getTraceQueryValuesResponseSchema.parse({
17825
+ values: rows.slice(0, plan.limit).map((row) => ({
17826
+ value: String(row.value),
17827
+ count: Number(row.count)
17828
+ })),
17829
+ valuesTruncated: rows.length > plan.limit
17830
+ });
17831
+ }
17655
17832
  async function queryTraces(client, schema, plan, timeoutMs) {
17656
17833
  const query = compilePostgresTraceQuery(schema, plan);
17657
17834
  const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
@@ -18598,6 +18775,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
18598
18775
  "metrics",
18599
18776
  "logs",
18600
18777
  "trace-query",
18778
+ "trace-query-discovery",
18601
18779
  "thread-query"
18602
18780
  ];
18603
18781
  return [
@@ -18605,6 +18783,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
18605
18783
  "logs",
18606
18784
  "delta-polling",
18607
18785
  "trace-query",
18786
+ "trace-query-discovery",
18608
18787
  "thread-query"
18609
18788
  ];
18610
18789
  }
@@ -18651,6 +18830,12 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
18651
18830
  async queryTraces(plan) {
18652
18831
  return this.#run("QUERY_TRACES", () => queryTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
18653
18832
  }
18833
+ async getTraceQueryObservedFields(plan) {
18834
+ return this.#run("GET_TRACE_QUERY_OBSERVED_FIELDS", () => getTraceQueryObservedFields(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
18835
+ }
18836
+ async getTraceQueryValues(plan) {
18837
+ return this.#run("GET_TRACE_QUERY_VALUES", () => getTraceQueryValues(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
18838
+ }
18654
18839
  async queryThreads(plan) {
18655
18840
  return this.#run("QUERY_THREADS", () => queryThreads(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
18656
18841
  }
@@ -23430,7 +23615,12 @@ var PgFactoryStorageOps = class {
23430
23615
  const assignments = columns.map((column, i) => `"${column}" = $${i + 1}`).join(", ");
23431
23616
  const filter = this.#buildWhere(schema, where, columns.length + 1);
23432
23617
  const args = [...columns.map((column) => this.#serialize(this.#column(schema, column), set[column])), ...filter.args];
23433
- return (await queryable.query(`UPDATE "${schema.name}" SET ${assignments} WHERE ${filter.sql}`, args)).rowCount ?? 0;
23618
+ try {
23619
+ return (await queryable.query(`UPDATE "${schema.name}" SET ${assignments} WHERE ${filter.sql}`, args)).rowCount ?? 0;
23620
+ } catch (error) {
23621
+ if (isUniqueViolation(error)) throw new _mastra_core_storage.UniqueViolationError(collection, { cause: error });
23622
+ throw error;
23623
+ }
23434
23624
  }
23435
23625
  async deleteMany(collection, where) {
23436
23626
  const schema = this.#schema(collection);