Files

130 lines
4.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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://localhost: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);
});