Fas 3 steg 3b: minimala efterfrågor + hushållsantagandeprofiler per ingrediens

This commit is contained in:
Sven (AAMOS AI)
2026-08-07 03:56:41 +07:00
parent 44f6385dce
commit e46281b9b5
24 changed files with 10130 additions and 5 deletions
@@ -0,0 +1,64 @@
/**
* Antagandeprofiler för matlagning (Fas 3 §6.2).
* Rensa, deterministiska funktioner för att uppdatera per-hushålls-per-ingrediens-profiler
* utifrån faktiska svar efter en cooking session.
*/
export interface ProfileUpdateInput {
householdId: string;
canonicalIngredientId: string;
plannedPortions: number;
actualPortionsEaten: number;
leftoverEstimatePortions: number;
sessionId: string;
date: string;
}
export interface ExistingProfile {
averageEatenPortions: number | null;
averageLeftoverPortions: number | null;
observationCount: number;
lastSessionAnswers?: Array<{ sessionId: string; eaten: number; leftovers: number; date: string }> | null;
}
/**
* Beräkna nya profilvärden med exponentiellt glidande medelvärde.
* alpha = 1 / (observationCount + 1) ger jämn viktning över tid.
*/
export function updateCookingAssumptionProfile(
input: ProfileUpdateInput,
existing: ExistingProfile,
): {
averageEatenPortions: number;
averageLeftoverPortions: number;
observationCount: number;
lastSessionAnswers: Array<{ sessionId: string; eaten: number; leftovers: number; date: string }>;
} {
const eaten = clamp(input.actualPortionsEaten, 0, input.plannedPortions);
const leftovers = clamp(input.leftoverEstimatePortions, 0, input.plannedPortions - eaten);
const prevEaten = existing.averageEatenPortions ?? eaten;
const prevLeftovers = existing.averageLeftoverPortions ?? leftovers;
const prevCount = existing.observationCount ?? 0;
const alpha = 1 / (prevCount + 1);
const averageEatenPortions = round2(prevEaten + alpha * (eaten - prevEaten));
const averageLeftoverPortions = round2(prevLeftovers + alpha * (leftovers - prevLeftovers));
const observationCount = prevCount + 1;
const history = existing.lastSessionAnswers ?? [];
const lastSessionAnswers = [
{ sessionId: input.sessionId, eaten, leftovers, date: input.date },
...history,
].slice(0, 10);
return { averageEatenPortions, averageLeftoverPortions, observationCount, lastSessionAnswers };
}
function clamp(n: number, min: number, max: number): number {
return Math.max(min, Math.min(max, n));
}
function round2(n: number): number {
return Math.round(n * 100) / 100;
}
+1
View File
@@ -6,3 +6,4 @@ export * from "./forecast.js";
export * from "./trust.js";
export * from "./reconciliation.js";
export * from "./scan-diff.js";
export * from "./cooking-profiles.js";