Skip to content

Conversation

@springtiming
Copy link
Contributor

Summary

  • Enable Dify adapter to collect and upload images from chat messages, normalizing data URLs and passing upload_file_id in requests.
  • Keep additional_kwargs untouched (no auto-injected images) while preserving multimodal metadata via chatluna_multimodal.

Testing

  • Manual: send a message with an image → upload succeeds, Dify workflow receives the file, response returns normally.

…ort processing

- Update file filtering logic to skip TypeScript declaration files (.d.ts) when retrieving files from directories.
- Ensure only relevant TypeScript files are processed for dynamic imports, improving overall functionality and reducing potential errors.
- Introduce robust conversation ID resolution in DifyRequester, allowing fallback from parameters and variables.
- Add debug logging to track conversation ID flow in DifyRequester and ChatLunaChatChain.
- Ensure conversation ID is preserved in ChatLunaChatModel and included in requests for downstream processing.
- Improve logging in ChatInterface and ChatInterfaceWrapper to verify conversation ID integrity.
- Update ModelInfo capabilities in DifyClient to include ToolCall support.
- Add platform loading wait logic in _initModel
- Add model value availability check with timeout
- Improve debug logging for model initialization
- Enhance conversation ID handling in Dify adapter
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

本PR为Dify适配器添加多模态和文件上传支持,完善MIME类型处理、文件payload解析及转换;同时在对话链中引入conversationId上下文追踪,并实施代码格式化调整和工具函数优化。

Changes

