Skip to content

feat(plugin): add Hitokoto quote plugin - #2474

Open
Alumin-Hydro wants to merge 4 commits into
Project-N-E-K-O:mainfrom
Alumin-Hydro:feat/hitokoto-plugin
Open

feat(plugin): add Hitokoto quote plugin#2474
Alumin-Hydro wants to merge 4 commits into
Project-N-E-K-O:mainfrom
Alumin-Hydro:feat/hitokoto-plugin

Conversation

@Alumin-Hydro

@Alumin-Hydro Alumin-Hydro commented Jul 26, 2026

Copy link
Copy Markdown

改动概述 / Summary

新增内置 hitokoto 插件:

  • 通过 Hitokoto 官方 HTTPS API 获取随机一言和每日一句,无需 API Key
  • random_quotedaily_quote 同时注册为普通插件入口和聊天可见的 LLM tools
  • 支持分类筛选、本地日历日缓存、跨线程/事件循环 single-flight 去重,以及每日首次聊天自然分享
  • 真实聊天输入通过 best-effort 用户话语事件桥接到插件进程;桥接失败不会中断正常对话
  • 通过 PluginStore 持久化运行时设置、每日缓存和问候尝试日期;每日缓存绑定 quote-affecting 设置身份,设置修改或恢复后不会复活旧缓存,已加入旧代 flight 的 leader / waiter 也会重试当前设置
  • 提供 Hosted UI 管理面板,可查看状态、修改设置、立即试用、测试 API、清缓存和恢复默认值
  • 面板与插件元数据覆盖 8 个仓库标准 locale:zh-CNzh-TWenjakoesptru
  • 新增针对 API 校验、错误映射、并发、缓存、持久化、每日问候、SDK 元数据和 Hosted UI 合同的单元测试

用户可见行为 / User-facing Behavior

  • 随机一言,可选 al 官方分类
  • 今日一言;启用缓存时,同一设备本地日期内复用同一句
  • 可选“每天首次聊天时自然分享”:插件通过 push_message(..., ai_behavior="respond") 把内容交给当前角色,由角色用自己的口吻表达
  • 两个聊天工具:
    • hitokoto_random_quote
    • hitokoto_daily_quote
  • 管理面板可调整:默认分类、请求超时、最大句长、每日缓存、每日首次聊天分享

回归报告 / Regression Report

本 PR 修改了 main_logic/core/__init__.pymain_logic/core/turn.py,用于把真实、非空的用户话语以 best-effort 方式发布到插件进程事件总线,从而让每日首次聊天分享在真实宿主链路中生效。

回归边界:

  • 不改变现有聊天输入、LLM 调用或 TTS 的返回值与控制流
  • 发布器构造失败、任务调度失败均静默降级;已创建但未调度的协程会被关闭
  • 空白输入和缺失 scheduler 时不发布事件
  • 由 focused 回归测试覆盖正常单次发布、构造异常、调度异常关闭与 agent-runtime 事件接收

不拆分理由 / Why Not Split

不拆分。插件的每日首次聊天能力必须依赖同一 PR 中的真实用户话语桥接;若拆开,插件会在生产聊天链路中无法触发。缓存设置身份、Hosted UI、i18n、测试和核心桥接共同构成一个可安装且可验证的完整功能。

scripts/check_pr_report.py --base upstream/main 当前结果:19 个变更文件、4 个计入上限、4 个高风险模块文件,均在仓库门限内。

测试 / Testing

