Skip to content

Conversation

@dingyi222666
Copy link
Member

This PR optimizes GIF frame extraction in the image-service plugin by implementing incremental decoding to handle GIF disposal methods correctly and improve performance.

Bug Fixes

Performance Improvements

  • Implemented incremental canvas decoding to avoid redundant frame processing
  • Added smart frame tracking to minimize duplicate decoding work
  • For extracting frames [0, 10, 20], now decodes 20 frames total instead of 30 frames

Other Changes

  • Added disposal method detection (0, 1, 2, 3) for proper differential frame handling
  • Added contextual hint text for GIF images to improve model understanding
  • Increased middleware priority to 100 for proper execution order

…coding

- Implement incremental canvas decoding to avoid redundant frame processing
- Handle GIF disposal methods (0, 1, 2, 3) correctly for differential frames
- Track last decoded frame position to minimize duplicate decoding work
- Add GIF context text hint for better model understanding
- Suppress duplicate GIF warnings when image-service plugin is installed
- Increase middleware priority to 100 for proper execution order

Performance improvement: For extracting frames [0, 10, 20], now decodes
20 frames total instead of 30 frames with the previous implementation.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 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

GIF 警告改为仅在未检测到 image-service 插件时发出;image-service 的 GIF 拦截流在 fakeMessage 前注入说明文本并传入额外参数;GIF 帧提取实现增量画布解码;两个适配器的构造函数泛型插件类型被具体化。

Changes

Cohort / File(s) 变更摘要
中间件:GIF 警告条件化
packages/core/src/middlewares/chat/read_chat_message.ts
将原本无条件发出的 GIF 警告改为仅在未检测到 image-service 插件时才发出(覆盖读取流与回退路径的三个位置);早期返回 false 的控制流保持不变。
图像服务:GIF 拦截与消息注入
packages/service-image/src/index.ts
在处理 GIF 的 intercept 流中向构造的 fakeMessage 注入前置文本 "This is a GIF image. See the frames below:",并在调用 intercept 时添加额外数值参数 100,随后仍将帧作为图片追加。
帧提取工具:增量画布解码
packages/service-image/src/utils.ts
新增内部 hasComplexDisposal 检测(disposal=2/3)与增量解码策略:维护 canvas(Uint8ClampedArray)与 lastDecodedFrame,根据是否存在复杂 disposal 或回退决定全量或增量解码;在每帧前复制画布为独立 frameData 后生成图像,减少重复从头解码并保留原有 PNG 列表生成逻辑。
适配器:构造函数泛型具体化
packages/adapter-dify/src/requester.ts, packages/adapter-spark/src/requester.ts
将构造函数中 _plugin 的泛型类型分别改为 ChatLunaPlugin<DifyClientConfig>ChatLunaPlugin<SparkClientConfig>,更新了公开/导出的构造签名类型。

Sequence Diagram(s)

sequenceDiagram
    participant Chat as ChatMiddleware
    participant ImageService as ImageService
    participant Utils as FrameUtils
    participant Canvas as CanvasBuffer

    Chat->>Chat: 识别到 GIF 图像
    alt image-service 插件 已安装
        Chat->>ImageService: 调用 intercept 处理 GIF (arg=100)
        ImageService->>ImageService: 在 fakeMessage 前注入提示文本
        ImageService->>Utils: 请求提取 GIF 帧
        Utils->>Canvas: 检查 lastDecodedFrame 与 hasComplexDisposal
        alt 需要全量解码
            Utils->>Canvas: 清空并从帧0解码至当前帧
        else 增量解码
            Utils->>Canvas: 从 lastDecodedFrame+1 解码至当前帧
        end
        Canvas-->>Utils: 返回当前画布数据(复制为 frameData)
        Utils-->>ImageService: 返回帧 buffers
        ImageService-->>Chat: 返回带前置文本与帧的 fakeMessage
    else image-service 插件 缺失
        Chat->>Chat: 发出 GIF 警告并早期返回 false
    end
