Takes the user's described situation plus the top search results and asks the AI model for a short legal assessment. Auth-protected since it's a paid AI call.
169 lines
6.9 KiB
TypeScript
169 lines
6.9 KiB
TypeScript
import { Request, Response } from 'express';
|
||
import { AuthenticatedRequest } from '../middleware/auth';
|
||
|
||
export const searchPrecedents = async (req: Request, res: Response) => {
|
||
try {
|
||
const { phrase, court_types, page_number } = req.body;
|
||
|
||
if (!phrase) {
|
||
return res.status(400).json({ error: 'Arama metni (phrase) zorunludur.' });
|
||
}
|
||
|
||
let optimizedPhrase = phrase;
|
||
|
||
// Eğer sorgu 3 kelimeden uzunsa (doğal dil sorusu ise), AI ile anahtar kelimelere dönüştür
|
||
if (phrase.split(' ').length > 3) {
|
||
const aiModel = process.env.AI_MODEL || 'mizan-fixed';
|
||
const runpodUrl = process.env.RUNPOD_API_BASE_URL || 'https://q190env94stwis-11434.proxy.runpod.net';
|
||
|
||
const systemPrompt = `Sen uzman bir Türk avukatısın. Kullanıcının uzun ve doğal dille yazdığı hukuki soruyu, Yargıtay içtihat arama motorunda en iyi sonucu verecek şekilde 2 ila en fazla 4 temel hukuki terime/anahtar kelimeye çevir.
|
||
SADECE anahtar kelimeleri aralarına boşluk koyarak yaz. Noktalama işareti, açıklama veya ek kelimeler (ve, ile, vb.) kullanma.
|
||
Örnek Kullanıcı: İşveren tarafından performans düşüklüğü gerekçesiyle iş sözleşmesi feshedilen işçinin işe iade davası açması halinde ispat yükü kime aittir
|
||
Örnek Cevap: performans düşüklüğü ispat yükü işe iade`;
|
||
|
||
try {
|
||
const aiResponse = await fetch(`${runpodUrl}/v1/chat/completions`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
model: aiModel,
|
||
messages: [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: phrase }
|
||
],
|
||
think: false,
|
||
stream: false,
|
||
}),
|
||
});
|
||
|
||
if (aiResponse.ok) {
|
||
const data = await aiResponse.json();
|
||
const aiContent = data.message?.content || (data.choices && data.choices[0]?.message?.content);
|
||
if (aiContent) {
|
||
optimizedPhrase = aiContent.trim().replace(/["'\n]/g, '');
|
||
console.log(`[AI Search] Orijinal: "${phrase}" -> Optimize: "${optimizedPhrase}"`);
|
||
}
|
||
} else {
|
||
console.error('AI Query Optimization API returned status:', aiResponse.status);
|
||
}
|
||
} catch (err) {
|
||
console.error('AI Query Optimization Fetch Error:', err);
|
||
}
|
||
}
|
||
|
||
// Yargı MCP REST API'sine (8001 portu veya YARGI_MCP_URL) optimize edilmiş sorgu ile istek at
|
||
const mcpBaseUrl = process.env.YARGI_MCP_URL || 'http://localhost:8001';
|
||
const mcpResponse = await fetch(`${mcpBaseUrl}/search`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
phrase: optimizedPhrase,
|
||
court_types: court_types || ["YARGITAYKARARI", "DANISTAYKARAR"],
|
||
page_number: page_number || 1
|
||
})
|
||
});
|
||
|
||
if (!mcpResponse.ok) {
|
||
const errorData = await mcpResponse.text();
|
||
console.error('Yargı MCP API Error:', errorData);
|
||
return res.status(mcpResponse.status).json({ error: 'Arama servisi hata döndürdü', details: errorData });
|
||
}
|
||
|
||
const data = await mcpResponse.json();
|
||
res.json(data);
|
||
} catch (error: any) {
|
||
console.error('Search Precedents Error:', error);
|
||
res.status(500).json({ error: error.message || 'Internal Server Error' });
|
||
}
|
||
};
|
||
|
||
interface PrecedentSummary {
|
||
birimAdi?: string;
|
||
mahkeme?: string;
|
||
kararTarihiStr?: string;
|
||
tarih?: string;
|
||
esasNo?: string;
|
||
esas_no?: string;
|
||
kararNo?: string;
|
||
karar_no?: string;
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
// Analiz modu: bulunan kararları kullanıcının anlattığı durumla birlikte AI'ye
|
||
// göndererek kısa bir hukuki değerlendirme üretir. Maliyetli bir AI çağrısı olduğu
|
||
// için requireAuth ile korunuyor (bkz. routes/precedent.routes.ts).
|
||
export const analyzePrecedents = async (req: AuthenticatedRequest, res: Response) => {
|
||
try {
|
||
const { situation, results } = req.body as { situation?: string; results?: PrecedentSummary[] };
|
||
if (!situation || !Array.isArray(results) || results.length === 0) {
|
||
return res.status(400).json({ error: 'situation ve results zorunludur.' });
|
||
}
|
||
|
||
const listText = results.slice(0, 8).map((r, i) => {
|
||
const mahkeme = r.birimAdi || r.mahkeme || 'Bilinmeyen Mahkeme';
|
||
const tarih = r.kararTarihiStr || r.tarih || '';
|
||
const esas = r.esasNo || r.esas_no || '';
|
||
const karar = r.kararNo || r.karar_no || '';
|
||
return `${i + 1}. ${mahkeme} (${tarih}) — Esas: ${esas}, Karar: ${karar}`;
|
||
}).join('\n');
|
||
|
||
const systemPrompt = 'Sen uzman bir Türk avukatısın. Kullanıcının anlattığı hukuki durumu, aşağıda listelenen emsal karar arama sonuçları ışığında kısaca değerlendir. Hangi kararların duruma daha yakın olabileceğini belirt ve olası bir hukuki değerlendirme sun. Emin olmadığın noktalarda bunu açıkça belirt, uydurma bilgi verme. Cevabın yaklaşık 150-250 kelime olsun.';
|
||
const userPrompt = `DURUM:\n${String(situation).slice(0, 3000)}\n\nBULUNAN KARARLAR:\n${listText}`;
|
||
|
||
const aiModel = process.env.AI_MODEL || 'mizan-fixed';
|
||
const runpodUrl = process.env.RUNPOD_API_BASE_URL || 'https://q190env94stwis-11434.proxy.runpod.net';
|
||
|
||
const response = await fetch(`${runpodUrl}/api/chat`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
model: aiModel,
|
||
messages: [
|
||
{ role: 'system', content: systemPrompt },
|
||
{ role: 'user', content: userPrompt },
|
||
],
|
||
think: false,
|
||
stream: false,
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`AI Model request failed: ${response.statusText}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
const analysis = data.message?.content || 'Analiz üretilemedi.';
|
||
res.json({ analysis });
|
||
} catch (error: any) {
|
||
console.error('Analyze Precedents Error:', error);
|
||
res.status(500).json({ error: error.message || 'Internal Server Error' });
|
||
}
|
||
};
|
||
|
||
export const getPrecedentDocument = async (req: Request, res: Response) => {
|
||
try {
|
||
const documentId = String(req.params.documentId || '');
|
||
|
||
if (!documentId) {
|
||
return res.status(400).json({ error: 'documentId zorunludur.' });
|
||
}
|
||
|
||
const mcpBaseUrl = process.env.YARGI_MCP_URL || 'http://localhost:8001';
|
||
const mcpResponse = await fetch(`${mcpBaseUrl}/document/${encodeURIComponent(documentId)}`);
|
||
|
||
if (!mcpResponse.ok) {
|
||
const errorData = await mcpResponse.text();
|
||
console.error('Yargı MCP API Error:', errorData);
|
||
return res.status(mcpResponse.status).json({ error: 'Belge servisi hata döndürdü', details: errorData });
|
||
}
|
||
|
||
const data = await mcpResponse.json();
|
||
res.json(data);
|
||
} catch (error: any) {
|
||
console.error('Get Precedent Document Error:', error);
|
||
res.status(500).json({ error: error.message || 'Internal Server Error' });
|
||
}
|
||
};
|