feat: add deficiency analysis route

This commit is contained in:
mstfyldz
2026-08-11 16:30:22 +03:00
parent 05fcc00de9
commit 5c8c676d45
3 changed files with 89 additions and 2 deletions
+62 -1
View File
@@ -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ış
+25
View File
@@ -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.
+2 -1
View File
@@ -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;