Loading

Estimated code review effort

🎯 3 (中等) | ⏱️ ~20 分钟

  • 重点检查 packages/service-image/src/utils.ts 的增量解码正确性、hasComplexDisposal 判定以及画布复制(缓冲/内存)逻辑与性能。
  • 验证 packages/service-image/src/index.ts 中注入文本与新增 100 参数对拦截器行为的语义影响。
  • 确认 packages/core/.../read_chat_message.ts 中插件存在性检测覆盖所有 GIF 路径(包括 oldImageRead 回退)。
  • 检查 packages/adapter-dify/src/requester.tspackages/adapter-spark/src/requester.ts 的泛型签名更改是否影响导出类型或调用方。

Possibly related PRs

兔子的诗

🐰 画布轻拂帧帧新,增量聪明少重寻,
插件在时静悄悄,警告只在缺时吟,
文本先行引帧出,GIF 舞步更从容。

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确总结了本PR的核心改变:优化GIF帧提取并实现增量解码。
Description check ✅ Passed 描述充分说明了PR的目的、修复的问题、性能改进和其他更改,与代码改动相关。
Linked Issues check ✅ Passed 代码改动满足#636的所有主要需求:修复了GIF未安装插件时的重复警告、实现了GIF帧增量解码、添加GIF上下文提示文本。
Out of Scope Changes check ✅ Passed Dify和Spark适配器中的类型参数更新是为了支持通用插件类型系统,与GIF优化目标相关且必要。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/image-service

📜 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 58e70fc and c931faa.

📒 Files selected for processing (1)
  • packages/adapter-spark/src/requester.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/adapter-spark/src/requester.ts (1)
packages/core/src/services/chat.ts (1)
  • ChatLunaPlugin (594-800)
⏰ 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 (1)
packages/adapter-spark/src/requester.ts (1)

31-40: 构造函数中将 _plugin 类型收紧为 ChatLunaPlugin<SparkClientConfig> 是合理的类型改进

这一改动让 SparkRequester 的构造函数在类型层面与自身的泛型参数 SparkClientConfig 以及 ClientConfigPool<SparkClientConfig> 保持一致,提升了类型安全性和可读性,不引入运行时行为变化。只要对应创建 SparkRequester 的调用处插件本身就是基于 Spark 的配置,这个收紧类型在实际使用上不会带来破坏性影响。


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 @dingyi222666, 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 delivers a crucial upgrade to the image-service plugin, primarily focusing on robust and efficient GIF frame extraction. It rectifies existing bugs related to frame corruption and repetitive warnings, while simultaneously boosting performance through a sophisticated incremental decoding mechanism. The changes ensure that GIF processing is both accurate and optimized, providing a more reliable and performant experience for handling animated images.

Highlights

  • GIF Frame Extraction Optimization: Implemented incremental decoding for GIF frame extraction in the image-service plugin, significantly improving performance and correctness by avoiding redundant processing and properly handling disposal methods.
  • Bug Fix: Incorrect GIF Frame Handling: Resolved issues where GIF frames were incorrectly extracted, leading to corruption, by accurately detecting and applying GIF disposal methods (0, 1, 2, 3).
  • Bug Fix: Duplicate GIF Warning Messages: Eliminated redundant warning messages about GIF support when the image-service plugin is already installed, ensuring a cleaner user experience.
  • Performance Improvement: Introduced smart frame tracking and incremental canvas decoding, reducing the total number of frames decoded when extracting specific frames (e.g., decoding 20 frames instead of 30 for frames [0, 10, 20]).
  • Contextual Hint for GIF Images: Added a descriptive text hint for GIF images to improve model understanding when frames are presented.
  • Middleware Priority Adjustment: Increased the middleware priority for the image-service plugin to 100 to ensure proper execution order.
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 introduces significant optimizations for GIF frame extraction by implementing incremental decoding and handling disposal methods. It also fixes an issue with duplicate warnings and improves the contextual information provided to the model for GIF images. The changes in read_chat_message.ts and index.ts are solid improvements. However, the core GIF decoding logic in utils.ts has a correctness issue. While it attempts to handle disposal methods, the optimization for incremental decoding is based on a flawed assumption and can lead to corrupted frames for certain GIFs. I've left a detailed comment on this critical issue.

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 51a0a23 and 193a97f.