凝聚 / 文件 变更摘要
Dify适配器多模态和文件上传功能
packages/adapter-dify/src/client.ts, packages/adapter-dify/src/index.ts, packages/adapter-dify/src/requester.ts, packages/adapter-dify/src/types.ts, packages/adapter-dify/src/utils.ts
新增ModelCapabilities导入,替换functionCall为capabilities;导出logger及enableFileUpload配置项;为请求者添加多模态文件收集、动态请求构建、文件上传管理(本地文件、远程URL、数据URL)、MIME类型推断等核心能力;新增InputFileObject、UploadCandidate、FilePayload类型定义;提供MIME处理、缓冲区转换、文件payload解析等工具函数。
对话链conversationId上下文追踪
packages/core/src/llm-core/chain/chat_chain.ts, packages/core/src/llm-core/chain/plugin_chat_chain.ts, packages/core/src/llm-core/platform/model.ts
在chat_chain和plugin_chat_chain的请求变量中添加built.conversationId元数据;在invocationParams中实现conversationId优先级推导(options.id > this._options.id > built.conversationId)。
平台和服务层逻辑更新
packages/core/src/services/chat.ts, packages/core/src/utils/string.ts
chat服务中添加fullModelName的平台解析;系统提示变量中添加platform字段。
代码格式化和工具更新
packages/core/src/llm-core/chain/base.ts, packages/core/src/llm-core/platform/api.ts, packages/extension-long-memory/src/utils/layer.ts, packages/service-vector-store/src/vectorstore/base/*, .eslintignore, scripts/processDynamicImport.ts, packages/core/src/middleware.ts
调整泛型类型参数和实现子句的换行格式;向ESLint忽略列表添加*.d.ts;在脚本中排除.d.ts文件的处理;移除多余空行。

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatChain
    participant DifyRequester
    participant FileResolver
    participant DifyAPI

    User->>ChatChain: 发送包含图片的消息
    ChatChain->>ChatChain: 提取conversationId,放入built
    ChatChain->>DifyRequester: 构建请求(含conversationId)
    
    DifyRequester->>DifyRequester: 提取多模态候选项
    
    loop 对每个文件候选项
        DifyRequester->>FileResolver: 解析文件payload<br/>(支持数据URL/本地文件/远程URL)
        alt 数据URL
            FileResolver-->>FileResolver: 解析为Buffer
        else 本地文件
            FileResolver->>FileResolver: 读取文件
        else 远程URL
            FileResolver->>DifyAPI: 拉取文件
            DifyAPI-->>FileResolver: 文件内容
        end
        FileResolver-->>DifyRequester: FilePayload
    end
    
    DifyRequester->>DifyRequester: 推断MIME类型,映射文件类型
    DifyRequester->>DifyRequester: 构建请求体<br/>(query, inputs, files, conversationId)
    DifyRequester->>DifyAPI: POST /chat-messages<br/>(含文件payload)
    DifyAPI-->>DifyRequester: 响应流
    DifyRequester-->>User: 返回结果
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

关键审查重点:

  • packages/adapter-dify/src/requester.ts — 新增的多模态文件上传逻辑、流式请求构建、错误处理路径,涉及多个辅助函数(extractUploadCandidates、uploadFileToDify、resolveDifyUser等),需要验证:
    • 文件上传流程的完整性和信号处理(AbortSignal)
    • 对话ID推导的优先级逻辑
    • 流式响应解析和错误传播
  • packages/adapter-dify/src/utils.ts — 新增工具函数库,包含MIME类型映射、缓冲区转换、远程文件拉取逻辑,需要验证:
    • 各类型来源(数据URL、本地文件、远程URL)的处理正确性
    • MIME类型推断的完整性和回退机制
    • 文件读取和网络请求的错误处理和资源清理
  • packages/core/src/llm-core/platform/model.ts — invocationParams中的conversationId推导链,需验证优先级顺序
  • packages/adapter-dify/src/client.ts — ModelCapabilities的应用方式和chat模型检查逻辑

Possibly related PRs

Poem

🐰 Dify来了新魔法,
文件上传飞得快,
ConversationId做记号,
多模态的梦想实现啦~ 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
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.
Title check ❓ Inconclusive 标题虽然提及了关键功能(Dify 图片上传和数据 URL 规范化),但末尾被截断,表述不完整,影响清晰度。 请完善标题,确保完整表述主要变更内容,例如:'Support Dify image uploads with data URL normalization'。
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed 描述与变更集高度相关,明确说明了功能目标、实现方式和测试方法。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @springtiming, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Dify adapter by introducing comprehensive support for image uploads. It standardizes the processing of various image sources, such as data URLs, local files, and remote URLs, ensuring they are correctly uploaded and associated with Dify API requests. Alongside this, the PR refines conversation ID management across the system and adds a new configuration option for file upload control, improving the overall robustness and flexibility of multimodal interactions.

Highlights

  • Dify Image Uploads: The Dify adapter now supports uploading images from chat messages, including normalization of data URLs, local files, and remote URLs, passing the upload_file_id in requests.
  • Multimodal Metadata Preservation: The system now preserves additional_kwargs without auto-injecting images and maintains multimodal metadata via chatluna_multimodal.
  • Configurable File Uploads: A new configuration option enableFileUpload has been added to the Dify adapter, allowing users to enable or disable file upload functionality.
  • Model Capabilities Declaration: Dify models now explicitly declare ModelCapabilities.ImageInput, enabling upstream middleware to correctly handle image messages.
  • Enhanced Conversation ID Resolution: Improved logic for resolving conversation IDs ensures consistent tracking across various components, including additional_kwargs and configurable options.
  • Dependency Update: The undici library has been added as a dependency to the Dify adapter to facilitate advanced HTTP requests for file handling.
  • Build Script Refinement: Build scripts have been updated to explicitly ignore TypeScript declaration files (.d.ts) during file processing.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly enhances the Dify adapter by introducing multimodal input capabilities, allowing users to upload images and other files to Dify models. Key changes include adding undici as a dependency, declaring ModelCapabilities.ImageInput for Dify models, and introducing a new enableFileUpload configuration option to control this feature. The requester.ts file was heavily refactored to handle file extraction from messages (data URLs, local files, remote URLs), upload them to Dify's /files/upload endpoint using FormData, and then include the upload_file_id in chat and workflow requests. Additionally, the pull request refines conversation ID propagation across various components (ChatInterface, ChatLunaChatModel, chat_chain, plugin_chat_chain, and rooms.ts) to ensure consistent session tracking. Several debug logs were added throughout the codebase to trace conversation ID and model initialization, which the reviewer suggested changing from logger.warn to logger.debug for cleaner production logs. The reviewer also recommended refactoring the tryReadLocalFile method to avoid synchronous file system calls and simplifying the uploadFileId extraction logic for better readability.

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 (12)
packages/extension-long-memory/src/utils/layer.ts (1)

23-25: 仅格式调整,语义安全,但请留意 Prettier 告警

这里把 implements MemoryRetrievalLayer{ 合在同一行,TypeScript 语义完全不变,功能上是安全的。不过 CodeFactor/Prettier 当前规则期望在 { 处换行(提示“Replace · with ”),如果仓库 CI 对 Prettier 严格校验,这里可能持续报黄。

建议二选一:

  • 要保持现有 Prettier 规则:把 { 挪回下一行以消除告警;或
  • 如果整个项目准备统一为这种单行写法:同步更新 Prettier 配置 / 忽略规则,避免后续 PR 反复被格式化工具改回去。
packages/core/src/llm-core/platform/api.ts (1)

92-95: 与上文一致:语义无变化,但与 Prettier 期望格式不一致

这里同样只是把 implements BaseRequester{ 合并到一行,逻辑和类型签名都不受影响,功能层面是 OK 的。

不过 CodeFactor/Prettier 针对这一行也给出了“Replace · with ”的提示,说明当前格式与项目的自动格式化规则不一致。为避免之后被自动格式化工具改回或 CI 报错,建议:

  • 若遵从现有 Prettier 规则:把 { 换行;
  • 若要统一采用单行写法:考虑调整 Prettier 配置或在对应规则上做忽略。
packages/service-vector-store/src/vectorstore/base/milvus.ts (1)

215-218: MilvusVectorStoreInput 接口改为单行 extends,仅属格式调整

这里只是将 extends ChatLunaSaveableVectorStoreInput<Milvus> 挪到同一行,逻辑与类型含义完全不变。不过 CodeFactor/Prettier 已提示建议换行,如果你希望保持全仓统一风格,可以按下面方式调整为多行:

-export interface MilvusVectorStoreInput extends ChatLunaSaveableVectorStoreInput<Milvus> {
+export interface MilvusVectorStoreInput
+    extends ChatLunaSaveableVectorStoreInput<Milvus> {
packages/service-vector-store/src/vectorstore/base/redis.ts (1)

86-88: RedisVectorStoreWrapperInput 接口单行 extends,同样仅为格式变化

这里与 Milvus 文件类似,只是把 extends ChatLunaSaveableVectorStoreInput<RedisVectorStore> 放到同一行,不影响任何行为。若想消除 CodeFactor/Prettier 的警告,可恢复为多行书写:

-export interface RedisVectorStoreWrapperInput extends ChatLunaSaveableVectorStoreInput<RedisVectorStore> {
+export interface RedisVectorStoreWrapperInput
+    extends ChatLunaSaveableVectorStoreInput<RedisVectorStore> {
packages/core/src/services/chat.ts (1)

835-843: 调试日志信息量大且使用 warn 级别,建议改为结构化 debug 日志并拆行

这段新日志对排查 conversationId 问题很有帮助,但当前写法存在两个小问题:

  1. 使用 warn 级别且始终输出,会在生产环境产生较多噪音;
  2. 单行模板字符串过长(~289 字符),已经触发 max-len 提示,也不利于阅读。

可以考虑改成结构化、可控级别的写法,例如:

-        this._service.ctx
-            .logger('chatluna')
-            .warn(
-                `[DEBUG] Chat service - roomName: ${room.roomName}, roomId: ${room.roomId}, conversationId: ${conversationId}, type: ${typeof conversationId}, isNull: ${conversationId == null}, isUndefined: ${conversationId === undefined}, room object keys: ${Object.keys(room).join(',')}`
-            )
+        this._service.ctx
+            .logger('chatluna')
+            .debug('[DEBUG] Chat service - room info', {
+                roomName: room.roomName,
+                roomId: room.roomId,
+                conversationId,
+                type: typeof conversationId,
+                isNull: conversationId == null,
+                isUndefined: conversationId === undefined,
+                roomKeys: Object.keys(room)
+            })

既能保留调试信息,又能降低日志级别、消除 max-len 报警,并让后续检索日志更方便。

scripts/processDynamicImport.ts (1)

171-197: 在动态导入扫描中排除 .d.ts 文件,与 ESLint 与预期用法保持一致

getFilesFromDirgetFilesRecursively 新增了:

stat.isFile() && file.endsWith('.ts') && !file.endsWith('.d.ts')

从而只对普通 .ts 文件生成动态 import,排除了 .d.ts,这和 .eslintignore 中忽略声明文件的策略相符,避免错误地把类型声明当作运行时模块处理,逻辑上是正确的。

CodeFactor/Prettier 提示这一长条件可拆成多行以提高可读性,可以按需稍作格式化,例如:

-        if (stat.isFile() && file.endsWith('.ts') && !file.endsWith('.d.ts')) {
+        if (
+            stat.isFile() &&
+            file.endsWith('.ts') &&
+            !file.endsWith('.d.ts')
+        ) {

保持风格统一即可。

Also applies to: 199-234

packages/core/src/llm-core/chain/base.ts (1)

78-81: 格式化问题:代码缩进不符合 Prettier 规范

静态分析工具检测到泛型类型参数的缩进格式不正确。建议运行 prettier --write 或相应的格式化命令来修复这些问题。

 export abstract class BaseChain<
-    RunInput extends ChainValues = ChainValues,
-    RunOutput extends ChainValues = ChainValues
->
+        RunInput extends ChainValues = ChainValues,
+        RunOutput extends ChainValues = ChainValues
+    >
     extends BaseLangChain<RunInput, RunOutput>

同样需要修复 ChatLunaLLMChain 类(第 273-276 行)的泛型参数缩进。

Also applies to: 273-276

packages/service-vector-store/src/vectorstore/base/lunavdb.ts (1)

331-333: 格式化问题:接口声明需要换行

静态分析工具指示此行需要在 extends 后插入换行符。建议恢复多行格式或运行格式化工具。

-export interface LunaVectorStoreInput extends ChatLunaSaveableVectorStoreInput<LunaDBVectorStore> {
+export interface LunaVectorStoreInput
+    extends ChatLunaSaveableVectorStoreInput<LunaDBVectorStore> {
     directory: string
 }
packages/service-vector-store/src/vectorstore/base/faiss.ts (1)

76-78: 格式化问题:接口声明需要换行

lunavdb.ts 相同的问题,静态分析工具指示需要在 extends 后插入换行符。

-export interface FaissVectorStoreInput extends ChatLunaSaveableVectorStoreInput<FaissStore> {
+export interface FaissVectorStoreInput
+    extends ChatLunaSaveableVectorStoreInput<FaissStore> {
     directory: string
 }
packages/core/src/llm-core/chat/app.ts (1)

498-504: 轮询等待模型加载的实现可接受,但建议添加注释说明。

当前使用 50ms 间隔轮询等待平台客户端可用,最长等待 5 秒。逻辑正确,但建议添加注释说明为何需要此等待机制。

         // Wait briefly for platform client to become available
+        // This handles race conditions where the platform client may not be immediately ready
+        // after awaitLoadPlatform returns
         if (!llmModel.value) {
             const start = Date.now()
             while (!llmModel.value && Date.now() - start < 5000) {
                 await sleep(50)
             }
         }
packages/adapter-dify/src/requester.ts (2)

940-944: 修复静态分析工具报告的格式问题。

静态分析工具报告了括号格式问题。

             const uploadFileId =
                 typeof result === 'object' && result != null
-                    ? ((result as { data?: { id?: string }; id?: string }).data
-                          ?.id ?? (result as { id?: string }).id)
+                    ? (result as { data?: { id?: string }; id?: string }).data
+                          ?.id ?? (result as { id?: string }).id
                     : undefined

480-484: 修复静态分析工具报告的格式问题。

             const displayName =
                 candidate.fileName ??
                 (typeof candidate.source === 'string'
                     ? candidate.source
-                    : (candidate.type ?? 'file'))
+                    : candidate.type ?? 'file')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a965fa and e5d31c8.

⛔ Files ignored due to path filters (4)
  • packages/adapter-dify/package.json is excluded by !**/*.json
  • packages/adapter-dify/src/locales/en-US.schema.yml is excluded by !**/*.yml
  • packages/adapter-dify/src/locales/zh-CN.schema.yml is excluded by !**/*.yml
  • tsconfig.json is excluded by !**/*.json
📒 Files selected for processing (20)
  • .eslintignore (1 hunks)
  • packages/adapter-dify/src/client.ts (2 hunks)
  • packages/adapter-dify/src/index.ts (2 hunks)
  • packages/adapter-dify/src/requester.ts (7 hunks)
  • packages/adapter-dify/src/types.ts (1 hunks)
  • packages/core/src/chains/rooms.ts (1 hunks)
  • packages/core/src/llm-core/chain/base.ts (2 hunks)
  • packages/core/src/llm-core/chain/chat_chain.ts (2 hunks)
  • packages/core/src/llm-core/chain/plugin_chat_chain.ts (1 hunks)
  • packages/core/src/llm-core/chat/app.ts (4 hunks)
  • packages/core/src/llm-core/platform/api.ts (1 hunks)
  • packages/core/src/llm-core/platform/model.ts (2 hunks)
  • packages/core/src/middleware.ts (1 hunks)
  • packages/core/src/services/chat.ts (1 hunks)
  • packages/extension-long-memory/src/utils/layer.ts (1 hunks)
  • packages/service-vector-store/src/vectorstore/base/faiss.ts (1 hunks)
  • packages/service-vector-store/src/vectorstore/base/lunavdb.ts (1 hunks)
  • packages/service-vector-store/src/vectorstore/base/milvus.ts (1 hunks)
  • packages/service-vector-store/src/vectorstore/base/redis.ts (1 hunks)
  • scripts/processDynamicImport.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-09-15T12:28:21.006Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 543
File: packages/vector-store-service/src/vectorstore/faiss.ts:47-47
Timestamp: 2025-09-15T12:28:21.006Z
Learning: 在 packages/core/src/llm-core/vectorstores/base.ts 中,ChatLunaSaveableVectorStore 基类的 addDocuments 方法已经实现了向量存储和数据库文档存储的同步逻辑:先调用底层存储的 addDocuments,再调用 this._docstore.add() 进行备份,所有继承类(如 FaissVectorStore、MilvusVectorStore 等)都会自动获得这个同步功能。

Applied to files:

  • packages/service-vector-store/src/vectorstore/base/redis.ts
  • packages/service-vector-store/src/vectorstore/base/faiss.ts
  • packages/service-vector-store/src/vectorstore/base/lunavdb.ts
  • packages/service-vector-store/src/vectorstore/base/milvus.ts
📚 Learning: 2025-09-15T09:29:19.385Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 543
File: packages/core/src/llm-core/vectorstores/base.ts:0-0
Timestamp: 2025-09-15T09:29:19.385Z
Learning: 在 packages/core/src/llm-core/vectorstores/base.ts 中,ChatLunaSaveableVectorStore 的 free() 方法设计为等待子类重写实现,不在基类中添加额外的资源清理操作。资源清理由具体实现类负责处理。

Applied to files:

  • packages/service-vector-store/src/vectorstore/base/redis.ts
  • packages/service-vector-store/src/vectorstore/base/faiss.ts
  • packages/service-vector-store/src/vectorstore/base/lunavdb.ts
  • packages/service-vector-store/src/vectorstore/base/milvus.ts
  • packages/extension-long-memory/src/utils/layer.ts
📚 Learning: 2025-09-15T12:28:21.006Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 543
File: packages/vector-store-service/src/vectorstore/faiss.ts:47-47
Timestamp: 2025-09-15T12:28:21.006Z
Learning: FaissVectorStore 继承自 ChatLunaSaveableVectorStore 基类,文档同步到 DataBaseDocstore 的逻辑已经在基类的 addDocuments 方法中实现,无需在派生类中重复实现。

Applied to files:

  • packages/service-vector-store/src/vectorstore/base/faiss.ts
  • packages/service-vector-store/src/vectorstore/base/lunavdb.ts
  • packages/service-vector-store/src/vectorstore/base/milvus.ts
📚 Learning: 2025-09-15T12:02:03.617Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 543
File: packages/core/src/llm-core/vectorstores/base.ts:82-113
Timestamp: 2025-09-15T12:02:03.617Z
Learning: 在 packages/core/src/llm-core/vectorstores/base.ts 中,ChatLunaSaveableVectorStore 的 delete() 方法设计为只处理 docstore 清理,具体的向量索引删除由各个向量数据库的具体实现类(如 FaissVectorStore、MilvusVectorStore 等)重写实现,体现了基类处理通用逻辑、派生类处理特定逻辑的设计模式。

Applied to files:

  • packages/service-vector-store/src/vectorstore/base/faiss.ts
  • packages/service-vector-store/src/vectorstore/base/lunavdb.ts
  • packages/service-vector-store/src/vectorstore/base/milvus.ts
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 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
📚 Learning: 2025-09-17T00:25:27.195Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 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
Repo: ChatLunaLab/chatluna PR: 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/chain/plugin_chat_chain.ts
🧬 Code graph analysis (4)
packages/service-vector-store/src/vectorstore/base/faiss.ts (1)
packages/core/src/llm-core/vectorstores/base.ts (1)
  • ChatLunaSaveableVectorStoreInput (169-175)
packages/service-vector-store/src/vectorstore/base/lunavdb.ts (1)
packages/core/src/llm-core/vectorstores/base.ts (1)
  • ChatLunaSaveableVectorStoreInput (169-175)
packages/core/src/llm-core/chat/app.ts (1)
packages/core/src/llm-core/platform/model.ts (1)
  • ChatLunaChatModel (105-798)
packages/service-vector-store/src/vectorstore/base/milvus.ts (1)
packages/core/src/llm-core/vectorstores/base.ts (1)
  • ChatLunaSaveableVectorStoreInput (169-175)
🪛 GitHub Check: CodeFactor
packages/core/src/chains/rooms.ts

[notice] 424-424: packages/core/src/chains/rooms.ts#L424
This line has a length of 250. Maximum allowed is 160. (max-len)

packages/service-vector-store/src/vectorstore/base/redis.ts

[warning] 86-86: packages/service-vector-store/src/vectorstore/base/redis.ts#L86
Insert ⏎··· (prettier/prettier)

packages/service-vector-store/src/vectorstore/base/faiss.ts

[warning] 76-76: packages/service-vector-store/src/vectorstore/base/faiss.ts#L76
Insert ⏎··· (prettier/prettier)

packages/service-vector-store/src/vectorstore/base/lunavdb.ts

[warning] 331-331: packages/service-vector-store/src/vectorstore/base/lunavdb.ts#L331
Insert ⏎··· (prettier/prettier)

packages/core/src/llm-core/chain/base.ts

[warning] 81-81: packages/core/src/llm-core/chain/base.ts#L81
Insert ···· (prettier/prettier)


[warning] 80-80: packages/core/src/llm-core/chain/base.ts#L80
Replace ···· with ········ (prettier/prettier)


[warning] 79-79: packages/core/src/llm-core/chain/base.ts#L79
Insert ···· (prettier/prettier)


[warning] 274-274: packages/core/src/llm-core/chain/base.ts#L274
Insert ···· (prettier/prettier)


[warning] 276-276: packages/core/src/llm-core/chain/base.ts#L276
Insert ···· (prettier/prettier)


[warning] 275-275: packages/core/src/llm-core/chain/base.ts#L275
Replace ···· with ········ (prettier/prettier)

scripts/processDynamicImport.ts

[warning] 213-213: scripts/processDynamicImport.ts#L213
Replace stat.isFile()·&&·file.endsWith('.ts')·&&·!file.endsWith('.d.ts') with ⏎················stat.isFile()·&&⏎················file.endsWith('.ts')·&&⏎················!file.endsWith('.d.ts')⏎············ (prettier/prettier)

packages/adapter-dify/src/requester.ts

[warning] 943-943: packages/adapter-dify/src/requester.ts#L943
Delete ) (prettier/prettier)


[warning] 484-484: packages/adapter-dify/src/requester.ts#L484
Replace (candidate.type·??·'file') with candidate.type·??·'file' (prettier/prettier)


[warning] 942-942: packages/adapter-dify/src/requester.ts#L942
Delete ( (prettier/prettier)

packages/service-vector-store/src/vectorstore/base/milvus.ts

[warning] 215-215: packages/service-vector-store/src/vectorstore/base/milvus.ts#L215
Insert ⏎··· (prettier/prettier)

packages/core/src/services/chat.ts

[notice] 841-841: packages/core/src/services/chat.ts#L841
This line has a length of 289. Maximum allowed is 160. (max-len)

packages/extension-long-memory/src/utils/layer.ts

[warning] 25-25: packages/extension-long-memory/src/utils/layer.ts#L25
Replace · with (prettier/prettier)

packages/core/src/llm-core/platform/api.ts

[warning] 95-95: packages/core/src/llm-core/platform/api.ts#L95
Replace · with (prettier/prettier)

🔇 Additional comments (15)
packages/core/src/middleware.ts (1)

61-64: 在 import 块后增加空行仅提升可读性

新增的空行把 import 区和 middleware 函数主体分隔开,可读性更好,对运行逻辑没有任何影响,看起来是合理的格式调整。

.eslintignore (1)

1-6: 忽略所有 .d.ts 声明文件与脚本中的行为保持一致

新增的 **/*.d.ts 让 ESLint 跳过声明文件,和 scripts/processDynamicImport.ts 里对 .d.ts 的排除策略一致,有利于避免对生成或第三方声明文件报风格错误。如果仓库里将来有需要强约束的手写 .d.ts,再单独调整即可。

