Refactor code structure for improved readability and maintainability
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
export interface AiModelInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
model: string;
|
||||
path: string;
|
||||
trigger: string;
|
||||
access: string;
|
||||
}
|
||||
export declare class AiController {
|
||||
getModels(): AiModelInfo[];
|
||||
}
|
||||
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AiController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const public_decorator_1 = require("../auth/decorators/public.decorator");
|
||||
const ai_service_1 = require("./ai.service");
|
||||
const RECEIPT_IMPORT_MODEL = 'mistral-small-2603';
|
||||
let AiController = class AiController {
|
||||
getModels() {
|
||||
return [
|
||||
{
|
||||
id: 'receipt-pdf',
|
||||
name: 'Kvittoimport — PDF-tolkning',
|
||||
description: 'Extraherar varunamn, mängd och pris ur PDF-kvitton via textanalys.',
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
path: '/import',
|
||||
trigger: 'Vid uppladdning av PDF-kvitto',
|
||||
access: 'Alla inloggade',
|
||||
},
|
||||
{
|
||||
id: 'receipt-image',
|
||||
name: 'Kvittoimport — Bildtolkning',
|
||||
description: 'Extraherar varunamn, mängd och pris ur kvittofoton via bildanalys.',
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
path: '/import',
|
||||
trigger: 'Vid uppladdning av kvittobild (JPEG, PNG, WebP, HEIC)',
|
||||
access: 'Alla inloggade',
|
||||
},
|
||||
{
|
||||
id: 'receipt-category',
|
||||
name: 'Kvittoimport — Kategorisuggestion',
|
||||
description: 'För varor som inte matchas mot befintliga produkter visas ett AI-förslag på kategori som ledtråd.',
|
||||
model: ai_service_1.AI_CATEGORIZATION_MODEL,
|
||||
path: '/import',
|
||||
trigger: 'Automatiskt efter kvittotolkning (om inga träffar hittas)',
|
||||
access: 'Premium-användare + Admin',
|
||||
},
|
||||
{
|
||||
id: 'product-suggest',
|
||||
name: 'AI-kategorisering per produkt',
|
||||
description: 'Ger ett AI-förslag på kategori för en enskild produkt med säkerhetsindikation (hög/medel/låg).',
|
||||
model: ai_service_1.AI_CATEGORIZATION_MODEL,
|
||||
path: '/admin/products',
|
||||
trigger: 'Manuell — klick på "✨ Fråga AI" i produktlistan',
|
||||
access: 'Admin',
|
||||
},
|
||||
{
|
||||
id: 'product-bulk',
|
||||
name: 'AI-bulk-kategorisering',
|
||||
description: 'Analyserar alla okategoriserade produkter och presenterar förslag i ett bekräftelsemodal.',
|
||||
model: ai_service_1.AI_CATEGORIZATION_MODEL,
|
||||
path: '/admin/products',
|
||||
trigger: 'Manuell — knappen "✨ AI-kategorisera okategoriserade"',
|
||||
access: 'Admin',
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
exports.AiController = AiController;
|
||||
__decorate([
|
||||
(0, common_1.Get)('models'),
|
||||
(0, public_decorator_1.Public)(),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Array)
|
||||
], AiController.prototype, "getModels", null);
|
||||
exports.AiController = AiController = __decorate([
|
||||
(0, common_1.Controller)('ai')
|
||||
], AiController);
|
||||
//# sourceMappingURL=ai.controller.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ai.controller.js","sourceRoot":"","sources":["../../src/ai/ai.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,0EAA6D;AAC7D,6CAAuD;AAEvD,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAa3C,IAAM,YAAY,GAAlB,MAAM,YAAY;IAGvB,SAAS;QACP,OAAO;YACL;gBACE,EAAE,EAAE,aAAa;gBACjB,IAAI,EAAE,6BAA6B;gBACnC,WAAW,EAAE,oEAAoE;gBACjF,KAAK,EAAE,oBAAoB;gBAC3B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,+BAA+B;gBACxC,MAAM,EAAE,gBAAgB;aACzB;YACD;gBACE,EAAE,EAAE,eAAe;gBACnB,IAAI,EAAE,6BAA6B;gBACnC,WAAW,EAAE,oEAAoE;gBACjF,KAAK,EAAE,oBAAoB;gBAC3B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,uDAAuD;gBAChE,MAAM,EAAE,gBAAgB;aACzB;YACD;gBACE,EAAE,EAAE,kBAAkB;gBACtB,IAAI,EAAE,mCAAmC;gBACzC,WAAW,EAAE,mGAAmG;gBAChH,KAAK,EAAE,oCAAuB;gBAC9B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,2DAA2D;gBACpE,MAAM,EAAE,2BAA2B;aACpC;YACD;gBACE,EAAE,EAAE,iBAAiB;gBACrB,IAAI,EAAE,+BAA+B;gBACrC,WAAW,EAAE,gGAAgG;gBAC7G,KAAK,EAAE,oCAAuB;gBAC9B,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,iDAAiD;gBAC1D,MAAM,EAAE,OAAO;aAChB;YACD;gBACE,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,wBAAwB;gBAC9B,WAAW,EAAE,2FAA2F;gBACxG,KAAK,EAAE,oCAAuB;gBAC9B,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,uDAAuD;gBAChE,MAAM,EAAE,OAAO;aAChB;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AApDY,oCAAY;AAGvB;IAFC,IAAA,YAAG,EAAC,QAAQ,CAAC;IACb,IAAA,yBAAM,GAAE;;;;6CAiDR;uBAnDU,YAAY;IADxB,IAAA,mBAAU,EAAC,IAAI,CAAC;GACJ,YAAY,CAoDxB"}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare class AiModule {
|
||||
}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AiModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const ai_service_1 = require("./ai.service");
|
||||
const ai_controller_1 = require("./ai.controller");
|
||||
let AiModule = class AiModule {
|
||||
};
|
||||
exports.AiModule = AiModule;
|
||||
exports.AiModule = AiModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
controllers: [ai_controller_1.AiController],
|
||||
providers: [ai_service_1.AiService],
|
||||
exports: [ai_service_1.AiService],
|
||||
})
|
||||
], AiModule);
|
||||
//# sourceMappingURL=ai.module.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ai.module.js","sourceRoot":"","sources":["../../src/ai/ai.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAyC;AACzC,mDAA+C;AAOxC,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAAG,CAAA;AAAX,4BAAQ;mBAAR,QAAQ;IALpB,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,4BAAY,CAAC;QAC3B,SAAS,EAAE,CAAC,sBAAS,CAAC;QACtB,OAAO,EAAE,CAAC,sBAAS,CAAC;KACrB,CAAC;GACW,QAAQ,CAAG"}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
import { FlatCategory } from '../categories/categories.service';
|
||||
export declare const AI_CATEGORIZATION_MODEL = "mistral-tiny";
|
||||
export type CategorySuggestion = {
|
||||
categoryId: number;
|
||||
categoryName: string;
|
||||
path: string;
|
||||
confidence: 'high' | 'medium' | 'low';
|
||||
usedFallback: boolean;
|
||||
};
|
||||
export declare class AiService {
|
||||
private readonly logger;
|
||||
suggestCategory(productName: string, categories: FlatCategory[]): Promise<CategorySuggestion>;
|
||||
private fallbackToOvrigt;
|
||||
}
|
||||
Vendored
+150
@@ -0,0 +1,150 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var AiService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AiService = exports.AI_CATEGORIZATION_MODEL = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const MISTRAL_API_URL = 'https://api.mistral.ai/v1/chat/completions';
|
||||
exports.AI_CATEGORIZATION_MODEL = 'mistral-tiny';
|
||||
const MODEL = exports.AI_CATEGORIZATION_MODEL;
|
||||
let AiService = AiService_1 = class AiService {
|
||||
constructor() {
|
||||
this.logger = new common_1.Logger(AiService_1.name);
|
||||
}
|
||||
async suggestCategory(productName, categories) {
|
||||
const apiKey = process.env.MISTRAL_API_KEY;
|
||||
if (!apiKey) {
|
||||
throw new common_1.ServiceUnavailableException('MISTRAL_API_KEY är inte konfigurerad i miljövariabler');
|
||||
}
|
||||
const categoryList = categories
|
||||
.map((c) => `[${c.id}] ${c.path}`)
|
||||
.join('\n');
|
||||
const systemPrompt = `Du är ett kategoriseringssystem för en livsmedelsapp. Din uppgift är att hitta den mest lämpliga kategorin för en produkt.
|
||||
|
||||
Tillgängliga kategorier (format: [id] Sökväg):
|
||||
${categoryList}
|
||||
|
||||
Regler:
|
||||
1. Välj den mest specifika underkategorin som passar produkten.
|
||||
2. Om ingen specifik kategori passar, välj en underkategori under "Övrigt" om möjligt.
|
||||
3. Om ingen underkategori under "Övrigt" passar, välj "Övrigt" (den kategori vars sökväg är exakt "Övrigt").
|
||||
4. Du MÅSTE alltid returnera ett svar — aldrig null eller tomt.
|
||||
5. Svara ENDAST med giltig JSON i detta format: { "categoryId": <nummer>, "confidence": "high" | "medium" | "low" }
|
||||
- "high": uppenbart matchande kategori
|
||||
- "medium": trolig matchning
|
||||
- "low": osäker, används fallback (Övrigt eller underkategori till Övrigt)`;
|
||||
const userPrompt = `Produkt: "${productName}"`;
|
||||
let raw = '';
|
||||
const MAX_RETRIES = 3;
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const response = await fetch(MISTRAL_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: MODEL,
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{ role: 'user', content: userPrompt },
|
||||
],
|
||||
max_tokens: 100,
|
||||
temperature: 0.1,
|
||||
response_format: { type: 'json_object' },
|
||||
}),
|
||||
});
|
||||
if (response.status === 503 || response.status === 429) {
|
||||
const err = await response.text();
|
||||
this.logger.warn(`Mistral API ${response.status} (försök ${attempt}/${MAX_RETRIES}): ${err}`);
|
||||
if (attempt < MAX_RETRIES) {
|
||||
await new Promise((r) => setTimeout(r, attempt * 1500));
|
||||
continue;
|
||||
}
|
||||
throw new common_1.ServiceUnavailableException('AI-tjänsten är tillfälligt otillgänglig, försök igen');
|
||||
}
|
||||
if (!response.ok) {
|
||||
const err = await response.text();
|
||||
this.logger.error(`Mistral API-fel: ${response.status} ${err}`);
|
||||
throw new common_1.ServiceUnavailableException('AI-tjänsten svarade inte korrekt');
|
||||
}
|
||||
const data = await response.json();
|
||||
raw = data.choices?.[0]?.message?.content ?? '';
|
||||
break;
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof common_1.ServiceUnavailableException)
|
||||
throw err;
|
||||
this.logger.error(`Mistral fetch-fel (försök ${attempt}/${MAX_RETRIES}): ${String(err)}`);
|
||||
if (attempt < MAX_RETRIES) {
|
||||
await new Promise((r) => setTimeout(r, attempt * 1500));
|
||||
continue;
|
||||
}
|
||||
throw new common_1.ServiceUnavailableException('Kunde inte nå AI-tjänsten');
|
||||
}
|
||||
}
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
}
|
||||
catch {
|
||||
this.logger.warn(`AI returnerade ogiltig JSON: ${raw}`);
|
||||
return this.fallbackToOvrigt(categories);
|
||||
}
|
||||
const validId = typeof parsed.categoryId === 'number';
|
||||
const matchedCategory = validId ? categories.find((c) => c.id === parsed.categoryId) : null;
|
||||
if (!matchedCategory) {
|
||||
this.logger.warn(`AI returnerade okänt categoryId ${parsed.categoryId}, använder fallback`);
|
||||
return this.fallbackToOvrigt(categories);
|
||||
}
|
||||
const confidence = ['high', 'medium', 'low'].includes(parsed.confidence)
|
||||
? parsed.confidence
|
||||
: 'medium';
|
||||
if (confidence === 'low') {
|
||||
const l1Name = matchedCategory.path.split(' > ')[0];
|
||||
const l1 = categories.find((c) => c.path === l1Name);
|
||||
if (l1 && l1.id !== matchedCategory.id) {
|
||||
this.logger.log(`AI-guardrail: ${confidence} konfidenspoäng → remappar "${matchedCategory.path}" → L1 "${l1.path}"`);
|
||||
return {
|
||||
categoryId: l1.id,
|
||||
categoryName: l1.name,
|
||||
path: l1.path,
|
||||
confidence,
|
||||
usedFallback: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
categoryId: matchedCategory.id,
|
||||
categoryName: matchedCategory.name,
|
||||
path: matchedCategory.path,
|
||||
confidence,
|
||||
usedFallback: confidence === 'low',
|
||||
};
|
||||
}
|
||||
fallbackToOvrigt(categories) {
|
||||
const ovrigt = categories.find((c) => c.path === 'Övrigt');
|
||||
if (!ovrigt) {
|
||||
const first = categories[0];
|
||||
return { categoryId: first.id, categoryName: first.name, path: first.path, confidence: 'low', usedFallback: true };
|
||||
}
|
||||
return {
|
||||
categoryId: ovrigt.id,
|
||||
categoryName: ovrigt.name,
|
||||
path: ovrigt.path,
|
||||
confidence: 'low',
|
||||
usedFallback: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
exports.AiService = AiService;
|
||||
exports.AiService = AiService = AiService_1 = __decorate([
|
||||
(0, common_1.Injectable)()
|
||||
], AiService);
|
||||
//# sourceMappingURL=ai.service.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ai.service.js","sourceRoot":"","sources":["../../src/ai/ai.service.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user