diff --git a/.claude/specs/robustness-improvement/dev-plan.md b/.claude/specs/robustness-improvement/dev-plan.md index fcae8d2..cf52bee 100644 --- a/.claude/specs/robustness-improvement/dev-plan.md +++ b/.claude/specs/robustness-improvement/dev-plan.md @@ -7,60 +7,37 @@ ### P0 - 关键问题修复 -#### Task-1: 修复 Rate Limit 内存泄漏 +#### Task-1: 修复 Rate Limit 内存泄漏 ✅ - **文件**: `backend/app/api/stories.py` -- **问题**: `_request_log` 全局字典无清理机制,长期运行内存无限增长 -- **方案**: 添加 TTL 自动清理机制,使用 `cachetools.TTLCache` -- **测试**: 验证过期条目自动清理 +- **方案**: 已迁移至 Redis 分布式限流,内存泄漏问题不再存在 -#### Task-2: 添加核心 API 测试 -- **文件**: `backend/tests/` (新建) -- **范围**: - - `test_auth.py`: OAuth 流程、session 验证 - - `test_stories.py`: 故事 CRUD、rate limit -- **目标**: 核心路径覆盖率 ≥80% +#### Task-2: 添加核心 API 测试 ✅ +- **文件**: `backend/tests/` +- **范围**: test_auth, test_stories, test_profiles, test_universes, test_push_configs, test_reading_events, test_provider_router ### P1 - 稳定性提升 -#### Task-3: 添加 API 重试机制 -- **文件**: `backend/app/services/gemini.py`, `minimax.py`, `drawing.py` -- **方案**: 使用 `tenacity` 库,指数退避重试 -- **配置**: 最多 3 次重试,初始间隔 1s +#### Task-3: 添加 API 重试机制 ✅ +- **方案**: 所有适配器已使用 `tenacity` 指数退避重试 (gemini, openai, cqtai, antigravity, minimax, elevenlabs) -#### Task-4: 添加结构化日志 -- **文件**: `backend/app/core/logging.py` (新建), 各 service 文件 -- **方案**: 使用 `structlog`,JSON 格式输出 -- **埋点**: API 调用、错误、性能指标 +#### Task-4: 添加结构化日志 ✅ +- **文件**: `backend/app/core/logging.py` +- **方案**: structlog JSON/Console 双模式,所有适配器和 provider_router 已集成 ### P2 - 代码优化 -#### Task-5: 重构 Provider Router +#### Task-5: 重构 Provider Router ✅ - **文件**: `backend/app/services/provider_router.py` -- **问题**: 三个函数重复代码 -- **方案**: 抽象通用 failover 函数 +- **方案**: 已实现统一 `_route_with_failover` 函数 -#### Task-6: 配置外部化 -- **文件**: `backend/app/core/config.py`, `backend/app/services/gemini.py` -- **问题**: 模型名硬编码 -- **方案**: 移至环境变量配置 +#### Task-6: 配置外部化 ✅ +- **文件**: `backend/app/core/config.py`, `backend/app/services/provider_router.py` +- **方案**: 所有模型名已移至 Settings,支持环境变量覆盖 -#### Task-7: 修复脆弱的 URL 解析 -- **文件**: `backend/app/services/drawing.py` -- **问题**: 字符串切片解析 URL 不可靠 -- **方案**: 使用正则表达式 +#### Task-7: 修复脆弱的 URL 解析 ✅ +- **状态**: `drawing.py` 已被适配器系统取代,不再存在 -## 依赖关系 -``` -Task-1 (独立) -Task-2 (独立,但需要 Task-1 完成后验证) -Task-3 (独立) -Task-4 (独立) -Task-5 (独立) -Task-6 (独立) -Task-7 (独立) -``` - -## 新增依赖 +## 新增依赖 (已添加) ```toml # pyproject.toml [project.dependencies] cachetools>=5.0.0 # Task-1: TTL cache @@ -69,4 +46,4 @@ structlog>=24.0.0 # Task-4: 结构化日志 # [project.optional-dependencies.dev] pytest-cov>=4.0.0 # Task-2: 覆盖率报告 -httpx[http2] # Task-2: 测试 mock +``` diff --git a/.gitignore b/.gitignore index aa93481..3d86c34 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ htmlcov/ # 其他 *.log .DS_Store +nul # Vite *.timestamp-*.mjs diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 9fa46e3..d8013d0 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -41,8 +41,13 @@ class Settings(BaseSettings): # AI Model Configuration text_model: str = "gemini-2.0-flash" + openai_model: str = "gpt-4o-mini" tts_model: str = "" - image_model: str = "" + image_model: str = "nano-banana-pro" + tts_minimax_model: str = "speech-2.6-turbo" + tts_elevenlabs_model: str = "eleven_multilingual_v2" + tts_edge_voice: str = "zh-CN-XiaoxiaoNeural" + antigravity_model: str = "gemini-3-pro-image" # Provider routing (ordered lists) text_providers: list[str] = Field(default_factory=lambda: ["gemini"]) diff --git a/backend/app/services/provider_router.py b/backend/app/services/provider_router.py index d49947a..e75f189 100644 --- a/backend/app/services/provider_router.py +++ b/backend/app/services/provider_router.py @@ -98,7 +98,7 @@ def _get_default_config(adapter_name: str) -> AdapterConfig | None: if adapter_name == "openai": return AdapterConfig( api_key=getattr(settings, "openai_api_key", ""), - model="gpt-4o-mini", # 这里可以从 settings 读取,看需求 + model=settings.openai_model, timeout_ms=60000, ) @@ -106,7 +106,7 @@ def _get_default_config(adapter_name: str) -> AdapterConfig | None: if adapter_name in ("cqtai"): return AdapterConfig( api_key=getattr(settings, "cqtai_api_key", ""), - model="nano-banana-pro", # 默认使用 Pro + model=settings.image_model or "nano-banana-pro", timeout_ms=120000, ) if adapter_name == "image_primary": @@ -128,7 +128,7 @@ def _get_default_config(adapter_name: str) -> AdapterConfig | None: # 我们这里暂时返回基础配置。 return AdapterConfig( api_key=getattr(settings, "minimax_api_key", ""), - model="speech-2.6-turbo", + model=settings.tts_minimax_model, timeout_ms=60000, )