Skip to content

feat: ✨ 渠道自定义标签系统(创建/编辑/筛选/记忆) - #198

Open
Moriarty6086 wants to merge 3 commits into
bestruirui:devfrom
Moriarty6086:feat/channel-tags
Open

feat: ✨ 渠道自定义标签系统(创建/编辑/筛选/记忆)#198
Moriarty6086 wants to merge 3 commits into
bestruirui:devfrom
Moriarty6086:feat/channel-tags

Conversation

@Moriarty6086

Copy link
Copy Markdown
Contributor
  • 渠道模型新增 Tags 字段(GORM serializer:json)
  • 渠道创建/编辑表单支持标签输入,支持从已有标签快速选择(Popover 建议)
  • 分组编辑器新增按标签筛选,筛选状态按分组独立记忆(Zustand store)
  • LLMChannel / model/channel API 透传 tags,前端 null 归一化
  • i18n:中英文标签相关翻译

实际效果:
标签下拉菜单(从已有标签快速选择):
image
已添加标签:
image
编辑分组页面效果:
image

- 渠道模型新增 Tags 字段(GORM serializer:json)
- 渠道创建/编辑表单支持标签输入,支持从已有标签快速选择(Popover 建议)
- 分组编辑器新增按标签筛选,筛选状态按分组独立记忆(Zustand store)
- LLMChannel / model/channel API 透传 tags,前端 null 归一化
- i18n:中英文标签相关翻译

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 17, 2026 03:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a comprehensive channel custom tagging system with creation, editing, filtering, and per-group filter state persistence capabilities.

