feat: use firm's own templates as style guide for AI-drafted petitions

Belge Şablonları uploads now also get extracted_text (same OCR/office
pipeline as documents), and a new POST /api/drafting/generate builds
a real AI draft from the case's actual data (title, UYAP parties,
latest Dosya Özeti) — if the lawyer picks one of their own templates,
its extracted text is passed as a style/format reference only (the
prompt explicitly forbids copying the template's old case facts).
Dilekçe Hazırlama no longer fakes a draft with setTimeout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mstfyldz
2026-08-09 20:20:47 +03:00
co-authored by Claude Sonnet 5
parent 50f6cbb1b2
commit fa01432ffe
5 changed files with 142 additions and 32 deletions
+27 -32
View File
@@ -68,7 +68,7 @@ async function callAiModel(systemPrompt: string, userText: string): Promise<{ re
return { resultJson, aiModel };
}
async function callAiModelRaw(systemPrompt: string, userText: string): Promise<{ aiContent: string; aiModel: string }> {
export async function callAiModelRaw(systemPrompt: string, userText: string): Promise<{ aiContent: string; aiModel: string }> {
const { runpodUrl, aiModel } = getAiConfig();
const response = await fetch(`${runpodUrl}/v1/chat/completions`, {
@@ -168,6 +168,27 @@ export const registerLocalDocument = async (req: AuthenticatedRequest, res: Resp
}
};
// uploadDocument ve uploadTemplate'in (template.controller.ts) ortak kullandığı
// metin çıkarma adımı: resimler için Tesseract OCR, ofis belgeleri için officeParser.
export async function extractTextFromUploadedFile(file: { buffer: Buffer; mimetype: string; originalname: string }): Promise<string> {
const mimeType = file.mimetype.toLowerCase();
if (mimeType.includes('image/')) {
let imageBuffer = file.buffer;
if (mimeType === 'image/heic' || file.originalname.toLowerCase().endsWith('.heic')) {
imageBuffer = await heicConvert({ buffer: file.buffer, format: 'JPEG', quality: 1 });
}
const { data: { text } } = await Tesseract.recognize(imageBuffer, 'tur');
return text;
}
// officeParser natively supports pdf, docx, doc, rtf, txt, etc.
// officeparser@7.x parseOffice() bir sonuc objesi donduruyor (duz string degil),
// duz metin icin .toText() cagirmak gerekiyor.
const result: any = await officeParser.parseOffice(file.buffer);
return typeof result === 'string' ? result : (result?.toText ? result.toText() : String(result || ''));
}
export const uploadDocument = async (req: AuthenticatedRequest, res: Response) => {
try {
const file = req.file;
@@ -179,37 +200,11 @@ export const uploadDocument = async (req: AuthenticatedRequest, res: Response) =
}
let extractedText = '';
const mimeType = file.mimetype.toLowerCase();
// IMAGE & OCR LOGIC
if (mimeType.includes('image/')) {
let imageBuffer = file.buffer;
// Convert HEIC to JPEG
if (mimeType === 'image/heic' || file.originalname.toLowerCase().endsWith('.heic')) {
imageBuffer = await heicConvert({
buffer: file.buffer,
format: 'JPEG',
quality: 1
});
}
// Run Tesseract OCR (Turkish language)
const { data: { text } } = await Tesseract.recognize(imageBuffer, 'tur');
extractedText = text;
// DOCUMENT LOGIC
} else {
try {
// officeParser natively supports pdf, docx, doc, rtf, txt, etc.
// officeparser@7.x parseOffice() bir sonuc objesi donduruyor (duz string degil),
// duz metin icin .toText() cagirmak gerekiyor.
const result: any = await officeParser.parseOffice(file.buffer);
extractedText = typeof result === 'string' ? result : (result?.toText ? result.toText() : String(result || ''));
} catch (err: any) {
console.error('officeParser Error:', err);
return res.status(400).json({ error: 'Dosya formatı okunamadı veya desteklenmiyor.' });
}
try {
extractedText = await extractTextFromUploadedFile(file);
} catch (err: any) {
console.error('Text Extraction Error:', err);
return res.status(400).json({ error: 'Dosya formatı okunamadı veya desteklenmiyor.' });
}
// 2. Upload File to Supabase Storage