agentic_huge_data_base / wiki
页面 Open WebUI · 9.5 版本控制与历史·DeepWiki 中文全文译文

9.5 · 版本控制与历史(Version Control and History)

多模型对话工作台与知识应用入口 · 本章是 Open WebUI DeepWiki 中文译文的独立章节页,保留原始链接、源码锚点、模块标签和章节层级。

项目Open WebUI 章节9.5 状态全文译文 模块界面与交互、频道、笔记与协作、接口与服务契约、工具、记忆与模型调用
源码线索
  • backend/open_webui/migrations/versions/e1f2a3b4c5d6_add_is_pinned_to_note.py
  • backend/open_webui/models/notes.py
  • backend/open_webui/routers/notes.py
  • src/lib/apis/notes/index.ts
  • src/lib/components/common/DropdownOptions.svelte
  • src/lib/components/common/DropdownSub.svelte
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
模块标签
  • 界面与交互
  • 频道、笔记与协作
  • 接口与服务契约
  • 工具、记忆与模型调用
  • 检索、召回与知识系统

中文译文

版本控制与历史(中文译文)

原始 DeepWiki 页面:https://deepwiki.com/open-webui/open-webui/9.5-version-control-and-history
翻译时间:2026-06-09T16:10:04.207Z
翻译模型:deepseek-chat
原文字符数:11149
项目:Open WebUI (open-webui)

---

版本控制与历史记录

相关源文件

以下文件用于生成此 wiki 页面:

  • backend/open_webui/migrations/versions/e1f2a3b4c5d6_add_is_pinned_to_note.py
  • backend/open_webui/models/notes.py
  • backend/open_webui/routers/notes.py
  • src/lib/apis/notes/index.ts
  • src/lib/components/common/DropdownOptions.svelte
  • src/lib/components/common/DropdownSub.svelte
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
  • src/lib/components/notes/NoteEditor/Chat/Message.svelte
  • src/lib/components/notes/NoteEditor/Chat/Messages.svelte
  • src/lib/components/notes/NoteEditor/Controls.svelte
  • src/lib/components/notes/NotePanel.svelte
  • src/lib/components/notes/Notes.svelte
  • src/lib/components/notes/Notes/NoteMenu.svelte
  • src/routes/(app)/notes/+layout.svelte/notes/+layout.svelte)

本文档介绍 Open WebUI 中笔记的版本控制与历史记录系统。该系统自动追踪内容变更,使用户能够浏览历史版本并在需要时恢复。关于使用 Yjs 的协作编辑功能,请参见协作编辑

目的与范围

版本控制系统通过维护内容快照的历史记录,为笔记内容提供撤销/重做功能。每个版本以三种格式(JSON、HTML 和 Markdown)存储笔记内容,使用户可以在编辑历史中前后导航,并恢复任意历史版本。该系统独立于 Yjs 协作编辑系统,作为本地历史机制运行。

来源: src/lib/components/notes/NoteEditor.svelte:105-140, src/lib/components/notes/NoteEditor.svelte:233-246

版本数据结构

版本历史记录以内容快照数组的形式存储在每个笔记的数据结构中。每个快照包含某一时刻的完整内容状态。

标题:笔记版本数据流

graph TB
    Note["笔记对象 (NoteModel)"]
    Data["data (dict)"]
    Content["content (当前)"]
    Versions["versions (列表)"]

    Snapshot1["版本快照"]
    Snapshot2["版本快照"]
    SnapshotN["版本快照"]

    ContentJSON["json: TipTap JSON"]
    ContentHTML["html: HTML 字符串"]
    ContentMD["md: Markdown 字符串"]

    Note --> Data
    Data --> Content
    Data --> Versions

    Versions --> Snapshot1
    Versions --> Snapshot2
    Versions --> SnapshotN

    Content --> ContentJSON
    Content --> ContentHTML
    Content --> ContentMD

    Snapshot1 --> ContentJSON
    Snapshot1 --> ContentHTML
    Snapshot1 --> ContentMD
笔记结构

笔记对象遵循 NoteEditor.sveltenewNote 模板定义的结构:

字段类型描述
titlestring笔记标题。
data.content.jsonobject富文本编辑器的 TipTap JSON 表示。
data.content.htmlstring用于显示和导出的渲染 HTML。
data.content.mdstring用于 AI 处理和导出的 Markdown 源。
data.versionsarray包含先前内容快照的历史数组。
data.filesarray与笔记关联的文件附件列表。

来源: src/lib/components/notes/NoteEditor.svelte:108-122, backend/open_webui/models/notes.py:38-53

版本快照创建

版本以当前内容状态的快照形式创建。系统在创建新版本前,使用深度相等性检查来检测内容是否实际发生变化。

版本比较

NoteEditor.svelte 中的 areContentsEqual 函数使用 fast-deep-equal 库执行深度相等性检查,以确定内容是否已更改:

标题:内容相等性检查

graph LR
    A["areContentsEqual(a, b)"]
    B["fast-deep-equal(a, b)"]
    C["返回布尔值"]

    A --> B
    B --> C

实现: src/lib/components/notes/NoteEditor.svelte:11, src/lib/components/notes/NoteEditor.svelte:229-231

创建版本快照

insertNoteVersion 函数仅在当前内容相对于历史中最新的版本发生变化时,才创建新的版本快照:

  1. note.data.content 中提取当前内容(JSON、HTML、Markdown)。
  2. 使用 .at(-1)note.data.versions 数组中获取 lastVersion
  3. 使用 areContentsEqual 比较当前内容与 lastVersion
  4. 如果内容不同或不存在先前版本,则将当前内容追加到 versions 数组中。
  5. 如果创建了版本则返回 true,如果是重复则返回 false

