chore: remove archived AI features and microservice-todo documentation

This commit is contained in:
Nils-Johan Gynther
2026-04-23 21:50:52 +02:00
parent 9028c79215
commit 2781b29f5a
7 changed files with 0 additions and 175 deletions
@@ -1 +0,0 @@
Flytta manuellt mappen recipe-document-converter till _archive/recipe-document-converter. Verktyget kan inte flytta kataloger automatiskt i denna miljö.
+62
View File
@@ -0,0 +1,62 @@
# AI-funktioner i Recipe-App: Premium-funktioner och rekommenderade modeller
> Se [README.md](README.md) för funktionsöversikt, [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk arkitektur och [NEXT_STEPS.md](NEXT_STEPS.md) för prioriterade nästa steg.
Detta dokument beskriver de AI-funktioner som implementeras eller planeras som **premium-funktioner** i **recipe-app**. Varje funktion är kopplad till en rekommenderad Mistral-modell, med fokus på att använda de enklaste och mest kostnadseffektiva alternativen.
---
## Översikt över AI-funktioner
| Funktion | Beskrivning | Modell | Status |
|---|---|---|---|
| **Automatisk kategorisering** | AI kategoriserar produkter baserat på namn mot systemets kategoriträd. Admins kör bulk-kategorisering; premium-användare får förslag per produkt. | `mistral-small-2603` | ✅ Klart |
| **Kvittoimport — kategorisuggestion** | Ej matchade kvittorader får ett AI-kategoriförslag för premium-användare, visas som ledtråd i gränssnittet. | `mistral-small-2603` | ✅ Klart |
| **Receptförslag utifrån hemmalager** | AI analyserar användarens inventory och föreslår recept baserat på tillgängliga ingredienser. | `mistral-small-2603` | ❌ Planerad |
| **Veckoplanering med AI** | AI genererar en veckoplan baserat på inventory, recept och användarpreferenser. | `mistral-small-2603` | ❌ Planerad |
| **Smart inköpslista** | AI skapar en inköpslista baserat på saknade ingredienser och historisk förbrukning. | `mistral-small-2603` | ❌ Planerad |
| **"Vad ska jag laga idag?"** | AI ger snabba receptförslag baserat på vad användaren har hemma. | `mistral-small-2603` | ❌ Planerad |
| **Enhetskonvertering** | AI hjälper till att konvertera enheter (t.ex. gram till msk) och hanterar osäkerheter. | `mistral-small-2603` | ❌ Planerad |
| **AI-assisterad lageravräkning** | AI hjälper till att dra av rätt mängder från inventory när ett recept lagas. | `mistral-small-2603` | ❌ Planerad |
| **Personliga matlagningsråd** | AI ger personliga tips baserat på användarens matlagningshistorik och inventory. | `mistral-small-2603` | ❌ Planerad |
| **AI-assisterad import av PDF/länkar** | AI extraherar recept och prisdata från PDF-filer och länkar för att underlätta importen. | `mistral-small-2603` | ❌ Planerad |
| **Kostnadseffektiv inköpslista** | AI skapar en kostnadseffektiv inköpslista baserat på inventory och aktuella butikspriser. | `mistral-small-2603` | ❌ Planerad |
---
## Bakgrund och motivering
### Varför dessa funktioner?
- **Receptförslag utifrån hemmalager** och **veckoplanering** är centrala för att hjälpa användare att minska matsvinn och spara tid.
- **Smart inköpslista** och **kostnadseffektiv inköpslista** är viktiga för att optimera användarens matbudget.
- **AI-assisterad import av PDF/länkar** underlättar för användare att lägga in recept och prisdata utan manuellt arbete.
### Varför dessa modeller?
- **`mistral-small-2603`** används för de flesta funktioner eftersom den är kostnadseffektiv och klarar av strukturerad data och logik.
- **`mistral-tiny-2603`** används för enklare, snabbare uppgifter som **"Vad ska jag laga idag?"** och **personliga matlagningsråd**.
- **`labs-leanstral-2603`** används för **enhetskonvertering** eftersom den är optimerad för kod och logik.
---
## Tekniska överväganden
### Integration med befintlig arkitektur
- **Backend**: AI-funktionerna kommer att implementeras som separata tjänster i NestJS, med egna endpoints för varje funktion.
- **Frontend**: Resultaten från AI-funktionerna kommer att visas i Next.js-gränssnittet, med tydlig feedback till användaren.
- **Databas**: Resultat och användardata lagras i MariaDB via Prisma ORM.
### Säkerhet och validering
- **Validering**: AI-output valideras alltid mot strukturerade scheman (t.ex. med Zod) för att säkerställa datakvalitet.
- **Säkerhet**: Känslig data krypteras enligt den hybridlösning med PGP och AES som vi tidigare diskuterat.
---
## Nästa steg
1. **Receptförslag** — "Vad ska jag laga idag?" är nästa naturliga premium-funktion; bygger direkt på inventory och recept som redan finns i systemet.
2. **Veckoplanering med AI** — kräver att receptförslag fungerar; planera mot kampanjpriser kräver extern datakälla.
3. **Validering av AI-output** — säkerställ att AI-svar alltid valideras mot strukturerade scheman (t.ex. Zod) för datakvalitet.
---
@@ -1,13 +0,0 @@
{
"name": "recipe-document-converter",
"version": "1.0.0",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc"
},
"devDependencies": {
"typescript": "^5.4.5"
}
}
@@ -1,2 +0,0 @@
export { parseRecipeMarkdown } from './parser';
export type { ParsedIngredient, ParsedRecipe } from './parser';
@@ -1,146 +0,0 @@
export interface ParsedIngredient {
rawName: string;
quantity: number;
unit: string;
note: string | null;
}
export interface ParsedRecipe {
name: string;
description: string;
instructions: string;
ingredients: ParsedIngredient[];
}
/**
* Parsar ett recept i Markdown-format och extraherar namn, beskrivning,
* instruktioner och ingredienser.
*
* Förväntat format:
* # Receptnamn
* Beskrivning (valfritt stycke efter titeln)
*
* ## Ingredienser
* - 400 g kycklingfilé
* - 2 dl grädde (eller crème fraiche)
*
* ## Instruktioner
* 1. Stek kycklingen …
*/
export function parseRecipeMarkdown(markdown: string): ParsedRecipe {
const lines = markdown.split('\n');
let name = '';
let description = '';
let instructions = '';
const ingredients: ParsedIngredient[] = [];
let currentSection: 'none' | 'description' | 'ingredients' | 'instructions' = 'none';
const descriptionLines: string[] = [];
const instructionLines: string[] = [];
for (const line of lines) {
const trimmed = line.trim();
// H1 — receptnamn
if (/^#\s+/.test(trimmed) && !trimmed.startsWith('##')) {
name = trimmed.replace(/^#\s+/, '').trim();
currentSection = 'description';
continue;
}
// H2 — sektionsrubriker
if (/^##\s+/.test(trimmed)) {
const heading = trimmed.replace(/^##\s+/, '').trim().toLowerCase();
if (/ingrediens/.test(heading)) {
currentSection = 'ingredients';
} else if (/instruktion|tillagning|gör så här|steg/.test(heading)) {
currentSection = 'instructions';
} else {
currentSection = 'none';
}
continue;
}
// Samla rader beroende på sektion
switch (currentSection) {
case 'description':
if (trimmed.length > 0) {
descriptionLines.push(trimmed);
}
break;
case 'ingredients':
if (/^[-*]\s+/.test(trimmed)) {
const ingredientText = trimmed.replace(/^[-*]\s+/, '');
ingredients.push(parseIngredientLine(ingredientText));
}
break;
case 'instructions':
if (trimmed.length > 0) {
instructionLines.push(trimmed);
}
break;
}
}
description = descriptionLines.join('\n');
instructions = instructionLines.join('\n');
return { name, description, instructions, ingredients };
}
/**
* Parsar en ingrediensrad, t.ex.:
* "400 g kycklingfilé"
* "2 dl grädde (eller crème fraiche)"
* "1 kruka basilika"
* "salt"
*/
function parseIngredientLine(text: string): ParsedIngredient {
const trimmed = text.trim();
// Extrahera eventuell parentes-not i slutet
let note: string | null = null;
let main = trimmed;
const parenMatch = trimmed.match(/\(([^)]+)\)\s*$/);
if (parenMatch) {
note = parenMatch[1].trim();
main = trimmed.slice(0, parenMatch.index).trim();
}
// Försök matcha "kvantitet enhet namn" — t.ex. "400 g kycklingfilé" eller "2.5 dl grädde"
const fullMatch = main.match(/^(\d+(?:[.,]\d+)?)\s+(\S+)\s+(.+)$/);
if (fullMatch) {
return {
quantity: parseNumber(fullMatch[1]),
unit: fullMatch[2],
rawName: fullMatch[3].trim(),
note,
};
}
// Försök matcha "kvantitet namn" utan enhet — t.ex. "3 ägg"
const noUnitMatch = main.match(/^(\d+(?:[.,]\d+)?)\s+(.+)$/);
if (noUnitMatch) {
return {
quantity: parseNumber(noUnitMatch[1]),
unit: 'st',
rawName: noUnitMatch[2].trim(),
note,
};
}
// Bara ett namn, ingen kvantitet — t.ex. "salt"
return {
quantity: 0,
unit: '',
rawName: main,
note,
};
}
function parseNumber(s: string): number {
return parseFloat(s.replace(',', '.'));
}
@@ -1,13 +0,0 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "commonjs",
"lib": ["ES2021"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"skipLibCheck": true
},
"include": ["src"]
}