@sentry/junior-memory 0.124.2 → 0.125.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/agent.d.ts +12 -2
- package/dist/api.d.ts +7 -1
- package/dist/events.d.ts +17 -1
- package/dist/index.js +168 -47
- package/dist/index.js.map +1 -1
- package/dist/operational-report.d.ts +2 -1
- package/dist/recall.d.ts +2 -1
- package/dist/store.d.ts +1 -0
- package/package.json +2 -2
- package/src/agent.ts +26 -8
- package/src/api.ts +20 -2
- package/src/events.ts +53 -16
- package/src/operational-report.ts +37 -1
- package/src/plugin.ts +17 -2
- package/src/process-session.ts +39 -28
- package/src/recall.ts +52 -5
- package/src/store.ts +2 -0
- package/src/tools.ts +1 -0
package/README.md
CHANGED
|
@@ -15,7 +15,9 @@ exported types, tools, and tests are authoritative.
|
|
|
15
15
|
inspection.
|
|
16
16
|
- The dashboard exposes a searchable, paginated **Memories** user page for
|
|
17
17
|
personal memories owned by actors linked to the signed-in viewer. Its
|
|
18
|
-
**Forget** action archives the selected memory.
|
|
18
|
+
**Forget** action archives the selected memory. The overview charts global
|
|
19
|
+
passive-extraction cost from the durable `memory/memories_captured` events;
|
|
20
|
+
the System plugin report uses the same event-cost feed.
|
|
19
21
|
- Authenticated REST clients can list and search personal memories through
|
|
20
22
|
`GET /api/plugins/memory/memories`, read one through
|
|
21
23
|
`GET /api/plugins/memory/memories/:id`, and archive one through
|
|
@@ -55,6 +57,10 @@ exported types, tools, and tests are authoritative.
|
|
|
55
57
|
learning.
|
|
56
58
|
- Passive extraction creates only durable, reusable facts—not transient tasks,
|
|
57
59
|
conversation summaries, secrets, or speculative interpretation.
|
|
60
|
+
- Every completed passive extraction emits the namespaced
|
|
61
|
+
`memory/memories_captured` conversation event with its best-effort model cost.
|
|
62
|
+
Empty extraction outcomes remain durable for reporting but do not produce a
|
|
63
|
+
transcript row.
|
|
58
64
|
- Candidate review resolves duplicates and supersession before activation.
|
|
59
65
|
- Search combines independently ranked vector and PostgreSQL full-text matches
|
|
60
66
|
with reciprocal rank fusion; provider-specific raw scores are never added
|
|
@@ -62,6 +68,10 @@ exported types, tools, and tests are authoritative.
|
|
|
62
68
|
- Automatic recall retrieves a broad candidate window, then uses the
|
|
63
69
|
memory-owned relevance model to admit at most five directly useful memories.
|
|
64
70
|
An empty result contributes no filler prompt text.
|
|
71
|
+
- Every completed automatic recall attempt emits an invisible, namespaced
|
|
72
|
+
`memory/memories_recalled` conversation event with the admitted memory IDs
|
|
73
|
+
and best-effort embedding and relevance-model cost, including retrievals that
|
|
74
|
+
find no candidates and decisions that admit no memories.
|
|
65
75
|
- Automatic recall degrades to no prompt contribution when relevance selection
|
|
66
76
|
fails. Review, extraction, and write failures still fail their owning
|
|
67
77
|
hook/task without corrupting existing memory state.
|
package/dist/agent.d.ts
CHANGED
|
@@ -183,12 +183,22 @@ export type MemoryRecallInput = z.output<typeof memoryRecallInputSchema>;
|
|
|
183
183
|
export type CreateMemoryRequest = z.output<typeof createMemoryRequestSchema>;
|
|
184
184
|
export type ExtractSessionRequest = z.output<typeof extractSessionRequestSchema>;
|
|
185
185
|
export type ExtractedMemory = z.output<typeof extractedMemoryResultSchema>;
|
|
186
|
+
/** Memories proposed by passive extraction and the model cost of that pass. */
|
|
187
|
+
export type MemoryExtractionResult = {
|
|
188
|
+
costUsd?: number;
|
|
189
|
+
memories: ExtractedMemory[];
|
|
190
|
+
};
|
|
191
|
+
/** Memories admitted by automatic recall and the model cost of that decision. */
|
|
192
|
+
export type MemoryRecallResult = {
|
|
193
|
+
costUsd?: number;
|
|
194
|
+
relevantIds: string[];
|
|
195
|
+
};
|
|
186
196
|
export interface MemoryAgent {
|
|
187
197
|
/** Select candidate memories that directly help with the current request. */
|
|
188
|
-
selectRelevantMemories(request: MemoryRecallInput): Promise<
|
|
198
|
+
selectRelevantMemories(request: MemoryRecallInput): Promise<MemoryRecallResult> | MemoryRecallResult;
|
|
189
199
|
/** Classify a new preference against related active preferences. */
|
|
190
200
|
adjudicateSupersession(request: MemorySupersessionInput): Promise<MemorySupersessionDecision> | MemorySupersessionDecision;
|
|
191
|
-
extractSessionMemories(request: ExtractSessionRequest): Promise<
|
|
201
|
+
extractSessionMemories(request: ExtractSessionRequest): Promise<MemoryExtractionResult> | MemoryExtractionResult;
|
|
192
202
|
reviewCreateRequest(request: CreateMemoryRequest): Promise<MemoryReview> | MemoryReview;
|
|
193
203
|
}
|
|
194
204
|
/** Create the memory-owned agent that reviews, extracts, and recalls memories. */
|
package/dist/api.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* workspace scopes.
|
|
7
7
|
*/
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { type PluginRouteApp, type PluginUserPageActor } from "@sentry/junior-plugin-api";
|
|
9
|
+
import { type PluginConversationEventStats, type PluginRouteApp, type PluginUserPageActor } from "@sentry/junior-plugin-api";
|
|
10
10
|
import type { MemoryDb } from "./store";
|
|
11
11
|
export declare const memoryApiSchema: z.ZodObject<{
|
|
12
12
|
content: z.ZodString;
|
|
@@ -49,6 +49,11 @@ export declare const memoryDashboardResponseSchema: z.ZodObject<{
|
|
|
49
49
|
personal: z.ZodNumber;
|
|
50
50
|
public: z.ZodNumber;
|
|
51
51
|
}, z.core.$strict>>;
|
|
52
|
+
extractionDays: z.ZodArray<z.ZodObject<{
|
|
53
|
+
costUsd: z.ZodNumber;
|
|
54
|
+
date: z.ZodISODate;
|
|
55
|
+
events: z.ZodNumber;
|
|
56
|
+
}, z.core.$strict>>;
|
|
52
57
|
generatedAt: z.ZodISODateTime;
|
|
53
58
|
stats: z.ZodObject<{
|
|
54
59
|
active: z.ZodNumber;
|
|
@@ -69,6 +74,7 @@ export type MemoryListResponse = z.output<typeof memoryListResponseSchema>;
|
|
|
69
74
|
interface MemoryApiOptions {
|
|
70
75
|
actors(email: string): Promise<PluginUserPageActor[]>;
|
|
71
76
|
db: MemoryDb;
|
|
77
|
+
eventStats: PluginConversationEventStats;
|
|
72
78
|
}
|
|
73
79
|
/** Create the authenticated viewer-memory REST app. */
|
|
74
80
|
export declare function createMemoryApi(options: MemoryApiOptions): PluginRouteApp;
|
package/dist/events.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { MemoryRecord } from "./store";
|
|
2
|
-
/**
|
|
2
|
+
/** Previous stored memory-capture event shape retained for transcript rendering. */
|
|
3
|
+
export declare const memoriesCapturedEventV1: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
|
|
4
|
+
memories: {
|
|
5
|
+
content: string;
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "preference" | "procedure" | "knowledge";
|
|
8
|
+
observedAtMs: number;
|
|
9
|
+
scope: "personal" | "conversation";
|
|
10
|
+
}[];
|
|
11
|
+
}>;
|
|
12
|
+
/** Durable outcome emitted after every completed passive memory extraction. */
|
|
3
13
|
export declare const memoriesCapturedEvent: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
|
|
4
14
|
memories: {
|
|
5
15
|
content: string;
|
|
@@ -8,6 +18,12 @@ export declare const memoriesCapturedEvent: import("@sentry/junior-plugin-api").
|
|
|
8
18
|
observedAtMs: number;
|
|
9
19
|
scope: "personal" | "conversation";
|
|
10
20
|
}[];
|
|
21
|
+
costUsd?: number | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
/** Durable outcome emitted after one completed automatic recall attempt. */
|
|
24
|
+
export declare const memoriesRecalledEvent: import("@sentry/junior-plugin-api").DefinedConversationEvent<{
|
|
25
|
+
memories: string[];
|
|
26
|
+
costUsd?: number | undefined;
|
|
11
27
|
}>;
|
|
12
28
|
/** Select the stable, safe memory fields retained in conversation history. */
|
|
13
29
|
export declare function capturedMemory(memory: MemoryRecord): {
|
package/dist/index.js
CHANGED
|
@@ -397,6 +397,7 @@ var memoryRecordSchema = z2.object({
|
|
|
397
397
|
}).strict();
|
|
398
398
|
var embeddingVectorSchema = z2.array(numberSchema).length(MEMORY_EMBEDDING_DIMENSIONS);
|
|
399
399
|
var embeddingResultSchema = z2.object({
|
|
400
|
+
costUsd: z2.number().finite().nonnegative().optional(),
|
|
400
401
|
dimensions: z2.literal(MEMORY_EMBEDDING_DIMENSIONS),
|
|
401
402
|
model: nonEmptyStringSchema2,
|
|
402
403
|
provider: nonEmptyStringSchema2,
|
|
@@ -1680,9 +1681,12 @@ function createMemoryAgent(model) {
|
|
|
1680
1681
|
});
|
|
1681
1682
|
const decision = memoryRecallDecisionSchema.parse(result.object);
|
|
1682
1683
|
const candidateIds = new Set(request.candidates.map(({ id }) => id));
|
|
1683
|
-
return
|
|
1684
|
-
|
|
1685
|
-
|
|
1684
|
+
return {
|
|
1685
|
+
relevantIds: [...new Set(decision.relevantIds)].filter(
|
|
1686
|
+
(id) => candidateIds.has(id)
|
|
1687
|
+
),
|
|
1688
|
+
...result.costUsd !== void 0 ? { costUsd: result.costUsd } : {}
|
|
1689
|
+
};
|
|
1686
1690
|
},
|
|
1687
1691
|
async adjudicateSupersession(rawRequest) {
|
|
1688
1692
|
const request = memorySupersessionInputSchema.parse(rawRequest);
|
|
@@ -1702,9 +1706,12 @@ function createMemoryAgent(model) {
|
|
|
1702
1706
|
prompt: sessionExtractionPrompt(request),
|
|
1703
1707
|
maxTokens: 1e3
|
|
1704
1708
|
});
|
|
1705
|
-
return
|
|
1706
|
-
|
|
1707
|
-
|
|
1709
|
+
return {
|
|
1710
|
+
memories: extractedMemoriesFromResponse(
|
|
1711
|
+
extractMemoriesResponseSchema.parse(result.object)
|
|
1712
|
+
),
|
|
1713
|
+
...result.costUsd !== void 0 ? { costUsd: result.costUsd } : {}
|
|
1714
|
+
};
|
|
1708
1715
|
},
|
|
1709
1716
|
async reviewCreateRequest(rawRequest) {
|
|
1710
1717
|
const request = parseCreateMemoryRequest(rawRequest);
|
|
@@ -2170,8 +2177,14 @@ var memoryDashboardDaySchema = z6.object({
|
|
|
2170
2177
|
personal: z6.number().int().min(0),
|
|
2171
2178
|
public: z6.number().int().min(0)
|
|
2172
2179
|
}).strict();
|
|
2180
|
+
var memoryExtractionDaySchema = z6.object({
|
|
2181
|
+
costUsd: z6.number().finite().nonnegative(),
|
|
2182
|
+
date: z6.iso.date(),
|
|
2183
|
+
events: z6.number().int().min(0)
|
|
2184
|
+
}).strict();
|
|
2173
2185
|
var memoryDashboardResponseSchema = z6.object({
|
|
2174
2186
|
days: z6.array(memoryDashboardDaySchema).length(90),
|
|
2187
|
+
extractionDays: z6.array(memoryExtractionDaySchema).length(90),
|
|
2175
2188
|
generatedAt: z6.iso.datetime(),
|
|
2176
2189
|
stats: z6.object({
|
|
2177
2190
|
active: z6.number().int().min(0),
|
|
@@ -2233,12 +2246,17 @@ function createMemoryApi(options) {
|
|
|
2233
2246
|
const memories = createViewerMemories(options.db, actors);
|
|
2234
2247
|
try {
|
|
2235
2248
|
if (isDashboard && (request.method === "GET" || request.method === "HEAD")) {
|
|
2236
|
-
const [stats, days] = await Promise.all([
|
|
2249
|
+
const [stats, days, extractionDays] = await Promise.all([
|
|
2237
2250
|
memories.stats(),
|
|
2238
|
-
memories.timeline({ days: 90 })
|
|
2251
|
+
memories.timeline({ days: 90 }),
|
|
2252
|
+
options.eventStats.costsByDay({
|
|
2253
|
+
days: 90,
|
|
2254
|
+
eventName: "memories_captured"
|
|
2255
|
+
})
|
|
2239
2256
|
]);
|
|
2240
2257
|
const body = memoryDashboardResponseSchema.parse({
|
|
2241
2258
|
days,
|
|
2259
|
+
extractionDays,
|
|
2242
2260
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2243
2261
|
stats
|
|
2244
2262
|
});
|
|
@@ -2679,6 +2697,7 @@ function memoryToolResult(target, data) {
|
|
|
2679
2697
|
}
|
|
2680
2698
|
function createMemoryCreateTool(context) {
|
|
2681
2699
|
return definePluginTool({
|
|
2700
|
+
approvalMode: "approve",
|
|
2682
2701
|
annotations: {
|
|
2683
2702
|
destructiveHint: false,
|
|
2684
2703
|
idempotentHint: true,
|
|
@@ -2858,23 +2877,47 @@ function eventPreview(content) {
|
|
|
2858
2877
|
if (trimmed.length <= MAX_EVENT_PREVIEW_CHARS) return trimmed;
|
|
2859
2878
|
return `${trimmed.slice(0, MAX_EVENT_PREVIEW_CHARS - 3).trimEnd()}...`;
|
|
2860
2879
|
}
|
|
2861
|
-
var
|
|
2880
|
+
var capturedMemoriesSchema = z8.object({
|
|
2881
|
+
memories: z8.array(capturedMemorySchema).max(100),
|
|
2882
|
+
costUsd: z8.number().finite().nonnegative().optional()
|
|
2883
|
+
}).strict();
|
|
2884
|
+
var recalledMemoriesSchema = z8.object({
|
|
2885
|
+
memories: z8.array(z8.string().min(1)).max(5),
|
|
2886
|
+
costUsd: z8.number().finite().nonnegative().optional()
|
|
2887
|
+
}).strict();
|
|
2888
|
+
function renderCapturedMemories(event) {
|
|
2889
|
+
const count = event.memories.length;
|
|
2890
|
+
if (count === 0) return void 0;
|
|
2891
|
+
return {
|
|
2892
|
+
icon: "brain",
|
|
2893
|
+
title: count === 1 ? "Memory captured" : "Memories captured",
|
|
2894
|
+
preview: count === 1 ? eventPreview(event.memories[0].content) : `${count} memories`,
|
|
2895
|
+
details: event.memories.map((memory) => ({
|
|
2896
|
+
title: memory.content,
|
|
2897
|
+
metadata: [memory.kind, memory.scope]
|
|
2898
|
+
}))
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
var memoriesCapturedEventV1 = defineConversationEvent({
|
|
2862
2902
|
name: "memories_captured",
|
|
2863
2903
|
version: 1,
|
|
2864
2904
|
schema: z8.object({
|
|
2865
2905
|
memories: z8.array(capturedMemorySchema).min(1).max(100)
|
|
2866
2906
|
}).strict(),
|
|
2867
|
-
renderEvent
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2907
|
+
renderEvent: renderCapturedMemories
|
|
2908
|
+
});
|
|
2909
|
+
var memoriesCapturedEvent = defineConversationEvent({
|
|
2910
|
+
name: "memories_captured",
|
|
2911
|
+
version: 2,
|
|
2912
|
+
schema: capturedMemoriesSchema,
|
|
2913
|
+
renderEvent: renderCapturedMemories
|
|
2914
|
+
});
|
|
2915
|
+
var memoriesRecalledEvent = defineConversationEvent({
|
|
2916
|
+
name: "memories_recalled",
|
|
2917
|
+
version: 1,
|
|
2918
|
+
schema: recalledMemoriesSchema,
|
|
2919
|
+
renderEvent() {
|
|
2920
|
+
return void 0;
|
|
2878
2921
|
}
|
|
2879
2922
|
});
|
|
2880
2923
|
function capturedMemory(memory) {
|
|
@@ -2895,14 +2938,19 @@ var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
2895
2938
|
"searchMemories"
|
|
2896
2939
|
]);
|
|
2897
2940
|
var MEMORY_TASK_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2898
|
-
var
|
|
2941
|
+
var extractedMemorySchema2 = z9.object({
|
|
2942
|
+
content: z9.string().min(1),
|
|
2943
|
+
expiresAtMs: z9.number().finite().nullable(),
|
|
2944
|
+
kind: z9.enum(MEMORY_KINDS),
|
|
2945
|
+
evidenceMessageIndices: z9.array(z9.number().int().nonnegative()).min(1).max(10)
|
|
2946
|
+
}).strict().transform(parseExtractedMemory);
|
|
2947
|
+
var extractedMemoryCacheSchema = z9.union([
|
|
2899
2948
|
z9.object({
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
);
|
|
2949
|
+
costUsd: z9.number().finite().nonnegative().optional(),
|
|
2950
|
+
memories: z9.array(extractedMemorySchema2).max(5)
|
|
2951
|
+
}).strict(),
|
|
2952
|
+
z9.array(extractedMemorySchema2).max(5).transform((memories) => ({ memories }))
|
|
2953
|
+
]);
|
|
2906
2954
|
function recordCapturedMemory(captured, result) {
|
|
2907
2955
|
const supersededIds = new Set(result.supersededIds ?? []);
|
|
2908
2956
|
for (let index2 = captured.length - 1; index2 >= 0; index2 -= 1) {
|
|
@@ -2969,7 +3017,7 @@ function passiveInput(sessionId, memory, sourceKey2, target) {
|
|
|
2969
3017
|
...memory.expiresAtMs !== null ? { expiresAtMs: memory.expiresAtMs } : {}
|
|
2970
3018
|
};
|
|
2971
3019
|
}
|
|
2972
|
-
async function
|
|
3020
|
+
async function getTaskExtraction(context, extract) {
|
|
2973
3021
|
const cacheKey = `memory-extraction:${context.id}`;
|
|
2974
3022
|
const cached = await context.state.get(cacheKey);
|
|
2975
3023
|
if (cached !== void 0) {
|
|
@@ -2979,11 +3027,9 @@ async function getTaskMemories(context, extract) {
|
|
|
2979
3027
|
}
|
|
2980
3028
|
await context.state.delete(cacheKey);
|
|
2981
3029
|
}
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
}
|
|
2986
|
-
return memories;
|
|
3030
|
+
const extraction = await extract();
|
|
3031
|
+
await context.state.set(cacheKey, extraction, MEMORY_TASK_STATE_TTL_MS);
|
|
3032
|
+
return extraction;
|
|
2987
3033
|
}
|
|
2988
3034
|
async function processMemorySession(context) {
|
|
2989
3035
|
const run = await context.run.load();
|
|
@@ -3015,7 +3061,7 @@ async function processMemorySession(context) {
|
|
|
3015
3061
|
supersessionDecider: agent
|
|
3016
3062
|
});
|
|
3017
3063
|
await store.archiveExpiredMemories();
|
|
3018
|
-
const
|
|
3064
|
+
const extraction = await getTaskExtraction(context, async () => {
|
|
3019
3065
|
const existingMemories = await store.searchMemories({
|
|
3020
3066
|
limit: 10,
|
|
3021
3067
|
query: evidenceText
|
|
@@ -3029,11 +3075,8 @@ async function processMemorySession(context) {
|
|
|
3029
3075
|
runtimeContext: runtimeContext2
|
|
3030
3076
|
});
|
|
3031
3077
|
});
|
|
3032
|
-
if (memories.length === 0) {
|
|
3033
|
-
return;
|
|
3034
|
-
}
|
|
3035
3078
|
const captured = [];
|
|
3036
|
-
for (const memory of memories) {
|
|
3079
|
+
for (const memory of extraction.memories) {
|
|
3037
3080
|
const target = routeExtractedMemory(memory, transcript, run);
|
|
3038
3081
|
if (target === "drop") {
|
|
3039
3082
|
continue;
|
|
@@ -3047,9 +3090,12 @@ async function processMemorySession(context) {
|
|
|
3047
3090
|
const result = await store.createMemory(input);
|
|
3048
3091
|
recordCapturedMemory(captured, result);
|
|
3049
3092
|
}
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3093
|
+
await context.events.emit(
|
|
3094
|
+
memoriesCapturedEvent({
|
|
3095
|
+
memories: captured,
|
|
3096
|
+
...extraction.costUsd !== void 0 ? { costUsd: extraction.costUsd } : {}
|
|
3097
|
+
})
|
|
3098
|
+
);
|
|
3053
3099
|
}
|
|
3054
3100
|
|
|
3055
3101
|
// src/recall.ts
|
|
@@ -3113,6 +3159,19 @@ function renderMemoryPrompt(memories) {
|
|
|
3113
3159
|
"Treat these as possibly stale context. Current user instructions and repository evidence take priority."
|
|
3114
3160
|
].join("\n");
|
|
3115
3161
|
}
|
|
3162
|
+
function addUsd(left, right) {
|
|
3163
|
+
if (left === void 0) return right;
|
|
3164
|
+
if (right === void 0) return left;
|
|
3165
|
+
return Math.round((left + right) * 1e12) / 1e12;
|
|
3166
|
+
}
|
|
3167
|
+
async function emitRecallOutcome(args) {
|
|
3168
|
+
await args.events?.emit(
|
|
3169
|
+
memoriesRecalledEvent({
|
|
3170
|
+
memories: args.memories,
|
|
3171
|
+
...args.costUsd !== void 0 ? { costUsd: args.costUsd } : {}
|
|
3172
|
+
})
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3116
3175
|
var memoryRecallContext = definePromptContext({
|
|
3117
3176
|
kind: "recall",
|
|
3118
3177
|
version: 1,
|
|
@@ -3128,19 +3187,33 @@ async function createMemoryPromptContributions(context) {
|
|
|
3128
3187
|
...context.actor ? { actor: context.actor } : {},
|
|
3129
3188
|
source: context.source
|
|
3130
3189
|
});
|
|
3190
|
+
let embeddingCostUsd;
|
|
3191
|
+
const sourceEmbedder = context.embedder;
|
|
3192
|
+
const embedder = sourceEmbedder ? {
|
|
3193
|
+
async embedTexts(input) {
|
|
3194
|
+
const result = await sourceEmbedder.embedTexts(input);
|
|
3195
|
+
embeddingCostUsd = addUsd(embeddingCostUsd, result.costUsd);
|
|
3196
|
+
return result;
|
|
3197
|
+
}
|
|
3198
|
+
} : void 0;
|
|
3131
3199
|
const candidates = await createMemoryStore(context.db, runtimeContext2, {
|
|
3132
|
-
...
|
|
3200
|
+
...embedder ? { embedder } : {},
|
|
3133
3201
|
...context.maxVectorDistance !== void 0 ? { maxVectorDistance: context.maxVectorDistance } : {}
|
|
3134
3202
|
}).recallMemories({
|
|
3135
3203
|
query: context.text,
|
|
3136
3204
|
limit: RECALL_CANDIDATE_LIMIT
|
|
3137
3205
|
});
|
|
3138
3206
|
if (candidates.length === 0) {
|
|
3207
|
+
await emitRecallOutcome({
|
|
3208
|
+
...embeddingCostUsd !== void 0 ? { costUsd: embeddingCostUsd } : {},
|
|
3209
|
+
events: context.events,
|
|
3210
|
+
memories: []
|
|
3211
|
+
});
|
|
3139
3212
|
return void 0;
|
|
3140
3213
|
}
|
|
3141
|
-
let
|
|
3214
|
+
let recall;
|
|
3142
3215
|
try {
|
|
3143
|
-
|
|
3216
|
+
recall = await context.agent.selectRelevantMemories({
|
|
3144
3217
|
candidates: candidates.map(({ content, id }) => ({ content, id })),
|
|
3145
3218
|
userRequest: context.text
|
|
3146
3219
|
});
|
|
@@ -3151,8 +3224,14 @@ async function createMemoryPromptContributions(context) {
|
|
|
3151
3224
|
const candidatesById = new Map(
|
|
3152
3225
|
candidates.map((memory) => [memory.id, memory])
|
|
3153
3226
|
);
|
|
3154
|
-
const relevant = relevantIds.map((id) => candidatesById.get(id)).filter((memory) => memory !== void 0).slice(0, DEFAULT_RECALL_LIMIT);
|
|
3227
|
+
const relevant = recall.relevantIds.map((id) => candidatesById.get(id)).filter((memory) => memory !== void 0).slice(0, DEFAULT_RECALL_LIMIT);
|
|
3155
3228
|
const selected = selectPromptMemories(relevant);
|
|
3229
|
+
const costUsd = addUsd(embeddingCostUsd, recall.costUsd);
|
|
3230
|
+
await emitRecallOutcome({
|
|
3231
|
+
...costUsd !== void 0 ? { costUsd } : {},
|
|
3232
|
+
events: context.events,
|
|
3233
|
+
memories: selected.map(({ id }) => id)
|
|
3234
|
+
});
|
|
3156
3235
|
if (selected.length === 0) {
|
|
3157
3236
|
return void 0;
|
|
3158
3237
|
}
|
|
@@ -3231,6 +3310,15 @@ function formatPercent(value) {
|
|
|
3231
3310
|
style: "percent"
|
|
3232
3311
|
}).format(value);
|
|
3233
3312
|
}
|
|
3313
|
+
function formatUsd(value) {
|
|
3314
|
+
const maximumFractionDigits = value > 0 && value < 0.01 ? 4 : 2;
|
|
3315
|
+
return new Intl.NumberFormat("en-US", {
|
|
3316
|
+
currency: "USD",
|
|
3317
|
+
maximumFractionDigits,
|
|
3318
|
+
minimumFractionDigits: 2,
|
|
3319
|
+
style: "currency"
|
|
3320
|
+
}).format(value);
|
|
3321
|
+
}
|
|
3234
3322
|
async function buildMemoryOperationalReport(args) {
|
|
3235
3323
|
const active = and4(
|
|
3236
3324
|
isNull3(juniorMemoryMemories.archivedAtMs),
|
|
@@ -3265,6 +3353,11 @@ async function buildMemoryOperationalReport(args) {
|
|
|
3265
3353
|
const activeCount = counts?.active ?? 0;
|
|
3266
3354
|
const embeddedCount = counts?.embedded ?? 0;
|
|
3267
3355
|
const embeddingCoverage = activeCount === 0 ? 0 : embeddedCount / activeCount;
|
|
3356
|
+
const extractionThirtyDays = args.extractionDays.slice(-30);
|
|
3357
|
+
const extractionCostThirtyDays = extractionThirtyDays.reduce(
|
|
3358
|
+
(total, day) => total + day.costUsd,
|
|
3359
|
+
0
|
|
3360
|
+
);
|
|
3268
3361
|
return {
|
|
3269
3362
|
generatedAt: new Date(args.nowMs).toISOString(),
|
|
3270
3363
|
title: "Memory",
|
|
@@ -3274,6 +3367,10 @@ async function buildMemoryOperationalReport(args) {
|
|
|
3274
3367
|
tone: activeCount > 0 ? "good" : "neutral",
|
|
3275
3368
|
value: formatCount(activeCount)
|
|
3276
3369
|
},
|
|
3370
|
+
{
|
|
3371
|
+
label: "extraction cost \xB7 30d",
|
|
3372
|
+
value: formatUsd(extractionCostThirtyDays)
|
|
3373
|
+
},
|
|
3277
3374
|
{
|
|
3278
3375
|
label: "created \xB7 30d",
|
|
3279
3376
|
value: formatCount(counts?.createdThirtyDays ?? 0)
|
|
@@ -3293,6 +3390,19 @@ async function buildMemoryOperationalReport(args) {
|
|
|
3293
3390
|
}
|
|
3294
3391
|
],
|
|
3295
3392
|
widgets: [
|
|
3393
|
+
{
|
|
3394
|
+
categories: args.extractionDays.map((day) => ({
|
|
3395
|
+
id: day.date,
|
|
3396
|
+
label: day.date,
|
|
3397
|
+
values: { costUsd: day.costUsd }
|
|
3398
|
+
})),
|
|
3399
|
+
description: "Estimated model cost of passive memory extraction",
|
|
3400
|
+
id: "extraction-cost",
|
|
3401
|
+
series: [{ format: "usd", key: "costUsd", label: "Cost" }],
|
|
3402
|
+
timeRangeDays: [...WINDOWS],
|
|
3403
|
+
title: "Extraction cost",
|
|
3404
|
+
type: "bar_chart"
|
|
3405
|
+
},
|
|
3296
3406
|
{
|
|
3297
3407
|
categories: memoryDays.map((day) => ({
|
|
3298
3408
|
id: day.date,
|
|
@@ -3453,7 +3563,11 @@ function memoryPlugin(options = {}) {
|
|
|
3453
3563
|
},
|
|
3454
3564
|
model: modelId ? { structuredModelId: modelId } : { structuredModel: "default" },
|
|
3455
3565
|
packageName: "@sentry/junior-memory",
|
|
3456
|
-
conversationEvents: [
|
|
3566
|
+
conversationEvents: [
|
|
3567
|
+
memoriesCapturedEventV1,
|
|
3568
|
+
memoriesCapturedEvent,
|
|
3569
|
+
memoriesRecalledEvent
|
|
3570
|
+
],
|
|
3457
3571
|
cli: {
|
|
3458
3572
|
commands: [createMemoryCliCommand()]
|
|
3459
3573
|
},
|
|
@@ -3467,15 +3581,21 @@ function memoryPlugin(options = {}) {
|
|
|
3467
3581
|
userPages: [createMemoryUserPage()],
|
|
3468
3582
|
hooks: {
|
|
3469
3583
|
async operationalReport(ctx) {
|
|
3584
|
+
const extractionDays = await ctx.eventStats.costsByDay({
|
|
3585
|
+
days: 90,
|
|
3586
|
+
eventName: "memories_captured"
|
|
3587
|
+
});
|
|
3470
3588
|
return await buildMemoryOperationalReport({
|
|
3471
3589
|
db: ctx.db,
|
|
3590
|
+
extractionDays,
|
|
3472
3591
|
nowMs: ctx.nowMs
|
|
3473
3592
|
});
|
|
3474
3593
|
},
|
|
3475
3594
|
apiRoutes(ctx) {
|
|
3476
3595
|
return createMemoryApi({
|
|
3477
3596
|
actors: ctx.viewer.actors,
|
|
3478
|
-
db: ctx.db
|
|
3597
|
+
db: ctx.db,
|
|
3598
|
+
eventStats: ctx.eventStats
|
|
3479
3599
|
});
|
|
3480
3600
|
},
|
|
3481
3601
|
tools(ctx) {
|
|
@@ -3508,6 +3628,7 @@ function memoryPlugin(options = {}) {
|
|
|
3508
3628
|
...ctx.actor ? { actor: ctx.actor } : {},
|
|
3509
3629
|
db: ctx.db,
|
|
3510
3630
|
embedder: ctx.embedder,
|
|
3631
|
+
events: ctx.events,
|
|
3511
3632
|
log: ctx.log,
|
|
3512
3633
|
maxVectorDistance: recallMaxVectorDistance(options),
|
|
3513
3634
|
source: ctx.source,
|