feat(danisman): araç kullanımı — program_ara/program_detay/universite_profili sorgu araçları + kapalı listeli profil (profil_guncelle)
- src/lib/ai/araclar.ts: OpenAI uyumlu 4 araç; sorgu araçları yalnız okur (yokatlas.db + program_ozellik + YÖK İstatistik + URAP), sayı uydurma yok. profil_guncelle yalnız 7 tanımlı alanı yazar (evIl, maxKm, ucretUst, burs, dil, ogretimTuru, kampus); sağlık/din/gelir/cinsiyet şemada yok (KVKK §8). Profil süzgeci sonucu sıfırlarsa profilsiz tekrar denenip modele söylenir. - src/lib/ai/cagri.ts: sohbetAkisiAraclarla — stream'de tool_calls biriktirme, en fazla 4 tur, son tur araç kapalı; sağlayıcı reddederse düz sohbete düşer. - src/lib/danisman-profil.ts (saf şema + metinler), danisman-profil-db.ts, drizzle 0005 danisman_profil tablosu, /api/soru/profil GET+DELETE. - /api/soru: profil prompt'a girer, araç kuralları eklendi. - Sohbet başlığı altında 'Kaydettiklerim' çipleri (tek tıkla sil); listem sayfası profili sunucudan indirir. Analitiğe yalnız alan adı gider. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
42
src/lib/mesafe.ts
Normal file
42
src/lib/mesafe.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// İl merkezleri arası kuş uçuşu mesafe (km). Profildeki "evden en fazla N km"
|
||||
// filtresi il düzeyinde çalışır: adres/koordinat tutulmaz (KVKK, §8).
|
||||
import { IL_MERKEZ, type LatLng } from "./uni-koordinatlar";
|
||||
|
||||
const DUNYA_YARICAP_KM = 6371;
|
||||
|
||||
export function kmMesafe(a: LatLng, b: LatLng): number {
|
||||
const rad = (d: number) => (d * Math.PI) / 180;
|
||||
const dLat = rad(b[0] - a[0]);
|
||||
const dLng = rad(b[1] - a[1]);
|
||||
const h =
|
||||
Math.sin(dLat / 2) ** 2 +
|
||||
Math.cos(rad(a[0])) * Math.cos(rad(b[0])) * Math.sin(dLng / 2) ** 2;
|
||||
return Math.round(2 * DUNYA_YARICAP_KM * Math.asin(Math.sqrt(h)));
|
||||
}
|
||||
|
||||
/** DB il adı biçimine (büyük harf, Türkçe) çevirir; bilinmiyorsa null. */
|
||||
export function ilAdiNormalize(ham: string): string | null {
|
||||
const aday = ham.trim().toLocaleUpperCase("tr-TR");
|
||||
if (aday in IL_MERKEZ) return aday;
|
||||
// "Afyon" → AFYONKARAHİSAR gibi öneki eşleşen tek il varsa onu al
|
||||
const adaylar = Object.keys(IL_MERKEZ).filter((il) => il.startsWith(aday));
|
||||
return adaylar.length === 1 ? adaylar[0] : null;
|
||||
}
|
||||
|
||||
/** Ev iline en fazla `km` uzaktaki iller (evin kendisi dahil), yakından uzağa. */
|
||||
export function yakinIller(evIl: string, km: number): string[] {
|
||||
const ev = IL_MERKEZ[evIl];
|
||||
if (!ev) return [];
|
||||
return Object.entries(IL_MERKEZ)
|
||||
.map(([il, k]) => ({ il, km: kmMesafe(ev, k) }))
|
||||
.filter((x) => x.km <= km)
|
||||
.sort((a, b) => a.km - b.km)
|
||||
.map((x) => x.il);
|
||||
}
|
||||
|
||||
/** İki il arası km; biri bilinmiyorsa null. */
|
||||
export function illerArasiKm(a: string, b: string): number | null {
|
||||
const ka = IL_MERKEZ[a];
|
||||
const kb = IL_MERKEZ[b];
|
||||
return ka && kb ? kmMesafe(ka, kb) : null;
|
||||
}
|
||||
Reference in New Issue
Block a user