150 lines
4.3 KiB
TypeScript
150 lines
4.3 KiB
TypeScript
|
|
/**
|
||
|
|
* Direct Z.ai API test - standalone runner
|
||
|
|
*/
|
||
|
|
import { createProxyServer } from "../src/proxy-server.js";
|
||
|
|
|
||
|
|
const ZAI_KEY = process.env.ZAI_API_KEY;
|
||
|
|
|
||
|
|
if (!ZAI_KEY) {
|
||
|
|
console.error("Error: ZAI_API_KEY environment variable is not set");
|
||
|
|
console.log("\nUsage: ZAI_API_KEY=your_key bun tests/zai-direct-test.ts");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("=== Z.ai Handler Test ===");
|
||
|
|
console.log(`API Key: ${ZAI_KEY.slice(0, 8)}...${ZAI_KEY.slice(-4)}`);
|
||
|
|
|
||
|
|
// Test 1: Direct Z.ai API call
|
||
|
|
async function testDirectApi() {
|
||
|
|
console.log("\n[Test 1] Direct Z.ai API call...");
|
||
|
|
|
||
|
|
const response = await fetch("https://api.z.ai/api/coding/paas/v4/chat/completions", {
|
||
|
|
method: "POST",
|
||
|
|
headers: {
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
"Authorization": `Bearer ${ZAI_KEY}`,
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
model: "glm-4.6",
|
||
|
|
max_tokens: 100,
|
||
|
|
stream: true,
|
||
|
|
messages: [
|
||
|
|
{ role: "user", content: "Say 'Hello from GLM!' and nothing else." }
|
||
|
|
]
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
const error = await response.text();
|
||
|
|
console.error(`❌ API Error (${response.status}):`, error);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("✓ Response status:", response.status);
|
||
|
|
console.log("✓ Content-Type:", response.headers.get("content-type"));
|
||
|
|
|
||
|
|
const reader = response.body!.getReader();
|
||
|
|
const decoder = new TextDecoder();
|
||
|
|
let output = "";
|
||
|
|
let textContent = "";
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) break;
|
||
|
|
const chunk = decoder.decode(value, { stream: true });
|
||
|
|
output += chunk;
|
||
|
|
|
||
|
|
// Extract content from SSE
|
||
|
|
const lines = chunk.split("\n");
|
||
|
|
for (const line of lines) {
|
||
|
|
if (line.startsWith("data: ") && !line.includes("[DONE]")) {
|
||
|
|
try {
|
||
|
|
const data = JSON.parse(line.slice(6));
|
||
|
|
const content = data.choices?.[0]?.delta?.content;
|
||
|
|
if (content) textContent += content;
|
||
|
|
} catch {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("✓ Response text:", textContent || "(streaming response received)");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test 2: Through Claudish proxy
|
||
|
|
async function testThroughProxy() {
|
||
|
|
console.log("\n[Test 2] Through Claudish proxy...");
|
||
|
|
|
||
|
|
const proxy = await createProxyServer(3458, undefined, "z-ai/glm-4.6");
|
||
|
|
console.log(`✓ Proxy started at ${proxy.url}`);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch(`${proxy.url}/v1/messages`, {
|
||
|
|
method: "POST",
|
||
|
|
headers: { "Content-Type": "application/json" },
|
||
|
|
body: JSON.stringify({
|
||
|
|
model: "z-ai/glm-4.6",
|
||
|
|
max_tokens: 100,
|
||
|
|
messages: [{ role: "user", content: "Say 'Hello through proxy!' and nothing else." }]
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
const error = await response.text();
|
||
|
|
console.error(`❌ Proxy Error (${response.status}):`, error);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("✓ Response status:", response.status);
|
||
|
|
|
||
|
|
const reader = response.body!.getReader();
|
||
|
|
const decoder = new TextDecoder();
|
||
|
|
let output = "";
|
||
|
|
let textContent = "";
|
||
|
|
let hasMessageStart = false;
|
||
|
|
let hasMessageStop = false;
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
const { done, value } = await reader.read();
|
||
|
|
if (done) break;
|
||
|
|
const chunk = decoder.decode(value, { stream: true });
|
||
|
|
output += chunk;
|
||
|
|
|
||
|
|
if (chunk.includes("message_start")) hasMessageStart = true;
|
||
|
|
if (chunk.includes("message_stop")) hasMessageStop = true;
|
||
|
|
|
||
|
|
// Extract text from Claude format
|
||
|
|
const textMatches = chunk.matchAll(/"text_delta".*?"text":\s*"([^"]*)"/g);
|
||
|
|
for (const match of textMatches) {
|
||
|
|
textContent += match[1].replace(/\\n/g, "\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("✓ Message start:", hasMessageStart ? "Yes" : "No");
|
||
|
|
console.log("✓ Message stop:", hasMessageStop ? "Yes" : "No");
|
||
|
|
console.log("✓ Response text:", textContent || "(content received)");
|
||
|
|
return hasMessageStart && hasMessageStop;
|
||
|
|
} finally {
|
||
|
|
await proxy.shutdown();
|
||
|
|
console.log("✓ Proxy shutdown");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run tests
|
||
|
|
(async () => {
|
||
|
|
let success = true;
|
||
|
|
|
||
|
|
const directOk = await testDirectApi();
|
||
|
|
if (!directOk) {
|
||
|
|
console.log("\n❌ Direct API test failed, skipping proxy test");
|
||
|
|
success = false;
|
||
|
|
} else {
|
||
|
|
const proxyOk = await testThroughProxy();
|
||
|
|
if (!proxyOk) success = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("\n" + "=".repeat(30));
|
||
|
|
console.log(success ? "✓ All tests passed!" : "❌ Some tests failed");
|
||
|
|
process.exit(success ? 0 : 1);
|
||
|
|
})();
|