📒 Files selected for processing (3)
  • packages/core/src/middlewares/chat/read_chat_message.ts (2 hunks)
  • packages/service-image/src/index.ts (2 hunks)
  • packages/service-image/src/utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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/service-image/src/index.ts
🧬 Code graph analysis (2)
packages/core/src/middlewares/chat/read_chat_message.ts (3)
packages/service-image/src/index.ts (1)
  • logger (17-17)
packages/extension-long-memory/src/index.ts (1)
  • logger (9-9)
packages/core/src/index.ts (1)
  • logger (38-38)
packages/service-image/src/index.ts (1)
packages/service-image/src/utils.ts (1)
  • addTextToContent (220-237)
⏰ 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 (5)
packages/core/src/middlewares/chat/read_chat_message.ts (2)

176-183: 与主代码路径保持一致的条件检查。

oldImageRead 函数中的条件检查与第 127-131 行逻辑一致,确保了两个代码路径的行为统一。


126-133: Conditional logic correctly gates GIF warnings based on plugin availability.

The changes properly suppress the GIF unsupported warning when image-service plugin is installed by checking ctx.chatluna.getPlugin('image-service') == null before logging. The getPlugin method is a legitimate API from the chat service that retrieves plugins from the internal registry. Returning false allows the middleware chain to continue, enabling the image-service plugin to handle GIF processing when available.

The identical conditional patterns at lines 127 and 177 handle separate message processing paths (buffer-based and URL-based image sources) and are appropriately duplicated for clarity.

packages/service-image/src/index.ts (2)

128-131: 添加上下文提示文本有助于模型理解 GIF 内容。

在帧图像之前添加描述性文本 "This is a GIF image. See the frames below:" 为 AI 模型提供了必要的上下文,有助于其正确理解后续的帧序列。


154-156: Interceptor priority 100 ensures correct GIF image handling order.

The priority value (100) is higher than the core module's 'img' interceptor priority (-100, line 156 of read_chat_message.ts), ensuring the image-service intercept runs before the core image processing logic. The insertion algorithm in message_transform.ts (lines 146–152) maintains interceptors in descending priority order, so higher numbers execute first.

packages/service-image/src/utils.ts (1)

105-111: 正确复制画布数据避免引用问题。

创建 frameData 的副本是正确的做法,避免了后续帧解码时覆盖已创建图像数据的问题。

Previously checked current frame's disposal method, but GIF spec requires
checking previous frames' disposal methods to determine canvas preparation.
Disposal methods indicate how to handle canvas AFTER displaying a frame,
not before.

- Add hasComplexDisposal() helper to check disposal methods in frame range
- Check frames between lastDecodedFrame and current for disposal 2/3
- Properly handle disposal 2 (restore to background) and 3 (restore to previous)
- Align with GIF specification for correct differential frame rendering
Add DifyClientConfig type parameter to ChatLunaPlugin for better type safety
and consistency with the parent class signature.
This change adds the SparkClientConfig generic type parameter to the ChatLunaPlugin instance in the SparkRequester constructor, ensuring type consistency with other adapters.
@dingyi222666 dingyi222666 merged commit 1e22f46 into v1-dev Nov 30, 2025
4 of 5 checks passed
@dingyi222666 dingyi222666 deleted the fix/image-service branch November 30, 2025 22:07
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.

chatluna Detected GIF image, which is not supported by most models. Please install chatluna-image-service plugin to parse GIF animations.

2 participants