packages/adapter-dify/src/client.ts (1)

3-7: 为 Dify workflow 显式声明 ImageInput 能力与本 PR 目标一致

refreshModels 中统一设置:

capabilities: [ModelCapabilities.ImageInput]

并加上注释,能让上游中间件正确识别这些 Dify 模型支持图像输入,从而放行图片消息,和本次“支持图片上传”的目标吻合。需要注意的是,这样会默认所有 additionalModels 都支持图片;如果后续出现不支持图片的 workflow,可能需要在配置层面区分能力。

Also applies to: 42-53

packages/adapter-dify/src/index.ts (1)

40-53: 新增 enableFileUpload 配置与 Schema 定义保持一致,默认行为合理

  • Config 接口增加了可选字段 enableFileUpload?: boolean
  • Schema 中对应增加 enableFileUpload: Schema.boolean().default(true)

接口和 Schema 是一致的,并且默认值为 true 能保持现有行为不变,只在需要时显式关闭上传功能即可。后续只要在 DifyRequester 等调用处使用该开关来控制是否执行文件上传逻辑,就可以完整地控制新特性。

Also applies to: 55-61

packages/core/src/llm-core/platform/model.ts (1)

169-186: LGTM! 对话 ID 解析逻辑合理

该实现提供了良好的向后兼容性,支持从多个来源获取对话 ID。注释清晰地说明了设计意图。

