Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ViNote 项目导航

Python/FastAPI + React/TypeScript/Vite,SQLite 记录索引,Markdown 保存内容。

- 后端入口:`backend/main.py`;生命周期:`backend/core/lifecycle.py`。
- 任务产物:`backend/routers/tasks.py` → `backend/services/note_generator.py`。
- 存储:`backend/services/note_repository.py`、`backend/routers/storage.py`。
- 问答:`backend/routers/qa.py`、`backend/services/video_qa_service.py`。
- [核心流程](docs/core.md)、[外部能力](docs/_external-capabilities.md);前端见 `web/CLAUDE.md`。

发布入口为 README、start.sh/start.bat、Dockerfile;无已配置极库云归属,不要求迁移。私人环境配置不提交。
11 changes: 10 additions & 1 deletion .claude/artifacts/_api-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@

1. 历史页调用 `POST /api/notes/{short_id}/regenerate`。
2. `targets` 可包含 `transcript`、`summary`、`mindmap`。
3. 文件使用临时文件原子替换,全部完成后再更新 SQLite 文件引用。
3. 对同一笔记加锁,所有目标生成成功后暂存并备份旧文件,再更新 SQLite 文件引用和全文索引;异常时恢复旧文件和事务,AI 降级结果不覆盖已有笔记。这不是跨文件系统/数据库的断电原子事务。

## 任务完成状态

1. 处理中状态保存在单进程内存,并通过 `GET /api/task-status/{task_id}` 或 SSE 返回。
2. 完成后笔记、完整任务 UUID、产物索引和固定降级提示写入 SQLite,再释放内存状态。
3. 客户端继续使用原始任务 UUID 查询时,服务端按 `task_id` 回读 SQLite,并保持 `warnings`、完成文案和产物内容一致。

## 可靠性与使用体验

- `GET /api/task-status/{task_id}` 返回实际 `transcript_filename`、`summary_filename`;恢复完成任务时按 SQLite 文件引用读取,不按标题猜测。前端兼容旧响应,下载请求非 2xx 必须显示失败。
- 任务/批次恢复仅在浏览器存储 ID;先读取当前状态,SSE 中断后串行轮询。服务端重启会中断尚未完成的任务,不自动续跑。
- `GET /api/tasks/completed?search=...` 检索标题、原始转录、整理稿与摘要;查询上限 500 字符、页大小 1–200。旧记录启动时幂等回填 SQLite 内容索引。
- 清理按完整笔记选择候选;活跃任务、近期产物或删除失败时保留对应记录,返回 `skipped_note_ids` / `failed_note_ids`。
- `GET /api/qa/sessions` 提供最近会话;问答按问题召回相关分段,每篇来源分配上下文预算,总资料 24000 字符,历史 12000 字符。答案仅保留有效来源编号,但不保证模型事实正确。
- API 默认本机访问;Host 白名单与状态变更请求 Origin 校验在业务路由前执行。此应用仍无多用户身份隔离。
2 changes: 2 additions & 0 deletions .claude/artifacts/_commit-metrics.jsonl

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions .claude/artifacts/_e2e-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@
| 无 LLM 本地视频 | 未配置 OpenAI,提交带内嵌字幕的本地视频 | 任务完成并生成非空原文、整理稿和摘要,返回固定降级提示 |
| 完成状态回读 | 任务完成并从内存转入 SQLite 后,继续用完整任务 UUID 查询 | `warnings`、降级完成文案和 32 位笔记 ID 保持一致 |
| 任务产物清理 | 删除刚完成的任务存储 | 仅删除该笔记的原文、整理稿、摘要和翻译,不影响其他记录 |

## reliability-usability(本地隔离验收)

当前执行结果与边界见 `features/reliability-usability/verification.md`;历史表格不作为本轮全部外部能力均已执行的证明。

