Files
hikmettanandClaude Fable 5.1 9b3809e21b
Quality gates / test-build (push) Canceled after 0s
Quality gates / container (push) Canceled after 0s
fix(ted-ceviri): GLM harf bozulmasını onar, TED çevirisinde önce GLM 5.3
Canlı ölçüm (2026-09-11): glm-5.3-flash 22 başlığın 4'ünde kelime içi
büyük İ ve büyük harfe dönen kuyruk üretti ("Arazİ araçları",
"Elektrİklİ", "temİNİ"); açıklamalar temizdi.

- harfOnar: kelime küçük harfle devam ettikten sonra gelen büyükleri Türkçe
  kuralıyla küçültür; tamamı büyük kısaltmalara dokunmaz. Model çıktısına
  ve daha önce önbelleğe girmiş kayıtlara okunurken uygulanır.
- Çeviri prompt'una Türkçe yazım kuralı uyarısı eklendi.
- ZAI_TRANSLATE_MODELS varsayılanı glm-5.3 → glm-5.3-flash (paketler 6
  ilanlık, hız kaybı küçük; başlıklar kullanıcıya doğrudan görünüyor).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 00:26:33 +03:00

103 lines
5.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { dusunmeAyari, zaiChat, gecici429, zaiAktifIstek } from '../server/zai.mjs';
import { analyzeSpec, routeFor } from '../server/ai.mjs';
import { tedCevir, harfOnar } from '../server/ted-ceviri.mjs';
const ZAI_URL = 'https://api.z.ai/api/coding/paas/v4/chat/completions';
const ok = (content) => new Response(JSON.stringify({ choices: [{ message: { content } }] }), { status: 200 });
const hata429 = (code) => new Response(JSON.stringify({ error: { code, message: 'x' } }), { status: 429 });
function ortam(vars) {
const eski = {};
for (const [k, v] of Object.entries(vars)) { eski[k] = process.env[k]; if (v === undefined) delete process.env[k]; else process.env[k] = v; }
return () => { for (const [k, v] of Object.entries(eski)) { if (v === undefined) delete process.env[k]; else process.env[k] = v; } };
}
const TEMEL = { ZAI_API_KEY: 'synthetic-test-only', ZAI_BASE_URL: ZAI_URL, ZAI_RETRY_BASE_MS: '0', ZAI_MAX_CONCURRENCY: '2', OPENROUTER_API_KEY: undefined, GEMINI_API_KEY: undefined };
test('düşünme ayarı modele göre: glm-5 reasoning_effort low, glm-4 thinking kapalı', () => {
const geri = ortam({ ZAI_REASONING_EFFORT: undefined });
try {
assert.deepEqual(dusunmeAyari('glm-5.3'), { reasoning_effort: 'low' });
assert.deepEqual(dusunmeAyari('glm-5.3-flash'), { reasoning_effort: 'low' });
assert.deepEqual(dusunmeAyari('glm-5.3', null), {});
assert.deepEqual(dusunmeAyari('glm-4.7-flash'), { thinking: { type: 'disabled' } });
} finally { geri(); }
});
test('geçici 429 aynı modelle yeniden denenir, bakiye hatası (1113) hemen fırlatılır', async () => {
const geri = ortam(TEMEL); const orijinal = globalThis.fetch;
try {
const govdeler = [];
globalThis.fetch = async (url, { body }) => { govdeler.push(JSON.parse(body)); return govdeler.length === 1 ? hata429('1302') : ok('{"a":1}'); };
const sonuc = await zaiChat('glm-5.3-flash', [{ role: 'user', content: 'x' }], { retries429: 2 });
assert.equal(sonuc, '{"a":1}');
assert.equal(govdeler.length, 2);
assert.equal(govdeler[1].model, 'glm-5.3-flash');
assert.equal(govdeler[1].reasoning_effort, 'low');
assert.deepEqual(govdeler[1].response_format, { type: 'json_object' });
let cagri = 0;
globalThis.fetch = async () => { cagri++; return hata429('1113'); };
await assert.rejects(zaiChat('glm-5.3', [{ role: 'user', content: 'x' }], { retries429: 3 }), /1113/);
assert.equal(cagri, 1);
assert.equal(gecici429('{"error":{"code":"1302"}}'), true);
} finally { globalThis.fetch = orijinal; geri(); }
});
test('eşzamanlı Z.ai isteği sınırı süreç genelinde 2 ile sınırlı', async () => {
const geri = ortam(TEMEL); const orijinal = globalThis.fetch;
try {
let acik = 0, tepe = 0;
globalThis.fetch = async () => { acik++; tepe = Math.max(tepe, acik); await new Promise((r) => setTimeout(r, 20)); acik--; return ok('{}'); };
await Promise.all([1, 2, 3, 4].map(() => zaiChat('glm-5.3-flash', [{ role: 'user', content: 'x' }])));
assert.equal(tepe, 2);
assert.equal(zaiAktifIstek(), 0);
} finally { globalThis.fetch = orijinal; geri(); }
});
test('analyzeSpec Z.ai anahtarı varken GLM 5.3\'ü doğrudan Z.ai\'den çağırır', async () => {
const geri = ortam(TEMEL); const orijinal = globalThis.fetch;
try {
assert.deepEqual(routeFor('zai'), { yol: 'direct', key: 'synthetic-test-only', model: 'glm-5.3' });
const istekler = [];
globalThis.fetch = async (url, { body }) => { istekler.push({ url, body: JSON.parse(body) }); return ok(JSON.stringify({ summary: 'özet', qualificationCriteria: [], criticalRequirements: [], risksAndPenalties: [], bidAnalysis: {} })); };
const out = await analyzeSpec({ text: 'Sentetik şartname metni', chain: ['zai'] });
assert.equal(istekler.length, 1);
assert.equal(istekler[0].url, ZAI_URL);
assert.equal(istekler[0].body.model, 'glm-5.3');
assert.equal(istekler[0].body.reasoning_effort, 'low');
assert.equal(out.model.provider, 'Z.ai');
assert.equal(out.model.yol, 'direct');
assert.equal(out.result.summary, 'özet');
} finally { globalThis.fetch = orijinal; geri(); }
});
test('TED çevirisi OpenRouter anahtarı olmadan önce Z.ai GLM 5.3 ile çalışır', async () => {
const geri = ortam(TEMEL); const orijinal = globalThis.fetch;
try {
const istekler = [];
globalThis.fetch = async (url, { body }) => {
const b = JSON.parse(body); istekler.push({ url, model: b.model });
const ilanlar = JSON.parse(b.messages[1].content).ilanlar;
return ok(JSON.stringify({ ceviriler: ilanlar.map((i) => ({ id: i.id, baslik: `TR ${i.baslik}`, aciklama: '' })) }));
};
const tenders = [{ id: 't1', baslik: 'Austria – Works' }, { id: 't2', baslik: 'Poland – Supplies' }];
const bekleyen = await tedCevir(tenders, { blockMs: 2000 });
assert.equal(bekleyen, 0);
assert.equal(istekler.length, 1);
assert.equal(istekler[0].url, ZAI_URL);
assert.equal(istekler[0].model, 'glm-5.3');
assert.equal(tenders[0].baslikTr, 'TR Austria – Works');
assert.equal(tenders[1].ceviriKaynagi, 'model');
} finally { globalThis.fetch = orijinal; geri(); }
});
test('harfOnar kelime içi büyük İ/I bozulmasını düzeltir, kısaltmalara dokunmaz', () => {
assert.equal(harfOnar('Romanya – Elektrİklİ otobüsler – Elektrİklİ otobüs ve şarj İstasyonlarİ temİNİ'),
'Romanya – Elektrikli otobüsler – Elektrikli otobüs ve şarj İstasyonlari temini');
assert.equal(harfOnar('Denİz Habİtatlarının ve NATURA 2000 Alanlarının Tespİtİ'), 'Deniz Habitatlarının ve NATURA 2000 Alanlarının Tespiti');
assert.equal(harfOnar('İstanbul MEB İl Müdürlüğü'), 'İstanbul MEB İl Müdürlüğü');
assert.equal(harfOnar(null), '');
});