可选优化:可以考虑将这些 configurable 字段名抽取为常量,便于后续维护:

const CONVERSATION_ID_KEYS = [
    'conversationId',
    'conversation_id', 
    'chatluna_conversation_id'
] as const
packages/core/src/llm-core/chain/plugin_chat_chain.ts (1)

176-187: LGTM!

内置元数据的添加逻辑清晰,将 conversationId 正确地传播到 requests.variables.builtrequests.configurable 中,便于下游组件恢复会话上下文。

packages/core/src/llm-core/chain/chat_chain.ts (1)

111-119: LGTM!

内置元数据的传播逻辑与 plugin_chat_chain.ts 保持一致,确保 conversationId 在整个请求链路中可用。

packages/adapter-dify/src/types.ts (1)

19-24: LGTM!

InputFileObject 接口定义清晰,类型约束合理,与 Dify API 的文件上传规范保持一致。

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

116-140: LGTM!

builtVariables 的构建逻辑完整,正确地从多个来源收集会话上下文信息(conversationId、botName、userId、platform),并确保不覆盖已有值。

packages/adapter-dify/src/requester.ts (6)

37-48: LGTM!

UploadCandidateFilePayload 类型定义清晰,为文件上传流程提供了良好的类型安全。


161-179: LGTM!

请求体构建逻辑清晰,正确处理了可选的 files 字段,仅在有文件时才添加。