| 验收项 | 执行证据 | 预期 |
| --- | --- | --- |
| AC-1 / AC-4 | `test_text_quality.py`、`test_qa_retrieval.py` | 分段不丢字;摘要输入有界;多来源及尾部召回;卡片首中尾采样 |
| AC-2 | `test_storage_regeneration.py` | 清理整条笔记;文件/数据库/全文索引异常恢复;同笔记操作串行化 |
| AC-3 / AC-5 | `browser_smoke.py`、前端9条回归 | 实际文件名下载;失败明确;任务/批次恢复;持久会话继续及确认删除 |
| AC-6 | `test_search_downloads.py`、`browser_smoke.py` | 标题/原文/摘要检索、旧数据回填、分页及兼容模式 |
| AC-7 / AC-8 | `test_asr_config.py`、`test_launchers.py`、发布校验 | provider默认与目录优先;缓存正确失效;运行包排除测试与私人数据 |
| 本地业务联调 | `media_smoke.py` | 合成字幕视频完成笔记、存储、索引与下载;未配置 LLM 时明确提示降级 |
101 changes: 101 additions & 0 deletions .claude/artifacts/_security-audit.md

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions .claude/artifacts/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,31 @@
| 归因口径 | Harness 默认 AI |
| 证据来源 | refs/notes/ai-engineering-harness;AgentBlame(可选增强) |
<!-- ai-engineering-harness:core-stability:end -->

<!-- ai-engineering-harness:reliability-usability:start -->
### reliability-usability — 2026-09-05

| 效能项 | 数据 |
|---|---|
| 需求摘要 | 优化 ViNote 项目,保留核心功能,改善内容完整性、数据一致性、检索问答和安装体验。;修复阻塞main合并的测试竞态 |
| 验收情况 | 仅修改测试,无业务运行时变动 |
| 验证结论 | 前端9条测试连续20轮通过,lint通过;同一代码89项后端、9项前端、8场景浏览器、媒体HTTP链路、lint、build及发布包校验通过。 |
| 提交记录 | 0d45bbcf feat(vinote): 优化长文处理、任务恢复和全文搜索;5881e38d test(vinote): 修复 Windows 轮询回归的定时竞态 |
| 开发 Step | implement, windows-test |
| 新增代码行数 | 1588 |
| 删除代码行数 | 535 |
| 测试新增行数 | 1617 |
| 代码文件触达数 | 43 |
| Input Tokens | 14830161 |
| Cached Input Tokens | 14255232 |
| Uncached Input Tokens | 574929 |
| Output Tokens | 65146 |
| Total Tokens | 14895307 |
| 开发时长(秒) | 2471.116 |
| AI代码行 | 3205 |
| Human代码行 | 0 |
| Unknown代码行 | 0 |
| AI代码占比(记录口径) | 100.0% |
| 归因口径 | Harness 默认 AI(非精确测量) |
| 证据来源 | refs/notes/ai-engineering-harness |
<!-- ai-engineering-harness:reliability-usability:end -->
14 changes: 14 additions & 0 deletions .claude/artifacts/features/reliability-usability/_manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 研发索引

- feature: reliability-usability
- branch: codex/reliability-and-usability
- base: b1f2a994f2f3bc7cafc3a8ec9648d7978b87ae9e
- risk: STRICT / fullstack
- route: 20260905T022746Z-5180
- status: verified-local
- specification: requirements.md
- plan: dev-plan.md
- execution: task-list.md
- verification: verification.md

原目录不动;本轮不自动推送、合并或关闭 issue。
9 changes: 9 additions & 0 deletions .claude/artifacts/features/reliability-usability/dev-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 实施计划

1. 无损分段、有界摘要、全文卡片材料、多来源召回;用纯函数测试。
2. 按笔记协调删除/重生成,先生成暂存再提交,异常回滚;不承诺断电级跨 SQLite/文件系统事务。
3. 增量文件名契约与 SQLite 内容索引/回填;保持旧响应兼容。
4. 前端恢复只存 ID;会话列表和确认删除;验证正常/失败/恢复。
5. 可选依赖、启动缓存、Windows call、回归 CI。

验收:隔离 unittest、发布静态检查、前端 lint/build、本地启动与浏览器;真实模型/ASR、Windows 实机分开说明。
14 changes: 14 additions & 0 deletions .claude/artifacts/features/reliability-usability/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ViNote 可靠性与使用体验

用户已采纳审查建议;不更换框架、不移除业务能力、不增加外部向量服务、不提交私人配置。

