Skip to content

Conversation

@dingyi222666
Copy link
Member

这个 PR 修复了核心服务中的错误处理和生命周期管理问题,提升了系统的稳定性和可靠性。

新功能

Bug 修复

  • 修复了消息延迟中间件中的超时 Promise 处理问题
  • 改进了错误处理和事件生命周期管理
  • 移除了不必要的插件清理和警告日志,防止服务停止时的处理问题
  • 注释了插件未找到的警告以减少日志噪音

其他更改

  • 改进了服务关闭过程的可靠性
  • 优化了错误日志输出,减少无关警告信息

- Add chatluna/after-chat-error event for proper error cleanup
- Fix message delay middleware to handle both success and error cases
- Migrate memory plugins from ctx.on to ctx.before for correct event timing
- Update lunar variable plugin to use proper event handler syntax
- Ensure chat session cleanup occurs regardless of success or failure

These changes resolve issues where chat sessions could remain in inconsistent
states after errors, particularly fixing timeout Promise handling and ensuring
proper resource cleanup.
- Comment out plugin not found warning to reduce noise
- Remove plugin cleanup loop in stop() method to prevent disposal issues
- Improve service shutdown process reliability
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 20, 2025

Walkthrough

扩展聊天错误处理以携带调用上下文并新增 chatluna/after-chat-error 事件;chat 流程引入 responseMessage、支持 postHandler 后处理并改写历史写入;若干插件事件从 ctx.on('chatluna/before-chat') 迁移为 ctx.before('chatluna/chat');抽取消息延迟中间件批次完成逻辑;移除 free-google-api 提供者并重构 DuckDuckGo Lite 与浏览器抓取的行为;调整工具标识与请求代理初始化。

Changes

Cohort / File(s) Summary of changes
聊天错误处理 & Chat 流程
packages/core/src/llm-core/chat/app.ts
扩展 handleChatError 签名为 (arg, wrapper, error);在 wrapper 创建失败时调用;引入 responseMessage,将响应 message 用于 displayResponse/ additional_kwargs;将 handlePostProcessing 结果整合入 displayResponse 与额外变量;在历史写入中以 rawOnCensor 选择保存项;触发并声明 chatluna/after-chat-error 事件。
批次收尾与错误路由
packages/core/src/middlewares/chat/message_delay.ts
抽出 completeBatch 统一处理批次完成与清理;将 chatluna/after-chat 与新增的 chatluna/after-chat-error 都路由至该函数;在有/无等待者两条路径中停止处理器、清除超时并删除批次。
Hook 迁移(on → before,事件名更新)
packages/core/src/llm-core/memory/authors_note/index.ts, packages/core/src/llm-core/memory/lore_book/index.ts, packages/variable-extension/src/plugins/lunar.ts
将事件注册从 ctx.on('chatluna/before-chat', ...) 改为 ctx.before('chatluna/chat', ...),回调签名与内部逻辑保持不变。
渲染拆分行为调整
packages/core/src/renders/mixed-voice.ts, packages/core/src/renders/voice.ts
_splitMessagemap 改为 flatMap;非文本消息返回 [];使用 message.attrs['content'] ?? '' 做默认;结果追加 .filter(Boolean),避免 undefined。
聊天服务卸载与停止流程
packages/core/src/services/chat.ts
uninstallPlugin 在未找到目标时直接返回(不再打印警告);stop() 仅释放聊天接口包装与平台服务,不再逐一卸载所有插件。
Search 服务:配置与提供者变更
packages/search-service/src/index.ts, packages/search-service/src/plugin.ts, packages/search-service/src/providers/free_google_api.ts, packages/search-service/src/providers/duckduckgo_lite.ts, packages/search-service/src/chain/browsing_chain.ts, packages/search-service/src/providers/bing_web.ts
从 Config 中移除 freeSearchBaseURL 并从 searchEngine 集合剔除 'free-google-api'(默认改为 bing-web);删除 FreeGoogleSearchProvider;DuckDuckGo Lite 重构为 performLiteSearch + HTML 解析、去重与 URL 清洗;工具 id 改为 'web_search'/'web_browser';Bing provider 增加反检测注入脚本、将 waitUntil 改为 networkidle0,并在 finally 中关闭 page。
链与工具相关调整
packages/core/src/llm-core/chain/plugin_chat_chain.ts
工具激活比较改为按工具 name 匹配(不再按对象引用);移除 parallelIntermediateSteps 的历史预写入分支;删除 ToolMessageAgentStep 导入。
请求/代理初始化微调
packages/core/src/utils/request.ts
将代理地址从全局依赖改为每次调用的 proxyAddress 参数;对 dispatcher/agent 的空值检查改为严格的 null 判定以决定是否创建/重用代理。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as 用户
  participant App as Chat(App)
  participant Ctx as Koishi Ctx
  participant Chain as LLM Chain
  participant MW as message_delay 中间件

  U->>App: 调用 chat(arg)
  App->>Ctx: ctx.before('chatluna/chat', ...) 获取 promptVariables / chain
  Ctx-->>App: 返回 promptVariables, chain
  App->>Chain: processChat(arg, wrapper)
  alt 成功
    Chain-->>App: 返回 responseMessage
    App->>App: 如果有 postHandler -> handlePostProcessing(responseMessage) -> 更新 displayResponse 与 additionalArgs
    App->>Ctx: 触发 'chatluna/after-chat'
    Ctx->>MW: after-chat
    MW->>MW: completeBatch()
    App-->>U: 返回 displayResponse
  else 抛错
    Chain--xApp: 抛出 error
    App->>App: handleChatError(arg, wrapper, error)
    App->>Ctx: 触发 'chatluna/after-chat-error'(error, conversationId, sourceMessage, promptVariables, chatInterface, chain)
    Ctx->>MW: after-chat-error
    MW->>MW: completeBatch()
    App--xU: 继续抛出 error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

我是小兔在代码堆,轻跳错落把线理清,
错误携上下文回家去,批次终结静又宁;
钩子换门少了迷雾,搜索更简清洗新声,
小兔记下这些改动,耳朵一翘心也轻。 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 标题 “[Fix] 修复核心服务错误处理和生命周期管理问题” 简洁且直接反映了本次 PR 的主要意图,准确覆盖了错误处理、生命周期管理和相关中间件的修复点;语句清晰无冗余信息或文件列表,便于团队通过变更记录快速了解主要目的。基于提供的变更摘要与目标,该标题与改动高度相关且表达明确。
Description Check ✅ Passed PR 描述与变更集内容相关且包含具体修复点(中间件超时 Promise 处理、错误处理/事件生命周期改进、移除多余插件清理与日志噪音、改进服务关闭流程),这些要点与原始文件级摘要一致,满足本检查的宽松通过条件。描述虽偏高层但足以说明目的和影响范围,有助于审查者理解改动意图。
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/core-message-delay

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dde022d and 9da5432.

