UNPKG

chaite

Version:

core for chatgpt-plugin and karin-plugin-chatgpt

1,882 lines (1,850 loc) 284 kB
import { s as __toESM } from "./rolldown-runtime-DVriDoez.mjs"; import { t as __dirname } from "./__dirname-BYuaqN8u.mjs"; import "./chunks/lop/index.mjs-BWbOMJZv.mjs"; import "./chunks/bluebird/index.mjs-D4zFjek8.mjs"; import { t as require_lib } from "./chunks/mammoth/index.mjs-DOMhYI-I.mjs"; import { n as GoogleGenAI, r as Type, t as FunctionCallingConfigMode } from "./chunks/@google/genai/index.mjs-BrdPnzXZ.mjs"; import "./chunks/jszip/index.mjs-DKtD_vT2.mjs"; import "./chunks/isarray/index.mjs-DaT02uUW.mjs"; import "./chunks/core-util-is/index.mjs-C0h_2dGX.mjs"; import "./chunks/body-parser/index.mjs-Bj6nfRGh.mjs"; import "./chunks/immediate/index.mjs-CkWjI_b-.mjs"; import "./chunks/@xmldom/xmldom/index.mjs-BARr5-gb.mjs"; import "./chunks/dingbat-to-unicode/index.mjs-DjR5pQCM.mjs"; import { t as axios_default } from "./chunks/axios/index.mjs-BJujW20l.mjs"; import "./chunks/accepts/index.mjs-CtXuJELB.mjs"; import "./chunks/asynckit/index.mjs-DShOwGhP.mjs"; import { t as sdk_default } from "./chunks/@anthropic-ai/sdk/index.mjs-sKjldQtK.mjs"; import { t as require_jsonwebtoken } from "./chunks/jsonwebtoken/index.mjs-DOuYe9_G.mjs"; import { t as src_default } from "./chunks/@karinjs/node-schedule/index.mjs-Ciyd8XlD.mjs"; import { t as esm_default } from "./chunks/chokidar/index.mjs-Dc80JdeQ.mjs"; import { t as OpenAI } from "./chunks/openai/index.mjs-CaEygjxO.mjs"; import { t as require_express } from "./chunks/express/index.mjs-DFJbf4Rc.mjs"; import "./chunks/encodeurl/index.mjs-BI1DQR59.mjs"; import "./chunks/escape-html/index.mjs-CH24eAfR.mjs"; import "./chunks/array-flatten/index.mjs-CQr55PjM.mjs"; import "./chunks/content-disposition/index.mjs-d-2yLiD4.mjs"; import "./chunks/etag/index.mjs-wzo82OOp.mjs"; import "./chunks/cookie-signature/index.mjs-BMcPfKwp.mjs"; import "./chunks/cookie/index.mjs-HM-UN05t.mjs"; import { t as require_lib$1 } from "./chunks/cors/index.mjs-DqcZUs8E.mjs"; import { t as McpToolExecutor } from "./mcp-DnSId7pp.mjs"; import * as path$2 from "node:path"; import { AsyncLocalStorage } from "async_hooks"; import fs from "fs/promises"; import path$1 from "path"; import * as fs$1 from "node:fs"; import { promises as promises$1 } from "fs"; import EventEmitter from "node:events"; import * as crypto from "node:crypto"; import { spawn } from "node:child_process"; //#region src/types/cloud.ts var AbstractShareable = class { constructor(params) { if (params) { if (params.id) this.id = params.id; if (params.name) this.name = params.name; if (params.embedded) this.embedded = params.embedded; if (params.uploader) this.uploader = params.uploader; if (params.createdAt) this.createdAt = params.createdAt; if (params.updatedAt) this.updatedAt = params.updatedAt; if (params.cloudId) this.cloudId = params.cloudId; if (params.description) this.description = params.description; if (params.modelType) this.modelType = params.modelType; if (params.code) this.code = params.code; if (params.md5) this.md5 = params.md5; } } modelType; code; createdAt; description; embedded; id; cloudId; name; uploader; updatedAt; fromString(str) { return JSON.parse(str); } toString() { return JSON.stringify(this); } toFormatedString(verbose) { return JSON.stringify(this, null, 2); } md5; }; //#endregion //#region src/types/common.ts const MultipleKeyStrategyChoice = { RANDOM: "random", ROUND_ROBIN: "round-robin", CONVERSATION_HASH: "conversation-hash" }; var BaseClientOptions = class BaseClientOptions { features; tools; baseUrl; apiKey; multipleKeyStrategy; proxy; historyManager; logger; postProcessorIds; postProcessors; preProcessorIds; preProcessors; /** * Optional ToolExecutor for remote-first / sandbox tool execution. * If provided, AbstractClient will use it instead of calling tool.run() directly. */ toolExecutor; /** Per-tool call timeout in ms (used by ToolExecutor if supported) */ toolTimeoutMs; constructor(options) { if (options) { this.features = options.features || []; this.tools = options.tools || []; this.baseUrl = options.baseUrl || ""; this.apiKey = options.apiKey || ""; this.multipleKeyStrategy = options.multipleKeyStrategy; this.proxy = options.proxy; this.preProcessorIds = options.preProcessorIds; this.postProcessorIds = options.postProcessorIds; if (options.historyManager) this.historyManager = options.historyManager; this.logger = options.logger; this.toolExecutor = options.toolExecutor; this.toolTimeoutMs = options.toolTimeoutMs; this.init(); } } static create(options) { return options instanceof BaseClientOptions ? options : new BaseClientOptions(options); } initPromise; async ready() { return this.initPromise; } setHistoryManager(historyManager) { this.historyManager = historyManager; } setLogger(logger) { this.logger = logger; } getPostProcessors() { return this.postProcessors || []; } getPreProcessors() { return this.preProcessors || []; } async init() { this.initPromise = (async () => {})(); } toString() { const json = { features: this.features, tools: this.tools, baseUrl: this.baseUrl, apiKey: this.apiKey, multipleKeyStrategy: this.multipleKeyStrategy, proxy: this.proxy, postProcessors: this.postProcessors?.map((p) => p.id), preProcessors: this.preProcessors?.map((p) => p.id) }; return JSON.stringify(json); } fromString(str) { return new BaseClientOptions(JSON.parse(str)); } }; const DefaultLogger = new class DefaultLogger$1 { name; enableColors; static COLORS = { reset: "\x1B[0m", debug: "\x1B[36m", info: "\x1B[32m", warn: "\x1B[33m", error: "\x1B[31m" }; constructor(name = "Chaite", enableColors = true) { this.name = name; this.enableColors = enableColors; } formatDate() { const now = /* @__PURE__ */ new Date(); return `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}.${now.getMilliseconds().toString().padStart(3, "0")}`; } formatMessage(level, msg, args) { const prefix = `[${this.name}][${this.formatDate()}][${level}]`; const formattedMsg = typeof msg === "string" ? msg : JSON.stringify(msg, null, 2); if (this.enableColors) return `${DefaultLogger$1.COLORS[level.toLowerCase()] || ""}${prefix}${DefaultLogger$1.COLORS.reset} ${formattedMsg}`; return `${prefix} ${formattedMsg}`; } debug(msg, ...args) { console.log(this.formatMessage("DEBUG", msg, args), ...args); } error(msg, ...args) { console.log(this.formatMessage("ERROR", msg, args), ...args); } info(msg, ...args) { console.log(this.formatMessage("INFO", msg, args), ...args); } warn(msg, ...args) { console.log(this.formatMessage("WARN", msg, args), ...args); } }(); var ChaiteContext = class { constructor(logger) { this.logger = logger; } logger; chaite; options; historyMessages; event; data; client; /** Set when this context is running inside a background job */ jobId; /** Set when this context is executing a plan */ planId; /** Active skill name for this request */ skillName; /** Active workflow id for this request */ workflowId; /** Resource identity used by usage logging. */ channelId; channelName; presetId; presetName; setHistoryMessages(histories) { this.historyMessages = histories; } getHistoryMessages() { return this.historyMessages; } setOptions(options) { this.options = options; } getOptions() { return this.options; } setEvent(event) { this.event = event; } setChaite(chaite) { this.chaite = chaite; } getEvent() { return this.event; } setData(data) { this.data = data; } getData() { return this.data; } setClient(client) { this.client = client; } getClient() { return this.client; } getChaite() { return this.chaite; } }; //#endregion //#region src/types/tools.ts var ToolDTO = class ToolDTO extends AbstractShareable { constructor(params) { super(params); this.modelType = "executable"; this.permission = params.permission || "private"; this.status = params.status || "enabled"; } status; permission; toFormatedString(_verbose) { let base = `ID: ${this.id}\n工具名称:${this.name}`; if (this.description) base += `\n工具描述:${this.description}`; if (this.permission) base += `\n权限:${this.permission}`; if (this.createdAt) base += `\n创建时间:${this.createdAt}`; if (this.updatedAt) base += `\n最后更新时间:${this.updatedAt}`; if (this.uploader?.username) base += `\n上传者:@${this.uploader.username}`; return base.trimEnd(); } fromString(str) { return new ToolDTO(JSON.parse(str)); } }; var ToolsGroupDTO = class extends AbstractShareable { constructor(params) { super(params); this.modelType = "settings"; } toolIds; isDefault; toFormatedString(_verbose) { let base = `工具组名称:${this.name}`; if (this.description) base += `\n工具组描述:${this.description}`; if (this.createdAt) base += `\n创建时间:${this.createdAt}`; if (this.updatedAt) base += `\n最后更新时间:${this.updatedAt}`; if (this.uploader?.username) base += `\n上传者:@${this.uploader.username}`; return base.trimEnd(); } }; /** * js写的工具可以继承这个抽象类 */ var CustomTool = class { constructor() { if (!this.name) this.name = this.function.name; } type = "function"; function = { name: "", description: "", parameters: { type: "object", properties: {}, required: [] } }; async run(_args, _context = new ChaiteContext()) { throw new Error("Not implemented"); } name; }; //#endregion //#region src/types/document.ts var DocumentPathParser = class { isCompatibleType(value) { return typeof value === "string"; } supportedExtensions; type; parse(document) { throw new Error("abstract class"); } }; var DocumentFileParser = class { isCompatibleType(value) { return Buffer.isBuffer(value); } supportedExtensions; type; parse(document) { throw new Error("abstract class"); } }; //#endregion //#region src/types/channel.ts var ChannelStatistics = class { callTimes; useToken; perModel; fromString(str) { const channel = JSON.parse(str); this.callTimes = channel.callTimes; this.useToken = channel.useToken; this.perModel = channel.perModel; return this; } toString() { return JSON.stringify(this); } }; //#endregion //#region src/types/adapter.ts var SendMessageOption = class SendMessageOption { constructor(option) { this.model = option.model; this.temperature = option.temperature; this.maxToken = option.maxToken; this.systemOverride = option.systemOverride; this.disableHistoryRead = option.disableHistoryRead; this.disableHistorySave = option.disableHistorySave; this.conversationId = option.conversationId; this.parentMessageId = option.parentMessageId; this.stream = option.stream; this.isThinkingModel = option.isThinkingModel; this.enableReasoning = option.enableReasoning; this.reasoningEffort = option.reasoningEffort; this.reasoningBudgetTokens = option.reasoningBudgetTokens; this.toolChoice = option.toolChoice; this.preProcessorIds = option.preProcessorIds; this.postProcessorIds = option.postProcessorIds; this.toolGroupId = option.toolGroupId; this.safetySettings = option.safetySettings; this.responseModalities = option.responseModalities; this.onChunk = option.onChunk; this.onMessageWithToolCall = option.onMessageWithToolCall; this.toolCallLimit = option.toolCallLimit; this._consecutiveToolCallCount = option._consecutiveToolCallCount; this._consecutiveIdenticalToolCallCount = option._consecutiveIdenticalToolCallCount; this._lastToolCallSignature = option._lastToolCallSignature; this.toolTimeoutMs = option.toolTimeoutMs; this.jobId = option.jobId; this.planId = option.planId; this.skillName = option.skillName; } static create(options) { return options instanceof SendMessageOption ? options : new SendMessageOption(options || {}); } model; temperature; maxToken; /** * 将系统提示词覆盖 */ systemOverride; /** * 本轮对话禁用历史 */ disableHistoryRead; /** * 禁用本轮对话存储到历史 */ disableHistorySave; /** * 对话ID。如果不传则默认为第一次对话 */ conversationId; /** * 上一条消息的ID,如果不传则默认为第一次对话 */ parentMessageId; /** * 流模式 */ stream; /** * 是否是思考模型 */ isThinkingModel; enableReasoning; reasoningEffort; reasoningBudgetTokens; toolChoice; postProcessorIds; preProcessorIds; toolGroupId; responseModalities; safetySettings; toolCallLimit; _consecutiveToolCallCount; _consecutiveIdenticalToolCallCount; _lastToolCallSignature; /** * Per-tool call timeout in ms. Overrides channel-level toolTimeoutMs. */ toolTimeoutMs; /** * Agent context fields propagated through the call chain. * Set automatically by Chaite when running under a background job or plan. */ jobId; planId; skillName; fromString(str) { return JSON.parse(str); } toString() { const json = { model: this.model, temperature: this.temperature, maxToken: this.maxToken, systemOverride: this.systemOverride, disableHistoryRead: this.disableHistoryRead, disableHistorySave: this.disableHistorySave, conversationId: this.conversationId, parentMessageId: this.parentMessageId, stream: this.stream, isThinkingModel: this.isThinkingModel, enableReasoning: this.enableReasoning, reasoningEffort: this.reasoningEffort, reasoningBudgetTokens: this.reasoningBudgetTokens, toolChoice: this.toolChoice, preProcessorIds: this.preProcessorIds, postProcessorIds: this.postProcessorIds, toolGroupId: this.toolGroupId, responseModalities: this.responseModalities, safetySettings: this.safetySettings, toolCallLimit: this.toolCallLimit }; return JSON.stringify(json); } }; var AbstractHistoryManager = class { name; }; //#endregion //#region src/const/cloud_api.ts const API_V1_PREFIX = "/api/v2"; const CloudAPI = { USER: API_V1_PREFIX + "/user", LIST: { tool: API_V1_PREFIX + "/tools", processor: API_V1_PREFIX + "/processors", channel: API_V1_PREFIX + "/channels", "chat-preset": API_V1_PREFIX + "/presets", "tool-group": API_V1_PREFIX + "/toolGroups", "trigger": API_V1_PREFIX + "/triggers" }, ADD: { tool: API_V1_PREFIX + "/tool", processor: API_V1_PREFIX + "/processor", channel: API_V1_PREFIX + "/channel", "chat-preset": API_V1_PREFIX + "/preset", "tool-group": API_V1_PREFIX + "/toolGroups", "trigger": API_V1_PREFIX + "/trigger" }, GET: { tool: API_V1_PREFIX + "/tool/", processor: API_V1_PREFIX + "/processor/", channel: API_V1_PREFIX + "/channel/", "chat-preset": API_V1_PREFIX + "/preset/", "tool-group": API_V1_PREFIX + "/toolGroups/", "trigger": API_V1_PREFIX + "/trigger/" }, TEMP_SHARE: { tool: API_V1_PREFIX + "/tool", processor: API_V1_PREFIX + "/processor", channel: API_V1_PREFIX + "/channel", "chat-preset": API_V1_PREFIX + "/preset", "tool-group": API_V1_PREFIX + "/toolGroups", "trigger": API_V1_PREFIX + "/trigger" }, DELETE: { tool: API_V1_PREFIX + "/tool", processor: API_V1_PREFIX + "/processor", channel: API_V1_PREFIX + "/channel", "chat-preset": API_V1_PREFIX + "/preset", "tool-group": API_V1_PREFIX + "/toolGroups", "trigger": API_V1_PREFIX + "/trigger" } }; //#endregion //#region src/const/map.ts const CHANNEL_STATUS_MAP = { enabled: "启用", disabled: "禁用" }; const PROCESSOR_TYPE_MAP = { pre: "前置处理器", post: "后置处理器" }; //#endregion //#region src/types/processors.ts var ProcessorDTO = class ProcessorDTO extends AbstractShareable { constructor(params) { super(params); this.modelType = "executable"; if (params.type) this.type = params.type; } type; fromString(str) { return new ProcessorDTO(JSON.parse(str)); } toFormatedString(_verbose) { let base = `处理器ID:${this.id}`; if (this.name) base += `\n处理器名称:${this.name}`; if (this.type) base += `\n类型:${PROCESSOR_TYPE_MAP[this.type]}`; if (this.description) base += `\n描述:${this.description}`; if (this.createdAt) base += `\n创建时间:${this.createdAt}`; if (this.updatedAt) base += `\n最后更新时间:${this.updatedAt}`; if (this.uploader?.username) base += `\n上传者:@${this.uploader.username}`; return base.trimEnd(); } }; /** * 继承这个来实现一个前处理器 */ var PreProcessor = class { type; name; id; }; /** * 继承这个来实现一个后处理器 */ var PostProcessor = class { type; name; id; }; //#endregion //#region src/types/storage.ts var ChaiteStorage = class { getName() { return "unknown"; } }; //#endregion //#region src/types/server.ts var ChaiteResponse = class ChaiteResponse { constructor(code, data, message) { this.code = code; this.data = data; this.message = message; } static ok(data) { return new ChaiteResponse(0, data, "ok"); } static okm(data, msg) { return new ChaiteResponse(0, data, msg); } static fail(data, msg) { return new ChaiteResponse(-1, data, msg); } static failc(data, msg, code) { return new ChaiteResponse(code, data, msg); } }; //#endregion //#region src/types/external.ts var AbstractUserModeSelector = class {}; //#endregion //#region src/utils/helpers.ts async function getKey(apiKeys, strategy) { const logger = asyncLocalStorage.getStore()?.logger; const candidateApiKeys = Array.isArray(apiKeys) ? apiKeys : [apiKeys]; const keyNum = candidateApiKeys.length; if (keyNum === 0) { logger.error(`No key ${keyNum} found.`); throw new Error("No key provided"); } let apiKey; switch (strategy) { case MultipleKeyStrategyChoice.RANDOM: apiKey = candidateApiKeys[Math.floor(Math.random() * keyNum)]; break; case MultipleKeyStrategyChoice.ROUND_ROBIN: logger.warn("ROUND_ROBIN is not supported yet"); throw new Error("ROUND_ROBIN is not supported yet"); case MultipleKeyStrategyChoice.CONVERSATION_HASH: logger.warn("CONVERSATION_HASH is not supported yet"); throw new Error("CONVERSATION_HASH is not supported yet"); default: throw new Error("unknown MultipleKeyStrategy"); } return apiKey; } const asyncLocalStorage = new AsyncLocalStorage(); async function useEvent() { return asyncLocalStorage.getStore()?.getEvent(); } /** * 将JS代码保存到指定目录并导入其默认导出 * * @param {string} jsContent - JS代码内容 * @param {string} modulesDir - 保存模块的目录 * @param {string} fileName - 文件名(不含路径) * @param {Function} AbstractClass - 抽象类,用于检查实例是否继承自它 * @returns {Promise<T | null>} 如果默认导出继承自AbstractClass则返回该实例,否则返回null */ async function saveAndLoadModule(jsContent, modulesDir, fileName, AbstractClass) { await fs.mkdir(modulesDir, { recursive: true }); const filePath = path$1.join(modulesDir, fileName); await fs.writeFile(filePath, jsContent, "utf8"); try { const module = await import(`${`file://${path$1.resolve(filePath)}`}?update=${Date.now()}`); if (!("default" in module)) return null; const instance = module.default; if (instance instanceof AbstractClass) return instance; return null; } catch (error) { console.error(`导入模块 ${fileName} 时出错:`, error); return null; } } /** * 解析 JavaScript 代码中的 class 变量 name 的值 * @param {string} code - 输入的 JavaScript 代码 * @returns {string|null} - 解析出的 class 的 name 值,或者 null 如果没有找到 */ function extractClassName(code) { const match = /class\s+\w+\s+extends\s+\w+\s+{[\s\S]*?name\s*=\s*['"]([^'"]+)['"]/.exec(code); return match ? match[1] : null; } //#endregion //#region src/utils/kv_history.ts var KVHistoryManager = class { constructor(kvStore, name) { this.kvStore = kvStore; this.name = name; } getConversationKey(conversationId) { return `conversation:${conversationId}`; } getMessageKey(conversationId, messageId) { return `${this.getConversationKey(conversationId)}:message:${messageId}`; } async saveHistory(message, conversationId) { const messageKey = this.getMessageKey(conversationId, message.id); await this.kvStore.set(messageKey, JSON.stringify(message)); const conversationKey = this.getConversationKey(conversationId); const messageList = JSON.parse(await this.kvStore.get(conversationKey) || "[]"); messageList.push(message.id); await this.kvStore.set(conversationKey, JSON.stringify(messageList)); } async getHistory(messageId, conversationId) { if (!conversationId) return []; if (!messageId) throw new Error("unspecified message id is not implemented yet"); let parentId = messageId; const histories = []; while (parentId) { const history = await this.getOneHistory(parentId, conversationId); if (history) { histories.push(history); parentId = history.parentId; } else parentId = null; } return histories.reverse(); } async deleteConversation(conversationId) { const conversationKey = this.getConversationKey(conversationId); const messageList = JSON.parse(await this.kvStore.get(conversationKey) || "[]"); for (const messageId of messageList) await this.kvStore.delete(this.getMessageKey(conversationId, messageId)); await this.kvStore.delete(conversationKey); } async getOneHistory(messageId, conversationId) { const messageKey = this.getMessageKey(conversationId, messageId); const messageData = await this.kvStore.get(messageKey); return messageData ? JSON.parse(messageData) : void 0; } }; //#endregion //#region src/utils/history.ts var InMemoryHistoryManager = class extends AbstractHistoryManager { cache; constructor() { super(); this.name = "InMemoryHistoryManager"; this.cache = /* @__PURE__ */ new Map(); } async saveHistory(message, conversationId) { if (!this.cache.has(conversationId)) this.cache.set(conversationId, /* @__PURE__ */ new Map()); this.cache.get(conversationId)?.set(message.id, message); } async getHistory(messageId, conversationId) { if (!conversationId) return []; if (!messageId) throw new Error("unspecified message id is not implemented yet"); let parentId = messageId; const histories = []; while (parentId) { const history = await this.getOneHistory(parentId, conversationId); if (history) { histories.push(history); parentId = history.parentId; } else parentId = null; } return histories.reverse(); } async deleteConversation(conversationId) { this.cache.delete(conversationId); } async getOneHistory(messageId, conversationId) { return this.cache.get(conversationId)?.get(messageId); } name; }; var history_default = new InMemoryHistoryManager(); //#endregion //#region src/rag/text.ts var TextProcessor = class { static punctuationRegex = /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~,。!?;:()、]/g; static stopWords = new Set([ "a", "an", "and", "are", "as", "at", "be", "by", "for", "from", "has", "he", "in", "is", "it", "its", "of", "on", "that", "the", "to", "was", "were", "will", "with", "的", "了", "和", "是", "就", "都", "而", "及", "与", "着", "或", "一个", "没有", "我们", "你们", "他们", "很", "但是", "然而", "不过", "因为", "所以", "如果", "虽然", "尽管" ]); /** * 将文本分割成小段落 * @param text 输入的长文本 * @param maxLength 每个段落的最大长度 * @returns 分割后的段落数组 */ static splitIntoChunks(text, maxLength) { const chunks = []; let currentChunk = ""; const sentences = text.split(/[.。!!??]+/); for (const sentence of sentences) if ((currentChunk + sentence).length <= maxLength) currentChunk += (currentChunk ? " " : "") + sentence.trim(); else { if (currentChunk) chunks.push(currentChunk); currentChunk = sentence.trim(); } if (currentChunk) chunks.push(currentChunk); return chunks; } /** * 清理和标准化文本 * @param text 输入文本 * @returns 清理后的文本 */ static cleanText(text) { return text.replace(this.punctuationRegex, "").replace(/\s+/g, " ").trim(); } /** * 简单的分词(按空格分割英文,按字符分割中文) * @param text 输入文本 * @returns 分词后的数组 */ static tokenize(text) { const words = []; let currentWord = ""; for (const char of text) if (/[\u4e00-\u9fa5]/.test(char)) { if (currentWord) { words.push(currentWord); currentWord = ""; } words.push(char); } else if (/\s/.test(char)) { if (currentWord) { words.push(currentWord); currentWord = ""; } } else currentWord += char; if (currentWord) words.push(currentWord); return words; } /** * 从文本中移除停用词 * @param text 输入文本 * @returns 移除停用词后的文本 */ static removeStopWords(text) { return this.tokenize(text).filter((word) => !this.stopWords.has(word.toLowerCase())).join(" "); } /** * 提取文本中的关键词(基于简单频率) * @param text 输入文本 * @param topN 返回的关键词数量 * @returns 关键词数组 */ static extractKeywords(text, topN = 5) { const words = this.tokenize(this.removeStopWords(this.cleanText(text))); const wordFrequency = {}; for (const word of words) wordFrequency[word] = (wordFrequency[word] || 0) + 1; return Object.entries(wordFrequency).sort((a, b) => b[1] - a[1]).slice(0, topN).map((entry) => entry[0]); } /** * 计算两个文本之间的简单相似度 * @param text1 第一个文本 * @param text2 第二个文本 * @returns 相似度分数(0-1之间) */ static simpleSimilarity(text1, text2) { const set1 = new Set(this.tokenize(this.removeStopWords(this.cleanText(text1)))); const set2 = new Set(this.tokenize(this.removeStopWords(this.cleanText(text2)))); const intersection = new Set([...set1].filter((x) => set2.has(x))); const union = new Set([...set1, ...set2]); return intersection.size / union.size; } }; //#endregion //#region src/rag/result.ts var RAGAggregator = class { /** * 聚合多个RAG结果 * @param results RAG结果数组 * @param maxLength 聚合结果的最大长度 * @returns 聚合后的文本 */ static aggregate(results, maxLength = 1e3) { const sortedResults = results.sort((a, b) => b.score - a.score); let aggregatedContent = ""; for (const result of sortedResults) if (aggregatedContent.length + result.content.length <= maxLength) aggregatedContent += (aggregatedContent ? " " : "") + result.content; else break; return aggregatedContent.trim(); } /** * 过滤结果集 * @param results RAG结果数组 * @param minScore 最小分数阈值 * @returns 过滤后的结果数组 */ static filterResults(results, minScore) { return results.filter((result) => result.score >= minScore); } }; //#endregion //#region src/rag/manager.ts var RAGManager = class { constructor(vectorDb, vectorizer) { this.vectorDb = vectorDb; this.vectorizer = vectorizer; } /** * 添加文档到 RAG 系统 * @param document 文档文本 * @param maxNum 最大分割数 */ async addDocument(document, maxNum = 100) { const chunks = TextProcessor.splitIntoChunks(document, maxNum); const vectors = await this.vectorizer.batchTextToVector(chunks); await this.vectorDb.addVectors(vectors, chunks); } /** * 执行 RAG 查询 * @param query 查询文本 * @param k 返回结果数量 * @returns 查询结果 */ async query(query, k) { const queryVector = await this.vectorizer.textToVector(query); return (await this.vectorDb.search(queryVector, k)).map((r) => ({ content: r.text, score: r.score })); } /** * 聚合检索出来的结果 * @param results 文本形式 * @param filterScore 过滤的最低分数 */ getAggregatedResults(results, filterScore) { if (filterScore) results = RAGAggregator.filterResults(results, filterScore); return RAGAggregator.aggregate(results); } }; //#endregion //#region src/rag/vector.ts var AbstractVectorDatabase = class {}; //#endregion //#region src/rag/impl/VectorizerImpl.ts var VectorizerImpl = class { constructor(client, model, dimensions) { this.client = client; this.model = model; this.dimensions = dimensions; } async textToVector(text) { return (await this.client.getEmbedding(text, { model: this.model, dimensions: this.dimensions })).embeddings[0]; } async batchTextToVector(texts) { return (await this.client.getEmbedding(texts, { model: this.model, dimensions: this.dimensions })).embeddings; } }; //#endregion //#region src/rag/impl/PdfParser.ts var PdfFileParser = class extends DocumentPathParser { constructor() { super(); this.type = "pdf"; this.supportedExtensions = [".pdf"]; this.initPromise = this.initializePdfParse(); } initPromise; valid; type; supportedExtensions; async initializePdfParse() { this.valid = await import("pdf-parse").then(() => true).catch(() => false); } async ready() { await this.initPromise; } async parse(documentPath) { if (this.valid) { const buffer = fs$1.readFileSync(documentPath); const { PDFParse } = await import("pdf-parse"); const parser = new PDFParse({ data: buffer }); const result = await parser.getText(); await parser.destroy(); return result.text; } throw new Error("PDF parsing is not available. To enable this feature, please install pdf-parse: npm install pdf-parse"); } }; var PdfBufferParser = class extends DocumentFileParser { constructor() { super(); this.type = "pdf"; this.supportedExtensions = [".pdf"]; this.initPromise = this.initializePdfParse(); } initPromise; async initializePdfParse() { try { await import("pdf-parse"); this.valid = true; } catch { this.valid = false; } } valid; type; supportedExtensions; async ready() { await this.initPromise; } async parse(buffer) { if (this.valid) { const { PDFParse } = await import("pdf-parse"); const parser = new PDFParse({ data: buffer }); const result = await parser.getText(); await parser.destroy(); return result.text; } throw new Error("PDF parsing is not available. To enable this feature, please install pdf-parse: npm install pdf-parse"); } }; //#endregion //#region src/rag/impl/DocxParser.ts var import_lib$1 = /* @__PURE__ */ __toESM(require_lib(), 1); var DocxFileParser = class extends DocumentPathParser { constructor() { super(); this.type = "docx"; this.supportedExtensions = ["docx"]; } type; supportedExtensions; async parse(documentPath) { return (await import_lib$1.extractRawText({ path: documentPath })).value; } }; var DocxBufferParser = class extends DocumentFileParser { constructor() { super(); this.type = "docx"; this.supportedExtensions = ["docx"]; } type; supportedExtensions; async parse(buffer) { return (await import_lib$1.extractRawText({ buffer })).value; } }; //#endregion //#region src/rag/impl/PureTextParser.ts const pureTextExtensions = [ ".txt", ".text", ".md", ".markdown", ".html", ".htm", ".xml", ".yaml", ".yml", ".json", "", ".jsx", ".ts", ".tsx", ".py", ".java", ".c", ".cpp", ".h", ".hpp", ".cs", ".go", ".rb", ".php", ".swift", ".kt", ".kts", ".scala", ".rs", ".sh", ".bash", ".pl", ".lua", ".ini", ".cfg", ".conf", ".properties", ".env", ".css", ".scss", ".sass", ".less", ".sql", ".r", ".vbs", ".ps1", ".log", ".csv", ".tsv", ".gitignore", ".gitattributes", ".npmrc", ".babelrc", ".eslintrc", ".tex" ]; var PureTextFileParser = class extends DocumentPathParser { constructor() { super(); this.type = "txt"; this.supportedExtensions = pureTextExtensions; } type; supportedExtensions; async parse(documentPath) { return fs$1.readFileSync(path$2.resolve(documentPath)).toString("utf8"); } }; var PureTextBufferParser = class extends DocumentFileParser { constructor() { super(); this.type = "txt"; this.supportedExtensions = pureTextExtensions; } type; supportedExtensions; async parse(buffer) { return buffer.toString("utf8"); } }; //#endregion //#region src/utils/document.ts /** * 文档解析器管理 */ var DocumentParserManager = class { parsers = /* @__PURE__ */ new Map(); constructor() { this.parsers = /* @__PURE__ */ new Map(); } route(ext, target) { const parsers = this.parsers.get(ext); if (!parsers) return void 0; return parsers.find((parser) => parser.isCompatibleType(target)); } register(parser) { if (!parser) return this; parser.supportedExtensions.forEach((ext) => { if (!this.parsers.has(ext)) this.parsers.set(ext, []); this.parsers.get(ext).push(parser); }); return this; } }; const documentParserManager = new DocumentParserManager(); documentParserManager.register(new DocxBufferParser()).register(new DocxFileParser()).register(new PureTextBufferParser()).register(new PureTextFileParser()); (async () => { const pdfFileParser = new PdfFileParser(); await pdfFileParser.ready(); if (pdfFileParser.valid) documentParserManager.register(pdfFileParser); const pdfBufferParser = new PdfBufferParser(); await pdfBufferParser.ready(); if (pdfBufferParser.valid) documentParserManager.register(pdfBufferParser); })(); //#endregion //#region src/utils/axios.ts /** * 创建HTTP客户端 */ var HttpClient = class { instance; options; /** * 构造HTTP客户端 * @param options 配置选项 */ constructor(options = {}) { this.options = { baseURL: "", timeout: 1e4, headers: { "Content-Type": "application/json", "Accept": "application/json" }, authorizationPrefix: "Bearer", ...options }; this.instance = this.createAxiosInstance(); this.setupInterceptors(); } /** * 创建Axios实例 */ createAxiosInstance() { return axios_default.create({ baseURL: this.options.baseURL, timeout: this.options.timeout, headers: this.options.headers }); } /** * 设置拦截器 */ setupInterceptors() { this.instance.interceptors.request.use(async (config) => { if (this.options.onRequest) config = this.options.onRequest(config); if (this.options.getToken && config.headers) { const token = await this.options.getToken(); if (token) config.headers.Authorization = `${this.options.authorizationPrefix} ${token}`; } return config; }, (error) => { return Promise.reject(error); }); this.instance.interceptors.response.use((response) => { if (this.options.onResponse) return this.options.onResponse(response.data); return response.data; }, (error) => { const apiError = this.normalizeError(error); if (this.options.onError) return Promise.reject(this.options.onError(apiError)); return Promise.reject(apiError); }); } /** * 标准化错误对象 */ normalizeError(error) { if (axios_default.isAxiosError(error)) { if (error.response) return { status: error.response.status, data: error.response.data, message: `Request failed with status code ${error.response.status}`, originalError: error }; else if (error.request) return { request: error.request, message: "No response received from server", originalError: error }; } if (error instanceof Error) return { message: error.message, originalError: error }; return { message: "Unknown error occurred", originalError: error instanceof Error ? error : new Error(String(error)) }; } /** * 更新配置 * @param options 新配置 */ updateOptions(options) { this.options = { ...this.options, ...options }; this.instance = this.createAxiosInstance(); this.setupInterceptors(); } /** * 获取底层 Axios 实例 */ getAxiosInstance() { return this.instance; } /** * HTTP GET 请求 */ async get(url, config) { return this.instance.get(url, config); } /** * HTTP POST 请求 */ async post(url, data, config) { return this.instance.post(url, data, config); } /** * HTTP PUT 请求 */ async put(url, data, config) { return this.instance.put(url, data, config); } /** * HTTP DELETE 请求 */ async delete(url, config) { return this.instance.delete(url, config); } /** * HTTP PATCH 请求 */ async patch(url, data, config) { return this.instance.patch(url, data, config); } }; /** * 创建默认的HTTP客户端 */ const createHttpClient = (options) => { return new HttpClient(options); }; //#endregion //#region src/utils/config.ts const DEFAULT_HOST = "127.0.0.1"; const DEFAULT_PORT = 48370; var GlobalConfig = class extends EventEmitter { /** * jwt密钥 */ authKey; /** * 监听地址 */ host; /** * 监听端口 */ port; /** * 是否开启调试模式 */ debug; /** * 登录有效期,单位秒 */ loginValidTime; setAuthKey(key) { if (key === this.authKey) return; this.emit("change", { key: "authKey", newVal: key, oldVal: this.authKey }); this.authKey = key; } setHost(host) { if (host === this.host) return; this.emit("change", { key: "host", newVal: host, oldVal: this.host }); this.host = host; } setPort(port) { if (port === this.port) return; this.emit("change", { key: "port", newVal: port, oldVal: this.port }); this.port = port; } setDebug(debug) { if (debug === this.debug) return; this.emit("change", { key: "debug", newVal: debug, oldVal: this.debug }); this.debug = debug; } setLoginValidTime(time) { if (time === this.loginValidTime) return; this.emit("change", { key: "loginValidTime", newVal: time, oldVal: this.loginValidTime }); this.loginValidTime = time; } getAuthKey() { return this.authKey; } getHost() { return this.host || DEFAULT_HOST; } getPort() { return this.port || DEFAULT_PORT; } getDebug() { return this.debug; } getLoginValidTime() { return this.loginValidTime || 1440 * 60; } }; //#endregion //#region src/utils/auth.ts var import_jsonwebtoken = /* @__PURE__ */ __toESM(require_jsonwebtoken(), 1); var FrontEndAuthHandler = class { token; constructor() {} generateToken(timeout = 300, onetime = false) { const token = `${Math.floor(Date.now() / 1e3)}-${Math.random().toString(36).substring(2, 15)}`; if (!onetime) { this.token = token; setTimeout(() => { this.token = null; }, timeout * 1e3); } return token; } validateToken(token) { if (token === this.token) { this.token = null; return true; } return false; } static generateJWT(key) { return import_jsonwebtoken.default.sign({ key }, key, { expiresIn: "30d" }); } static validateJWT(key, token) { try { import_jsonwebtoken.default.verify(token, key); return true; } catch (error) { return false; } } }; //#endregion //#region src/utils/logger.ts function getLogger() { return (asyncLocalStorage.getStore()?.chaite)?.getLogger() || DefaultLogger; } //#endregion //#region src/utils/hash.ts function getMd5(str) { return crypto.createHash("md5").update(str).digest("hex"); } //#endregion //#region src/share/shareable.ts /** * T是DTO * C是T对应的可执行的实例类型 * @interface ExecutableShareableManager */ var ExecutableShareableManager = class { watcher = null; /** * 存储示例名称和文件名的映射 * @private */ instanceMap = /* @__PURE__ */ new Map(); /** * codeDirectory是存放实际代码的目录 * @private */ codeDirectory; cloudService; constructor(type, codeDirectory, storage) { this.type = type; this.storage = storage; this.codeDirectory = codeDirectory; } setCloudService(cloudService) { this.cloudService = cloudService; } /** * 只有可执行类型的实例才需要,如工具、处理器等。设置类不需要 * @private */ async initialize() { await this.scanInstances(); await this.setupFileWatcher(); } async setupFileWatcher() { if (this.watcher) await this.watcher.close(); this.watcher = esm_default.watch(this.codeDirectory, { persistent: true, ignoreInitial: true, ignorePermissionErrors: true, depth: 1 }).on("all", async (event, filepath) => { if (![ "add", "change", "unlink" ].includes(event)) return; if (path$1.extname(filepath) !== ".js") return; await this.scanInstances(); getLogger().debug(`File ${filepath} ${event}, rescanned instances`); }); getLogger().debug(`File watcher set up for directory: ${this.codeDirectory}`); } async scanInstances() { try { const files = await promises$1.readdir(this.codeDirectory); const newInstanceMap = /* @__PURE__ */ new Map(); for (const file of files) if (path$1.extname(file) === ".js") try { const filePath = path$1.join(this.codeDirectory, file); const module = await import(`file://${path$1.resolve(filePath).replace(/\\/g, "/")}?t=${Date.now()}`); const instance = this.resolveModuleInstance(module); if (instance && typeof instance === "object" && typeof instance.name === "string") { const instanceName = instance.name; getLogger().debug(`Loaded ${this.type} '${instanceName}' from file '${file}'`); newInstanceMap.set(instanceName, file); } } catch (error) { getLogger().error(`Error loading ${this.type} from file '${file}':`, error); } this.instanceMap = newInstanceMap; getLogger().info(`Scanned ${this.instanceMap.size} ${this.type}: [${[...this.instanceMap.keys()].join(", ")}]`); } catch (error) { getLogger().error(`Error scanning ${this.type} directory:`, error); } } /** * 返回实例名字们 * @returns 实例名字列表 */ async listInstanceNames() { return Array.from(this.instanceMap.keys()); } async listInstances() { return this.storage.listItems(); } async getInstanceT(id) { return this.storage.getItem(id); } /** * 获取实例对象而非Shareable DTO * @param name */ async getInstance(name) { const filename = this.instanceMap.get(name); if (!filename) return void 0; const filePath = path$1.join(this.codeDirectory, filename); const fileURL = `file://${path$1.resolve(filePath).replace(/\\/g, "/")}`; try { const module = await import(fileURL + `?t=${Date.now()}`); return this.resolveModuleInstance(module); } catch (error) { getLogger().error(`Error loading tool '${name}':`, error); return; } } /** * Plugins historically used both `export default new Tool()` and * `export class MyProcessor ...`. Resolve both shapes from the same loader. */ resolveModuleInstance(module) { const candidates = [module.default, ...Object.entries(module).filter(([key]) => key !== "default").map(([, value]) => value)]; for (const candidate of candidates) { if (candidate && typeof candidate === "object" && typeof candidate.name === "string") return candidate; if (typeof candidate === "function") try { const instance = new candidate(); if (instance && typeof instance === "object" && typeof instance.name === "string") return instance; } catch {} } } /** * 新增或更新 * 有id就是更新,靠storage实现去控制,这里不管 * @param instance * @return id */ async addInstance(instance) { const cleanObj = JSON.parse(JSON.stringify(instance)); cleanObj.md5 = ""; instance.md5 = getMd5(JSON.stringify(cleanObj)); const id = await this.storage.setItem(instance.id, instance); await this.addInstanceCode(instance.name, instance.code); return id; } async addInstanceCode(name, code) { const filename = `${name.replace(/[^a-zA-Z0-9_]/g, "_")}.js`; const filePath = path$1.join(this.codeDirectory, filename); await promises$1.writeFile(filePath, code); } async upsertInstanceT(t) { const { name, code } = t; if (!name || !code) throw new Error("name and code are required"); const existInstance = await this.storage.getItem(t.id); if (existInstance) { const existName = existInstance.name; if (existName !== name) { const oldFilename = this.instanceMap.get(existName); if (oldFilename) { getLogger().debug(`Removing old file for instance '${existName}'`); await promises$1.unlink(path$1.join(this.codeDirectory, oldFilename)); } } } return this.addInstance(t); } async getInstanceTByCloudId(cloudId) { return this.storage.listItemsByEqFilter({ cloudId }); } async renameFile(id, oldName, newName) { const filename = `${oldName.replace(/[^a-zA-Z0-9_]/g, "_")}.js`; if (filename) { const filePath = path$1.join(this.codeDirectory, filename); const newFilename = `${newName.replace(/[^a-zA-Z0-9_]/g, "_")}.js`; const newFilePath = path$1.join(this.codeDirectory, newFilename); await promises$1.rename(filePath, newFilePath); } } async deleteInstance(id) { const t = await this.storage.getItem(id); if (!t) return; const filename = this.instanceMap.get(t.name); if (filename) { const filePath = path$1.join(this.codeDirectory, filename); await promises$1.unlink(filePath); } await this.storage.removeItem(id); } checkCloudService() { if (!this.cloudService) throw new Error("Cloud service is not initialized"); return this.cloudService; } async shareToCloud(id) { const service = this.checkCloudService(); const t = await this.getInstanceT(id); if (!t) throw new Error(`${this.type} not found`); const instance = await service.upload(t); if (instance) { instance.cloudId = instance.id; instance.id = t.id; await this.storage.setItem(id, instance); } return instance?.id; } async listFromCloud(filter, query, searchOption) { const result = await this.checkCloudService().list(filter, query, searchOption); const downloaded = await this.storage.listItemsByInQuery([{ field: "cloudId", values: result.items.map((item) => item.id) }]); result.items.forEach((item) => { const local = downloaded.find((d) => d.cloudId === item.id); if (local) item.downloaded = local.id; }); return result; } async getFromCloud(shareId) { return await this.checkCloudService().download(shareId); } async shareP2P(name) { const service = this.checkCloudService(); const serialized = await this.serializeInstance(name); if (!serialized) throw new Error(`${this.type} not found`); return service.initializeTransfer(serialized); } async dispose() { if (this.watcher) { await this.watcher.close(); this.watcher = null; } } }; var NonExecutableShareableManager = class { cloudService; constructor(type, storage) { this.type = type; this.storage = storage; } setCloudService(cloudService) { this.cloudService = cloudService; } async addInstance(instance) { return await this.storage.setItem(instance.id, instance); } async deleteInstance(key) { await this.storage.removeItem(key); } async listInstances() { return await this.storage.listItems(); } async getInstance(key) { return await this.storage.getItem(key); } async upsertInstance(t) { const { name } = t; if (!name) throw new Error("name is required"); return this.addInstance(t); } async getInstanceByCloudId(cloudId) { return this.storage.listItemsByEqFilter({ cloudId }); } checkCloudService() { if (!this.cloudService) throw new Error("Cloud service is not initialized"); return this.cloudService; } async shareToCloud(key) { const service = this.checkCloudService(); const item = await this.storage.getItem(key); if (!item) throw new Error(`${this.type} not found`); return (await service.upload(item))?.id; } async shareP2P(key) { const service = this.checkCloudService(); const item = await this.storage.getItem(key); if (!item) throw new Error(`${this.type} not found`); return service.initializeTransfer(item); } async getFromCloud(shareId) { return await this.checkCloudService().download(shareId); } async listFromCloud(filter, query, searchOption) { return await this.checkCloudService().list(filter, query, searchOption); } async deleteFromCloud(key) { await this.checkCloudService().delete(key); } }; //#endregion //#region src/share/tool.ts var ToolManager = class ToolManager extends ExecutableShareableManager { static instance; constructor(toolsDirectory, storage) { super("tool", toolsDirectory, storage); } setCloudService(cloudService) { this.cloudService = cloudService; } static async init(toolsDirectory, storage) { if (ToolManager.instance) return ToolManager.instance; ToolManager.instance = new ToolManager(toolsDirectory, storage); await ToolManager.instance.initialize(); return ToolManager.instance; } static async getInstance() { if (!ToolManager.instance) return null; return ToolManager.instance; } async serializeInstance(name) { const filename = this.instanceMap.get(name); if (!filename) return null; const filePath = path$1.join(this.codeDirectory, filename); try { return new ToolDTO({ name, code: await promises$1.readFile(filePath, "utf-8") }); } catch (error) { console.error(`Error serializing tool '${name}':`, error); return null; } } }; //#endregion //#region src/channels/channels.ts var DefaultChannelLoadBalancer = class { constructor() {} async getChannel(name, channels) { const priorityGroups = this.groupByPriority(channels); for (const group of priorityGroups) { const availableChannels = group.filter((c) => c.status === "enabled"); if (availableChannels.length > 0) return this.selectByWeight(availableChannels); } throw new Error("No available channels"); } groupByPriority(channels) { const priorityMap = /* @__PURE__ */ new Map(); channels.forEach((channel) => { const group = priorityMap.get(channel.priority) || []; group.push(channel); priorityMap.set(channel.priority, group); }); return Array.from(priorityMap.keys()).sort((a, b) => b - a).map((priority) => priorityMap.get(priority)); } selectByWeight(channels) { const totalWeight = channels.reduce((sum, c) => sum + c.weight, 0); let random = Math.random() * totalWeight; for (const channel of channels) { random -= channel.weight; if (random <= 0) return channel; } return channels[0]; } /** * 获取渠道列表及其分配的数量 * generated by qwen-2.5-max * * @param modelName 模型名称 * @param channels 渠道列表 * @param totalQuantity 总数量 * @returns 返回一个包含渠道及其分配数量的数组 */ async getChannels(modelName, channels, totalQuantity) { const supportedChannels = channels.filter((channel) => channel.models.some((m) => m.name === modelName)); if (supportedChannels.length === 0) throw new Error("No channels support the specified model"); const highestPriorityGroup = this.groupByPriority(supportedChannels)[0]; if (!highestPriorityGroup || highestPriorityGroup.length === 0) throw new Error("No available channels with the highest priority"); const totalWeight = highestPriorityGroup.reduce((sum, c) => sum + c.weight, 0); const result = []; let remainingQuantity = totalQuantity; for (const channel of highestPriorityGroup) { const allocatedQuantity = Math.floor(channel.weight / totalWeight * totalQuantity); result.push({ channel, quantity: allocatedQuantity }); remainingQuantity -= allocatedQuantity; } if (remainingQuantity > 0 && result.length > 0) result[0].quantity += remainingQuantity; return result; } }; /** * 渠道 * 每个渠道对应一个adapter,并记录客户端的options */ var Channel = class Channel extends AbstractShareable { constructor(params) { super(params); this.modelType = "settings"; this.models = (params.models || []).map((m) => typeof m === "string" ? { name: m, features: ["chat", "tool"] } : m); if (params.adapterType) this.adapterType = params.adapterType; if (params.options) this.options = new BaseClientOptions().fromString(JSON.stringify(params.options)); this.weight = params.weight || 1; this.priority =