- AC-1:中英长段落、无标点、末句分段不丢正文。
- AC-2:按年龄清理只删除候选笔记;异常保留文件和记录;重生成失败保留旧产物。
- AC-3:任务返回真实下载文件名,下载失败不提示成功。
- AC-4:问答召回多来源及相关后段;摘要请求有界;卡片材料抽样覆盖首、中、尾,不再只取开头。
- AC-5:刷新/切换/断网恢复任务和批次;会话可列出、继续、删除。
- AC-6:搜索标题、正文、摘要,旧记录回填;筛选分页保持兼容。
- AC-7:默认不强制可选 ASR 依赖;Windows 脚本可继续执行;避免不必要 npm ci。
- AC-8:开发回归与 CI,不把测试/私人配置/模型缓存打入运行包。

EXE 是独立交付需求,需 Windows 构建实测;本轮先优化启动和依赖,不宣称有可用 EXE。
10 changes: 10 additions & 0 deletions .claude/artifacts/features/reliability-usability/task-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 任务清单

- [x] 内容完整性、摘要、卡片、问答(AC-1、AC-4)
- [x] 清理与重生成一致性(AC-2)
- [x] 文件名契约与全文搜索(AC-3、AC-6)
- [x] 前端下载、恢复、会话管理(AC-3、AC-5)
- [x] 安装与回归 CI 配置(AC-7、AC-8;Windows 实机/远程 CI 未执行)
- [x] 本地集成验收、隐私检查、地图同步

验收详情见 verification.md;真实 LLM/ASR、Docker 构建和 Windows 实机不混同为已验证。
44 changes: 44 additions & 0 deletions .claude/artifacts/features/reliability-usability/verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 本地验收记录(2026-09-05)

基于 `origin/main` 的 `b1f2a994f2f3bc7cafc3a8ec9648d7978b87ae9e`,分支 `codex/reliability-and-usability`。使用独立工作树、临时 SQLite 和合成资料;原工作区未提交修改保持原样。本轮未提交或推送 Git,也未改 issue 状态。

## 已验证

| 范围 | 结果 | 证据 |
| --- | --- | --- |
| 后端与启动器回归 | 89/89 通过,无跳过(macOS / Python 3.12) | `python -m unittest discover -s tests -v`;分段、摘要、卡片、问答召回、存储/索引回滚、下载路径、ASR 配置、启动缓存、包内容 |
| 前端逻辑 | 9/9 通过 | `cd web && npm test`;文件名、HTTP 错误、ID 存储、流中止、断线轮询、串行请求 |
| 浏览器业务 | 8/8 通过,无未捕获页面异常 | `tests/browser_smoke.py`,本机 Chrome headless;真实 API/文件/SQLite,问答提供方为可控替身 |
| 本地视频业务 | 通过,约 1.1 秒 | `tests/media_smoke.py`;FFmpeg 生成内嵌字幕视频 → 实际 HTTP 提交 → 字幕提取 → 明确 AI 降级的笔记 → SQLite/全文检索 → 三类下载;原视频保留 |
| 静态与构建 | 通过 | `scripts/validate_release.py`、`bash -n start.sh`、`uv lock --check --offline`、`npm run lint`、`npm run build`、`git diff --check` |
| 后端 wheel | 构建与内容校验通过 | `uv build --wheel` 与 `scripts/validate_release.py --wheel ...`;不含测试、私人配置、Demo 初始化文件。源码 Demo 保留,wheel 不是带前端的完整安装器 |
| 默认依赖 | 0 已知漏洞 | `pip-audit --path .venv/lib/python3.12/site-packages`;本项目非 PyPI 包被跳过,不外推可选依赖 |
| 前端生产依赖 | 0 严重 / 0 高危 / 2 中危 | 官方 npm registry 审计;React Router 6 的已有开放跳转及 SSR hydration 公告,未进行跨主版本升级 |
| 发布内容隐私 | 暂存变更扫描未命中真实密钥、公司邮箱或私人 LLM 地址模式 | 未读取或加入私人 `.env`;测试使用合成资料;测试、模型缓存、运行数据和本地工具不进入 Docker 运行镜像 |

## 浏览器场景

1. 按已存任务 ID 恢复已完成笔记,下载原有实际文件名的整理稿、摘要。
2. 模拟下载 404:显示失败,不显示“文件已下载”。
3. 处理中刷新、SSE 连接中断后恢复状态轮询,不重复 POST 创建任务。
4. 刷新恢复批次进度。
5. 用正文尾部词、摘要词检索历史,以及不存在关键词的空态。
6. 最近会话继续与刷新;基于超过 8 万字资料尾部事实提问,真实召回/提示词/流式消息路径及 SQLite 保存通过(假模型断言资料含尾部证据)。
7. 删除失败保留会话与数据库消息,确认成功后实际删除。
8. 非法分页大小返回 422。

