feat(eval): tercih robotu ve danışman sohbeti için tekrar koşulabilir eval + ilk taban çizgisi
- scripts/eval/robot.ts: 40 profilde modelsiz havuz değişmezleri (CI, çıkış kodu) ve --model ile gerçek liste üretimi denetimi (şema, havuz-dışı, yasak ifade, il çelişkisi, sayı tutarlılığı, Jev gerekçe grubu, gecikme, maliyet) - scripts/eval/sohbet.ts: 112 soru gerçek danismanAkisi ile; araç kuralları, metin kontrolü, sayı kaynağı, vaat regex'i, Jev danışman grubu - scripts/eval/ortak.ts: kullanım ölçümü, Jev istemcisi, kod denetimleri - package.json: eval:robot, eval:sohbet - İlk koşu sonuçları ve rapor (04-eval-sonuclari.md) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
363
scripts/eval/sohbet.ts
Normal file
363
scripts/eval/sohbet.ts
Normal file
@@ -0,0 +1,363 @@
|
||||
/**
|
||||
* Danışman sohbeti eval'i — gerçek danismanAkisi (11 araç) ile. PARA HARCAR.
|
||||
*
|
||||
* pnpm eval:sohbet # 112 soru × 1
|
||||
* pnpm eval:sohbet --limit 10 # ilk 10 (süzgeçlerden sonra)
|
||||
* pnpm eval:sohbet --etiket vaat,kriz --profil p05 --soru s001,s035
|
||||
* pnpm eval:sohbet --eszaman 5 --butce 1.5 --liste scripts/eval/sonuc/<robot>.json
|
||||
*
|
||||
* Bağlam route.ts'in kurduğu biçimde kurulur: sistemPromptu + listeOzetiCikar
|
||||
* + modelPenceresi + aracBaglami aynı modüllerden import edilir. Tek kopya:
|
||||
* kilitli (paketsiz) özetin ilk ACIK_SATIR satıra kırpılması (route.ts içinde,
|
||||
* dışa aktarılmamış). Liste: en yeni `--model` robot koşusundan; yoksa havuzdan
|
||||
* deterministik (model çağrısı yapılmaz).
|
||||
*
|
||||
* profil_guncelle gerçek kodla çalışır ama data/app.db'nin GEÇİCİ KOPYASINA
|
||||
* yazar; her soru kendi sahte kullanıcısıyla koşar, yazılan profil geri okunur.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import Database from "better-sqlite3";
|
||||
import {
|
||||
argOku,
|
||||
dayanaksizSayilar,
|
||||
evalAppDbHazirla,
|
||||
genelKarar,
|
||||
gitDurumu,
|
||||
havuzdaKos,
|
||||
icerikKontrolu,
|
||||
jevKontrolleri,
|
||||
jevMaliyeti,
|
||||
jevSor,
|
||||
JEV_TOPLAM,
|
||||
kisalt,
|
||||
olcerekKos,
|
||||
robotSonuclari,
|
||||
sonucYaz,
|
||||
tablo,
|
||||
TOPLAM,
|
||||
vaatIfadeleri,
|
||||
yuzdelik,
|
||||
type JevSonuc,
|
||||
type Karar,
|
||||
type Kontrol,
|
||||
} from "./ortak";
|
||||
import { JEV_EVAL_MODEL, JEV_SORU_GRUPLARI, type JevDanismanState, type JevSoruAdi } from "./jev-sorulari";
|
||||
import { PROFILLER, SORULAR, type EvalProfil, type EvalSoru } from "./profiller";
|
||||
import type { RaporParams, RaporSonuc } from "@/features/rapor/types/rapor";
|
||||
|
||||
const paramsYap = (p: EvalProfil): RaporParams => ({ sira: p.sihirbaz.sira, tur: p.sihirbaz.tur, ...p.sihirbaz.secimler });
|
||||
|
||||
type AracCagrisi = { ad: string; girdi: unknown; cikti: unknown; hata?: string };
|
||||
|
||||
async function main() {
|
||||
// Ortam ÖNCE: appdb modülü import anında bağlanır
|
||||
const dbYolu = evalAppDbHazirla();
|
||||
const sqlite = new Database(dbYolu);
|
||||
const { danismanAkisi } = await import("@/lib/ai/sohbet-akisi");
|
||||
const { sistemPromptu } = await import("@/lib/ai/danisman-prompt");
|
||||
const { modelPenceresi } = await import("@/lib/ai/sohbet-gecmisi");
|
||||
const { listeOzetiCikar } = await import("@/lib/ai/rapor");
|
||||
const { sohbetModeli, saglayiciAdi, anahtarVar } = await import("@/lib/ai/client");
|
||||
const { JEV_DUZELTME_NOTU } = await import("@/lib/ai/jev");
|
||||
const { ACIK_SATIR } = await import("@/lib/rapor-maske");
|
||||
const { havuzOlustur, dilimHedefiHesapla } = await import("@/lib/rapor-havuzu");
|
||||
const { PUAN_TURLERI } = await import("@/types/yokatlas");
|
||||
|
||||
if (!anahtarVar()) {
|
||||
console.error("Model anahtarı yok (AI_API_KEY / OPENROUTER_API_KEY): sohbet eval'i gerçek modelle koşulamadı.");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
// ── soru seçimi ──
|
||||
const liste = (ad: string) => argOku(ad)?.split(",").filter(Boolean);
|
||||
const etiketS = liste("etiket");
|
||||
const profilS = liste("profil");
|
||||
const soruS = liste("soru");
|
||||
const limit = Number(argOku("limit")) || Infinity;
|
||||
const eszaman = Number(argOku("eszaman")) || 5;
|
||||
const butce = Number(argOku("butce")) || 1.5;
|
||||
const sorular = SORULAR.filter(
|
||||
(s) =>
|
||||
(!etiketS || s.etiketler.some((e) => etiketS.includes(e))) &&
|
||||
(!profilS || profilS.some((p) => s.profilId.startsWith(p))) &&
|
||||
(!soruS || soruS.includes(s.id)),
|
||||
).slice(0, limit);
|
||||
|
||||
// ── liste bağlamı ──
|
||||
// --liste verilmezse: gerçek model listesi içeren en yeni robot sonucu
|
||||
const robotListeleri = new Map<string, RaporSonuc>();
|
||||
const robotOku = (yol: string) => {
|
||||
const j = JSON.parse(readFileSync(yol, "utf8")) as { model?: { profiller?: { id: string; liste: RaporSonuc | null }[] } };
|
||||
for (const p of j.model?.profiller ?? []) if (p.liste && !p.liste.devMock) robotListeleri.set(p.id, p.liste);
|
||||
};
|
||||
let robotYolu = argOku("liste") || undefined;
|
||||
if (robotYolu) robotOku(robotYolu);
|
||||
else
|
||||
for (const yol of robotSonuclari()) {
|
||||
robotOku(yol);
|
||||
if (robotListeleri.size) {
|
||||
robotYolu = yol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const listeKaynagi = new Map<string, "robot" | "deterministik">();
|
||||
const raporlar = new Map<string, RaporSonuc>();
|
||||
const raporGetir = (p: EvalProfil): RaporSonuc => {
|
||||
const var_ = raporlar.get(p.id);
|
||||
if (var_) return var_;
|
||||
let r = robotListeleri.get(p.id);
|
||||
listeKaynagi.set(p.id, r ? "robot" : "deterministik");
|
||||
if (!r) {
|
||||
// Model çağrısı yok: her dilimden taban sırası en iyi `hedef` kadar program
|
||||
const { havuz } = havuzOlustur(paramsYap(p), p.danismanProfili);
|
||||
const hedef = dilimHedefiHesapla(havuz);
|
||||
const secilen = (["hayal", "dengeli", "garanti"] as const).flatMap((d) =>
|
||||
havuz.filter((x) => x.dilim === d).sort((a, b) => (a.efektifSira ?? 0) - (b.efektifSira ?? 0)).slice(0, hedef[d]),
|
||||
);
|
||||
r = {
|
||||
tercihler: secilen.map((x, i) => ({ programId: x.id, sira: i + 1, dilim: x.dilim, gerekce: "", riskNotu: "", trendOzeti: "" })),
|
||||
genelDegerlendirme: "",
|
||||
programlar: Object.fromEntries(secilen.map((x) => [x.id, { isim: x.isim, universite: x.universite, il: x.il, unitur: x.unitur, efektifSira: x.efektifSira }])),
|
||||
} as RaporSonuc;
|
||||
}
|
||||
raporlar.set(p.id, r);
|
||||
return r;
|
||||
};
|
||||
|
||||
const now = Date.now();
|
||||
const kullaniciEkle = sqlite.prepare("INSERT OR REPLACE INTO user (id, name, email, email_verified, credit_balance, has_paket, created_at, updated_at) VALUES (?, 'Eval', ?, 1, 0, 1, ?, ?)");
|
||||
const profilEkle = sqlite.prepare("INSERT OR REPLACE INTO danisman_profil (user_id, alanlar, updated_at) VALUES (?, ?, ?)");
|
||||
const profilOku = sqlite.prepare("SELECT alanlar FROM danisman_profil WHERE user_id = ?");
|
||||
|
||||
console.log(`\n== Sohbet eval — ${sorular.length} soru, model ${sohbetModeli()} @ ${saglayiciAdi()}, eşzamanlılık ${eszaman}, bütçe ${butce} $ ==`);
|
||||
console.log(`Liste bağlamı: ${robotYolu && robotListeleri.size ? `${robotListeleri.size} profil ${robotYolu.split("/").pop()} dosyasından` : "deterministik (robot --model sonucu yok)"}\n`);
|
||||
|
||||
// src tarafındaki "[soru] araç:" / "[soru] jev" satırları özeti boğmasın
|
||||
const asilInfo = console.info;
|
||||
console.info = () => {};
|
||||
|
||||
async function soruKos(s: EvalSoru) {
|
||||
const p = PROFILLER.find((x) => x.id === s.profilId);
|
||||
if (!p) throw new Error(`Profil yok: ${s.profilId}`);
|
||||
const params = paramsYap(p);
|
||||
const rapor = raporGetir(p);
|
||||
const kilitli = p.paketli === false;
|
||||
const tamOzet = listeOzetiCikar(rapor, params.sira);
|
||||
// route.ts ile aynı kırpma (dışa aktarılmamış; "24 - ACIK_SATIR" sabiti dahil birebir)
|
||||
const listeOzeti = kilitli ? [...tamOzet.split("\n").slice(0, ACIK_SATIR), `… kalan ${24 - ACIK_SATIR} tercih kilitli.`].join("\n") : tamOzet;
|
||||
const profil = { ...(p.danismanProfili ?? {}) };
|
||||
const system = sistemPromptu({ params, listeOzeti, kilitli, profil });
|
||||
const gecmis = (s.gecmis ?? []).map((m) => ({ role: m.rol, content: m.icerik }));
|
||||
const mesajlar = modelPenceresi(gecmis, s.soru);
|
||||
|
||||
const userId = `eval-${process.pid}-${s.id}`;
|
||||
kullaniciEkle.run(userId, `${userId}@eval.invalid`, now, now);
|
||||
if (p.danismanProfili) profilEkle.run(userId, JSON.stringify(p.danismanProfili), now);
|
||||
|
||||
const araclar: AracCagrisi[] = [];
|
||||
const girdiler = new Map<string, AracCagrisi>();
|
||||
let adim = 0;
|
||||
let ilkTokenMs: number | null = null;
|
||||
let tamMetin: string | null = null;
|
||||
let yarimMetin: string | null = null;
|
||||
let akisHatasi: string | null = null;
|
||||
const t0 = Date.now();
|
||||
const { kullanim } = await olcerekKos(async () => {
|
||||
try {
|
||||
const okuyucu = danismanAkisi({
|
||||
system,
|
||||
mesajlar,
|
||||
aracBaglami: { userId, sira: params.sira, tur: params.tur, profil },
|
||||
onTamam: async (m) => {
|
||||
tamMetin = m;
|
||||
},
|
||||
onYarim: async (m) => {
|
||||
yarimMetin = m;
|
||||
},
|
||||
}).getReader();
|
||||
for (;;) {
|
||||
const { done, value: parca } = await okuyucu.read();
|
||||
if (done) break;
|
||||
const v = parca as { type: string; toolCallId?: string; toolName?: string; input?: unknown; output?: unknown; errorText?: string };
|
||||
if (v.type === "start-step") adim++;
|
||||
else if (v.type === "text-delta" && ilkTokenMs == null) ilkTokenMs = Date.now() - t0;
|
||||
else if (v.type === "tool-input-available") {
|
||||
const c: AracCagrisi = { ad: v.toolName ?? "?", girdi: v.input, cikti: undefined };
|
||||
araclar.push(c);
|
||||
if (v.toolCallId) girdiler.set(v.toolCallId, c);
|
||||
} else if (v.type === "tool-output-available" && v.toolCallId) {
|
||||
const c = girdiler.get(v.toolCallId);
|
||||
if (c) c.cikti = v.output;
|
||||
} else if (v.type === "tool-input-error" || v.type === "tool-output-error") {
|
||||
const c = v.toolCallId ? girdiler.get(v.toolCallId) : undefined;
|
||||
if (c) c.hata = v.errorText ?? v.type;
|
||||
else araclar.push({ ad: v.toolName ?? "?", girdi: v.input, cikti: undefined, hata: v.errorText ?? v.type });
|
||||
} else if (v.type === "error") akisHatasi = v.errorText ?? "hata";
|
||||
}
|
||||
} catch (e) {
|
||||
akisHatasi = e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
});
|
||||
const toplamMs = Date.now() - t0;
|
||||
|
||||
const kayitliProfil = (() => {
|
||||
const satir = profilOku.get(userId) as { alanlar: string } | undefined;
|
||||
try {
|
||||
return satir ? (JSON.parse(satir.alanlar) as Record<string, unknown>) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
const hamMetin: string = tamMetin ?? yarimMetin ?? "";
|
||||
const canliJevNotu = hamMetin.includes(JEV_DUZELTME_NOTU);
|
||||
const metin = hamMetin.replace(JEV_DUZELTME_NOTU, "").trim();
|
||||
const m = metin.toLocaleLowerCase("tr-TR");
|
||||
const cagrilan = araclar.map((a) => a.ad);
|
||||
const kontroller: Kontrol[] = [];
|
||||
|
||||
// 0) akış
|
||||
if (tamMetin == null) kontroller.push({ ad: "akis", durum: "kaldi", neden: `cevap tamamlanmadı: ${akisHatasi ?? "?"}` });
|
||||
|
||||
// 1) araç kuralları
|
||||
if (s.beklenenAraclar.length) {
|
||||
const eksik = s.beklenenAraclar.filter((a) => !cagrilan.includes(a));
|
||||
const ok = s.aracKurali === "herhangi" ? eksik.length < s.beklenenAraclar.length : eksik.length === 0;
|
||||
kontroller.push({ ad: "beklenen-arac", durum: ok ? "gecti" : "kaldi", neden: ok ? undefined : `çağrılmadı: ${eksik.join(", ")} (çağrılan: ${cagrilan.join(", ") || "yok"})` });
|
||||
}
|
||||
const yasak = (s.yasakAraclar ?? []).filter((a) => cagrilan.includes(a));
|
||||
if (s.yasakAraclar) kontroller.push({ ad: "yasak-arac", durum: yasak.length ? "kaldi" : "gecti", neden: yasak.length ? `çağrıldı: ${yasak.join(", ")}` : undefined });
|
||||
const aracHatalari = araclar.filter((a) => a.hata);
|
||||
if (aracHatalari.length) kontroller.push({ ad: "arac-hatasi", durum: "gri", neden: aracHatalari.map((a) => `${a.ad}: ${kisalt(a.hata ?? "", 80)}`).join("; ") });
|
||||
|
||||
// 2) metin kontrolü
|
||||
kontroller.push(...icerikKontrolu(metin, s.kontrol));
|
||||
|
||||
// 3) "kaydettim" deyip kaydetmeme (H4): başarılı profil_guncelle çıktısı yoksa
|
||||
const basariliKayit = araclar.filter((a) => a.ad === "profil_guncelle" && a.cikti != null && typeof a.cikti === "object" && "kaydedildi" in (a.cikti as object)).length;
|
||||
if (/kaydettim|kaydedildi|profiline ekledim|profilini güncelledim/.test(m)) {
|
||||
kontroller.push({ ad: "kaydettim-ama-yazmadi", durum: basariliKayit ? "gecti" : "kaldi", neden: basariliKayit ? undefined : "cevap kayıt bildiriyor, başarılı profil_guncelle çağrısı yok" });
|
||||
}
|
||||
|
||||
// 4) vaat regex'i
|
||||
const vaat = vaatIfadeleri(metin);
|
||||
|
||||
// 5) sayılar: araç çıktısı / liste / profil / soru / geçmiş / araç girdisi
|
||||
const kaynaklar = [system, s.soru, ...gecmis.map((g) => g.content), ...araclar.map((a) => JSON.stringify(a.girdi ?? "")), ...araclar.map((a) => JSON.stringify(a.cikti ?? ""))];
|
||||
const sayi = metin ? dayanaksizSayilar(metin, kaynaklar, params.sira) : [];
|
||||
if (sayi.length) kontroller.push({ ad: "sayi", durum: "gri", neden: `kaynakta yok: ${sayi.join(", ")}` });
|
||||
|
||||
// 6) Jev danışman grubu (tek çağrı). Kullanıcı mesajı GİTMEZ.
|
||||
let jev: JevSonuc | null = null;
|
||||
if (metin) {
|
||||
const il = p.danismanProfili?.evIl ?? null;
|
||||
const jevSorulari: JevSoruAdi[] = JEV_SORU_GRUPLARI.danisman.filter((q) => q !== "profil_il" || il != null);
|
||||
let kalan = 24_000;
|
||||
const aracCiktisi = araclar
|
||||
.filter((a) => a.cikti != null)
|
||||
.map((a) => {
|
||||
const j = kisalt(JSON.stringify(a.cikti), Math.min(8000, Math.max(0, kalan)));
|
||||
kalan -= j.length;
|
||||
return { arac: a.ad, girdi: a.girdi, cikti: j };
|
||||
});
|
||||
const state: JevDanismanState = {
|
||||
metin,
|
||||
arac_ciktisi: aracCiktisi.length ? aracCiktisi : "yok",
|
||||
liste: listeOzeti.split("\n"),
|
||||
profil: { sira: params.sira, puan_turu: PUAN_TURLERI[params.tur], il },
|
||||
};
|
||||
jev = await jevSor(state, jevSorulari);
|
||||
const kriz = s.etiketler.includes("kriz") ? "kriz" : s.etiketler.includes("kriz-degil") ? "kriz-degil" : null;
|
||||
kontroller.push(...jevKontrolleri(jev, kriz));
|
||||
}
|
||||
const jevVaatP = jev?.durum === "ok" ? (jev.p.yerlesme_vaadi ?? 0) : 0;
|
||||
if (vaat.length) kontroller.push({ ad: "vaat-regex", durum: jevVaatP >= 0.35 ? "kaldi" : "gri", neden: vaat.join(" | ") });
|
||||
|
||||
const karar: Karar = genelKarar(kontroller);
|
||||
const sorunlar = kontroller.filter((k) => k.durum !== "gecti");
|
||||
asilInfo(` ${karar.padEnd(6)} ${s.id} [${s.etiketler.join(",")}] ${(toplamMs / 1000).toFixed(1)} sn, ${adim} adım, araç: ${cagrilan.join("+") || "—"}${sorunlar.length ? ` ⇒ ${sorunlar.map((k) => `${k.ad}${k.neden ? `(${kisalt(k.neden, 90)})` : ""}`).join("; ")}` : ""}`);
|
||||
|
||||
return {
|
||||
id: s.id,
|
||||
profilId: s.profilId,
|
||||
etiketler: s.etiketler,
|
||||
soru: s.soru,
|
||||
gecmisVar: gecmis.length > 0,
|
||||
beklenenAraclar: s.beklenenAraclar,
|
||||
beklenenDavranis: s.beklenenDavranis,
|
||||
listeKaynagi: listeKaynagi.get(p.id),
|
||||
karar,
|
||||
kontroller: sorunlar,
|
||||
araclar: araclar.map((a) => ({ ad: a.ad, girdi: a.girdi, hata: a.hata, cikti: a.cikti == null ? null : kisalt(JSON.stringify(a.cikti), 1500) })),
|
||||
adim,
|
||||
cevap: metin,
|
||||
canliJevNotu,
|
||||
kayitliProfil,
|
||||
jev: jev?.durum === "ok" ? jev.p : jev ? { atlandi: jev.neden } : null,
|
||||
ilkTokenMs: ilkTokenMs as number | null,
|
||||
toplamMs,
|
||||
modelCagrisi: kullanim.cagri,
|
||||
girdiToken: kullanim.girdiToken,
|
||||
ciktiToken: kullanim.ciktiToken,
|
||||
maliyet: kullanim.maliyet,
|
||||
};
|
||||
}
|
||||
|
||||
const sonuclar = (await havuzdaKos(sorular, eszaman, soruKos, () => TOPLAM.maliyet >= butce)).filter((x) => x != null);
|
||||
console.info = asilInfo;
|
||||
|
||||
// ── özet ──
|
||||
const oran = (d: typeof sonuclar) => {
|
||||
const g = d.filter((x) => x.karar === "gecti").length;
|
||||
const gri = d.filter((x) => x.karar === "gri").length;
|
||||
const k = d.filter((x) => x.karar === "kaldi").length;
|
||||
return { n: d.length, gecti: g, gri, kaldi: k, gecmeOrani: d.length ? Math.round((100 * g) / d.length) : 0 };
|
||||
};
|
||||
const grupla = (anahtarlar: (x: (typeof sonuclar)[number]) => string[]) => {
|
||||
const g = new Map<string, typeof sonuclar>();
|
||||
for (const x of sonuclar) for (const a of anahtarlar(x)) g.set(a, [...(g.get(a) ?? []), x]);
|
||||
return Object.fromEntries([...g].sort((a, b) => b[1].length - a[1].length).map(([a, d]) => [a, oran(d)]));
|
||||
};
|
||||
const etiketBasina = grupla((x) => x.etiketler);
|
||||
const aracBasina = grupla((x) => x.beklenenAraclar);
|
||||
const kontrolBasina: Record<string, { kaldi: number; gri: number; atlandi: number }> = {};
|
||||
for (const x of sonuclar)
|
||||
for (const k of x.kontroller) {
|
||||
const o = (kontrolBasina[k.ad] ??= { kaldi: 0, gri: 0, atlandi: 0 });
|
||||
if (k.durum !== "gecti") o[k.durum]++;
|
||||
}
|
||||
const sure = sonuclar.map((x) => x.toplamMs);
|
||||
const ilk = sonuclar.map((x) => x.ilkTokenMs).filter((x): x is number => x != null);
|
||||
const ozet = {
|
||||
model: sohbetModeli(),
|
||||
saglayici: saglayiciAdi(),
|
||||
jevModel: JEV_EVAL_MODEL,
|
||||
...oran(sonuclar),
|
||||
atlananSoru: sorular.length - sonuclar.length,
|
||||
canliJevNotuEklenen: sonuclar.filter((x) => x.canliJevNotu).length,
|
||||
ortalamaAdim: sonuclar.length ? +(sonuclar.reduce((a, x) => a + x.adim, 0) / sonuclar.length).toFixed(2) : 0,
|
||||
gecikmeMs: { ilkToken: { p50: yuzdelik(ilk, 50), p95: yuzdelik(ilk, 95) }, toplam: { p50: yuzdelik(sure, 50), p95: yuzdelik(sure, 95), max: Math.max(0, ...sure) } },
|
||||
harcama: { ...TOPLAM, jev: { ...JEV_TOPLAM, maliyet: jevMaliyeti() } },
|
||||
etiketBasina,
|
||||
aracBasina,
|
||||
kontrolBasina,
|
||||
};
|
||||
|
||||
const satir = (o: Record<string, ReturnType<typeof oran>>) => Object.entries(o).map(([a, v]) => [a, v.n, v.gecti, v.gri, v.kaldi, `%${v.gecmeOrani}`]);
|
||||
console.log(`\nÖzet: ${ozet.gecti} geçti · ${ozet.gri} gri · ${ozet.kaldi} kaldı / ${ozet.n} (%${ozet.gecmeOrani})${ozet.atlananSoru ? ` — bütçe freni: ${ozet.atlananSoru} soru koşulmadı` : ""}`);
|
||||
console.log(`\n${tablo(["etiket", "n", "geçti", "gri", "kaldı", "geçme"], satir(etiketBasina))}`);
|
||||
console.log(`\n${tablo(["beklenen araç", "n", "geçti", "gri", "kaldı", "geçme"], satir(aracBasina))}`);
|
||||
console.log(`\n${tablo(["kontrol", "kaldı", "gri", "atlandı"], Object.entries(kontrolBasina).sort((a, b) => b[1].kaldi - a[1].kaldi).map(([a, v]) => [a, v.kaldi, v.gri, v.atlandi]))}`);
|
||||
console.log(`\nGecikme: ilk token p50 ${ozet.gecikmeMs.ilkToken.p50} ms / p95 ${ozet.gecikmeMs.ilkToken.p95} ms · toplam p50 ${ozet.gecikmeMs.toplam.p50} ms / p95 ${ozet.gecikmeMs.toplam.p95} ms · ortalama ${ozet.ortalamaAdim} adım`);
|
||||
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 hakem ${JEV_TOPLAM.cagri} çağrı ≈ ${jevMaliyeti().toFixed(4)} $ (${JEV_TOPLAM.atlanan} atlandı); ayrıca canlı Jev denetimi cevap başına 1 çağrı (akışın içinde, ölçülmedi)`);
|
||||
|
||||
const yol = sonucYaz("sohbet", { tur: "sohbet", tarih: new Date().toISOString(), git: gitDurumu(), argv: process.argv.slice(2), listeDosyasi: robotYolu?.split("/").pop() ?? null, ozet, sorular: sonuclar });
|
||||
console.log(`\nHam sonuç: ${yol}`);
|
||||
sqlite.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(2);
|
||||
});
|
||||
Reference in New Issue
Block a user