feat(memory): 记忆浏览页面添加事实、反思和事实归档的预览查看 - #2565
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🔗 Linked repositories identifiedCodeRabbit considers these linked repositories for cross-repo context during reviews:
🚧 Files skipped from review as they are similar to previous changes (2)
Walkthrough新增记忆组件读取 API,并在记忆浏览器中加入事实、事实归档和反思标签页,配套实现数据渲染、保存串行化、竞态控制、样式及多语言文案更新,喵。 Changes记忆组件浏览
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
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. Comment |
Greptile Summary本次改动为记忆浏览器增加长期记忆组件的只读预览,并修复此前反馈的异步切换、并发保存与本地化问题喵。
Confidence Score: 5/5当前改动看起来可以安全合并喵。 此前报告的跨标签陈旧响应、近期记忆响应覆盖、快速标签选择覆盖、并发保存覆盖及组件本地化缺失均已在当前代码中得到处理,未发现仍然存在的阻塞性故障喵。
|
| Filename | Overview |
|---|---|
| static/js/memory_browser.js | 新增组件标签页加载与渲染逻辑,并通过跨视图请求失效和保存队列修复此前报告的竞态喵。 |
| main_routers/memory_router.py | 新增只读组件文件接口,并对白名单类型、解析错误和目录越界进行处理喵。 |
| templates/memory_browser.html | 在现有编辑器头部加入具备 ARIA 状态的记忆组件标签页喵。 |
| static/css/memory_browser.css | 为组件标签、列表、折叠区和明暗主题状态徽标增加样式喵。 |
| static/locales/en.json | 补充组件视图所需英文文案并更新长期记忆提示喵。 |
| static/locales/es.json | 补充组件视图所需西班牙语文案并更新长期记忆提示喵。 |
| static/locales/ja.json | 补充组件视图所需日语文案并更新长期记忆提示喵。 |
| static/locales/ko.json | 补充组件视图所需韩语文案并更新长期记忆提示喵。 |
| static/locales/pt.json | 补充组件视图所需葡萄牙语文案并更新长期记忆提示喵。 |
| static/locales/ru.json | 补充组件视图所需俄语文案并更新长期记忆提示喵。 |
| static/locales/zh-CN.json | 补充组件视图所需简体中文文案并更新长期记忆提示喵。 |
| static/locales/zh-TW.json | 补充组件视图所需繁体中文文案并更新长期记忆提示喵。 |
Sequence Diagram
sequenceDiagram
participant U as 用户
participant B as 记忆浏览器
participant A as 记忆组件 API
participant F as 角色记忆文件
U->>B: 选择事实/归档/反思标签
B->>B: 使旧请求失效
B->>A: "GET /api/memory/component/{name}"
A->>A: 校验组件类型与目录边界
A->>F: 读取白名单文件
F-->>A: JSON 数据
A-->>B: 组件数据
B->>B: 校验请求序号并渲染卡片
Reviews (9): Last reviewed commit: "fix(i18n): revert locale clear typo fix ..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ce9d38bd3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| _loadComponentTab(container, currentCatName, tab); |
There was a problem hiding this comment.
Preserve unsaved edits when switching tabs
When the recent-chat editor has unsaved textarea edits, clicking Facts/Archive/Reflections reaches this load path without the existing unsaved-change dialog used for role switches. The component load replaces the editor and hides the save controls; returning to Recent then reloads from disk and clears the dirty state, so those edits are silently lost. Gate component tab switches on memoryHasUnsavedChanges or reuse the same save/discard dialog before replacing the editor.
Useful? React with 👍 / 👎.
| .then(function (result) { | ||
| _componentDataCache[type] = result.data; | ||
| if (type === 'facts' || type === 'facts_archive') _renderMemoryFacts(container, result.data); | ||
| else if (type === 'reflections') _renderMemoryReflections(container, result.data); |
There was a problem hiding this comment.
Ignore stale component loads after selection changes
If a component request is in flight and the user quickly selects another role or switches back to Recent, this handler still renders the response for the old name/type into the shared editor. On a slow read this can show one character's facts under another selected role or overwrite the chat view after selectMemoryFile has already rendered it; add a request id or verify _activeMemoryTab and currentCatName before rendering, like the recent-file loader does.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
static/css/memory_browser.css (1)
3819-3824: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
word-break: break-word已经过时啦,换成overflow-wrap喵!Stylelint 报告这个属性值已废弃,规范推荐用
overflow-wrap: break-word(或anywhere)替代。🎀 建议修复
.memory-component-text { font-size: var(--memory-font-body, 14px); line-height: 1.55; color: var(--memory-text); - word-break: break-word; + overflow-wrap: break-word; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/css/memory_browser.css` around lines 3819 - 3824, Update the .memory-component-text rule by replacing the deprecated word-break: break-word declaration with overflow-wrap: break-word, while preserving the existing typography and color properties.Source: Linters/SAST tools
templates/memory_browser.html (1)
284-297: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick wintablist的aria-label忘记走i18n了喵!
role="tablist"上的aria-label="记忆组件"是硬编码中文,四个tab按钮本身都乖乖用了data-i18n,就这个容器漏网了。非中文语言下用屏幕阅读器的用户永远只会听到中文"记忆组件",笨蛋才会漏掉这种细节喵!🌐 建议修复
- <div class="memory-tab-bar" role="tablist" aria-label="记忆组件"> + <div class="memory-tab-bar" role="tablist" data-i18n-aria="memory.componentTabsAria" aria-label="记忆组件">需要在各 locale 文件里补一个类似
memory.componentTabsAria的key喵。Based on learnings, the repo's i18n aria-label convention is to use
data-i18n-aria="<key>"to drivearia-label, with a hardcoded fallback kept in place.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@templates/memory_browser.html` around lines 284 - 297, Update the memory tablist in the editor header to use the established data-i18n-aria convention with a new memory.componentTabsAria key while retaining the existing Chinese aria-label as fallback, and add the corresponding translation key to every locale file.Source: Learnings
static/js/memory_browser.js (1)
3976-4038: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win两个渲染函数长得跟双胞胎一样,合并一下喵~
_renderMemoryFacts和_renderMemoryReflections除了meta字段(source/entityvsstatus/entity)几乎完全一样,排序、空态、卡片DOM构建全是重复代码,笨蛋才会复制粘贴这么长一段喵!抽一个通用函数出来更省心。♻️ 建议重构
- function _renderMemoryFacts(container, data) { - container.innerHTML = ''; - if (!Array.isArray(data) || !data.length) { - container.innerHTML = '<div class="memory-component-empty">无数据</div>'; - return; - } - var sorted = data.slice().sort(function (a, b) { - return (b.created_at || '').localeCompare(a.created_at || ''); - }); - var list = document.createElement('div'); - list.className = 'memory-component-list'; - sorted.forEach(function (fact) { - var card = document.createElement('div'); - card.className = 'memory-component-item'; - var text = document.createElement('div'); - text.className = 'memory-component-text'; - text.textContent = fact.text || '(空)'; - card.appendChild(text); - var meta = document.createElement('div'); - meta.className = 'memory-component-meta'; - var parts = []; - if (fact.created_at) parts.push(fact.created_at.replace('T', ' ').slice(0, 19)); - if (fact.importance != null) parts.push('重要性: ' + fact.importance); - if (fact.source) parts.push('来源: ' + fact.source); - if (fact.entity) parts.push('实体: ' + fact.entity); - meta.textContent = parts.join(' | '); - card.appendChild(meta); - list.appendChild(card); - }); - container.appendChild(list); - } - - function _renderMemoryReflections(container, data) { - container.innerHTML = ''; - if (!Array.isArray(data) || !data.length) { - container.innerHTML = '<div class="memory-component-empty">无数据</div>'; - return; - } - var sorted = data.slice().sort(function (a, b) { - return (b.created_at || '').localeCompare(a.created_at || ''); - }); - var list = document.createElement('div'); - list.className = 'memory-component-list'; - sorted.forEach(function (ref) { - var card = document.createElement('div'); - card.className = 'memory-component-item'; - var text = document.createElement('div'); - text.className = 'memory-component-text'; - text.textContent = ref.text || '(空)'; - card.appendChild(text); - var meta = document.createElement('div'); - meta.className = 'memory-component-meta'; - var parts = []; - if (ref.created_at) parts.push(ref.created_at.replace('T', ' ').slice(0, 19)); - if (ref.importance != null) parts.push('重要性: ' + ref.importance); - if (ref.status) parts.push('状态: ' + ref.status); - if (ref.entity) parts.push('实体: ' + ref.entity); - meta.textContent = parts.join(' | '); - card.appendChild(meta); - list.appendChild(card); - }); - container.appendChild(list); - } + function _renderMemoryCards(container, data, metaBuilder) { + container.innerHTML = ''; + if (!Array.isArray(data) || !data.length) { + container.innerHTML = '<div class="memory-component-empty">无数据</div>'; + return; + } + var sorted = data.slice().sort(function (a, b) { + return (b.created_at || '').localeCompare(a.created_at || ''); + }); + var list = document.createElement('div'); + list.className = 'memory-component-list'; + sorted.forEach(function (item) { + var card = document.createElement('div'); + card.className = 'memory-component-item'; + var text = document.createElement('div'); + text.className = 'memory-component-text'; + text.textContent = item.text || '(空)'; + card.appendChild(text); + var meta = document.createElement('div'); + meta.className = 'memory-component-meta'; + meta.textContent = metaBuilder(item).join(' | '); + card.appendChild(meta); + list.appendChild(card); + }); + container.appendChild(list); + } + + function _renderMemoryFacts(container, data) { + _renderMemoryCards(container, data, function (fact) { + var parts = []; + if (fact.created_at) parts.push(fact.created_at.replace('T', ' ').slice(0, 19)); + if (fact.importance != null) parts.push('重要性: ' + fact.importance); + if (fact.source) parts.push('来源: ' + fact.source); + if (fact.entity) parts.push('实体: ' + fact.entity); + return parts; + }); + } + + function _renderMemoryReflections(container, data) { + _renderMemoryCards(container, data, function (ref) { + var parts = []; + if (ref.created_at) parts.push(ref.created_at.replace('T', ' ').slice(0, 19)); + if (ref.importance != null) parts.push('重要性: ' + ref.importance); + if (ref.status) parts.push('状态: ' + ref.status); + if (ref.entity) parts.push('实体: ' + ref.entity); + return parts; + }); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/js/memory_browser.js` around lines 3976 - 4038, 合并 `_renderMemoryFacts` 和 `_renderMemoryReflections` 中重复的排序、空态及卡片 DOM 构建逻辑,提取一个通用渲染函数;通过参数或回调分别配置 facts 的 `source` 元数据和 reflections 的 `status` 元数据,同时保留两者现有的 `entity`、文本、排序及空态行为,并让两个入口函数委托该通用实现。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@main_routers/memory_router.py`:
- Around line 1239-1243: 在读取文件的解析逻辑中为 ndjson 逐行解析和普通 JSON 整体解析增加
try/except,覆盖空文件、截断行及其他 JSONDecodeError;捕获后按同文件其他接口的既有风格返回明确的
JSONResponse“加载失败”错误,而不是让异常未处理导致 500。保留成功解析时现有的 {"data": ...} 返回结构。
- Around line 1225-1238: Update get_memory_component to validate name before
constructing the path, rejecting path separators and traversal components, and
perform a resolved-path boundary check against the resolved memory_dir before
reading the fixed component filename. Preserve the existing invalid-type and
missing-file responses while ensuring no requested path can escape memory_dir.
In `@static/js/memory_browser.js`:
- Around line 4040-4052: Update _loadComponentTab to validate the fetch response
before parsing and rendering it: reject or otherwise route any response where
r.ok is false to the existing failure handler, so HTTP errors display “加载失败”
instead of passing undefined result.data to the renderers. Preserve the current
successful response handling and caching behavior.
- Around line 4066-4074: Update the recent-tab branch of switchMemoryTab and/or
_selectMemoryFileInternal so the current memory file’s corresponding list item
is resolved via findMemoryRoleListItem (using current_filename/current_catname)
before calling selectMemoryFile. Preserve the existing selection while switching
back to “recent” so the sidebar li remains highlighted instead of being cleared.
In `@static/locales/es.json`:
- Around line 1827-1834: Update the Spanish locale’s clear translation in the
chat history strings from “Claro” to an action meaning “clear,” such as the
established Spanish equivalent “Borrar” or “Limpiar,” while leaving the
surrounding translations unchanged.
In `@static/locales/pt.json`:
- Around line 1827-1834: Update the Portuguese translation value for the "clear"
key in the locale entries to "Limpar" instead of "Claro", preserving the
existing command-style wording used by "save".
---
Nitpick comments:
In `@static/css/memory_browser.css`:
- Around line 3819-3824: Update the .memory-component-text rule by replacing the
deprecated word-break: break-word declaration with overflow-wrap: break-word,
while preserving the existing typography and color properties.
In `@static/js/memory_browser.js`:
- Around line 3976-4038: 合并 `_renderMemoryFacts` 和 `_renderMemoryReflections`
中重复的排序、空态及卡片 DOM 构建逻辑,提取一个通用渲染函数;通过参数或回调分别配置 facts 的 `source` 元数据和 reflections 的
`status` 元数据,同时保留两者现有的 `entity`、文本、排序及空态行为,并让两个入口函数委托该通用实现。
In `@templates/memory_browser.html`:
- Around line 284-297: Update the memory tablist in the editor header to use the
established data-i18n-aria convention with a new memory.componentTabsAria key
while retaining the existing Chinese aria-label as fallback, and add the
corresponding translation key to every locale file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 444c9c75-e15e-46c2-a9e2-e79ca87cae0a
📒 Files selected for processing (12)
main_routers/memory_router.pystatic/css/memory_browser.cssstatic/js/memory_browser.jsstatic/locales/en.jsonstatic/locales/es.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/pt.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/locales/zh-TW.jsontemplates/memory_browser.html
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
- 后端: 添加路径遍历防护与JSON解析异常处理 - 前端: 添加异步响应过时防护、硬编码字符串本地化、标签页切换前保存未修改内容、API错误日志记录、侧栏高亮恢复 - 区域: ja/ko 反思选项卡翻译修正
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
static/js/memory_browser.js (1)
4063-4098: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win切换标签页前取消旧请求,并等待保存成功喵!
saveCurrentMemory()是异步函数,但这里既未等待也未检查结果;保存失败后仍会进入只读标签页喵。另一个问题是从组件标签切回recent时没有递增_componentRequestId,未完成的组件请求会在第 4050 行仍被视为最新响应,并覆盖刚恢复的聊天记录视图喵。建议修复喵
- function switchMemoryTab(tab) { + async function switchMemoryTab(tab) { if (tab === _activeMemoryTab) return; + _componentRequestId++; + // Preserve unsaved edits when switching away from recent tab if (_activeMemoryTab === 'recent' && memoryHasUnsavedChanges) { - saveCurrentMemory(); + var saved = await saveCurrentMemory(); + if (!saved) return; }Based on learnings,已将该 JS 文件中紧邻的标签切换问题合并在此评论中喵。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/js/memory_browser.js` around lines 4063 - 4098, Update switchMemoryTab so it invalidates any in-flight component request when leaving a component tab by incrementing _componentRequestId before restoring the recent view. Await saveCurrentMemory() and only complete the tab switch after it succeeds; on save failure, keep the current tab and do not proceed to the requested read-only tab. Preserve the existing tab rendering and memory restoration behavior.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@static/js/memory_browser.js`:
- Around line 4063-4098: Update switchMemoryTab so it invalidates any in-flight
component request when leaving a component tab by incrementing
_componentRequestId before restoring the recent view. Await saveCurrentMemory()
and only complete the tab switch after it succeeds; on save failure, keep the
current tab and do not proceed to the requested read-only tab. Preserve the
existing tab rendering and memory restoration behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d0c4206f-769e-4000-9bd5-6965e8ed5d6f
📒 Files selected for processing (4)
main_routers/memory_router.pystatic/js/memory_browser.jsstatic/locales/ja.jsonstatic/locales/ko.json
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
🚧 Files skipped from review as they are similar to previous changes (2)
- static/locales/ko.json
- static/locales/ja.json
- CSS: word-break: break-word → overflow-wrap: break-word (已废弃属性) - HTML: tablist aria-label 添加 data-i18n-aria 本地化支持 - JS: 合并 _renderMemoryFacts/_renderMemoryReflections 为 _renderMemoryCards - JS: switchMemoryTab 改为 async、await saveCurrentMemory()、切换标签时递增 _componentRequestId 防止过期响应覆盖 - i18n: es.json clear: Claro→Borrar, pt.json clear: Claro→Limpar - i18n: 新增 memory.componentTabsAria 覆盖全部8个区域
快速连续点击两个组件标签时,较早的 switchMemoryTab 在 await saveCurrentMemory() 恢复后会覆盖较新的 _activeMemoryTab。现在在 await 后校验 _componentRequestId 是否被其它调用递增过,是则放弃本次切换。
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@static/js/memory_browser.js`:
- Around line 4048-4056: Update switchMemoryTab so clicking the currently active
tab while another tab switch is awaiting saveCurrentMemory still cancels or
supersedes the pending switch. Adjust the early-return logic and request
tracking around _componentRequestId and the pending target, preserving normal
no-op behavior when no switch is in progress.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e6e1c55f-3170-4695-a6b0-0d52fb9e74d4
📒 Files selected for processing (1)
static/js/memory_browser.js
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
- 将 ++_componentRequestId 提前到 switchMemoryTab 的 early return 之前, 防止 await save 期间点击当前标签时不递增序号导致旧请求继续执行 - 在所有 8 个 locale 文件中新增 memory.componentEmpty/empty/importance/ source/entity/status 共 6 个翻译键
pending recent-memory 请求使用独立的 memoryFileRequestId 守卫, 切换到组件标签时只 bump _componentRequestId 不够,旧响应仍会通过 自己的 guard 并覆盖组件视图。现在离开 recent 标签时同步递增 memoryFileRequestId 使其失效喵。
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@static/js/memory_browser.js`:
- Around line 4049-4050: Update the click handling around _componentRequestId
and _activeMemoryTab so repeated clicks on the currently active facts,
facts_archive, or reflections tab return without incrementing
_componentRequestId while _loadComponentTab is still pending. Only advance the
request token when an actual tab switch or save operation is being cancelled,
preserving the in-progress load and its response validation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1efac8be-af0b-4cd0-aa02-3bc784bae4ef
📒 Files selected for processing (9)
static/js/memory_browser.jsstatic/locales/en.jsonstatic/locales/es.jsonstatic/locales/ja.jsonstatic/locales/ko.jsonstatic/locales/pt.jsonstatic/locales/ru.jsonstatic/locales/zh-CN.jsonstatic/locales/zh-TW.json
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
🚧 Files skipped from review as they are similar to previous changes (8)
- static/locales/pt.json
- static/locales/zh-TW.json
- static/locales/es.json
- static/locales/ru.json
- static/locales/ja.json
- static/locales/zh-CN.json
- static/locales/ko.json
- static/locales/en.json
|
有完没完 😡 |
- 将 ++_componentRequestId 移至 same-tab 早退检查之后 - 仅在需要取消挂起保存时(同标签+最近+未保存)才递增序号 - 组件标签加载中重复点击直接返回,不会中断进行中的请求
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
static/js/memory_browser.js (2)
4107-4122: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win先尊重记忆导入冻结,再修改标签状态喵
原始
selectMemoryFile在 Line 3276 会拦截导入期间的普通文件切换喵,但这个包装器在调用原始函数前已经修改_activeMemoryTab、按钮状态并递增_componentRequestId喵。于是用户在事实或反思页触发一次被禁止的文件选择时,文件切换虽被拦截,界面却会错误回到“聊天记录”并取消当前组件请求喵。请把相同的导入守卫放在所有 UI 和令牌修改之前,或先委托原始函数确认允许切换后再同步标签状态喵。
🐾 建议修复
selectMemoryFile = async function (filename, li, catName, options) { + var allowDuringImport = !!(options && options.allowDuringImport); + if (window._memoryImportInProgress && !allowDuringImport) { + return _selectMemoryFileInternal(filename, li, catName, options); + } if (_activeMemoryTab !== 'recent') {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/js/memory_browser.js` around lines 4107 - 4122, Update the selectMemoryFile wrapper to honor the original import-freeze guard before changing _activeMemoryTab, tab button state, save-row visibility, or incrementing _componentRequestId. Delegate to _selectMemoryFileOrig first or replicate its guard so blocked selections leave the current tab and request state unchanged, while allowed selections retain the existing recent-tab synchronization.
4058-4061: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift不要把
_componentRequestId当成保存取消的钥匙喵
saveCurrentMemory()直接发了整份chatData去/api/memory/recent_file/save,只在返回后才用_componentRequestId决定这次标签切换要不要继续;后端也只用chat数据无版本/ETag 保护就直接atomic_write_json_async,所以旧的保存响应晚到仍会覆盖新快照,造成刚编辑的内容丢失喵。请按文件串行化保存,或使用前端文件请求 ID + 后端版本号/ETag 拒绝过期快照喵。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/js/memory_browser.js` around lines 4058 - 4061, 修复 saveCurrentMemory 与 /api/memory/recent_file/save 的并发保存竞态,不能仅依赖返回后的 _componentRequestId 判断是否继续。为 recent_file 保存按文件串行化请求,或引入前端请求 ID 配合后端版本号/ETag 校验,在写入前拒绝过期快照,确保旧响应不会覆盖更新后的 chatData。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@static/js/memory_browser.js`:
- Around line 4107-4122: Update the selectMemoryFile wrapper to honor the
original import-freeze guard before changing _activeMemoryTab, tab button state,
save-row visibility, or incrementing _componentRequestId. Delegate to
_selectMemoryFileOrig first or replicate its guard so blocked selections leave
the current tab and request state unchanged, while allowed selections retain the
existing recent-tab synchronization.
- Around line 4058-4061: 修复 saveCurrentMemory 与 /api/memory/recent_file/save
的并发保存竞态,不能仅依赖返回后的 _componentRequestId 判断是否继续。为 recent_file 保存按文件串行化请求,或引入前端请求 ID
配合后端版本号/ETag 校验,在写入前拒绝过期快照,确保旧响应不会覆盖更新后的 chatData。
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9002e0bf-64b8-4270-aa90-818bbd4030dd
📒 Files selected for processing (1)
static/js/memory_browser.js
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
1. selectMemoryFile包装器先检查导入冻结再修改标签状态 2. saveCurrentMemory加入保存请求ID校验防止并发保存竞态
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@static/js/memory_browser.js`:
- Around line 3370-3378: Update the save request error handler associated with
reqId in the recent-file save flow to first check whether reqId !==
_saveRequestId; stale requests must return false without calling showSaveStatus,
while the current request retains the existing failure-status behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0f78de2a-e681-4895-8114-fd2c8e19d245
📒 Files selected for processing (1)
static/js/memory_browser.js
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Project-N-E-K-O/N.E.K.O.-PC(manual)
| var reqId = ++_saveRequestId; | ||
| try { | ||
| const resp = await fetch('/api/memory/recent_file/save', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ filename: currentMemoryFile, chat: chatData }) | ||
| }); | ||
| const data = await resp.json(); | ||
| if (reqId !== _saveRequestId) return false; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
过期保存请求仍可能覆盖最新保存状态喵
reqId 只在成功响应路径校验;如果旧请求在新请求发起后进入 catch,它仍会调用 showSaveStatus(...保存失败...),把最新保存的状态短暂覆盖成过期错误喵。请在 catch 中同样先判断 reqId !== _saveRequestId,仅让当前请求更新 UI;旧请求仍可返回 false 终止对应的过期切换喵。
🐾 建议修复
} catch (e) {
+ if (reqId !== _saveRequestId) return false;
showSaveStatus(window.t ? window.t('memory.saveFailedGeneral') : '保存失败', false);
return false;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var reqId = ++_saveRequestId; | |
| try { | |
| const resp = await fetch('/api/memory/recent_file/save', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ filename: currentMemoryFile, chat: chatData }) | |
| }); | |
| const data = await resp.json(); | |
| if (reqId !== _saveRequestId) return false; | |
| var reqId = ++_saveRequestId; | |
| try { | |
| const resp = await fetch('/api/memory/recent_file/save', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ filename: currentMemoryFile, chat: chatData }) | |
| }); | |
| const data = await resp.json(); | |
| if (reqId !== _saveRequestId) return false; | |
| } catch (e) { | |
| if (reqId !== _saveRequestId) return false; | |
| showSaveStatus(window.t ? window.t('memory.saveFailedGeneral') : '保存失败', false); | |
| return false; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@static/js/memory_browser.js` around lines 3370 - 3378, Update the save
request error handler associated with reqId in the recent-file save flow to
first check whether reqId !== _saveRequestId; stale requests must return false
without calling showSaveStatus, while the current request retains the existing
failure-status behavior.
…iles; serialize save to prevent concurrent overwrite (Greptile P1) The i18n sync CI check requires identical diff hunk ranges across all locale files. es.json/pt.json had an extra hunk from fixing a translation typo (Claro→Borrar/Limpar) that the other 6 files don't have. Reverting these two hunks makes all 8 locale diffs match the reference en.json. Concurrent-save guard: chain saves via _savePromise so that only one save is in-flight per file at any time, preventing the backend from receiving writes out of order (older request overwriting newer data).
|
这项功能在testbench(tests/testbench)里有更完善的版本,不仅包含内容查看,还有embedding聚类以及自动化issue挖掘,目前仅供开发人员参考。 |
|
我们认为对一般用户暴露更多记忆细节适得其反。未来也许有日记之类的专门给用户看的功能会好一些。另外下版本我们会加入记忆卡片锻造 |
改动概述 / Summary
给记忆浏览页面加了标签页,可以以只读方式查看

facts.jsonfacts_archive.jsonreflections.json中的内容测试 / Testing
test_recent_chat_payload_caps.pytest_external_import_eta.pytest_character_memory_regression.py手动验证和比对文件没问题
Summary by CodeRabbit