feat(ai): Z.ai GLM birincil sağlayıcı; OpenRouter yalnız yedek
Quality gates / test-build (push) Canceled after 0s
Quality gates / container (push) Canceled after 0s

OpenRouter anahtarı iptal olup bakiye eksiye düşünce şartname analizi,
TED çevirisi ve site taraması durmuştu. Artık ZAI_API_KEY ile GLM 5.3 ve
GLM 5.3 Flash doğrudan Z.ai Coding Plan üzerinden çağrılır; kalan modeller
OpenRouter anahtarı varsa yedek olarak denenir.

- server/zai.mjs: doğrudan istemci; glm-5.x için reasoning_effort:low,
  glm-4.x için thinking kapalı; süreç genelinde en fazla 2 eşzamanlı istek
  (kullanıcı istekleri arka plan çevirisinden öncelikli); geçici 429'da
  aynı modelle yeniden deneme, 1113 bakiye hatasında hemen sıradaki model.
- ai.mjs: 'zai' (GLM 5.3) sağlayıcısı eklendi, Z.ai yolu yeni istemciyi
  kullanıyor; varsayılan sıra zai → glmflash → gemini → …
- ted-ceviri.mjs: önce ZAI_TRANSLATE_MODELS (glm-5.3-flash, glm-5.3),
  sonra OpenRouter; 2'li eşzamanlı parça işleme.
- Admin model kataloğuna GLM 5.3 eklendi, kayıtlı zincirler sürüm 4'e taşınır.
- tests/zai.test.mjs: düşünme ayarı, 429/1113 davranışı, eşzamanlılık,
  analyzeSpec ve TED çevirisinin Z.ai'ye gittiği.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 23:11:41 +03:00
co-authored by Claude Fable 5.1
parent be41ec7f4a
commit 7fa71f1a4e
8 changed files with 323 additions and 48 deletions
+4 -4
View File
@@ -70,7 +70,7 @@ export const AiModelSettings = () => {
<Zap className="w-4 h-4 text-emerald-700 shrink-0 mt-0.5" />
<div className="text-xs text-emerald-900 leading-relaxed">
<strong>AI aktif</strong> — {cfg.providers.length} model kullanılabilir
{aktifYol === 'openrouter' ? ' (OpenRouter üzerinden tek anahtarla)' : ' (doğrudan sağlayıcı anahtarlarıyla)'}.
{aktifYol === 'openrouter' ? ' (OpenRouter üzerinden tek anahtarla)' : ' (birincil sağlayıcı doğrudan, kalanlar yedek)'}.
<div className="font-mono text-[11px] mt-1 text-emerald-800">
{cfg.providers.map((p) => `${p.label}: ${p.model}`).join(' · ')}
</div>
@@ -80,9 +80,9 @@ export const AiModelSettings = () => {
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 flex gap-3">
<AlertTriangle className="w-4 h-4 text-amber-700 shrink-0 mt-0.5" />
<p className="text-xs text-amber-900 leading-relaxed">
<strong>AI anahtarı yok.</strong> Sunucuya tek bir <code className="bg-amber-100 px-1 rounded">OPENROUTER_API_KEY</code> ekleyin —
aşağıdaki tüm modeller bu tek anahtarla çalışır. Aşağıdaki sıralama yalnızca tercih kaydeder;
çağrılar anahtar eklendiğinde başlar.
<strong>AI anahtarı yok.</strong> Sunucuya <code className="bg-amber-100 px-1 rounded">ZAI_API_KEY</code> (birincil, GLM modelleri)
ve isteğe bağlı yedek <code className="bg-amber-100 px-1 rounded">OPENROUTER_API_KEY</code> ekleyin.
Aşağıdaki sıralama yalnızca tercih kaydeder; çağrılar anahtar eklendiğinde başlar.
</p>
</div>
)
+8 -7
View File
@@ -465,6 +465,7 @@ export const AppProvider = ({ children }) => {
// AI Model Yönetimi (admin panelden seçilebilir + fallback zinciri)
// ---------------------------------------------------------------------------
const AI_MODEL_CATALOG = [
{ id: 'zai', label: 'GLM 5.3', provider: 'Z.ai', or: '~z-ai/glm-latest', model: 'glm-5.3', renk: 'blue' },
{ id: 'glmflash', label: 'GLM 5.3 Flash', provider: 'Z.ai', or: 'z-ai/glm-5.3-flash', model: 'glm-5.3-flash', renk: 'blue' },
{ id: 'gemini', label: 'Gemini Flash Latest', provider: 'Google', or: '~google/gemini-flash-latest', model: 'gemini-flash-latest', renk: 'amber' },
{ id: 'deepseek', label: 'DeepSeek V4 Flash Latest', provider: 'DeepSeek', or: '~deepseek/deepseek-v4-flash-latest', model: 'deepseek-chat', renk: 'indigo' },
@@ -473,21 +474,21 @@ export const AppProvider = ({ children }) => {
{ id: 'claude', label: 'Claude Opus Latest', provider: 'Anthropic', or: '~anthropic/claude-opus-latest', model: 'claude-opus-5', renk: 'orange' }
];
// Varsayılan fallback zinciri: GLM 5.3 Flash (birincil) → Gemini → DeepSeek
// (glm-latest kaldırıldı: birincil ile aynı modele çözünüyordu, gerçek yedek değildi)
const VARSAYILAN_ZINCIR = ['glmflash', 'gemini', 'deepseek'];
const ZINCIR_SURUM = 3; // artırıldığında kayıtlı zincir yeni düzene taşınır
// Varsayılan fallback zinciri (2026-09-10, Z.ai birincil): GLM 5.3 → GLM 5.3 Flash
// → Gemini → DeepSeek. İlk ikisi doğrudan Z.ai'den, kalanlar OpenRouter yedeği.
const VARSAYILAN_ZINCIR = ['zai', 'glmflash', 'gemini', 'deepseek'];
const ZINCIR_SURUM = 4; // artırıldığında kayıtlı zincir yeni düzene taşınır
const aiModelAnahtari = (email) => `ihaleist_ai_model_config:${encodeURIComponent(String(email || '').toLowerCase())}`;
const normalizeAiModelConfig = (saved) => {
if (saved) {
try {
const cfg = JSON.parse(saved);
// Geçiş: kayıtlı zinciri yeni düzene taşı (glm çıkar, glmflash → gemini başa)
// Geçiş: kayıtlı zinciri yeni düzene taşı (Z.ai GLM 5.3 ve Flash başa, eski glm çıkar)
if ((cfg.surum || 1) < ZINCIR_SURUM) {
const chain = Array.isArray(cfg.chain) ? cfg.chain : [];
const kalan = chain.filter((id) => !['glmflash', 'gemini', 'glm'].includes(id));
return { ...cfg, surum: ZINCIR_SURUM, chain: ['glmflash', 'gemini', ...kalan] };
const kalan = chain.filter((id) => !['zai', 'glmflash', 'gemini', 'glm'].includes(id));
return { ...cfg, surum: ZINCIR_SURUM, chain: ['zai', 'glmflash', 'gemini', ...kalan] };
}
// Katalogdan çıkarılmış modelleri kayıtlı zincirden ayıkla
if (Array.isArray(cfg.chain)) {