Files
lagos-back/src/controllers/drafting.controller.ts
T
mstfyldzandClaude Sonnet 5 2c28cdbf9e fix: forbid markdown formatting in AI-generated petition drafts
The draft prompt didn't say anything about output format, so the model
would sometimes emit **bold**/## headers like it does elsewhere -
wrong for a document meant to go straight into a court filing. Now
explicitly instructed to use plain text with conventional Turkish
petition section labels (AÇIKLAMALAR:, SONUÇ VE İSTEM: etc.) instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 22:05:37 +03:00

96 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Response } from 'express';
import { supabase } from '../lib/supabase';
import { AuthenticatedRequest } from '../middleware/auth';
import { callAiModelRaw } from './document.controller';
const DRAFT_SYSTEM_PROMPT = `
Sen uzman bir Türk avukatısın. Sana verilen dosya bilgilerine göre, istenen türde
bir dilekçe TASLAĞI yazacaksın. Türk hukuk uygulamasına uygun, resmî bir dille yaz
(hitap, hususlar, netice-i talep gibi standart dilekçe yapısını kullan).
EĞER sana bir "BÜRO ŞABLONU" verilmişse: bu şablon, bu hukuk bürosunun kendi imzası
olan format/dil/üslup tercihidir — YALNIZCA bunun yapısını, dilini, başlıklandırmasını
ve üslubunu takip et. Şablondaki taraf isimleri, esas no'su, tarihler, mahkeme adı gibi
ESKİ dosyaya ait somut bilgileri ASLA kullanma veya kopyalama — onların yerine SADECE
aşağıda verilen YENİ dosyanın gerçek bilgilerini koy.
ÖNEMLİ — BİÇİMLENDİRME: Bu çıktı doğrudan bir dilekçe belgesine dönüştürülecek, o yüzden
KESİNLİKLE Markdown biçimlendirmesi kullanma (**, ##, -, *, 1. gibi işaretler YASAK).
Sadece düz metin yaz. Başlık/vurgu gerekiyorsa BÜYÜK HARF veya standart dilekçe
başlıkları (örn. "AÇIKLAMALAR:", "HUKUKİ SEBEPLER:", "SONUÇ VE İSTEM:") kullan,
yıldız veya diyez işareti asla kullanma.
Sadece dilekçe taslağının kendisini döndür — başlık, açıklama veya ek yorum ekleme.
`;
// Dilekçe Hazırlama ekranındaki "AI ile Taslak Oluştur" — dosyanın gerçek bilgilerini
// (başlık, UYAP'tan gelen taraflar, varsa Dosya Özeti) ve kullanıcının notlarını,
// isteğe bağlı olarak seçilen büro şablonunun DİLİNİ/FORMATINI referans alarak
// gerçek bir AI taslağı üretir.
export const generateDraft = async (req: AuthenticatedRequest, res: Response) => {
try {
const { case_id, petition_type, notes, template_id } = req.body || {};
const userId = req.user?.id;
if (!case_id || !petition_type) {
return res.status(400).json({ error: 'case_id ve petition_type zorunludur.' });
}
const { data: caseRow, error: caseError } = await supabase
.from('cases')
.select('id, title, parties, user_id')
.eq('id', case_id)
.single();
if (caseError || !caseRow) {
return res.status(404).json({ error: 'Dava dosyası bulunamadı.' });
}
if (caseRow.user_id !== userId) {
return res.status(403).json({ error: 'Bu dosyaya erişim yetkiniz yok.' });
}
const { data: lastAnalysis } = await supabase
.from('analyses')
.select('summary_json')
.eq('case_id', case_id)
.order('created_at', { ascending: false })
.limit(1)
.maybeSingle();
let template: { name: string; extracted_text: string | null } | null = null;
if (template_id) {
const { data: templateRow } = await supabase
.from('templates')
.select('name, extracted_text, user_id')
.eq('id', template_id)
.single();
if (templateRow && templateRow.user_id === userId) {
template = templateRow;
}
}
const partiesText = Array.isArray(caseRow.parties) && caseRow.parties.length > 0
? caseRow.parties.map((p: any) => `${p.adi || ''} (${p.rol || ''}${p.vekil ? `, Vekil: ${p.vekil}` : ''})`).join('; ')
: 'Belirtilmemiş — UYAP Taraf Bilgileri sekmesi henüz çekilmemiş olabilir.';
const ozet = lastAnalysis?.summary_json?.ozet;
let userContent = `YENİ DOSYA BİLGİLERİ:\nDosya: ${caseRow.title}\nTaraflar: ${partiesText}\nDilekçe Türü: ${petition_type}\n`;
if (notes && String(notes).trim()) {
userContent += `Vurgulanmasını istediğiniz noktalar: ${String(notes).trim()}\n`;
}
if (ozet && typeof ozet === 'string') {
userContent += `\nDosya Özeti (bağlam için):\n${ozet.slice(0, 4000)}\n`;
}
if (template?.extracted_text) {
userContent += `\n---\nBÜRO ŞABLONU "${template.name}" (SADECE dil/format/üslup referansı — içeriğini kopyalama):\n${template.extracted_text.slice(0, 6000)}\n`;
}
const { aiContent, aiModel } = await callAiModelRaw(DRAFT_SYSTEM_PROMPT, userContent);
res.json({ draft: aiContent, model: aiModel, usedTemplate: !!template });
} catch (error: any) {
console.error('Generate Draft Error:', error);
res.status(500).json({ error: error.message || 'Internal Server Error' });
}
};