llm-provider-smoke.mjs 2.62 KB
#!/usr/bin/env node
/**
 * 真实 API smoke —— 对 Gemini 与一个 OpenAI 兼容厂商(volcengine / openrouter)各发一次真实 chat。
 * env 有对应 key 才跑,否则 SKIP。任一断言失败即 process.exit(1)。
 *
 * env:GEMINI_API_KEY(+可选 GEMINI_BASE_URL)、VOLCENGINE_API_KEY(+可选 VOLCENGINE_BASE_URL)、
 *      OPENROUTER_API_KEY(+可选 OPENROUTER_BASE_URL)。无 key 的 provider 跳过。
 *
 * 结构参照 apps/visual-game-server/tools/scenarios/waitlist-e2e(真实链路 + 生成报告)。
 */

import { createLLM, createStaticKeyProvider } from '../../src/index.js'

/** @type {Array<{ provider:string, envKey:string, envBase:string, model:string }>} */
const TARGETS = [
  { provider: 'gemini', envKey: 'GEMINI_API_KEY', envBase: 'GEMINI_BASE_URL', model: process.env.GEMINI_MODEL ?? 'gemini-2.5-flash' },
  { provider: 'volcengine', envKey: 'VOLCENGINE_API_KEY', envBase: 'VOLCENGINE_BASE_URL', model: process.env.VOLCENGINE_MODEL ?? 'doubao-pro-32k' },
  { provider: 'openrouter', envKey: 'OPENROUTER_API_KEY', envBase: 'OPENROUTER_BASE_URL', model: process.env.OPENROUTER_MODEL ?? 'google/gemini-2.5-flash' },
]

/** @param {{ provider:string, model:string, apiKey:string, baseUrl?:string }} cfg */
async function smokeOne({ provider, model, apiKey, baseUrl }) {
  const llm = createLLM({
    provider,
    model,
    keyProvider: createStaticKeyProvider({ api_key: apiKey, base_url: baseUrl }),
  })
  const r = await llm.chat({ messages: [{ role: 'user', content: 'Reply with the single word: ok' }] })
  if (typeof r.content !== 'string' || r.content.length === 0) {
    throw new Error(`[${provider}] empty content`)
  }
  if (!(r.usage.total_tokens > 0)) {
    throw new Error(`[${provider}] usage.total_tokens not > 0`)
  }
  return r
}

async function main() {
  const lines = []
  let ran = 0
  let failed = 0
  for (const t of TARGETS) {
    const apiKey = process.env[t.envKey]
    if (!apiKey) {
      lines.push(`- ${t.provider} (${t.model}): SKIP (no ${t.envKey})`)
      continue
    }
    ran += 1
    try {
      const r = await smokeOne({ provider: t.provider, model: t.model, apiKey, baseUrl: process.env[t.envBase] })
      const snip = r.content.slice(0, 40).replace(/\n/g, ' ')
      lines.push(`- ${t.provider} (${t.model}): PASS content="${snip}" usage.total_tokens=${r.usage.total_tokens}`)
    } catch (err) {
      failed += 1
      lines.push(`- ${t.provider} (${t.model}): FAIL ${err?.reason ?? ''} ${String(err?.message ?? err)}`)
    }
  }
  console.log('# llm-provider-smoke')
  console.log(lines.join('\n'))
  console.log(`\nran=${ran} failed=${failed}`)
  if (failed > 0) process.exit(1)
}

main()