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
@@ -1,8 +1,4 @@
import type {
ProvenanceEntry,
RecommendationCandidate,
RecommendationContext,
} from "./types.js";
import type { ProvenanceEntry, RecommendationCandidate, RecommendationContext } from "./types.js";
import { renderProvenance } from "./provenance-templates.js";
import type { LanguageTag } from "./provenance-templates.js";
@@ -182,7 +178,8 @@ const WHY_TEMPLATES: Record<LanguageTag, WhyTemplates> = {
nl: {
coverage100: "Jullie hebben alle ingrediënten thuis.",
coveragePct: "Jullie hebben {{pct}} % van de ingrediënten thuis.",
coverageLow: "Jullie hebben {{pct}} % van de ingrediënten; de rest komt op het boodschappenlijstje.",
coverageLow:
"Jullie hebben {{pct}} % van de ingrediënten; de rest komt op het boodschappenlijstje.",
expiringToday: "{{ingredient}} moet vandaag gebruikt worden.",
expiringTomorrow: "{{ingredient}} moet uiterlijk morgen gebruikt worden.",
expiringDays: "{{ingredient}} moet binnen {{days}} dagen gebruikt worden.",
@@ -7,23 +7,9 @@ import type { ProvenanceEntry } from "./types.js";
*/
export type LanguageTag =
| "sv"
| "en"
| "es"
| "it"
| "de"
| "fr"
| "da"
| "nb"
| "fi"
| "nl"
| "pl"
| "pt";
"sv" | "en" | "es" | "it" | "de" | "fr" | "da" | "nb" | "fi" | "nl" | "pl" | "pt";
const TEMPLATES: Record<
string,
Record<LanguageTag, string>
> = {
const TEMPLATES: Record<string, Record<LanguageTag, string>> = {
favoriteCuisine: {
sv: "För att du har berättat att du gillar {{cuisine}} mat.",
en: "Because you said you like {{cuisine}} food.",
@@ -124,10 +110,7 @@ const TEMPLATES: Record<
},
};
export function renderProvenance(
entries: ProvenanceEntry[],
languageTag: string,
): string {
export function renderProvenance(entries: ProvenanceEntry[], languageTag: string): string {
const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag;
const parts: string[] = [];
for (const entry of entries) {
@@ -142,10 +125,7 @@ export function renderProvenance(
return parts.join(" ");
}
export function renderProvenanceList(
entries: ProvenanceEntry[],
languageTag: string,
): string[] {
export function renderProvenanceList(entries: ProvenanceEntry[], languageTag: string): string[] {
const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag;
return entries
.map((entry) => {
@@ -230,7 +230,12 @@ function memoryFit(candidate: RecommendationCandidate, ctx: RecommendationContex
// Favoritkök
if (memory.kind === "structured_fact" && value.favoriteCuisine === candidate.cuisine) {
const weight = memory.verifiedByUser || memory.origin === "user_stated" ? 1 : memory.origin === "observed" ? 0.7 : 0.4;
const weight =
memory.verifiedByUser || memory.origin === "user_stated"
? 1
: memory.origin === "observed"
? 0.7
: 0.4;
score = Math.max(score, weight);
if (weight >= 0.7) {
provenance.push({
@@ -454,13 +454,29 @@ describe("S4 rekommendationsvyer", () => {
});
// default utan samtycke = depersonalize(DEFAULT_WEIGHTS) == NON_PERSONALIZED_WEIGHTS
const defaultRanked = rankAll([pantry, healthy, tasty], nonPersonalCtx, depersonalize(viewWeights("default")));
const nonPersonalDefaultRanked = rankAll([pantry, healthy, tasty], nonPersonalCtx, NON_PERSONALIZED_WEIGHTS);
expect(defaultRanked.map((r) => r.recipeId)).toEqual(nonPersonalDefaultRanked.map((r) => r.recipeId));
const defaultRanked = rankAll(
[pantry, healthy, tasty],
nonPersonalCtx,
depersonalize(viewWeights("default")),
);
const nonPersonalDefaultRanked = rankAll(
[pantry, healthy, tasty],
nonPersonalCtx,
NON_PERSONALIZED_WEIGHTS,
);
expect(defaultRanked.map((r) => r.recipeId)).toEqual(
nonPersonalDefaultRanked.map((r) => r.recipeId),
);
// vyerna är fortfarande skilda från default även utan samtycke
expect(rankAll([tasty, healthy, pantry], nonPersonalCtx, depersonalize(viewWeights("pantry")))[0]?.recipeId).toBe("pantry");
expect(rankAll([tasty, pantry, healthy], nonPersonalCtx, depersonalize(viewWeights("health")))[0]?.recipeId).toBe("healthy");
expect(
rankAll([tasty, healthy, pantry], nonPersonalCtx, depersonalize(viewWeights("pantry")))[0]
?.recipeId,
).toBe("pantry");
expect(
rankAll([tasty, pantry, healthy], nonPersonalCtx, depersonalize(viewWeights("health")))[0]
?.recipeId,
).toBe("healthy");
// taste-vyn honoreras men personliga axlar är nollade
const tasteWeights = depersonalize(viewWeights("taste"));
@@ -8,20 +8,7 @@ import {
type ProvenanceEntry,
} from "../src/index.js";
const EXPECTED_LANGS = [
"sv",
"en",
"es",
"it",
"de",
"fr",
"da",
"nb",
"fi",
"nl",
"pl",
"pt",
];
const EXPECTED_LANGS = ["sv", "en", "es", "it", "de", "fr", "da", "nb", "fi", "nl", "pl", "pt"];
const fullCoverage = { coverage: 1, matches: [], missing: [], expiringUsed: [] };
const nutrition = {
@@ -71,7 +58,9 @@ describe("S6 i18n-paritet", () => {
const langsByTemplate = provenanceTemplateLanguages();
expect(Object.keys(langsByTemplate).length).toBeGreaterThan(0);
for (const [key, langs] of Object.entries(langsByTemplate)) {
expect(langs.length, `mall ${key} har dubbletter eller saknar språk`).toBe(EXPECTED_LANGS.length);
expect(langs.length, `mall ${key} har dubbletter eller saknar språk`).toBe(
EXPECTED_LANGS.length,
);
expect(new Set(langs).size, `mall ${key} har dubbletter`).toBe(EXPECTED_LANGS.length);
for (const lang of EXPECTED_LANGS) {
expect(langs).toContain(lang);
@@ -89,11 +78,31 @@ describe("S6 i18n-paritet", () => {
});
it("buildWhy renderar på rätt språk för varje supporterat språk", () => {
const provenance: ProvenanceEntry[] = [
{ key: "favoriteCuisine", args: { cuisine: "svensk" } },
];
const provenance: ProvenanceEntry[] = [{ key: "favoriteCuisine", args: { cuisine: "svensk" } }];
for (const lang of EXPECTED_LANGS) {
const why = buildWhy(candidate(), { ...ctx, personalizationEnabled: true }, { coverage: 1, expiry: 0, nutritionFit: 0.9, taste: 0.5, rating: 0.5, season: 0, holiday: 0, time: 0, budget: 0, variety: 0.5, weather: 0.5, craving: 0.5, memoryFit: 1, tasteFit: 0, cookingAssumptionFit: 0 }, provenance, `${lang}-XX`);
const why = buildWhy(
candidate(),
{ ...ctx, personalizationEnabled: true },
{
coverage: 1,
expiry: 0,
nutritionFit: 0.9,
taste: 0.5,
rating: 0.5,
season: 0,
holiday: 0,
time: 0,
budget: 0,
variety: 0.5,
weather: 0.5,
craving: 0.5,
memoryFit: 1,
tasteFit: 0,
cookingAssumptionFit: 0,
},
provenance,
`${lang}-XX`,
);
expect(why.length, `tom why för ${lang}`).toBeGreaterThan(0);
// Ingen why får falla tillbaka på svenska fallback om språket finns.
if (lang !== "sv") {