#!/usr/bin/env node /** * Switch H5 + goosed to direct DeepSeek API (custom_deepseek), bypassing andu relay. * * Usage: * DEEPSEEK_API_KEY=sk-... node scripts/fix-deepseek-direct.mjs */ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { Agent, fetch } from 'undici'; import { createDbPool } from '../db.mjs'; import { createLlmProviderService } from '../llm-providers.mjs'; const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); function loadEnvFile(filePath) { if (!fs.existsSync(filePath)) return; for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eq = trimmed.indexOf('='); if (eq < 0) continue; const key = trimmed.slice(0, eq).trim(); const value = trimmed.slice(eq + 1).trim(); if (!process.env[key]) process.env[key] = value; } } loadEnvFile(path.join(root, '.env')); const apiKey = String(process.env.DEEPSEEK_API_KEY ?? process.argv[2] ?? '').trim(); if (!apiKey) { console.error('缺少 DEEPSEEK_API_KEY(环境变量或命令行参数)'); process.exit(1); } const CONFIG_NAME = 'DeepSeek 直连'; async function testDirectDeepSeek() { const dispatcher = new Agent({ connect: { rejectUnauthorized: true } }); const started = Date.now(); const res = await fetch('https://api.deepseek.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify({ model: 'deepseek-v4-pro', messages: [{ role: 'user', content: 'Hello' }], stream: false, }), dispatcher, }); const text = await res.text(); return { ok: res.ok, status: res.status, latencyMs: Date.now() - started, detail: text.slice(0, 300), }; } const pool = createDbPool(); const svc = createLlmProviderService(pool, { apiTarget: process.env.TKMIND_API_TARGET ?? 'https://127.0.0.1:18006', apiSecret: process.env.TKMIND_SERVER__SECRET_KEY ?? 'local-dev-secret', }); const [rows] = await pool.query( 'SELECT id, name, provider_id FROM h5_llm_provider_keys WHERE provider_id = ? OR name = ?', ['custom_deepseek', CONFIG_NAME], ); let rowId = rows[0]?.id ?? null; if (rowId) { const updated = await svc.updateKey(rowId, { name: CONFIG_NAME, apiKey, defaultModel: 'deepseek-v4-pro', }); if (!updated.ok) { console.error('更新 DeepSeek 配置失败:', updated.message); process.exit(1); } console.log('已更新现有 DeepSeek 直连配置'); } else { const created = await svc.createKey({ providerId: 'custom_deepseek', name: CONFIG_NAME, apiKey, defaultModel: 'deepseek-v4-pro', }); if (!created.ok) { console.error('创建 DeepSeek 配置失败:', created.message); process.exit(1); } rowId = created.key.id; console.log('已创建 DeepSeek 直连配置'); } const selected = await svc.selectKey(rowId); if (!selected.ok) { console.error('启用 DeepSeek 配置失败:', selected.message); process.exit(1); } const synced = await svc.syncSelectedToGoosed(); if (!synced.ok) { console.error('同步 goosed 失败:', synced.message); process.exit(1); } const test = await testDirectDeepSeek(); console.log('goosed provider:', synced.providerId, 'model:', synced.model); console.log( 'DeepSeek 直连测试:', test.ok ? `OK (${test.latencyMs}ms)` : `失败 HTTP ${test.status}: ${test.detail}`, ); await pool.end(); process.exit(test.ok ? 0 : 1);