feat(danisman): profil onayı kredi harcamaz; yapay zekâ sağlayıcı adresi .env'den (AI_BASE_URL/AI_API_KEY)

- Bilal kararı (26 Eyl 2026): 'kaydedeyim mi?' sorusuna kısa evet/hayır
  cevabı bedelsiz. Sunucu son danışman mesajında onay ibaresini arar,
  mesaj ≤40 karakter ve evet/hayır kalıbıyla başlıyorsa kredi düşmez,
  ledger'a yazılmaz, iade yolu atlanır; X-Kredi-Dusuldu başlığıyla istemci
  bakiyeyi buna göre günceller. Gerçek soru içeren 'evet ama …' bedava geçmez.
- Bilal kararı: model Türkiye'de barındırılacak. client.ts baseURL ve
  anahtar AI_BASE_URL/AI_API_KEY'den okunur (OPENROUTER_* geriye uyumlu);
  sağlayıcı değişince kod değil .env değişir, /gizlilik aynı commit'te.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-25 10:44:44 +03:00
parent 5a04db7e4b
commit 70423853f3
4 changed files with 108 additions and 30 deletions

View File

@@ -1,9 +1,29 @@
import OpenAI from "openai";
// Tüm AI akışı OpenRouter üzerinden tek kapıdan gider (kullanıcı kararı,
// 7 Ağu 2026 — önceki Anthropic/Gemini/lokal sağlayıcı seçimi kaldırıldı).
// Model .env'den değiştirilebilir; rapor ve sohbet istenirse ayrışabilir.
// Tüm AI akışı OpenAI uyumlu TEK kapıdan gider. Varsayılan OpenRouter
// (7 Ağu 2026 kararı); 26 Eyl 2026 kararıyla model Türkiye'de barındırılacak:
// sağlayıcı değişince kod değil .env değişir — AI_BASE_URL + AI_API_KEY
// (OpenRouter için eski OPENROUTER_* adları da çalışır). Sağlayıcı değişirse
// /gizlilik "Hizmet sağlayıcılar" bölümü aynı commit'te güncellenir.
const VARSAYILAN_MODEL = "deepseek/deepseek-v4-flash";
const VARSAYILAN_BASE_URL = "https://openrouter.ai/api/v1";
function baseUrl(): string {
return process.env.AI_BASE_URL?.trim() || VARSAYILAN_BASE_URL;
}
function apiKey(): string | undefined {
return process.env.AI_API_KEY?.trim() || process.env.OPENROUTER_API_KEY?.trim();
}
/** OpenRouter dışı bir sağlayıcı tanımlı mı (gizlilik metni ve log için). */
export function saglayiciAdi(): string {
try {
return new URL(baseUrl()).hostname;
} catch {
return "bilinmiyor";
}
}
export function raporModeli(): string {
return (
@@ -23,20 +43,20 @@ export function sohbetModeli(): string {
/** API anahtarı tanımlı mı (mock moduna düşülecek mi)? */
export function anahtarVar(): boolean {
return Boolean(process.env.OPENROUTER_API_KEY);
return Boolean(apiKey());
}
const globalForAi = globalThis as unknown as { __openrouter?: OpenAI };
export function getOpenRouter(): OpenAI {
const apiKey = process.env.OPENROUTER_API_KEY;
if (!apiKey) {
const key = apiKey();
if (!key) {
throw new Error("AI_KEY_MISSING");
}
if (!globalForAi.__openrouter) {
globalForAi.__openrouter = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey,
baseURL: baseUrl(),
apiKey: key,
// Tek retry otoritesi cagri.ts'teki geciciHatadaTekrarla
maxRetries: 0,
// OpenRouter panelindeki kullanım dökümünde uygulama atfı (opsiyonel)