ci: trigga på master + formatfix inför Gitea Actions
CI / Typecheck, test & build (push) Failing after 2s

This commit is contained in:
Sven (AAMOS AI)
2026-08-13 17:25:14 +07:00
parent 61d60931ad
commit c32a7e33c7
141 changed files with 40555 additions and 45210 deletions
+126 -12
View File
@@ -212,16 +212,27 @@ export const EVAL_CASES: EvalCase[] = [
input: {
scope: "user",
scopeId: "user-1",
events: [{ id: "evt-1", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} }],
events: [
{ id: "evt-1", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} },
],
existingMemoryKeys: [],
},
verify(output) {
const o = output as TaskOutput<"UPDATE_USER_MEMORY">;
const eventIds = new Set(["evt-1"]);
return [
check("alla minnen har kända sourceEventIds", o.memoryUpdates.every((u) => u.sourceEventIds.every((id) => eventIds.has(id)))),
check("inga minnen utan sourceEventIds", o.memoryUpdates.every((u) => u.sourceEventIds.length > 0)),
check("origin är observed eller ai_inferred", o.memoryUpdates.every((u) => u.origin === "observed" || u.origin === "ai_inferred")),
check(
"alla minnen har kända sourceEventIds",
o.memoryUpdates.every((u) => u.sourceEventIds.every((id) => eventIds.has(id))),
),
check(
"inga minnen utan sourceEventIds",
o.memoryUpdates.every((u) => u.sourceEventIds.length > 0),
),
check(
"origin är observed eller ai_inferred",
o.memoryUpdates.every((u) => u.origin === "observed" || u.origin === "ai_inferred"),
),
];
},
},
@@ -232,14 +243,19 @@ export const EVAL_CASES: EvalCase[] = [
input: {
scope: "user",
scopeId: "user-1",
events: [{ id: "evt-sv", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} }],
events: [
{ id: "evt-sv", type: "recipe_cooked", occurredAt: "2026-08-01T12:00:00Z", payload: {} },
],
existingMemoryKeys: [],
},
verify(output) {
const o = output as TaskOutput<"UPDATE_USER_MEMORY">;
return [
check("minst ett minne returneras", o.memoryUpdates.length >= 1),
check("summarySv är på svenska", o.memoryUpdates.every((u) => /[åäöÅÄÖ]/.test(u.summarySv) || u.summarySv.length > 0)),
check(
"summarySv är på svenska",
o.memoryUpdates.every((u) => /[åäöÅÄÖ]/.test(u.summarySv) || u.summarySv.length > 0),
),
];
},
},
@@ -253,14 +269,40 @@ export const EVAL_CASES: EvalCase[] = [
cuisine: "swedish",
tags: [],
totalTimeMinutes: 30,
nutritionPerPortion: { kcal: 550, proteinG: 45, carbsG: 50, fatG: 18, saturatedFatG: 6, fiberG: 6, sugarG: 4, saltG: 1.5 },
nutritionPerPortion: {
kcal: 550,
proteinG: 45,
carbsG: 50,
fatG: 18,
saturatedFatG: 6,
fiberG: 6,
sugarG: 4,
saltG: 1.5,
},
estimatedCostMinorPerPortion: 2200,
ratingAverage: null,
ratingCount: 0,
peakSeasons: ["summer"],
holidayTags: [],
spiceLevel: 1,
coverage: { coverage: 0.85, matches: [], missing: [], expiringUsed: [{ canonicalIngredientId: "chicken", displayNameSv: "kycklingen", required: 400, unit: "GRAM", availableInUnit: 500, covered: true, optional: false, mostUrgentDaysLeft: 2, usesExpiringItem: true }] },
coverage: {
coverage: 0.85,
matches: [],
missing: [],
expiringUsed: [
{
canonicalIngredientId: "chicken",
displayNameSv: "kycklingen",
required: 400,
unit: "GRAM",
availableInUnit: 500,
covered: true,
optional: false,
mostUrgentDaysLeft: 2,
usesExpiringItem: true,
},
],
},
daysSinceLastCooked: 14,
householdRating: 4.8,
ingredientIds: ["chicken"],
@@ -275,12 +317,84 @@ export const EVAL_CASES: EvalCase[] = [
remainingProteinG: 60,
remainingKcal: 800,
personalizationEnabled: true,
memoryItems: [{ id: "m1", userId: "u1", kind: "structured_fact", key: "favorite_cuisine_swedish", summarySv: "Gillar svensk mat", value: { favoriteCuisine: "swedish" }, origin: "user_stated", confidence: 1, verifiedByUser: true, paused: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }],
memoryItems: [
{
id: "m1",
userId: "u1",
kind: "structured_fact",
key: "favorite_cuisine_swedish",
summarySv: "Gillar svensk mat",
value: { favoriteCuisine: "swedish" },
origin: "user_stated",
confidence: 1,
verifiedByUser: true,
paused: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
],
tasteSignals: [],
cookingAssumptions: [{ canonicalIngredientId: "chicken", averageEatenPortions: 4, averageLeftoverPortions: 0.5, observationCount: 5 }],
cookingAssumptions: [
{
canonicalIngredientId: "chicken",
averageEatenPortions: 4,
averageLeftoverPortions: 0.5,
observationCount: 5,
},
],
};
const whySv = buildWhy(candidate, context, { coverage: 0.85, expiry: 0.8, nutritionFit: 0.9, taste: 0.8, rating: 0.9, season: 1, holiday: 0, time: 1, budget: 0.6, variety: 1, weather: 0.5, craving: 0.5, memoryFit: 1, tasteFit: 0, cookingAssumptionFit: 0.8 }, [{ key: "favoriteCuisine", args: { cuisine: "svensk" } }, { key: "usesStapleYouFinish", args: { ingredient: "kyckling" } }], "sv-SE");
const whyEn = buildWhy(candidate, context, { coverage: 0.85, expiry: 0.8, nutritionFit: 0.9, taste: 0.8, rating: 0.9, season: 1, holiday: 0, time: 1, budget: 0.6, variety: 1, weather: 0.5, craving: 0.5, memoryFit: 1, tasteFit: 0, cookingAssumptionFit: 0.8 }, [{ key: "favoriteCuisine", args: { cuisine: "Swedish" } }, { key: "usesStapleYouFinish", args: { ingredient: "chicken" } }], "en-US");
const whySv = buildWhy(
candidate,
context,
{
coverage: 0.85,
expiry: 0.8,
nutritionFit: 0.9,
taste: 0.8,
rating: 0.9,
season: 1,
holiday: 0,
time: 1,
budget: 0.6,
variety: 1,
weather: 0.5,
craving: 0.5,
memoryFit: 1,
tasteFit: 0,
cookingAssumptionFit: 0.8,
},
[
{ key: "favoriteCuisine", args: { cuisine: "svensk" } },
{ key: "usesStapleYouFinish", args: { ingredient: "kyckling" } },
],
"sv-SE",
);
const whyEn = buildWhy(
candidate,
context,
{
coverage: 0.85,
expiry: 0.8,
nutritionFit: 0.9,
taste: 0.8,
rating: 0.9,
season: 1,
holiday: 0,
time: 1,
budget: 0.6,
variety: 1,
weather: 0.5,
craving: 0.5,
memoryFit: 1,
tasteFit: 0,
cookingAssumptionFit: 0.8,
},
[
{ key: "favoriteCuisine", args: { cuisine: "Swedish" } },
{ key: "usesStapleYouFinish", args: { ingredient: "chicken" } },
],
"en-US",
);
return [
check("whySv innehåller ingen förbjuden copy", !containsForbiddenCopy(whySv)),
check("whyEn innehåller ingen förbjuden copy", !containsForbiddenCopy(whyEn)),
+27 -13
View File
@@ -36,14 +36,16 @@ interface ScanEvalCase {
imageUrls: string[];
locationType: "fridge" | "pantry";
marketLocale: string;
checks: (items: Array<{
detectedName: string;
brand: string | null;
estimatedQuantity: number | null;
unit: string | null;
confidence: number;
requiresConfirmation: boolean;
}>) => { name: string; passed: boolean }[];
checks: (
items: Array<{
detectedName: string;
brand: string | null;
estimatedQuantity: number | null;
unit: string | null;
confidence: number;
requiresConfirmation: boolean;
}>,
) => { name: string; passed: boolean }[];
}
const CASES: ScanEvalCase[] = [
@@ -57,7 +59,10 @@ const CASES: ScanEvalCase[] = [
return [
{ name: "hittade mjölkprodukt", passed: !!milk },
{ name: "konfidens > 0.5", passed: !!milk && milk.confidence > 0.5 },
{ name: "kräver bekräftelse om låg konfidens", passed: !!milk && (milk.confidence >= 0.92 || milk.requiresConfirmation) },
{
name: "kräver bekräftelse om låg konfidens",
passed: !!milk && (milk.confidence >= 0.92 || milk.requiresConfirmation),
},
];
},
},
@@ -71,7 +76,10 @@ const CASES: ScanEvalCase[] = [
return [
{ name: "hittade konserverad produkt", passed: !!found },
{ name: "konfidens > 0.5", passed: !!found && found.confidence > 0.5 },
{ name: "minst ett item med confidence > 0.7", passed: items.some((i) => i.confidence > 0.7) },
{
name: "minst ett item med confidence > 0.7",
passed: items.some((i) => i.confidence > 0.7),
},
];
},
},
@@ -101,7 +109,9 @@ async function main() {
apiKey,
model: process.env.GEMINI_MODEL,
timeoutMs: process.env.GEMINI_TIMEOUT_MS ? Number(process.env.GEMINI_TIMEOUT_MS) : 60_000,
dailyBudgetUsd: process.env.GEMINI_DAILY_BUDGET_USD ? Number(process.env.GEMINI_DAILY_BUDGET_USD) : 10,
dailyBudgetUsd: process.env.GEMINI_DAILY_BUDGET_USD
? Number(process.env.GEMINI_DAILY_BUDGET_USD)
: 10,
budgetStore: new SilentBudgetStore(),
});
@@ -153,8 +163,12 @@ async function main() {
if (error) console.log(` fel: ${error}`);
}
console.log(`\n[eval:scan] ${CASES.length} fall, ${totalChecks} kontroller, ${failedChecks} fallerade.`);
console.log(`[eval:scan] Total latens: ${totalLatencyMs} ms, total kostnad: ~$${totalCostUsd.toFixed(6)}`);
console.log(
`\n[eval:scan] ${CASES.length} fall, ${totalChecks} kontroller, ${failedChecks} fallerade.`,
);
console.log(
`[eval:scan] Total latens: ${totalLatencyMs} ms, total kostnad: ~$${totalCostUsd.toFixed(6)}`,
);
if (failedChecks > 0) {
console.log("[eval:scan] UNDERKÄND åtgärda innan Skiva 1 går till fälttest.");
+1 -5
View File
@@ -9,11 +9,7 @@
import { randomUUID } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import {
PutObjectCommand,
S3Client,
type S3ClientConfig,
} from "@aws-sdk/client-s3";
import { PutObjectCommand, S3Client, type S3ClientConfig } from "@aws-sdk/client-s3";
import type { AamosTaskType } from "@app/ai-contracts";
export interface CaptureConsentFlags {
+6 -2
View File
@@ -338,14 +338,18 @@ export async function processMemorySync(ctx: WorkerContext): Promise<number> {
proposal.sourceEventIds.every((id) => eventIdSet.has(id));
if (!hasEventSupport) {
// eslint-disable-next-line no-console
console.log(`UPDATE_USER_MEMORY: avvisar ${proposal.key} för ${userId}: saknar event-stöd.`);
console.log(
`UPDATE_USER_MEMORY: avvisar ${proposal.key} för ${userId}: saknar event-stöd.`,
);
continue;
}
// R1: ai_inferred ska ha låg konfidens.
if (proposal.origin === "ai_inferred" && proposal.confidence > AI_INFERRED_MAX_CONFIDENCE) {
// eslint-disable-next-line no-console
console.log(`UPDATE_USER_MEMORY: avvisar ${proposal.key} för ${userId}: ai_inferred med för hög confidence.`);
console.log(
`UPDATE_USER_MEMORY: avvisar ${proposal.key} för ${userId}: ai_inferred med för hög confidence.`,
);
continue;
}
-1
View File
@@ -334,7 +334,6 @@ function tokenize(text: string): string[] {
.filter((t) => t.length > 1);
}
function classifyScanError(error?: string | null): string {
if (!error) return "unknown";
const lower = error.toLowerCase();