feat: kitchen screen, scan dedup, gemini-3.5-flash-lite, seed grapes/beetroot/pea shoots

This commit is contained in:
Sven (AAMOS AI)
2026-08-16 20:13:49 +07:00
parent d098bf705e
commit c0a15bb1b5
20 changed files with 295 additions and 48 deletions
+6 -1
View File
@@ -266,7 +266,7 @@ export class GeminiAamosClient implements AamosClient {
constructor(config: GeminiAamosClientConfig) {
this.cfg = {
apiKey: config.apiKey,
model: config.model ?? "gemini-2.5-flash",
model: config.model ?? "gemini-3.5-flash-lite",
timeoutMs: config.timeoutMs ?? 60_000,
dailyBudgetUsd: config.dailyBudgetUsd ?? 0,
promptVersion: config.promptVersion ?? "gemini-fridge-v1",
@@ -856,6 +856,11 @@ Identifiera varje livsmedelsprodukt du ser. För varje produkt, svara med:
- sistaForbruk: sista förbrukningsdatum om det syns (YYYY-MM-DD), annars null
- konfidens: 0.01.0
Viktigt:
- Kvantitet: uppskatta den FAKTISKA synliga/kvarvarande mängden, inte tryckta förpackningsantal. Ett tomt eller öppnat paket ska ha låg kvantitet (nära 0), även om lådan säger t.ex. "24-pack". Är du osäker, gissa lågt och sänk konfidensen.
- Svenska mjölk- och mejerifärger: röd förpackning = standardmjölk 3%, grön = mellanmjölk 1,5%, blå = lättmjölk 0,5%. Använd färgen för att avgöra fetthalt.
- Hellre lägre konfidens än en exakt gissning när du är osäker.
Svara ENDAST med giltig JSON i exakt detta format:
{
"items": [...],
+2 -2
View File
@@ -67,7 +67,7 @@ describe("GeminiAamosClient", () => {
const budget = new MemoryBudgetStore();
const client = new GeminiAamosClient({
apiKey: "test-key",
model: "gemini-2.5-flash",
model: "gemini-3.5-flash-lite",
fetchImpl: makeFetch(JPEG_BYTES),
budgetStore: budget,
dailyBudgetUsd: 10,
@@ -82,7 +82,7 @@ describe("GeminiAamosClient", () => {
expect(result.status).toBe("ok");
expect(result.output).not.toBeNull();
expect(result.modelVersion).toBe("gemini-2.5-flash");
expect(result.modelVersion).toBe("gemini-3.5-flash-lite");
expect(result.costUsd).toBeGreaterThan(0);
expect(result.inputTokens).toBe(392);
expect(result.outputTokens).toBe(121);
@@ -978,6 +978,9 @@ export const SEED_INGREDIENTS: SeedIngredient[] = [
ing("lingonberry_jam", "Lingonsylt", "Lingonberry jam", "skafferi", { n: { kcal: 200, p: 0.3, k: 49, f: 0.2, s: 45 }, isVegan: true, isVegetarian: true }),
ing("prinskorv", "Prinskorv", "Cocktail sausage", "chark", { n: { kcal: 280, p: 11, k: 3, f: 25, mf: 9, salt: 2 }, isPork: true }),
ing("rye_flour", "Rågmjöl", "Rye flour", "skafferi", { n: { kcal: 325, p: 9, k: 60, f: 2, fib: 14 }, containsGluten: true, allergens: ["gluten"], isVegan: true, isVegetarian: true }),
ing("grape", "Vindruvor", "Grapes", "frukt", { n: { kcal: 69, p: 0.7, k: 18, f: 0.2, fib: 0.9, s: 15 }, isVegan: true, isVegetarian: true, aliases: ["druvor", "vindruva", "roda druvor", "röda druvor", "grona druvor", "gröna druvor", "drue druvor", "drue", "grapes"] }),
ing("beetroot", "Rödbetor", "Beetroot", "gronsaker", { n: { kcal: 43, p: 1.6, k: 10, f: 0.2, fib: 2.8, s: 7 }, isVegan: true, isVegetarian: true, aliases: ["rödbeta", "rodbeta", "rödbetor", "rodbetor", "inlagda rödbetor", "inlagda rodbetor", "skivad rödbeta", "skivade rödbetor", "beetroot", "beets", "red beets"] }),
ing("pea_shoots", "Ärtskott", "Pea shoots", "gronsaker", { n: { kcal: 42, p: 4, k: 4, f: 0.5, fib: 2 }, isVegan: true, isVegetarian: true, aliases: ["ärtskott", "artskott", "ertskott", "artiskott", "pea shoots"] })
];
export const SEED_INGREDIENT_IDS = new Set(SEED_INGREDIENTS.map((i) => i.id));
+6 -5
View File
@@ -24,7 +24,8 @@ export interface PlanDefinition {
googleProductId: string;
}
export const TRIAL_DAYS = 7;
export const TRIAL_DAYS = 14;
export const TRIAL_SCANS = 25;
export const PLAN_DEFINITIONS: Record<SubscriptionPlan, PlanDefinition> = {
free: {
@@ -33,7 +34,7 @@ export const PLAN_DEFINITIONS: Record<SubscriptionPlan, PlanDefinition> = {
priceMinorPerMonth: 0,
currency: "SEK",
maxHouseholdMembers: 1,
aiScansPerMonth: 10,
aiScansPerMonth: 5,
weekPlanning: false,
advancedNutrition: false,
communityPublish: false,
@@ -46,7 +47,7 @@ export const PLAN_DEFINITIONS: Record<SubscriptionPlan, PlanDefinition> = {
priceMinorPerMonth: 7900,
currency: "SEK",
maxHouseholdMembers: 3,
aiScansPerMonth: 300,
aiScansPerMonth: 100,
weekPlanning: true,
advancedNutrition: true,
communityPublish: true,
@@ -59,7 +60,7 @@ export const PLAN_DEFINITIONS: Record<SubscriptionPlan, PlanDefinition> = {
priceMinorPerMonth: 12900,
currency: "SEK",
maxHouseholdMembers: 6,
aiScansPerMonth: 600,
aiScansPerMonth: 200,
weekPlanning: true,
advancedNutrition: true,
communityPublish: true,
@@ -72,7 +73,7 @@ export const PLAN_DEFINITIONS: Record<SubscriptionPlan, PlanDefinition> = {
priceMinorPerMonth: 16900,
currency: "SEK",
maxHouseholdMembers: 12,
aiScansPerMonth: 1000,
aiScansPerMonth: 400,
weekPlanning: true,
advancedNutrition: true,
communityPublish: true,
+2 -1
View File
@@ -1,6 +1,7 @@
import {
PLAN_DEFINITIONS,
TRIAL_DAYS,
TRIAL_SCANS,
type Entitlements,
type SubscriptionPlan,
type SubscriptionStatus,
@@ -67,7 +68,7 @@ export function computeEntitlements(
plan: "family",
status: "trial",
maxHouseholdMembers: def.maxHouseholdMembers,
aiScansPerMonth: def.aiScansPerMonth,
aiScansPerMonth: TRIAL_SCANS,
aiScansUsedThisMonth: usage.aiScansUsedThisMonth,
weekPlanning: true,
advancedNutrition: true,