实现: src/lib/components/notes/NoteEditor.svelte:233-246

版本导航系统

导航系统允许用户通过基于状态的方法,使用 versionIdx 指针在版本历史中前后移动。

版本索引状态
versionIdx含义显示的内容
null当前/最新内容note.data.content(实时编辑)
0n-1查看特定历史版本note.data.versions[versionIdx]

状态变量: src/lib/components/notes/NoteEditor.svelte:139

导航逻辑流程

versionNavigateHandler 管理版本遍历:

标题:版本导航逻辑

graph TD
    Start["versionNavigateHandler(direction)"]
    CheckEmpty{"versions.length == 0?"}
    ReturnEarly["return"]

    CheckNull{"versionIdx == null?"}
    GetLatest["lastVersion = versions.at(-1)"]
    CompareCurrent{"当前 != lastVersion?"}
    InsertNew["insertNoteVersion(note)"]
    SetIdx1["versionIdx = versions.length - 1"]
    SetIdx2["versionIdx = versions.length"]

    CheckDirection{"direction"}
    NavPrev["导航上一个 ('prev')"]
    NavNext["导航下一个 ('next')"]

    DecIdx{"versionIdx > 0?"}
    DecrementIdx["versionIdx -= 1"]

    IncIdx{"versionIdx < versions.length - 1?"}
    IncrementIdx["versionIdx += 1"]
    ResetNull["versionIdx = null"]

    SetContent["setContentByVersion(versionIdx)"]

    Start --> CheckEmpty
    CheckEmpty -->|是| ReturnEarly
    CheckEmpty -->|否| CheckNull

    CheckNull -->|是| GetLatest
    GetLatest --> CompareCurrent
    CompareCurrent -->|是| InsertNew
    InsertNew --> SetIdx1
    CompareCurrent -->|否| SetIdx2
    SetIdx1 --> CheckDirection
    SetIdx2 --> CheckDirection

    CheckNull -->|否| CheckDirection
    CheckDirection -->|prev| NavPrev
    CheckDirection -->|next| NavNext

    NavPrev --> DecIdx
    DecIdx -->|是| DecrementIdx
    DecIdx -->|否| SetContent
    DecrementIdx --> SetContent

    NavNext --> IncIdx
    IncIdx -->|是| IncrementIdx
    IncIdx -->|否| ResetNull
    IncrementIdx --> ResetNull
    ResetNull --> SetContent

实现: src/lib/components/notes/NoteEditor.svelte:379-409

内容恢复

setContentByVersion 函数将笔记内容恢复到指定的版本快照。选择版本后,RichTextInput(TipTap 编辑器)会更新以显示该版本的 HTML 内容。

恢复过程
  1. 验证历史: 如果 note.data.versions 中没有版本,则提前返回。
  2. 解析索引: 使用提供的索引,如果 versionIdxnull,则默认使用最后一个版本。
  3. 提取版本: 获取解析索引处的版本快照。
  4. 恢复内容: 将快照中的 JSON、HTML 和 Markdown 复制到活动的 note.data.content 中。
  5. 清理: 如果导航回"当前"状态(versionIdx === null),系统确保 UI 与最新的实时数据同步。

实现: src/lib/components/notes/NoteEditor.svelte:357-376

AI 集成与历史记录

当通过 AIMenu.svelteChat.svelte 面板触发 AI 增强笔记功能时,系统会与历史机制集成,允许用户撤销 AI 的更改。

  1. 编辑前快照: 在 AI 开始编辑之前,调用 insertNoteVersion 保存修改前的状态。
  2. 流式更新: Chat.svelte 中的 chatCompletionHandler 流式传输 AI 编辑内容。如果 editEnabled 处于活动状态,则使用 DEFAULT_DOCUMENT_EDITOR_PROMPT 重写内容。
  3. 完成: 完成后,系统触发 onEdited(),调用 editor.commands.setContent(note.data.content.html) 刷新编辑器视图。

来源: src/lib/components/notes/NoteEditor.svelte:248-251, src/lib/components/notes/NoteEditor/Chat.svelte:83-102, src/lib/components/notes/NoteEditor/Chat.svelte:122-240

后端持久化

笔记历史记录通过 updateNoteById API 持久化到后端数据库。

更新防抖

为防止快速编辑期间过多的数据库写入,changeDebounceHandler 实现了 200ms 的延迟。如果笔记标题、数据(包括版本)或访问权限发生变化,系统会等待活动暂停后再发送更新。

标题:持久化数据流

graph TD
    FE[("前端:NoteEditor.svelte")] -->|调用 updateNoteById()| API["src/lib/apis/notes/index.ts"]
    API -->|POST /notes/{id}/update| ROUTE["backend/open_webui/routers/notes.py:update_note_by_id"]
    ROUTE -->|NoteUpdateForm| MODEL["backend/open_webui/models/notes.py:Notes.update_note_by_id"]
    MODEL --> DB[(SQL 数据库:note 表)]

实现: src/lib/components/notes/NoteEditor.svelte:207-223, backend/open_webui/routers/notes.py:247-279, backend/open_webui/models/notes.py:230-244

列表截断

获取笔记列表时(例如用于侧边栏或主笔记页面),后端使用 _truncate_note_data 将内容大小限制为 1000 个字符,并剥离版本历史以优化性能。仅在按 ID 获取特定笔记时才会加载完整的版本历史。

来源: backend/open_webui/routers/notes.py:45-49, backend/open_webui/routers/notes.py:99, backend/open_webui/routers/notes.py:194