Files
lcbp3/backend/eslint-intent.json
admin ea5499123e
CI / CD Pipeline / build (push) Failing after 3m57s
CI / CD Pipeline / deploy (push) Has been skipped
690519:1631 224 to 226 AI #01
2026-05-19 16:31:50 +07:00

8 lines
18 KiB
JSON

npm warn Unknown project config "shamefully-hoist". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm warn Unknown project config "hoist-pattern". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm warn Unknown project config "public-hoist-pattern". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm warn Unknown project config "node-linker". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm warn Unknown project config "strict-peer-dependencies". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm warn Unknown project config "auto-install-peers". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
[{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\controllers\\intent-admin.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\controllers\\intent-classify.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\dto\\classify-query.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\dto\\create-intent-definition.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\dto\\create-intent-pattern.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\dto\\update-intent-definition.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\dto\\update-intent-pattern.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\entities\\intent-definition.entity.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\entities\\intent-pattern.entity.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\intent-classifier.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\interfaces\\classification-result.interface.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\interfaces\\intent-category.enum.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\intent-classifier.service.spec.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\intent-classifier.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\intent-definition.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\intent-pattern-cache.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\intent-pattern.service.ts","messages":[{"ruleId":"prettier/prettier","severity":2,"message":"Replace `·PatternLanguage,·PatternType·` with `⏎··PatternLanguage,⏎··PatternType,⏎`","line":15,"column":9,"nodeType":null,"messageId":"replace","endLine":15,"endColumn":39,"fix":{"range":[512,542],"text":"\n PatternLanguage,\n PatternType,\n"}}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":1,"fixableWarningCount":0,"source":"// File: src/modules/ai/intent-classifier/services/intent-pattern.service.ts\n// Change Log\n// - 2026-05-19: สร้าง CRUD service สำหรับ Intent Patterns (Admin, ADR-024).\n\nimport {\n Injectable,\n Logger,\n NotFoundException,\n BadRequestException,\n} from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { Repository } from 'typeorm';\nimport { IntentPattern } from '../entities/intent-pattern.entity';\nimport { IntentPatternCacheService } from './intent-pattern-cache.service';\nimport { PatternLanguage, PatternType } from '../interfaces/intent-category.enum';\n\n/** DTO สำหรับสร้าง Pattern */\nexport interface CreateIntentPatternData {\n intentCode: string;\n language?: PatternLanguage;\n patternType: PatternType;\n patternValue: string;\n priority?: number;\n}\n\n/** DTO สำหรับ update Pattern */\nexport interface UpdateIntentPatternData {\n language?: PatternLanguage;\n patternType?: PatternType;\n patternValue?: string;\n priority?: number;\n isActive?: boolean;\n}\n\n/**\n * Service สำหรับจัดการ Intent Patterns (Admin CRUD)\n * Invalidate cache ทุกครั้งที่มีการเปลี่ยนแปลง\n */\n@Injectable()\nexport class IntentPatternService {\n private readonly logger = new Logger(IntentPatternService.name);\n\n constructor(\n @InjectRepository(IntentPattern)\n private readonly repo: Repository<IntentPattern>,\n private readonly cacheService: IntentPatternCacheService\n ) {}\n\n /** ดึง Patterns ตาม intentCode */\n async findByIntentCode(intentCode: string): Promise<IntentPattern[]> {\n return this.repo.find({\n where: { intentCode },\n order: { priority: 'ASC' },\n });\n }\n\n /** ดึง Pattern ตาม publicId */\n async findByPublicId(publicId: string): Promise<IntentPattern> {\n const entity = await this.repo.findOne({ where: { publicId } });\n if (!entity) {\n throw new NotFoundException(`Pattern \"${publicId}\" not found`);\n }\n return entity;\n }\n\n /** สร้าง Pattern ใหม่ + invalidate cache */\n async create(data: CreateIntentPatternData): Promise<IntentPattern> {\n // Validate regex ถ้าเป็น regex type\n if (data.patternType === PatternType.REGEX) {\n this.validateRegex(data.patternValue);\n }\n\n const entity = this.repo.create({\n intentCode: data.intentCode,\n language: data.language ?? PatternLanguage.ANY,\n patternType: data.patternType,\n patternValue: data.patternValue,\n priority: data.priority ?? 100,\n });\n\n const saved = await this.repo.save(entity);\n await this.cacheService.invalidate();\n this.logger.log(\n `Created pattern for ${saved.intentCode}: \"${saved.patternValue}\"`\n );\n return saved;\n }\n\n /** อัปเดต Pattern + invalidate cache */\n async update(\n publicId: string,\n data: UpdateIntentPatternData\n ): Promise<IntentPattern> {\n const entity = await this.findByPublicId(publicId);\n\n // Validate regex ถ้ามีการเปลี่ยน patternValue เป็น regex\n const newType = data.patternType ?? entity.patternType;\n const newValue = data.patternValue ?? entity.patternValue;\n if (newType === PatternType.REGEX && data.patternValue) {\n this.validateRegex(newValue);\n }\n\n Object.assign(entity, data);\n const saved = await this.repo.save(entity);\n await this.cacheService.invalidate();\n this.logger.log(`Updated pattern ${publicId}`);\n return saved;\n }\n\n /** Soft delete Pattern + invalidate cache */\n async remove(publicId: string): Promise<void> {\n const entity = await this.findByPublicId(publicId);\n entity.isActive = false;\n await this.repo.save(entity);\n await this.cacheService.invalidate();\n this.logger.log(`Soft-deleted pattern ${publicId}`);\n }\n\n /**\n * Validate regex pattern (research decision: try-catch ที่ service layer)\n * @throws BadRequestException ถ้า regex ไม่ถูกต้อง\n */\n private validateRegex(pattern: string): void {\n try {\n new RegExp(pattern);\n } catch (err) {\n throw new BadRequestException(\n `Invalid regex pattern: \"${pattern}\" — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\llm-semaphore.service.spec.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\llm-semaphore.service.ts","messages":[{"ruleId":"prettier/prettier","severity":2,"message":"Replace ``LLM·Semaphore·initialized:·max·${this.maxConcurrent}·concurrent`` with `⏎······`LLM·Semaphore·initialized:·max·${this.maxConcurrent}·concurrent`⏎····`","line":25,"column":21,"nodeType":null,"messageId":"replace","endLine":25,"endColumn":86,"fix":{"range":[875,940],"text":"\n `LLM Semaphore initialized: max ${this.maxConcurrent} concurrent`\n "}}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":1,"fixableWarningCount":0,"source":"// File: src/modules/ai/intent-classifier/services/llm-semaphore.service.ts\n// Change Log\n// - 2026-05-19: สร้าง Semaphore สำหรับควบคุม concurrent LLM calls (ADR-024).\n\nimport { Injectable, Logger } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\n\n/**\n * Semaphore Pattern สำหรับจำกัด concurrent LLM calls\n * ป้องกัน GPU overload บน Admin Desktop (ADR-023A)\n * ใช้ Promise-based queue แทน p-limit เพื่อลด dependency\n */\n@Injectable()\nexport class LlmSemaphoreService {\n private readonly logger = new Logger(LlmSemaphoreService.name);\n private readonly maxConcurrent: number;\n private currentCount = 0;\n private readonly queue: Array<() => void> = [];\n\n constructor(private readonly configService: ConfigService) {\n this.maxConcurrent = this.configService.get<number>(\n 'INTENT_CLASSIFIER_LLM_SEMAPHORE',\n 3\n );\n this.logger.log(`LLM Semaphore initialized: max ${this.maxConcurrent} concurrent`);\n }\n\n /** จำนวน requests ที่กำลังประมวลผลอยู่ */\n get activeCount(): number {\n return this.currentCount;\n }\n\n /** จำนวน requests ที่รอใน queue */\n get pendingCount(): number {\n return this.queue.length;\n }\n\n /** ตรวจสอบว่า semaphore เต็มหรือไม่ */\n get isFull(): boolean {\n return this.currentCount >= this.maxConcurrent;\n }\n\n /**\n * Acquire semaphore slot — รอถ้าเต็ม\n * @returns release function ที่ต้องเรียกเมื่อเสร็จ\n */\n async acquire(): Promise<() => void> {\n if (this.currentCount < this.maxConcurrent) {\n this.currentCount++;\n return this.createRelease();\n }\n\n // รอจนกว่าจะมี slot ว่าง\n return new Promise<() => void>((resolve) => {\n this.queue.push(() => {\n this.currentCount++;\n resolve(this.createRelease());\n });\n });\n }\n\n /**\n * Try acquire — ไม่รอ ถ้าเต็มจะ return null ทันที\n * ใช้สำหรับ semaphore_overflow fallback\n */\n tryAcquire(): (() => void) | null {\n if (this.currentCount < this.maxConcurrent) {\n this.currentCount++;\n return this.createRelease();\n }\n return null;\n }\n\n /** สร้าง release function (เรียกได้ครั้งเดียว) */\n private createRelease(): () => void {\n let released = false;\n return () => {\n if (released) return;\n released = true;\n this.currentCount--;\n\n // ปล่อย request ถัดไปใน queue\n if (this.queue.length > 0) {\n const next = this.queue.shift();\n if (next) next();\n }\n };\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\ollama-client.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\pattern-matcher.service.spec.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"E:\\np-dms\\lcbp3\\backend\\src\\modules\\ai\\intent-classifier\\services\\pattern-matcher.service.ts","messages":[{"ruleId":"prettier/prettier","severity":2,"message":"Replace `normalizedQuery:·string,·pattern:·CachedPattern` with `⏎····normalizedQuery:·string,⏎····pattern:·CachedPattern⏎··`","line":43,"column":26,"nodeType":null,"messageId":"replace","endLine":43,"endColumn":73,"fix":{"range":[1343,1390],"text":"\n normalizedQuery: string,\n pattern: CachedPattern\n "}}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":1,"fixableWarningCount":0,"source":"// File: src/modules/ai/intent-classifier/services/pattern-matcher.service.ts\n// Change Log\n// - 2026-05-19: สร้าง Pattern Matcher Service — จับคู่ query กับ cached patterns (ADR-024).\n\nimport { Injectable, Logger } from '@nestjs/common';\nimport {\n CachedPattern,\n ClassificationResult,\n} from '../interfaces/classification-result.interface';\n\n/**\n * Service สำหรับจับคู่ query กับ Intent Patterns\n * Strategy: iterate ตาม priority (ASC) — keyword ใช้ includes, regex ใช้ RegExp.test\n * ผลลัพธ์แรกที่ match จะ return ทันที (confidence = 1.0)\n */\n@Injectable()\nexport class PatternMatcherService {\n private readonly logger = new Logger(PatternMatcherService.name);\n\n /**\n * จับคู่ query กับ patterns ที่ cache ไว้\n * @returns ClassificationResult ถ้า match, null ถ้าไม่ match\n */\n match(query: string, patterns: CachedPattern[]): ClassificationResult | null {\n const normalizedQuery = query.toLowerCase().trim();\n const startTime = Date.now();\n\n for (const pattern of patterns) {\n if (this.isPatternMatch(normalizedQuery, pattern)) {\n return {\n intentCode: pattern.intentCode,\n confidence: 1.0,\n method: 'pattern',\n latencyMs: Date.now() - startTime,\n };\n }\n }\n\n return null;\n }\n\n /** ตรวจสอบว่า query match กับ pattern หรือไม่ */\n private isPatternMatch(normalizedQuery: string, pattern: CachedPattern): boolean {\n try {\n if (pattern.patternType === 'keyword') {\n return normalizedQuery.includes(pattern.patternValue.toLowerCase());\n }\n\n if (pattern.patternType === 'regex') {\n const regex = new RegExp(pattern.patternValue, 'i');\n return regex.test(normalizedQuery);\n }\n\n return false;\n } catch (err) {\n // Invalid regex จะไม่ crash — log แล้วข้ามไป\n this.logger.warn(\n `Invalid pattern \"${pattern.patternValue}\" (${pattern.publicId}): ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n return false;\n }\n }\n}\n","usedDeprecatedRules":[]}]