From 6e107200bbd6779178b3e5cafc160a4b9d6255b3 Mon Sep 17 00:00:00 2001 From: Linghong Date: Tue, 9 Jun 2026 19:17:57 +0800 Subject: [PATCH] add test file --- scripts/test-ve-llm.mjs | 163 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 scripts/test-ve-llm.mjs diff --git a/scripts/test-ve-llm.mjs b/scripts/test-ve-llm.mjs new file mode 100644 index 00000000..764a5e55 --- /dev/null +++ b/scripts/test-ve-llm.mjs @@ -0,0 +1,163 @@ +import { readFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, '..'); + +function loadEnv(path) { + const content = readFileSync(path, 'utf-8'); + const env = {}; + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const eqIndex = trimmed.indexOf('='); + if (eqIndex === -1) continue; + const key = trimmed.slice(0, eqIndex).trim(); + let value = trimmed.slice(eqIndex + 1).trim(); + if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { + value = value.slice(1, -1); + } + env[key] = value; + } + return env; +} + +const env = loadEnv(resolve(root, '.env.secrets.local')); +const BASE = env.VECTOR_ENGINE_BASE_URL?.replace(/\/+$/, '') || 'https://api.vectorengine.cn'; +const KEY = env.VECTOR_ENGINE_API_KEY || ''; + +if (!KEY) { + console.error('未找到 VECTOR_ENGINE_API_KEY'); + process.exit(1); +} + +const TIMEOUT_MS = 60_000; + +async function test(name, method, path, body = null) { + const url = `${BASE}${path}`; + const start = Date.now(); + try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); + const headers = { + 'Authorization': `Bearer ${KEY}`, + 'Content-Type': 'application/json', + }; + const options = { method, headers, signal: controller.signal }; + if (body) options.body = JSON.stringify(body); + + const resp = await fetch(url, options); + clearTimeout(timer); + const elapsed = Date.now() - start; + const text = await resp.text(); + let json = null; + try { json = JSON.parse(text); } catch {} + + if (resp.ok) { + const model = json?.model || json?.data?.[0]?.id || '?'; + const summary = json?.choices?.[0] ? `choices[0]: ${json.choices[0].message?.content?.slice(0, 80)}` : + json?.output_text ? `output_text: ${json.output_text.slice(0, 80)}` : + json?.data ? `${json.data.length} models` : JSON.stringify(json).slice(0, 120); + return { ok: true, elapsed, code: resp.status, model, summary }; + } else { + const errMsg = json?.error?.message || json?.message || text.slice(0, 200); + return { ok: false, elapsed, code: resp.status, error: errMsg }; + } + } catch (e) { + const elapsed = Date.now() - start; + return { ok: false, elapsed, code: 0, error: e.name === 'AbortError' ? `超时(${TIMEOUT_MS / 1000}s)` : e.message }; + } +} + +console.log(`VectorEngine LLM 能力探测`); +console.log(`目标: ${BASE}\n`); + +const tests = [ + // 1. 探测 /v1/models - 基础连通性 + 列出可用模型 + { name: 'GET /v1/models (列出可用模型)', method: 'GET', path: '/v1/models' }, + + // 2. Chat Completions - 最标准协议,项目已有 LlmProvider::OpenAiCompatible 支持 + { + name: 'POST /v1/chat/completions (Chat)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-4o', + messages: [{ role: 'user', content: '回复 ok,不要解释' }], + max_tokens: 10, + }, + }, + + // 3. Responses - Apimart 当前使用的协议 + { + name: 'POST /v1/responses (Responses)', + method: 'POST', + path: '/v1/responses', + body: { + model: 'gpt-4o', + input: [ + { role: 'user', content: [{ type: 'input_text', text: '回复 ok,不要解释' }] }, + ], + }, + }, + + // 4. 测试 gpt-5 (creative_agent 模型) + { + name: 'POST /v1/chat/completions (gpt-5, Chat)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-5', + messages: [{ role: 'user', content: '回复 ok' }], + max_tokens: 10, + }, + }, + + // 5. 抓大鹅生成需要的 JSON 输出能力验证 + { + name: 'POST /v1/chat/completions (JSON 输出: 抓大鹅物品)', + method: 'POST', + path: '/v1/chat/completions', + body: { + model: 'gpt-4o', + messages: [ + { role: 'system', content: '你是抓大鹅游戏编辑,只返回 JSON。' }, + { role: 'user', content: '题材:水果。请生成 JSON:{"gameName":"水果切切乐","items":[{"name":"苹果","itemSize":"中"},{"name":"西瓜","itemSize":"大"}]}' }, + ], + max_tokens: 200, + }, + }, +]; + +let pass = 0; +let fail = 0; + +for (let i = 0; i < tests.length; i++) { + const t = tests[i]; + console.log(`[${i + 1}/${tests.length}] ${t.name}`); + const result = await test(t.name, t.method, t.path, t.body); + + if (result.ok) { + console.log(` ✅ HTTP ${result.code} ${result.elapsed}ms model: ${result.model}`); + console.log(` ${result.summary}`); + pass++; + } else { + const codeStr = result.code === 0 ? 'NET' : `HTTP ${result.code}`; + console.log(` ❌ ${codeStr} ${result.elapsed}ms ${result.error}`); + fail++; + } + console.log(); +} + +console.log(`=== 结果: ${pass}/${tests.length} 通过, ${fail}/${tests.length} 失败 ===`); + +// 结论 +if (pass >= 3) { + console.log('\n✅ VectorEngine 支持 LLM 文本调用,可替代 Apimart。'); + console.log(' 将 .env.secrets.local 中 APIMART_BASE_URL 改为 VectorEngine 地址即可。'); +} else if (pass <= 1) { + console.log('\n❌ VectorEngine 不支持 LLM 文本调用。'); +} else { + console.log('\n⚠️ 部分支持,需进一步评估。'); +}