@mastra/upstash 0.0.0-working-memory-per-user-20250620161509 → 0.0.0-zod-v4-compat-part-2-20250820135355
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +268 -4
- package/LICENSE.md +12 -4
- package/README.md +98 -0
- package/dist/index.cjs +1629 -626
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1645 -642
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/legacy-evals/index.d.ts +28 -0
- package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +86 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +40 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/scores/index.d.ts +65 -0
- package/dist/storage/domains/scores/index.d.ts.map +1 -0
- package/dist/storage/domains/traces/index.d.ts +28 -0
- package/dist/storage/domains/traces/index.d.ts.map +1 -0
- package/dist/storage/domains/utils.d.ts +12 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +36 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +208 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/vector/filter.d.ts +21 -0
- package/dist/vector/filter.d.ts.map +1 -0
- package/dist/vector/index.d.ts +79 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/prompt.d.ts +6 -0
- package/dist/vector/prompt.d.ts.map +1 -0
- package/dist/vector/types.d.ts +23 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/docker-compose.yaml +1 -1
- package/package.json +12 -12
- package/src/storage/domains/legacy-evals/index.ts +279 -0
- package/src/storage/domains/memory/index.ts +972 -0
- package/src/storage/domains/operations/index.ts +168 -0
- package/src/storage/domains/scores/index.ts +216 -0
- package/src/storage/domains/traces/index.ts +172 -0
- package/src/storage/domains/utils.ts +57 -0
- package/src/storage/domains/workflows/index.ts +243 -0
- package/src/storage/index.test.ts +13 -0
- package/src/storage/index.ts +149 -1078
- package/src/vector/filter.test.ts +7 -6
- package/src/vector/filter.ts +10 -4
- package/src/vector/hybrid.test.ts +1455 -0
- package/src/vector/index.test.ts +4 -4
- package/src/vector/index.ts +155 -69
- package/src/vector/types.ts +26 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +22 -0
- package/dist/_tsup-dts-rollup.d.cts +0 -318
- package/dist/_tsup-dts-rollup.d.ts +0 -318
- package/dist/index.d.cts +0 -4
- package/src/storage/upstash.test.ts +0 -1386
package/src/storage/index.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { MessageList } from '@mastra/core/agent';
|
|
2
1
|
import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
|
|
3
|
-
import type { MetricResult, TestInfo } from '@mastra/core/eval';
|
|
4
2
|
import type { StorageThreadType, MastraMessageV1 } from '@mastra/core/memory';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
TABLE_MESSAGES,
|
|
8
|
-
TABLE_THREADS,
|
|
9
|
-
TABLE_RESOURCES,
|
|
10
|
-
TABLE_WORKFLOW_SNAPSHOT,
|
|
11
|
-
TABLE_EVALS,
|
|
12
|
-
TABLE_TRACES,
|
|
13
|
-
} from '@mastra/core/storage';
|
|
3
|
+
import type { ScoreRowData } from '@mastra/core/scores';
|
|
4
|
+
import { MastraStorage } from '@mastra/core/storage';
|
|
14
5
|
import type {
|
|
15
6
|
TABLE_NAMES,
|
|
16
7
|
StorageColumn,
|
|
@@ -22,9 +13,18 @@ import type {
|
|
|
22
13
|
PaginationInfo,
|
|
23
14
|
PaginationArgs,
|
|
24
15
|
StorageGetTracesArg,
|
|
16
|
+
StoragePagination,
|
|
17
|
+
StorageDomains,
|
|
25
18
|
} from '@mastra/core/storage';
|
|
19
|
+
|
|
26
20
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
27
21
|
import { Redis } from '@upstash/redis';
|
|
22
|
+
import { StoreLegacyEvalsUpstash } from './domains/legacy-evals';
|
|
23
|
+
import { StoreMemoryUpstash } from './domains/memory';
|
|
24
|
+
import { StoreOperationsUpstash } from './domains/operations';
|
|
25
|
+
import { ScoresUpstash } from './domains/scores';
|
|
26
|
+
import { TracesUpstash } from './domains/traces';
|
|
27
|
+
import { WorkflowsUpstash } from './domains/workflows';
|
|
28
28
|
|
|
29
29
|
export interface UpstashConfig {
|
|
30
30
|
url: string;
|
|
@@ -33,6 +33,7 @@ export interface UpstashConfig {
|
|
|
33
33
|
|
|
34
34
|
export class UpstashStore extends MastraStorage {
|
|
35
35
|
private redis: Redis;
|
|
36
|
+
stores: StorageDomains;
|
|
36
37
|
|
|
37
38
|
constructor(config: UpstashConfig) {
|
|
38
39
|
super({ name: 'Upstash' });
|
|
@@ -40,258 +41,60 @@ export class UpstashStore extends MastraStorage {
|
|
|
40
41
|
url: config.url,
|
|
41
42
|
token: config.token,
|
|
42
43
|
});
|
|
43
|
-
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
const operations = new StoreOperationsUpstash({ client: this.redis });
|
|
46
|
+
const traces = new TracesUpstash({ client: this.redis, operations });
|
|
47
|
+
const scores = new ScoresUpstash({ client: this.redis, operations });
|
|
48
|
+
const workflows = new WorkflowsUpstash({ client: this.redis, operations });
|
|
49
|
+
const memory = new StoreMemoryUpstash({ client: this.redis, operations });
|
|
50
|
+
const legacyEvals = new StoreLegacyEvalsUpstash({ client: this.redis, operations });
|
|
51
|
+
|
|
52
|
+
this.stores = {
|
|
53
|
+
operations,
|
|
54
|
+
traces,
|
|
55
|
+
scores,
|
|
56
|
+
workflows,
|
|
57
|
+
memory,
|
|
58
|
+
legacyEvals,
|
|
52
59
|
};
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
// Parse JSON strings if needed
|
|
57
|
-
let result = record.result;
|
|
58
|
-
if (typeof result === 'string') {
|
|
59
|
-
try {
|
|
60
|
-
result = JSON.parse(result);
|
|
61
|
-
} catch {
|
|
62
|
-
console.warn('Failed to parse result JSON:');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let testInfo = record.test_info;
|
|
67
|
-
if (typeof testInfo === 'string') {
|
|
68
|
-
try {
|
|
69
|
-
testInfo = JSON.parse(testInfo);
|
|
70
|
-
} catch {
|
|
71
|
-
console.warn('Failed to parse test_info JSON:');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
62
|
+
public get supports() {
|
|
75
63
|
return {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
instructions: record.instructions,
|
|
82
|
-
testInfo: testInfo as TestInfo | undefined,
|
|
83
|
-
globalRunId: record.global_run_id,
|
|
84
|
-
runId: record.run_id,
|
|
85
|
-
createdAt:
|
|
86
|
-
typeof record.created_at === 'string'
|
|
87
|
-
? record.created_at
|
|
88
|
-
: record.created_at instanceof Date
|
|
89
|
-
? record.created_at.toISOString()
|
|
90
|
-
: new Date().toISOString(),
|
|
64
|
+
selectByIncludeResourceScope: true,
|
|
65
|
+
resourceWorkingMemory: true,
|
|
66
|
+
hasColumn: false,
|
|
67
|
+
createTable: false,
|
|
68
|
+
deleteMessages: true,
|
|
91
69
|
};
|
|
92
70
|
}
|
|
93
71
|
|
|
94
|
-
private parseJSON(value: any): any {
|
|
95
|
-
if (typeof value === 'string') {
|
|
96
|
-
try {
|
|
97
|
-
return JSON.parse(value);
|
|
98
|
-
} catch {
|
|
99
|
-
return value;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private getKey(tableName: TABLE_NAMES, keys: Record<string, any>): string {
|
|
106
|
-
const keyParts = Object.entries(keys)
|
|
107
|
-
.filter(([_, value]) => value !== undefined)
|
|
108
|
-
.map(([key, value]) => `${key}:${value}`);
|
|
109
|
-
return `${tableName}:${keyParts.join(':')}`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Scans for keys matching the given pattern using SCAN and returns them as an array.
|
|
114
|
-
* @param pattern Redis key pattern, e.g. "table:*"
|
|
115
|
-
* @param batchSize Number of keys to scan per batch (default: 1000)
|
|
116
|
-
*/
|
|
117
|
-
private async scanKeys(pattern: string, batchSize = 10000): Promise<string[]> {
|
|
118
|
-
let cursor = '0';
|
|
119
|
-
let keys: string[] = [];
|
|
120
|
-
do {
|
|
121
|
-
// Upstash: scan(cursor, { match, count })
|
|
122
|
-
const [nextCursor, batch] = await this.redis.scan(cursor, {
|
|
123
|
-
match: pattern,
|
|
124
|
-
count: batchSize,
|
|
125
|
-
});
|
|
126
|
-
keys.push(...batch);
|
|
127
|
-
cursor = nextCursor;
|
|
128
|
-
} while (cursor !== '0');
|
|
129
|
-
return keys;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
72
|
/**
|
|
133
|
-
*
|
|
134
|
-
* @param pattern Redis key pattern, e.g. "table:*"
|
|
135
|
-
* @param batchSize Number of keys to delete per batch (default: 1000)
|
|
73
|
+
* @deprecated Use getEvals instead
|
|
136
74
|
*/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let totalDeleted = 0;
|
|
140
|
-
do {
|
|
141
|
-
const [nextCursor, keys] = await this.redis.scan(cursor, {
|
|
142
|
-
match: pattern,
|
|
143
|
-
count: batchSize,
|
|
144
|
-
});
|
|
145
|
-
if (keys.length > 0) {
|
|
146
|
-
await this.redis.del(...keys);
|
|
147
|
-
totalDeleted += keys.length;
|
|
148
|
-
}
|
|
149
|
-
cursor = nextCursor;
|
|
150
|
-
} while (cursor !== '0');
|
|
151
|
-
return totalDeleted;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private getMessageKey(threadId: string, messageId: string): string {
|
|
155
|
-
const key = this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
|
|
156
|
-
return key;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private getThreadMessagesKey(threadId: string): string {
|
|
160
|
-
return `thread:${threadId}:messages`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private parseWorkflowRun(row: any): WorkflowRun {
|
|
164
|
-
let parsedSnapshot: WorkflowRunState | string = row.snapshot as string;
|
|
165
|
-
if (typeof parsedSnapshot === 'string') {
|
|
166
|
-
try {
|
|
167
|
-
parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
|
|
168
|
-
} catch (e) {
|
|
169
|
-
// If parsing fails, return the raw snapshot string
|
|
170
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
workflowName: row.workflow_name,
|
|
176
|
-
runId: row.run_id,
|
|
177
|
-
snapshot: parsedSnapshot,
|
|
178
|
-
createdAt: this.ensureDate(row.createdAt)!,
|
|
179
|
-
updatedAt: this.ensureDate(row.updatedAt)!,
|
|
180
|
-
resourceId: row.resourceId,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private processRecord(tableName: TABLE_NAMES, record: Record<string, any>) {
|
|
185
|
-
let key: string;
|
|
186
|
-
|
|
187
|
-
if (tableName === TABLE_MESSAGES) {
|
|
188
|
-
// For messages, use threadId as the primary key component
|
|
189
|
-
key = this.getKey(tableName, { threadId: record.threadId, id: record.id });
|
|
190
|
-
} else if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
191
|
-
key = this.getKey(tableName, {
|
|
192
|
-
namespace: record.namespace || 'workflows',
|
|
193
|
-
workflow_name: record.workflow_name,
|
|
194
|
-
run_id: record.run_id,
|
|
195
|
-
...(record.resourceId ? { resourceId: record.resourceId } : {}),
|
|
196
|
-
});
|
|
197
|
-
} else if (tableName === TABLE_EVALS) {
|
|
198
|
-
key = this.getKey(tableName, { id: record.run_id });
|
|
199
|
-
} else {
|
|
200
|
-
key = this.getKey(tableName, { id: record.id });
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Convert dates to ISO strings before storing
|
|
204
|
-
const processedRecord = {
|
|
205
|
-
...record,
|
|
206
|
-
createdAt: this.serializeDate(record.createdAt),
|
|
207
|
-
updatedAt: this.serializeDate(record.updatedAt),
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
return { key, processedRecord };
|
|
75
|
+
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
76
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
211
77
|
}
|
|
212
78
|
|
|
213
79
|
/**
|
|
214
|
-
*
|
|
80
|
+
* Get all evaluations with pagination and total count
|
|
81
|
+
* @param options Pagination and filtering options
|
|
82
|
+
* @returns Object with evals array and total count
|
|
215
83
|
*/
|
|
216
|
-
async
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Use pipeline for batch fetching to improve performance
|
|
227
|
-
const pipeline = this.redis.pipeline();
|
|
228
|
-
keys.forEach(key => pipeline.get(key));
|
|
229
|
-
const results = await pipeline.exec();
|
|
230
|
-
|
|
231
|
-
// Filter by agent name and remove nulls
|
|
232
|
-
const nonNullRecords = results.filter(
|
|
233
|
-
(record): record is Record<string, any> =>
|
|
234
|
-
record !== null && typeof record === 'object' && 'agent_name' in record && record.agent_name === agentName,
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
let filteredEvals = nonNullRecords;
|
|
238
|
-
|
|
239
|
-
if (type === 'test') {
|
|
240
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
241
|
-
if (!record.test_info) return false;
|
|
242
|
-
|
|
243
|
-
// Handle test_info as a JSON string
|
|
244
|
-
try {
|
|
245
|
-
if (typeof record.test_info === 'string') {
|
|
246
|
-
const parsedTestInfo = JSON.parse(record.test_info);
|
|
247
|
-
return parsedTestInfo && typeof parsedTestInfo === 'object' && 'testPath' in parsedTestInfo;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Handle test_info as an object
|
|
251
|
-
return typeof record.test_info === 'object' && 'testPath' in record.test_info;
|
|
252
|
-
} catch {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
} else if (type === 'live') {
|
|
257
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
258
|
-
if (!record.test_info) return true;
|
|
259
|
-
|
|
260
|
-
// Handle test_info as a JSON string
|
|
261
|
-
try {
|
|
262
|
-
if (typeof record.test_info === 'string') {
|
|
263
|
-
const parsedTestInfo = JSON.parse(record.test_info);
|
|
264
|
-
return !(parsedTestInfo && typeof parsedTestInfo === 'object' && 'testPath' in parsedTestInfo);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Handle test_info as an object
|
|
268
|
-
return !(typeof record.test_info === 'object' && 'testPath' in record.test_info);
|
|
269
|
-
} catch {
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Transform to EvalRow format
|
|
276
|
-
return filteredEvals.map(record => this.transformEvalRecord(record));
|
|
277
|
-
} catch (error) {
|
|
278
|
-
console.error('Failed to get evals for the specified agent:', error);
|
|
279
|
-
return [];
|
|
280
|
-
}
|
|
84
|
+
async getEvals(
|
|
85
|
+
options: {
|
|
86
|
+
agentName?: string;
|
|
87
|
+
type?: 'test' | 'live';
|
|
88
|
+
} & PaginationArgs,
|
|
89
|
+
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
90
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
281
91
|
}
|
|
282
92
|
|
|
283
93
|
/**
|
|
284
94
|
* @deprecated use getTracesPaginated instead
|
|
285
95
|
*/
|
|
286
96
|
public async getTraces(args: StorageGetTracesArg): Promise<any[]> {
|
|
287
|
-
|
|
288
|
-
(args as any).dateRange = {
|
|
289
|
-
start: args.fromDate,
|
|
290
|
-
end: args.toDate,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
const { traces } = await this.getTracesPaginated(args);
|
|
294
|
-
return traces;
|
|
97
|
+
return this.stores.traces.getTraces(args);
|
|
295
98
|
}
|
|
296
99
|
|
|
297
100
|
public async getTracesPaginated(
|
|
@@ -302,106 +105,11 @@ export class UpstashStore extends MastraStorage {
|
|
|
302
105
|
filters?: Record<string, any>;
|
|
303
106
|
} & PaginationArgs,
|
|
304
107
|
): Promise<PaginationInfo & { traces: any[] }> {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const toDate = dateRange?.end;
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
const pattern = `${TABLE_TRACES}:*`;
|
|
311
|
-
const keys = await this.scanKeys(pattern);
|
|
312
|
-
|
|
313
|
-
if (keys.length === 0) {
|
|
314
|
-
return {
|
|
315
|
-
traces: [],
|
|
316
|
-
total: 0,
|
|
317
|
-
page,
|
|
318
|
-
perPage: perPage || 100,
|
|
319
|
-
hasMore: false,
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const pipeline = this.redis.pipeline();
|
|
324
|
-
keys.forEach(key => pipeline.get(key));
|
|
325
|
-
const results = await pipeline.exec();
|
|
326
|
-
|
|
327
|
-
let filteredTraces = results.filter(
|
|
328
|
-
(record): record is Record<string, any> => record !== null && typeof record === 'object',
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
if (name) {
|
|
332
|
-
filteredTraces = filteredTraces.filter(record => record.name?.toLowerCase().startsWith(name.toLowerCase()));
|
|
333
|
-
}
|
|
334
|
-
if (scope) {
|
|
335
|
-
filteredTraces = filteredTraces.filter(record => record.scope === scope);
|
|
336
|
-
}
|
|
337
|
-
if (attributes) {
|
|
338
|
-
filteredTraces = filteredTraces.filter(record => {
|
|
339
|
-
const recordAttributes = record.attributes;
|
|
340
|
-
if (!recordAttributes) return false;
|
|
341
|
-
const parsedAttributes =
|
|
342
|
-
typeof recordAttributes === 'string' ? JSON.parse(recordAttributes) : recordAttributes;
|
|
343
|
-
return Object.entries(attributes).every(([key, value]) => parsedAttributes[key] === value);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
if (filters) {
|
|
347
|
-
filteredTraces = filteredTraces.filter(record =>
|
|
348
|
-
Object.entries(filters).every(([key, value]) => record[key] === value),
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
if (fromDate) {
|
|
352
|
-
filteredTraces = filteredTraces.filter(
|
|
353
|
-
record => new Date(record.createdAt).getTime() >= new Date(fromDate).getTime(),
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
if (toDate) {
|
|
357
|
-
filteredTraces = filteredTraces.filter(
|
|
358
|
-
record => new Date(record.createdAt).getTime() <= new Date(toDate).getTime(),
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
filteredTraces.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
363
|
-
|
|
364
|
-
const transformedTraces = filteredTraces.map(record => ({
|
|
365
|
-
id: record.id,
|
|
366
|
-
parentSpanId: record.parentSpanId,
|
|
367
|
-
traceId: record.traceId,
|
|
368
|
-
name: record.name,
|
|
369
|
-
scope: record.scope,
|
|
370
|
-
kind: record.kind,
|
|
371
|
-
status: this.parseJSON(record.status),
|
|
372
|
-
events: this.parseJSON(record.events),
|
|
373
|
-
links: this.parseJSON(record.links),
|
|
374
|
-
attributes: this.parseJSON(record.attributes),
|
|
375
|
-
startTime: record.startTime,
|
|
376
|
-
endTime: record.endTime,
|
|
377
|
-
other: this.parseJSON(record.other),
|
|
378
|
-
createdAt: this.ensureDate(record.createdAt),
|
|
379
|
-
}));
|
|
380
|
-
|
|
381
|
-
const total = transformedTraces.length;
|
|
382
|
-
const resolvedPerPage = perPage || 100;
|
|
383
|
-
const start = page * resolvedPerPage;
|
|
384
|
-
const end = start + resolvedPerPage;
|
|
385
|
-
const paginatedTraces = transformedTraces.slice(start, end);
|
|
386
|
-
const hasMore = end < total;
|
|
108
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
109
|
+
}
|
|
387
110
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
total,
|
|
391
|
-
page,
|
|
392
|
-
perPage: resolvedPerPage,
|
|
393
|
-
hasMore,
|
|
394
|
-
};
|
|
395
|
-
} catch (error) {
|
|
396
|
-
console.error('Failed to get traces:', error);
|
|
397
|
-
return {
|
|
398
|
-
traces: [],
|
|
399
|
-
total: 0,
|
|
400
|
-
page,
|
|
401
|
-
perPage: perPage || 100,
|
|
402
|
-
hasMore: false,
|
|
403
|
-
};
|
|
404
|
-
}
|
|
111
|
+
async batchTraceInsert(args: { records: Record<string, any>[] }): Promise<void> {
|
|
112
|
+
return this.stores.traces.batchTraceInsert(args);
|
|
405
113
|
}
|
|
406
114
|
|
|
407
115
|
async createTable({
|
|
@@ -411,9 +119,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
411
119
|
tableName: TABLE_NAMES;
|
|
412
120
|
schema: Record<string, StorageColumn>;
|
|
413
121
|
}): Promise<void> {
|
|
414
|
-
|
|
415
|
-
// But we can store the schema for reference
|
|
416
|
-
await this.redis.set(`schema:${tableName}`, schema);
|
|
122
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
417
123
|
}
|
|
418
124
|
|
|
419
125
|
/**
|
|
@@ -422,141 +128,55 @@ export class UpstashStore extends MastraStorage {
|
|
|
422
128
|
* @param schema Schema of the table
|
|
423
129
|
* @param ifNotExists Array of column names to add if they don't exist
|
|
424
130
|
*/
|
|
425
|
-
async alterTable(
|
|
131
|
+
async alterTable(args: {
|
|
426
132
|
tableName: TABLE_NAMES;
|
|
427
133
|
schema: Record<string, StorageColumn>;
|
|
428
134
|
ifNotExists: string[];
|
|
429
135
|
}): Promise<void> {
|
|
430
|
-
|
|
136
|
+
return this.stores.operations.alterTable(args);
|
|
431
137
|
}
|
|
432
138
|
|
|
433
139
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
434
|
-
|
|
435
|
-
await this.scanAndDelete(pattern);
|
|
140
|
+
return this.stores.operations.clearTable({ tableName });
|
|
436
141
|
}
|
|
437
142
|
|
|
438
|
-
async
|
|
439
|
-
|
|
143
|
+
async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
144
|
+
return this.stores.operations.dropTable({ tableName });
|
|
145
|
+
}
|
|
440
146
|
|
|
441
|
-
|
|
147
|
+
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
148
|
+
return this.stores.operations.insert({ tableName, record });
|
|
442
149
|
}
|
|
443
150
|
|
|
444
151
|
async batchInsert(input: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
445
|
-
|
|
446
|
-
if (!records.length) return;
|
|
447
|
-
|
|
448
|
-
const batchSize = 1000;
|
|
449
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
450
|
-
const batch = records.slice(i, i + batchSize);
|
|
451
|
-
const pipeline = this.redis.pipeline();
|
|
452
|
-
for (const record of batch) {
|
|
453
|
-
const { key, processedRecord } = this.processRecord(tableName, record);
|
|
454
|
-
pipeline.set(key, processedRecord);
|
|
455
|
-
}
|
|
456
|
-
await pipeline.exec();
|
|
457
|
-
}
|
|
152
|
+
return this.stores.operations.batchInsert(input);
|
|
458
153
|
}
|
|
459
154
|
|
|
460
155
|
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
461
|
-
|
|
462
|
-
const data = await this.redis.get<R>(key);
|
|
463
|
-
return data || null;
|
|
156
|
+
return this.stores.operations.load<R>({ tableName, keys });
|
|
464
157
|
}
|
|
465
158
|
|
|
466
159
|
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
467
|
-
|
|
468
|
-
tableName: TABLE_THREADS,
|
|
469
|
-
keys: { id: threadId },
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
if (!thread) return null;
|
|
473
|
-
|
|
474
|
-
return {
|
|
475
|
-
...thread,
|
|
476
|
-
createdAt: this.ensureDate(thread.createdAt)!,
|
|
477
|
-
updatedAt: this.ensureDate(thread.updatedAt)!,
|
|
478
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
479
|
-
};
|
|
160
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
480
161
|
}
|
|
481
162
|
|
|
482
163
|
/**
|
|
483
164
|
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
484
165
|
*/
|
|
485
166
|
async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
|
|
486
|
-
|
|
487
|
-
const pattern = `${TABLE_THREADS}:*`;
|
|
488
|
-
const keys = await this.scanKeys(pattern);
|
|
489
|
-
|
|
490
|
-
if (keys.length === 0) {
|
|
491
|
-
return [];
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const allThreads: StorageThreadType[] = [];
|
|
495
|
-
const pipeline = this.redis.pipeline();
|
|
496
|
-
keys.forEach(key => pipeline.get(key));
|
|
497
|
-
const results = await pipeline.exec();
|
|
498
|
-
|
|
499
|
-
for (let i = 0; i < results.length; i++) {
|
|
500
|
-
const thread = results[i] as StorageThreadType | null;
|
|
501
|
-
if (thread && thread.resourceId === resourceId) {
|
|
502
|
-
allThreads.push({
|
|
503
|
-
...thread,
|
|
504
|
-
createdAt: this.ensureDate(thread.createdAt)!,
|
|
505
|
-
updatedAt: this.ensureDate(thread.updatedAt)!,
|
|
506
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
allThreads.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
512
|
-
return allThreads;
|
|
513
|
-
} catch (error) {
|
|
514
|
-
console.error('Error in getThreadsByResourceId:', error);
|
|
515
|
-
return [];
|
|
516
|
-
}
|
|
167
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
517
168
|
}
|
|
518
169
|
|
|
519
|
-
public async getThreadsByResourceIdPaginated(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
try {
|
|
527
|
-
const allThreads = await this.getThreadsByResourceId({ resourceId });
|
|
528
|
-
|
|
529
|
-
const total = allThreads.length;
|
|
530
|
-
const start = page * perPage;
|
|
531
|
-
const end = start + perPage;
|
|
532
|
-
const paginatedThreads = allThreads.slice(start, end);
|
|
533
|
-
const hasMore = end < total;
|
|
534
|
-
|
|
535
|
-
return {
|
|
536
|
-
threads: paginatedThreads,
|
|
537
|
-
total,
|
|
538
|
-
page,
|
|
539
|
-
perPage,
|
|
540
|
-
hasMore,
|
|
541
|
-
};
|
|
542
|
-
} catch (error) {
|
|
543
|
-
console.error('Error in getThreadsByResourceIdPaginated:', error);
|
|
544
|
-
return {
|
|
545
|
-
threads: [],
|
|
546
|
-
total: 0,
|
|
547
|
-
page,
|
|
548
|
-
perPage,
|
|
549
|
-
hasMore: false,
|
|
550
|
-
};
|
|
551
|
-
}
|
|
170
|
+
public async getThreadsByResourceIdPaginated(args: {
|
|
171
|
+
resourceId: string;
|
|
172
|
+
page: number;
|
|
173
|
+
perPage: number;
|
|
174
|
+
}): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
175
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
552
176
|
}
|
|
553
177
|
|
|
554
178
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
555
|
-
|
|
556
|
-
tableName: TABLE_THREADS,
|
|
557
|
-
record: thread,
|
|
558
|
-
});
|
|
559
|
-
return thread;
|
|
179
|
+
return this.stores.memory.saveThread({ thread });
|
|
560
180
|
}
|
|
561
181
|
|
|
562
182
|
async updateThread({
|
|
@@ -568,44 +188,11 @@ export class UpstashStore extends MastraStorage {
|
|
|
568
188
|
title: string;
|
|
569
189
|
metadata: Record<string, unknown>;
|
|
570
190
|
}): Promise<StorageThreadType> {
|
|
571
|
-
|
|
572
|
-
if (!thread) {
|
|
573
|
-
throw new Error(`Thread ${id} not found`);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const updatedThread = {
|
|
577
|
-
...thread,
|
|
578
|
-
title,
|
|
579
|
-
metadata: {
|
|
580
|
-
...thread.metadata,
|
|
581
|
-
...metadata,
|
|
582
|
-
},
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
await this.saveThread({ thread: updatedThread });
|
|
586
|
-
return updatedThread;
|
|
191
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
587
192
|
}
|
|
588
193
|
|
|
589
194
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
590
|
-
|
|
591
|
-
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
592
|
-
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
593
|
-
const messageIds: string[] = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
594
|
-
|
|
595
|
-
const pipeline = this.redis.pipeline();
|
|
596
|
-
pipeline.del(threadKey);
|
|
597
|
-
pipeline.del(threadMessagesKey);
|
|
598
|
-
|
|
599
|
-
for (let i = 0; i < messageIds.length; i++) {
|
|
600
|
-
const messageId = messageIds[i];
|
|
601
|
-
const messageKey = this.getMessageKey(threadId, messageId as string);
|
|
602
|
-
pipeline.del(messageKey);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
await pipeline.exec();
|
|
606
|
-
|
|
607
|
-
// Bulk delete all message keys for this thread if any remain
|
|
608
|
-
await this.scanAndDelete(this.getMessageKey(threadId, '*'));
|
|
195
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
609
196
|
}
|
|
610
197
|
|
|
611
198
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -613,118 +200,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
613
200
|
async saveMessages(
|
|
614
201
|
args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
|
|
615
202
|
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
616
|
-
|
|
617
|
-
if (messages.length === 0) return [];
|
|
618
|
-
|
|
619
|
-
const threadId = messages[0]?.threadId;
|
|
620
|
-
if (!threadId) {
|
|
621
|
-
throw new Error('Thread ID is required');
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Check if thread exists
|
|
625
|
-
const thread = await this.getThreadById({ threadId });
|
|
626
|
-
if (!thread) {
|
|
627
|
-
throw new Error(`Thread ${threadId} not found`);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// Add an index to each message to maintain order
|
|
631
|
-
const messagesWithIndex = messages.map((message, index) => ({
|
|
632
|
-
...message,
|
|
633
|
-
_index: index,
|
|
634
|
-
}));
|
|
635
|
-
|
|
636
|
-
// Get current thread data once (all messages belong to same thread)
|
|
637
|
-
const threadKey = this.getKey(TABLE_THREADS, { id: threadId });
|
|
638
|
-
const existingThread = await this.redis.get<StorageThreadType>(threadKey);
|
|
639
|
-
|
|
640
|
-
const batchSize = 1000;
|
|
641
|
-
for (let i = 0; i < messagesWithIndex.length; i += batchSize) {
|
|
642
|
-
const batch = messagesWithIndex.slice(i, i + batchSize);
|
|
643
|
-
const pipeline = this.redis.pipeline();
|
|
644
|
-
|
|
645
|
-
for (const message of batch) {
|
|
646
|
-
const key = this.getMessageKey(message.threadId!, message.id);
|
|
647
|
-
const createdAtScore = new Date(message.createdAt).getTime();
|
|
648
|
-
const score = message._index !== undefined ? message._index : createdAtScore;
|
|
649
|
-
|
|
650
|
-
// Store the message data
|
|
651
|
-
pipeline.set(key, message);
|
|
652
|
-
|
|
653
|
-
// Add to sorted set for this thread
|
|
654
|
-
pipeline.zadd(this.getThreadMessagesKey(message.threadId!), {
|
|
655
|
-
score,
|
|
656
|
-
member: message.id,
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// Update the thread's updatedAt field (only in the first batch)
|
|
661
|
-
if (i === 0 && existingThread) {
|
|
662
|
-
const updatedThread = {
|
|
663
|
-
...existingThread,
|
|
664
|
-
updatedAt: new Date(),
|
|
665
|
-
};
|
|
666
|
-
pipeline.set(threadKey, this.processRecord(TABLE_THREADS, updatedThread).processedRecord);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
await pipeline.exec();
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
const list = new MessageList().add(messages, 'memory');
|
|
673
|
-
if (format === `v2`) return list.get.all.v2();
|
|
674
|
-
return list.get.all.v1();
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
private async _getIncludedMessages(
|
|
678
|
-
threadId: string,
|
|
679
|
-
selectBy: StorageGetMessagesArg['selectBy'],
|
|
680
|
-
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
681
|
-
const messageIds = new Set<string>();
|
|
682
|
-
const messageIdToThreadIds: Record<string, string> = {};
|
|
683
|
-
|
|
684
|
-
// First, get specifically included messages and their context
|
|
685
|
-
if (selectBy?.include?.length) {
|
|
686
|
-
for (const item of selectBy.include) {
|
|
687
|
-
messageIds.add(item.id);
|
|
688
|
-
|
|
689
|
-
// Use per-include threadId if present, else fallback to main threadId
|
|
690
|
-
const itemThreadId = item.threadId || threadId;
|
|
691
|
-
messageIdToThreadIds[item.id] = itemThreadId;
|
|
692
|
-
const itemThreadMessagesKey = this.getThreadMessagesKey(itemThreadId);
|
|
693
|
-
|
|
694
|
-
// Get the rank of this message in the sorted set
|
|
695
|
-
const rank = await this.redis.zrank(itemThreadMessagesKey, item.id);
|
|
696
|
-
if (rank === null) continue;
|
|
697
|
-
|
|
698
|
-
// Get previous messages if requested
|
|
699
|
-
if (item.withPreviousMessages) {
|
|
700
|
-
const start = Math.max(0, rank - item.withPreviousMessages);
|
|
701
|
-
const prevIds = rank === 0 ? [] : await this.redis.zrange(itemThreadMessagesKey, start, rank - 1);
|
|
702
|
-
prevIds.forEach(id => {
|
|
703
|
-
messageIds.add(id as string);
|
|
704
|
-
messageIdToThreadIds[id as string] = itemThreadId;
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// Get next messages if requested
|
|
709
|
-
if (item.withNextMessages) {
|
|
710
|
-
const nextIds = await this.redis.zrange(itemThreadMessagesKey, rank + 1, rank + item.withNextMessages);
|
|
711
|
-
nextIds.forEach(id => {
|
|
712
|
-
messageIds.add(id as string);
|
|
713
|
-
messageIdToThreadIds[id as string] = itemThreadId;
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const pipeline = this.redis.pipeline();
|
|
719
|
-
Array.from(messageIds).forEach(id => {
|
|
720
|
-
const tId = messageIdToThreadIds[id] || threadId;
|
|
721
|
-
pipeline.get(this.getMessageKey(tId, id as string));
|
|
722
|
-
});
|
|
723
|
-
const results = await pipeline.exec();
|
|
724
|
-
return results.filter(result => result !== null) as MastraMessageV2[] | MastraMessageV1[];
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
return [];
|
|
203
|
+
return this.stores.memory.saveMessages(args);
|
|
728
204
|
}
|
|
729
205
|
|
|
730
206
|
/**
|
|
@@ -737,81 +213,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
737
213
|
selectBy,
|
|
738
214
|
format,
|
|
739
215
|
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
740
|
-
|
|
741
|
-
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
742
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
743
|
-
|
|
744
|
-
const messageIds = new Set<string>();
|
|
745
|
-
const messageIdToThreadIds: Record<string, string> = {};
|
|
746
|
-
|
|
747
|
-
if (limit === 0 && !selectBy?.include) {
|
|
748
|
-
return [];
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// Then get the most recent messages (or all if no limit)
|
|
752
|
-
if (limit === Number.MAX_SAFE_INTEGER) {
|
|
753
|
-
// Get all messages
|
|
754
|
-
const allIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
755
|
-
allIds.forEach(id => {
|
|
756
|
-
messageIds.add(id as string);
|
|
757
|
-
messageIdToThreadIds[id as string] = threadId;
|
|
758
|
-
});
|
|
759
|
-
} else if (limit > 0) {
|
|
760
|
-
// Get limited number of recent messages
|
|
761
|
-
const latestIds = await this.redis.zrange(threadMessagesKey, -limit, -1);
|
|
762
|
-
latestIds.forEach(id => {
|
|
763
|
-
messageIds.add(id as string);
|
|
764
|
-
messageIdToThreadIds[id as string] = threadId;
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
769
|
-
|
|
770
|
-
// Fetch all needed messages in parallel
|
|
771
|
-
const messages = [
|
|
772
|
-
...includedMessages,
|
|
773
|
-
...((
|
|
774
|
-
await Promise.all(
|
|
775
|
-
Array.from(messageIds).map(async id => {
|
|
776
|
-
const tId = messageIdToThreadIds[id] || threadId;
|
|
777
|
-
const byThreadId = await this.redis.get<MastraMessageV2 & { _index?: number }>(this.getMessageKey(tId, id));
|
|
778
|
-
if (byThreadId) return byThreadId;
|
|
779
|
-
|
|
780
|
-
return null;
|
|
781
|
-
}),
|
|
782
|
-
)
|
|
783
|
-
).filter(msg => msg !== null) as (MastraMessageV2 & { _index?: number })[]),
|
|
784
|
-
];
|
|
785
|
-
|
|
786
|
-
// Sort messages by their position in the sorted set
|
|
787
|
-
messages.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
788
|
-
|
|
789
|
-
const seen = new Set<string>();
|
|
790
|
-
const dedupedMessages = messages.filter(row => {
|
|
791
|
-
if (seen.has(row.id)) return false;
|
|
792
|
-
seen.add(row.id);
|
|
793
|
-
return true;
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
// Remove _index before returning and handle format conversion properly
|
|
797
|
-
const prepared = dedupedMessages
|
|
798
|
-
.filter(message => message !== null && message !== undefined)
|
|
799
|
-
.map(message => {
|
|
800
|
-
const { _index, ...messageWithoutIndex } = message as MastraMessageV2 & { _index?: number };
|
|
801
|
-
return messageWithoutIndex as unknown as MastraMessageV1;
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
// For backward compatibility, return messages directly without using MessageList
|
|
805
|
-
// since MessageList has deduplication logic that can cause issues
|
|
806
|
-
if (format === 'v2') {
|
|
807
|
-
// Convert V1 format back to V2 format
|
|
808
|
-
return prepared.map(msg => ({
|
|
809
|
-
...msg,
|
|
810
|
-
content: msg.content || { format: 2, parts: [{ type: 'text', text: '' }] },
|
|
811
|
-
})) as MastraMessageV2[];
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
return prepared;
|
|
216
|
+
return this.stores.memory.getMessages({ threadId, selectBy, format });
|
|
815
217
|
}
|
|
816
218
|
|
|
817
219
|
public async getMessagesPaginated(
|
|
@@ -819,82 +221,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
819
221
|
format?: 'v1' | 'v2';
|
|
820
222
|
},
|
|
821
223
|
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
822
|
-
|
|
823
|
-
const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
|
|
824
|
-
const fromDate = dateRange?.start;
|
|
825
|
-
const toDate = dateRange?.end;
|
|
826
|
-
const threadMessagesKey = this.getThreadMessagesKey(threadId);
|
|
827
|
-
const messages: (MastraMessageV2 | MastraMessageV1)[] = [];
|
|
828
|
-
|
|
829
|
-
const includedMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
830
|
-
messages.push(...includedMessages);
|
|
831
|
-
|
|
832
|
-
try {
|
|
833
|
-
const allMessageIds = await this.redis.zrange(threadMessagesKey, 0, -1);
|
|
834
|
-
if (allMessageIds.length === 0) {
|
|
835
|
-
return {
|
|
836
|
-
messages: [],
|
|
837
|
-
total: 0,
|
|
838
|
-
page,
|
|
839
|
-
perPage,
|
|
840
|
-
hasMore: false,
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
// Use pipeline to fetch all messages efficiently
|
|
845
|
-
const pipeline = this.redis.pipeline();
|
|
846
|
-
allMessageIds.forEach(id => pipeline.get(this.getMessageKey(threadId, id as string)));
|
|
847
|
-
const results = await pipeline.exec();
|
|
848
|
-
|
|
849
|
-
// Process messages and apply filters - handle undefined results from pipeline
|
|
850
|
-
let messagesData = results.filter((msg): msg is MastraMessageV2 | MastraMessageV1 => msg !== null) as (
|
|
851
|
-
| MastraMessageV2
|
|
852
|
-
| MastraMessageV1
|
|
853
|
-
)[];
|
|
854
|
-
|
|
855
|
-
// Apply date filters if provided
|
|
856
|
-
if (fromDate) {
|
|
857
|
-
messagesData = messagesData.filter(msg => msg && new Date(msg.createdAt).getTime() >= fromDate.getTime());
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
if (toDate) {
|
|
861
|
-
messagesData = messagesData.filter(msg => msg && new Date(msg.createdAt).getTime() <= toDate.getTime());
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// Sort messages by their position in the sorted set
|
|
865
|
-
messagesData.sort((a, b) => allMessageIds.indexOf(a!.id) - allMessageIds.indexOf(b!.id));
|
|
866
|
-
|
|
867
|
-
const total = messagesData.length;
|
|
868
|
-
|
|
869
|
-
const start = page * perPage;
|
|
870
|
-
const end = start + perPage;
|
|
871
|
-
const hasMore = end < total;
|
|
872
|
-
const paginatedMessages = messagesData.slice(start, end);
|
|
873
|
-
|
|
874
|
-
messages.push(...paginatedMessages);
|
|
875
|
-
|
|
876
|
-
const list = new MessageList().add(messages, 'memory');
|
|
877
|
-
const finalMessages = (format === `v2` ? list.get.all.v2() : list.get.all.v1()) as
|
|
878
|
-
| MastraMessageV1[]
|
|
879
|
-
| MastraMessageV2[];
|
|
880
|
-
|
|
881
|
-
return {
|
|
882
|
-
messages: finalMessages,
|
|
883
|
-
total,
|
|
884
|
-
page,
|
|
885
|
-
perPage,
|
|
886
|
-
hasMore,
|
|
887
|
-
};
|
|
888
|
-
} catch (error) {
|
|
889
|
-
console.error('Failed to get paginated messages:', error);
|
|
890
|
-
return {
|
|
891
|
-
messages: [],
|
|
892
|
-
total: 0,
|
|
893
|
-
page,
|
|
894
|
-
perPage,
|
|
895
|
-
hasMore: false,
|
|
896
|
-
};
|
|
897
|
-
}
|
|
224
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
898
225
|
}
|
|
899
226
|
|
|
900
227
|
async persistWorkflowSnapshot(params: {
|
|
@@ -903,18 +230,7 @@ export class UpstashStore extends MastraStorage {
|
|
|
903
230
|
runId: string;
|
|
904
231
|
snapshot: WorkflowRunState;
|
|
905
232
|
}): Promise<void> {
|
|
906
|
-
|
|
907
|
-
await this.insert({
|
|
908
|
-
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
909
|
-
record: {
|
|
910
|
-
namespace,
|
|
911
|
-
workflow_name: workflowName,
|
|
912
|
-
run_id: runId,
|
|
913
|
-
snapshot,
|
|
914
|
-
createdAt: new Date(),
|
|
915
|
-
updatedAt: new Date(),
|
|
916
|
-
},
|
|
917
|
-
});
|
|
233
|
+
return this.stores.workflows.persistWorkflowSnapshot(params);
|
|
918
234
|
}
|
|
919
235
|
|
|
920
236
|
async loadWorkflowSnapshot(params: {
|
|
@@ -922,319 +238,60 @@ export class UpstashStore extends MastraStorage {
|
|
|
922
238
|
workflowName: string;
|
|
923
239
|
runId: string;
|
|
924
240
|
}): Promise<WorkflowRunState | null> {
|
|
925
|
-
|
|
926
|
-
const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
927
|
-
namespace,
|
|
928
|
-
workflow_name: workflowName,
|
|
929
|
-
run_id: runId,
|
|
930
|
-
});
|
|
931
|
-
const data = await this.redis.get<{
|
|
932
|
-
namespace: string;
|
|
933
|
-
workflow_name: string;
|
|
934
|
-
run_id: string;
|
|
935
|
-
snapshot: WorkflowRunState;
|
|
936
|
-
}>(key);
|
|
937
|
-
if (!data) return null;
|
|
938
|
-
return data.snapshot;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* Get all evaluations with pagination and total count
|
|
943
|
-
* @param options Pagination and filtering options
|
|
944
|
-
* @returns Object with evals array and total count
|
|
945
|
-
*/
|
|
946
|
-
async getEvals(
|
|
947
|
-
options?: {
|
|
948
|
-
agentName?: string;
|
|
949
|
-
type?: 'test' | 'live';
|
|
950
|
-
} & PaginationArgs,
|
|
951
|
-
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
952
|
-
try {
|
|
953
|
-
// Default pagination parameters
|
|
954
|
-
const { agentName, type, page = 0, perPage = 100, dateRange } = options || {};
|
|
955
|
-
const fromDate = dateRange?.start;
|
|
956
|
-
const toDate = dateRange?.end;
|
|
957
|
-
|
|
958
|
-
// Get all keys that match the evals table pattern using cursor-based scanning
|
|
959
|
-
const pattern = `${TABLE_EVALS}:*`;
|
|
960
|
-
const keys = await this.scanKeys(pattern);
|
|
961
|
-
|
|
962
|
-
// Check if we have any keys before using pipeline
|
|
963
|
-
if (keys.length === 0) {
|
|
964
|
-
return {
|
|
965
|
-
evals: [],
|
|
966
|
-
total: 0,
|
|
967
|
-
page,
|
|
968
|
-
perPage,
|
|
969
|
-
hasMore: false,
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Use pipeline for batch fetching to improve performance
|
|
974
|
-
const pipeline = this.redis.pipeline();
|
|
975
|
-
keys.forEach(key => pipeline.get(key));
|
|
976
|
-
const results = await pipeline.exec();
|
|
977
|
-
|
|
978
|
-
// Process results and apply filters
|
|
979
|
-
let filteredEvals = results
|
|
980
|
-
.map((result: any) => result as Record<string, any> | null)
|
|
981
|
-
.filter((record): record is Record<string, any> => record !== null && typeof record === 'object');
|
|
982
|
-
|
|
983
|
-
// Apply agent name filter if provided
|
|
984
|
-
if (agentName) {
|
|
985
|
-
filteredEvals = filteredEvals.filter(record => record.agent_name === agentName);
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
// Apply type filter if provided
|
|
989
|
-
if (type === 'test') {
|
|
990
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
991
|
-
if (!record.test_info) return false;
|
|
992
|
-
|
|
993
|
-
try {
|
|
994
|
-
if (typeof record.test_info === 'string') {
|
|
995
|
-
const parsedTestInfo = JSON.parse(record.test_info);
|
|
996
|
-
return parsedTestInfo && typeof parsedTestInfo === 'object' && 'testPath' in parsedTestInfo;
|
|
997
|
-
}
|
|
998
|
-
return typeof record.test_info === 'object' && 'testPath' in record.test_info;
|
|
999
|
-
} catch {
|
|
1000
|
-
return false;
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
} else if (type === 'live') {
|
|
1004
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
1005
|
-
if (!record.test_info) return true;
|
|
1006
|
-
|
|
1007
|
-
try {
|
|
1008
|
-
if (typeof record.test_info === 'string') {
|
|
1009
|
-
const parsedTestInfo = JSON.parse(record.test_info);
|
|
1010
|
-
return !(parsedTestInfo && typeof parsedTestInfo === 'object' && 'testPath' in parsedTestInfo);
|
|
1011
|
-
}
|
|
1012
|
-
return !(typeof record.test_info === 'object' && 'testPath' in record.test_info);
|
|
1013
|
-
} catch {
|
|
1014
|
-
return true;
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
// Apply date filters if provided
|
|
1020
|
-
if (fromDate) {
|
|
1021
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
1022
|
-
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
1023
|
-
return createdAt.getTime() >= fromDate.getTime();
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
if (toDate) {
|
|
1028
|
-
filteredEvals = filteredEvals.filter(record => {
|
|
1029
|
-
const createdAt = new Date(record.created_at || record.createdAt || 0);
|
|
1030
|
-
return createdAt.getTime() <= toDate.getTime();
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// Sort by creation date (newest first)
|
|
1035
|
-
filteredEvals.sort((a, b) => {
|
|
1036
|
-
const dateA = new Date(a.created_at || a.createdAt || 0).getTime();
|
|
1037
|
-
const dateB = new Date(b.created_at || b.createdAt || 0).getTime();
|
|
1038
|
-
return dateB - dateA;
|
|
1039
|
-
});
|
|
1040
|
-
|
|
1041
|
-
const total = filteredEvals.length;
|
|
1042
|
-
|
|
1043
|
-
// Apply pagination
|
|
1044
|
-
const start = page * perPage;
|
|
1045
|
-
const end = start + perPage;
|
|
1046
|
-
const paginatedEvals = filteredEvals.slice(start, end);
|
|
1047
|
-
const hasMore = end < total;
|
|
1048
|
-
|
|
1049
|
-
// Transform to EvalRow format
|
|
1050
|
-
const evals = paginatedEvals.map(record => this.transformEvalRecord(record));
|
|
1051
|
-
|
|
1052
|
-
return {
|
|
1053
|
-
evals,
|
|
1054
|
-
total,
|
|
1055
|
-
page,
|
|
1056
|
-
perPage,
|
|
1057
|
-
hasMore,
|
|
1058
|
-
};
|
|
1059
|
-
} catch (error) {
|
|
1060
|
-
const { page = 0, perPage = 100 } = options || {};
|
|
1061
|
-
console.error('Failed to get evals:', error);
|
|
1062
|
-
return {
|
|
1063
|
-
evals: [],
|
|
1064
|
-
total: 0,
|
|
1065
|
-
page,
|
|
1066
|
-
perPage,
|
|
1067
|
-
hasMore: false,
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
241
|
+
return this.stores.workflows.loadWorkflowSnapshot(params);
|
|
1070
242
|
}
|
|
1071
243
|
|
|
1072
|
-
async getWorkflowRuns(
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
resourceId?: string;
|
|
1089
|
-
} = { namespace: 'workflows' },
|
|
1090
|
-
): Promise<WorkflowRuns> {
|
|
1091
|
-
try {
|
|
1092
|
-
// Get all workflow keys
|
|
1093
|
-
let pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace }) + ':*';
|
|
1094
|
-
if (workflowName && resourceId) {
|
|
1095
|
-
pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, {
|
|
1096
|
-
namespace,
|
|
1097
|
-
workflow_name: workflowName,
|
|
1098
|
-
run_id: '*',
|
|
1099
|
-
resourceId,
|
|
1100
|
-
});
|
|
1101
|
-
} else if (workflowName) {
|
|
1102
|
-
pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName }) + ':*';
|
|
1103
|
-
} else if (resourceId) {
|
|
1104
|
-
pattern = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: '*', run_id: '*', resourceId });
|
|
1105
|
-
}
|
|
1106
|
-
const keys = await this.scanKeys(pattern);
|
|
1107
|
-
|
|
1108
|
-
// Check if we have any keys before using pipeline
|
|
1109
|
-
if (keys.length === 0) {
|
|
1110
|
-
return { runs: [], total: 0 };
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
// Use pipeline for batch fetching to improve performance
|
|
1114
|
-
const pipeline = this.redis.pipeline();
|
|
1115
|
-
keys.forEach(key => pipeline.get(key));
|
|
1116
|
-
const results = await pipeline.exec();
|
|
1117
|
-
|
|
1118
|
-
// Filter and transform results - handle undefined results
|
|
1119
|
-
let runs = results
|
|
1120
|
-
.map((result: any) => result as Record<string, any> | null)
|
|
1121
|
-
.filter(
|
|
1122
|
-
(record): record is Record<string, any> =>
|
|
1123
|
-
record !== null && record !== undefined && typeof record === 'object' && 'workflow_name' in record,
|
|
1124
|
-
)
|
|
1125
|
-
// Only filter by workflowName if it was specifically requested
|
|
1126
|
-
.filter(record => !workflowName || record.workflow_name === workflowName)
|
|
1127
|
-
.map(w => this.parseWorkflowRun(w!))
|
|
1128
|
-
.filter(w => {
|
|
1129
|
-
if (fromDate && w.createdAt < fromDate) return false;
|
|
1130
|
-
if (toDate && w.createdAt > toDate) return false;
|
|
1131
|
-
return true;
|
|
1132
|
-
})
|
|
1133
|
-
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
1134
|
-
|
|
1135
|
-
const total = runs.length;
|
|
1136
|
-
|
|
1137
|
-
// Apply pagination if requested
|
|
1138
|
-
if (limit !== undefined && offset !== undefined) {
|
|
1139
|
-
runs = runs.slice(offset, offset + limit);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
return { runs, total };
|
|
1143
|
-
} catch (error) {
|
|
1144
|
-
console.error('Error getting workflow runs:', error);
|
|
1145
|
-
throw error;
|
|
1146
|
-
}
|
|
244
|
+
async getWorkflowRuns({
|
|
245
|
+
workflowName,
|
|
246
|
+
fromDate,
|
|
247
|
+
toDate,
|
|
248
|
+
limit,
|
|
249
|
+
offset,
|
|
250
|
+
resourceId,
|
|
251
|
+
}: {
|
|
252
|
+
workflowName?: string;
|
|
253
|
+
fromDate?: Date;
|
|
254
|
+
toDate?: Date;
|
|
255
|
+
limit?: number;
|
|
256
|
+
offset?: number;
|
|
257
|
+
resourceId?: string;
|
|
258
|
+
} = {}): Promise<WorkflowRuns> {
|
|
259
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
1147
260
|
}
|
|
1148
261
|
|
|
1149
262
|
async getWorkflowRunById({
|
|
1150
|
-
namespace = 'workflows',
|
|
1151
263
|
runId,
|
|
1152
264
|
workflowName,
|
|
1153
265
|
}: {
|
|
1154
|
-
namespace: string;
|
|
1155
266
|
runId: string;
|
|
1156
267
|
workflowName?: string;
|
|
1157
268
|
}): Promise<WorkflowRun | null> {
|
|
1158
|
-
|
|
1159
|
-
const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName, run_id: runId }) + '*';
|
|
1160
|
-
const keys = await this.scanKeys(key);
|
|
1161
|
-
const workflows = await Promise.all(
|
|
1162
|
-
keys.map(async key => {
|
|
1163
|
-
const data = await this.redis.get<{
|
|
1164
|
-
workflow_name: string;
|
|
1165
|
-
run_id: string;
|
|
1166
|
-
snapshot: WorkflowRunState | string;
|
|
1167
|
-
createdAt: string | Date;
|
|
1168
|
-
updatedAt: string | Date;
|
|
1169
|
-
resourceId: string;
|
|
1170
|
-
}>(key);
|
|
1171
|
-
return data;
|
|
1172
|
-
}),
|
|
1173
|
-
);
|
|
1174
|
-
const data = workflows.find(w => w?.run_id === runId && w?.workflow_name === workflowName) as WorkflowRun | null;
|
|
1175
|
-
if (!data) return null;
|
|
1176
|
-
return this.parseWorkflowRun(data);
|
|
1177
|
-
} catch (error) {
|
|
1178
|
-
console.error('Error getting workflow run by ID:', error);
|
|
1179
|
-
throw error;
|
|
1180
|
-
}
|
|
269
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
1181
270
|
}
|
|
1182
271
|
|
|
1183
272
|
async close(): Promise<void> {
|
|
1184
273
|
// No explicit cleanup needed for Upstash Redis
|
|
1185
274
|
}
|
|
1186
275
|
|
|
1187
|
-
async updateMessages(
|
|
1188
|
-
messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
}[];
|
|
276
|
+
async updateMessages(args: {
|
|
277
|
+
messages: (Partial<Omit<MastraMessageV2, 'createdAt'>> & {
|
|
278
|
+
id: string;
|
|
279
|
+
content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
|
|
280
|
+
})[];
|
|
1193
281
|
}): Promise<MastraMessageV2[]> {
|
|
1194
|
-
this.
|
|
1195
|
-
throw new Error('Method not implemented');
|
|
282
|
+
return this.stores.memory.updateMessages(args);
|
|
1196
283
|
}
|
|
1197
284
|
|
|
1198
|
-
async
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
const data = await this.redis.get<StorageResourceType>(key);
|
|
1202
|
-
|
|
1203
|
-
if (!data) {
|
|
1204
|
-
return null;
|
|
1205
|
-
}
|
|
285
|
+
async deleteMessages(messageIds: string[]): Promise<void> {
|
|
286
|
+
return this.stores.memory.deleteMessages(messageIds);
|
|
287
|
+
}
|
|
1206
288
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
createdAt: new Date(data.createdAt),
|
|
1210
|
-
updatedAt: new Date(data.updatedAt),
|
|
1211
|
-
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1212
|
-
workingMemory: typeof data.workingMemory === 'object' ? JSON.stringify(data.workingMemory) : data.workingMemory,
|
|
1213
|
-
metadata: typeof data.metadata === 'string' ? JSON.parse(data.metadata) : data.metadata,
|
|
1214
|
-
};
|
|
1215
|
-
} catch (error) {
|
|
1216
|
-
this.logger.error('Error getting resource by ID:', error);
|
|
1217
|
-
throw error;
|
|
1218
|
-
}
|
|
289
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
290
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
1219
291
|
}
|
|
1220
292
|
|
|
1221
293
|
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
1222
|
-
|
|
1223
|
-
const key = `${TABLE_RESOURCES}:${resource.id}`;
|
|
1224
|
-
const serializedResource = {
|
|
1225
|
-
...resource,
|
|
1226
|
-
metadata: JSON.stringify(resource.metadata),
|
|
1227
|
-
createdAt: resource.createdAt.toISOString(),
|
|
1228
|
-
updatedAt: resource.updatedAt.toISOString(),
|
|
1229
|
-
};
|
|
1230
|
-
|
|
1231
|
-
await this.redis.set(key, serializedResource);
|
|
1232
|
-
|
|
1233
|
-
return resource;
|
|
1234
|
-
} catch (error) {
|
|
1235
|
-
this.logger.error('Error saving resource:', error);
|
|
1236
|
-
throw error;
|
|
1237
|
-
}
|
|
294
|
+
return this.stores.memory.saveResource({ resource });
|
|
1238
295
|
}
|
|
1239
296
|
|
|
1240
297
|
async updateResource({
|
|
@@ -1246,36 +303,50 @@ export class UpstashStore extends MastraStorage {
|
|
|
1246
303
|
workingMemory?: string;
|
|
1247
304
|
metadata?: Record<string, unknown>;
|
|
1248
305
|
}): Promise<StorageResourceType> {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
306
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async getScoreById({ id: _id }: { id: string }): Promise<ScoreRowData | null> {
|
|
310
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async saveScore(score: ScoreRowData): Promise<{ score: ScoreRowData }> {
|
|
314
|
+
return this.stores.scores.saveScore(score);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async getScoresByRunId({
|
|
318
|
+
runId,
|
|
319
|
+
pagination,
|
|
320
|
+
}: {
|
|
321
|
+
runId: string;
|
|
322
|
+
pagination: StoragePagination;
|
|
323
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
324
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
325
|
+
}
|
|
1263
326
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
327
|
+
async getScoresByEntityId({
|
|
328
|
+
entityId,
|
|
329
|
+
entityType,
|
|
330
|
+
pagination,
|
|
331
|
+
}: {
|
|
332
|
+
pagination: StoragePagination;
|
|
333
|
+
entityId: string;
|
|
334
|
+
entityType: string;
|
|
335
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
336
|
+
return this.stores.scores.getScoresByEntityId({
|
|
337
|
+
entityId,
|
|
338
|
+
entityType,
|
|
339
|
+
pagination,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
1273
342
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
343
|
+
async getScoresByScorerId({
|
|
344
|
+
scorerId,
|
|
345
|
+
pagination,
|
|
346
|
+
}: {
|
|
347
|
+
scorerId: string;
|
|
348
|
+
pagination: StoragePagination;
|
|
349
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
350
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, pagination });
|
|
1280
351
|
}
|
|
1281
352
|
}
|