From 108ad270bfbdc01e266d177aebc63d3abc053a7d Mon Sep 17 00:00:00 2001 From: huangjianwu Date: Thu, 7 May 2026 11:10:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20AILogo=20=E5=99=AA?= =?UTF-8?q?=E9=9F=B3=E3=80=81=E8=AE=BE=E7=BD=AE=E9=A1=B5=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E4=B8=8E=E4=BE=9B=E5=BA=94=E5=95=86=E6=89=B9=E9=87=8F=E4=BC=AA?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E8=84=8F=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AILogo: `custom` 名称为合法兜底场景,不再以 console.error 上报;其余未匹配名称降级为 console.warn - SettingPage/Model: 双栏加 `min-h-0 overflow-y-auto`,让供应商列表与右侧表单各自可滚动 - ProviderService.add_provider: API 创建一律落到 `type='custom'`,并对同名供应商抛 ValueError,避免再产生伪内置行 - CLAUDE.md: 补充 v2.0.0 子系统(RAG/Chat、可选 Nacos+RabbitMQ、i18n、cookie/transcriber 管理器) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/Form/modelForm/Icons/index.tsx | 6 ++++-- .../src/components/Icons/index.tsx | 6 ++++-- .../src/pages/SettingPage/Model.tsx | 6 +++--- CLAUDE.md | 20 ++++++++++++++----- backend/app/services/provider.py | 10 +++++++++- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/BillNote_frontend/src/components/Form/modelForm/Icons/index.tsx b/BillNote_frontend/src/components/Form/modelForm/Icons/index.tsx index c9faa08..5636e7c 100644 --- a/BillNote_frontend/src/components/Form/modelForm/Icons/index.tsx +++ b/BillNote_frontend/src/components/Form/modelForm/Icons/index.tsx @@ -8,9 +8,11 @@ interface AILogoProps { } const AILogo = ({ name, style = 'Color', size = 24 }: AILogoProps) => { - const Icon = Icons[name as keyof typeof Icons] + const Icon = name ? Icons[name as keyof typeof Icons] : undefined if (!Icon) { - console.error(`❌ 图标组件不存在: ${name}`) + if (name && name !== 'custom') { + console.warn(`AILogo: 未匹配到图标,使用自定义占位: ${name}`) + } return ( CustomLogo diff --git a/BillNote_frontend/src/components/Icons/index.tsx b/BillNote_frontend/src/components/Icons/index.tsx index ca8ac53..360593f 100644 --- a/BillNote_frontend/src/components/Icons/index.tsx +++ b/BillNote_frontend/src/components/Icons/index.tsx @@ -7,9 +7,11 @@ interface AILogoProps { } const AILogo = ({ name, style = 'Color', size = 24 }: AILogoProps) => { - const Icon = Icons[name as keyof typeof Icons]; + const Icon = name ? Icons[name as keyof typeof Icons] : undefined; if (!Icon) { - console.error(`❌ 图标组件不存在: ${name}`); + if (name && name !== 'custom') { + console.warn(`AILogo: 未匹配到图标,使用占位: ${name}`); + } return 🚫; } diff --git a/BillNote_frontend/src/pages/SettingPage/Model.tsx b/BillNote_frontend/src/pages/SettingPage/Model.tsx index 775c1ff..9cc849b 100644 --- a/BillNote_frontend/src/pages/SettingPage/Model.tsx +++ b/BillNote_frontend/src/pages/SettingPage/Model.tsx @@ -3,11 +3,11 @@ import { Outlet } from 'react-router-dom' const Model = () => { return ( -
-
+
+
-
+
diff --git a/CLAUDE.md b/CLAUDE.md index 0379af5..49ae8f8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,22 +39,31 @@ cd BillNote_frontend && pnpm tauri build ## Architecture **Backend** (`backend/`) — FastAPI app, entry point `main.py`: -- `app/routers/` — API routes: `note.py` (generation), `provider.py`, `model.py`, `config.py` -- `app/services/` — Business logic: `note.py` (NoteGenerator orchestrates the full pipeline), `task_serial_executor.py` (task queue) +- `app/routers/` — API routes: `note.py` (generation), `provider.py`, `model.py`, `config.py`, `chat.py` (RAG Q&A on generated notes) +- `app/services/` — Business logic: + - `note.py` — `NoteGenerator` orchestrates the full pipeline (download → transcribe → LLM → notes) + - `task_serial_executor.py` — task queue + - `chat_service.py` + `chat_tools.py` + `vector_store.py` — RAG-based AI Q&A with Function Calling, indexing transcripts and video metadata + - `cookie_manager.py` — per-platform cookie storage; injected into yt-dlp by downloaders (e.g. Bilibili) + - `transcriber_config_manager.py` — persisted transcriber settings + - `worker_registry.py` — **optional** Nacos registration + heartbeat for distributed worker mode (no-op when `NACOS_SERVER_ADDR` unset) +- `app/messaging/` — **optional** RabbitMQ producer/consumer publishing task progress/results to `bilinote.task.feedback` exchange. Silently degrades when `RABBITMQ_URL` is unset; always import-safe. - `app/downloaders/` — Platform adapters (bilibili, youtube, douyin, kuaishou, local) with shared `base.py` interface -- `app/transcriber/` — Speech-to-text engines (fast-whisper, groq, bcut, kuaishou, mlx-whisper) with factory in `transcriber_provider.py` +- `app/transcriber/` — Speech-to-text engines (fast-whisper, groq, bcut, kuaishou, mlx-whisper) with factory in `transcriber_provider.py`. YouTube path prefers existing subtitles and skips audio download when available. - `app/gpt/` — LLM integration with factory pattern (`gpt_factory.py`), prompt templates (`prompt.py`, `prompt_builder.py`), and `request_chunker.py` for long transcripts - `app/db/` — SQLite + SQLAlchemy: DAO pattern (`provider_dao.py`, `model_dao.py`, `video_task_dao.py`), models in `models/` -- `app/utils/` — `response.py` (ResponseWrapper for consistent JSON), `video_helper.py` (screenshots via FFmpeg), `export.py` (PDF/DOCX) +- `app/utils/` — `response.py` (ResponseWrapper for consistent JSON), `video_helper.py` (screenshots via FFmpeg), `export.py` (PDF/DOCX), `ppt_generator.py`, `minio_client.py` +- `app/i18n/` — backend localization - `events/` (root level) — Blinker signal system for post-processing (e.g., temp file cleanup after transcription) **Frontend** (`BillNote_frontend/src/`) — React 19 + Vite + Tailwind + shadcn/ui: - `pages/HomePage/` — Main note generation UI: `NoteForm.tsx` (input), `MarkdownViewer.tsx` (preview), `MarkmapComponent.tsx` (mind map) - `pages/SettingPage/` — LLM provider management, system monitoring, transcriber config -- `store/` — Zustand stores: `taskStore`, `modelStore`, `configStore`, `providerStore` +- `store/` — Zustand stores: `taskStore`, `modelStore`, `configStore`, `providerStore`. Persists to IndexedDB. - `services/` — Axios API clients matching backend routes - `hooks/useTaskPolling.ts` — Polls task status every 3 seconds - `components/ui/` — shadcn/ui (Radix-based) components +- `i18n/` — `react-i18next` setup with locale JSON in `i18n/locales/`; toggled via `components/LanguageSwitcher.tsx` - Path alias: `@` → `./src` **Core Workflow**: User submits URL → task queued → download video → extract audio (FFmpeg) → transcribe (Whisper/Groq/etc) → generate notes (LLM) → frontend polls for completion → display Markdown + mind map. @@ -66,6 +75,7 @@ cd BillNote_frontend && pnpm tauri build - **Database**: SQLite at `backend/app/db/bili_note.db`, auto-initialized on first run - **FFmpeg**: Required system dependency for video/audio processing - **Vite proxy**: Dev server proxies `/api` and `/static` to backend (configured in `vite.config.ts`, reads env from parent dir) +- **Distributed mode (optional)**: Setting `NACOS_SERVER_ADDR` enables Nacos worker registration; setting `RABBITMQ_URL` enables MQ feedback. Both are no-ops when unset — single-node deployment works without either. Other knobs: `WORKER_ID`, `WORKER_SELF_URL`, `WORKER_MAX_CONCURRENT`, `TASK_MAX_WORKERS`. ## Code Style diff --git a/backend/app/services/provider.py b/backend/app/services/provider.py index b6cef62..0eb82cf 100644 --- a/backend/app/services/provider.py +++ b/backend/app/services/provider.py @@ -71,11 +71,19 @@ class ProviderService: @staticmethod def add_provider( name: str, api_key: str, base_url: str, logo: str, type_: str, enabled: int = 1): try: + # 内置供应商(type='built-in')只能由 seed 流程写入;API 创建一律落到 'custom', + # 否则历史上出现过批量伪内置脏数据 + if type_ != 'custom': + type_ = 'custom' + existing = get_provider_by_name(name) + if existing is not None: + raise ValueError(f'供应商名称已存在: {name}') id = uuid().lower() - logo='custom' + logo = 'custom' return insert_provider(id, name, api_key, base_url, logo, type_, enabled) except Exception as e: print('创建模式失败',e) + raise @staticmethod def provider_to_dict(p: Provider): return {