fix(liste): model seçimi önce kodla onarılsın; ikinci çağrı ve zaman aşımı azalsın [KARAR]
Kök neden 2 (04-eval-sonuclari): 10/40 listede ikinci model çağrısı, 3/40'ta 60 sn zaman aşımı. rapor.ts - secimiOnar: yinelenen ve havuz-dışı kimlik atılır, yanlış listeye konan program havuzdaki gerçek dilimine taşınır, hedefi aşan dilim kırpılır, eksik satır havuzdan deterministik sırayla (önce adayın seçimine uyanlar, sonra sıraya yakınlık) tamamlanır; tamamlanan satırın gerekçesi şablondur. İkinci model çağrısı yalnız çıktı şemaya uymazsa ya da modelin seçiminin yarısından azı kullanılabiliyorsa yapılır. Havuz-dışı koruması aynen durur. - Prompt: havuz dilim başına üç dizi (hayal/dengeli/guvenli) olarak gider, üçüncü listenin tel adı `guvenli` (iç anahtar `garanti` kalır); vaat ve rakam yasağı netleşti; adayın serbest metni etiket içinde veri olarak girer. - İlk üretimde zaman aşımı: liste havuzdan kodla kurulur, üst metin bunu söyler (ZAMAN_ASIMINDA_KODLA_KUR). KREDİ: bu yolda üretim başarılı sayılır, iade yapılmaz. Revizyonda zaman aşımı eskisi gibi hata + iade. - raporUretAyrintili + RaporTani: onarım/şablon/çağrı gözlemi (eval ve log). cagri.ts - Üst sağlayıcı gözlemi (CagriGozlemi) ve tepkisel dışlama: reasoning kapalı istendiği hâlde düşünme token'ı üreten üst sağlayıcı 6 saat provider.ignore ile atlanır. Ölçüm: 16 üst sağlayıcıdan yalnız AtlasCloud isteğe uymuyor (4.000 token'ın 3.999'u düşünme, ~50 sn, içerik yarım); zaman aşımlarının ve "iki denemede başarısız" hatasının kaynağı buydu. robot.ts: tanılı API, yeni tel şeması, onarım/şablon/üst sağlayıcı özeti, "Kıbrıs'ta" il çelişkisi yanlış alarmı. Önce → sonra (40 profil, gerçek model; ayrıntı 07-liste-duzeltmeleri.md): üretilen liste 37 → 40, zaman aşımı 3 → 0, ikinci çağrı 10 → 1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ import { getDb, havuzAdaylari } from "@/lib/db";
|
||||
import { kategoriEslesir } from "@/lib/kategoriler";
|
||||
import { dilimHedefiHesapla, havuzOlustur, havuzuKompaktJson, ozelKontenjanSatiriMi, turLabel, type AdayProgram, type DilimHedef } from "@/lib/rapor-havuzu";
|
||||
import type { RaporParams, RaporSonuc } from "@/features/rapor/types/rapor";
|
||||
import type { RaporTani } from "@/lib/ai/rapor";
|
||||
|
||||
const DILIMLER = ["hayal", "dengeli", "garanti"] as const;
|
||||
const IDEAL: DilimHedef = { hayal: 5, dengeli: 13, garanti: 6 };
|
||||
@@ -70,6 +71,8 @@ function gerekcedekiIller(metin: string): string[] {
|
||||
return [...bulunan];
|
||||
}
|
||||
|
||||
const KKTC_ILLERI = new Set(["LEFKOŞA", "GİRNE", "GAZİMAĞUSA", "GÜZELYURT", "LEFKE"]);
|
||||
|
||||
type Bulgu = { ad: string; durum: "ihlal" | "bilinen" | "duzeldi"; neden: string };
|
||||
|
||||
function degismezler(p: EvalProfil) {
|
||||
@@ -156,9 +159,11 @@ function degismezler(p: EvalProfil) {
|
||||
|
||||
// ───────────────────────── --model: gerçek liste üretimi ─────────────────────────
|
||||
|
||||
const KURTARMA_GEREKCESI = "Sıralamana ve seçimlerine uygun olduğu için listene eklendi.";
|
||||
|
||||
type HamListe = { hayal?: { programId?: string }[]; dengeli?: { programId?: string }[]; garanti?: { programId?: string }[] };
|
||||
// Modelin tel şeması 5 Eki 2026'da değişti: üçüncü liste `guvenli`, kimlik alanı `id`
|
||||
// (öncesi: `garanti`, `programId`). İkisi de okunur ki eski ham çıktılar da denetlenebilsin.
|
||||
// 5 Eki (2): satır tek dizgi, "id|gerekçe".
|
||||
type HamSatir = string | { id?: string; programId?: string };
|
||||
type HamListe = { hayal?: HamSatir[]; dengeli?: HamSatir[]; garanti?: HamSatir[]; guvenli?: HamSatir[] };
|
||||
|
||||
/** Modelin HAM çıktısı (normalize öncesi): rapor.ts havuz-dışını sessizce atar, burada sayılır. */
|
||||
function hamDenetle(ham: string, havuzMap: Map<string, AdayProgram>, hedef: DilimHedef) {
|
||||
@@ -168,33 +173,36 @@ function hamDenetle(ham: string, havuzMap: Map<string, AdayProgram>, hedef: Dili
|
||||
} catch {
|
||||
return { semaGecerli: false, havuzDisi: 0, yinelenen: 0, yanlisDilim: 0, sayiTuttu: false };
|
||||
}
|
||||
const semaGecerli = DILIMLER.every((d) => Array.isArray(j[d]) && j[d]!.every((t) => typeof t?.programId === "string"));
|
||||
const liste = (d: (typeof DILIMLER)[number]) => (d === "garanti" ? (j.guvenli ?? j.garanti) : j[d]);
|
||||
const kimlik = (t: HamSatir) => (typeof t === "string" ? t.split("|")[0].trim() : (t?.id ?? t?.programId));
|
||||
const semaGecerli = DILIMLER.every((d) => Array.isArray(liste(d)) && liste(d)!.every((t) => typeof kimlik(t) === "string"));
|
||||
if (!semaGecerli) return { semaGecerli, havuzDisi: 0, yinelenen: 0, yanlisDilim: 0, sayiTuttu: false };
|
||||
let havuzDisi = 0;
|
||||
let yinelenen = 0;
|
||||
let yanlisDilim = 0;
|
||||
const gorulen = new Set<string>();
|
||||
for (const d of DILIMLER)
|
||||
for (const t of j[d]!) {
|
||||
const id = t.programId!;
|
||||
for (const t of liste(d)!) {
|
||||
const id = kimlik(t)!;
|
||||
const p = havuzMap.get(id);
|
||||
if (!p) havuzDisi++;
|
||||
else if (gorulen.has(id)) yinelenen++;
|
||||
else if (p.dilim !== d) yanlisDilim++;
|
||||
gorulen.add(id);
|
||||
}
|
||||
return { semaGecerli, havuzDisi, yinelenen, yanlisDilim, sayiTuttu: DILIMLER.every((d) => j[d]!.length === hedef[d]) };
|
||||
return { semaGecerli, havuzDisi, yinelenen, yanlisDilim, sayiTuttu: DILIMLER.every((d) => liste(d)!.length === hedef[d]) };
|
||||
}
|
||||
|
||||
async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, raporUret: typeof import("@/lib/ai/rapor").raporUret) {
|
||||
async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, raporUretAyrintili: typeof import("@/lib/ai/rapor").raporUretAyrintili) {
|
||||
const havuzMap = new Map(d.havuz.map((x) => [x.id, x]));
|
||||
const kompakt = new Map((JSON.parse(havuzuKompaktJson(d.havuz)) as { id: string }[]).map((x) => [x.id, JSON.stringify(x)]));
|
||||
const t0 = Date.now();
|
||||
let rapor: RaporSonuc | null = null;
|
||||
let tani: RaporTani | null = null;
|
||||
let hata: string | null = null;
|
||||
const { kullanim } = await olcerekKos(async () => {
|
||||
try {
|
||||
rapor = await raporUret(d.params, undefined, undefined, p.danismanProfili);
|
||||
({ rapor, tani } = await raporUretAyrintili(d.params, undefined, undefined, p.danismanProfili));
|
||||
} catch (e) {
|
||||
hata = e instanceof Error ? e.message.slice(0, 200) : String(e);
|
||||
}
|
||||
@@ -202,11 +210,14 @@ async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, rapo
|
||||
const ms = Date.now() - t0;
|
||||
const ham = kullanim.hamIcerik.map((h) => hamDenetle(h, havuzMap, d.hedef));
|
||||
const kontroller: Kontrol[] = [];
|
||||
const temel = { id: p.id, ms, cagri: kullanim.cagri, girdiToken: kullanim.girdiToken, ciktiToken: kullanim.ciktiToken, maliyet: kullanim.maliyet, ham };
|
||||
const temel = { id: p.id, ms, cagri: kullanim.cagri, girdiToken: kullanim.girdiToken, ciktiToken: kullanim.ciktiToken, maliyet: kullanim.maliyet, ham, tani: tani as RaporTani | null };
|
||||
if (!rapor) {
|
||||
return { ...temel, karar: "kaldi" as const, kontroller: [{ ad: "uretim", durum: "kaldi" as const, neden: hata ?? "rapor yok" }], satirlar: [], liste: null };
|
||||
}
|
||||
const r = rapor as RaporSonuc;
|
||||
const t_ = tani as RaporTani | null;
|
||||
// Gerekçesi koddan basılan satırlar (tamamlama + denetimde şablona düşenler)
|
||||
const sablonNedeni = new Map((t_?.sablonGerekce ?? []).map((x) => [x.programId, x.neden]));
|
||||
if (r.devMock) kontroller.push({ ad: "uretim", durum: "atlandi", neden: "anahtar yok: dev mock rapor (gerçek modelle koşulmadı)" });
|
||||
|
||||
// Ham çıktı (ilk deneme belirleyici: kullanıcı ikinci denemenin süresini bekler)
|
||||
@@ -218,7 +229,8 @@ async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, rapo
|
||||
kontroller.push({ ad: "ham.yinelenen", durum: hy ? "gri" : "gecti", neden: hy ? `${hy} yinelenen programId` : undefined });
|
||||
const yd = ham.reduce((a, h) => a + h.yanlisDilim, 0);
|
||||
kontroller.push({ ad: "ham.yanlis-dilim", durum: yd ? "gri" : "gecti", neden: yd ? `${yd} program yanlış dilim listesinde` : undefined });
|
||||
if (ham.length > 1) kontroller.push({ ad: "ikinci-deneme", durum: "gri", neden: `${ham.length} model denemesi gerekti` });
|
||||
if ((t_?.modelCagrisi ?? ham.length) > 1) kontroller.push({ ad: "ikinci-deneme", durum: "gri", neden: `${t_?.modelCagrisi ?? ham.length} model denemesi gerekti` });
|
||||
if (t_?.kaynak === "deterministik") kontroller.push({ ad: "deterministik-liste", durum: "gri", neden: "model zaman aşımı: liste tamamen koddan kuruldu (kredi iade edilmez)" });
|
||||
|
||||
// Nihai liste (kullanıcının gördüğü)
|
||||
const say = { hayal: 0, dengeli: 0, garanti: 0 };
|
||||
@@ -233,8 +245,12 @@ async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, rapo
|
||||
kontroller.push({ ad: "havuz-disi", durum: disari ? "kaldi" : "gecti", neden: disari ? `${disari} satır havuz dışı` : undefined });
|
||||
const yin = r.tercihler.length - new Set(r.tercihler.map((t) => t.programId)).size;
|
||||
kontroller.push({ ad: "yinelenen", durum: yin ? "kaldi" : "gecti", neden: yin ? `${yin}` : undefined });
|
||||
const kurtarma = r.tercihler.filter((t) => t.gerekce === KURTARMA_GEREKCESI).length;
|
||||
const kurtarma = t_?.onarim.tamamlanan ?? 0;
|
||||
if (kurtarma) kontroller.push({ ad: "kurtarma", durum: "gri", neden: `${kurtarma} satır kodla tamamlandı (model eksik döndü)` });
|
||||
const sablonDusen = (t_?.sablonGerekce.length ?? 0) - kurtarma;
|
||||
// Bilgi satırı: denetim çalıştı ve metin kullanıcıya gitmedi; karar "geçti" kalır, oran özetten izlenir
|
||||
if (sablonDusen > 0 || t_?.genelSablon || t_?.uyariAtilan.length)
|
||||
kontroller.push({ ad: "sablon", durum: "gecti", neden: `${sablonDusen} gerekçe şablona düştü${t_?.genelSablon ? `; genel değerlendirme şablon (${t_.genelSablon})` : ""}${t_?.uyariAtilan.length ? `; ${t_.uyariAtilan.length} uyarı atıldı` : ""}` });
|
||||
|
||||
// Liste düzeyi metinler
|
||||
const ustMetin = [r.genelDegerlendirme, ...(r.uyarilar ?? [])].join("\n");
|
||||
@@ -250,7 +266,8 @@ async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, rapo
|
||||
const yasak = [...new Set([...yasakSozcukler(t.gerekce), ...vaatIfadeleri(t.gerekce)])];
|
||||
const sayi = prog ? dayanaksizSayilar(t.gerekce, [kompakt.get(t.programId) ?? "", adayMetni, JSON.stringify(o ?? {})]) : [];
|
||||
// Üniversite adında geçen il (ör. "Ankara Üniversitesi" İstanbul'da olmasa da) çelişki sayılmaz
|
||||
const yanlisIl = prog ? gerekcedekiIller(t.gerekce).filter((il) => il !== prog.il && !prog.universite.includes(il) && !prog.isim.toLocaleUpperCase("tr-TR").includes(il)) : [];
|
||||
// "Kıbrıs'ta" KKTC şehirlerindeki (Lefke, Girne…) program için çelişki değildir (5 Eki: yanlış alarm düzeltildi)
|
||||
const yanlisIl = prog ? gerekcedekiIller(t.gerekce).filter((il) => il !== prog.il && !prog.universite.includes(il) && !prog.isim.toLocaleUpperCase("tr-TR").includes(il) && !(il === "KIBRIS" && KKTC_ILLERI.has(prog.il ?? ""))) : [];
|
||||
const state: JevGerekceState = {
|
||||
metin: t.gerekce,
|
||||
program: {
|
||||
@@ -267,14 +284,16 @@ async function modelleKos(p: EvalProfil, d: ReturnType<typeof degismezler>, rapo
|
||||
},
|
||||
aday: { sira: d.params.sira, puan_turu: turLabel(d.params.tur) },
|
||||
};
|
||||
const jev = t.gerekce === KURTARMA_GEREKCESI || r.devMock ? null : await jevSor(state, JEV_SORU_GRUPLARI.gerekce);
|
||||
// Şablon gerekçe koddan gelir (modelin metni değil): hakeme sorulmaz, kod denetimleri yine koşar
|
||||
const sablon = sablonNedeni.get(t.programId) ?? null;
|
||||
const jev = sablon != null || r.devMock ? null : await jevSor(state, JEV_SORU_GRUPLARI.gerekce);
|
||||
const k: Kontrol[] = [
|
||||
{ ad: "yasak-ifade", durum: yasak.length ? "kaldi" : "gecti", neden: yasak.length ? yasak.join(" | ") : undefined },
|
||||
{ ad: "il-celiski", durum: yanlisIl.length ? "kaldi" : "gecti", neden: yanlisIl.length ? `gerekçe ${yanlisIl.join(", ")} diyor, program ${prog?.il}` : undefined },
|
||||
{ ad: "sayi", durum: sayi.length ? "gri" : "gecti", neden: sayi.length ? `kaynakta yok: ${sayi.join(", ")}` : undefined },
|
||||
...(jev ? jevKontrolleri(jev, null) : []),
|
||||
];
|
||||
return { sira: t.sira, programId: t.programId, dilim: t.dilim, program: prog ? `${prog.isim} — ${prog.universite}` : "?", taban: prog?.efektifSira ?? null, gerekce: t.gerekce, jev: jev?.durum === "ok" ? jev.p : null, karar: genelKarar(k), kontroller: k.filter((x) => x.durum !== "gecti") };
|
||||
return { sira: t.sira, programId: t.programId, dilim: t.dilim, program: prog ? `${prog.isim} — ${prog.universite}` : "?", taban: prog?.efektifSira ?? null, gerekce: t.gerekce, sablon, jev: jev?.durum === "ok" ? jev.p : null, karar: genelKarar(k), kontroller: k.filter((x) => x.durum !== "gecti") };
|
||||
}),
|
||||
);
|
||||
const kalan = satirlar.filter((s) => s.karar === "kaldi").length;
|
||||
@@ -340,7 +359,7 @@ async function main() {
|
||||
|
||||
let model: unknown = null;
|
||||
if (bayrakVar("model")) {
|
||||
const { raporUret } = await import("@/lib/ai/rapor");
|
||||
const { raporUretAyrintili } = await import("@/lib/ai/rapor");
|
||||
const { raporModeli, saglayiciAdi, anahtarVar } = await import("@/lib/ai/client");
|
||||
const butce = Number(argOku("butce")) || 1;
|
||||
const eszaman = Number(argOku("eszaman")) || 4;
|
||||
@@ -351,7 +370,7 @@ async function main() {
|
||||
det,
|
||||
eszaman,
|
||||
async ({ p, d }) => {
|
||||
const s = await modelleKos(p, d, raporUret);
|
||||
const s = await modelleKos(p, d, raporUretAyrintili);
|
||||
console.log(` ${s.karar.padEnd(6)} ${p.id} ${(s.ms / 1000).toFixed(1)} sn, ${s.cagri} çağrı, ${s.maliyet == null ? "?" : s.maliyet.toFixed(4)} $ ${s.kontroller.filter((k) => k.durum !== "gecti").map((k) => `${k.ad}${k.neden ? `(${k.neden})` : ""}`).join("; ")}`);
|
||||
return s;
|
||||
},
|
||||
@@ -374,7 +393,45 @@ async function main() {
|
||||
semaGecersiz: kontrolKalan("sema"),
|
||||
uretimHatasi: kontrolKalan("uretim"),
|
||||
hamHavuzDisiProgram: sonuclar.reduce((a, s) => a + s.ham.reduce((b, h) => b + h.havuzDisi, 0), 0),
|
||||
ikinciDenemeGereken: sonuclar.filter((s) => s.ham.length > 1).length,
|
||||
ikinciDenemeGereken: sonuclar.filter((s) => (s.tani?.modelCagrisi ?? s.cagri) > 1).length,
|
||||
// Üst sağlayıcı (OpenRouter'ın yönlendirdiği): çağrı sayısı ve reasoning kapalı isteğine uymayanlar
|
||||
saglayicilar: (() => {
|
||||
const m: Record<string, { cagri: number; dusunen: number; ortMs: number; tokSn: number }> = {};
|
||||
for (const c of sonuclar.flatMap((s) => s.tani?.cagrilar ?? [])) {
|
||||
const k = (m[c.saglayici ?? "?"] ??= { cagri: 0, dusunen: 0, ortMs: 0, tokSn: 0 });
|
||||
k.cagri++;
|
||||
if (c.dusunmeToken > 0) k.dusunen++;
|
||||
k.ortMs += c.ms;
|
||||
k.tokSn += c.ciktiToken;
|
||||
}
|
||||
for (const k of Object.values(m)) {
|
||||
k.tokSn = Math.round(k.tokSn / (k.ortMs / 1000));
|
||||
k.ortMs = Math.round(k.ortMs / k.cagri);
|
||||
}
|
||||
return m;
|
||||
})(),
|
||||
deterministikListe: sonuclar.filter((s) => s.tani?.kaynak === "deterministik").length,
|
||||
zamanAsimi: sonuclar.filter((s) => s.tani?.zamanAsimi).length,
|
||||
// Kod onarımı (ikinci model çağrısı yerine): kaç listede, kaç satır
|
||||
onarim: {
|
||||
liste: sonuclar.filter((s) => s.tani && s.tani.onarim.havuzDisi + s.tani.onarim.yinelenen + s.tani.onarim.yanlisDilim + s.tani.onarim.tamamlanan > 0).length,
|
||||
havuzDisi: sonuclar.reduce((a, s) => a + (s.tani?.onarim.havuzDisi ?? 0), 0),
|
||||
yinelenen: sonuclar.reduce((a, s) => a + (s.tani?.onarim.yinelenen ?? 0), 0),
|
||||
yanlisDilimTasinan: sonuclar.reduce((a, s) => a + (s.tani?.onarim.yanlisDilim ?? 0), 0),
|
||||
tamamlanan: sonuclar.reduce((a, s) => a + (s.tani?.onarim.tamamlanan ?? 0), 0),
|
||||
},
|
||||
// Şablona düşme: çıktı denetiminin bedeli (çok yüksekse prompt/model sorunu)
|
||||
sablon: {
|
||||
gerekce: sonuclar.reduce((a, s) => a + (s.tani?.sablonGerekce.length ?? 0), 0),
|
||||
gerekceTamamlama: sonuclar.reduce((a, s) => a + (s.tani?.onarim.tamamlanan ?? 0), 0),
|
||||
gerekceYasak: sonuclar.reduce((a, s) => a + (s.tani?.sablonGerekce.filter((x) => x.neden.startsWith("yasak")).length ?? 0), 0),
|
||||
gerekceYer: sonuclar.reduce((a, s) => a + (s.tani?.sablonGerekce.filter((x) => x.neden.startsWith("başka programa")).length ?? 0), 0),
|
||||
gerekceSayi: sonuclar.reduce((a, s) => a + (s.tani?.sablonGerekce.filter((x) => x.neden.startsWith("dayanaksız")).length ?? 0), 0),
|
||||
genel: sonuclar.filter((s) => s.tani?.genelSablon).length,
|
||||
uyariAtilan: sonuclar.reduce((a, s) => a + (s.tani?.uyariAtilan.length ?? 0), 0),
|
||||
uyariEklenen: sonuclar.reduce((a, s) => a + (s.tani?.uyariSablon ?? 0), 0),
|
||||
listeUyariAtilan: sonuclar.filter((s) => s.tani?.uyariAtilan.length).length,
|
||||
},
|
||||
satirSayisiTutmayan: kontrolKalan("satir-sayisi"),
|
||||
nihaiHavuzDisi: kontrolKalan("havuz-disi"),
|
||||
gerekce: {
|
||||
@@ -392,6 +449,9 @@ async function main() {
|
||||
console.log(`\nÖzet (model): ${ozet.gecti} geçti · ${ozet.gri} gri · ${ozet.kaldi} kaldı / ${ozet.profil}${ozet.atlananProfil ? ` (bütçe freni: ${ozet.atlananProfil} profil koşulmadı)` : ""}`);
|
||||
console.log(` ham havuz-dışı program: ${ozet.hamHavuzDisiProgram} · şema geçersiz: ${ozet.semaGecersiz} · 2. deneme: ${ozet.ikinciDenemeGereken} · satır sayısı tutmayan: ${ozet.satirSayisiTutmayan}`);
|
||||
console.log(` gerekçe ${ozet.gerekce.toplam}: yasak ifade ${ozet.gerekce.yasakIfade} · il çelişkisi ${ozet.gerekce.ilCeliski} · sayı adayı ${ozet.gerekce.sayiAdayi} · Jev ihlal ${ozet.gerekce.jevIhlal} · Jev gri ${ozet.gerekce.jevGri} · Jev atlanan ${ozet.gerekce.jevAtlanan}`);
|
||||
console.log(` onarım: ${ozet.onarim.liste} liste (yinelenen ${ozet.onarim.yinelenen}, taşınan ${ozet.onarim.yanlisDilimTasinan}, tamamlanan ${ozet.onarim.tamamlanan}) · deterministik liste ${ozet.deterministikListe} · zaman aşımı ${ozet.zamanAsimi}`);
|
||||
console.log(` şablon: gerekçe ${ozet.sablon.gerekce}/${ozet.gerekce.toplam} (tamamlama ${ozet.sablon.gerekceTamamlama}, yasak ${ozet.sablon.gerekceYasak}, yer ${ozet.sablon.gerekceYer}, sayı ${ozet.sablon.gerekceSayi}) · genel ${ozet.sablon.genel}/${ozet.profil} · uyarı atılan ${ozet.sablon.uyariAtilan} (${ozet.sablon.listeUyariAtilan} liste)`);
|
||||
console.log(` üst sağlayıcılar: ${Object.entries(ozet.saglayicilar).map(([ad, k]) => `${ad} ${k.cagri} çağrı/ort ${(k.ortMs / 1000).toFixed(1)} sn/${k.tokSn} tok·sn${k.dusunen ? `/DÜŞÜNEN ${k.dusunen}` : ""}`).join(" · ")}`);
|
||||
console.log(` gecikme p50 ${ozet.gecikmeMs.p50} ms · p95 ${ozet.gecikmeMs.p95} ms`);
|
||||
console.log(` harcama: ${TOPLAM.cagri} model çağrısı, ${TOPLAM.girdiToken} girdi + ${TOPLAM.ciktiToken} çıktı token, ${TOPLAM.maliyet.toFixed(4)} $${TOPLAM.maliyetBilinmeyenCagri ? ` (+${TOPLAM.maliyetBilinmeyenCagri} çağrının maliyeti bildirilmedi)` : ""}; Jev ${JEV_TOPLAM.cagri} çağrı ≈ ${jevMaliyeti().toFixed(4)} $`);
|
||||
// Dosya küçük kalsın: geçen satırların gerekçesi zaten `liste.tercihler` içinde
|
||||
|
||||
Reference in New Issue
Block a user