659-711: 远程文件获取存在潜在的 SSRF 风险。

当前实现会获取用户消息中指定的任意 HTTP/HTTPS URL。虽然这是 multimodal 功能的必要行为,但建议:

  1. 考虑添加 URL 白名单或黑名单机制
  2. 禁止获取内网地址(如 127.0.0.1localhost10.x.x.x192.168.x.x 等)

请确认是否需要添加 SSRF 防护措施,或者上游是否已有相关安全控制。


991-1003: LGTM!

_post 方法中清理 undefined 字段是个好做法,确保发送给 Dify API 的 JSON 是干净的。


371-407: LGTM!

prepareFiles 方法作为文件上传的协调器,逻辑清晰:

  1. 提取上传候选项
  2. 安全序列化 multimodal 信息
  3. 根据配置决定是否执行上传

859-900: LGTM!

safeSerializeMultimodal 方法的防御性编程做得很好:

  1. 限制文件数量(最多 5 个)
  2. 截断过长的字符串来源(64 字符)
  3. 限制最终结果长度(256 字符)
  4. 捕获序列化异常

…debug logs

- Extract file resolution, MIME type mapping, and multimodal serialization logic into separate utils.ts module
- Remove undici dependency from package.json (moved to shared dependencies)
- Clean up all debug/trace logging statements across core and adapter-dify
- Add platform variable to system prompt variables in core/utils/string.ts
- Pass all custom variables from chain to Dify with chatluna_ prefix
- Simplify conversation ID handling by removing redundant checks and temporary workarounds
- Improve code organization and maintainability by separating concerns
@dingyi222666
Copy link
Member

我优化了一下代码,起床了你 pull --rebase 一下,测试能不能用。

另外检查一下是否还有多余的打印日志,要全去掉

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/utils/string.ts (1)

191-195: getTimeInUTCsubstring 参数顺序看起来有误

当前实现:

export const getTimeInUTC = (offset: number): string => {
    const date = new Date()
    date.setMinutes(date.getMinutes() + offset * 60)
    return date.toISOString().substring(11, 8)
}

String.prototype.substring(start, end) 如果 start > end,会交换两个参数,因此这里实际效果等价于 substring(8, 11),返回的并不是标准的 HH:mm:ss 片段,和函数名/用途不一致。

建议修正为截取 HH:mm:ss

-export const getTimeInUTC = (offset: number): string => {
-    const date = new Date()
-    date.setMinutes(date.getMinutes() + offset * 60)
-    return date.toISOString().substring(11, 8)
-}
+export const getTimeInUTC = (offset: number): string => {
+    const date = new Date()
+    date.setMinutes(date.getMinutes() + offset * 60)
+    // 形如 2025-12-04T12:34:56.789Z,截取 11~19 即 HH:mm:ss
+    return date.toISOString().substring(11, 19)
+}

不直接影响本 PR 目标,但属于一个顺手可以修掉的小 bug。

🧹 Nitpick comments (3)
packages/core/src/llm-core/chain/chat_chain.ts (1)

107-117: variables.built / configurable 中注入 conversationId 的方式可以再稍微收敛一下

整体思路是对的:

  • built.conversationId 方便下游模板 / 适配器读取会话 ID;
  • configurable.conversationIdsession 一起传入,也和其他链的改法保持一致,看起来是刻意对齐的设计。

有一个小点可以考虑优化:

  • 现在直接赋值:

    requests['variables']['built'] = { conversationId }

    如果上游在 variables 里本来就带了一个 built 对象(例如别的内置字段),这里会把它整体覆盖掉。

如果需要兼容旧调用方、避免无意覆盖,可以用 merge 的方式构造:

-requests['variables']['built'] = {
-    conversationId
-}
+requests['variables']['built'] = {
+    ...(variables as Record<string, unknown>)?.['built'],
+    conversationId
+}

(当然,如果可以确认目前没有人在 variables.built 里塞其它东西,那保持现状也没问题。)

packages/adapter-dify/src/requester.ts (2)

62-121: 对话 ID 解析逻辑完备但较复杂。

实现了多路径回退机制来解析对话 ID,包括从 params.idparams.variables、以及消息的 additional_kwargs 中提取。这种防御性编程确保了兼容性。

如果希望提升可维护性,可以考虑将解析逻辑封装为独立的辅助函数,并添加清晰的注释说明各个回退路径的用途。


495-589: 文件上传到 Dify 的实现完整且错误处理充分。

FormData 构建正确,响应处理涵盖多种格式。第 571-575 行的 uploadFileId 提取逻辑虽然较复杂,但正确处理了多种响应格式。

如果希望提升可读性,可将 uploadFileId 提取逻辑封装为单独的辅助函数:

private extractUploadFileId(result: unknown): string | undefined {
    if (typeof result !== 'object' || result === null) {
        return undefined
    }
    const resultObj = result as { data?: { id?: string }; id?: string }
    return resultObj.data?.id ?? resultObj.id
}

然后在第 571 行调用:const uploadFileId = this.extractUploadFileId(result)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5d31c8 and 67d91b0.

