This commit is contained in:
mstfyldz
2026-08-11 16:27:02 +03:00
parent 6bd6da3776
commit 05fcc00de9
3 changed files with 86 additions and 2 deletions
+52 -1
View File
@@ -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ış
+32
View File
@@ -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.
+2 -1
View File
@@ -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;