Kullanıcı testi hataları: sabit tarihler, kişisel varsayılanlar, kılavuz düğmesi, mobil başlık, AI taslak çökmesi

- Günlük Bülten ve İhale Takvimi sabit "10.08.2026" yerine bugünden hesaplar;
  takvim 7 günü gerçek ihale tarihleriyle sayar
- Ekip/üye/firma varsayılanları oturumdaki kullanıcıdan; sahte "Ekip Notları"
  ve sabit "%94 Uyum" rozeti kaldırıldı (gerçek uyum skoru varsa gösterilir)
- Kılavuz düğmesi document.body'ye portal (ekran dışına düşüyordu)
- Detay modalı havuzdaki güncel kaydı okur: Takip Et / Görev aç etiketleri güncellenir
- Mobil: başlıkta ikincil düğmeler md altında gizli, arama formu küçülebilir
- PWA sayfası: sahte önbellek sayıları yerine Cache API ve bellek sayımı
- Sözleşme sorgula pazar payı bir ondalık (hep %0 görünüyordu)
- Rakip Radar "SMS" vaadi düzeltildi; sektör=CPV etiketi tekrarı gizlendi;
  Stripe kurulum uyarısı yalnız yöneticiye
- Açılışta 7 gereksiz PUT /api/me/data: ilk yükleme yazımı atlanır
- server: teklif taslağı çökmesi (kalemOku nesne döndürür), denetim yanıtından
  model adı kaldırıldı, gereklilikler satır bazlı; derin analiz 12k token +
  kesik JSON onarımı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 09:43:41 +03:00
co-authored by Claude Fable 5.1
parent 6d7d938711
commit 08c0a0af7e
14 changed files with 130 additions and 60 deletions
+34 -2
View File
@@ -276,7 +276,39 @@ function extractJson(raw) {
const start = s.indexOf('{');
const end = s.lastIndexOf('}');
if (start === -1 || end === -1) throw new Error('Yanıtta JSON bulunamadı');
return JSON.parse(s.slice(start, end + 1));
const govde = s.slice(start, end + 1);
try { return JSON.parse(govde); } catch (e) {
const onarilmis = kesikJsonOnar(s.slice(start));
if (onarilmis) return onarilmis;
throw e;
}
}
/**
* Model çıktısı max_tokens'ta kesildiğinde JSON yarım kalır. Sondan geriye
* doğru güvenli kesme noktaları denenir; açık kalan dizi/nesneler kapatılır.
*/
function kesikJsonOnar(s) {
for (let cut = s.length; cut > 50; ) {
cut = Math.max(s.lastIndexOf('}', cut - 1), s.lastIndexOf(']', cut - 1), s.lastIndexOf('"', cut - 1));
if (cut <= 0) return null;
let parca = s.slice(0, cut + 1).replace(/,\s*$/, '');
// yığın: string dışındaki açık parantezler
const yigin = []; let strIcinde = false, kacis = false;
for (const ch of parca) {
if (strIcinde) { if (kacis) kacis = false; else if (ch === '\\') kacis = true; else if (ch === '"') strIcinde = false; continue; }
if (ch === '"') strIcinde = true;
else if (ch === '{' || ch === '[') yigin.push(ch);
else if (ch === '}' || ch === ']') yigin.pop();
}
if (strIcinde) { cut -= 1; continue; }
parca = parca.replace(/,\s*$/, '');
// "anahtar": ile biten yarım çift -> anahtarı at
parca = parca.replace(/,?\s*"[^"]*"\s*:\s*$/, '');
while (yigin.length) parca += yigin.pop() === '{' ? '}' : ']';
try { return JSON.parse(parca); } catch { cut -= 1; }
}
return null;
}
/** Zinciri sırayla dener; ilk başarılı sonucu döndürür. */
@@ -288,7 +320,7 @@ export async function analyzeSpec({ text, fileName, tender, chain, systemOverrid
// kesilirse JSON parse edilemez ve zincir boşuna tüketilir.
const userPrompt = buildUserPrompt({ text, fileName, tender, limit: derin ? 90000 : 60000 });
const sistem = systemOverride || (derin ? DERIN_SYSTEM_PROMPT : SYSTEM_PROMPT);
const maxTokens = derin ? 8000 : 4000;
const maxTokens = derin ? 12000 : 4000;
const timeoutMs = derin ? 150000 : 90000;
const attempts = [];