// ═══════════════════════════════════════════════════════════════════════════ // vector/llm/llm-service.js // ═══════════════════════════════════════════════════════════════════════════ import { xbLog } from '../../../../core/debug-core.js'; import { getVectorConfig } from '../../data/config.js'; const MODULE_ID = 'vector-llm-service'; const SILICONFLOW_API_URL = 'https://api.siliconflow.cn'; const DEFAULT_L0_MODEL = 'Qwen/Qwen3-8B'; // 唯一 ID 计数器 let callCounter = 0; function getStreamingModule() { const mod = window.xiaobaixStreamingGeneration; return mod?.xbgenrawCommand ? mod : null; } function generateUniqueId(prefix = 'llm') { callCounter = (callCounter + 1) % 100000; return `${prefix}-${callCounter}-${Date.now().toString(36)}`; } function b64UrlEncode(str) { const utf8 = new TextEncoder().encode(String(str)); let bin = ''; utf8.forEach(b => bin += String.fromCharCode(b)); return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } /** * 统一LLM调用 - 走酒馆后端(非流式) */ export async function callLLM(messages, options = {}) { const { temperature = 0.2, max_tokens = 500, } = options; const mod = getStreamingModule(); if (!mod) throw new Error('Streaming module not ready'); const cfg = getVectorConfig(); const apiKey = cfg?.online?.key || ''; if (!apiKey) { throw new Error('L0 requires siliconflow API key'); } const top64 = b64UrlEncode(JSON.stringify(messages)); // 每次调用用唯一 ID,避免 session 冲突 const uniqueId = generateUniqueId('l0'); const args = { as: 'user', nonstream: 'true', top64, id: uniqueId, temperature: String(temperature), max_tokens: String(max_tokens), api: 'openai', apiurl: SILICONFLOW_API_URL, apipassword: apiKey, model: DEFAULT_L0_MODEL, }; try { // 非流式直接返回结果 const result = await mod.xbgenrawCommand(args, ''); return String(result ?? ''); } catch (e) { xbLog.error(MODULE_ID, 'LLM调用失败', e); throw e; } } export function parseJson(text) { if (!text) return null; let s = text.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim(); try { return JSON.parse(s); } catch { } const i = s.indexOf('{'), j = s.lastIndexOf('}'); if (i !== -1 && j > i) try { return JSON.parse(s.slice(i, j + 1)); } catch { } return null; }