From 5c8c676d4589801ee56d62f790135b413201b415 Mon Sep 17 00:00:00 2001 From: mstfyldz Date: Tue, 11 Aug 2026 16:30:22 +0300 Subject: [PATCH] feat: add deficiency analysis route --- src/controllers/case.controller.ts | 63 +++++++++++++++++++++++++- src/controllers/document.controller.ts | 25 ++++++++++ src/routes/case.routes.ts | 3 +- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/controllers/case.controller.ts b/src/controllers/case.controller.ts index cc293fe..5f3c5ac 100644 --- a/src/controllers/case.controller.ts +++ b/src/controllers/case.controller.ts @@ -1,7 +1,7 @@ import { Response } from 'express'; import { supabase } from '../lib/supabase'; import { AuthenticatedRequest } from '../middleware/auth'; -import { runIddianameAnalysis, runCaseNarrativeAnalysis, runStatementAnalysis } from './document.controller'; +import { runIddianameAnalysis, runCaseNarrativeAnalysis, runStatementAnalysis, runDeficiencyAnalysis } from './document.controller'; import { findOrCreateCaseByTitle } from '../lib/caseLookup'; export const createCase = async (req: AuthenticatedRequest, res: Response) => { @@ -160,6 +160,67 @@ export const analyzeStatements = async (req: AuthenticatedRequest, res: Response } }; +export const analyzeDeficiencies = async (req: AuthenticatedRequest, res: Response) => { + try { + const { caseId } = req.params; + if (!caseId) { + return res.status(400).json({ error: 'Geçersiz caseId' }); + } + + const { data: docs, error: docsError } = await supabase + .from('documents') + .select('filename, extracted_text') + .eq('case_id', caseId) + .order('uploaded_at', { ascending: true }); + if (docsError) throw docsError; + + // Sadece tensip, zaptı, tutanak, karar, müzekkere gibi evrakları filtrele + const keywords = ['tensip', 'zaptı', 'tutanak', 'duruşma', 'müzekkere', 'karar', 'iddianame']; + const deficiencyDocs = (docs || []).filter((d) => { + if (!d.extracted_text || !String(d.extracted_text).trim()) return false; + const fn = normalizeTr(d.filename); + return keywords.some(kw => fn.includes(kw)); + }); + + // Eğer hiç spesifik evrak yoksa, çöp olmayanları al + let targetDocs = deficiencyDocs; + if (targetDocs.length === 0) { + const trashKeywords = ['tebligat', 'makbuz', 'pul', 'zarf', 'masraf', 'havale', 'barkod', 'alındı', 'görüldü', 'ilan', 'talimat', 'nüfus', 'sicil', 'sabıka']; + targetDocs = (docs || []).filter(d => { + if (!d.extracted_text || !String(d.extracted_text).trim()) return false; + const fn = normalizeTr(d.filename); + return !trashKeywords.some(kw => fn.includes(kw)); + }); + } + + if (targetDocs.length === 0) { + return res.status(400).json({ error: 'Bu dosyada analiz edilecek uygun evrak bulunamadı.' }); + } + + const perDocLimit = Math.max(1000, Math.floor(15000 / targetDocs.length)); + const combinedText = targetDocs + .map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`) + .join('\n\n'); + + const analysisInput = `Aşağıdaki belgeler dava dosyasına aittir:\n\n${combinedText}`; + + const { summaryJson, aiModel } = await runDeficiencyAnalysis(analysisInput); + + // Eksiklik analizi sonucunu 'analyses' tablosuna kaydet + const { data: analysisRecord, error: analysisError } = await supabase + .from('analyses') + .insert([{ case_id: caseId, document_id: null, summary_json: { deficiencyAnalysis: summaryJson }, model_used: aiModel }]) + .select() + .single(); + if (analysisError) throw analysisError; + + res.json({ analysis: analysisRecord }); + } catch (error: any) { + console.error('Analyze Deficiencies Error:', error); + res.status(500).json({ error: error.message || 'Internal Server Error' }); + } +}; + // UYAP Evrak İndirici eklentisinin "Taraf Bilgileri" sekmesinden yakaladığı // davacı/davalı/sanık + vekil bilgisini kaydeder. AI'nin OCR metninden tahmin // ettiği "Taraflar" alanından çok daha güvenilir — doğrudan UYAP'ın yapılandırılmış diff --git a/src/controllers/document.controller.ts b/src/controllers/document.controller.ts index 638adc6..26ae4a4 100644 --- a/src/controllers/document.controller.ts +++ b/src/controllers/document.controller.ts @@ -57,6 +57,26 @@ const CASE_NARRATIVE_SYSTEM_PROMPT = ` veya başka bir açıklama ekleme. `; +const DEFICIENCY_ANALYSIS_SYSTEM_PROMPT = `Sen kıdemli bir avukatsın. Dosyadaki evrakları (özellikle tensip tutanakları, müzekkereler, duruşma zabıtları) inceleyerek dosyanın röntgenini çekeceksin. +Görevin, dosyadaki süreçte ne aşamada olunduğunu özetlemek ve EKSİK olan işlemleri veya beklenen evrakları (örn: bilirkişi raporu bekleniyor, tanıklar dinlenmedi, harç yatırılmadı vb.) tespit etmektir. + +Çıktını SADECE aşağıdaki JSON formatında ver, fazladan tek kelime etme: +{ + "currentStage": "Dosyanın şu anki aşaması (örn: Ön inceleme aşamasında, Tahkikat aşamasında vb.)", + "deficiencies": [ + { + "type": "Eksik türü (örn: Belge, İşlem, Ara Karar)", + "description": "Eksiğin açıklaması (örn: Trafik şubesinden hasar kayıtları bekleniyor)", + "urgency": "high" | "medium" | "low" (Eksikliğin aciliyet veya önem derecesi) + } + ], + "completedSteps": [ + "Tamamlanmış önemli işlemler (örn: Tensip zaptı düzenlendi, Harçlar yatırıldı)" + ] +} +DİKKAT: JSON formatı tam ve geçerli olmalıdır. +`; + const STATEMENT_ANALYSIS_SYSTEM_PROMPT = ` Sen uzman bir Türk ceza avukatısın. Sana bir dava dosyasındaki "ifade", "sorgu" veya "beyan" içeren çeşitli belgelerin metinleri verilecek. Amacın, bu belgelerdeki kişi beyanlarını analiz edip "Yalan Dedektörü" mantığıyla çelişkileri bulmaktır. @@ -129,6 +149,11 @@ export async function runStatementAnalysis(combinedText: string): Promise<{ summ return { summaryJson: resultJson, aiModel }; } +export async function runDeficiencyAnalysis(combinedText: string): Promise<{ summaryJson: any; aiModel: string }> { + const { resultJson, aiModel } = await callAiModel(DEFICIENCY_ANALYSIS_SYSTEM_PROMPT, combinedText); + return { summaryJson: resultJson, aiModel }; +} + // UYAP Evrak İndirici eklentisinden gelen, Electron ana sürecinde YERELDE metni // çıkarılmış belgeleri kaydeder. Ham dosya baytları bu isteğe hiç dahil değil — // sadece çıkarılan metin (JSON) geliyor, Supabase Storage'a hiçbir yükleme yapılmıyor. diff --git a/src/routes/case.routes.ts b/src/routes/case.routes.ts index ca7631f..6f5dc3e 100644 --- a/src/routes/case.routes.ts +++ b/src/routes/case.routes.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import { requireAuth } from '../middleware/auth'; -import { createCase, deleteCase, summarizeCase, updateCaseParties, analyzeStatements } from '../controllers/case.controller'; +import { createCase, deleteCase, summarizeCase, updateCaseParties, analyzeStatements, analyzeDeficiencies } from '../controllers/case.controller'; const router = Router(); @@ -8,6 +8,7 @@ router.post('/', requireAuth, createCase); router.delete('/:caseId', requireAuth, deleteCase); router.post('/:caseId/summarize', requireAuth, summarizeCase); router.post('/:caseId/analyze-statements', requireAuth, analyzeStatements); +router.post('/:caseId/analyze-deficiencies', requireAuth, analyzeDeficiencies); router.post('/parties', requireAuth, updateCaseParties); export default router;