import { supabase } from './supabase'; import { callOllama } from './aiClient'; import * as promptService from './promptService'; export interface LegislationMatch { maddeNo: string; baslik: string | null; metin: string; // 800 karaktere kesilmiş kanun: string; // mevzuat.kisa_ad || mevzuat.ad } const MAX_MADDE_LEN = 800; const MAX_MATCHES = 5; // mevzuat.tur sütunuyla eşleşen snake_case değerler — mevzuat-etl/mevzuat_etl/config.py::MEVZUAT_TUR_MAP // ile aynı 8 anahtar, burada bağımsız bir TS kopyası (Python paketine bağımlılık yok). const TUR_LABEL_TO_DB: Record = { 'Kanun': 'kanun', 'KHK': 'khk', 'Tüzük': 'tuzuk', 'Cumhurbaşkanlığı Kararnamesi': 'cb_kararnamesi', 'Cumhurbaşkanı Kararı': 'cb_karari', 'Cumhurbaşkanlığı Yönetmeliği': 'cb_yonetmeligi', 'Tebliğ': 'teblig', }; // Kısaltma bazlı, iki sıra da olası — "TCK m.141" (m./madde sayıdan ÖNCE) ve // "CMK 100. maddesi" (sayı m./madde'den ÖNCE) — ayrı ayrı test edildi (bkz. // _verify_legislation_lookup.ts), tek regex'te birleştirmek grup-indeksini // karmaşıklaştırdığı için iki ayrı desen daha güvenli. const ABBREVIATION_BEFORE_RE = /\b(TCK|CMK|TMK|TBK|TTK|ANAYASA)\b[^\d\n]{0,10}\b(?:m\.|madde)\s*(\d+[/A-Za-z]*)/gi; const ABBREVIATION_AFTER_RE = /\b(TCK|CMK|TMK|TBK|TTK|ANAYASA)\b[^\d\n]{0,15}(\d+[/A-Za-z]*)\.?\s*madde/gi; // Genel/tur-agnostik: "5237 sayılı Kanun'un 141. maddesi", "700 sayılı KHK'nın 3. maddesi". const GENERIC_RE = /(\d+)\s+say[ıi]l[ıi]\s+(Kanun|KHK|Tüzük|Cumhurbaşkanlığı\s+Kararnamesi|Cumhurbaşkanı\s+Kararı|Cumhurbaşkanlığı\s+Yönetmeliği|Tebliğ)[^\d]{0,30}(\d+[/A-Za-z]*)\.?\s*madde/gi; interface Citation { kisaAd?: string; mevzuatNo?: string; turHint?: string; // DB'nin snake_case değeri maddeNo: string; } function extractCitations(text: string): Citation[] { const citations: Citation[] = []; for (const m of text.matchAll(ABBREVIATION_BEFORE_RE)) { citations.push({ kisaAd: m[1].toUpperCase(), maddeNo: m[2] }); } for (const m of text.matchAll(ABBREVIATION_AFTER_RE)) { citations.push({ kisaAd: m[1].toUpperCase(), maddeNo: m[2] }); } for (const m of text.matchAll(GENERIC_RE)) { citations.push({ mevzuatNo: m[1], turHint: TUR_LABEL_TO_DB[m[2]], maddeNo: m[3] }); } // Tekilleştir (kisaAd|mevzuatNo + maddeNo bazında). const seen = new Set(); return citations.filter((c) => { const key = `${c.kisaAd || c.mevzuatNo}:${c.maddeNo}`; if (seen.has(key)) return false; seen.add(key); return true; }); } function truncate(text: string): string { if (text.length <= MAX_MADDE_LEN) return text; return text.slice(0, MAX_MADDE_LEN) + '…'; } async function resolveMevzuatId(citation: Citation): Promise<{ id: string; kanun: string } | null> { let query = supabase.from('mevzuat').select('id, ad, kisa_ad'); if (citation.kisaAd) { query = query.eq('kisa_ad', citation.kisaAd); } else if (citation.mevzuatNo) { query = query.eq('mevzuat_no', citation.mevzuatNo); if (citation.turHint) query = query.eq('tur', citation.turHint); } else { return null; } const { data, error } = await query.maybeSingle(); if (error || !data) return null; return { id: data.id, kanun: data.kisa_ad || data.ad }; } async function fetchByCitations(citations: Citation[]): Promise { const matches: LegislationMatch[] = []; for (const citation of citations) { if (matches.length >= MAX_MATCHES) break; const mevzuat = await resolveMevzuatId(citation); if (!mevzuat) continue; const { data, error } = await supabase .from('mevzuat_madde') .select('madde_no, baslik, metin') .eq('mevzuat_id', mevzuat.id) .eq('madde_no', citation.maddeNo) .maybeSingle(); if (error || !data) continue; matches.push({ maddeNo: data.madde_no, baslik: data.baslik, metin: truncate(data.metin), kanun: mevzuat.kanun, }); } return matches; } async function fetchByFullTextSearch(caseText: string): Promise { let keywords = ''; try { const keywordPrompt = await promptService.getPrompt('legislation_keywords'); const aiContent = await callOllama( [ { role: 'system', content: keywordPrompt }, { role: 'user', content: caseText.slice(0, 2000) }, ], { numPredict: 40, timeoutMs: 20_000 } ); keywords = (aiContent || '').trim().replace(/["'\n]/g, ''); } catch (err) { console.error('Legislation keyword compression error:', err); return []; } if (!keywords) return []; const { data, error } = await supabase .from('mevzuat_madde') .select('madde_no, baslik, metin, mevzuat:mevzuat_id(ad, kisa_ad)') .textSearch('search_vector', keywords, { type: 'websearch', config: 'turkish' }) .limit(MAX_MATCHES); if (error || !data) return []; return data.map((row: any) => ({ maddeNo: row.madde_no, baslik: row.baslik, metin: truncate(row.metin), kanun: row.mevzuat?.kisa_ad || row.mevzuat?.ad || 'Bilinmeyen Mevzuat', })); } // Dava metninde geçen kanun maddesi atıflarını bulur ve o maddelerin metnini döner. // Hiçbir zaman exception fırlatmaz — Supabase/Ollama erişilemezse veya eşleşme // yoksa boş dizi döner, çağıran taraf bunu "mevzuat bağlamı yok" olarak yorumlar. export async function findRelevantLegislation(caseText: string): Promise { try { const citations = extractCitations(caseText); if (citations.length > 0) { const matches = await fetchByCitations(citations); if (matches.length > 0) return matches; } return await fetchByFullTextSearch(caseText); } catch (err) { console.error('findRelevantLegislation error:', err); return []; } }