📒 Files selected for processing (1)
  • packages/search-service/src/index.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/search-service/src/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/middlewares/chat/message_delay.ts (1)

133-154: 阻塞:先调用 resolveWaiters 再 delete 会误删新批次,导致死锁/消息丢失

resolveWaiters 执行时(如 waitForBatchCompletion 的 waiter),会立即 batches.set(conversationId, ...) 生成“下一批”。紧接着当前代码的 batches.delete(conversationId) 会把这个新批次删掉,后续就无法继续处理,表现为卡死或消息丢失。应先删除“旧批次”,再唤醒 waiters,并对 delete 做同一性校验以避免竞态。

建议修复(最小改动,保持现有语义):

-    const completeBatch = async (conversationId: string) => {
-        const batch = batches.get(conversationId)
-        if (batch?.resolveWaiters.length > 0) {
+    const completeBatch = async (conversationId: string) => {
+        const batch = batches.get(conversationId)
+        if (!batch) return
+        if (batch.resolveWaiters.length > 0) {
             logger.debug(
                 `Completing batch for ${conversationId}, messages: ${batch.messages.length}`
             )
-            if (batch.timeout) {
-                batch.timeout()
-            }
-            batch.resolveWaiters.forEach((resolve) => resolve())
-            batches.delete(conversationId)
+            if (batch.timeout) {
+                batch.timeout()
+                // 释放引用,防误用
+                batch.timeout = undefined
+            }
+            // 先删除旧批次,避免 waiter 里 set 的新批次被误删
+            if (batches.get(conversationId) === batch) {
+                batches.delete(conversationId)
+            }
+            for (const resolve of batch.resolveWaiters) resolve()
         } else if (batch) {
             logger.debug(`Cleaning up batch for ${conversationId}`)
             if (batch.timeout) {
                 batch.timeout()
             }
             if (batch.processorResolve) {
                 batch.processorResolve(ChainMiddlewareRunStatus.STOP)
             }
-            batches.delete(conversationId)
+            if (batches.get(conversationId) === batch) {
+                batches.delete(conversationId)
+            }
         }
     }

此修复为必须合入项。

🧹 Nitpick comments (1)
packages/core/src/services/chat.ts (1)

162-163: 注释掉的警告日志可能会隐藏插件卸载问题

注释掉插件未找到的警告日志可能会让开发者难以发现插件卸载时的问题。虽然这样可以减少日志噪音,但建议考虑:

  1. 使用 debug 级别日志而非完全注释掉
  2. 或者在特定场景下(如服务停止时)跳过警告
-            // this.ctx.logger.warn('Plugin %c not found', platformName)
+            this.ctx.logger.debug('Plugin %c not found during uninstall', platformName)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70e8b30 and 48f1645.

📒 Files selected for processing (6)
  • packages/core/src/llm-core/chat/app.ts (3 hunks)
  • packages/core/src/llm-core/memory/authors_note/index.ts (1 hunks)
  • packages/core/src/llm-core/memory/lore_book/index.ts (1 hunks)
  • packages/core/src/middlewares/chat/message_delay.ts (2 hunks)
  • packages/core/src/services/chat.ts (1 hunks)
  • packages/variable-extension/src/plugins/lunar.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/core/src/llm-core/chat/app.ts (1)
packages/core/src/llm-core/chain/base.ts (1)
  • ChatLunaLLMCallArg (37-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (9)
packages/core/src/llm-core/memory/lore_book/index.ts (1)

12-55: 事件钩子从 on 改为 before 的变更看起来合理

将事件注册从 ctx.on('chatluna/before-chat', ...) 改为 ctx.before('chatluna/chat', ...) 符合整体 PR 的事件重构方向。回调参数保持不变,功能逻辑也未改变。

packages/core/src/llm-core/memory/authors_note/index.ts (1)

7-39: 事件钩子迁移符合预期

ctx.on('chatluna/before-chat', ...) 改为 ctx.before('chatluna/chat', ...),与 lore_book 的变更保持一致,是统一事件处理模式的一部分。

packages/variable-extension/src/plugins/lunar.ts (1)

16-32: 农历插件的事件钩子更新正确

事件注册从 ctx.on('chatluna/before-chat', ...) 改为 ctx.before('chatluna/chat', ...),与其他文件的变更保持一致。农历数据填充逻辑保持不变。

packages/core/src/llm-core/chat/app.ts (3)

52-65: 新的错误处理机制设计良好

handleChatError 方法的签名变更增加了更多上下文信息(arg 和 wrapper),这样能够在错误事件中提供更丰富的调试信息。新增的 chatluna/after-chat-error 事件可以让其他模块统一处理聊天错误。


112-112: 错误处理调用正确更新

catch 块中正确地传递了新增的参数(arg 和 wrapper)给 handleChatError 方法。


483-490: 新增的错误事件声明完整

chatluna/after-chat-error 事件的声明包含了所有必要的参数,可以为错误处理提供充分的上下文信息。

packages/core/src/services/chat.ts (1)

341-344: 确认:stop() 不需要显式遍历卸载插件 — 插件在其它位置自动清理
ChatLunaPlugin 构造函数通过 ctx.on('dispose') 调用 ctx.chatluna.uninstallPlugin(this),各 register* 调用使用 this.ctx.effect(...) 注册资源/清理;uninstallPlugin 会调用 _chatInterfaceWrapper?.dispose(platform) 并从 _plugins 删除插件。故 stop() 仅 dispose chatInterfaceWrapper 与 platformService 是预期行为,无需在此处再次遍历卸载插件。

packages/core/src/middlewares/chat/message_delay.ts (2)

156-164: 事件参数顺序正确;建议(可选)为回调加异常捕获

仓库中 emit('chatluna/after-chat-error', error, arg.conversationId, arg.message) 位于 packages/core/src/llm-core/chat/app.ts,参数顺序为 (error, conversationId, ...),因此 message_delay.ts 中的 async (_, conversationId) 签名是正确的。仍建议将 completeBatch 的调用包在 try/catch 中以防未捕获异常。

Likely an incorrect or invalid review comment.


145-152: 忽略此建议:completeBatch 的无等待者分支只在 after-chat-error 或显式清理时调用,正常延时回调会在定时器内删除批次并返回 CONTINUE,不会丢弃积压消息。

Likely an incorrect or invalid review comment.

- Replace map() with flatMap() to properly handle undefined values
- Return empty arrays instead of undefined for non-text messages
- Add null coalescing operator for content attribute safety

Closes #553
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/renders/mixed-voice.ts (1)

83-96: 对齐 voice.ts:为 content 增加空值兜底,避免 marked.lexer 接收 undefined 崩溃

  • 目前 marked.lexer(message.attrs['content']) 在 content 缺失时可能抛错;voice.ts 已对返回分支使用 ?? '',建议这里也同时对 lexer 入参与返回分支兜底,保持一致性与健壮性。

应用以下补丁:

-                const tokens = renderTokens(
-                    marked.lexer(message.attrs['content'])
-                )
+                const tokens = renderTokens(
+                    marked.lexer(message.attrs['content'] ?? '')
+                )
@@
-                if (tokens.length === 0 || tokens[0].length === 0) {
-                    return message.attrs['content']
-                }
+                if (tokens.length === 0 || tokens[0].length === 0) {
+                    return message.attrs['content'] ?? ''
+                }
🧹 Nitpick comments (1)
packages/core/src/renders/mixed-voice.ts (1)

81-98: 抽公用拆分逻辑,避免日后漂移

  • mixed-voice 与 voice 两处 _splitMessage 基本相同,建议提取到共享 util,降低重复与差异化回归的风险。
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48f1645 and fd60ab7.

📒 Files selected for processing (2)
  • packages/core/src/renders/mixed-voice.ts (1 hunks)
  • packages/core/src/renders/voice.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#548
File: packages/core/src/llm-core/chat/app.ts:0-0
Timestamp: 2025-09-17T00:25:27.195Z
Learning: 在 ChatInterface 类中,响应式 watch 调用已经通过 ctx.effect() 包装来自动处理清理工作,避免内存泄漏。字段 _chain 和 _embeddings 的类型已更新为可空类型。
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: build
  • GitHub Check: CodeFactor
🔇 Additional comments (3)
packages/core/src/renders/mixed-voice.ts (1)

83-86: flatMap 丢弃非文本:方向正确

  • 使用 flatMap 并对非文本直接返回 [],能避免 undefined 被保留,管线更干净。
packages/core/src/renders/voice.ts (2)

50-53: flatMap 与非文本丢弃实现稳健

  • 非文本返回 [] 避免脏数据进入语音渲染;实现清晰可读。

48-65: 可选:合并重复的空串过滤以减少一次遍历

经核查,packages/core/src/renders/voice.ts 与 packages/core/src/renders/mixed-voice.ts 的 _splitMessage 在返回后都做了 .filter(Boolean),且两处调用方随后都做了 .trim().split('\n\n').filter(text => text.length > 0)。建议合并过滤以减少一次数组遍历——推荐保留调用方的精确过滤并移除 _splitMessage 末尾的 .filter(Boolean)。

注意:仓内针对 marked.lexer 的全量调用未在本次搜索中找到,请确认上游是否始终传入字符串;若不能保证,在 _splitMessage 内加入类型/空值保护。

…deprecated providers

- Completely rewrote DuckDuckGo Lite provider to use proper response parsing with regex extraction
- Added CallbackSearchResult and CallbackNextSearch interfaces for better type safety
- Implemented performSearch method with modern fetch API and proper error handling
- Added html-entities dependency for proper HTML entity decoding
- Removed deprecated free_google_api provider and related configuration
- Updated default search engine from free-google-api to bing-web
- Fixed response parsing to handle DuckDuckGo's JavaScript callback format correctly
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/search-service/src/index.ts (1)

251-256: Schema 仍包含 freeSearchBaseURL,接口/PR 目标不一致

接口 Config 已不含该字段,但 Schema 仍暴露该配置,易致用户侧出现“幽灵配置”。请移除该段并清理 i18n 文案。

应用此变更删除该配置块:

-    Schema.object({
-        freeSearchBaseURL: Schema.string().default(
-            'https://search.dingyi222666.top'
-        )
-    }),
🧹 Nitpick comments (9)
packages/search-service/src/providers/duckduckgo_lite.ts (9)

1-1: 移除未使用的导入

sleep 未使用,建议删除以消除噪音与潜在 lint 失败。

-import { Context, Schema, sleep } from 'koishi'
+import { Context, Schema } from 'koishi'

66-79: 硬编码 UA/客户端提示头易失效,建议可配置化或降级

Chrome 版本号与 sec-* 头硬编码,未来被风控拦截概率高。建议:

  • 允许通过 Config 覆盖 UA/headers;
  • 或提供最小集通用头,并在 429/阻断时降级重试。

84-85: 空查询处理建议返回空结果而非抛错

上层已做错误汇聚,空查询直接返回 [] 更平滑,避免无意义异常链。

-        if (!query) throw new Error('Query cannot be empty!')
+        if (!query?.trim()) return []

96-101: catch 日志与异常类型安全

error.message 在 TS 严格模式下类型不安全;同时建议记录完整错误对象。

-        } catch (error) {
-            logger.error(`DuckDuckGo search failed: ${error.message}`)
-            throw error
+        } catch (e: unknown) {
+            if (e instanceof Error) {
+                logger.error('DuckDuckGo search failed: %s', e.message)
+            } else {
+                logger.error('DuckDuckGo search failed: %o', e)
+            }
+            throw e
         }

120-154: 区域/语言参数硬编码,导致非英语环境结果劣化

dl/ct/bing_market 固定为 EN/US,和 locale/region 不一致。建议从 options 派生。

-            dl: 'en',
-            ct: 'US',
-            bing_market: options.marketRegion,
+            // derive language/country
+            // locale like: en-us / zh-CN
+            dl: (options.locale.split(/[-_]/)[0] || 'en').toLowerCase(),
+            ct: ((options.marketRegion.split('-')[1] || options.locale.split(/[-_]/)[1] || 'US')).toUpperCase(),
+            bing_market: options.marketRegion || `${(options.locale.split(/[-_]/)[0] || 'en').toLowerCase()}-${(options.locale.split(/[-_]/)[1] || 'US').toUpperCase()}`,

156-163: 为外部请求增加超时/可中断机制

避免请求悬挂影响链路。可用 AbortController + config 超时。

-        const response = await this._plugin.fetch(
-            `https://links.duckduckgo.com/d.js?${searchParams.toString()}`,
-            {
-                headers: this.COMMON_HEADERS
-            }
-        )
+        const controller = new AbortController()
+        const timeout = setTimeout(
+            () => controller.abort(),
+            this.config.puppeteerTimeout ?? 60000
+        )
+        const response = await this._plugin.fetch(
+            `https://links.duckduckgo.com/d.js?${searchParams.toString()}`,
+            { headers: this.COMMON_HEADERS, signal: controller.signal }
+        )
+        clearTimeout(timeout)

182-189: 解析健壮性:增加 JSON.parse 容错

建议对解析失败抛出更可诊断的错误。

-        const searchResults = JSON.parse(match[1].replace(/\t/g, '    ')) as (
+        let searchResults: (CallbackSearchResult | CallbackNextSearch)[]
+        try {
+            searchResults = JSON.parse(match[1].replace(/\t/g, '    ')) as (
                 | CallbackSearchResult
                 | CallbackNextSearch
-        )[]
+            )[]
+        } catch (e) {
+            throw new Error('Failed to parse DuckDuckGo JSON payload.')
+        }

208-219: 结果过滤与 URL 规范化

避免非 http(s) 协议/无效 URL 泄入下游;同时对 URL 也做解码。

-            if (result.u && result.t && result.a) {
-                results.push({
-                    title: decode(result.t),
-                    url: result.u,
-                    description: decode(result.a)
-                })
-            }
+            if (result.u && result.t && result.a) {
+                const url = decode(result.u)
+                if (!/^https?:\/\//i.test(url)) continue
+                try { new URL(url) } catch { continue }
+                results.push({
+                    title: decode(result.t),
+                    url,
+                    description: decode(result.a),
+                })
+            }

257-259: _getVQD 异常信息类型安全

同上,使用 unknown 守卫,避免 e.message 未定义。

-        } catch (e) {
-            const err = `Failed to get the VQD for query "${query}". Error: ${e.message}`
-            throw new Error(err)
+        } catch (e: unknown) {
+            const msg = e instanceof Error ? e.message : String(e)
+            throw new Error(`Failed to get the VQD for query "${query}". Error: ${msg}`)
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd60ab7 and bfc9643.

⛔ Files ignored due to path filters (1)
  • packages/search-service/package.json is excluded by !**/*.json
📒 Files selected for processing (4)
  • packages/search-service/src/index.ts (1 hunks)
  • packages/search-service/src/plugin.ts (0 hunks)
  • packages/search-service/src/providers/duckduckgo_lite.ts (3 hunks)
  • packages/search-service/src/providers/free_google_api.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/search-service/src/plugin.ts
  • packages/search-service/src/providers/free_google_api.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/search-service/src/providers/duckduckgo_lite.ts (3)
packages/search-service/src/types.ts (1)
  • SearchResult (1-6)
packages/search-service/src/index.ts (1)
  • logger (18-18)
packages/search-service/src/provide.ts (1)
  • search (57-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: lint
🔇 Additional comments (3)
packages/search-service/src/providers/duckduckgo_lite.ts (3)

6-7: 确认依赖声明

新增 html-entities 用于解码,请确认已在对应 package.json 中声明依赖(非 devDependency)。


262-267: 提供者 schema 声明:LGTM

与注册名一致,便于 UI 显示。


277-280: 按需注册 provider:LGTM

按配置开关加载,符合预期。

…essage handling

- Fix tool registration names: use underscores instead of hyphens for consistency
  - web-search -> web_search
  - web-browser -> web_browser
- Move intermediate tool message handling from plugin_chat_chain to chat app
- Fix tool comparison logic to use tool name instead of object reference
- Add webdriver detection avoidance to Bing web search provider
- Improve logging format for active tools debug output
- Remove unused imports and clean up tool call handling
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/search-service/src/chain/browsing_chain.ts (2)

198-204: _selectTool 未做空值判定,工具不存在时会抛出 TypeError

当 this.tools 中找不到对应 name 时,chatLunaTool 为 undefined,继续访问 chatLunaTool.tool 会直接抛异常。建议显式校验并抛出可诊断错误。

可参考:

private async _selectTool(name: string): Promise<StructuredTool> {
  const chatLunaTool = this.tools.value.find((t) => t.name === name)
  if (!chatLunaTool) {
    throw new Error(`Tool "${name}" not found in tools registry.`)
  }
  return chatLunaTool.tool.createTool({ embeddings: this.embeddings })
}

375-383: searchTool 返回解析未做防御性处理,JSON.parse 失败将使整个搜索流程中断

第三方搜索工具偶发返回非严格 JSON(或为空),这里缺少 try/catch,会直接抛错并终止 _search。

建议:

let parsedSearchResults: { title: string; description: string; url: string }[] = []
try {
  parsedSearchResults = JSON.parse(rawSearchResults) ?? []
} catch (e) {
  logger?.warn(`search result is not valid JSON: ${String(e)}`)
  return // 跳过本次条目,避免打断整个流程
}
🧹 Nitpick comments (9)
packages/search-service/src/providers/bing_web.ts (3)

1-1: 避免全局禁用 eslint 规则

不建议对整文件 /* eslint-disable no-proto */。后续将 __proto__ 黑魔法替换为更安全方式后,可去掉此禁用。

-/* eslint-disable no-proto */

51-51: 移除页面上下文中的调试日志

console.log(liElements) 仅打印到浏览器控制台,生产无用且可能暴露痕迹。

-            console.log(liElements)

2-2: 若删除固定休眠,则移除 sleep 依赖导入

避免无用依赖与误用。

-import { Context, Schema, sleep } from 'koishi'
+import { Context, Schema } from 'koishi'
packages/search-service/src/chain/browsing_chain.ts (5)

366-374: Abort 监听器未清理,存在累积监听与潜在内存泄漏风险

在 Promise.race 中为 signal 添加的 abort 监听没有移除;当未触发 abort 时,这些未清理的监听会累积。建议使用 once: true 或封装复用。

示例封装:

const waitForAbort = (signal: AbortSignal) =>
  new Promise<never>((_, reject) => {
    if (signal?.aborted) return reject(new ChatLunaError(ChatLunaErrorCode.ABORTED))
    const onAbort = () => reject(new ChatLunaError(ChatLunaErrorCode.ABORTED))
    signal?.addEventListener('abort', onAbort, { once: true })
  })

// 用法
await Promise.race([
  searchTool.invoke(question).then((t) => t as string),
  waitForAbort(signal),
])

Also applies to: 392-404, 418-441


510-512: closeBrowser 缺少防守性检查,非 PuppeteerBrowserTool 场景下可能抛错

如果实际获取到的并非 PuppeteerBrowserTool 实例,这里会因不存在 closeBrowser 而报错。建议守护式调用。

if (typeof (webBrowserTool as any)?.closeBrowser === 'function') {
  await (webBrowserTool as any).closeBrowser()
}

495-498: 英文提示语存在语法错误与用户体验问题

当前字符串含有 “the your's question”等错误,建议修正以免误导或降低专业感。

建议文案:
"I understand. I will respond to your question using the same language as your input. What's your question?"


445-457: 搜索结果字符串拼接依赖 for...in 的枚举顺序,不够稳定

为确保上下文可预测,建议按固定字段顺序格式化,或使用 JSON.stringify 并限制字段。

示例:

return `title: ${result.title}, url: ${result.url}, description: ${result.description}`

或:

return JSON.stringify({ title: result.title, url: result.url, description: result.description })

336-341: 添加对旧工具名(连字符)的兼容回退

仓库已注册 'web_search' 与 'web_browser'(packages/search-service/src/index.ts),PuppeteerBrowserTool 实现了 closeBrowser(packages/search-service/src/tools/puppeteerBrowserTool.ts);为防外部仍使用旧名 'web-search'/'web-browser' 导致运行时失败,建议在 select 时加入回退。

位置:packages/search-service/src/chain/browsing_chain.ts(约 336–341 行)

-        const searchTool = await this._selectTool('web_search')
+        const searchTool =
+            await this._selectTool('web_search')
+                .catch(() => this._selectTool('web-search'))

-        const webBrowserTool = (await this._selectTool(
-            'web_browser'
-        )) as PuppeteerBrowserTool
+        const webBrowserTool = (await this._selectTool('web_browser')
+            .catch(() => this._selectTool('web-browser'))) as PuppeteerBrowserTool
packages/core/src/llm-core/chain/plugin_chat_chain.ts (1)

196-196: 工具匹配逻辑改变可能影响正确性

从按对象引用匹配改为按名称匹配工具可能会产生问题。如果不同的工具实例具有相同的名称但不同的配置或行为,这种改变可能导致错误的工具被识别为"相同"。

建议使用更稳定的标识符进行匹配,如工具的 ID:

-                (tool) => tool.name === differenceTool[0].name
+                (tool) => tool.id === differenceTool[0].id

这样可以确保精确匹配,避免名称冲突的问题。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfc9643 and 40e1217.

📒 Files selected for processing (5)
  • packages/core/src/llm-core/chain/plugin_chat_chain.ts (3 hunks)
  • packages/core/src/llm-core/chat/app.ts (5 hunks)
  • packages/search-service/src/chain/browsing_chain.ts (1 hunks)
  • packages/search-service/src/index.ts (3 hunks)
  • packages/search-service/src/providers/bing_web.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/search-service/src/index.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#546
File: packages/core/src/llm-core/chat/app.ts:114-119
Timestamp: 2025-09-15T17:40:57.183Z
Learning: 在 ChatLuna 的 packages/core/src/llm-core/chat/app.ts 中,displayResponse 的构造只需要保留 content 和 additional_kwargs,tool_calls、tool_call_id、name、id 等元数据在代码库的其他地方处理,无需在 displayResponse 中保留这些字段。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
🧬 Code graph analysis (1)
packages/core/src/llm-core/chat/app.ts (2)
packages/core/src/llm-core/chain/base.ts (1)
  • ChatLunaLLMCallArg (37-48)
packages/core/src/llm-core/agent/types.ts (1)
  • AgentStep (250-253)
🔇 Additional comments (9)
packages/core/src/llm-core/chain/plugin_chat_chain.ts (3)

202-204: LGTM!逻辑修正合理

修复了工具激活时的处理逻辑。之前当 differenceTool[1] === true 时会跳过将工具添加到 oldActiveTools 的操作,现在正确地将激活的工具添加到活跃工具列表中。


250-252: 日志格式改进

改进了执行器重建时的日志格式,添加了空格并将工具列表连接为单个字符串,提高了日志的可读性。


1-1: 结论:移除的导入不会导致编译错误。

packages/core/src/llm-core/chain/plugin_chat_chain.ts 中未发现对 ToolMessage 或 AgentStep 的任何引用;AgentStep 的类型定义位于 packages/core/src/llm-core/agent/types.ts。

packages/core/src/llm-core/chat/app.ts (6)

27-27: 导入恢复正确

重新添加了 ToolMessageAgentStep 的导入,这与 AI 摘要中提到的在 plugin_chat_chain.ts 中移除这些导入是一致的。这些导入在当前文件中的新功能(并行中间步骤处理)中是必需的。

Also applies to: 32-32


53-66: 错误处理增强符合 PR 目标

handleChatError 方法的签名扩展很好地符合了 PR 的目标——改进错误处理和事件生命周期管理。新增的 chatluna/after-chat-error 事件提供了更丰富的错误上下文信息,有助于调试和监控。


113-113: 错误处理调用更新正确

正确地将新的参数传递给 handleChatError 方法,确保错误处理时能够获得完整的调用上下文。


121-128: 响应处理逻辑改进

引入 responseMessage 变量来存储原始响应,然后基于它创建 displayResponse。这种分离很好地支持了原始响应与显示响应的不同处理需求。

根据从长期记忆中获取的学习,displayResponse 的构造只需要保留 contentadditional_kwargs,这与当前的实现一致。


521-528: 新事件定义完善

chatluna/after-chat-error 事件的定义提供了丰富的错误上下文信息,包括错误对象、对话 ID、源消息、提示变量、聊天接口和链。这将有助于其他模块更好地处理和响应错误情况。


155-196: 并行中间步骤实现正确 — 类型验证通过

AgentStep/AgentAction 在 packages/core/src/llm-core/agent/types.ts 中定义,AgentAction 包含可选字段 toolCallId,且 output_parser 与 openai/index.ts 中对该字段有设置与检查;step.action.toolCallId 的使用与类型一致,批准变更。

…providers

- Enhanced error handling in ChatInterface with proper try-catch blocks
- Fixed proxy agent checks in request utils to handle null values correctly
- Removed unnecessary sleep delays in Bing web search provider
- Improved DuckDuckGo Lite provider with better HTML parsing and error handling
- Cleaned up tool logging format in plugin chat chain
- Add null safety to voice renderer content processing
- Wrap Bing web search in try-finally block for proper page cleanup
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
packages/search-service/src/providers/bing_web.ts (1)

73-76: 页面关闭放在 finally 已修复资源泄漏,LGTM;建议吞掉 close 异常

极端情况下 page.close() 也可能抛错(不应覆盖原始错误)。

-        } finally {
-            await page.close()
-        }
+        } finally {
+            try {
+                await page.close()
+            } catch {}
+        }
packages/search-service/src/providers/duckduckgo_lite.ts (2)

97-164: HTML 结果解析逻辑

解析 HTML 结果的正则表达式看起来合理,但需要注意几个问题:

  1. 正则表达式使用 result-linkresult-snippet 类名,这些可能会随着 DuckDuckGo 的 HTML 结构变化而失效
  2. 链接和片段的匹配逻辑假设它们有 1:1 的对应关系,但实际可能不是这样

建议增加更多的错误处理和备用解析策略,以应对 HTML 结构的变化。


216-223: VQD 提取方法未被使用

extractVqd 方法被保留但在当前实现中未被使用。如果这是为了未来的功能准备,建议添加注释说明其用途。

可以考虑将暂时不用的方法标记为私有或添加注释说明其预期用途。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40e1217 and b1d712c.

📒 Files selected for processing (6)
  • packages/core/src/llm-core/chain/plugin_chat_chain.ts (3 hunks)
  • packages/core/src/llm-core/chat/app.ts (6 hunks)
  • packages/core/src/renders/voice.ts (1 hunks)
  • packages/core/src/utils/request.ts (3 hunks)
  • packages/search-service/src/providers/bing_web.ts (2 hunks)
  • packages/search-service/src/providers/duckduckgo_lite.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/llm-core/chain/plugin_chat_chain.ts
  • packages/core/src/renders/voice.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#546
File: packages/core/src/llm-core/chat/app.ts:114-119
Timestamp: 2025-09-15T17:40:57.183Z
Learning: 在 ChatLuna 的 packages/core/src/llm-core/chat/app.ts 中,displayResponse 的构造只需要保留 content 和 additional_kwargs,tool_calls、tool_call_id、name、id 等元数据在代码库的其他地方处理,无需在 displayResponse 中保留这些字段。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
🧬 Code graph analysis (2)
packages/core/src/llm-core/chat/app.ts (2)
packages/core/src/llm-core/chain/base.ts (1)
  • ChatLunaLLMCallArg (37-48)
packages/core/src/llm-core/agent/types.ts (1)
  • AgentStep (250-253)
packages/search-service/src/providers/duckduckgo_lite.ts (3)
packages/search-service/src/index.ts (3)
  • Config (162-194)
  • Config (196-385)
  • logger (18-18)
packages/core/src/services/chat.ts (1)
  • ChatLunaPlugin (571-779)
packages/search-service/src/types.ts (1)
  • SearchResult (1-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: build
🔇 Additional comments (19)
packages/core/src/utils/request.ts (3)

19-26: 已批准此代码更改!

函数参数从全局代理地址迁移到每次调用的 proxyAddress 参数是正确的,这与 Undici 的最佳实践一致,能够为每个请求提供更精细的代理控制。检查 proxyAddress 是否为 null 而不是依赖全局状态的逻辑改进是合理的。


100-106: 已批准此代码更改!

条件检查的改进使用了明确的 null 检查 init?.['dispatcher'] == null 而不是布尔否定,这与 Undici 的最佳实践一致。proxyAddress !== 'null' 的字符串比较确保了字符串 'null' 不被误认为有效的代理地址。


126-135: 已批准此代码更改!

条件守卫改为对 options?.agent 进行 null 检查,与新模式保持一致。移除不必要的类型转换并在提供有效的 proxyAddress 且尚未存在 agent 时附加代理 agent 的逻辑是正确的。

packages/search-service/src/providers/bing_web.ts (2)

15-37: 反爬伪装过度且易失效;改为“最小、可配置、与网络层 UA 一致”的策略

当前直接改 navigator.proto、在页面上下文覆写 UA、注入大量 window.chrome 桩,指纹风险高且与请求头 UA 不一致。建议仅屏蔽 navigator.webdriver,并将 UA 在 Node 侧通过 page.setUserAgent 对齐;同时加配置开关(如 this.config.searchStealth)。

-        try {
-            // webdriver
-            await page.evaluateOnNewDocument(() => {
-                const newProto = navigator['__proto__']
-                delete newProto.webdriver
-                navigator['__proto__'] = newProto
-
-                window['chrome'] = {}
-                window['chrome'].app = {
-                    InstallState: 'hehe',
-                    RunningState: 'haha',
-                    getDetails: 'xixi',
-                    getIsInstalled: 'ohno'
-                }
-                window['chrome'].csi = function () {}
-                window['chrome'].loadTimes = function () {}
-                window['chrome'].runtime = function () {}
-
-                Object.defineProperty(navigator, 'userAgent', {
-                    get: () =>
-                        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.4044.113 Safari/537.36'
-                })
-            })
+        try {
+            // 可选“隐身”:最低限度且与网络层 UA 一致
+            if (this.config.searchStealth) {
+                await page.evaluateOnNewDocument(() => {
+                    try {
+                        Object.defineProperty(navigator, 'webdriver', { get: () => undefined })
+                    } catch {}
+                    Object.defineProperty(window, 'chrome', { value: { runtime: {} }, configurable: true })
+                })
+                const ua = await page.browser().userAgent()
+                await page.setUserAgent(ua.replace('Headless', ''))
+            }

39-46: 避免 networkidle0 潜在长时间挂起;改用 domcontentloaded + 明确超时,并等待结果选择器

Bing 页面存在长连/异步请求,networkidle0 过严易卡。建议设置合理超时并在 evaluate 前等待结果 DOM。

-            await page.goto(
-                `https://cn.bing.com/search?form=QBRE&q=${encodeURIComponent(
-                    query
-                )}`,
-                {
-                    waitUntil: 'networkidle0'
-                }
-            )
+            await page.goto(
+                `https://cn.bing.com/search?form=QBRE&q=${encodeURIComponent(query)}`,
+                { waitUntil: 'domcontentloaded', timeout: this.config.timeout ?? 10_000 }
+            )
+            await page.waitForSelector('#b_results .b_algo', { timeout: this.config.timeout ?? 10_000 })
packages/search-service/src/providers/duckduckgo_lite.ts (11)

1-6: 导入了新的 HTML 实体解码库

添加了 html-entities 库的 decode 函数导入,用于解码 HTML 实体。


8-16: 定义了 DuckDuckGo 搜索的默认基础 URL

合理地定义了不同搜索类型的基础 URL 常量,包括 lite 版本的 URL。


21-21: VQD 正则表达式已保留但未在当前实现中使用

代码保留了 VQD 提取的正则表达式,这在某些 DuckDuckGo 搜索场景中可能会用到,但当前的 lite 搜索实现并未实际使用它。


24-40: 新增了实例属性和通用请求头设置

添加了 useLitesearchType 等配置属性以及通用的请求头设置,为不同搜索模式做准备。User-Agent 设置合理,有助于避免被服务端识别为机器人。


42-58: 简化了主搜索方法的实现

主搜索方法现在专注于参数验证和错误处理,将具体搜索逻辑委托给 performLiteSearch 方法。这种设计提高了代码的可读性和可维护性。


63-92: 实现了 lite 搜索的具体逻辑

使用了正确的 DuckDuckGo lite 搜索 URL 格式,基于搜索结果可知 lite.duckduckgo.com/lite 是有效的搜索端点。dc 参数用于指定结果数量,q 参数传递查询字符串,这符合 DuckDuckGo 的 API 参数规范。


103-109: ** 正则表达式的跨行匹配问题**

当前的正则表达式可能无法正确处理跨行的内容,特别是在 HTML 结构复杂的情况下。


169-183: URL 清理和重定向处理逻辑

URL 清理逻辑正确处理了 DuckDuckGo 的重定向 URL 格式(/l/?uddg=),这是一个很好的实现细节。


188-197: HTML 实体解码和文本标准化

使用 html-entities 库进行 HTML 实体解码是正确的做法,能够正确处理搜索结果中的特殊字符。


202-211: URL 标准化逻辑

URL 标准化逻辑处理了协议缺失的情况,确保所有 URL 都有有效的 HTTP/HTTPS 前缀。


238-240: 提供者注册逻辑

提供者的注册逻辑简洁明了,正确检查配置并添加到搜索管理器中。

packages/core/src/llm-core/chat/app.ts (3)

27-27: 引入 ToolMessage 合理,和后续并行工具调用写入历史的实现一致。
保持与 LangChain 消息模型对齐,有助于工具调用轨迹回放。


137-143: 仅保留 content 与 additional_kwargs 的显示消息构造与既有约定一致。
与“displayResponse 仅需 content 和 additional_kwargs”的团队约定一致,避免在显示层混入工具元数据。


32-32: 确认:AgentStep 导入路径正确(无需更改)

packages/core/src/llm-core/agent/types.ts 定义了 AgentStep,且 packages/core/src/llm-core/agent/index.ts 包含 export * from './types',因此在 packages/core/src/llm-core/chat/app.ts 从 ../agent 导入是有效的。

…d config

- Fix handleChatError method signature to accept undefined wrapper
- Improve tool message handling logic in chat processing
- Add null safety to Bing web search element selectors
- Remove deprecated freeSearchBaseURL configuration
- Filter out empty search results in Bing provider
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/search-service/src/index.ts (1)

106-109: 空指针风险:keywordExtractModel 可能为 null

此处直接访问 .value 会在 summaryModel==='empty' 时抛异常;与上面的 browserModelRef 用法不一致。

-                const summaryModel = computed(
-                    () => keywordExtractModel.value ?? params.model
-                )
+                const summaryModel = computed(
+                    () => keywordExtractModel?.value ?? params.model
+                )
🧹 Nitpick comments (5)
packages/search-service/src/providers/bing_web.ts (2)

39-45: goto 缺少显式超时且域名/地区硬编码;接入超时并用 mkt 对齐地区

  • 接入 this.config.puppeteerTimeout
  • mkt=... 而非固定 cn.bing.com,复用 bingSearchLocation
-            await page.goto(
-                `https://cn.bing.com/search?form=QBRE&q=${encodeURIComponent(
-                    query
-                )}`,
-                {
-                    waitUntil: 'networkidle0'
-                }
-            )
+            await page.goto(
+                `https://www.bing.com/search?form=QBRE&mkt=${encodeURIComponent(this.config.bingSearchLocation ?? 'zh-CN')}&q=${encodeURIComponent(query)}`,
+                {
+                    waitUntil: 'networkidle0',
+                    timeout: this.config.puppeteerTimeout
+                }
+            )

48-68: 提取逻辑可再稳一点:等待结果选择器;标题/描述 trim;图片补充 data-src 回退

避免空列表和脏数据,提升解析稳健性。

-            const summaries = await page.evaluate(() => {
-                const liElements = Array.from(
-                    document.querySelectorAll('#b_results > .b_algo')
-                )
+            const summaries = await page.evaluate(() => {
+                const liElements = Array.from(
+                    document.querySelectorAll('#b_results .b_algo')
+                )
 
                 return liElements
                     .map((li) => {
-                        const abstractElement =
-                            li.querySelector('.b_caption > p')
+                        const abstractElement = li.querySelector('.b_caption > p')
                         const linkElement = li.querySelector('a')
                         const imageElement = li.querySelector('img')
 
-                        const href = linkElement?.getAttribute('href') ?? ''
-                        const title = linkElement?.textContent ?? ''
-                        const description = abstractElement?.textContent ?? ''
-                        const image = imageElement?.getAttribute('src') ?? ''
+                        const href = linkElement?.getAttribute('href') ?? ''
+                        const title = (linkElement?.textContent ?? '').trim()
+                        const description = (abstractElement?.textContent ?? '').trim()
+                        const image =
+                            imageElement?.getAttribute('src') ??
+                            imageElement?.getAttribute('data-src') ??
+                            ''
 
                         return { url: href, title, description, image }
                     })
                     .filter((summary) => summary.url && summary.title)
             })

在 goto 之后、evaluate 之前补充:

await page.waitForSelector('#b_results .b_algo', { timeout: this.config.puppeteerTimeout })
packages/search-service/src/index.ts (1)

196-233: 为“最小隐身”开关补充配置项(与 bing_web.ts 建议配套)

建议将隐身策略置于可配置项,默认关闭。

 export const Config: Schema<Config> = Schema.intersect([
   ChatLunaPlugin.Config,
   Schema.object({
@@
-        puppeteerTimeout: Schema.number().default(60000),
-        puppeteerIdleTimeout: Schema.number().default(300000),
+        puppeteerTimeout: Schema.number().default(60000),
+        puppeteerIdleTimeout: Schema.number().default(300000),
+        puppeteerStealth: Schema.boolean().default(false),

并在 export interface Config 中同步:

   puppeteerTimeout: number
   puppeteerIdleTimeout: number
+  puppeteerStealth?: boolean
packages/core/src/llm-core/chat/app.ts (2)

53-66: ****

根据过往评审记录,您已表示不希望修改此部分。解决方案是在使用前明确检查值或将值作为必需参数传递给函数,确保类型安全并防止运行时错误。当前实现在 arg.variables 可能为 undefined 时直接传递给需要 ChainValues 类型的事件,可能存在类型不匹配问题。


82-89: ****

此处存在变量未初始化就使用的潜在问题。虽然您之前表示此修改"不行",但TypeScript 的严格空值检查模式防止您在代码中引入不需要的行为,比如那个臭名昭著的 TypeError Cannot read properties of undefined。

createChatLunaLLMChainWrapper() 抛出异常时,wrapper 变量未被赋值就传递给 handleChatError,这会导致运行时问题。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1d712c and dde022d.

⛔ Files ignored due to path filters (2)
  • packages/search-service/src/locales/en-US.schema.yml is excluded by !**/*.yml
  • packages/search-service/src/locales/zh-CN.schema.yml is excluded by !**/*.yml
📒 Files selected for processing (3)
  • packages/core/src/llm-core/chat/app.ts (6 hunks)
  • packages/search-service/src/index.ts (3 hunks)
  • packages/search-service/src/providers/bing_web.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-17T00:25:27.195Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#548
File: packages/core/src/llm-core/chat/app.ts:0-0
Timestamp: 2025-09-17T00:25:27.195Z
Learning: 在 ChatInterface 类中,响应式 watch 调用通过 ctx.effect() 包装来自动处理清理工作,避免内存泄漏。字段 _chain 和 _embeddings 的类型已更新为可空类型 (| undefined),并添加 ctx.on('dispose') 处理器提供额外的清理保障。这种使用 Koishi effect 系统的方式比手动管理 stop 句柄更优雅。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
📚 Learning: 2025-09-17T00:25:27.195Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#548
File: packages/core/src/llm-core/chat/app.ts:0-0
Timestamp: 2025-09-17T00:25:27.195Z
Learning: 在 ChatInterface 类中,响应式 watch 调用已经通过 ctx.effect() 包装来自动处理清理工作,避免内存泄漏。字段 _chain 和 _embeddings 的类型已更新为可空类型。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#546
File: packages/core/src/llm-core/chat/app.ts:114-119
Timestamp: 2025-09-15T17:40:57.183Z
Learning: 在 ChatLuna 的 packages/core/src/llm-core/chat/app.ts 中,displayResponse 的构造只需要保留 content 和 additional_kwargs,tool_calls、tool_call_id、name、id 等元数据在代码库的其他地方处理,无需在 displayResponse 中保留这些字段。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
🧬 Code graph analysis (1)
packages/core/src/llm-core/chat/app.ts (2)
packages/core/src/llm-core/chain/base.ts (1)
  • ChatLunaLLMCallArg (37-48)
packages/core/src/llm-core/agent/types.ts (1)
  • AgentStep (250-253)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: lint
🔇 Additional comments (12)
packages/search-service/src/providers/bing_web.ts (2)

1-1: 移除全局 ESLint 禁令,消除对 proto 的依赖

文件级 /* eslint-disable no-proto */ 不再必要,应通过删除对 navigator.__proto__ 的操作来避免放宽规则。

-/* eslint-disable no-proto */

70-73: 页面生命周期处理已正确用 finally 保障关闭

保证不泄漏 page 资源,👍。

packages/search-service/src/index.ts (1)

214-214: 默认搜索引擎改为 bing-web:LGTM

与迁移方向一致;请同步用户文档与配置示例。

packages/core/src/llm-core/chat/app.ts (9)

27-27: ****

此导入将用于处理工具调用历史,新增的导入是正确的。


32-32: ****

新增 AgentStep 导入用于处理并行中间步骤,这是必要的。


105-116: 验证变量初始化的安全性

当前代码在 Line 108 进行了 arg.variables = arg.variables ?? {} 的空值合并初始化,这是一个良好的防护措施。Line 110-113 的 postHandler.variables 遍历也被正确防护。


130-135: ****

根据过往评审,您已明确表示"无需"修改 maxToken 的处理方式。当前实现会始终使用预设配置的 maxToken 值。


137-137: 引入响应消息变量用于后续处理

正确提取 responseMessage 变量,这为后续的显示响应和历史记录处理提供了清晰的分离。


139-143: 改进显示响应的构建

现在从 responseMessage 中提取 contentadditional_kwargs,这确保了显示响应保持必要的消息信息。


164-167: 合理的历史记录保存逻辑

根据 rawOnCensor 配置选择保存原始响应还是显示响应是合理的设计。


531-538: ****

根据过往评审记录,您已表示"无需"修改事件签名中 chain 参数的可选性。


169-204: 请手动确认 AIMessage 构造函数的 tool_calls 参数签名
当前 grep 仅在本仓库中定位到数据库保存时对 tool_calls 的引用,未能获知 LangChain 中 AIMessagetool_calls 结构的具体定义,请检查你本地或项目中安装的 LangChain 版本文档,确保所需字段(如可能的 type)已正确提供。

- Change tool name filters from hyphen to underscore format
- Fixes tool registration to match actual tool names
@dingyi222666 dingyi222666 merged commit 2ef8fbf into v1-dev Sep 21, 2025
5 checks passed
@dingyi222666 dingyi222666 deleted the fix/core-message-delay branch September 24, 2025 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants