my-ai-chat-framework 2.7.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"my-ai-chat-framework.browser.es.js","names":[],"sources":["../src/core/EventEmitter.js","../src/core/MessageStore.js","../src/core/SystemPromptStore.js","../src/core/Errors.js","../src/core/ChatService.js","../src/utils/url.js","../src/utils/typeCheck.js","../src/utils/MessageFormatter.js","../src/adapters/openai.js","../src/plugins/tool-calling.js","../src/plugins/model-registry.js"],"sourcesContent":["/**\n * 极简事件发射器\n * 支持同步/异步事件,handler 返回 Promise 时自动 await\n */\nexport class EventEmitter {\n constructor() {\n this._events = new Map();\n }\n\n on(event, handler) {\n if (!this._events.has(event)) this._events.set(event, []);\n this._events.get(event).push(handler);\n return () => this.off(event, handler);\n }\n\n off(event, handler) {\n if (!this._events.has(event)) return;\n const handlers = this._events.get(event).filter(h => h !== handler);\n if (handlers.length) this._events.set(event, handlers);\n else this._events.delete(event);\n }\n\n /**\n * 同步触发事件\n */\n emit(event, data) {\n if (!this._events.has(event)) return;\n for (const handler of this._events.get(event)) {\n try {\n handler(data);\n } catch (err) {\n console.error(`事件 ${event} 处理出错:`, err);\n }\n }\n }\n\n /**\n * 异步触发事件,逐个 await handler\n * handler 返回 Promise 时自动等待\n */\n async emitAsync(event, data) {\n if (!this._events.has(event)) return;\n for (const handler of this._events.get(event)) {\n try {\n await handler(data);\n } catch (err) {\n console.error(`事件 ${event} 处理出错:`, err);\n }\n }\n }\n}\n","/**\n * 消息存储 - 极简实现\n * 每条消息格式:\n * {\n * id?: string,\n * role: 'user'|'assistant'|'system'|'tool',\n * content: string,\n * images?: Array<string>, // 图片引用(URL / id),框架不存储图片数据\n * _ephemeral?: boolean, // 临时消息:仅底部连续时参与请求,续写后转正\n * _complete?: boolean, // 流式是否完成(false=未完成/中断)\n * prefix?: boolean, // 前缀续写标记\n * toolCalls?: Array,\n * toolCallId?: string,\n * timestamp?: number,\n * metadata?: any\n * }\n */\nexport class MessageStore {\n constructor() {\n this._messages = [];\n this._idCounter = 0;\n }\n\n add(message) {\n const msg = {\n id: message.id || `msg_${Date.now()}_${++this._idCounter}`,\n timestamp: message.timestamp || Date.now(),\n ...message\n };\n this._messages.push(msg);\n return msg;\n }\n\n addUser(content, metadata = {}) {\n return this.add({ role: 'user', content, metadata });\n }\n\n addAssistant(content, metadata = {}) {\n return this.add({ role: 'assistant', content, metadata });\n }\n\n addSystem(content, metadata = {}) {\n return this.add({ role: 'system', content, metadata });\n }\n\n addTool(content, toolCallId, metadata = {}) {\n return this.add({ role: 'tool', content, toolCallId, metadata });\n }\n\n /**\n * 快速添加一次性的、带续写标记的 assistant 消息\n * 适用于思维链引导、临时注入等场景\n * @param {string} content — 引导内容\n * @param {Object} [options] — { reasoningContent, prefix }\n * @returns {Object} 添加的消息\n */\n addOnceAssistant(content, options = {}) {\n return this.add({\n role: 'assistant',\n content,\n reasoningContent: options.reasoningContent,\n prefix: options.prefix !== false, // 默认开启续写\n _ephemeral: true\n });\n }\n\n getLast() {\n return this._messages[this._messages.length - 1] || null;\n }\n\n getAll() {\n return [...this._messages];\n }\n\n clear() {\n this._messages = [];\n }\n\n // 撤回最后一条助手消息及其后的所有消息(用于工具调用循环)\n undoToLastAssistant() {\n let lastAssistantIndex = -1;\n for (let i = this._messages.length - 1; i >= 0; i--) {\n if (this._messages[i].role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n if (lastAssistantIndex >= 0) {\n const removed = this._messages.splice(lastAssistantIndex + 1);\n return removed;\n }\n return [];\n }\n\n /**\n * 更新消息:按 id 查找并合并 changes,不新增消息\n * @param {string} id — 消息 id\n * @param {Object} changes — 要合并的字段\n * @returns {Object|null} 更新后的消息,未找到返回 null\n */\n update(id, changes) {\n for (const msg of this._messages) {\n if (msg.id === id) {\n Object.assign(msg, changes);\n // 更新 timestamp 便于排查\n msg.timestamp = changes.timestamp || Date.now();\n return msg;\n }\n }\n return null;\n }\n}\n","/**\r\n * SystemPromptStore — 系统提示词存储\r\n * \r\n * 职责:管理多条 system prompt 的增删改查与开关\r\n * 与 MessageStore 分离,因为 system prompt 是\"AI 行为准则\",不是对话事件\r\n * \r\n * 每条记录格式:\r\n * {\r\n * id: string,\r\n * content: string,\r\n * enabled: boolean,\r\n * timestamp: number\r\n * }\r\n */\r\n\r\nexport class SystemPromptStore {\r\n constructor() {\r\n this._prompts = [];\r\n this._idCounter = 0;\r\n }\r\n\r\n /**\r\n * 添加一条 system prompt\r\n * @param {string} content — 提示词内容\r\n * @param {boolean} [enabled=true] — 是否启用\r\n * @returns {Object} 添加的记录\r\n */\r\n add(content, enabled = true) {\r\n if (!content || typeof content !== 'string' || !content.trim()) {\r\n throw new Error('[SystemPromptStore] content 必须是非空字符串');\r\n }\r\n const record = {\r\n id: `sys_${Date.now()}_${++this._idCounter}`,\r\n content: content.trim(),\r\n enabled: Boolean(enabled),\r\n timestamp: Date.now()\r\n };\r\n this._prompts.push(record);\r\n return record;\r\n }\r\n\r\n /**\r\n * 删除指定索引的 system prompt\r\n * @param {number} index\r\n * @returns {Object} 被删除的记录\r\n */\r\n remove(index) {\r\n if (index < 0 || index >= this._prompts.length) {\r\n throw new Error(`[SystemPromptStore] 索引越界: ${index}`);\r\n }\r\n return this._prompts.splice(index, 1)[0];\r\n }\r\n\r\n /**\r\n * 切换指定索引的启用/禁用状态\r\n * @param {number} index\r\n * @returns {boolean} 切换后的状态\r\n */\r\n toggle(index) {\r\n if (index < 0 || index >= this._prompts.length) {\r\n throw new Error(`[SystemPromptStore] 索引越界: ${index}`);\r\n }\r\n this._prompts[index].enabled = !this._prompts[index].enabled;\r\n return this._prompts[index].enabled;\r\n }\r\n\r\n /**\r\n * 更新指定索引的 content\r\n * @param {number} index\r\n * @param {string} content\r\n */\r\n update(index, content) {\r\n if (index < 0 || index >= this._prompts.length) {\r\n throw new Error(`[SystemPromptStore] 索引越界: ${index}`);\r\n }\r\n if (!content || typeof content !== 'string' || !content.trim()) {\r\n throw new Error('[SystemPromptStore] content 必须是非空字符串');\r\n }\r\n this._prompts[index].content = content.trim();\r\n this._prompts[index].timestamp = Date.now();\r\n }\r\n\r\n /**\r\n * 清空并用一条 content 替换(便捷方法,常用于 config.system = '...')\r\n * @param {string} content\r\n */\r\n set(content) {\r\n this._prompts = [];\r\n if (content && typeof content === 'string' && content.trim()) {\r\n this.add(content, true);\r\n }\r\n }\r\n\r\n /**\r\n * 按 enabled 筛选后,转为适配器可用的格式\r\n * @returns {Array<{role:'system', content:string}>}\r\n */\r\n getEnabled() {\r\n return this._prompts\r\n .filter(p => p.enabled)\r\n .map(p => ({ role: 'system', content: p.content }));\r\n }\r\n\r\n /**\r\n * 返回所有记录(含 enabled 状态,用于 UI 展示)\r\n * @returns {Array}\r\n */\r\n getAll() {\r\n return [...this._prompts];\r\n }\r\n\r\n /**\r\n * 清空所有 system prompt\r\n */\r\n clear() {\r\n this._prompts = [];\r\n }\r\n}\r\n","/**\r\n * 自定义错误类\r\n * 用于区分不同类型的错误,方便用户通过 `error.name` 或 `instanceof` 处理\r\n */\r\n\r\n// API 错误 (如 401 认证失败、400 参数错误、429 限流等)\r\nexport class APIError extends Error {\r\n /**\r\n * @param {string} message - 错误消息(通常来自 API 响应)\r\n * @param {number} statusCode - HTTP 状态码\r\n * @param {any} originalError - 原始错误对象或相关信息\r\n * @param {string} responseText - 原始响应文本(如果有)\r\n */\r\n constructor(message, statusCode, originalError, responseText) {\r\n super(message)\r\n this.name = 'APIError'\r\n this.statusCode = statusCode\r\n this.originalError = originalError\r\n this.responseText = responseText\r\n }\r\n}\r\n\r\n// 网络错误 (fetch 失败、连接超时等)\r\nexport class NetworkError extends Error {\r\n /**\r\n * @param {string} message - 错误消息\r\n * @param {any} originalError - 原始错误对象或相关信息\r\n */\r\n constructor(message, originalError) {\r\n super(message)\r\n this.name = 'NetworkError'\r\n this.originalError = originalError\r\n }\r\n}\r\n\r\n// 配置错误 (缺少必要配置项、配置项类型错误等)\r\nexport class ConfigurationError extends Error {\r\n /**\r\n * @param {string} message - 错误消息\r\n */\r\n constructor(message) {\r\n super(message)\r\n this.name = 'ConfigurationError'\r\n }\r\n}\r\n\r\n// 解析错误 (解析 API 响应失败、响应格式不正确,不符合预期等)\r\nexport class ParsingError extends Error {\r\n /**\r\n * @param {string} message - 错误消息\r\n * @param {any} originalError - 原始错误对象或相关信息 \r\n * @param {string} responseText - 原始响应文本(如果有)\r\n **/\r\n constructor(message, originalError, responseText) {\r\n super(message)\r\n this.name = 'ParsingError'\r\n this.originalError = originalError\r\n this.responseText = responseText\r\n }\r\n}","import { EventEmitter } from './EventEmitter.js';\r\nimport { MessageStore } from './MessageStore.js';\r\nimport { SystemPromptStore } from './SystemPromptStore.js';\r\nimport { ConfigurationError, NetworkError } from './Errors.js';\r\n\r\nexport class ChatService extends EventEmitter {\r\n constructor(config = {}) {\r\n super();\r\n this.config = { ...config };\r\n this.messages = new MessageStore();\r\n this.systemPrompts = new SystemPromptStore();\r\n this._adapter = null;\r\n this._abortController = null;\r\n this._isGenerating = false;\r\n this._hooks = new EventEmitter();\r\n this._processResponse = null;\r\n\r\n const model = this.config.model || this.config.modelParams?.model;\r\n if (!model || typeof model !== 'string' || !model.trim()) {\r\n throw new ConfigurationError('缺少 model 配置');\r\n }\r\n\r\n const temperature = this.config.modelParams?.temperature ?? this.config.temperature;\r\n if (temperature !== undefined && (typeof temperature !== 'number' || temperature < 0 || temperature > 2)) {\r\n throw new ConfigurationError(`temperature 必须在 0-2 之间,当前值: ${temperature}`);\r\n }\r\n\r\n const maxTokens = this.config.modelParams?.maxTokens ?? this.config.maxTokens;\r\n if (maxTokens !== undefined && (typeof maxTokens !== 'number' || maxTokens < 1 || !Number.isInteger(maxTokens))) {\r\n throw new ConfigurationError(`maxTokens 必须为正整数,当前值: ${maxTokens}`);\r\n }\r\n\r\n if (typeof config.system === 'string' && config.system.trim()) {\r\n this.systemPrompts.set(config.system);\r\n }\r\n }\r\n\r\n use(plugin) {\r\n plugin.install(this);\r\n return this;\r\n }\r\n\r\n setAdapter(adapter) {\r\n this._adapter = adapter;\r\n }\r\n\r\n abort() {\r\n if (this._abortController) this._abortController.abort();\r\n }\r\n\r\n get isGenerating() { return this._isGenerating; }\r\n\r\n async continueLast() {\r\n const target = this._prepareContinue();\r\n this.messages.update(target.id, { prefix: true });\r\n try {\r\n await this._request({ addUser: false, isStream: false, mergeToEntry: target.id });\r\n this.messages.update(target.id, {\r\n _complete: true, prefix: undefined, _ephemeral: undefined\r\n });\r\n return target;\r\n } catch (err) {\r\n this.messages.update(target.id, { prefix: undefined });\r\n throw err;\r\n }\r\n }\r\n\r\n async continueLastStream(onProgress, onDone) {\r\n const target = this._prepareContinue();\r\n console.log('[DEBUG] continueLastStream target.id:', target.id);\r\n this.messages.update(target.id, { prefix: true });\r\n const baseLen = (target.content || '').length;\r\n\r\n try {\r\n await this._request({\r\n addUser: false, isStream: true, mergeToEntry: target.id,\r\n onProgress: (chunk) => {\r\n if (onProgress) {\r\n onProgress({ ...chunk, content: (chunk.content || '').slice(baseLen) });\r\n }\r\n },\r\n onDone: (final) => { if (onDone) onDone(final); }\r\n });\r\n this.messages.update(target.id, {\r\n _complete: true, prefix: undefined, _ephemeral: undefined\r\n });\r\n return target;\r\n } catch (err) {\r\n this.messages.update(target.id, { prefix: undefined });\r\n throw err;\r\n }\r\n }\r\n\r\n _prepareContinue() {\r\n const msgs = this.messages.getAll();\r\n const last = msgs[msgs.length - 1];\r\n if (last && (last.role === 'assistant' || last._ephemeral)) return last;\r\n throw new Error('最后一条消息不是 assistant,无法续写');\r\n }\r\n\r\n updateConfig(partial) {\r\n Object.assign(this.config, partial);\r\n if (typeof partial.system === 'string') this.systemPrompts.set(partial.system);\r\n this.emit('config-updated', { changes: partial, timestamp: Date.now() });\r\n }\r\n\r\n // ========== 编排角色 ==========\r\n async _request(options) {\r\n let { userInput, addUser = true, isStream = false, onProgress, onDone, mergeToEntry } = options;\r\n\r\n this.emit('sending', { addUser, userInput, timestamp: Date.now() });\r\n this._addUserMessage(userInput, addUser);\r\n\r\n // 发送前钩子:在 adapter 转换 messages 之前执行,可修改 messages/config\r\n await this._hooks.emitAsync('beforeRequest', {\r\n messages: this.messages,\r\n config: this.config,\r\n options\r\n });\r\n\r\n // 自动续写:启用 autoContinue 时,检测底部是否有 prefix 标记的消息,自动设为续写目标\r\n // 用于 ephemeral 注入场景:注入一条带 prefix 的引导消息后,自动合并结果回原消息\r\n // 不启用时 prefix 仅透传给 API,结果仍作为新消息追加\r\n if (this.config.ephemeralContinue && !mergeToEntry) {\r\n const msgs = this.messages.getAll();\r\n const last = msgs[msgs.length - 1];\r\n if (last && last.prefix && (last.role === 'assistant' || last._ephemeral)) {\r\n mergeToEntry = last.id;\r\n }\r\n }\r\n\r\n const body = this._adapter.buildRequest(\r\n this.messages.getAll(), this.config, this.systemPrompts.getEnabled()\r\n );\r\n\r\n const retryCfg = this.config.retry || {};\r\n try {\r\n return await this._withRetry(body, {\r\n isStream, onProgress, onDone, mergeToEntry,\r\n maxRetries: retryCfg.maxRetries ?? 0,\r\n retryDelay: retryCfg.retryDelay ?? 1000\r\n });\r\n } catch (error) {\r\n this.emit('error', { error, timestamp: Date.now() });\r\n throw error;\r\n }\r\n }\r\n\r\n _addUserMessage(userInput, addUser) {\r\n if (addUser && userInput !== undefined) {\r\n const msg = typeof userInput === 'string'\r\n ? { role: 'user', content: userInput }\r\n : { role: 'user', ...userInput };\r\n this.messages.add(msg);\r\n }\r\n }\r\n\r\n /** retry 循环,占位消息只 push 一次 */\r\n async _withRetry(body, { isStream, onProgress, onDone, mergeToEntry, maxRetries, retryDelay }) {\r\n let lastError = null;\r\n const adapterOptions = { signal: this._abortController?.signal };\r\n\r\n let placeholder = null;\r\n let base = null;\r\n if (isStream) {\r\n if (mergeToEntry) {\r\n placeholder = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n if (placeholder) base = { content: placeholder.content || '', reasoning: placeholder.reasoningContent || '' };\r\n }\r\n if (!placeholder) {\r\n placeholder = this.messages.add({ role: 'assistant', content: '', _complete: false });\r\n }\r\n }\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n this.emit('retry', { attempt, maxRetries, lastError, timestamp: Date.now() });\r\n await new Promise(r => setTimeout(r, retryDelay));\r\n }\r\n try {\r\n this._isGenerating = true;\r\n if (isStream) {\r\n return await this._stream(body, adapterOptions, { placeholder, base, mergeToEntry, onProgress, onDone });\r\n } else {\r\n const resp = await this._adapter.send(body, this.config, adapterOptions);\r\n let result = this._handleResult(this._adapter.parseResponse(resp), mergeToEntry);\r\n // 响应后处理钩子(tool-calling 用),仅非续写时\r\n if (this._processResponse && !mergeToEntry) {\r\n result = await this._processResponse(result, { isStream: false });\r\n }\r\n return result;\r\n }\r\n } catch (error) {\r\n lastError = error;\r\n if (error.name === 'AbortError' || this._abortController?.signal.aborted) {\r\n this._isGenerating = false; throw error;\r\n }\r\n if (error instanceof NetworkError && attempt < maxRetries) continue;\r\n this._isGenerating = false; throw error;\r\n }\r\n }\r\n this._isGenerating = false;\r\n throw lastError;\r\n }\r\n\r\n /** 流式:ChatService 维护占位,adapter 只管解析 */\r\n async _stream(body, adapterOptions, { placeholder, base, mergeToEntry, onProgress, onDone }) {\r\n let finalMsg = null;\r\n\r\n await this._adapter.stream(body, this.config,\r\n (snap) => {\r\n placeholder.content = snap.content || '';\r\n if (snap.reasoningContent) placeholder.reasoningContent = snap.reasoningContent;\r\n if (snap.toolCalls) placeholder.toolCalls = [...snap.toolCalls];\r\n this.emit('stream-progress', snap);\r\n if (onProgress) onProgress(snap);\r\n },\r\n async (final) => {\r\n finalMsg = final;\r\n\r\n if (this._processResponse && !mergeToEntry) {\r\n finalMsg = await this._processResponse(finalMsg, {\r\n isStream: true, onProgress, onDone\r\n });\r\n }\r\n\r\n if (mergeToEntry && base) {\r\n const newContent = base.content + (final.content || '');\r\n const changes = { content: newContent, _complete: true };\r\n if (final.reasoningContent) changes.reasoningContent = base.reasoning + final.reasoningContent;\r\n if (final.toolCalls) changes.toolCalls = final.toolCalls;\r\n this.messages.update(mergeToEntry, changes);\r\n const updated = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n this.emit('message', updated);\r\n if (onDone) onDone(updated);\r\n } else {\r\n placeholder._complete = true;\r\n this.emit('message', finalMsg);\r\n if (onDone) onDone(finalMsg);\r\n }\r\n },\r\n adapterOptions\r\n );\r\n return mergeToEntry || finalMsg;\r\n }\r\n\r\n /** 非流式结果落位 */\r\n _handleResult(assistantMsg, mergeToEntry) {\r\n if (mergeToEntry) {\r\n const entry = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n if (!entry) {\r\n this.messages.add(assistantMsg);\r\n this.emit('message', assistantMsg);\r\n return assistantMsg;\r\n }\r\n const changes = {\r\n content: (entry.content || '') + (assistantMsg.content || ''),\r\n _complete: true\r\n };\r\n if (assistantMsg.reasoningContent) {\r\n changes.reasoningContent = (entry.reasoningContent || '') + assistantMsg.reasoningContent;\r\n }\r\n if (assistantMsg.toolCalls) changes.toolCalls = assistantMsg.toolCalls;\r\n this.messages.update(mergeToEntry, changes);\r\n const updated = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n this.emit('message', updated);\r\n return updated;\r\n }\r\n this.messages.add(assistantMsg);\r\n this.emit('message', assistantMsg);\r\n return assistantMsg;\r\n }\r\n\r\n // ========== 公共 API ==========\r\n async send(userInput) {\r\n this._abortController = new AbortController();\r\n try { return await this._request({ userInput, addUser: true, isStream: false }); }\r\n finally { this._abortController = null; this._isGenerating = false; }\r\n }\r\n\r\n async stream(userInput, onProgress, onDone) {\r\n this._abortController = new AbortController();\r\n try { return await this._request({ userInput, addUser: true, isStream: true, onProgress, onDone }); }\r\n finally { this._abortController = null; this._isGenerating = false; }\r\n }\r\n\r\n async sendExisting() {\r\n this._abortController = new AbortController();\r\n try { return await this._request({ addUser: false, isStream: false }); }\r\n finally { this._abortController = null; this._isGenerating = false; }\r\n }\r\n\r\n async sendExistingStream(onProgress, onDone) {\r\n this._abortController = new AbortController();\r\n try { return await this._request({ addUser: false, isStream: true, onProgress, onDone }); }\r\n finally { this._abortController = null; this._isGenerating = false; }\r\n }\r\n}","/**\r\n * URL 工具函数\r\n */\r\n\r\n/**\r\n * 拼接 baseUrl 和 path\r\n * 注意:path 应以 '/' 开头,否则会替换 baseUrl 的最后一段\r\n * @param {string} baseUrl - 基础 URL(如 https://api.deepseek.com)\r\n * @param {string} path - 路径(如 /v1/chat/completions)\r\n * @returns {string} 完整的 URL\r\n */\r\nexport function joinUrl(baseUrl, path) {\r\n // 移除 baseUrl 末尾的斜杠(如果有)\r\n const base = baseUrl.replace(/\\/$/, '');\r\n // 确保 path 以斜杠开头\r\n const p = path.startsWith('/') ? path : '/' + path;\r\n return base + p;\r\n}","/**\r\n * 类型判断工具\r\n */\r\n\r\n/**\r\n * 判断一个值是否为字符串\r\n * @param {any} value - 要检查的值\r\n * @returns {boolean} 如果是字符串则返回 true\r\n */\r\nexport function isString(value) {\r\n return typeof value === 'string';\r\n}","/**\r\n * MessageFormatter — 可注册的消息格式转换器\r\n *\r\n * 职责:\r\n * 1. 内置 OpenA I兼容格式的转换逻辑\r\n * 2. 支持 register(name, fn) 注册自定义格式(如 Anthropic、Gemini 等)\r\n * 3. 统一处理 capabilities 过滤(reasoning、vision 等)\r\n * 4. 所有适配器通过此工具获取 API 消息数组,消除重复代码\r\n *\r\n * 用法:\r\n * import { MessageFormatter } from 'my-ai-chat-framework';\r\n *\r\n * // 使用内置格式\r\n * const msgs = MessageFormatter.format({\r\n * messages, systemPrompts, capabilities, resolveImage\r\n * }); // 默认 'openai'\r\n *\r\n * // 注册自定义格式\r\n * MessageFormatter.register('anthropic', ({ messages, systemPrompts, capabilities }) => {\r\n * // 返回 Anthropic 格式的消息数组\r\n * });\r\n */\r\n\r\nimport { isString } from './typeCheck.js';\r\n\r\n// ====== 内置格式 ======\r\n\r\nconst IS_URL = /^https?:\\/\\//i;\r\n\r\nfunction defaultResolveImage(imageId) {\r\n if (IS_URL.test(imageId)) return { url: imageId };\r\n return null;\r\n}\r\n\r\nfunction buildMultimodalContent(textContent, images, resolveImage) {\r\n const content = [{ type: 'text', text: textContent }];\r\n for (const ref of images) {\r\n const resolved = resolveImage(ref);\r\n if (!resolved) continue;\r\n if (resolved.url) {\r\n content.push({ type: 'image_url', image_url: { url: resolved.url } });\r\n } else if (resolved.data) {\r\n const mime = resolved.mimeType || 'image/png';\r\n content.push({ type: 'image_url', image_url: { url: `data:${mime};base64,${resolved.data}` } });\r\n }\r\n }\r\n return content;\r\n}\r\n\r\n/** OpenAI 兼容格式 */\r\nfunction toOpenAI({ messages, systemPrompts, capabilities, resolveImage }) {\r\n const rImg = typeof resolveImage === 'function' ? resolveImage : defaultResolveImage;\r\n const result = [];\r\n\r\n // 1) system 置顶\r\n for (const sp of systemPrompts) {\r\n if (sp?.content && isString(sp.content) && sp.content.trim()) {\r\n result.push({ role: 'system', content: sp.content.trim() });\r\n }\r\n }\r\n\r\n // 2) 识别底部连续 ephemeral(只保留这些临时消息,其他 ephemeral 忽略)\r\n let ephemEnd = messages.length;\r\n for (let i = messages.length - 1; i >= 0; i--) {\r\n if (messages[i]._ephemeral) ephemEnd = i;\r\n else break;\r\n }\r\n\r\n // 3) 过滤 + 字段映射\r\n for (let i = 0; i < messages.length; i++) {\r\n const msg = messages[i];\r\n\r\n // ephemeral 消息:仅底部连续的保留,其余跳过\r\n if (msg._ephemeral) {\r\n if (i < ephemEnd) continue;\r\n // 底部 ephemeral 按原始 role 处理,不加 _ephemeral 标记\r\n }\r\n\r\n // 非底部 ephemeral 的正常消息仍保留原有过滤\r\n if (msg.role === 'system' && !msg._ephemeral) continue;\r\n\r\n if (msg.role === 'tool') {\r\n result.push({ role: 'tool', content: msg.content || '', tool_call_id: msg.toolCallId });\r\n continue;\r\n }\r\n\r\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\r\n const entry = { role: 'assistant', content: msg.content || '', tool_calls: msg.toolCalls };\r\n if (msg.prefix) entry.prefix = true;\r\n if (capabilities?.reasoning && msg.reasoningContent) {\r\n entry.reasoning_content = msg.reasoningContent;\r\n }\r\n result.push(entry);\r\n continue;\r\n }\r\n\r\n // 常规消息\r\n const hasText = msg.content && isString(msg.content) && msg.content.trim();\r\n const hasImages = msg.images && Array.isArray(msg.images) && msg.images.length > 0;\r\n if (!hasText && !hasImages) {\r\n if (msg.role === 'assistant' && msg.prefix) {\r\n const prefixEntry = { role: 'assistant', content: '', prefix: true };\r\n if (capabilities?.reasoning && msg.reasoningContent) {\r\n prefixEntry.reasoning_content = msg.reasoningContent;\r\n }\r\n result.push(prefixEntry);\r\n }\r\n continue;\r\n }\r\n\r\n const entry = { role: msg.role };\r\n\r\n if (hasImages) {\r\n if (!capabilities?.vision) {\r\n throw new Error(\r\n '[MessageFormatter] 消息包含图片但模型不支持视觉(capabilities.vision=false)。' +\r\n '请切换模型或移除图片。'\r\n );\r\n }\r\n entry.content = buildMultimodalContent(msg.content || '', msg.images, rImg);\r\n } else {\r\n entry.content = msg.content;\r\n }\r\n\r\n if (msg.prefix) entry.prefix = true;\r\n // 常规 assistant 不回传 reasoningContent——API 自己会重新推理\r\n result.push(entry);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ====== 格式化器 ======\r\n\r\nexport const MessageFormatter = {\r\n _formats: new Map([\r\n ['openai', toOpenAI]\r\n ]),\r\n\r\n /**\r\n * 注册自定义格式\r\n * @param {string} name — 格式名称(如 'anthropic', 'gemini')\r\n * @param {Function} fn — 转换函数,接收 { messages, systemPrompts, capabilities, resolveImage },返回消息数组\r\n */\r\n register(name, fn) {\r\n if (!name || typeof name !== 'string' || !name.trim()) {\r\n throw new Error('[MessageFormatter] 格式名称必须是非空字符串');\r\n }\r\n if (typeof fn !== 'function') {\r\n throw new Error('[MessageFormatter] 转换函数必须是 function');\r\n }\r\n this._formats.set(name.trim(), fn);\r\n },\r\n\r\n /**\r\n * 移除自定义格式(内置格式不可移除)\r\n * @param {string} name\r\n */\r\n unregister(name) {\r\n if (name === 'openai') {\r\n throw new Error('[MessageFormatter] 内置格式 \"openai\" 不可移除');\r\n }\r\n this._formats.delete(name);\r\n },\r\n\r\n /**\r\n * 列出所有已注册的格式名称\r\n * @returns {Array<string>}\r\n */\r\n listFormats() {\r\n return [...this._formats.keys()];\r\n },\r\n\r\n /**\r\n * 按指定格式转换消息\r\n * @param {Object} options\r\n * @param {Array} options.messages — 内部消息列表\r\n * @param {Array} [options.systemPrompts=[]] — 启用的 system prompt\r\n * @param {Object} [options.capabilities={}] — 模型能力标签\r\n * @param {Function} [options.resolveImage] — 图片解析钩子\r\n * @param {string} [options.format='openai'] — 目标格式名称\r\n * @returns {Array<Object>}\r\n */\r\n format(options = {}) {\r\n const formatName = options.format || 'openai';\r\n const fn = this._formats.get(formatName);\r\n if (!fn) {\r\n throw new Error(\r\n `[MessageFormatter] 未知格式 \"${formatName}\"。可用格式: ${[...this._formats.keys()].join(', ')}`\r\n );\r\n }\r\n return fn({\r\n messages: options.messages || [],\r\n systemPrompts: options.systemPrompts || [],\r\n capabilities: options.capabilities || {},\r\n resolveImage: options.resolveImage\r\n });\r\n }\r\n};\r\n\r\n// 向后兼容:保留 assembleMessages 函数\r\nexport function assembleMessages(options = {}) {\r\n return MessageFormatter.format({ ...options, format: 'openai' });\r\n}\r\n","/**\n * OpenAI 兼容 API 适配器\n * 支持 DeepSeek 等完全兼容 OpenAI 接口的服务\n */\n\nimport { joinUrl } from \"../utils/url.js\";\nimport { isString } from \"../utils/typeCheck.js\";\nimport { APIError, NetworkError, ParsingError } from '../core/Errors.js';\nimport { MessageFormatter } from '../utils/MessageFormatter.js';\n\nexport const openaiAdapter = {\n name: 'openai',\n\n install(chatService) {\n chatService.setAdapter(this);\n },\n\n /**\n * 构建 OpenAI 格式的请求体\n * @param {Array} messages - 内部消息列表\n * @param {Object} config - 完整配置对象\n * @param {Array} [systemPrompts=[]] - 启用的 system prompt 列表\n * @returns {Object} 请求体\n */\n buildRequest(messages, config, systemPrompts = []) {\n const model = config.model || config.modelParams?.model;\n if (!model) {\n throw new Error('Missing required config: model (either at top level or in modelParams)');\n }\n\n const temperature = config.modelParams?.temperature ?? config.temperature ?? 0.7;\n const maxTokens = config.modelParams?.maxTokens ?? config.maxTokens ?? 2000;\n const reasoningEffort = config.modelParams?.reasoningEffort ?? config.reasoningEffort;\n\n // 用 MessageFormatter 统一处理:system 置顶、消息过滤、字段映射、图片解析\n // format 由 config.messageFormat 指定(默认 'openai')\n const apiMessages = MessageFormatter.format({\n messages,\n systemPrompts,\n capabilities: config.capabilities || {},\n resolveImage: config.resolveImage,\n format: config.messageFormat\n });\n\n const requestBody = {\n model,\n messages: apiMessages,\n temperature,\n max_tokens: maxTokens,\n stream: false\n };\n\n if (config.tools && Array.isArray(config.tools) && config.tools.length > 0) {\n requestBody.tools = config.tools;\n requestBody.tool_choice = 'auto';\n }\n\n if (reasoningEffort && model === 'deepseek-reasoner') {\n requestBody.reasoning_effort = reasoningEffort;\n }\n\n return requestBody;\n },\n\n /**\n * 处理URL相关配置,将其转换为完整请求URL\n * @param {Object} config - 配置参数\n * @returns {String} 实际请求地址\n */\n _getUrl(config) {\n config = config || {};\n const { apiUrl, baseUrl, path } = config;\n // 默认 path 没有 v1 因为 baseUrl 一般包括版本号\n const defaultPath = '/chat/completions';\n\n if (apiUrl && isString(apiUrl)) {\n return apiUrl;\n }\n\n if (baseUrl && isString(baseUrl)) {\n const finalPath = (path && isString(path)) ? path : defaultPath;\n return joinUrl(baseUrl, finalPath);\n }\n\n return 'https://api.openai.com/v1/chat/completions';\n },\n\n /**\n * 非流式发送\n * @param {Object} requestBody\n * @param {Object} config\n * @param {Object} [options] — { signal, headers: {...} }\n */\n async send(requestBody, config, options = {}) {\n try {\n const url = this._getUrl(config);\n\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n ...(config.headers || {}),\n ...(options.headers || {})\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n signal: options.signal\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data;\n\n } catch (error) {\n if (error instanceof APIError) throw error;\n if (error.name === 'AbortError') throw error;\n\n if (error.name === 'TypeError' && error.message.includes('fetch')) {\n throw new NetworkError(`Request failed: ${error.message}`, error);\n }\n\n throw error;\n }\n },\n\n /**\n * 解析 API 响应为内部消息格式\n */\n parseResponse(apiResponse) {\n // 检查响应结构是否有效\n if (!apiResponse || !apiResponse.choices || !apiResponse.choices[0] || !apiResponse.choices[0].message) {\n throw new ParsingError('Invalid API response: missing choices or message', apiResponse, JSON.stringify(apiResponse));\n }\n // 将 API 响应转换为内部消息格式\n const msg = apiResponse.choices[0].message;\n const internal = {\n role: msg.role,\n content: msg.content || '',\n };\n if (msg.tool_calls) internal.toolCalls = msg.tool_calls;\n if (msg.reasoning_content) internal.reasoningContent = msg.reasoning_content;\n return internal;\n },\n\n /**\n * 流式发送\n * @param {Object} requestBody\n * @param {Object} config\n * @param {Function} onProgress — 接收累积快照 { content, reasoningContent, toolCalls }\n * @param {Function} onDone — 接收最终消息对象\n * @param {Object} [options] — { signal, headers: {...} }\n */\n async stream(requestBody, config, onProgress, onDone, options = {}) {\n const streamBody = { ...requestBody, stream: true };\n const url = this._getUrl(config);\n let response;\n\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n 'Accept': 'text/event-stream',\n ...(config.headers || {}),\n ...(options.headers || {})\n };\n\n try {\n response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(streamBody),\n signal: options.signal\n });\n\n } catch (error) {\n if (error.name === 'AbortError') throw error;\n\n if (error.name === 'TypeError' && error.message.includes('fetch')) {\n throw new NetworkError(`Stream request failed: ${error.message}`, error);\n }\n\n throw error;\n }\n\n // 处理非 2xx 响应\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n // 解析流式响应\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let accumulated = { role: 'assistant', content: '', reasoningContent: '' };\n let reachedDone = false; // 是否收到 [DONE]\n\n while (true) {\n // 用户调用 abort() 后 signal.aborted 为 true,跳出循环\n if (options.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(/\\n\\n/);\n buffer = lines.pop();\n\n for (const line of lines) {\n const dataLine = line.replace(/^data: /, '').trim();\n if (!dataLine) continue;\n if (dataLine === '[DONE]') { reachedDone = true; break; }\n try {\n const chunk = JSON.parse(dataLine);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n\n if (delta.content) accumulated.content += delta.content;\n if (delta.tool_calls) {\n if (!accumulated.toolCalls) accumulated.toolCalls = [];\n for (const toolCallDelta of delta.tool_calls) {\n const index = toolCallDelta.index;\n if (!accumulated.toolCalls[index]) {\n accumulated.toolCalls[index] = {\n id: '',\n type: 'function',\n function: { name: '', arguments: '' }\n };\n }\n if (toolCallDelta.id) accumulated.toolCalls[index].id = toolCallDelta.id;\n if (toolCallDelta.type) accumulated.toolCalls[index].type = toolCallDelta.type;\n if (toolCallDelta.function?.name) {\n accumulated.toolCalls[index].function.name += toolCallDelta.function.name;\n }\n if (toolCallDelta.function?.arguments) {\n accumulated.toolCalls[index].function.arguments += toolCallDelta.function.arguments;\n }\n }\n }\n if (delta.reasoning_content) accumulated.reasoningContent += delta.reasoning_content;\n\n // 传出累积快照,ChatService 自行处理占位消息更新\n onProgress({ ...accumulated });\n } catch (e) {\n console.warn('stream parsing failed :', e, dataLine);\n }\n }\n }\n\n const finalMessage = {\n role: 'assistant',\n content: accumulated.content,\n toolCalls: accumulated.toolCalls,\n reasoningContent: accumulated.reasoningContent\n };\n onDone(finalMessage);\n },\n\n /**\n * 处理对话请求的非 2xx 响应,抛出 APIError,包含状态码、错误消息和原始响应文本\n * @param {Response} response - fetch API 的 Response 对象\n * @returns {Promise<never>} 抛出错误 \n */\n async handleErrorResponse(response) {\n let errorText = await response.text();\n let errorMessage = `HTTP ${response.status}`;\n // 尝试从响应体中解析 JSON 格式的错误信息\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error?.message || errorJson.message || errorText.slice(0, 200);\n } catch (e) {\n // 如果解析失败,使用原始文本(已截断)\n errorMessage = errorText.length > 200 ? errorText.slice(0, 200) + '...' : errorText;\n }\n throw new APIError(errorMessage, response.status, null, errorText);\n }\n\n};","/**\n * 工具调用插件\n * 功能:拦截助手消息中的 tool_calls,执行对应的工具,将结果作为 tool 消息加入对话,\n * 然后自动继续对话(通过 sendExisting / sendExistingStream),直到没有新的工具调用。\n * \n * 设计要点:\n * - 支持普通请求和流式请求(通过 isStream 标志区分)\n * - 支持多次工具调用循环(maxIterations 防止无限循环)\n * - 串行执行工具(可后续升级为并行)\n * - 工具执行失败时,仍然返回错误信息给 AI,而不是中断整个流程\n * - 触发 tool-error 事件,方便用户监听工具执行异常\n */\nexport const toolCallingPlugin = {\n name: 'tool-calling',\n maxIterations: 5, // 最大工具调用循环次数,防止无限循环(例如 AI 反复请求同一工具)\n\n /**\n * 安装插件,覆盖 ChatService 的 registerTool、send、stream 方法\n * @param {ChatService} chatService - ChatService 实例\n */\n install(chatService) {\n this.chatService = chatService;\n this._tools = new Map(); // 存储工具名 -> { executor, description }\n\n // 初始化 config.tools 数组(用于存放 OpenAI 格式的工具定义)\n if (!chatService.config.tools) {\n chatService.config.tools = [];\n }\n\n /**\n * 注册工具\n * @param {string} name - 工具名称(唯一标识)\n * @param {string} description - 工具描述(告诉 AI 何时调用)\n * @param {Function} executor - 异步执行函数,接收参数对象,返回结果(字符串或对象)\n * @param {Object} parameters - JSON Schema 参数定义(可选,默认为空对象)\n * @returns {ChatService} 返回 chatService 实例,支持链式调用\n */\n chatService.registerTool = (name, description, executor, parameters = {}) => {\n // 保存执行器\n this._tools.set(name, { executor, description });\n\n // 构建 OpenAI 兼容的工具定义\n const toolDefinition = {\n type: 'function',\n function: {\n name,\n description,\n parameters: {\n type: 'object',\n properties: parameters,\n required: Object.keys(parameters).filter(key => parameters[key]?.required)\n }\n }\n };\n // 避免重复添加相同工具\n const existing = chatService.config.tools.find(t => t.function.name === name);\n if (!existing) {\n chatService.config.tools.push(toolDefinition);\n }\n return chatService;\n };\n\n // 注册响应后处理钩子,不再覆盖 send/stream\n chatService._processResponse = async (response, { isStream, onProgress, onDone }) => {\n return this._handleWithTools(response, isStream, onProgress, onDone);\n };\n },\n\n /**\n * 核心处理逻辑:检查响应 → 执行工具 → 继续请求 → 循环\n * @param {Object} initialResponse - 初始 AI 响应\n * @param {boolean} isStream - 是否流式\n * @param {Function} onProgress - 流式进度回调\n * @param {Function} onDone - 流式完成回调\n * @returns {Promise<Object>} 最终 AI 响应\n */\n async _handleWithTools(initialResponse, isStream, onProgress, onDone) {\n const self = this;\n const chat = this.chatService;\n let iteration = 0;\n\n /**\n * 递归处理工具调用\n * @param {Object} initialResponse - 初始 AI 响应(可能是第一次请求的响应)\n * @returns {Promise<Object>} 最终 AI 响应(不含 tool_calls)\n */\n async function processResponse(initialResponse) {\n let lastResponse = initialResponse;\n while (iteration < self.maxIterations) {\n // 检查 AI 返回的消息中是否包含工具调用请求\n const toolCalls = lastResponse?.toolCalls;\n if (!toolCalls || toolCalls.length === 0) break;\n\n iteration++;\n // 并行执行所有工具调用(彼此独立,不会因一个失败而阻塞其他)\n const toolResults = await Promise.all(\n toolCalls.map(async (call) => {\n const toolName = call.function?.name;\n\n // 解析参数(JSON.parse 可能失败,需单独 try/catch)\n let args;\n try {\n args = JSON.parse(call.function?.arguments || '{}');\n } catch (parseErr) {\n chat.emit('tool-error', {\n toolName: toolName || 'unknown',\n error: parseErr,\n toolCallId: call.id,\n stage: 'parse',\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: `参数解析失败: ${parseErr.message}`,\n success: false\n };\n }\n\n const tool = self._tools.get(toolName);\n if (!tool) {\n chat.emit('tool-error', {\n toolName,\n error: new Error(`Tool not registered: ${toolName}`),\n toolCallId: call.id,\n stage: 'lookup',\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: `工具 ${toolName} 未注册`,\n success: false\n };\n }\n\n try {\n // 工具执行超时控制\n const toolTimeout = chat.config.toolTimeout;\n let execPromise = tool.executor(args);\n if (toolTimeout && typeof toolTimeout === 'number' && toolTimeout > 0) {\n const timeoutErr = new Error(`工具 ${toolName} 执行超时 (${toolTimeout}ms)`);\n timeoutErr.name = 'ToolTimeoutError';\n const timeoutPromise = new Promise((_, reject) =>\n setTimeout(() => reject(timeoutErr), toolTimeout)\n );\n execPromise = Promise.race([execPromise, timeoutPromise]);\n }\n\n const result = await execPromise;\n chat.emit('tool-success', {\n toolName,\n result,\n toolCallId: call.id,\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n content: typeof result === 'string' ? result : JSON.stringify(result),\n success: true\n };\n } catch (err) {\n const stage = err.name === 'ToolTimeoutError' ? 'timeout' : 'execute';\n chat.emit('tool-error', {\n toolName,\n error: err,\n toolCallId: call.id,\n stage,\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: err.message,\n success: false\n };\n }\n })\n );\n\n // 将工具执行结果作为 tool 消息添加到对话历史\n for (const tr of toolResults) {\n // 注意:addTool 的第二个参数是 tool_call_id\n // 如果工具执行失败且没有内容,则传递错误信息作为 content\n const content = tr.success ? tr.content : (tr.error || '工具执行失败');\n chat.messages.addTool(content, tr.tool_call_id);\n }\n\n // 再次请求 AI,获取基于工具结果的回复\n if (isStream) {\n // 流式继续:使用 sendExistingStream\n await new Promise((resolve) => {\n chat.sendExistingStream(\n (chunk) => {\n if (onProgress) onProgress(chunk);\n lastResponse = chunk;\n },\n (final) => {\n lastResponse = final;\n resolve();\n }\n );\n });\n } else {\n // 非流式继续:使用 sendExisting\n lastResponse = await chat.sendExisting();\n }\n }\n return lastResponse;\n }\n\n // 初始响应已由 ChatService 发出,直接进入工具调用循环\n const final = await processResponse(initialResponse);\n return final;\n }\n};","/**\r\n * model-registry 插件\r\n * \r\n * 职责:\r\n * 1. 内置常用模型的能力标签表(model → capabilities 映射)\r\n * 2. 安装时自动根据 config.model 查表,写入 config.capabilities\r\n * 3. 监听 config-updated 事件,切换模型时自动同步能力\r\n * 4. 提供 chat.registerModel(),允许用户追加/覆盖自定义模型\r\n * \r\n * 能力标签字段(全部可选,缺失视为 false / 未知):\r\n * {\r\n * reasoning: boolean — 是否返回 reasoning_content(思维链)\r\n * vision: boolean — 是否支持图片输入\r\n * toolCalling: boolean — 是否支持 function calling\r\n * streaming: boolean — 是否支持流式传输\r\n * maxInputTokens: number — 最大输入 token 数\r\n * maxOutputTokens: number — 最大输出 token 数\r\n * }\r\n * \r\n * 使用:\r\n * chat.use(modelRegistryPlugin);\r\n * // config.capabilities 现在已自动填充\r\n * chat.registerModel('my-custom-model', { toolCalling: true });\r\n */\r\n\r\n// ====== 内置模型能力表 ======\r\n// 数据来源:各厂商官方文档(2026-05 快照),用户可通过 registerModel() 覆盖\r\nconst BUILTIN_MODELS = {\r\n // ---- DeepSeek ----\r\n 'deepseek-chat': {\r\n reasoning: false,\r\n vision: false,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 128000,\r\n maxOutputTokens: 8192\r\n },\r\n 'deepseek-reasoner': {\r\n reasoning: true,\r\n vision: false,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 128000,\r\n maxOutputTokens: 8192\r\n },\r\n\r\n // ---- OpenAI ----\r\n 'gpt-4o': {\r\n reasoning: false,\r\n vision: true,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 128000,\r\n maxOutputTokens: 16384\r\n },\r\n 'gpt-4o-mini': {\r\n reasoning: false,\r\n vision: true,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 128000,\r\n maxOutputTokens: 16384\r\n },\r\n 'gpt-3.5-turbo': {\r\n reasoning: false,\r\n vision: false,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 16385,\r\n maxOutputTokens: 4096\r\n },\r\n 'o1': {\r\n reasoning: true,\r\n vision: false,\r\n toolCalling: false,\r\n streaming: false,\r\n maxInputTokens: 200000,\r\n maxOutputTokens: 100000\r\n },\r\n 'o3-mini': {\r\n reasoning: true,\r\n vision: false,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 200000,\r\n maxOutputTokens: 100000\r\n },\r\n\r\n // ---- Anthropic ----\r\n 'claude-3.5-sonnet': {\r\n reasoning: false,\r\n vision: true,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 200000,\r\n maxOutputTokens: 8192\r\n },\r\n 'claude-3.5-haiku': {\r\n reasoning: false,\r\n vision: true,\r\n toolCalling: true,\r\n streaming: true,\r\n maxInputTokens: 200000,\r\n maxOutputTokens: 8192\r\n }\r\n};\r\n\r\nconst DEFAULT_CAPABILITIES = {\r\n streaming: true,\r\n toolCalling: false,\r\n reasoning: false,\r\n vision: false\r\n};\r\n\r\nexport const modelRegistryPlugin = {\r\n name: 'model-registry',\r\n\r\n install(chatService) {\r\n this.chat = chatService;\r\n // 合并内置表 + 用户通过 registerModel 追加的\r\n this._registry = new Map(Object.entries(BUILTIN_MODELS));\r\n\r\n /**\r\n * 注册/覆盖一个模型的能力标签\r\n * @param {string} name — 模型名称\r\n * @param {Object} capabilities — 能力标签对象(部分字段即可,未提供的取默认值)\r\n * @returns {ChatService}\r\n */\r\n chatService.registerModel = (name, capabilities = {}) => {\r\n if (!name || typeof name !== 'string' || !name.trim()) {\r\n throw new Error('[model-registry] 模型名称必须是非空字符串');\r\n }\r\n const merged = { ...DEFAULT_CAPABILITIES, ...capabilities };\r\n this._registry.set(name.trim(), merged);\r\n // 如果注册的模型与当前使用的模型同名,立即同步\r\n if (chatService.config.model === name.trim()) {\r\n this._syncCapabilities();\r\n }\r\n return chatService;\r\n };\r\n\r\n /**\r\n * 列出所有已注册的模型名称\r\n * @returns {Array<string>}\r\n */\r\n chatService.listModels = () => {\r\n return [...this._registry.keys()];\r\n };\r\n\r\n // 安装时立即同步一次\r\n this._syncCapabilities();\r\n\r\n // 监听配置变更:model 变了就重新同步\r\n chatService.on('config-updated', ({ changes }) => {\r\n if ('model' in changes) {\r\n this._syncCapabilities();\r\n }\r\n // 如果用户手动改了 capabilities,用 merge 保留未指定的字段\r\n if ('capabilities' in changes && !('model' in changes)) {\r\n const current = this._lookupCapabilities();\r\n if (current) {\r\n chatService.config.capabilities = { ...current, ...changes.capabilities };\r\n }\r\n }\r\n });\r\n },\r\n\r\n // ---- 内部方法 ----\r\n\r\n /** 查表获取模型能力,未注册返回 null */\r\n _lookupCapabilities() {\r\n const model = this.chat.config.model;\r\n if (!model) return null;\r\n return this._registry.get(model) || null;\r\n },\r\n\r\n /** 将查到的能力写入 config.capabilities */\r\n _syncCapabilities() {\r\n const caps = this._lookupCapabilities();\r\n if (caps) {\r\n // 若用户已手动设了 capabilities,用模型的合并(用户值优先?还是模型优先?)\r\n // 策略:模型优先,因为换了模型能力应该重置\r\n this.chat.config.capabilities = { ...caps };\r\n }\r\n // 未注册的模型:不清除已有 capabilities,保留用户手动设的值\r\n }\r\n};\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"my-ai-chat-framework.browser.es.js","names":[],"sources":["../src/core/EventEmitter.js","../src/core/MessageStore.js","../src/core/Errors.js","../src/core/SystemPromptStore.js","../src/core/Pipeline.js","../src/core/ChatService.js","../src/utils/url.js","../src/utils/typeCheck.js","../src/utils/MessageFormatter.js","../src/adapters/openai.js","../src/plugins/tool-calling.js","../src/plugins/model-registry.js","../src/plugins/continuation.js"],"sourcesContent":["/**\n * 极简事件发射器\n * 支持同步/异步事件,handler 返回 Promise 时自动 await\n */\nexport class EventEmitter {\n constructor() {\n this._events = new Map();\n }\n\n on(event, handler) {\n if (!this._events.has(event)) this._events.set(event, []);\n this._events.get(event).push(handler);\n return () => this.off(event, handler);\n }\n\n off(event, handler) {\n if (!this._events.has(event)) return;\n const handlers = this._events.get(event).filter(h => h !== handler);\n if (handlers.length) this._events.set(event, handlers);\n else this._events.delete(event);\n }\n\n /**\n * 同步触发事件\n */\n emit(event, data) {\n if (!this._events.has(event)) return;\n for (const handler of this._events.get(event)) {\n try {\n handler(data);\n } catch (err) {\n console.error(`事件 ${event} 处理出错:`, err);\n }\n }\n }\n\n /**\n * 异步触发事件,逐个 await handler\n * handler 返回 Promise 时自动等待\n */\n async emitAsync(event, data) {\n if (!this._events.has(event)) return;\n for (const handler of this._events.get(event)) {\n try {\n await handler(data);\n } catch (err) {\n console.error(`事件 ${event} 处理出错:`, err);\n }\n }\n }\n}\n","/**\n * 消息存储 - 极简实现\n * 每条消息格式:\n * {\n * id?: string,\n * role: 'user'|'assistant'|'system'|'tool',\n * content: string,\n * images?: Array<string>, // 图片引用(URL / id),框架不存储图片数据\n * _ephemeral?: boolean, // 临时消息:仅底部连续时参与请求,续写后转正\n * _complete?: boolean, // 流式是否完成(false=未完成/中断)\n * prefix?: boolean, // 前缀续写标记\n * toolCalls?: Array,\n * toolCallId?: string,\n * timestamp?: number,\n * metadata?: any\n * }\n */\nexport class MessageStore {\n constructor() {\n this._messages = [];\n this._idCounter = 0;\n }\n\n add(message) {\n const msg = {\n id: message.id || `msg_${Date.now()}_${++this._idCounter}`,\n timestamp: message.timestamp || Date.now(),\n ...message\n };\n this._messages.push(msg);\n return msg;\n }\n\n addUser(content, metadata = {}) {\n return this.add({ role: 'user', content, metadata });\n }\n\n addAssistant(content, metadata = {}) {\n return this.add({ role: 'assistant', content, metadata });\n }\n\n addSystem(content, metadata = {}) {\n return this.add({ role: 'system', content, metadata });\n }\n\n addTool(content, toolCallId, metadata = {}) {\n return this.add({ role: 'tool', content, toolCallId, metadata });\n }\n\n /**\n * 快速添加一次性的、带续写标记的 assistant 消息\n * 适用于思维链引导、临时注入等场景\n * @param {string} content — 引导内容\n * @param {Object} [options] — { reasoningContent, prefix }\n * @returns {Object} 添加的消息\n */\n addOnceAssistant(content, options = {}) {\n return this.add({\n role: 'assistant',\n content,\n reasoningContent: options.reasoningContent,\n prefix: options.prefix !== false, // 默认开启续写\n _ephemeral: true\n });\n }\n\n getLast() {\n return this._messages[this._messages.length - 1] || null;\n }\n\n getAll() {\n return [...this._messages];\n }\n\n clear() {\n this._messages = [];\n }\n\n // 撤回最后一条助手消息及其后的所有消息(用于工具调用循环)\n undoToLastAssistant() {\n let lastAssistantIndex = -1;\n for (let i = this._messages.length - 1; i >= 0; i--) {\n if (this._messages[i].role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n if (lastAssistantIndex >= 0) {\n const removed = this._messages.splice(lastAssistantIndex + 1);\n return removed;\n }\n return [];\n }\n /**\n * 撤回到指定消息 id 的上一个用户消息,删除两者之间的所有消息\n * 适用于重发\n * @param {string} id — 目标消息 id\n * @returns {Array} 被删除的消息列表\n */\n undoToPreviousUser(id) {\n // 从后往前找到指定消息及其上一个用户消息,删除两者之间的所有消息\n let targetIndex = -1;\n let previousUserIndex = -1;\n for (let i = this._messages.length - 1; i >= 0; i--) {\n if (this._messages[i].id === id) {\n targetIndex = i;\n }\n if (targetIndex >= 0 && this._messages[i].role === 'user') {\n previousUserIndex = i;\n break;\n }\n }\n if (targetIndex >= 0 && previousUserIndex >= 0) {\n const removed = this._messages.splice(previousUserIndex + 1, targetIndex - previousUserIndex);\n return removed;\n }\n return [];\n }\n\n\n /**\n * 更新消息:按 id 查找并合并 changes,不新增消息\n * @param {string} id — 消息 id\n * @param {Object} changes — 要合并的字段\n * @returns {Object|null} 更新后的消息,未找到返回 null\n */\n update(id, changes) {\n for (const msg of this._messages) {\n if (msg.id === id) {\n Object.assign(msg, changes);\n // 更新 timestamp 便于排查\n msg.timestamp = changes.timestamp || Date.now();\n return msg;\n }\n }\n return null;\n }\n\n}\n","/**\n * 自定义错误类\n * 用于区分不同类型的错误,方便用户通过 `error.name` 或 `instanceof` 处理\n */\n\n// API 错误 (如 401 认证失败、400 参数错误、429 限流等)\nexport class APIError extends Error {\n /**\n * @param {string} message - 错误消息(通常来自 API 响应)\n * @param {number} statusCode - HTTP 状态码\n * @param {any} originalError - 原始错误对象或相关信息\n * @param {string} responseText - 原始响应文本(如果有)\n */\n constructor(message, statusCode, originalError, responseText) {\n super(message)\n this.name = 'APIError'\n this.statusCode = statusCode\n this.originalError = originalError\n this.responseText = responseText\n }\n}\n\n// 网络错误 (fetch 失败、连接超时等)\nexport class NetworkError extends Error {\n /**\n * @param {string} message - 错误消息\n * @param {any} originalError - 原始错误对象或相关信息\n */\n constructor(message, originalError) {\n super(message)\n this.name = 'NetworkError'\n this.originalError = originalError\n }\n}\n\n// 配置错误 (缺少必要配置项、配置项类型错误等)\nexport class ConfigurationError extends Error {\n /**\n * @param {string} message - 错误消息\n */\n constructor(message) {\n super(message)\n this.name = 'ConfigurationError'\n }\n}\n\n// 校验错误 (传参非法、索引越界、调用前提不满足等 —— 使用者传错了东西)\n// 继承 ConfigurationError:老的 `catch (e) { if (e instanceof ConfigurationError) }` 仍然命中,\n// 需要精细区分时用 `instanceof ValidationError`。\nexport class ValidationError extends ConfigurationError {\n /**\n * @param {string} message - 错误消息\n */\n constructor(message) {\n super(message)\n this.name = 'ValidationError'\n }\n}\n\n// 解析错误 (解析 API 响应失败、响应格式不正确,不符合预期等)\nexport class ParsingError extends Error {\n /**\n * @param {string} message - 错误消息\n * @param {any} originalError - 原始错误对象或相关信息 \n * @param {string} responseText - 原始响应文本(如果有)\n **/\n constructor(message, originalError, responseText) {\n super(message)\n this.name = 'ParsingError'\n this.originalError = originalError\n this.responseText = responseText\n }\n}","/**\n * SystemPromptStore — 系统提示词存储\n * \n * 职责:管理多条 system prompt 的增删改查与开关\n * 与 MessageStore 分离,因为 system prompt 是\"AI 行为准则\",不是对话事件\n * \n * 每条记录格式:\n * {\n * id: string,\n * content: string,\n * enabled: boolean,\n * timestamp: number\n * }\n */\n\nimport { ValidationError } from './Errors.js';\n\nexport class SystemPromptStore {\n constructor() {\n this._prompts = [];\n this._idCounter = 0;\n }\n\n /**\n * 添加一条 system prompt\n * @param {string} content — 提示词内容\n * @param {boolean} [enabled=true] — 是否启用\n * @returns {Object} 添加的记录\n */\n add(content, enabled = true) {\n if (!content || typeof content !== 'string' || !content.trim()) {\n throw new ValidationError('[SystemPromptStore] content 必须是非空字符串');\n }\n const record = {\n id: `sys_${Date.now()}_${++this._idCounter}`,\n content: content.trim(),\n enabled: Boolean(enabled),\n timestamp: Date.now()\n };\n this._prompts.push(record);\n return record;\n }\n\n /**\n * 删除指定索引的 system prompt\n * @param {number} index\n * @returns {Object} 被删除的记录\n */\n remove(index) {\n if (index < 0 || index >= this._prompts.length) {\n throw new ValidationError(`[SystemPromptStore] 索引越界: ${index}`);\n }\n return this._prompts.splice(index, 1)[0];\n }\n\n /**\n * 切换指定索引的启用/禁用状态\n * @param {number} index\n * @returns {boolean} 切换后的状态\n */\n toggle(index) {\n if (index < 0 || index >= this._prompts.length) {\n throw new ValidationError(`[SystemPromptStore] 索引越界: ${index}`);\n }\n this._prompts[index].enabled = !this._prompts[index].enabled;\n return this._prompts[index].enabled;\n }\n\n /**\n * 更新指定索引的 content\n * @param {number} index\n * @param {string} content\n */\n update(index, content) {\n if (index < 0 || index >= this._prompts.length) {\n throw new ValidationError(`[SystemPromptStore] 索引越界: ${index}`);\n }\n if (!content || typeof content !== 'string' || !content.trim()) {\n throw new ValidationError('[SystemPromptStore] content 必须是非空字符串');\n }\n this._prompts[index].content = content.trim();\n this._prompts[index].timestamp = Date.now();\n }\n\n /**\n * 清空并用一条 content 替换(便捷方法,常用于 config.system = '...')\n * @param {string} content\n */\n set(content) {\n this._prompts = [];\n if (content && typeof content === 'string' && content.trim()) {\n this.add(content, true);\n }\n }\n\n /**\n * 按 enabled 筛选后,转为适配器可用的格式\n * @returns {Array<{role:'system', content:string}>}\n */\n getEnabled() {\n return this._prompts\n .filter(p => p.enabled)\n .map(p => ({ role: 'system', content: p.content }));\n }\n\n /**\n * 返回所有记录(含 enabled 状态,用于 UI 展示)\n * @returns {Array}\n */\n getAll() {\n return [...this._prompts];\n }\n\n /**\n * 清空所有 system prompt\n */\n clear() {\n this._prompts = [];\n }\n}\n","/**\n * Pipeline —— 极简顺序管道(v3.0 提案 · 阶段 1)\n *\n * 三个概念(对照 pipeline-demo.html):\n * - 小车 ctx —— 本次请求的全部\"状态\",车间之间只通过它交接\n * - 车间 stage —— { name, run(ctx) },只干一件事,不认别的车间\n * - 调度 run() —— for + await,顺序执行(线性,无\"回程\";洋葱能力留给阶段 2 的 after 车间)\n */\n\nexport class Pipeline {\n constructor() {\n this._stages = [];\n }\n\n /**\n * 注册一个车间(追加到末尾)。\n * @param {{name: string, run: (ctx: object) => void | Promise<void>}} stage\n * @returns {Pipeline} this,支持链式\n */\n register(stage) {\n if (!stage || typeof stage.run !== 'function') {\n throw new TypeError('Pipeline.register: stage 需要 { name, run(ctx) },运行 run 必须是函数');\n }\n this._stages.push(stage);\n return this;\n }\n\n /** 按名字移除车间(移除不存在的名字是安全的) */\n unregister(name) {\n this._stages = this._stages.filter(s => s.name !== name);\n return this;\n }\n\n /** 列出当前车间名(调试用) */\n names() {\n return this._stages.map(s => s.name);\n }\n\n /** 把小车开过所有车间;返回 ctx(车间可改 ctx 上的字段) */\n async run(ctx) {\n for (const stage of this._stages) {\n await stage.run(ctx);\n }\n return ctx;\n }\n}\n\nexport default Pipeline;\n","import { EventEmitter } from './EventEmitter.js';\r\nimport { MessageStore } from './MessageStore.js';\r\nimport { SystemPromptStore } from './SystemPromptStore.js';\r\nimport { ConfigurationError, NetworkError, ValidationError } from './Errors.js';\r\nimport { Pipeline } from './Pipeline.js';\r\n\r\n/**\r\n * @typedef {Object} ChatAdapter 适配器协议(v3.0:写同级适配器只需实现这 4+1 个方法)\r\n * @property {Function} buildRequest (messages, config, systemPrompts) => requestBody\r\n * @property {Function} send (body, config, opts) => Promise<apiResponse>\r\n * @property {Function} stream (body, config, onProgress, onDone, opts) => Promise<void>\r\n * @property {Function} parseResponse (apiResponse) => assistantMessage\r\n * @property {Function} [getRequestDefaults] () => 默认请求参数(可选)\r\n * @property {Function} [install] (chatService, options) 插件式安装(可选)\r\n */\r\n\r\n/**\r\n * @typedef {Object} PipelineContext 管道小车 ctx(public pipe 车间可见/可改的全部字段)\r\n * @property {Object} options 原始请求选项\r\n * @property {*} userInput 用户输入(可为 undefined)\r\n * @property {boolean} addUser 是否已新增/将新增用户消息\r\n * @property {MessageStore} messages 消息列表(可读可改:push / unshift / update)\r\n * @property {SystemPromptStore} systemPrompts 系统提示词(可读可改)\r\n * @property {Object} config 合并后的请求级配置(prepareInput 后生效)\r\n * @property {boolean} isStream 本次是否流式\r\n * @property {Function|null} onProgress 流式进度回调\r\n * @property {Function|null} onDone 流式完成回调\r\n * @property {string|null} mergeToEntry 续写目标 id(null = 普通发送)\r\n * @property {*} body 请求体(buildRequest 后产出)\r\n * @property {*} result 最终结果(send 后产出)\r\n */\r\n\r\n/**\r\n * @typedef {Object} PipeStage 车间(v3.0 公开扩展单元)\r\n * @property {string} name 车间名(唯一,不能与内部车间重名)\r\n * @property {string} [phase] 'beforeSend'(默认)| 'afterSend'\r\n * @property {Function} run (ctx: PipelineContext) => void | Promise<void>\r\n */\r\n\r\n// updateConfig 白名单:会话层字段(运输层/请求默认参数归适配器,能力表归插件)\r\nconst SESSION_CONFIG_KEYS = ['model', 'temperature', 'maxTokens', 'modelParams', 'system', 'retry'];\r\n\r\n// 内部车间四步 + 发送(顺序即执行顺序);v3.1 起每一步都可以被 replaceStage 顶替、被 unstage 关闭\r\nconst INTERNAL_STAGES = ['prepareInput', 'beforeSend', 'autoContinue', 'buildRequest', 'send'];\r\n\r\nexport class ChatService extends EventEmitter {\r\n constructor(config = {}) {\r\n super();\r\n this.config = { ...config };\r\n // v4.0(B3):消息存储可注入 —— 想换成自定义 store(如将来支持对话树)不用改核心\r\n if (config.store) {\r\n if (typeof config.store.getAll !== 'function' || typeof config.store.add !== 'function') {\r\n throw new ValidationError('store 必须实现 MessageStore 的接口(至少 getAll / add / update)');\r\n }\r\n this.messages = config.store;\r\n } else {\r\n this.messages = new MessageStore();\r\n }\r\n this.systemPrompts = new SystemPromptStore();\r\n this._adapter = null;\r\n // v4.0(B1):每个请求各自持有 AbortController —— 多请求互不干扰(工具循环里再发请求也安全)\r\n this._activeRequests = new Map(); // requestId → AbortController\r\n this._requestSeq = 0;\r\n\r\n const model = this.config.model || this.config.modelParams?.model;\r\n if (!model || typeof model !== 'string' || !model.trim()) {\r\n throw new ConfigurationError('缺少 model 配置');\r\n }\r\n\r\n this._assertValidConfig(this.config);\r\n\r\n if (typeof config.system === 'string' && config.system.trim()) {\r\n this.systemPrompts.set(config.system);\r\n }\r\n if (config.adapter) {\r\n this.setAdapter(config.adapter);\r\n }\r\n\r\n // ===== 内部管道(v3.0):内部车间 + 用户车间(pipe 注册)按 phase 插入 =====\r\n this._userStages = [];\r\n this._stageOverrides = new Map(); // v3.1 出口:内部步骤名 → 用户实现\r\n this._disabledStages = new Set(); // v3.1 出口:被 unstage 关闭的内部步骤\r\n this._extraConfigKeys = new Map(); // v4.0:插件注册的运行时可改字段 → 校验器(可空)\r\n this.plugins = {}; // v4.0(B4):插件命名空间 —— 插件 API 挂在 chat.plugins.<插件名> 下,互不撞名\r\n this._pipeline = this._buildPipeline();\r\n }\r\n\r\n /**\r\n * 校验配置中的可校验字段(model 必填由构造器负责,这里只校验存在性)\r\n * @throws {ConfigurationError}\r\n */\r\n _assertValidConfig(cfg) {\r\n const temperature = cfg.modelParams?.temperature ?? cfg.temperature;\r\n if (temperature !== undefined && (typeof temperature !== 'number' || temperature < 0 || temperature > 2)) {\r\n throw new ConfigurationError(`temperature 必须在 0-2 之间,当前值: ${temperature}`);\r\n }\r\n\r\n const maxTokens = cfg.modelParams?.maxTokens ?? cfg.maxTokens;\r\n if (maxTokens !== undefined && (typeof maxTokens !== 'number' || maxTokens < 1 || !Number.isInteger(maxTokens))) {\r\n throw new ConfigurationError(`maxTokens 必须为正整数,当前值: ${maxTokens}`);\r\n }\r\n\r\n const model = cfg.model ?? cfg.modelParams?.model;\r\n if (model !== undefined && (typeof model !== 'string' || !model.trim())) {\r\n throw new ConfigurationError('model 必须是非空字符串');\r\n }\r\n }\r\n\r\n /**\r\n * 安装插件。\r\n * - 插件实现 `install(chat, options)`\r\n * - 若 install 返回一个对象,会挂到 **`chat.plugins[plugin.name]`**(v4.0 命名空间,插件之间不会撞名)\r\n * - 插件要提供卸载时,自己在返回值里带 `uninstall()`(或调用 `plugin.uninstall(chat)`)\r\n * @returns {ChatService} this\r\n */\r\n use(plugin, options = {}) {\r\n const api = plugin.install(this, options);\r\n if (plugin.name && api && typeof api === 'object') {\r\n this.plugins[plugin.name] = api;\r\n }\r\n return this;\r\n }\r\n\r\n // ========== 公开管道 API(v3.0 · 阶段 2)==========\r\n\r\n /**\r\n * 往请求管道里挂一个\"车间\"。\r\n * - phase 'beforeSend'(默认):在内部 beforeRequest 钩子之后、构建请求体之前执行\r\n * - phase 'afterSend' :在内部发送(流式/非流式)完成、结果落位之后执行(可改 ctx.result)\r\n * 不注册任何车间 = 行为与版本 2.8.x 完全一致。\r\n * @param {PipeStage} stage\r\n * @returns {ChatService} this\r\n * @throws {ConfigurationError} 参数非法 / 名字被占用 / 与内部车间重名\r\n */\r\n pipe(stage) {\r\n if (!stage || typeof stage !== 'object' || typeof stage.run !== 'function') {\r\n throw new ConfigurationError('pipe: 需要 { name, phase?, run(ctx) },run 必须是函数');\r\n }\r\n if (typeof stage.name !== 'string' || !stage.name.trim()) {\r\n throw new ConfigurationError('pipe: 需要 name(车间名,非空字符串)');\r\n }\r\n if (INTERNAL_STAGES.includes(stage.name)) {\r\n throw new ConfigurationError(`pipe: \"${stage.name}\" 是内部车间名,请换一个名字`);\r\n }\r\n if (this._userStages.some(s => s.name === stage.name)) {\r\n throw new ConfigurationError(`pipe: 车间 \"${stage.name}\" 已存在,请先 chat.unpipe(\"${stage.name}\")`);\r\n }\r\n const phase = stage.phase === 'afterSend' ? 'afterSend' : 'beforeSend';\r\n this._userStages.push({ name: stage.name, phase, run: stage.run });\r\n this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /** 移除一个用户车间(移除不存在的名字是安全的)。@returns {ChatService} this */\r\n unpipe(name) {\r\n const before = this._userStages.length;\r\n this._userStages = this._userStages.filter(s => s.name !== name);\r\n if (this._userStages.length !== before) this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /** 查看当前管道里所有车间名(调试/说明用,含内部车间) */\r\n get pipelineStages() {\r\n return this._pipeline.names();\r\n }\r\n\r\n // ========== 出口 API(v3.1):替换 / 关闭 内部步骤 ==========\r\n\r\n /** 内部步骤名(顺序即执行顺序):prepareInput / beforeSend / autoContinue / buildRequest / send */\r\n get internalStages() {\r\n return [...INTERNAL_STAGES];\r\n }\r\n\r\n /** 内部步骤的默认实现表 */\r\n _defaultStageRuns() {\r\n return {\r\n prepareInput: (ctx) => this._stagePrepareInput(ctx),\r\n beforeSend: (ctx) => this._stageBeforeSend(ctx),\r\n autoContinue: (ctx) => this._stageAutoContinue(ctx),\r\n buildRequest: (ctx) => this._stageBuildRequest(ctx),\r\n send: (ctx) => this._stageSend(ctx),\r\n };\r\n }\r\n\r\n /**\r\n * 用你自己的实现**顶替**某个内部步骤。框架不再执行该步的默认行为。\r\n * 例:`chat.replaceStage('send', async (ctx) => { ctx.result = await 我的发送(ctx.body) })`\r\n * 想\"包一层\"默认实现:先 `const base = chat.getStage('send')`,再在自己的函数里调用它。\r\n * @param {string} name 步骤名(见 internalStages)\r\n * @param {Function} run (ctx) => void | Promise<void>\r\n * @throws {ConfigurationError} 步骤名未知 / run 不是函数\r\n */\r\n replaceStage(name, run) {\r\n if (!INTERNAL_STAGES.includes(name)) {\r\n throw new ConfigurationError(`replaceStage: 未知的内部步骤 \"${name}\"。可选: ${INTERNAL_STAGES.join(', ')}`);\r\n }\r\n if (typeof run !== 'function') {\r\n throw new ConfigurationError('replaceStage: 第二个参数必须是函数 (ctx) => void | Promise<void>');\r\n }\r\n this._stageOverrides.set(name, run);\r\n this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /** 撤销替换,恢复该步骤的默认实现(不影响 unstage 的关闭状态) */\r\n restoreStage(name) {\r\n if (!INTERNAL_STAGES.includes(name)) {\r\n throw new ConfigurationError(`restoreStage: 未知的内部步骤 \"${name}\"。可选: ${INTERNAL_STAGES.join(', ')}`);\r\n }\r\n this._stageOverrides.delete(name);\r\n this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /**\r\n * **关闭**某个内部步骤(从管道里移除,不再执行)。\r\n * 例:`chat.unstage('autoContinue')` —— 彻底不要\"自动续写检测\"这一步。\r\n * 警告:关闭 prepareInput / buildRequest / send 会让流程失去必要产物(如 ctx.body)。\r\n */\r\n unstage(name) {\r\n if (!INTERNAL_STAGES.includes(name)) {\r\n throw new ConfigurationError(`unstage: 未知的内部步骤 \"${name}\"。可选: ${INTERNAL_STAGES.join(', ')}`);\r\n }\r\n this._disabledStages.add(name);\r\n this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /** 重新启用被 unstage 关闭的步骤 */\r\n restage(name) {\r\n if (!INTERNAL_STAGES.includes(name)) {\r\n throw new ConfigurationError(`restage: 未知的内部步骤 \"${name}\"。可选: ${INTERNAL_STAGES.join(', ')}`);\r\n }\r\n this._disabledStages.delete(name);\r\n this._rebuildPipeline();\r\n return this;\r\n }\r\n\r\n /** 取得某个内部步骤的默认实现(用于\"包一层\":做自己的事,再调用它) */\r\n getStage(name) {\r\n if (!INTERNAL_STAGES.includes(name)) {\r\n throw new ConfigurationError(`getStage: 未知的内部步骤 \"${name}\"。可选: ${INTERNAL_STAGES.join(', ')}`);\r\n }\r\n return this._defaultStageRuns()[name];\r\n }\r\n\r\n /**\r\n * 重建内部管道:内部步骤(可被 replaceStage 顶替 / 被 unstage 关闭)+ 用户车间按 phase 插入\r\n * 顺序:prepareInput → beforeSend → [用户 beforeSend 车间] → autoContinue → buildRequest → send → [用户 afterSend 车间]\r\n */\r\n _buildPipeline() {\r\n const p = new Pipeline();\r\n const defaults = this._defaultStageRuns();\r\n const insertUsers = (phase) => {\r\n for (const s of this._userStages) {\r\n if (s.phase === phase) p.register({ name: s.name, run: (ctx) => s.run(ctx) });\r\n }\r\n };\r\n for (const name of INTERNAL_STAGES) {\r\n if (!this._disabledStages.has(name)) {\r\n p.register({ name, run: this._stageOverrides.get(name) || defaults[name] });\r\n }\r\n if (name === 'beforeSend') insertUsers('beforeSend');\r\n if (name === 'send') insertUsers('afterSend');\r\n }\r\n return p;\r\n }\r\n\r\n _rebuildPipeline() {\r\n this._pipeline = this._buildPipeline();\r\n }\r\n\r\n setAdapter(adapter) {\r\n assertAdapter(adapter);\r\n this._adapter = adapter;\r\n }\r\n\r\n /**\r\n * 中断请求(v4.0:按请求隔离)。\r\n * - 不传参数:中断**所有**活跃请求(旧版只会中断\"最后一个\",多请求时会中断错的那个)\r\n * - 传 requestId:只中断那一个(id 见 `chat.activeRequests`)\r\n */\r\n abort(requestId) {\r\n if (requestId) {\r\n const controller = this._activeRequests.get(requestId);\r\n if (controller) controller.abort();\r\n return;\r\n }\r\n for (const controller of this._activeRequests.values()) controller.abort();\r\n }\r\n\r\n /** 是否有请求正在生成中 */\r\n get isGenerating() { return this._activeRequests.size > 0; }\r\n\r\n /** 当前活跃请求的 id 列表(调试 / 精确中断用) */\r\n get activeRequests() { return [...this._activeRequests.keys()]; }\r\n\r\n /** 内部:登记一个请求,返回 { id, signal } */\r\n _beginRequest() {\r\n const id = 'req_' + (++this._requestSeq);\r\n const controller = new AbortController();\r\n this._activeRequests.set(id, controller);\r\n return { id, signal: controller.signal };\r\n }\r\n\r\n /** 内部:注销一个请求 */\r\n _endRequest(id) {\r\n this._activeRequests.delete(id);\r\n }\r\n\r\n // 注(v4.0):continueLast / continueLastStream / _prepareContinue 已迁出核心。\r\n // 续写是「玩法」,由 src/plugins/continuation.js 提供 —— chat.use(createContinuationPlugin())。\r\n\r\n /**\r\n * 注册\"运行时可改的配置字段\"(v4.0)。\r\n * 让插件自带配置(如 continuation 的 autoContinue)不必回头改核心白名单。\r\n * @param {string[]|Object<string, Function>} keys 字段名数组,或 { 字段名: 校验器(value)=>boolean }\r\n * @returns {ChatService} this\r\n * @throws {ConfigurationError} 字段名非法 / 校验器不是函数\r\n */\r\n registerConfigKeys(keys) {\r\n const entries = Array.isArray(keys) ? keys.map(k => [k, null]) : Object.entries(keys || {});\r\n for (const [key, validator] of entries) {\r\n if (typeof key !== 'string' || !key.trim()) {\r\n throw new ConfigurationError('registerConfigKeys: 字段名必须是非空字符串');\r\n }\r\n if (validator != null && typeof validator !== 'function') {\r\n throw new ConfigurationError(`registerConfigKeys: \"${key}\" 的校验器必须是函数`);\r\n }\r\n this._extraConfigKeys.set(key.trim(), validator || null);\r\n }\r\n return this;\r\n }\r\n\r\n /** 注销运行时可改字段(插件卸载时调用)@returns {ChatService} this */\r\n unregisterConfigKeys(keys) {\r\n for (const k of (Array.isArray(keys) ? keys : [keys])) this._extraConfigKeys.delete(k);\r\n return this;\r\n }\r\n\r\n /** 当前允许在运行时修改的配置字段(内置基础字段 + 插件注册) */\r\n get configKeys() {\r\n return [...SESSION_CONFIG_KEYS, ...this._extraConfigKeys.keys()];\r\n }\r\n\r\n /**\r\n * 运行时修改配置(仅\"可改字段\":内置基础字段 + 插件注册字段)。\r\n *\r\n * - 运输层(apiKey/baseUrl/headers):归适配器,要改就换适配器实例\r\n * - 请求参数默认值:走适配器工厂 options 或请求级覆盖(chat.send(x, params))\r\n * - 插件自有配置:插件用 `registerConfigKeys` 登记后才可改(工厂 options 仍是首选)\r\n *\r\n * 与构造器一样经过校验,不能注入非法值。\r\n * @throws {ConfigurationError} 非白名单字段、非法值、或插件校验器拒绝\r\n */\r\n updateConfig(partial) {\r\n const allowed = this.configKeys;\r\n for (const key of Object.keys(partial)) {\r\n if (!allowed.includes(key)) {\r\n throw new ConfigurationError(\r\n `updateConfig 不支持修改 \"${key}\"。允许的字段: ${allowed.join(', ')}`\r\n );\r\n }\r\n }\r\n this._assertValidConfig(partial);\r\n // 插件注册字段的校验(校验器返回 false 即拒绝)\r\n for (const [key, validator] of this._extraConfigKeys) {\r\n if (validator && key in partial && validator(partial[key]) === false) {\r\n throw new ConfigurationError(`updateConfig: 字段 \"${key}\" 的值不合法: ${JSON.stringify(partial[key])}`);\r\n }\r\n }\r\n Object.assign(this.config, partial);\r\n if (typeof partial.system === 'string') this.systemPrompts.set(partial.system);\r\n this.emit('config-updated', { changes: partial, timestamp: Date.now() });\r\n }\r\n\r\n // ========== 编排角色 ==========\r\n\r\n /**\r\n * 合并请求参数(近者优先):\r\n * 适配器默认(getRequestDefaults) ← 会话配置(this.config) ← 本次覆盖(params)\r\n * modelParams 深层合并;平铺便捷键(temperature/maxTokens/reasoningEffort)自动折叠进 modelParams\r\n */\r\n _mergeRequestConfig(params = {}) {\r\n const adapterDefaults = this._adapter?.getRequestDefaults?.() || {};\r\n const requestConfig = { ...adapterDefaults, ...this.config, ...params };\r\n const modelParams = {\r\n ...(adapterDefaults.modelParams || {}),\r\n ...(this.config.modelParams || {}),\r\n ...(params.modelParams || {})\r\n };\r\n for (const key of ['temperature', 'maxTokens', 'reasoningEffort']) {\r\n const value = params[key] ?? this.config[key] ?? adapterDefaults[key];\r\n if (value !== undefined) modelParams[key] = value;\r\n }\r\n requestConfig.modelParams = modelParams;\r\n return requestConfig;\r\n }\r\n\r\n /**\r\n * 内部调度(v3.0 提案 · 阶段 1):造一辆小车 ctx,开过内部管道,返回结果。\r\n * 行为与旧版 _request 完全一致,只是把固定线拆成了车间(见 _stage* 方法)。\r\n *\r\n * ctx 字段(车间可见):\r\n * options —— 原始请求选项\r\n * userInput —— 用户输入\r\n * addUser —— 是否新增用户消息\r\n * messages —— MessageStore(可读可改)\r\n * systemPrompts —— SystemPromptStore\r\n * config —— 合并后的请求级配置(prepareInput 车间产出)\r\n * isStream —— 本次是否流式\r\n * onProgress —— 流式进度回调(可为 null)\r\n * onDone —— 流式完成回调(可为 null)\r\n * mergeToEntry —— 续写目标 id(空 = 普通发送)\r\n * body —— adapter 构建出的请求体(buildRequest 车间产出)\r\n * result —— 最终结果(send 车间产出)\r\n */\r\n async _request(options = {}) {\r\n const ctx = {\r\n options,\r\n userInput: options.userInput,\r\n addUser: options.addUser !== false,\r\n messages: this.messages,\r\n systemPrompts: this.systemPrompts,\r\n config: null,\r\n isStream: !!options.isStream,\r\n onProgress: options.onProgress || null,\r\n onDone: options.onDone || null,\r\n mergeToEntry: options.mergeToEntry || null,\r\n signal: options.signal || null, // v4.0(B1):本请求自己的中断信号\r\n body: null,\r\n result: null,\r\n };\r\n try {\r\n await this._pipeline.run(ctx);\r\n return ctx.result;\r\n } catch (error) {\r\n // AbortError 不是异常,是用户主动行为,由公共 API 层处理\r\n if (error.name === 'AbortError') throw error;\r\n this.emit('error', { error, timestamp: Date.now() });\r\n throw error;\r\n }\r\n }\r\n\r\n // ===== 内部车间(阶段 1):与原 _request 的固定线 1:1 对应 =====\r\n\r\n /** 车间 1:加用户消息 → 合并请求参数 → 广播 sending */\r\n async _stagePrepareInput(ctx) {\r\n this._addUserMessage(ctx.userInput, ctx.addUser);\r\n // 合并请求参数(合并后的 ctx.config 供后续车间使用)\r\n ctx.config = this._mergeRequestConfig(ctx.options.params);\r\n this.emit('sending', { addUser: ctx.addUser, userInput: ctx.userInput, timestamp: Date.now() });\r\n }\r\n\r\n /**\r\n * 车间 2:发送前 —— **v4.0 起默认什么都不做**。\r\n * 需要在这里做事的(注入、改写消息/配置):`chat.replaceStage('beforeSend', fn)`\r\n * 或挂一个 `chat.pipe({ phase: 'beforeSend' })` 车间(在它之后执行)。\r\n * 旧的 `_hooks.on('beforeRequest')` 兼容桥已于 v4.0 移除。\r\n */\r\n async _stageBeforeSend(ctx) {\r\n // 默认空实现(no-op)\r\n }\r\n\r\n /**\r\n * 车间 3:自动续写检测 —— **v4.0 起默认什么都不做**。\r\n * 续写是玩法:需要它的装上 continuation 插件,由插件 replaceStage('autoContinue', ...) 接管。\r\n * 步骤名与位置保留,方便插件替换、也方便使用者 `unstage('autoContinue')` 明确移除。\r\n */\r\n async _stageAutoContinue(ctx) {\r\n // 默认空实现(no-op)\r\n }\r\n\r\n /** 车间 4:构建请求体 */\r\n async _stageBuildRequest(ctx) {\r\n ctx.body = this._adapter.buildRequest(\r\n ctx.messages.getAll(), ctx.config, ctx.systemPrompts.getEnabled()\r\n );\r\n }\r\n\r\n /** 车间 5:发送(流式/非流式 + 重试 + 占位消息 + _processResponse 工具钩子) */\r\n async _stageSend(ctx) {\r\n const retryCfg = this.config.retry || {};\r\n ctx.result = await this._withRetry(ctx.body, {\r\n isStream: ctx.isStream,\r\n onProgress: ctx.onProgress,\r\n onDone: ctx.onDone,\r\n mergeToEntry: ctx.mergeToEntry,\r\n config: ctx.config,\r\n maxRetries: retryCfg.maxRetries ?? 0,\r\n retryDelay: retryCfg.retryDelay ?? 1000,\r\n signal: ctx.signal\r\n });\r\n }\r\n\r\n _addUserMessage(userInput, addUser) {\r\n if (addUser && userInput !== undefined) {\r\n const msg = typeof userInput === 'string'\r\n ? { role: 'user', content: userInput }\r\n : { role: 'user', ...userInput };\r\n this.messages.add(msg);\r\n }\r\n }\r\n\r\n /** retry 循环,占位消息只 push 一次 */\r\n async _withRetry(body, { isStream, onProgress, onDone, mergeToEntry, config, maxRetries, retryDelay, signal }) {\r\n let lastError = null;\r\n // v4.0(B1):使用\"本请求自己的\"信号(不再读写共享的 this._abortController)\r\n const adapterOptions = { signal };\r\n\r\n let placeholder = null;\r\n let base = null;\r\n if (isStream) {\r\n if (mergeToEntry) {\r\n placeholder = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n if (placeholder) base = { content: placeholder.content || '', reasoning: placeholder.reasoningContent || '' };\r\n }\r\n if (!placeholder) {\r\n placeholder = this.messages.add({ role: 'assistant', content: '', _complete: false });\r\n }\r\n }\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n this.emit('retry', { attempt, maxRetries, lastError, timestamp: Date.now() });\r\n await new Promise(r => setTimeout(r, retryDelay));\r\n }\r\n try {\r\n if (isStream) {\r\n return await this._stream(body, adapterOptions, { placeholder, base, mergeToEntry, onProgress, onDone, config });\r\n } else {\r\n const resp = await this._adapter.send(body, config, adapterOptions);\r\n // 响应落位后交给管道:afterSend 车间接管后续处理(如工具调用循环)\r\n return this._handleResult(this._adapter.parseResponse(resp), mergeToEntry);\r\n }\r\n } catch (error) {\r\n lastError = error;\r\n if (error.name === 'AbortError' || signal?.aborted) {\r\n throw error;\r\n }\r\n if (error instanceof NetworkError && attempt < maxRetries) continue;\r\n throw error;\r\n }\r\n }\r\n throw lastError;\r\n }\r\n\r\n /**\r\n * 流式处理:ChatService 维护占位消息,adapter 只负责解析 SSE\r\n *\r\n * 参数说明:\r\n * - placeholder:流式期间实时更新的目标消息(续写时 = target 本身,普通发送时 = 新建的空消息)\r\n * - base:续写时保存的旧内容快照,用于和 API 新内容拼接\r\n * - mergeToEntry:续写目标的 id,有值时走续写逻辑\r\n * - onProgress:外部回调,每收到一个 chunk 触发\r\n * - onDone:流结束回调\r\n */\r\n async _stream(body, adapterOptions, { placeholder, base, mergeToEntry, onProgress, onDone, config }) {\r\n let finalMsg = null;\r\n\r\n await this._adapter.stream(body, config,\r\n // === onProgress:每收到一个 SSE chunk 触发 ===\r\n // snap = 全量累积快照;delta = 本次新增片段(v4.0 起两个都给你)\r\n (snap, delta) => {\r\n // snap.content 是 adapter 累积的全部 API 新内容(不是增量)\r\n // 续写时:base.content(旧内容)+ snap.content(API 新内容)= 完整内容\r\n // 普通发送时:直接用 snap.content\r\n placeholder.content = mergeToEntry && base\r\n ? base.content + (snap.content || '')\r\n : (snap.content || '');\r\n // 思维链同样拼接:续写时 base 旧思维 + 新思维\r\n if (snap.reasoningContent) {\r\n placeholder.reasoningContent = mergeToEntry && base\r\n ? base.reasoning + snap.reasoningContent\r\n : snap.reasoningContent;\r\n }\r\n if (snap.toolCalls) placeholder.toolCalls = [...snap.toolCalls];\r\n this.emit('stream-progress', snap);\r\n if (onProgress) onProgress(snap, delta); // v4.0:第二个参数是本次增量\r\n },\r\n // === onDone:流结束,做最终落位 ===\r\n async (final) => {\r\n finalMsg = final;\r\n\r\n if (mergeToEntry && base) {\r\n // 续写:把 base + 最终内容写回 target,标记完成\r\n const newContent = base.content + (final.content || '');\r\n const changes = { content: newContent, _complete: true, _ephemeral: false, prefix: undefined };\r\n if (final.reasoningContent) changes.reasoningContent = base.reasoning + final.reasoningContent;\r\n if (final.toolCalls) changes.toolCalls = final.toolCalls;\r\n this.messages.update(mergeToEntry, changes);\r\n const updated = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n this.emit('message', updated);\r\n if (onDone) onDone(updated);\r\n } else {\r\n // 普通发送:标记占位消息完成\r\n placeholder._complete = true;\r\n this.emit('message', finalMsg);\r\n if (onDone) onDone(finalMsg);\r\n }\r\n },\r\n adapterOptions\r\n );\r\n // 返回续写目标 id 或最终消息(供 _withRetry 返回给调用方)\r\n return mergeToEntry || finalMsg;\r\n }\r\n\r\n /** 非流式结果落位 */\r\n _handleResult(assistantMsg, mergeToEntry) {\r\n if (mergeToEntry) {\r\n const entry = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n if (!entry) {\r\n this.messages.add(assistantMsg);\r\n this.emit('message', assistantMsg);\r\n return assistantMsg;\r\n }\r\n const changes = {\r\n content: (entry.content || '') + (assistantMsg.content || ''),\r\n _complete: true,\r\n _ephemeral: false,\r\n prefix: undefined\r\n };\r\n if (assistantMsg.reasoningContent) {\r\n changes.reasoningContent = (entry.reasoningContent || '') + assistantMsg.reasoningContent;\r\n }\r\n if (assistantMsg.toolCalls) changes.toolCalls = assistantMsg.toolCalls;\r\n this.messages.update(mergeToEntry, changes);\r\n const updated = this.messages.getAll().find(m => m.id === mergeToEntry);\r\n this.emit('message', updated);\r\n return updated;\r\n }\r\n this.messages.add(assistantMsg);\r\n this.emit('message', assistantMsg);\r\n return assistantMsg;\r\n }\r\n\r\n // ========== 公共 API ==========\r\n\r\n /**\r\n * 发送消息。\r\n * @param {string|Object} userInput - 用户输入\r\n * @param {Object} [params] - 请求级参数覆盖(model/temperature/modelParams 等,仅本次生效)\r\n */\r\n async send(userInput, params) {\r\n const { id, signal } = this._beginRequest();\r\n try {\r\n return await this._request({ userInput, addUser: true, isStream: false, params, signal });\r\n } catch (err) {\r\n if (err.name === 'AbortError') {\r\n this.emit('aborted', { timestamp: Date.now() });\r\n return;\r\n }\r\n throw err;\r\n } finally {\r\n this._endRequest(id);\r\n }\r\n }\r\n\r\n /**\r\n * 流式发送。\r\n * @param {string|Object} userInput - 用户输入\r\n * @param {Object} [params] - 请求级参数覆盖(仅本次生效)\r\n * @param {Function} [onProgress] - 流式进度回调\r\n * @param {Function} [onDone] - 流式完成回调\r\n */\r\n async stream(userInput, params, onProgress, onDone) {\r\n // 兼容旧签名 stream(input, onProgress, onDone)\r\n if (typeof params === 'function') {\r\n onDone = onProgress;\r\n onProgress = params;\r\n params = undefined;\r\n }\r\n const { id, signal } = this._beginRequest();\r\n try {\r\n return await this._request({ userInput, addUser: true, isStream: true, onProgress, onDone, params, signal });\r\n } catch (err) {\r\n if (err.name === 'AbortError') {\r\n this.emit('aborted', { timestamp: Date.now() });\r\n return;\r\n }\r\n throw err;\r\n } finally {\r\n this._endRequest(id);\r\n }\r\n }\r\n\r\n /**\r\n * 重发当前消息(不加用户消息)。\r\n * @param {Object} [params] - 请求级参数覆盖(仅本次生效)\r\n * @param {Object} [options] - { mergeToEntry } 可选:把结果合并进指定的已有消息(续写等场景)\r\n */\r\n async sendExisting(params, options = {}) {\r\n const mergeToEntry = options?.mergeToEntry || null;\r\n const { id, signal } = this._beginRequest();\r\n try {\r\n return await this._request({ addUser: false, isStream: false, params, mergeToEntry, signal });\r\n } catch (err) {\r\n if (err.name === 'AbortError') {\r\n this.emit('aborted', { timestamp: Date.now() });\r\n return;\r\n }\r\n throw err;\r\n } finally {\r\n this._endRequest(id);\r\n }\r\n }\r\n\r\n /**\r\n * 流式重发(不加用户消息)。\r\n * @param {Object} [params] - 请求级参数覆盖(仅本次生效)\r\n * @param {Object} [options] - { mergeToEntry } 可选:把结果合并进指定的已有消息\r\n * @param {Function} [onProgress] - 流式进度回调\r\n * @param {Function} [onDone] - 流式完成回调\r\n */\r\n async sendExistingStream(params, options, onProgress, onDone) {\r\n // 兼容签名1:sendExistingStream(onProgress, onDone)\r\n if (typeof params === 'function') {\r\n onDone = options;\r\n onProgress = params;\r\n options = {};\r\n params = undefined;\r\n } else if (typeof options === 'function') {\r\n // 兼容签名2:sendExistingStream(params, onProgress, onDone)\r\n onDone = onProgress;\r\n onProgress = options;\r\n options = {};\r\n }\r\n const mergeToEntry = options?.mergeToEntry || null;\r\n const { id, signal } = this._beginRequest();\r\n try {\r\n return await this._request({ addUser: false, isStream: true, onProgress, onDone, params, mergeToEntry, signal });\r\n } catch (err) {\r\n if (err.name === 'AbortError') {\r\n this.emit('aborted', { timestamp: Date.now() });\r\n return;\r\n }\r\n throw err;\r\n } finally {\r\n this._endRequest(id);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 校验 adapter 是否满足协议(v3.0 · 阶段 3)。\r\n * 缺少必要方法时抛 ConfigurationError,并列出缺少的方法名——写\"同级适配器\"不再靠猜。\r\n * @param {*} adapter\r\n * @throws {ConfigurationError}\r\n */\r\nexport function assertAdapter(adapter) {\r\n if (!adapter || typeof adapter !== 'object') {\r\n throw new ConfigurationError('setAdapter: adapter 必须是对象(如 openaiAdapter / createOpenAIAdapter() 实例)');\r\n }\r\n const required = ['buildRequest', 'send', 'stream', 'parseResponse'];\r\n const missing = required.filter(k => typeof adapter[k] !== 'function');\r\n if (missing.length) {\r\n throw new ConfigurationError(\r\n `setAdapter: adapter 缺少必要方法: ${missing.join(', ')}(协议见 ChatService.js 顶部的 @typedef ChatAdapter)`\r\n );\r\n }\r\n}","/**\n * URL 工具函数\n */\n\n/**\n * 拼接 baseUrl 和 path\n * 注意:path 应以 '/' 开头,否则会替换 baseUrl 的最后一段\n * @param {string} baseUrl - 基础 URL(如 https://api.deepseek.com)\n * @param {string} path - 路径(如 /v1/chat/completions)\n * @returns {string} 完整的 URL\n */\nexport function joinUrl(baseUrl, path) {\n // 移除 baseUrl 末尾的斜杠(如果有)\n const base = baseUrl.replace(/\\/$/, '');\n // 确保 path 以斜杠开头\n const p = path.startsWith('/') ? path : '/' + path;\n return base + p;\n}","/**\n * 类型判断工具\n */\n\n/**\n * 判断一个值是否为字符串\n * @param {any} value - 要检查的值\n * @returns {boolean} 如果是字符串则返回 true\n */\nexport function isString(value) {\n return typeof value === 'string';\n}","/**\n * MessageFormatter — 可注册的消息格式转换器 + 可注册的过滤器链(v4.0)\n *\n * 职责:\n * 1. 内置 OpenAI 兼容格式的转换逻辑\n * 2. 支持 register(name, fn) 注册自定义格式(如 Anthropic、Gemini 等)\n * 3. v4.0:消息\"保留 / 丢弃\"规则改为可注册的过滤器链(registerFilter)\n * —— 原来硬编码在格式里的 ephemeral / system / 空内容规则,现在都是过滤器;\n * 业务插件(如 continuation)可以同名覆盖内置过滤器,接管规则。\n *\n * 用法:\n * import { MessageFormatter } from 'my-ai-chat-framework';\n *\n * // 使用内置格式\n * const msgs = MessageFormatter.format({ messages, systemPrompts, capabilities, resolveImage });\n *\n * // 注册自己的过滤器(决定哪些消息进入请求)\n * MessageFormatter.registerFilter('my-rule', (msg, ctx) => msg.role === 'system' ? null : msg);\n */\n\nimport { isString } from './typeCheck.js';\nimport { ValidationError } from '../core/Errors.js';\n\n// ====== 内置工具 ======\n\nconst IS_URL = /^https?:\\/\\//i;\n\nfunction defaultResolveImage(imageId) {\n if (IS_URL.test(imageId)) return { url: imageId };\n return null;\n}\n\nfunction buildMultimodalContent(textContent, images, resolveImage) {\n const content = [{ type: 'text', text: textContent }];\n for (const ref of images) {\n const resolved = resolveImage(ref);\n if (!resolved) continue;\n if (resolved.url) {\n content.push({ type: 'image_url', image_url: { url: resolved.url } });\n } else if (resolved.data) {\n const mime = resolved.mimeType || 'image/png';\n content.push({ type: 'image_url', image_url: { url: `data:${mime};base64,${resolved.data}` } });\n }\n }\n return content;\n}\n\n// ====== 内置过滤器(通用清理规则,无业务含义)======\n\n/**\n * ephemeral 规则:临时消息只有\"位于底部连续段\"时才保留,其余丢弃。\n * (v3.x 时硬编码在 toOpenAI 里;v4.0 起是过滤器,插件可同名覆盖)\n */\nfunction filterEphemeral(msg, { index, messages }) {\n if (!msg._ephemeral) return msg;\n let ephemStart = messages.length;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]._ephemeral) ephemStart = i;\n else break;\n }\n return index >= ephemStart ? msg : null;\n}\n\n/** system 角色消息不进对话数组(system 由 systemPrompts 统一置顶) */\nfunction filterSystemDrop(msg) {\n if (msg.role === 'system' && !msg._ephemeral) return null;\n return msg;\n}\n\n/** 空内容清理:无文本、无图片的消息丢弃(tool、带 toolCalls 的 assistant、prefix 空消息例外) */\nfunction filterEmptyDrop(msg) {\n if (msg.role === 'system' || msg.role === 'tool') return msg;\n if (msg.role === 'assistant' && msg.toolCalls?.length) return msg;\n const hasText = msg.content && isString(msg.content) && msg.content.trim();\n const hasImages = Array.isArray(msg.images) && msg.images.length > 0;\n if (!hasText && !hasImages) {\n if (msg.role === 'assistant' && msg.prefix) return msg;\n return null;\n }\n return msg;\n}\n\n// ====== 内置格式:OpenAI 兼容 ======\n\nfunction toOpenAI({ messages, systemPrompts, capabilities, resolveImage }) {\n const rImg = typeof resolveImage === 'function' ? resolveImage : defaultResolveImage;\n const result = [];\n\n // 1) system 置顶(来自 SystemPromptStore)\n for (const sp of systemPrompts) {\n if (sp?.content && isString(sp.content) && sp.content.trim()) {\n result.push({ role: 'system', content: sp.content.trim() });\n }\n }\n\n // 2) 跑过滤器链 → 决定\"哪些消息进入请求\"(规则可插拔)\n const kept = MessageFormatter._runFilters(messages, capabilities || {});\n\n // 3) 字段映射(过滤后的消息 → API 格式)\n for (const msg of kept) {\n if (msg.role === 'tool') {\n result.push({ role: 'tool', content: msg.content || '', tool_call_id: msg.toolCallId });\n continue;\n }\n\n if (msg.role === 'assistant' && msg.toolCalls?.length) {\n const entry = { role: 'assistant', content: msg.content || '', tool_calls: msg.toolCalls };\n if (msg.prefix) entry.prefix = true;\n if (capabilities?.reasoning && msg.reasoningContent) entry.reasoning_content = msg.reasoningContent;\n result.push(entry);\n continue;\n }\n\n const hasText = msg.content && isString(msg.content) && msg.content.trim();\n const hasImages = msg.images && Array.isArray(msg.images) && msg.images.length > 0;\n\n // 走到这里说明是被过滤器\"特意保留\"的空消息(prefix 续写占位)\n if (!hasText && !hasImages) {\n if (msg.role === 'assistant' && msg.prefix) {\n const prefixEntry = { role: 'assistant', content: '', prefix: true };\n if (capabilities?.reasoning && msg.reasoningContent) prefixEntry.reasoning_content = msg.reasoningContent;\n result.push(prefixEntry);\n }\n continue;\n }\n\n const entry = { role: msg.role };\n\n if (hasImages) {\n if (!capabilities?.vision) {\n throw new ValidationError(\n '[MessageFormatter] 消息包含图片但模型不支持视觉(capabilities.vision=false)。' +\n '请切换模型或移除图片。'\n );\n }\n entry.content = buildMultimodalContent(msg.content || '', msg.images, rImg);\n } else {\n entry.content = msg.content;\n }\n\n if (msg.prefix) entry.prefix = true;\n result.push(entry);\n }\n\n return result;\n}\n\n// ====== 格式化器 ======\n\nexport const MessageFormatter = {\n _formats: new Map([\n ['openai', toOpenAI]\n ]),\n\n /** 过滤器表:name → { name, fn, priority, order } */\n _filters: new Map(),\n _filterOrder: 0,\n\n /**\n * 注册自定义格式\n * @param {string} name — 格式名称(如 'anthropic', 'gemini')\n * @param {Function} fn — 转换函数,接收 { messages, systemPrompts, capabilities, resolveImage },返回消息数组\n */\n register(name, fn) {\n if (!name || typeof name !== 'string' || !name.trim()) {\n throw new ValidationError('[MessageFormatter] 格式名称必须是非空字符串');\n }\n if (typeof fn !== 'function') {\n throw new ValidationError('[MessageFormatter] 转换函数必须是 function');\n }\n this._formats.set(name.trim(), fn);\n },\n\n /**\n * 移除自定义格式(内置格式不可移除)\n * @param {string} name\n */\n unregister(name) {\n if (name === 'openai') {\n throw new ValidationError('[MessageFormatter] 内置格式 \"openai\" 不可移除');\n }\n this._formats.delete(name);\n },\n\n /**\n * 列出所有已注册的格式名称\n * @returns {Array<string>}\n */\n listFormats() {\n return [...this._formats.keys()];\n },\n\n // ===== 过滤器 API(v4.0)=====\n\n /**\n * 注册一个消息过滤器(决定消息是否进入请求、允许改写字段)。\n * 同名会**覆盖**(返回被覆盖的旧函数),因此插件可以接管内置规则。\n * @param {string} name\n * @param {(msg: Object, ctx: {index:number,total:number,messages:Array,capabilities:Object}) => Object|null} fn\n * 返回消息 = 保留(可改字段 / 返回新对象);返回 null = 丢弃\n * @param {{priority?: number}} [options] 数字越小越先执行,默认 100\n * @returns {Function|undefined} 被覆盖的旧过滤器(没有则 undefined)\n */\n registerFilter(name, fn, options = {}) {\n if (!name || typeof name !== 'string' || !name.trim()) {\n throw new ValidationError('[MessageFormatter] 过滤器名称必须是非空字符串');\n }\n if (typeof fn !== 'function') {\n throw new ValidationError('[MessageFormatter] 过滤器必须是 function');\n }\n const key = name.trim();\n const priority = typeof options.priority === 'number' ? options.priority : 100;\n const prev = this._filters.get(key);\n this._filters.set(key, { name: key, fn, priority, order: ++this._filterOrder });\n return prev ? prev.fn : undefined;\n },\n\n /**\n * 移除过滤器\n * @param {string} name\n * @returns {boolean} 是否移除成功\n */\n unregisterFilter(name) {\n return this._filters.delete(name);\n },\n\n /**\n * 列出过滤器(按执行顺序)\n * @returns {Array<{name: string, priority: number}>}\n */\n listFilters() {\n return [...this._filters.values()]\n .sort((a, b) => a.priority - b.priority || a.order - b.order)\n .map(f => ({ name: f.name, priority: f.priority }));\n },\n\n /**\n * 跑过滤器链(内部使用)\n * @param {Array} messages\n * @param {Object} capabilities\n * @returns {Array} 保留的消息\n */\n _runFilters(messages, capabilities = {}) {\n if (this._filters.size === 0) return [...messages];\n const list = [...this._filters.values()].sort((a, b) => a.priority - b.priority || a.order - b.order);\n const out = [];\n for (let i = 0; i < messages.length; i++) {\n let msg = messages[i];\n const ctx = { index: i, total: messages.length, messages, capabilities };\n let dropped = false;\n for (const f of list) {\n msg = f.fn(msg, ctx);\n if (msg === null || msg === undefined) { dropped = true; break; }\n }\n if (!dropped) out.push(msg);\n }\n return out;\n },\n\n /**\n * 按指定格式转换消息\n * @param {Object} options\n * @param {Array} options.messages — 内部消息列表\n * @param {Array} [options.systemPrompts=[]] — 启用的 system prompt\n * @param {Object} [options.capabilities={}] — 模型能力标签\n * @param {Function} [options.resolveImage] — 图片解析钩子\n * @param {string} [options.format='openai'] — 目标格式名称\n * @returns {Array<Object>}\n */\n format(options = {}) {\n const formatName = options.format || 'openai';\n const fn = this._formats.get(formatName);\n if (!fn) {\n throw new ValidationError(\n `[MessageFormatter] 未知格式 \"${formatName}\"。可用格式: ${[...this._formats.keys()].join(', ')}`\n );\n }\n return fn({\n messages: options.messages || [],\n systemPrompts: options.systemPrompts || [],\n capabilities: options.capabilities || {},\n resolveImage: options.resolveImage\n });\n }\n};\n\n// ====== 内置过滤器注册 ======\n// v4.0:核心只保留「通用清理」规则(system 置顶分流、空内容清理)。\n// ephemeral(临时消息)属于玩法规则,由 continuation 插件注册 —— 见 src/plugins/continuation.js。\nMessageFormatter.registerFilter('system-drop', filterSystemDrop, { priority: 900 });\nMessageFormatter.registerFilter('empty-drop', filterEmptyDrop, { priority: 910 });\n\n// 向后兼容:保留 assembleMessages 函数\nexport function assembleMessages(options = {}) {\n return MessageFormatter.format({ ...options, format: 'openai' });\n}\n","/**\n * OpenAI 兼容 API 适配器\n * 支持 DeepSeek 等完全兼容 OpenAI 接口的服务\n */\n\nimport { joinUrl } from \"../utils/url.js\";\nimport { isString } from \"../utils/typeCheck.js\";\nimport { APIError, NetworkError, ParsingError, ConfigurationError } from '../core/Errors.js';\nimport { MessageFormatter } from '../utils/MessageFormatter.js';\n\n// 属于\"运输层\"的配置键:工厂实例持有,请求时覆盖 config 中同名项\nconst TRANSPORT_KEYS = ['apiKey', 'baseUrl', 'apiUrl', 'path', 'headers'];\n\nexport const openaiAdapter = {\n name: 'openai',\n\n /**\n * 安装适配器。\n * - 单例(openaiAdapter):options 无意义,配置从 chat.config 读取(平铺兼容路径)\n * - 工厂实例(createOpenAIAdapter):options 并入实例自持的运输配置\n */\n install(chatService, options = {}) {\n if (this._transport) this._transport = { ...this._transport, ...options };\n chatService.setAdapter(this);\n },\n\n /**\n * 实例自持配置(运输层)覆盖调用方传入的 config 同名项。\n * modelParams 特殊处理:深度合并(config 优先,transport 兜底)——\n * 避免工厂实例的默认参数覆盖掉会话/请求级覆盖。\n */\n _resolveConfig(config) {\n const transport = this._transport || {};\n const resolved = { ...config, ...transport };\n if (transport.modelParams) {\n resolved.modelParams = { ...transport.modelParams, ...(config.modelParams || {}) };\n }\n return resolved;\n },\n\n /** 工厂实例可提供给会话层的\"请求默认参数\"(非运输键的部分) */\n getRequestDefaults() {\n if (!this._transport) return {};\n const defaults = {};\n for (const [key, value] of Object.entries(this._transport)) {\n if (!TRANSPORT_KEYS.includes(key) && value !== undefined) defaults[key] = value;\n }\n return defaults;\n },\n\n /**\n * 构建 OpenAI 格式的请求体\n * @param {Array} messages - 内部消息列表\n * @param {Object} config - 合并后的请求参数(含 model/temperature 等)\n * @param {Array} [systemPrompts=[]] - 启用的 system prompt 列表\n * @returns {Object} 请求体\n */\n buildRequest(messages, config, systemPrompts = []) {\n config = this._resolveConfig(config);\n const model = config.model || config.modelParams?.model;\n if (!model) {\n throw new ConfigurationError('Missing required config: model (either at top level or in modelParams)');\n }\n\n const mp = config.modelParams || {};\n // v4.0:**不再提供默认值** —— 没传就不放进请求体,由 API 用自己的默认\n const temperature = mp.temperature ?? config.temperature;\n const maxTokens = mp.maxTokens ?? config.maxTokens;\n const reasoningEffort = mp.reasoningEffort ?? config.reasoningEffort;\n\n // 用 MessageFormatter 统一处理:system 置顶、消息过滤、字段映射、图片解析\n // format 由 config.messageFormat 指定(默认 'openai')\n const apiMessages = MessageFormatter.format({\n messages,\n systemPrompts,\n capabilities: config.capabilities || {},\n resolveImage: config.resolveImage,\n format: config.messageFormat\n });\n\n const requestBody = {\n model,\n messages: apiMessages,\n stream: false\n };\n\n // v4.0:可选参数只在\"显式传了\"时才放进请求体(不替使用者决定要什么默认值)\n if (temperature !== undefined) requestBody.temperature = temperature;\n if (maxTokens !== undefined) requestBody.max_tokens = maxTokens;\n\n // 硬编码常用参数:驼峰命名,适配器负责转 API 格式\n if (mp.topP !== undefined) requestBody.top_p = mp.topP;\n if (mp.frequencyPenalty !== undefined) requestBody.frequency_penalty = mp.frequencyPenalty;\n if (mp.presencePenalty !== undefined) requestBody.presence_penalty = mp.presencePenalty;\n if (mp.stop !== undefined) requestBody.stop = mp.stop;\n if (mp.responseFormat !== undefined) requestBody.response_format = mp.responseFormat;\n if (mp.seed !== undefined) requestBody.seed = mp.seed;\n\n if (config.tools && Array.isArray(config.tools) && config.tools.length > 0) {\n requestBody.tools = config.tools;\n requestBody.tool_choice = 'auto';\n }\n\n if (reasoningEffort !== undefined) {\n requestBody.reasoning_effort = reasoningEffort;\n }\n\n // modelParams 中未被硬编码处理的字段,透传到请求体\n const consumedKeys = new Set([\n 'model', 'temperature', 'maxTokens', 'reasoningEffort',\n 'topP', 'frequencyPenalty', 'presencePenalty',\n 'stop', 'responseFormat', 'seed'\n ]);\n for (const [key, value] of Object.entries(mp)) {\n if (!consumedKeys.has(key) && value !== undefined) {\n requestBody[key] = value;\n }\n }\n\n return requestBody;\n },\n\n /**\n * 处理URL相关配置,将其转换为完整请求URL\n * @param {Object} config - 配置参数\n * @returns {String} 实际请求地址\n */\n _getUrl(config) {\n config = config || {};\n const { apiUrl, baseUrl, path } = config;\n // 默认 path 没有 v1 因为 baseUrl 一般包括版本号\n const defaultPath = '/chat/completions';\n\n if (apiUrl && isString(apiUrl)) {\n return apiUrl;\n }\n\n if (baseUrl && isString(baseUrl)) {\n const finalPath = (path && isString(path)) ? path : defaultPath;\n return joinUrl(baseUrl, finalPath);\n }\n\n return 'https://api.openai.com/v1/chat/completions';\n },\n\n /**\n * 非流式发送\n * @param {Object} requestBody\n * @param {Object} config\n * @param {Object} [options] — { signal, headers: {...} }\n */\n async send(requestBody, config, options = {}) {\n try {\n config = this._resolveConfig(config);\n const url = this._getUrl(config);\n\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n ...(config.headers || {}),\n ...(options.headers || {})\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n signal: options.signal\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data;\n\n } catch (error) {\n if (error instanceof APIError) throw error;\n if (error.name === 'AbortError') throw error;\n\n if (error.name === 'TypeError' && error.message.includes('fetch')) {\n throw new NetworkError(`Request failed: ${error.message}`, error);\n }\n\n throw error;\n }\n },\n\n /**\n * 解析 API 响应为内部消息格式\n */\n parseResponse(apiResponse) {\n // 检查响应结构是否有效\n if (!apiResponse || !apiResponse.choices || !apiResponse.choices[0] || !apiResponse.choices[0].message) {\n throw new ParsingError('Invalid API response: missing choices or message', apiResponse, JSON.stringify(apiResponse));\n }\n // 将 API 响应转换为内部消息格式\n const msg = apiResponse.choices[0].message;\n const internal = {\n role: msg.role,\n content: msg.content || '',\n };\n if (msg.tool_calls) internal.toolCalls = msg.tool_calls;\n if (msg.reasoning_content) internal.reasoningContent = msg.reasoning_content;\n return internal;\n },\n\n /**\n * 流式发送\n * @param {Object} requestBody\n * @param {Object} config\n * @param {Function} onProgress — 接收累积快照 { content, reasoningContent, toolCalls }\n * @param {Function} onDone — 接收最终消息对象\n * @param {Object} [options] — { signal, headers: {...} }\n */\n async stream(requestBody, config, onProgress, onDone, options = {}) {\n config = this._resolveConfig(config);\n const streamBody = { ...requestBody, stream: true };\n const url = this._getUrl(config);\n let response;\n\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n 'Accept': 'text/event-stream',\n ...(config.headers || {}),\n ...(options.headers || {})\n };\n\n try {\n response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(streamBody),\n signal: options.signal\n });\n\n } catch (error) {\n if (error.name === 'AbortError') throw error;\n\n if (error.name === 'TypeError' && error.message.includes('fetch')) {\n throw new NetworkError(`Stream request failed: ${error.message}`, error);\n }\n\n throw error;\n }\n\n // 处理非 2xx 响应\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n // 解析流式响应\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let accumulated = { role: 'assistant', content: '', reasoningContent: '' };\n let reachedDone = false; // 是否收到 [DONE]\n\n while (true) {\n // 用户调用 abort() 后 signal.aborted 为 true,跳出循环\n if (options.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n // ===== v4.0:稳健的 SSE 事件解析 =====\n // 支持:\\r\\n 换行、`data:`(无空格)、多行 data(按 SSE 规范用 \\n 连接)、注释行\n let sepMatch;\n const EVENT_SEP = /\\r?\\n\\r?\\n/;\n while ((sepMatch = EVENT_SEP.exec(buffer)) !== null) {\n const rawEvent = buffer.slice(0, sepMatch.index);\n buffer = buffer.slice(sepMatch.index + sepMatch[0].length);\n\n const dataPayload = rawEvent\n .split(/\\r?\\n/)\n .filter(l => l.startsWith('data:'))\n .map(l => l.slice(5).replace(/^ /, ''))\n .join('\\n');\n if (!dataPayload) continue;\n if (dataPayload.trim() === '[DONE]') { reachedDone = true; break; }\n\n try {\n const chunk = JSON.parse(dataPayload);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n\n // v4.0:本次 chunk 的\"增量\"(onProgress 第二个参数)\n const deltaInfo = { content: '', reasoningContent: '', toolCalls: null };\n\n if (delta.content) {\n accumulated.content += delta.content;\n deltaInfo.content = delta.content;\n }\n if (delta.tool_calls) {\n const newCalls = [];\n if (!accumulated.toolCalls) accumulated.toolCalls = [];\n for (const toolCallDelta of delta.tool_calls) {\n const index = toolCallDelta.index;\n if (!accumulated.toolCalls[index]) {\n accumulated.toolCalls[index] = {\n id: '',\n type: 'function',\n function: { name: '', arguments: '' }\n };\n }\n if (toolCallDelta.id) accumulated.toolCalls[index].id = toolCallDelta.id;\n if (toolCallDelta.type) accumulated.toolCalls[index].type = toolCallDelta.type;\n if (toolCallDelta.function?.name) {\n accumulated.toolCalls[index].function.name += toolCallDelta.function.name;\n }\n if (toolCallDelta.function?.arguments) {\n accumulated.toolCalls[index].function.arguments += toolCallDelta.function.arguments;\n }\n newCalls.push({ index, delta: toolCallDelta });\n }\n deltaInfo.toolCalls = newCalls;\n }\n if (delta.reasoning_content) {\n accumulated.reasoningContent += delta.reasoning_content;\n deltaInfo.reasoningContent = delta.reasoning_content;\n }\n\n // onProgress(snap, delta):snap = 全量累积快照;delta = 本次新增片段\n onProgress({ ...accumulated }, deltaInfo);\n } catch (e) {\n console.warn('stream parsing failed :', e, dataPayload);\n }\n }\n if (reachedDone) break;\n }\n\n const finalMessage = {\n role: 'assistant',\n content: accumulated.content,\n toolCalls: accumulated.toolCalls,\n reasoningContent: accumulated.reasoningContent\n };\n onDone(finalMessage);\n },\n\n /**\n * 处理对话请求的非 2xx 响应,抛出 APIError,包含状态码、错误消息和原始响应文本\n * @param {Response} response - fetch API 的 Response 对象\n * @returns {Promise<never>} 抛出错误 \n */\n async handleErrorResponse(response) {\n let errorText = await response.text();\n let errorMessage = `HTTP ${response.status}`;\n // 尝试从响应体中解析 JSON 格式的错误信息\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error?.message || errorJson.message || errorText.slice(0, 200);\n } catch (e) {\n // 如果解析失败,使用原始文本(已截断)\n errorMessage = errorText.length > 200 ? errorText.slice(0, 200) + '...' : errorText;\n }\n throw new APIError(errorMessage, response.status, null, errorText);\n }\n\n};\n\n/**\n * 创建 OpenAI 兼容适配器实例(工厂)。\n *\n * 与单例 openaiAdapter 的区别:每个实例自持一份运输配置(apiKey/baseUrl/apiUrl/path/headers),\n * 互不干扰——解决\"同一适配器装到多个 ChatService 互相覆盖\"的单例陷阱。\n *\n * 用法:\n * const adapter = createOpenAIAdapter({ apiKey, baseUrl, modelParams: { temperature: 0.8 } });\n * const chat = new ChatService({ adapter, model: 'deepseek-chat' });\n * chat.use(adapter); // 或直接 new ChatService({ adapter })\n *\n * 非运输键(model/modelParams/messageFormat/resolveImage/capabilities 等)会作为\n * \"请求默认参数\"供会话层合并,单次请求仍可覆盖。\n */\nexport function createOpenAIAdapter(options = {}) {\n return { ...openaiAdapter, _transport: { ...options } };\n}","/**\n * 工具调用插件\n * 功能:拦截助手消息中的 tool_calls,执行对应的工具,将结果作为 tool 消息加入对话,\n * 然后自动继续对话(通过 sendExisting / sendExistingStream),直到没有新的工具调用。\n *\n * 设计要点:\n * - 支持普通请求和流式请求(通过 isStream 标志区分)\n * - 支持多次工具调用循环(maxIterations 防止无限循环)\n * - 并行执行工具\n * - 工具执行失败时,仍然返回错误信息给 AI,而不是中断整个流程\n * - 触发 tool-error 事件,方便用户监听工具执行异常\n *\n * 注意:请使用 createToolCallingPlugin() 工厂创建实例。\n * 默认导出的 toolCallingPlugin 是兼容用的模块级单例,装到多个 ChatService\n * 实例会互相覆盖(executor 表、工具定义、配置),新代码不要直接用单例。\n */\nexport const toolCallingPlugin = createToolCallingPlugin();\n\n/**\n * 创建工具调用插件实例(工厂)。\n * @param {Object} [options] — { timeout?, maxIterations? }\n * - 两者**默认都不限制**:不传就是\"循环到没有工具调用为止 / 不超时\"\n * - 想限制才传(这是使用者的选择,不是框架替你做的决定)\n */\nexport function createToolCallingPlugin(options = {}) {\n return {\n name: 'tool-calling',\n // 默认不限制循环轮数(旧版硬编码 5 轮,属于\"框架替用户做决定\",v4.0 移除)\n maxIterations: options.maxIterations ?? Infinity,\n _options: { ...options },\n _tools: new Map(), // 工具名 -> { executor, description }\n _toolDefs: [], // OpenAI 格式的工具定义(按注册顺序)\n chatService: null,\n\n /**\n * 安装插件:挂 registerTool、注册 beforeRequest 钩子注入工具定义、注册响应处理钩子\n * @param {ChatService} chatService - ChatService 实例\n * @param {Object} [options] - 安装时配置(合并进 _options)\n */\n install(chatService, options = {}) {\n this._options = { ...this._options, ...options };\n this.chatService = chatService;\n\n // 每次请求前把工具定义注入到请求配置(v3.0:beforeSend 车间,不再用 _hooks 钩子)\n chatService.pipe({\n name: 'tool-calling-inject',\n phase: 'beforeSend',\n run: ({ config }) => {\n if (this._toolDefs.length) config.tools = this._toolDefs;\n }\n });\n\n /**\n * 注册工具\n * @param {string} name - 工具名称(唯一标识)\n * @param {string} description - 工具描述(告诉 AI 何时调用)\n * @param {Function} executor - 异步执行函数,接收参数对象,返回结果(字符串或对象)\n * @param {Object} parameters - JSON Schema 参数定义(可选,默认为空对象)\n * @returns {ChatService} 返回 chatService 实例,支持链式调用\n */\n const registerTool = (name, description, executor, parameters = {}) => {\n // 保存执行器\n this._tools.set(name, { executor, description });\n\n // 构建 OpenAI 兼容的工具定义\n // 参数写法:{ 参数名: { type, description, required? } }\n // → 必填项收集到顶层 required 数组;properties 里不残留 required 字段(标准 JSON Schema 要求)\n const properties = {};\n const requiredKeys = [];\n for (const [key, def] of Object.entries(parameters || {})) {\n const { required: isRequired, ...schema } = def || {};\n properties[key] = schema;\n if (isRequired) requiredKeys.push(key);\n }\n const toolDefinition = {\n type: 'function',\n function: {\n name,\n description,\n parameters: { type: 'object', properties, required: requiredKeys }\n }\n };\n // 避免重复添加相同工具\n const existing = this._toolDefs.find(t => t.function.name === name);\n if (!existing) {\n this._toolDefs.push(toolDefinition);\n }\n return chatService;\n };\n\n // 响应后处理(v3.0:afterSend 车间,不再占用 ChatService._processResponse 单座槽位)\n chatService.pipe({\n name: 'tool-calling-loop',\n phase: 'afterSend',\n run: async (ctx) => {\n ctx.result = await this._handleWithTools(\n ctx.result,\n ctx.isStream,\n ctx.onProgress || undefined,\n ctx.onDone || undefined\n );\n }\n });\n\n // 交给 use() 挂到 chat.plugins['tool-calling'](v4.0 命名空间)\n return { registerTool, tools: this._tools, uninstall: (c) => this.uninstall(c) };\n },\n\n /** 卸载:移除注入车间(工具定义注入 + 工具循环) */\n uninstall(chat) {\n if (!chat) return chat;\n chat.unpipe?.('tool-calling-inject');\n chat.unpipe?.('tool-calling-loop');\n if (chat.plugins && chat.plugins['tool-calling']) delete chat.plugins['tool-calling'];\n return chat;\n },\n\n /**\n * 核心处理逻辑:检查响应 → 执行工具 → 继续请求 → 循环\n * @param {Object} initialResponse - 初始 AI 响应\n * @param {boolean} isStream - 是否流式\n * @param {Function} onProgress - 流式进度回调\n * @param {Function} onDone - 流式完成回调\n * @returns {Promise<Object>} 最终 AI 响应\n */\n async _handleWithTools(initialResponse, isStream, onProgress, onDone) {\n const self = this;\n const chat = this.chatService;\n let iteration = 0;\n\n /**\n * 递归处理工具调用\n * @param {Object} initialResponse - 初始 AI 响应(可能是第一次请求的响应)\n * @returns {Promise<Object>} 最终 AI 响应(不含 tool_calls)\n */\n async function processResponse(initialResponse) {\n let lastResponse = initialResponse;\n while (iteration < self.maxIterations) {\n // 检查 AI 返回的消息中是否包含工具调用请求\n const toolCalls = lastResponse?.toolCalls;\n if (!toolCalls || toolCalls.length === 0) break;\n\n iteration++;\n // 并行执行所有工具调用(彼此独立,不会因一个失败而阻塞其他)\n const toolResults = await Promise.all(\n toolCalls.map(async (call) => {\n const toolName = call.function?.name;\n\n // 解析参数(JSON.parse 可能失败,需单独 try/catch)\n let args;\n try {\n args = JSON.parse(call.function?.arguments || '{}');\n } catch (parseErr) {\n chat.emit('tool-error', {\n toolName: toolName || 'unknown',\n error: parseErr,\n toolCallId: call.id,\n stage: 'parse',\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: `参数解析失败: ${parseErr.message}`,\n success: false\n };\n }\n\n const tool = self._tools.get(toolName);\n if (!tool) {\n chat.emit('tool-error', {\n toolName,\n error: new Error(`Tool not registered: ${toolName}`),\n toolCallId: call.id,\n stage: 'lookup',\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: `工具 ${toolName} 未注册`,\n success: false\n };\n }\n\n try {\n // 工具执行超时:只在工厂 options.timeout 显式传入时生效(不传 = 不限制)\n const toolTimeout = self._options.timeout;\n let result;\n if (typeof toolTimeout === 'number' && toolTimeout > 0) {\n const controller = new AbortController(); // 通知支持 signal 的工具去清理\n const timeoutErr = new Error(`工具 ${toolName} 执行超时 (${toolTimeout}ms)`);\n timeoutErr.name = 'ToolTimeoutError';\n let timer;\n const timeoutPromise = new Promise((_, reject) => {\n timer = setTimeout(() => { controller.abort(); reject(timeoutErr); }, toolTimeout);\n });\n const execPromise = Promise.resolve().then(() => tool.executor(args, { signal: controller.signal }));\n execPromise.catch(() => {}); // 超时后工具的后续结果不再冒泡为 unhandled\n try {\n result = await Promise.race([execPromise, timeoutPromise]);\n } finally {\n clearTimeout(timer); // 工具先完成时不留悬挂的 timer(旧版泄漏点)\n }\n } else {\n result = await tool.executor(args);\n }\n chat.emit('tool-success', {\n toolName,\n result,\n toolCallId: call.id,\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n content: typeof result === 'string' ? result : JSON.stringify(result),\n success: true\n };\n } catch (err) {\n const stage = err.name === 'ToolTimeoutError' ? 'timeout' : 'execute';\n chat.emit('tool-error', {\n toolName,\n error: err,\n toolCallId: call.id,\n stage,\n timestamp: Date.now()\n });\n return {\n tool_call_id: call.id,\n error: err.message,\n success: false\n };\n }\n })\n );\n\n // 将工具执行结果作为 tool 消息添加到对话历史\n for (const tr of toolResults) {\n // 注意:addTool 的第二个参数是 tool_call_id\n // 如果工具执行失败且没有内容,则传递错误信息作为 content\n const content = tr.success ? tr.content : (tr.error || '工具执行失败');\n chat.messages.addTool(content, tr.tool_call_id);\n }\n\n // 再次请求 AI,获取基于工具结果的回复\n if (isStream) {\n // 流式继续:使用 sendExistingStream\n await new Promise((resolve) => {\n chat.sendExistingStream(\n (chunk) => {\n if (onProgress) onProgress(chunk);\n lastResponse = chunk;\n },\n (final) => {\n lastResponse = final;\n resolve();\n }\n );\n });\n } else {\n // 非流式继续:使用 sendExisting\n lastResponse = await chat.sendExisting();\n }\n }\n return lastResponse;\n }\n\n // 初始响应已由 ChatService 发出,直接进入工具调用循环\n const final = await processResponse(initialResponse);\n return final;\n }\n };\n}","/**\n * model-registry 插件\n * \n * 职责:\n * 1. 内置常用模型的能力标签表(model → capabilities 映射)\n * 2. 安装时自动根据 config.model 查表,写入 config.capabilities\n * 3. 监听 config-updated 事件,切换模型时自动同步能力\n * 4. 提供 chat.registerModel(),允许用户追加/覆盖自定义模型\n * \n * 能力标签字段(全部可选,缺失视为 false / 未知):\n * {\n * reasoning: boolean — 是否返回 reasoning_content(思维链)\n * vision: boolean — 是否支持图片输入\n * toolCalling: boolean — 是否支持 function calling\n * streaming: boolean — 是否支持流式传输\n * maxInputTokens: number — 最大输入 token 数\n * maxOutputTokens: number — 最大输出 token 数\n * }\n * \n * 使用:\n * chat.use(modelRegistryPlugin);\n * // config.capabilities 现在已自动填充\n * chat.registerModel('my-custom-model', { toolCalling: true });\n */\n\nimport { ValidationError } from '../core/Errors.js';\n\n// ====== 内置模型能力表 ======\n// 数据来源:各厂商官方文档(2026-05 快照),用户可通过 registerModel() 覆盖\nconst BUILTIN_MODELS = {\n // ---- DeepSeek ----\n 'deepseek-chat': {\n reasoning: false,\n vision: false,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 128000,\n maxOutputTokens: 8192\n },\n 'deepseek-reasoner': {\n reasoning: true,\n vision: false,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 128000,\n maxOutputTokens: 8192\n },\n\n // ---- OpenAI ----\n 'gpt-4o': {\n reasoning: false,\n vision: true,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 128000,\n maxOutputTokens: 16384\n },\n 'gpt-4o-mini': {\n reasoning: false,\n vision: true,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 128000,\n maxOutputTokens: 16384\n },\n 'gpt-3.5-turbo': {\n reasoning: false,\n vision: false,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 16385,\n maxOutputTokens: 4096\n },\n 'o1': {\n reasoning: true,\n vision: false,\n toolCalling: false,\n streaming: false,\n maxInputTokens: 200000,\n maxOutputTokens: 100000\n },\n 'o3-mini': {\n reasoning: true,\n vision: false,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 200000,\n maxOutputTokens: 100000\n },\n\n // ---- Anthropic ----\n 'claude-3.5-sonnet': {\n reasoning: false,\n vision: true,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 200000,\n maxOutputTokens: 8192\n },\n 'claude-3.5-haiku': {\n reasoning: false,\n vision: true,\n toolCalling: true,\n streaming: true,\n maxInputTokens: 200000,\n maxOutputTokens: 8192\n }\n};\n\nconst DEFAULT_CAPABILITIES = {\n streaming: true,\n toolCalling: false,\n reasoning: false,\n vision: false\n};\n\n/**\n * 注意:请使用 createModelRegistryPlugin() 工厂创建实例。\n * 默认导出的 modelRegistryPlugin 是兼容用的模块级单例,装到多个 ChatService\n * 实例会互相覆盖(注册表、实例引用),新代码不要直接用单例。\n */\nexport const modelRegistryPlugin = createModelRegistryPlugin();\n\n/**\n * 创建模型能力注册表插件实例(工厂)。\n * @param {Object} [options] — { models: { '模型名': capabilities } }(合并进内置表)\n */\nexport function createModelRegistryPlugin(options = {}) {\n return {\n name: 'model-registry',\n\n _options: { ...options },\n _registry: new Map([\n ...Object.entries(BUILTIN_MODELS),\n ...Object.entries(options.models || {})\n ]),\n chat: null,\n\n /**\n * 安装插件\n * @param {ChatService} chatService - ChatService 实例\n * @param {Object} [options] - 安装时配置(合并进 _options)\n */\n install(chatService, options = {}) {\n this._options = { ...this._options, ...options };\n this.chat = chatService;\n\n /**\n * 注册/覆盖一个模型的能力标签\n * @param {string} name — 模型名称\n * @param {Object} capabilities — 能力标签对象(部分字段即可,未提供的取默认值)\n * @returns {ChatService}\n */\n const registerModel = (name, capabilities = {}) => {\n if (!name || typeof name !== 'string' || !name.trim()) {\n throw new ValidationError('[model-registry] 模型名称必须是非空字符串');\n }\n const merged = { ...DEFAULT_CAPABILITIES, ...capabilities };\n this._registry.set(name.trim(), merged);\n // 如果注册的模型与当前使用的模型同名,立即同步\n if (chatService.config.model === name.trim()) {\n this._syncCapabilities();\n }\n return chatService;\n };\n\n /**\n * 列出所有已注册的模型名称\n * @returns {Array<string>}\n */\n const listModels = () => {\n return [...this._registry.keys()];\n };\n\n // 安装时立即同步一次\n this._syncCapabilities();\n\n // 每次请求时按\"本次生效的 model\"(含请求级覆盖)解析 capabilities\n // v3.0:beforeSend 车间,不再用 _hooks 钩子(时机与之前一致)\n chatService.pipe({\n name: 'model-registry-caps',\n phase: 'beforeSend',\n run: ({ config }) => {\n if (!config.model) return;\n const caps = this._registry.get(config.model);\n if (caps) config.capabilities = { ...caps };\n }\n });\n\n // 监听配置变更:model 变了就重新同步(兼容路径;请求时按生效 model 实时查表)\n chatService.on('config-updated', ({ changes }) => {\n if ('model' in changes) {\n this._syncCapabilities();\n }\n // 如果用户手动改了 capabilities,用 merge 保留未指定的字段\n if ('capabilities' in changes && !('model' in changes)) {\n const current = this._lookupCapabilities();\n if (current) {\n chatService.config.capabilities = { ...current, ...changes.capabilities };\n }\n }\n });\n\n // 交给 use() 挂到 chat.plugins['model-registry'](v4.0 命名空间)\n return { registerModel, listModels, uninstall: (c) => this.uninstall(c) };\n },\n\n /** 卸载:移除能力同步车间 + 事件监听 + 命名空间条目 */\n uninstall(chat = this.chat) {\n if (!chat) return chat;\n chat.unpipe?.('model-registry-caps');\n if (chat.plugins && chat.plugins['model-registry']) delete chat.plugins['model-registry'];\n return chat;\n },\n\n // ---- 内部方法 ----\n\n /** 查表获取模型能力,未注册返回 null */\n _lookupCapabilities() {\n const model = this.chat.config.model;\n if (!model) return null;\n return this._registry.get(model) || null;\n },\n\n /** 将查到的能力写入 config.capabilities */\n _syncCapabilities() {\n const caps = this._lookupCapabilities();\n if (caps) {\n // 若用户已手动设了 capabilities,用模型的合并(用户值优先?还是模型优先?)\n // 策略:模型优先,因为换了模型能力应该重置\n this.chat.config.capabilities = { ...caps };\n }\n // 未注册的模型:不清除已有 capabilities,保留用户手动设的值\n }\n };\n}\n","/**\n * continuation 插件(v4.0)\n *\n * 「续写 / 临时消息」这套玩法住在这里 —— 核心不认识它,装上才有:\n * 1. 续写检测(替换核心的 autoContinue 步骤):底部若有带 prefix 标记的消息,就把结果合并回它\n * 2. ephemeral 过滤器:临时消息只有\"位于底部连续段\"时才发给 API\n * 3. continueLast / continueLastStream 两个 API\n * 4. uninstall():把自己装的东西全部还原(步骤、过滤器、方法)\n *\n * 用法:\n * import { createContinuationPlugin } from 'my-ai-chat-framework';\n * chat.use(createContinuationPlugin({ autoContinue: true }));\n *\n * // 模拟思维链:注入一条临时引导,结果合并回上一条 assistant,不留痕\n * chat.messages.addOnceAssistant('(内心:他肯定又要熬夜)');\n * await chat.stream('在吗');\n */\n\nimport { MessageFormatter } from '../utils/MessageFormatter.js';\nimport { ValidationError } from '../core/Errors.js';\n\n/**\n * ephemeral 规则:只有底部连续的临时消息保留,其余丢弃(与 v3.x 核心内置行为一致)。\n * 导出以便使用者直接注册到 MessageFormatter(不使用插件时也能用这条规则)。\n */\nexport function ephemeralFilterRule(msg, { index, messages }) {\n if (!msg._ephemeral) return msg;\n let start = messages.length;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]._ephemeral) start = i;\n else break;\n }\n return index >= start ? msg : null;\n}\n\n/**\n * 找续写目标:最后一条必须是 assistant 或临时消息(否则抛错)。\n * @param {object} chat ChatService 实例\n */\nexport function prepareContinue(chat) {\n const msgs = chat.messages.getAll();\n const last = msgs[msgs.length - 1];\n if (last && (last.role === 'assistant' || last._ephemeral)) return last;\n throw new ValidationError('最后一条消息不是 assistant,无法续写');\n}\n\n/** 续写结束后的\"转正\" */\nfunction finalize(chat, targetId) {\n chat.messages.update(targetId, { _complete: true, prefix: undefined, _ephemeral: false });\n}\n\n/**\n * 创建续写插件实例(工厂)\n * @param {Object} [options] — { autoContinue?: boolean } 是否自动识别底部 prefix 消息并合并(默认 false)\n */\nexport function createContinuationPlugin(options = {}) {\n return {\n name: 'continuation',\n _options: { autoContinue: false, ...options },\n _prevEphemeralFilter: undefined,\n _installedOn: null,\n\n /**\n * 安装:替换续写步骤 + 接管 ephemeral 过滤 + 挂续写 API\n * @param {import('../core/ChatService.js').ChatService} chat\n */\n install(chat) {\n this._installedOn = chat;\n\n // 0) 登记可运行时修改的配置字段(v4.0:插件自带配置,不用改核心白名单)\n if (typeof chat.registerConfigKeys === 'function') {\n chat.registerConfigKeys({ autoContinue: (v) => typeof v === 'boolean' });\n }\n\n // 1) 续写检测(用 v3.1 的出口替换核心的 autoContinue 步骤)\n chat.replaceStage('autoContinue', (ctx) => {\n // 运行时 `chat.updateConfig({ autoContinue })` 优先,其次取安装时的工厂 options\n const enabled = chat.config.autoContinue ?? this._options.autoContinue;\n if (!enabled) return; // 默认关:不替用户做选择\n if (ctx.mergeToEntry) return;\n const last = ctx.messages.getLast();\n if (last && last.prefix && (last.role === 'assistant' || last._ephemeral)) {\n ctx.mergeToEntry = last.id;\n }\n });\n\n // 2) 接管 ephemeral 过滤(同名覆盖;保存旧函数,卸载时还原)\n this._prevEphemeralFilter = MessageFormatter.registerFilter('ephemeral', ephemeralFilterRule, { priority: 100 });\n\n // 3) 续写 API(通过 chat.plugins.continuation 暴露,v4.0 命名空间)\n const continueLast = async () => {\n const target = prepareContinue(chat);\n chat.messages.update(target.id, { prefix: true });\n try {\n await chat.sendExisting(undefined, { mergeToEntry: target.id });\n finalize(chat, target.id);\n return target;\n } catch (err) {\n finalize(chat, target.id);\n if (err.name === 'AbortError') {\n chat.emit('aborted', { timestamp: Date.now() });\n return target;\n }\n throw err;\n }\n };\n\n const continueLastStream = async (onProgress, onDone) => {\n const target = prepareContinue(chat);\n chat.messages.update(target.id, { prefix: true });\n const baseLen = (target.content || '').length;\n try {\n await chat.sendExistingStream(\n undefined,\n { mergeToEntry: target.id },\n (chunk) => { if (onProgress) onProgress({ ...chunk, content: (chunk.content || '').slice(baseLen) }); },\n (final) => { if (onDone) onDone(final); }\n );\n finalize(chat, target.id);\n return target;\n } catch (err) {\n finalize(chat, target.id);\n if (err.name === 'AbortError') {\n chat.emit('aborted', { timestamp: Date.now() });\n return target;\n }\n throw err;\n }\n };\n\n // 交给 use() 挂到 chat.plugins['continuation']\n return { continueLast, continueLastStream, uninstall: () => this.uninstall(chat) };\n },\n\n /**\n * 卸载:恢复被替换的步骤、还原过滤器、移除挂载的方法\n * @param {import('../core/ChatService.js').ChatService} [chat]\n */\n uninstall(chat = this._installedOn) {\n if (!chat) return chat;\n if (typeof chat.restoreStage === 'function') chat.restoreStage('autoContinue');\n if (this._prevEphemeralFilter) {\n MessageFormatter.registerFilter('ephemeral', this._prevEphemeralFilter, { priority: 100 });\n } else {\n MessageFormatter.unregisterFilter('ephemeral');\n }\n if (chat.plugins && chat.plugins['continuation']) delete chat.plugins['continuation'];\n if (typeof chat.unregisterConfigKeys === 'function') chat.unregisterConfigKeys(['autoContinue']);\n this._installedOn = null;\n return chat;\n }\n };\n}\n\n/** 兼容用单例(多实例场景请用工厂) */\nexport const continuationPlugin = createContinuationPlugin();\n"],"mappings":"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"}