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>
95 lines
5.1 KiB
JavaScript
95 lines
5.1 KiB
JavaScript
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 } 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 Z.ai GLM 5.3 Flash 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-flash');
|
||
assert.equal(tenders[0].baslikTr, 'TR Austria – Works');
|
||
assert.equal(tenders[1].ceviriKaynagi, 'model');
|
||
} finally { globalThis.fetch = orijinal; geri(); }
|
||
});
|