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:
bilalgursen
2026-09-25 01:13:58 +03:00
parent c23910cc79
commit aa2051732d
15 changed files with 1996 additions and 8 deletions

View File

@@ -0,0 +1,29 @@
import type { NextRequest } from "next/server";
import { getSession } from "@/lib/session";
import { profilAlanYaz, profilGetir } from "@/lib/danisman-profil-db";
import { PROFIL_ALANLARI, profilSatirlari, type ProfilAlani } from "@/lib/danisman-profil";
/**
* Danışman profili — sohbetten `profil_guncelle` aracıyla yazılan alanlar.
* GET: alanlar + öğrenciye okunan satırlar. DELETE {alan}: tek alanı siler
* (KVKK: öğrenci her alanı görür ve tek tıkla siler). Yazma yalnız araç
* üzerinden; elle düzenleme sohbette "şunu değiştir" diyerek yapılır.
*/
export async function GET() {
const session = await getSession();
if (!session) return Response.json({ error: "Oturum gerekli." }, { status: 401 });
const profil = await profilGetir(session.user.id);
return Response.json({ profil, satirlar: profilSatirlari(profil) });
}
export async function DELETE(request: NextRequest) {
const session = await getSession();
if (!session) return Response.json({ error: "Oturum gerekli." }, { status: 401 });
const body = (await request.json().catch(() => null)) as { alan?: unknown } | null;
const alan = typeof body?.alan === "string" ? (body.alan as ProfilAlani) : null;
if (!alan || !PROFIL_ALANLARI.includes(alan)) {
return Response.json({ error: "Geçersiz alan." }, { status: 400 });
}
const profil = await profilAlanYaz(session.user.id, alan, null);
return Response.json({ profil, satirlar: profilSatirlari(profil) });
}