feat: iddianame-first case summary + remove hardcoded AI endpoint

- summarizeCase: if an iddianame is among the case's documents, run a
  narrow first-pass analysis on it alone (isnatEdilenSuc, iddiaOzeti,
  onemliTarihler), then feed that as context into the general
  case-wide evaluation instead of treating every document equally.
- ANALYSIS_SYSTEM_PROMPT no longer asks the AI to guess "taraflar" -
  that now comes from real UYAP data (cases.parties) instead.
- Removed the hardcoded RunPod proxy URL fallback from every call
  site (document/chat/precedent controllers + lib/openai.ts) - it was
  a real infra endpoint checked into source. New lib/aiConfig.ts
  throws if RUNPOD_API_BASE_URL isn't set rather than silently
  falling back to it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mstfyldz
2026-08-09 20:00:06 +03:00
co-authored by Claude Sonnet 5
parent af5a03db92
commit c939dcf564
6 changed files with 105 additions and 33 deletions
+42 -2
View File
@@ -1,7 +1,7 @@
import { Response } from 'express';
import { supabase } from '../lib/supabase';
import { AuthenticatedRequest } from '../middleware/auth';
import { runDocumentAnalysis } from './document.controller';
import { runDocumentAnalysis, runIddianameAnalysis } from './document.controller';
import { findOrCreateCaseByTitle } from '../lib/caseLookup';
export const createCase = async (req: AuthenticatedRequest, res: Response) => {
@@ -28,9 +28,26 @@ export const createCase = async (req: AuthenticatedRequest, res: Response) => {
}
};
// Türkçe İ/I harflerinin regex /i bayrağıyla güvenilir eşleşmemesi (Unicode
// case-folding sorunu) yüzünden manuel normalize ediyoruz.
function normalizeTr(s: string): string {
return s.replace(/İ/g, 'i').replace(/I/g, 'i').toLowerCase();
}
// Dava dosyasındaki TÜM belgelerin çıkarılmış metnini birleştirip tek bir AI
// özeti üretir (Dosya Ayrıntı ekranındaki "Özet Hazırla" butonu). uploadDocument'ın
// aksine tek bir belgeye değil, dosyanın o anki tüm belgelerine bakıyor.
//
// Dosyada bir iddianame varsa önce SADECE onun üzerinden dar kapsamlı bir ön
// çıkarım yapılıyor (isnat edilen suç, iddia özeti, kritik tarihler); genel
// değerlendirme bu ön çıkarımı bağlam olarak alıp tüm belgelerle birlikte
// üretiliyor — böylece iddianamedeki temel olgular genel değerlendirmenin
// başlangıç noktası oluyor, kaybolmuyor.
//
// Not: "Bu dosyadaki tüm evraklar işlenene kadar bekle" kontrolü burada DEĞİL,
// frontend'de (CaseDetail) yapılıyor — çünkü eklentiden indirilip henüz OCR/kayıt
// kuyruğunda bekleyen evraklar sadece Electron'un yerel durumunda görünüyor,
// backend'in bundan haberi yok.
export const summarizeCase = async (req: AuthenticatedRequest, res: Response) => {
try {
const { caseId } = req.params;
@@ -50,6 +67,21 @@ export const summarizeCase = async (req: AuthenticatedRequest, res: Response) =>
return res.status(400).json({ error: 'Bu dosyada metni çıkarılmış belge yok, özet hazırlanamıyor.' });
}
const iddianameDoc = withText.find((d) =>
normalizeTr(d.filename).includes('iddianame') ||
normalizeTr(String(d.extracted_text)).slice(0, 500).includes('iddianame')
);
let iddianameJson: any = null;
if (iddianameDoc) {
try {
const iddianameResult = await runIddianameAnalysis(String(iddianameDoc.extracted_text));
iddianameJson = iddianameResult.summaryJson;
} catch (e) {
console.error('İddianame ön analizi başarısız, genel değerlendirmeye iddianamesiz devam ediliyor:', e);
}
}
// Tüm belgeleri modele gönderiyoruz; runDocumentAnalysis toplamı ~15000 karaktere
// kırpıyor, o yüzden belge sayısına göre kabaca dengeli bir pay ayırıyoruz.
const perDocLimit = Math.max(1000, Math.floor(15000 / withText.length));
@@ -57,7 +89,15 @@ export const summarizeCase = async (req: AuthenticatedRequest, res: Response) =>
.map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`)
.join('\n\n');
const { summaryJson, aiModel } = await runDocumentAnalysis(combinedText);
const analysisInput = iddianameJson
? `İDDİANAME ÖN ANALİZİ (genel değerlendirmeyi buna dayandır):\n${JSON.stringify(iddianameJson)}\n\n---\n\nDOSYADAKİ TÜM BELGELER:\n${combinedText}`
: combinedText;
const { summaryJson, aiModel } = await runDocumentAnalysis(analysisInput);
if (iddianameJson) {
summaryJson.isnatEdilenSuc = iddianameJson.isnatEdilenSuc || null;
summaryJson.iddianameOzeti = iddianameJson.iddiaOzeti || null;
}
const { data: analysisRecord, error: analysisError } = await supabase
.from('analyses')