Changes:

  • Added Tags field to Channel and LLMChannel models with JSON serialization support in backend (Go) and proper null-to-empty-array normalization in frontend APIs
  • Implemented tag input UI with autocomplete suggestions from existing tags in channel create/edit forms
  • Added per-group tag filtering in the group editor with Zustand-based state persistence to remember filter selections across component re-mounts
  • Added i18n translations for tag-related UI labels in Chinese (simplified/traditional) and English

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/model/channel.go Added Tags field to Channel model with GORM JSON serializer
internal/model/llm.go Added Tags field to LLMChannel struct for API responses
internal/op/channel.go Added tags field handling in channel update operations and LLM channel list generation
web/src/api/endpoints/channel.ts Added tags field to Channel and related types with null normalization
web/src/api/endpoints/model.ts Added tags field to LLMChannel with null normalization in select function
web/src/components/modules/channel/Form.tsx Implemented tag input with popover suggestions and badge display
web/src/components/modules/channel/Create.tsx Added tags initialization in channel creation form
web/src/components/modules/channel/CardContent.tsx Added tags comparison and update logic in channel edit
web/src/components/modules/group/Editor.tsx Added Zustand store for per-group tag filter persistence and tag-based channel filtering UI
web/src/components/modules/group/Card.tsx Added tags field to SelectedMember via tagsByKey map
web/public/locale/*.json Added translations for "tags", "filterByTag", and related labels

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

onClick={() => toggleTag(tag)}
className={cn(
'px-2 py-0.5 text-xs rounded-md transition-colors',
selectedTags.includes(tag)

Copilot AI Feb 17, 2026

Copy link

Choose a reason for hiding this comment

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

The UI uses selectedTags to highlight selected tag buttons, but the actual filtering logic uses effectiveTags (which prunes stale tags). This creates an inconsistency where stale tags (tags that no longer exist in any channel) will still appear as selected/highlighted in the UI, even though they're not actually filtering anything. Consider using effectiveTags.includes(tag) here instead to ensure the UI accurately reflects which tags are actively being used for filtering.

Suggested change
selectedTags.includes(tag)
effectiveTags.includes(tag)

Copilot uses AI. Check for mistakes.
{tag}
</button>
))}
{selectedTags.length > 0 && (

Copilot AI Feb 17, 2026

Copy link

Choose a reason for hiding this comment

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

Similar to the tag button highlighting, the visibility of the "All" clear button is determined by selectedTags.length > 0, but the actual filtering uses effectiveTags. This means the clear button will remain visible even when all selected tags are stale and not actually filtering anything. Consider using effectiveTags.length > 0 instead for consistency.

Suggested change
{selectedTags.length > 0 && (
{effectiveTags.length > 0 && (

Copilot uses AI. Check for mistakes.
Moriarty6086 and others added 2 commits February 17, 2026 15:03
- useTagFilterStore 添加 persist 中间件,刷新页面后保留标签筛选
- 修复 Zustand selector 返回新空数组引用导致的无限渲染循环(EMPTY_TAGS 稳定引用)
- 添加 effectiveTags 派生状态,自动过滤已失效的标签

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
按钮高亮和"全部"按钮显示条件从 selectedTags 改为 effectiveTags,
避免 localStorage 残留失效标签时 UI 状态与过滤行为不一致。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Moriarty6086

Copy link
Copy Markdown
Contributor Author

tag 相关状态现在分为 4 类,持久化方式不同:

  1. Channel.tags(渠道本身的标签数据)
  • 持久化到后端数据库。
  • 后端字段是 channels.tags(GORM serializer:json)。
  • 这是业务数据,跨刷新/跨浏览器/跨设备都能保留(同一服务端数据下)。
  • 相关:internal/model/channel.go、internal/op/channel.go。
  1. 分组编辑器的标签筛选选择(selectedTags)
  • 持久化到前端 localStorage,通过 Zustand persist。
  • 存储 key:tag-filter-storage。
  • 结构:tagsByGroup[groupId] = string[](按分组隔离)。
  • 这是 UI 偏好,不写入后端。
  • 相关:web/src/components/modules/group/Editor.tsx。
  1. 新建分组(groupId 不存在)时的标签筛选
  • 仅组件本地 useState,不持久化。
  • 关闭弹窗或刷新后丢失,符合“新建草稿态”预期。
  • 相关:web/src/components/modules/group/Create.tsx、web/src/components/modules/group/Editor.tsx。
  1. 运行时派生状态(effectiveTags、Popover 开关、输入框内容等)
  • 都是不持久化的临时状态。
  • effectiveTags 是从已选标签和当前可用标签动态计算,用于避免 stale 标签造成过滤异常。
  • 相关:web/src/components/modules/group/Editor.tsx、web/src/components/modules/channel/Form.tsx。

说明:

  • 当前方案是“业务数据后端持久化 + UI 偏好前端持久化”的分层设计。
  • 分组页面的筛选状态在 localStorage,不支持跨浏览器同步。

@Moriarty6086

Copy link
Copy Markdown
Contributor Author

分组编辑器的标签筛选选择是否应该改成存到后端有待商榷。

@bestruirui

Copy link
Copy Markdown
Owner

感谢PR

我感觉可以和 #159 一起实现

tag 改为分组是不是更好?
左侧添加模式区域改为文件夹样式

- 分组一
   - 渠道1
- 分组二
   - 渠道2
- 未分组
   - 渠道2

改为 可以向右侧添加 分组,渠道,模型

任务量有些大,我再仔细想想,目前我先修复完现有的BUG,后面再增加新功能

chenjingxiong added a commit to chenjingxiong/octopus that referenced this pull request Jun 21, 2026
…truirui#208 已内置)

合入的 PR:
- bestruirui#262 添加活动热力图自定义日期范围选择功能 (cherry-pick)
- bestruirui#271 修复日志无法滚动问题 (手动适配:VirtualizedGrid reachEnd 重置 + 日志分页偏移)
- bestruirui#287 加入 rerank 转发路由 + 重复资源检测 (手动适配)
- bestruirui#208 空响应检测触发负载均衡重试 → 已被 axonhub/llm 内置 WithEmptyResponseDetection() 覆盖,无需手动移植

跳过的 PR:
- bestruirui#282 TOTP + JSON 替换 (+1425/-115,改动过大)
- bestruirui#270 key 选择模式 (改了已删除的 images.go,需大量适配)
- bestruirui#199 熔断器开关 (改了已删除的 transformer,冲突复杂)
- bestruirui#198 渠道标签系统 (13 文件,改动偏大)
- bestruirui#194 日志筛选增强 (8 文件 +512/-86,改动偏大)
- bestruirui#190 统计清除 (合并提交多,冲突复杂)
- bestruirui#184 渠道排序 (排序已内置上游,复制功能非核心)

版本升级至 v0.9.28-2

Co-Authored-By: Claude <noreply@anthropic.com>
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