diff --git a/src/controllers/case.controller.ts b/src/controllers/case.controller.ts index 8481bbf..cc293fe 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 } from './document.controller'; +import { runIddianameAnalysis, runCaseNarrativeAnalysis, runStatementAnalysis } from './document.controller'; import { findOrCreateCaseByTitle } from '../lib/caseLookup'; export const createCase = async (req: AuthenticatedRequest, res: Response) => { @@ -109,6 +109,57 @@ export const summarizeCase = async (req: AuthenticatedRequest, res: Response) => } }; +// Çelişki Avcısı: İfade ve sorgu evraklarını alıp analiz eder. +export const analyzeStatements = 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 ifade, sorgu, beyan, tutanak, duruşma içeren belgeleri filtrele + const keywords = ['ifade', 'sorgu', 'beyan', 'tutanak', 'duruşma']; + const statementDocs = (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)); + }); + + if (statementDocs.length === 0) { + return res.status(400).json({ error: 'Bu dosyada ifade/beyan evrakı bulunamadı.' }); + } + + const perDocLimit = Math.max(1000, Math.floor(15000 / statementDocs.length)); + const combinedText = statementDocs + .map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`) + .join('\n\n'); + + const analysisInput = `Aşağıdaki belgeler sadece ifade ve sorgu evraklarıdır:\n\n${combinedText}`; + + const { summaryJson, aiModel } = await runStatementAnalysis(analysisInput); + + // Çelişki analizi sonucunu 'analyses' tablosuna kaydet + const { data: analysisRecord, error: analysisError } = await supabase + .from('analyses') + .insert([{ case_id: caseId, document_id: null, summary_json: { statementAnalysis: summaryJson }, model_used: aiModel }]) + .select() + .single(); + if (analysisError) throw analysisError; + + res.json({ analysis: analysisRecord }); + } catch (error: any) { + console.error('Analyze Statements 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 5560023..638adc6 100644 --- a/src/controllers/document.controller.ts +++ b/src/controllers/document.controller.ts @@ -57,6 +57,32 @@ const CASE_NARRATIVE_SYSTEM_PROMPT = ` veya başka bir açıklama ekleme. `; +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. + + Çıktıyı KESİNLİKLE geçerli bir JSON formatında vermelisin. JSON yapısı tam olarak şu şekilde olmalıdır: + { + "persons": [ + { + "name": "Kişinin Adı Soyadı", + "role": "Müşteki, Şüpheli, Sanık, Tanık vb.", + "statements": [ + { + "stage": "Karakol / Savcılık / Mahkeme / Diğer", + "summary": "Bu aşamadaki beyanının çok kısa özeti" + } + ], + "contradictions": [ + "Eğer aynı kişinin farklı aşamalardaki ifadelerinde veya somut delillerle çelişen bir durumu varsa buraya yaz. Yoksa boş bırak." + ] + } + ] + } + + Sadece ve sadece JSON döndür, kod bloğu işareti (\`\`\`) veya başka bir açıklama ekleme. + `; + async function callAiModel(systemPrompt: string, userText: string): Promise<{ resultJson: any; aiModel: string }> { const { aiContent, aiModel } = await callAiModelRaw(systemPrompt, userText); let resultJson: any = {}; @@ -97,6 +123,12 @@ export async function runCaseNarrativeAnalysis(combinedText: string): Promise<{ return { summaryJson: { ozet: aiContent }, aiModel }; } +// Yeni İfade Analizi ve Çelişki Avcısı adımı +export async function runStatementAnalysis(combinedText: string): Promise<{ summaryJson: any; aiModel: string }> { + const { resultJson, aiModel } = await callAiModel(STATEMENT_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 c004510..ca7631f 100644 --- a/src/routes/case.routes.ts +++ b/src/routes/case.routes.ts @@ -1,12 +1,13 @@ import { Router } from 'express'; import { requireAuth } from '../middleware/auth'; -import { createCase, deleteCase, summarizeCase, updateCaseParties } from '../controllers/case.controller'; +import { createCase, deleteCase, summarizeCase, updateCaseParties, analyzeStatements } from '../controllers/case.controller'; const router = Router(); router.post('/', requireAuth, createCase); router.delete('/:caseId', requireAuth, deleteCase); router.post('/:caseId/summarize', requireAuth, summarizeCase); +router.post('/:caseId/analyze-statements', requireAuth, analyzeStatements); router.post('/parties', requireAuth, updateCaseParties); export default router;