## 使用变化与验证边界

- 现有核心功能和数据库记录格式保留;全文索引是新增表,旧笔记启动幂等回填。无需外部向量数据库。
- 默认仅安装 Whisper 依赖,FunASR/Qwen ASR/ANP 按配置选择。`ASR_MODEL` 留空采用对应默认值;旧配置切换 provider 时需清空或同步修改模型名。Whisper 已按 `ASR_MODEL_DIR` 优先加载。
- 浏览器恢复不等于后端重启续跑;服务端重启仍中断未完成任务。
- 资料检索与卡片采样改善后段覆盖,不保证长文所有事实都进入提示词,更不承诺真实模型回答百分之百正确。
- 已验证普通异常/取消时的文件、数据库和全文索引回滚;未承诺断电恢复,也未支持多进程共享笔记锁。
- 没有调用私人 LLM;没有下载或运行真实 ASR 模型。真实模型质量、音频转写效果、外网视频源和 ANP 跨机器调用需另测。
- Windows 启动器与 GitHub Actions 双平台配置已静态检查;未运行 Windows 实机、远程 CI、Docker 构建或 EXE。Windows 缺少创建 symlink 权限时,CI 只跳过两条符号链接专用测试并明确提示。
- 默认面向可信本机单用户;局域网/代理访问需要配置 Host 白名单及入口访问控制。通用媒体 URL 没有完整出网隔离,不宣称可直接开放给公网非可信用户。
- Vite 构建有两处大 chunk 提示;Markmap 为独立懒加载,未调整阈值隐藏告警。

## 重跑可选浏览器/媒体验收

先完成默认依赖安装与前端构建。在独立测试终端运行 `uv run --no-sync python -m tests.smoke_server`,它只绑定本机 18999 端口并创建临时数据。另一个终端使用已安装 Playwright 的 Python 运行 `python tests/browser_smoke.py`,再运行 `uv run --no-sync python tests/media_smoke.py`。前者默认使用本机 Chrome(可通过 `VINOTE_BROWSER_CHANNEL=chromium` 选择 Playwright Chromium),后者需要 FFmpeg。每轮重新启动测试服务以重置资料,结束后停止服务。
11 changes: 11 additions & 0 deletions .claude/docs/_external-capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 外部能力

配置只引用环境变量,不记录私人地址与凭据。依据调用源码:

- `backend/core/ai_client.py`、`backend/config/ai_config.py`:OpenAI 兼容文本/流;超时重试以实际 SDK 配置为准。回归用假客户端;降级结果不得作为成功覆盖旧笔记。
- `backend/core/ai_client.py`、`audio_transcriber.py`:本地 Whisper/FunASR/Qwen,首次可能下载模型;测试不下载。
- `media_ingestion.py`、`video_downloader.py`:yt-dlp 和 FFmpeg,媒体/字幕输入输出;本地样本代替站点稳定性测试。
- `note_repository.py`、`qa_repository.py`:SQLite 参数化查询,Markdown 文件受控路径;删除/重生成协同,临时数据库测试。
- `web/src/api/client.ts`:同源 API + SSE;连接失败不是任务失败;恢复只保存 ID。

部署由本地脚本和 Docker 管理,无已确认企业流水线。用户配置运行环境,网关由其提供方负责。
1 change: 1 addition & 0 deletions .claude/docs/_map-health.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schema_version":1,"kind":"backend","owner":"build-project-docs","workspace":".","source_roots":["backend"],"units":[{"id":"core","source_paths":["backend"],"map_paths":[".claude/docs/core.md",".claude/docs/_external-capabilities.md"]}]}
15 changes: 15 additions & 0 deletions .claude/docs/core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 核心流程

Task API → NoteGenerator → 媒体/转录/优化/摘要/导图 → Markdown + SQLite。TaskStatus 是 UI 进度和产物文件名契约。

历史、标签、卡片和会话使用 SQLite;文件限定 TEMP_DIR。删除与重生成必须按笔记协调,不能保留文件却删除索引。问答绑定 1–5 条笔记,需要在预算内覆盖各来源及相关后段;摘要/卡片同样不能只读开头。