自动化检查

  • uv run ruff check . — 通过
  • Analyze workflow 对应的项目检查脚本 — 通过:
    • async blocking、logging imports、tkinter、temperature、startup imports
    • prompt hygiene、LLM budget、i18n sync、docstring CJK
    • API trailing slash、module layering、docs paths、core contracts
  • uv run pytest plugin/tests/unit/plugins/test_hitokoto.py -q147 passed
  • Hitokoto + 用户话语桥接 focused 回归(3 个测试文件)— 193 passed
  • uv run pytest plugin/tests/integration/test_neko_plugin_cli_repo_plugins.py -q22 passed
  • uv run pytest plugin/tests -q(macOS,初版全量基线)— 2859 passed, 20 skipped, 9 failed
    • 9 个失败全部位于现有 galgame_* / study_companion* Windows 专用路径;没有 Hitokoto 失败
    • 官方 Plugin Tests workflow 固定运行在 windows-latest,用于覆盖这些 Win32 路径
  • scripts/check_pr_report.py --base upstream/main — 通过(19 files / 4 counted / 4 watched)
  • neko_plugin_cli check0 errors;仅有内置插件不适用的 standalone-repository scaffolding warnings
  • neko_plugin_cli buildinspectverify — 通过,payload_hash_verified=True(payload 8e98d640fcfadd2054d59db3733e3e9408fefef886b6af34f32961f56567a390
  • 构建包内已确认包含 Python、manifest、8 个 locale 和完整 static/index.html

宿主与界面验证

  • 隔离真宿主:插件发现、启动、Hosted UI 注册通过
  • main_server /api/tools 中确认两个 remote LLM tools 均已注册
  • 两条 LLM callback 均通过插件进程真实调用 Hitokoto API
  • Hosted UI /runs create / poll / export 链路通过
  • 浏览器验证:加载、修改、保存、刷新持久化、恢复默认、随机一言、今日缓存、API 测试、清缓存与重建缓存均通过
  • stopped 状态会显示明确错误,且 11 个操作控件全部禁用
  • Playwright 390 px 视口无水平溢出、裁切或重叠
  • Windows Steam 版已由贡献者手动导入并实际使用,核心功能与管理面板表现正常

隐私与外部服务 / Privacy and External Service

  • 仅请求公开的 https://v1.hitokoto.cn/,不上传聊天内容,不需要账号或密钥
  • 面板为每条一言提供 Hitokoto 来源链接
  • 日志只记录 quote ID、UUID、分类、长度、缓存状态和失败类型,不记录一言正文、用户输入或完整 API payload
  • 所有外部内容通过 textContent 渲染;未使用 innerHTMLeval 等危险 DOM sink

开发说明 / Development Note

实现过程使用了 AI 辅助;贡献者对代码、隐私、安全、许可证和测试结果负责,并完成了 Windows 端实际使用验证。

Summary by CodeRabbit

  • 新功能
    • 新增“一言”轻量插件:随机一言、每日一言、每日首次聊天分享注入,提供运行/API 状态与执行/测试入口、缓存查看与清理。
    • 增强多语言界面(中文、英文、日文、韩文、西班牙文、葡萄牙文、俄文及繁体中文)。
    • 新增用户语音/文本观测事件的发布与传递(含规范化与字段约束)。
  • 改进
    • 强化每日缓存与并发控制(避免同日重复拉取),提升首聊触发的准确性与稳定性。
    • 强化事件去重与校验,防止重复写入与异常数据传播。
  • 文档
    • 新增插件使用说明,补充 API 调用规则、隐私边界与本地验证方式。

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

新增 Hitokoto 一言插件,支持随机与每日引文、首次聊天问候、缓存并发控制、管理面板、多语言界面及测试;同时扩展用户话语事件的跨进程发布、校验、去重与上下文写入链路喵。

Changes

Hitokoto 插件功能

Layer / File(s) Summary
引文契约、请求与运行时
plugin/plugins/hitokoto/__init__.py, plugin/tests/unit/plugins/test_hitokoto.py
定义插件配置、引文解析、API 请求、生命周期、存储容错及跨事件循环客户端喵。
每日缓存与问候交付
plugin/plugins/hitokoto/__init__.py, plugin/tests/unit/plugins/test_hitokoto.py
实现每日缓存 single-flight、随机与每日引文、用户上下文轮询及每日首次聊天问候喵。
面板、配置与多语言界面
plugin/plugins/hitokoto/static/index.html, plugin/plugins/hitokoto/i18n/*, plugin/plugins/hitokoto/plugin.toml, plugin/plugins/hitokoto/README.md, plugin/tests/unit/plugins/test_hitokoto.py
提供管理面板、设置与缓存操作、API 测试、多语言资源、文档及静态校验喵。

用户话语观测事件

Layer / File(s) Summary
观测事件契约与中继发布
main_logic/agent_event_bus.py, main_logic/core/..., tests/test_user_utterance_bus_publish.py
新增事件边界、规范化发布函数、包级导出、本地镜像及异步跨进程调度喵。
Agent 事件校验与上下文去重
app/agent_server/api_runtime.py, tests/unit/test_agent_runtime_user_utterance_event.py
处理事件分发、字段校验、桶归一化、事件 ID 去重及插件上下文写入喵。

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

Poem

一言落入星光里,缓存守住每朝夕喵
并发来时不慌张,单飞护住小请求喵
用户话语过桥去,去重写入不重复喵
多语面板齐展开,Hitokoto 闪亮登场喵

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 标题准确概括了新增 Hitokoto 插件这一主变更喵
Description check ✅ Passed 描述包含模板要求的概述、回归报告、拆分理由和测试信息,内容完整喵

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.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 26, 2026
@greptile-apps

greptile-apps Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

新增 Hitokoto 一言插件,并完成真实用户话语到 Agent 插件运行时的跨进程桥接喵。

  • 提供随机一言、每日缓存、每日首次聊天分享及两个 LLM tools 喵。
  • 增加 Hosted UI、持久化设置、八种语言资源和完整插件元数据喵。
  • 为跨线程 single-flight、缓存代际、设置失效、事件桥接及 UI 合同补充回归测试喵。

Confidence Score: 5/5

当前实现看起来可以安全合并,先前报告的三个缓存代际问题均已修复喵。

当前代码会在设置变化时推进缓存代际、清除内存缓存并隔离旧持久化记录;旧 flight 的 leader 与 waiter 都会在返回前检测代际变化并使用最新设置重试,因此没有剩余的阻塞性失败喵。

Important Files Changed

Filename Overview
plugin/plugins/hitokoto/init.py 实现一言获取、设置持久化、代际化每日缓存、跨事件循环 single-flight、每日问候和管理入口;先前报告的旧代际返回路径已完整处理喵。
main_logic/core/turn.py 将经过裁剪和规范化的真实用户话语同步写入兼容镜像,并以 best-effort 方式异步转发至 Agent 进程喵。
main_logic/agent_event_bus.py 新增带严格类型、长度和时间戳校验的用户话语跨进程事件发布接口喵。
app/agent_server/api_runtime.py 验证、去重并将用户话语事件写入 Agent 所拥有的插件上下文存储喵。
plugin/plugins/hitokoto/static/index.html 新增 Hosted UI,用于状态查看、设置管理、API 测试、缓存清理和即时试用喵。
plugin/tests/unit/plugins/test_hitokoto.py 覆盖 API 校验、缓存竞争、设置代际、持久化、每日问候、SDK 元数据和 Hosted UI 合同喵。

Sequence Diagram

sequenceDiagram
  participant U as User
  participant M as Main conversation runtime
  participant A as Agent server
  participant P as Hitokoto plugin
  participant H as Hitokoto API
  U->>M: Submit text or voice utterance
  M->>M: Record compatibility mirror
  M-->>A: Publish bounded utterance event
  A->>A: Validate and deduplicate event
  A->>P: Append plugin memory observation
  P->>P: Detect first chat of local day
  P->>H: Fetch daily quote when cache misses
  H-->>P: Return quote
  P->>M: Push quote with respond behavior
  M-->>U: Character shares quote naturally
Loading

Reviews (4): Last reviewed commit: "fix(hitokoto): retry stale daily flights" | Re-trigger Greptile

Comment thread plugin/plugins/hitokoto/__init__.py Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (4)
plugin/plugins/hitokoto/__init__.py (3)

248-255: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

这里的 if uuid 分支永远为真喵~

Line 249 已经在 not uuid 时提前 return None 了,所以下面的三元判断和 https://hitokoto.cn/ 兜底根本走不到,属于死代码喵。

♻️ 简化建议
-    trace_url = (
-        f"https://hitokoto.cn?uuid={url_quote(uuid, safe='')}"
-        if uuid
-        else "https://hitokoto.cn/"
-    )
+    trace_url = f"https://hitokoto.cn?uuid={url_quote(uuid, safe='')}"
🤖 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 `@plugin/plugins/hitokoto/__init__.py` around lines 248 - 255, Remove the
unreachable conditional fallback in the trace_url assignment after the
validation in the surrounding Hitokoto parsing function. Since missing uuid
already returns None, always construct the URL using the quoted uuid while
preserving the existing URL encoding behavior.

1487-1523: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

内存缓存已经清掉了却返回 Err,状态和结果对不上喵~

Line 1496-1498 已经推进了 _cache_generation、清空 _memory_daily 并置 _ignore_persisted_daily = True,之后持久化删除失败才 return Err。调用方看到失败,但实际内存缓存确实已清除,面板上会显示"清除失败"却又拿不到旧缓存喵。

考虑返回 Ok({"cleared": True, "persisted": 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 `@plugin/plugins/hitokoto/__init__.py` around lines 1487 - 1523, Update
clear_daily_cache so a failed _store_delete does not return Err after the
in-memory cache has already been cleared. Preserve the cleared state and return
Ok with cleared=True, persisted=False, and the existing previous_date when
persistence fails; ensure callers can distinguish the incomplete persistence
outcome and present it as such.

608-684: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

三个 store 包装器几乎一模一样,抽一个小 helper 会更清爽喵~

_store_get / _store_set / _store_delete 的 try/except + Err + 非 Ok 三段判定逻辑完全重复,只有操作名和失败返回值不同。可以抽成一个 _store_call(op_name, key, coro_factory, failure_value) 之类的内部方法,未来再加 store 操作时也不用复制粘贴喵。(非阻塞项,你要是嫌麻烦本喵也不逼你啦~)

🤖 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 `@plugin/plugins/hitokoto/__init__.py` around lines 608 - 684, Extract the
duplicated store operation handling from _store_get, _store_set, and
_store_delete into a shared internal helper such as _store_call, parameterized
by operation name, key, coroutine factory, and failure value. Preserve each
wrapper’s existing disabled-store behavior, success value handling for
_store_get, and operation-specific failure returns and warning messages.
plugin/tests/unit/plugins/test_hitokoto.py (1)

899-921: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

跨线程测试靠固定 delay/timeout,在慢 CI 上有点容易翻车喵~

delay=0.05barrier.wait(timeout=1)join(timeout=2) 这套组合在负载高的 runner 上可能偶发失败。把 join 超时放宽(比如 10s)能明显降低 flaky 概率,同时不影响正常情况下的执行速度喵。

Also applies to: 1092-1115

🤖 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 `@plugin/tests/unit/plugins/test_hitokoto.py` around lines 899 - 921, In
test_cross_loop_daily_calls_share_single_flight and the additionally affected
cross-thread test, increase the thread join timeout from 2 seconds to
approximately 10 seconds while preserving the existing barrier timeout,
assertions, and normal execution behavior.
🤖 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 `@plugin/plugins/hitokoto/static/index.html`:
- Around line 713-715: 扩展 SUPPORTED_LOCALES 和 normalizeLocale(),纳入已发布的 es、ja、ko
资源,并单独支持 zh-TW;确保这些 locale 能被规范化并选择对应语言包,而不是回退到 zh-CN,同时保留现有 zh-CN 和 en 的行为。
- Around line 1112-1118: Update the error/finalization flow around the initial
panel-state request so a failed first load does not leave the panel permanently
disabled. In the catch/finally logic near setBusy and initialLoadSucceeded,
clear the busy state after failure while preserving the existing busy behavior
for successful initial loading and subsequent requests.

---

Nitpick comments:
In `@plugin/plugins/hitokoto/__init__.py`:
- Around line 248-255: Remove the unreachable conditional fallback in the
trace_url assignment after the validation in the surrounding Hitokoto parsing
function. Since missing uuid already returns None, always construct the URL
using the quoted uuid while preserving the existing URL encoding behavior.
- Around line 1487-1523: Update clear_daily_cache so a failed _store_delete does
not return Err after the in-memory cache has already been cleared. Preserve the
cleared state and return Ok with cleared=True, persisted=False, and the existing
previous_date when persistence fails; ensure callers can distinguish the
incomplete persistence outcome and present it as such.
- Around line 608-684: Extract the duplicated store operation handling from
_store_get, _store_set, and _store_delete into a shared internal helper such as
_store_call, parameterized by operation name, key, coroutine factory, and
failure value. Preserve each wrapper’s existing disabled-store behavior, success
value handling for _store_get, and operation-specific failure returns and
warning messages.

In `@plugin/tests/unit/plugins/test_hitokoto.py`:
- Around line 899-921: In test_cross_loop_daily_calls_share_single_flight and
the additionally affected cross-thread test, increase the thread join timeout
from 2 seconds to approximately 10 seconds while preserving the existing barrier
timeout, assertions, and normal execution 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: e7255bbe-dec1-407f-b31b-94fc5a31864b

📥 Commits

Reviewing files that changed from the base of the PR and between 3412ed5 and 40fb226.

📒 Files selected for processing (13)
  • plugin/plugins/hitokoto/README.md
  • plugin/plugins/hitokoto/__init__.py
  • plugin/plugins/hitokoto/i18n/en.json
  • plugin/plugins/hitokoto/i18n/es.json
  • plugin/plugins/hitokoto/i18n/ja.json
  • plugin/plugins/hitokoto/i18n/ko.json
  • plugin/plugins/hitokoto/i18n/pt.json
  • plugin/plugins/hitokoto/i18n/ru.json
  • plugin/plugins/hitokoto/i18n/zh-CN.json
  • plugin/plugins/hitokoto/i18n/zh-TW.json
  • plugin/plugins/hitokoto/plugin.toml
  • plugin/plugins/hitokoto/static/index.html
  • plugin/tests/unit/plugins/test_hitokoto.py
🔗 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)

Comment thread plugin/plugins/hitokoto/static/index.html Outdated
Comment thread plugin/plugins/hitokoto/static/index.html Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40fb2260c7

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugin/plugins/hitokoto/__init__.py
Comment thread plugin/plugins/hitokoto/static/index.html Outdated
Comment thread plugin/plugins/hitokoto/static/index.html Outdated
@Alumin-Hydro
Alumin-Hydro requested a review from wehos as a code owner July 26, 2026 09:07
Comment thread plugin/plugins/hitokoto/__init__.py Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
plugin/tests/unit/plugins/test_hitokoto.py (2)

2261-2296: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

契约断言直接绑死源码字符串,改一个空格就红一片喵。

这一串 in submit_handler 断言等于把 JS 实现细节抄了一遍,后续任何无害的重构(换变量名、换行、Prettier 一跑)都会误报。下面 test_static_panel_submits_valid_settings_with_exact_payload 已经用真实执行覆盖了同样的行为,这里可以只保留少量真正的契约点(例如 checkValidity 先于 runAction),其余交给行为测试就够啦~ 当然,如果是刻意要防回归,保持现状也不是不行喵。

🤖 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 `@plugin/tests/unit/plugins/test_hitokoto.py` around lines 2261 - 2296, 简化
test_static_panel_settings_submit_contract,移除对具体变量名、格式、日志及实现字符串的逐项断言,避免无害重构导致测试脆弱;仅保留必要的提交契约断言,例如
event.currentTarget.checkValidity() 在 void runAction 之前执行,并依赖下方真实执行测试覆盖有效载荷行为。

41-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

内联脚本提取方式有点脆弱喵~

rsplit("<script>", maxsplit=1) 取的是最后一个 script 块,一旦 index.html 以后追加尾部脚本(哪怕只是个 analytics 片段),所有 run_panel_js 场景就会静默地跑错代码块,报错信息还会非常难懂喵。加一条断言把假设钉死会舒服很多的说~

🧷 建议加一层保护
     html = PANEL_HTML.read_text(encoding="utf-8")
+    assert html.count("<script>") == 1, "面板只应有一个内联脚本块"
     inline_script = html.rsplit("<script>", maxsplit=1)[1].split(
🤖 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 `@plugin/tests/unit/plugins/test_hitokoto.py` around lines 41 - 55, 在
run_panel_js 中为内联脚本提取增加显式断言,验证 PANEL_HTML 仍符合“最后一个 script 块就是面板脚本”的假设;若 script
块数量或结构发生变化,应立即以清晰错误失败,而不是继续静默执行错误脚本。保留现有脚本提取和测试暴露逻辑。
main_logic/core/__init__.py (1)

88-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

注释清单忘记加新名字啦,笨蛋主人喵!

第171-188行的注释专门枚举了所有需要通过 _core_facade.<attr> late-binding 读取(而非普通 rebind)的 patch 目标,比如 publish_analyze_request_reliablypublish_voice_transcript_observed_best_effort 等。现在新加的 publish_user_utterance_observed(本行)也是走同一套机制(turn.py 里用 _core_facade.publish_user_utterance_observed 读取,测试里也确实 monkeypatch.setattr(core_facade, "publish_user_utterance_observed", ...)),但没被列进那份清单,容易误导后来者喵。

📝 建议补充注释
 # get_tts_worker, publish_analyze_request_reliably and
-# publish_voice_transcript_observed_best_effort therefore keeps working
+# publish_voice_transcript_observed_best_effort, publish_user_utterance_observed
+# therefore keeps working
 # through a different mechanism: the mixins do not from-import those symbols
🤖 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 `@main_logic/core/__init__.py` at line 88, 在第171-188行列举 `_core_facade.<attr>`
late-binding patch 目标的注释清单中补充
`publish_user_utterance_observed`,并保持其余注释内容和导出代码不变。
main_logic/agent_event_bus.py (1)

365-407: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

发布侧和接收侧的校验逻辑重复了两遍,笨蛋主人快去合并一下喵! 两处都对 event_id/timestamp/lanlan_name/content/is_voice 做了完全相同的类型检查、长度上限判断与 strip 归一化,共享同一组 USER_UTTERANCE_*_MAX_CHARS 常量却各写了一份校验代码,以后改规则容易漏掉一处、两侧行为分叉喵。

  • main_logic/agent_event_bus.py#L365-L407:把这段校验+归一化抽成一个共享纯函数(例如 _normalize_and_validate_user_utterance_fields(event_id, timestamp, lanlan_name, content, is_voice) -> Optional[dict]),本函数只负责调用它再转发给 publish_session_event
  • app/agent_server/api_runtime.py#L196-L307_handle_user_utterance_observed 改为 import 并复用 main_logic.agent_event_bus 里抽出的同一个校验函数,只保留去重与写入 bucket 这部分接收侧特有的逻辑。
♻️ 抽取共享校验函数示意
# main_logic/agent_event_bus.py
def normalize_user_utterance_fields(
    *, event_id, timestamp, lanlan_name, content, is_voice,
) -> Optional[dict]:
    """Shared validation/normalization for the user_utterance_observed contract."""
    if type(event_id) is not str:
        return None
    normalized_event_id = event_id.strip()
    if not normalized_event_id or len(normalized_event_id) > USER_UTTERANCE_EVENT_ID_MAX_CHARS:
        return None
    if type(timestamp) is not float or not math.isfinite(timestamp) or timestamp <= 0:
        return None
    if type(lanlan_name) is not str:
        return None
    normalized_lanlan = lanlan_name.strip() or "default"
    if len(normalized_lanlan) > USER_UTTERANCE_LANLAN_MAX_CHARS:
        return None
    if type(content) is not str:
        return None
    normalized_content = content.strip()
    if not normalized_content or len(normalized_content) > USER_UTTERANCE_CONTENT_MAX_CHARS:
        return None
    if type(is_voice) is not bool:
        return None
    return {
        "event_id": normalized_event_id,
        "timestamp": timestamp,
        "lanlan_name": normalized_lanlan,
        "content": normalized_content,
        "is_voice": is_voice,
    }

两处调用方各自消费返回的 dict(或 None 表示拒绝),行为与现状完全一致,只是消灭了重复代码喵。

🤖 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 `@main_logic/agent_event_bus.py` around lines 365 - 407, Extract the duplicated
user-utterance validation and normalization into a shared pure function in
main_logic/agent_event_bus.py around publish_user_utterance_observed, returning
normalized fields or None while preserving all existing checks and defaults.
Update publish_user_utterance_observed to call it before publish_session_event,
and update app/agent_server/api_runtime.py lines 196-307 in
_handle_user_utterance_observed to import and reuse the shared function,
retaining only receiver-specific deduplication and bucket-writing logic.
🤖 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_logic/core/turn.py`:
- Around line 692-709: 将 _core_facade.publish_user_utterance_observed 的协程构造与
fire_task 调度统一纳入同一个 try 块,确保属性访问或调用异常也被 best-effort 处理并记录 debug
日志,同时仅在协程已成功创建且可关闭时执行 close,避免异常向 handle_input_transcript 冒泡;在
tests/test_user_utterance_bus_publish.py 补充 fire_task 抛异常后协程被正确 close
且异常不外传的回归测试。

---

Nitpick comments:
In `@main_logic/agent_event_bus.py`:
- Around line 365-407: Extract the duplicated user-utterance validation and
normalization into a shared pure function in main_logic/agent_event_bus.py
around publish_user_utterance_observed, returning normalized fields or None
while preserving all existing checks and defaults. Update
publish_user_utterance_observed to call it before publish_session_event, and
update app/agent_server/api_runtime.py lines 196-307 in
_handle_user_utterance_observed to import and reuse the shared function,
retaining only receiver-specific deduplication and bucket-writing logic.

In `@main_logic/core/__init__.py`:
- Line 88: 在第171-188行列举 `_core_facade.<attr>` late-binding patch 目标的注释清单中补充
`publish_user_utterance_observed`,并保持其余注释内容和导出代码不变。

In `@plugin/tests/unit/plugins/test_hitokoto.py`:
- Around line 2261-2296: 简化
test_static_panel_settings_submit_contract,移除对具体变量名、格式、日志及实现字符串的逐项断言,避免无害重构导致测试脆弱;仅保留必要的提交契约断言,例如
event.currentTarget.checkValidity() 在 void runAction 之前执行,并依赖下方真实执行测试覆盖有效载荷行为。
- Around line 41-55: 在 run_panel_js 中为内联脚本提取增加显式断言,验证 PANEL_HTML 仍符合“最后一个 script
块就是面板脚本”的假设;若 script 块数量或结构发生变化,应立即以清晰错误失败,而不是继续静默执行错误脚本。保留现有脚本提取和测试暴露逻辑。
🪄 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: f40c4c36-a0ac-4a56-92d1-711e1a05e57e

📥 Commits

Reviewing files that changed from the base of the PR and between 40fb226 and 18afeb1.

📒 Files selected for processing (17)
  • app/agent_server/api_runtime.py
  • main_logic/agent_event_bus.py
  • main_logic/core/__init__.py
  • main_logic/core/turn.py
  • plugin/plugins/hitokoto/__init__.py
  • plugin/plugins/hitokoto/i18n/en.json
  • plugin/plugins/hitokoto/i18n/es.json
  • plugin/plugins/hitokoto/i18n/ja.json
  • plugin/plugins/hitokoto/i18n/ko.json
  • plugin/plugins/hitokoto/i18n/pt.json
  • plugin/plugins/hitokoto/i18n/ru.json
  • plugin/plugins/hitokoto/i18n/zh-CN.json
  • plugin/plugins/hitokoto/i18n/zh-TW.json
  • plugin/plugins/hitokoto/static/index.html
  • plugin/tests/unit/plugins/test_hitokoto.py
  • tests/test_user_utterance_bus_publish.py
  • tests/unit/test_agent_runtime_user_utterance_event.py
🔗 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)
  • plugin/plugins/hitokoto/i18n/en.json
  • plugin/plugins/hitokoto/i18n/es.json
  • plugin/plugins/hitokoto/i18n/zh-TW.json
  • plugin/plugins/hitokoto/i18n/pt.json
  • plugin/plugins/hitokoto/i18n/zh-CN.json
  • plugin/plugins/hitokoto/i18n/ja.json
  • plugin/plugins/hitokoto/static/index.html
  • plugin/plugins/hitokoto/init.py

Comment thread main_logic/core/turn.py Outdated
Invalidate settings-sensitive caches across restarts, harden the user utterance relay boundary, and make Hosted UI tests decode Node output as UTF-8 on Windows.\n\nVerified by two independent read-only reviews, focused tests, integration tests, Ruff, package verification, and the PR report gate.
Comment thread plugin/plugins/hitokoto/__init__.py
Retry current-generation daily quote work when settings or cache invalidation makes an in-flight leader or waiter stale, without swallowing caller cancellation.
@MingTianSang

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 01ae08321e

ℹ️ 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".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants