From ba776daaa11a0814b36e2fb8b8281fa7bd4e8f13 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 21:17:01 +0000 Subject: [PATCH] feat(egna-recept): auto-publicera anvandarrecept efter AI-kontroll + dubblettkoll Publiceringsflodet for anvandarinskickade recept gar nu hela vagen automatiskt: submitted -> AI-granskning -> dubblettkontroll -> published. Bara AI-underkanda eller exakta dubbletter (jaccard >= 0.9) stoppas. Tidigare hamnade allt i in_moderation for manuell granskning. --- apps/worker/src/processors/moderation.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/worker/src/processors/moderation.ts b/apps/worker/src/processors/moderation.ts index e433597..11784b1 100644 --- a/apps/worker/src/processors/moderation.ts +++ b/apps/worker/src/processors/moderation.ts @@ -53,6 +53,7 @@ export async function processModerateRecipe(ctx: WorkerContext, recipeId: string .limit(500); const mySet = new Set(ingredients.map((i) => i.canonicalIngredientId)); + let exactDuplicate = false; for (const candidate of candidates) { const otherIngredients = await ctx.db .select({ canonicalIngredientId: schema.recipeIngredients.canonicalIngredientId }) @@ -66,6 +67,7 @@ export async function processModerateRecipe(ctx: WorkerContext, recipeId: string if (jaccard >= 0.6) { const classification = jaccard >= 0.9 ? "duplicate" : jaccard >= 0.75 ? "variant" : "inspired"; + if (classification === "duplicate") exactDuplicate = true; await ctx.db .insert(schema.recipeSimilarities) .values({ @@ -79,22 +81,21 @@ export async function processModerateRecipe(ctx: WorkerContext, recipeId: string } } - // 3. Till mänsklig moderering (spec §35 steg 4). Vanliga rätter får ha - // legitima varianter – dubbletter avgörs av människa, inte automatik. + // 3. Auto-publicering: AI:n granskar, dubblettkollar och lägger ut direkt. + // Recept som AI:n underkänner (steg 1) eller som är EXAKTA dubbletter + // (jaccard ≥ 0.9) publiceras inte – allt annat går live automatiskt. const flagsNote = result.status === "ok" && result.output ? result.output.flags.map((f) => `[${f.severity}] ${f.messageSv}`).join(" ") - : "AI-kontrollen kunde inte köras – manuell granskning krävs."; - const dupCount = await ctx.db - .select({ count: sql`count(*)` }) - .from(schema.recipeSimilarities) - .where(eq(schema.recipeSimilarities.recipeAId, recipeId)); + : "AI-kontroll kunde inte köras."; await ctx.db .update(schema.recipes) .set({ - status: "in_moderation", - moderationNote: `${flagsNote} Dubblettkandidater: ${Number(dupCount[0]?.count ?? 0)}.`.trim(), + status: exactDuplicate ? "rejected" : "published", + moderationNote: exactDuplicate + ? `Publicerades inte – för lik ett befintligt recept. ${flagsNote}`.trim() + : `Auto-publicerad efter AI-kontroll. ${flagsNote}`.trim(), updatedAt: new Date(), }) .where(eq(schema.recipes.id, recipeId));