验证使用临时数据库/文件与假模型,另做启动和浏览器验收。真实模型质量、模型下载、Windows 安装需要独立证据。

## 当前职责

- `note_operations.py`:单进程内按笔记串行化清理和重生成,取消时等待提交/回滚结束。
- `note_search.py`:SQLite 正文缓存、FTS5 trigram 加速及无 FTS 时的兼容搜索;保存/重生成同事务刷新,启动只回填缺项。
- `qa_retrieval.py`:中文/英文词法召回、分来源预算和历史裁剪;不增加向量服务。卡片用均匀采样覆盖首中尾,不代表长文每个事实都会进入提示词。
- `ai_client.py`:默认 Whisper 延迟加载,可选 ASR 依赖分组安装,本地模型目录优先。
- `frontend_cache.mjs`:依赖和输入/输出内容哈希决定前端安装与构建是否可复用。
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@ backend/anp/client_did_keys/

# uv 缓存
.uv

# 开发验证与本地工具上下文不进入运行镜像
tests/
web/tests/
.github/
.claude/
.ai-frontend/
.codex/
.opencode/
.githooks/
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
# ============================================
APP_HOST=127.0.0.1
APP_PORT=8999
# 默认仅接受本机 Host;局域网/反向代理使用时添加实际访问域名或 IP(不要填写协议、端口)
# ALLOWED_HOSTS=localhost,127.0.0.1,::1,notes.example.com
# Docker 默认只绑定本机;如显式开放,必须同时配置访问控制与上面的 Host 白名单
# APP_BIND_HOST=127.0.0.1
# 跨域开发地址,逗号分隔;生产构建与 API 同源时无需修改
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173

Expand Down Expand Up @@ -34,7 +38,9 @@ OPENAI_MODEL=gpt-4o
# 旧版兼容:显式设置后仅在 ASR_PROVIDER=whisper 时覆盖 ASR_MODEL
# WHISPER_MODEL_SIZE=base
ASR_PROVIDER=whisper
ASR_MODEL=base
# 可选 ASR 依赖由 start.sh/start.bat 和 Docker Compose 按此项自动安装
# 留空时按 provider 选默认模型;切换 provider 时清空旧模型名或填写匹配模型
ASR_MODEL=
ASR_MODEL_SOURCE=huggingface
ASR_MODEL_DIR=
ASR_DEVICE=cpu
Expand All @@ -52,6 +58,7 @@ ANP_SERVER_URL=http://localhost:8000/ad.json
# 搜索源配置(可选)
# ============================================
# 逗号分隔, 可选值: anp, local
# 为兼容所有启动脚本,此项使用小写、无空格、无引号(例如 anp,local)
# anp = 通过 ANP 协议调用远程搜索服务
# local = 通过 yt-dlp 本地搜索 (YouTube/Bilibili)
# 示例: VIDEO_SEARCH_PROVIDERS=anp,local (同时使用两个源)
Expand Down
84 changes: 84 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Checks

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: checks-${{ github.ref }}
cancel-in-progress: true

jobs:
regression:
name: Regression (${{ matrix.os }}, Python 3.12)
runs-on: ${{ matrix.os }}
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
defaults:
run:
shell: bash
env:
PYTHONUTF8: "1"
PYTHONDONTWRITEBYTECODE: "1"
PYTHON_DOTENV_DISABLED: "1"
OPENAI_API_KEY: ""
UV_PYTHON: "3.12"

steps:
- name: Check out source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

- name: Set up uv
uses: astral-sh/setup-uv@94527f2e458b27549849d47d273a16bec83a01e9 # v7
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Node.js
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: "22"
cache: npm
cache-dependency-path: web/package-lock.json

- name: Install locked backend dependencies
run: uv sync --frozen --python 3.12

- name: Run backend and launcher regressions
run: uv run --no-sync python -m unittest discover -s tests -v

- name: Validate release sources
run: uv run --no-sync python scripts/validate_release.py

- name: Build and validate backend package
run: |
uv build --wheel
uv run --no-sync python scripts/validate_release.py --wheel dist/*.whl

- name: Install locked frontend dependencies
working-directory: web
run: npm ci

- name: Run frontend regressions
working-directory: web
run: npm test

- name: Lint frontend
working-directory: web
run: npm run lint

- name: Build frontend
working-directory: web
run: npm run build
Loading