#!/usr/bin/env node 'use strict'; /** * 用 System 会话配置 LibreDesk 的 AI Provider(Completion + Embedding)。 * 默认复用旧项目 crm-ai-demo/bridge/kb.config.json 里的 DashScope key(可用 DASHSCOPE_API_KEY 覆盖)。 * * 用法:node scripts/configure-ai.js */ const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const OLD_KB_CFG = '/home/dev/code/crm/crm-ai-demo/bridge/kb.config.json'; function loadEnv(file) { const out = {}; for (const line of fs.readFileSync(file, 'utf8').split('\n')) { const t = line.trim(); if (!t || t.startsWith('#')) continue; const eq = t.indexOf('='); if (eq < 0) continue; let v = t.slice(eq + 1).trim(); if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1); out[t.slice(0, eq).trim()] = v; } return out; } function createClient(baseURL) { const cookies = new Map(); let csrf = ''; const cookieHeader = () => [...cookies.entries()].map(([k, v]) => `${k}=${v}`).join('; '); function store(res) { const list = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : [res.headers.get('set-cookie')].filter(Boolean); for (const c of list) { const [pair] = c.split(';'); const eq = pair.indexOf('='); if (eq > 0) { const k = pair.slice(0, eq).trim(); const v = pair.slice(eq + 1).trim(); cookies.set(k, v); if (k === 'csrf_token') csrf = v; } } } async function req(method, p, body) { const headers = { Accept: 'application/json' }; if (cookies.size) headers.Cookie = cookieHeader(); if (body !== undefined) headers['Content-Type'] = 'application/json'; if (csrf && method !== 'GET') headers['X-CSRFTOKEN'] = csrf; const res = await fetch(baseURL + p, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) }); store(res); const text = await res.text(); let json = null; try { json = text ? JSON.parse(text) : null; } catch { /* ignore */ } if (!res.ok) throw new Error(`${method} ${p} -> HTTP ${res.status}: ${(json && json.message) || text.slice(0, 300)}`); return json; } return { get: (p) => req('GET', p), post: (p, b) => req('POST', p, b), put: (p, b) => req('PUT', p, b) }; } const mask = (k) => (k ? k.slice(0, 6) + '…' + k.slice(-4) : '(empty)'); async function main() { const env = loadEnv(path.join(ROOT, '.env')); const base = (env.LIBREDESK_BASE_URL || 'http://192.168.31.61:9000').replace(/\/+$/, ''); const password = env.LIBREDESK_SYSTEM_USER_PASSWORD; const email = env.LIBREDESK_SYSTEM_EMAIL || 'System'; let apiKey = process.env.DASHSCOPE_API_KEY || ''; let baseURL = 'https://dashscope.aliyuncs.com/compatible-mode/v1'; let completionModel = 'qwen-plus-latest'; let embeddingModel = 'text-embedding-v4'; let dimensions = 1024; if (!apiKey && fs.existsSync(OLD_KB_CFG)) { const kb = JSON.parse(fs.readFileSync(OLD_KB_CFG, 'utf8')); apiKey = kb.apiKey || ''; baseURL = kb.baseURL || baseURL; embeddingModel = kb.model || embeddingModel; dimensions = kb.dim || dimensions; } if (!apiKey) throw new Error('没有可用的 DashScope key:设置 DASHSCOPE_API_KEY 或确认旧 kb.config.json 存在'); const c = createClient(base); await c.post('/api/v1/auth/login', { email, password }); console.log(`登录成功:${email}`); const completion = { provider: 'openai', base_url: baseURL, api_key: apiKey, model: completionModel, temperature: 0.3, max_tokens: 800, vision: false, }; const embedding = { provider: 'openai', base_url: baseURL, api_key: apiKey, model: embeddingModel, dimensions, embedding_max_tokens: 8192, }; console.log(`Completion: ${completionModel} @ ${baseURL} key=${mask(apiKey)}`); console.log(`Embedding : ${embeddingModel} dim=${dimensions} key=${mask(apiKey)}`); console.log('测试 Completion…'); await c.post('/api/v1/ai/config/completion/test', completion); console.log(' 通过'); console.log('测试 Embedding…'); await c.post('/api/v1/ai/config/embedding/test', embedding); console.log(' 通过'); await c.put('/api/v1/ai/config/completion', completion); console.log('已保存 Completion 配置'); await c.put('/api/v1/ai/config/embedding', embedding); console.log('已保存 Embedding 配置(会自动开始重建知识库向量)'); } main().catch((err) => { console.error('[configure-ai]', err.message); process.exit(1); });