📒 Files selected for processing (9)
  • packages/adapter-dify/src/client.ts (2 hunks)
  • packages/adapter-dify/src/index.ts (3 hunks)
  • packages/adapter-dify/src/requester.ts (7 hunks)
  • packages/adapter-dify/src/types.ts (1 hunks)
  • packages/adapter-dify/src/utils.ts (1 hunks)
  • packages/core/src/llm-core/chain/chat_chain.ts (1 hunks)
  • packages/core/src/llm-core/chain/plugin_chat_chain.ts (1 hunks)
  • packages/core/src/services/chat.ts (1 hunks)
  • packages/core/src/utils/string.ts (2 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/services/chat.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-17T16:30:50.592Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 601
File: packages/adapter-dify/src/index.ts:85-85
Timestamp: 2025-10-17T16:30:50.592Z
Learning: 在 packages/adapter-dify/src/index.ts 中,inject 数组包含 'database' 依赖是隐式使用,即使代码中没有显式的 ctx.database 引用也不应该移除。

Applied to files:

  • packages/adapter-dify/src/client.ts
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 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/chain/chat_chain.ts
🧬 Code graph analysis (2)
packages/adapter-dify/src/requester.ts (9)
packages/core/src/utils/error.ts (1)
  • ChatLunaError (13-41)
packages/core/src/llm-core/platform/api.ts (1)
  • ModelRequestParams (30-78)
packages/core/src/services/chat.ts (1)
  • query (951-966)
packages/core/src/utils/string.ts (2)
  • getMessageContent (151-167)
  • isMessageContentImageUrl (40-44)
packages/adapter-dify/src/types.ts (3)
  • InputFileObject (20-24)
  • UploadCandidate (26-31)
  • FilePayload (33-37)
packages/adapter-dify/src/utils.ts (3)
  • safeSerializeMultimodal (152-190)
  • resolveFilePayload (192-239)
  • mapMimeToFileType (120-150)
packages/core/src/utils/request.ts (1)
  • FormData (17-17)
packages/adapter-dify/src/index.ts (1)
  • logger (7-7)
packages/adapter-spark/src/requester.ts (1)
  • logger (247-249)
packages/adapter-dify/src/utils.ts (5)
packages/adapter-dify/src/types.ts (3)
  • InputFileObject (20-24)
  • UploadCandidate (26-31)
  • FilePayload (33-37)
packages/adapter-dify/src/index.ts (1)
  • logger (7-7)
packages/adapter-dify/src/requester.ts (1)
  • logger (692-694)
packages/core/src/services/chat.ts (1)
  • ChatLunaPlugin (594-800)
packages/extension-tools/src/plugins/fs.ts (1)
  • readFile (166-175)
🪛 GitHub Check: CodeFactor
packages/adapter-dify/src/requester.ts

[warning] 484-484: packages/adapter-dify/src/requester.ts#L484
Replace (candidate.type·??·'file') with candidate.type·??·'file' (prettier/prettier)

🔇 Additional comments (24)
packages/core/src/utils/string.ts (1)

392-416: 为系统提示变量增加 platform 字段的设计是合理的

这里在两层都补充了平台信息:

  • 顶层增加 platform: session.platform,方便模板里直接用 {{ platform }}
  • built 里也加了 platform: session.platform,和 presetconversationId 一起作为“内建”元数据。

这两层用法场景不同(模板 vs 程序化消费),同时存在问题不大,而且对现有调用都是向后兼容的。改动本身没有明显风险,看起来和本 PR 想要在多平台/多适配器间透传上下文的目标是一致的。

packages/adapter-dify/src/client.ts (2)

4-4: 导入正确,支持模型能力声明。

此导入为模型添加图像输入能力声明提供了必要的类型。


50-51: 正确声明了图像输入能力。

为 Dify 模型添加了 ImageInput 能力声明,与 PR 的多模态上传功能相匹配。

packages/adapter-dify/src/index.ts (2)

2-2: 日志器设置正确。

日志器的导出和初始化遵循标准模式,允许其他模块(如 requester.tsutils.ts)共享使用。

Also applies to: 5-5, 7-9


47-48: 文件上传配置项添加合理。

enableFileUpload 配置项默认为 true,为用户提供了关闭文件上传功能的选项,设计合理。

Also applies to: 63-63

packages/adapter-dify/src/types.ts (1)

20-37: 类型定义清晰且结构良好。

新增的三个类型(InputFileObjectUploadCandidateFilePayload)分别表示 Dify API 文件对象、待上传文件候选和已解析的文件负载,职责划分明确。

packages/adapter-dify/src/requester.ts (9)

1-37: 导入项完整且必要。

新增的导入项支持多模态文件处理,包括消息内容提取、文件上传和序列化等功能。


130-160: Agent 流文件准备逻辑清晰。

正确提取最后一条消息、准备文件上传列表,并根据实际是否有文件决定是否在请求体中包含 files 字段,避免发送空数组。


235-260: Workflow 流文件准备逻辑正确。

与 Agent 流类似,但正确地省略了 queryconversation_id 字段,符合 Dify Workflow API 的要求。


318-346: 输入构建逻辑符合 PR 目标。

通过 chatluna_ 前缀的输入字段传递多模态元数据,保持了 additional_kwargs 不被修改,与 PR 描述一致。


348-354: 用户标识解析逻辑简洁。

从变量中提取用户标识,具有合理的回退链和默认值。


356-392: 文件准备流程设计合理。

正确检查配置开关和候选文件列表,避免不必要的上传操作。


394-454: 候选文件提取逻辑合理。

仅从消息内容中提取图像 URL,通过 Set 进行去重,避免重复上传。注释掉的 additional_kwargs 提取代码与 PR 目标一致。


456-493: 文件上传循环处理健壮。

每个候选文件独立上传,失败不会中断整个流程,日志记录详细。第 484 行有格式化提示(prettier),但不影响功能。


622-634: 请求体清理逻辑合理。

移除 undefined 字段可确保发送给 Dify API 的 JSON 格式干净,避免潜在的序列化问题。

packages/adapter-dify/src/utils.ts (9)

10-55: MIME 类型推断覆盖常见格式。

函数处理了常见的图像、文档、音频和视频格式,并提供合理的默认值。


57-95: 扩展名推断实现合理。

guessMimeType 形成对应关系,对未知图像类型的特殊处理提升了健壮性。


97-118: 文件名生成和缓冲区转换实现正确。

convertToBuffer 正确处理了 BufferArrayBufferTypedArray(包括偏移量和长度),逻辑严谨。


120-150: MIME 到文件类型的映射完整。

覆盖了图像、音频、视频和文档类型,与 InputFileObject 类型定义一致。


152-190: 多模态元数据序列化安全且健壮。

多层截断机制(限制文件数量、源字符串长度和总长度)防止元数据过长,错误处理确保序列化失败不会中断流程。


192-239: 文件负载解析流程设计合理。

按照成本递增的顺序尝试解析(data URL → 本地文件 → 远程文件),每个步骤失败后继续尝试下一个,逻辑清晰。


241-260: Data URL 解析实现正确。

正则表达式匹配标准的 data URL 格式,base64 解码正确。


303-355: 远程文件获取实现安全且健壮。

正确限制了 URL 协议为 http/https,支持请求取消(signal),错误处理完善,从响应头和 URL 路径中提取文件名的逻辑合理。


262-301: Path traversal vulnerability requires context verification.

The code lacks explicit path validation and normalization, which could allow directory traversal attacks if source parameter comes from untrusted input. However, the actual exploitability depends on how this function is called.

Critical verification points needed:

  1. Is source derived from untrusted user input or controlled sources?
  2. What constraints or validations exist at call sites?
  3. Is this function intended for user-provided paths or only pre-validated sources?

If this function processes untrusted file paths, implement path normalization and boundary validation using path.resolve(), fs.promises.realpath(), and a whitelist of allowed base directories before attempting file operations. The suggested approach of adding allowedBasePaths validation is sound, but requires confirming it fits the codebase architecture and all call sites can provide appropriate path restrictions.

Replace multiple fallback checks for conversation ID from configurable options with direct access to variables_hide.built.conversationId for cleaner and more maintainable code.
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67d91b0 and 3b0eb6a.

📒 Files selected for processing (1)
  • packages/core/src/llm-core/platform/model.ts (2 hunks)
🔇 Additional comments (1)
packages/core/src/llm-core/platform/model.ts (1)

193-193: 正确使用了计算后的会话 ID。

该行正确地使用了前面计算的 id 变量,在前面不安全的属性访问问题修复后,此处的逻辑是正确的。

@dingyi222666
Copy link
Member

如果可以了话,在此 issue 下回复。不要在群聊里艾特我,我不一定看得到。在 issue 底下交流可以保证互相都能看到消息,因为是有抄送为邮件。

@dingyi222666
Copy link
Member

确认可以了我才合并,,,你不测试的话我不知道能不能用

- add undici to Dify adapter deps and expose getModels override
- clean refreshModels ModelInfo shape and harden non-chat model error
- allow ChatLunaChatModel detection via constructor name to handle module isolation
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

🧹 Nitpick comments (2)
packages/core/src/llm-core/chat/app.ts (1)

455-465: 代码逻辑正确,建议改进类型安全性。

使用 constructor.name 作为后备检查是处理模块隔离问题的合理方案,整体逻辑正确。不过静态分析工具标记了第 458 行的 any 类型使用。

可以考虑使用更具体的类型来改进类型安全性:

-        const value = llmModel.value as any
+        const value = llmModel.value as unknown
         if (
             value instanceof ChatLunaChatModel ||
-            value.constructor?.name === 'ChatLunaChatModel'
+            (value as { constructor?: { name?: string } }).constructor?.name === 'ChatLunaChatModel'
         ) {
             return [llmModel, llmInfo]
         }

或者定义一个类型守卫函数:

function isChatLunaChatModel(value: unknown): value is ChatLunaChatModel {
    if (value == null) return false
    const obj = value as { constructor?: { name?: string } }
    return value instanceof ChatLunaChatModel || 
           obj.constructor?.name === 'ChatLunaChatModel'
}

// 使用时:
if (llmModel.value != null && isChatLunaChatModel(llmModel.value)) {
    return [llmModel, llmInfo]
}
packages/adapter-dify/src/client.ts (1)

78-82: 为非 llm 类型模型显式抛出 MODEL_INIT_ERROR 合理,建议按 Prettier 要求调整格式

这里对 info.type !== ModelType.llm 的情况直接抛出 ChatLunaError(ChatLunaErrorCode.MODEL_INIT_ERROR, …),并在 originError 中带上模型名与类型字符串,符合 fail-fast 和可观测性的需求,能避免后续以错误类型模型走聊天链路,行为修改是正向的。

CodeFactor/Prettier 对第 81 行给出了格式化告警,建议按项目统一风格稍作换行,类似下面这样以避免 CI 继续报 warning:

-        throw new ChatLunaError(
-            ChatLunaErrorCode.MODEL_INIT_ERROR,
-            new Error(`Model ${model} is not a chat model (type: ${ModelType[info.type]})`)
-        )
+        throw new ChatLunaError(
+            ChatLunaErrorCode.MODEL_INIT_ERROR,
+            new Error(
+                `Model ${model} is not a chat model (type: ${ModelType[info.type]})`
+            )
+        )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b0eb6a and db6a821.

⛔ Files ignored due to path filters (1)
  • packages/adapter-dify/package.json is excluded by !**/*.json
📒 Files selected for processing (2)
  • packages/adapter-dify/src/client.ts (3 hunks)
  • packages/core/src/llm-core/chat/app.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-04T20:25:49.216Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 653
File: packages/core/src/llm-core/platform/model.ts:169-176
Timestamp: 2025-12-04T20:25:49.216Z
Learning: In packages/core/src/llm-core/platform/model.ts, the code intentionally uses unsafe nested property access (e.g., options?.variables_hide['built']['conversationId']) without full optional chaining to enable explicit TypeErrors for debugging and diagnosis when the expected structure is missing. This fail-fast approach is preferred over silently returning undefined.

Applied to files:

  • packages/adapter-dify/src/client.ts
  • packages/core/src/llm-core/chat/app.ts
📚 Learning: 2025-09-17T20:44:22.729Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 551
File: packages/core/src/services/chat.ts:564-583
Timestamp: 2025-09-17T20:44:22.729Z
Learning: 在 packages/core/src/services/chat.ts 中的 chatluna_docstore 表定义中,createdAt 字段使用 'date' 类型即可,无需使用 'timestamp' 类型,因为这是用于 ORM 操作的场景。

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
📚 Learning: 2025-09-17T20:43:57.714Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 551
File: packages/core/src/llm-core/vectorstores/database.ts:138-140
Timestamp: 2025-09-17T20:43:57.714Z
Learning: In packages/core/src/llm-core/vectorstores/database.ts, the createdAt field in ChatLunaDocument is intentionally used as a "lastUpdated" timestamp rather than a true creation timestamp. The toStoredDocument() function sets createdAt: new Date() on every upsert operation to track when documents were last modified, and the stat() method uses max(createdAt) to get the last update time.

Applied to files:

  • packages/core/src/llm-core/chat/app.ts
🧬 Code graph analysis (2)
packages/adapter-dify/src/client.ts (1)
packages/core/src/utils/error.ts (1)
  • ChatLunaError (13-41)
packages/core/src/llm-core/chat/app.ts (1)
packages/core/src/llm-core/platform/model.ts (1)
  • ChatLunaChatModel (105-788)
🪛 GitHub Check: CodeFactor
packages/adapter-dify/src/client.ts

[warning] 81-81: packages/adapter-dify/src/client.ts#L81
Replace Model·${model}·is·not·a·chat·model·(type:·${ModelType[info.type]}) with ⏎················Model·${model}·is·not·a·chat·model·(type:·${ModelType[info.type]})⏎············ (prettier/prettier)

packages/core/src/llm-core/chat/app.ts

[warning] 458-458: packages/core/src/llm-core/chat/app.ts#L458
Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)

🔇 Additional comments (1)
packages/adapter-dify/src/client.ts (1)

3-7: Dify 模型显式声明 ImageInput 能力的实现是合理的

refreshModels 中为每个 additionalModels 返回的 ModelInfo 增加 capabilities: [ModelCapabilities.ImageInput],配合新增的 ModelCapabilities 导入,能够让上层能力判断清晰知道这些 Dify workflow 支持图像输入,和本 PR 的目标吻合,且不会改变现有文本对话逻辑,设计合理、实现也没有明显问题。

Also applies to: 42-51

Copy link
Member

@dingyi222666 dingyi222666 left a comment

Choose a reason for hiding this comment

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

need review

@springtiming
Copy link
Contributor Author

在 adapter-dify 的 devDependencies 中加入了 undici,仅用于本地/的测试构建;
发布产物仍依赖 core 中的实现,不会引入额外运行时依赖。

同时修复了一个会导致 303 的问题。已通过手动测试验证,功能运行正常。

@dingyi222666
Copy link
Member

会导致 303,应该是你的本地例子多例了。。。。删掉 lock 文件重新 install。。。这里的判断完全不需要

@springtiming
Copy link
Contributor Author

那我再看一下

@dingyi222666
Copy link
Member

多例是指:本地开发的 chatluna,和 koishi 其他插件依赖的 chatluna,版本不一致的问题。此问题常见在本地开发中(本地开发的版本没有被引入,实际引入了 npm 的版本),例如下图(这是正常情况):
image

如果 chatluna 相关的依赖(包括 chatluna 本身)右边的版本没有全部统一,则表示你的开发环境存在多例问题。

此问题在删除包管理器的 lock 文件后重新安装可以解决。

@springtiming
Copy link
Contributor Author

已回退模型类型检测 ,通过手动图片传输测试

@xiaolan20020118-create
Copy link
Contributor

谢谢两位大哥!

@dingyi222666
Copy link
Member

新电脑弄好了,下周一或者周二合并

- Add history length limiting (130k chars) with truncation logic
- Extract text content from multimodal messages
- Improve null safety for message access
- Format error message for better readability
@dingyi222666
Copy link
Member

下次提交 pr 之前,记得给自己的 git 和 github 设置 gpg 签名.... 不然我不合并了,我会全部 squash 到一个我签名的 commit 里....

@dingyi222666 dingyi222666 merged commit 0b36349 into ChatLunaLab:v1-dev Dec 14, 2025
0 of 2 checks passed
@springtiming
Copy link
Contributor Author

下次提交 pr 之前,记得给自己的 git 和 github 设置 gpg 签名.... 不然我不合并了,我会全部 squash 到一个我签名的 commit 里....

知道了

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

🧹 Nitpick comments (3)
packages/adapter-dify/src/requester.ts (2)

445-505: 候选提取逻辑清晰。

使用 Set 去重,仅从消息内容提取图像 URL。注释的 additional_kwargs 处理代码符合 PR 目标(保留 additional_kwargs,不自动注入图像)。

注意:第 484 行有 prettier 格式问题,去除多余括号。

应用此修复以符合代码格式规范:

                     typeof imageUrl === 'string' ? imageUrl : imageUrl?.url
-                    : (candidate.type ?? 'file'))
+                    : candidate.type ?? 'file')

584-640: 文件上传逻辑健全,但响应解析可优化可读性。

FormData 使用正确,错误处理全面。第 622-626 行的 uploadFileId 提取逻辑较为复杂(嵌套可选链和类型断言),建议拆分为中间变量以提升可读性。

考虑重构以提升可读性:

-            const uploadFileId =
-                typeof result === 'object' && result != null
-                    ? ((result as { data?: { id?: string }; id?: string }).data
-                          ?.id ?? (result as { id?: string }).id)
-                    : undefined
+            const resultObj =
+                typeof result === 'object' && result != null
+                    ? (result as { data?: { id?: string }; id?: string })
+                    : undefined
+            const uploadFileId = resultObj?.data?.id ?? resultObj?.id
packages/adapter-dify/src/client.ts (1)

48-51: maxTokens: 100000000000 不符合实际的 token 限制规范。

虽然此值在数学运算中不会导致溢出,但设置为 1000 亿 token 超出常规。建议使用合理的上限值(如其他适配器的 100_000-200_000)或添加代码注释说明 Dify 工作流支持动态 token 限制的设计意图。

capabilities 添加正确支持了图像输入能力。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db6a821 and 62beeeb.

📒 Files selected for processing (2)
  • packages/adapter-dify/src/client.ts (3 hunks)
  • packages/adapter-dify/src/requester.ts (7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-04T20:25:49.237Z
Learnt from: dingyi222666
Repo: ChatLunaLab/chatluna PR: 653
File: packages/core/src/llm-core/platform/model.ts:169-176
Timestamp: 2025-12-04T20:25:49.237Z
Learning: In packages/core/src/llm-core/platform/model.ts, the code intentionally uses unsafe nested property access (e.g., options?.variables_hide['built']['conversationId']) without full optional chaining to enable explicit TypeErrors for debugging and diagnosis when the expected structure is missing. This fail-fast approach is preferred over silently returning undefined.

Applied to files:

  • packages/adapter-dify/src/requester.ts
🧬 Code graph analysis (1)
packages/adapter-dify/src/client.ts (1)
packages/core/src/utils/error.ts (1)
  • ChatLunaError (13-41)
🪛 GitHub Check: CodeFactor
packages/adapter-dify/src/requester.ts

[warning] 484-484: packages/adapter-dify/src/requester.ts#L484
Replace (candidate.type·??·'file') with candidate.type·??·'file' (prettier/prettier)

🔇 Additional comments (16)
packages/adapter-dify/src/client.ts (2)

4-4: LGTM!

导入 ModelCapabilities 用于标记模型的多模态能力,与 PR 目标一致。


78-84: LGTM!

为非聊天模型添加了明确的错误处理,使用 MODEL_INIT_ERROR 代码和描述性错误消息。枚举访问 ModelType[info.type] 在类型值有效时是安全的。

packages/adapter-dify/src/requester.ts (14)

1-38: LGTM!

新增的导入支持多模态处理和文件上传功能,与 PR 目标一致。undiciFileFormData 仅用于开发/测试环境(devDependency),生产环境使用核心实现。


61-76: 会话 ID 解析逻辑合理。

多重回退路径(params.idbuilt.conversationIdchatluna_conversation_id → 从消息提取)确保了兼容性。根据项目学习记录,此处有意使用非完全可选链以便在结构缺失时触发显式错误用于调试诊断。

错误消息已更新,清晰指出 Dify 适配器仅支持 chatluna 聊天模式。

基于学习记录,此处代码有意采用当前访问模式。


102-122: LGTM!

从消息的 additional_kwargs 中提取 conversationId 的逻辑清晰,支持多种字段名提供了灵活性。


124-168: 流式聊天逻辑更新合理。

新增的多模态文件准备、结构化请求体构建和信号传递支持了图像上传功能。条件性添加 files 字段(仅在有文件时)保持了 payload 简洁。


230-267: LGTM!

工作流模式的更新与聊天模式保持一致,正确省略了 queryconversation_id(工作流模式不需要)。


317-338: LGTM!

buildInputs 方法全面构建输入对象,使用 chatluna_ 前缀提供了良好的命名空间隔离,避免与 Dify 原生字段冲突。


340-376: 历史记录截断策略合理。

130k 字符限制下的截断逻辑正确:优先移除最旧消息,单条消息过长时保留最后 130k 字符。循环终止条件安全(长度递减且有单消息兜底)。


378-397: LGTM!

文本提取逻辑正确处理了字符串和数组内容,仅提取文本部分(忽略图像),返回 undefined 而非空字符串便于过滤。


399-405: LGTM!

用户标识解析逻辑简洁,回退到 'chatluna' 确保始终返回有效值。


407-443: 文件准备流程设计良好。

检查 enableFileUpload 配置并提前返回避免了不必要的上传。使用 safeSerializeMultimodal 保留多模态元数据,符合 PR 目标(不自动注入图像,保留元数据)。


507-544: LGTM!

逐个上传候选文件并容错处理,单个文件失败不影响其他文件上传,错误日志记录充分。


546-582: LGTM!

多模态文件转换逻辑清晰,使用工具函数 resolveFilePayloadmapMimeToFileType 保持了职责分离。transfer_method: 'local_file' 符合 Dify API 规范。


664-695: 请求清理逻辑重要且正确。

删除 datadata.inputs 中的 undefined 字段防止将无效值发送到 Dify API,这对于避免 API 错误至关重要。

当前实现明确且易读,虽然可以使用递归工具函数,但对于两层结构当前方法已足够。


743-745: LGTM!

使用导出的 logger 实例符合模块重构目标,与 src/index.ts 的变更保持一致。

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.

3 participants