feat(worker): S3 3b härda UPDATE_USER_MEMORY — budget, kostnad, anti-påhitt, GDPR-regression
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { AamosClient } from "@app/ai-contracts";
|
||||
import { TASK_CONTRACTS, type AamosClient } from "@app/ai-contracts";
|
||||
import type { MemoryItem, MemoryKind, SignalOrigin } from "@app/shared-types";
|
||||
|
||||
/**
|
||||
@@ -21,12 +21,14 @@ export interface MemoryUpdateProposal {
|
||||
origin: Exclude<SignalOrigin, "user_stated">;
|
||||
confidence: number;
|
||||
expiresAt?: string | null;
|
||||
/** Händelse-ID:n som stödjer förslaget (R1: grundat, aldrig påhittat). */
|
||||
sourceEventIds: string[];
|
||||
}
|
||||
|
||||
export interface MemorySyncInput {
|
||||
scope: "user" | "household";
|
||||
scopeId: string;
|
||||
events: Array<{ type: string; occurredAt: string; payload: unknown }>;
|
||||
events: Array<{ id: string; type: string; occurredAt: string; payload: unknown }>;
|
||||
existingMemoryKeys: string[];
|
||||
consentFlags: {
|
||||
personalization: boolean;
|
||||
@@ -38,15 +40,23 @@ export interface MemorySyncInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Kör minnesuppdatering via AAMOS. Returnerar förslag – anroparen (workern)
|
||||
* persisterar dem i memory_items och publicerar MEMORY_UPDATED-event.
|
||||
* Kör minnesuppdatering via AAMOS. Returnerar granskade förslag + faktisk
|
||||
* token/kostnadsanvändning så att workern kan bokföra i ai_usage_counters.
|
||||
* Utan personaliseringssamtycke körs ingenting.
|
||||
*
|
||||
* R1 (grundat, aldrig påhittat): AAMOS får explicit instruktion om att aldrig
|
||||
* fabricera minnen; svaret valideras mot Zod-kontraktet.
|
||||
*/
|
||||
export async function deriveMemoryUpdates(
|
||||
aamos: AamosClient,
|
||||
input: MemorySyncInput,
|
||||
): Promise<MemoryUpdateProposal[]> {
|
||||
if (!input.consentFlags.personalization) return [];
|
||||
): Promise<{
|
||||
proposals: MemoryUpdateProposal[];
|
||||
usage: { costUsd: number; inputTokens: number; outputTokens: number } | null;
|
||||
}> {
|
||||
if (!input.consentFlags.personalization) {
|
||||
return { proposals: [], usage: null };
|
||||
}
|
||||
|
||||
const result = await aamos.runTask(
|
||||
"UPDATE_USER_MEMORY",
|
||||
@@ -59,21 +69,47 @@ export async function deriveMemoryUpdates(
|
||||
{
|
||||
subjectRef: pseudonymize(input.scopeId),
|
||||
consentFlags: input.consentFlags,
|
||||
systemInstruction:
|
||||
"Du får ENDAST sammanfatta faktiska events. Hitta ALDRIG på minnen, preferenser eller mönster som inte har explicit stöd i events. Varje förslag måste referera till ett eller flera events via sourceEventIds. ai_inferred-förslag ska ha låg konfidens.",
|
||||
...(input.localeContext ? { localeContext: input.localeContext } : {}),
|
||||
...(input.correlationId ? { correlationId: input.correlationId } : {}),
|
||||
},
|
||||
);
|
||||
|
||||
if (result.status !== "ok" || !result.output) return [];
|
||||
return result.output.memoryUpdates.map((u) => ({
|
||||
key: u.key,
|
||||
kind: u.kind,
|
||||
summarySv: u.summarySv,
|
||||
value: u.value,
|
||||
origin: u.origin,
|
||||
confidence: u.confidence,
|
||||
expiresAt: u.expiresAt,
|
||||
}));
|
||||
const usage =
|
||||
result.status === "ok" && result.output
|
||||
? {
|
||||
costUsd: result.costUsd ?? 0,
|
||||
inputTokens: result.inputTokens ?? 0,
|
||||
outputTokens: result.outputTokens ?? 0,
|
||||
}
|
||||
: null;
|
||||
|
||||
if (result.status !== "ok" || !result.output) {
|
||||
return { proposals: [], usage };
|
||||
}
|
||||
|
||||
// Extra Zod-validering: ett brutet kontrakt ska aldrig nå användardata.
|
||||
const parsed = TASK_CONTRACTS["UPDATE_USER_MEMORY"].output.safeParse(result.output);
|
||||
if (!parsed.success) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("UPDATE_USER_MEMORY output bröt mot kontraktet:", parsed.error.message);
|
||||
return { proposals: [], usage };
|
||||
}
|
||||
|
||||
return {
|
||||
proposals: parsed.data.memoryUpdates.map((u) => ({
|
||||
key: u.key,
|
||||
kind: u.kind,
|
||||
summarySv: u.summarySv,
|
||||
value: u.value,
|
||||
origin: u.origin,
|
||||
confidence: u.confidence,
|
||||
expiresAt: u.expiresAt,
|
||||
sourceEventIds: u.sourceEventIds,
|
||||
})),
|
||||
usage,
|
||||
};
|
||||
}
|
||||
|
||||
export interface MemoryOverviewItem extends MemoryItem {
|
||||
|
||||
Reference in New Issue
Block a user