wip: snapshot full local workspace state
Some checks are pending
Build and Push Docker Images / changes (push) Waiting to run
Build and Push Docker Images / build-backend (push) Blocked by required conditions
Build and Push Docker Images / build-frontend (push) Blocked by required conditions
Build and Push Docker Images / build-admin-frontend (push) Blocked by required conditions
Some checks are pending
Build and Push Docker Images / changes (push) Waiting to run
Build and Push Docker Images / build-backend (push) Blocked by required conditions
Build and Push Docker Images / build-frontend (push) Blocked by required conditions
Build and Push Docker Images / build-admin-frontend (push) Blocked by required conditions
This commit is contained in:
@@ -38,7 +38,11 @@
|
||||
"Bash(npx vue-tsc:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(git init:*)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git remote add:*)",
|
||||
"Bash(git push:*)",
|
||||
"Bash(git branch:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
134
AGENTS.md
Normal file
134
AGENTS.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
DreamWeaver (梦语织机) - AI-powered children's story generation app for ages 3-8. Features story generation from keywords, story enhancement, AI-generated cover images, and text-to-speech narration.
|
||||
|
||||
**Language:** Chinese (Simplified) for UI and comments.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Backend:** FastAPI + PostgreSQL (Neon) + SQLAlchemy (async) + Celery/Redis | **Python 3.11+**
|
||||
- **Frontend:** Vue 3 + TypeScript + Pinia + Tailwind CSS + vue-i18n
|
||||
- **Auth:** OAuth 2.0 (GitHub/Google) with JWT in httpOnly cookie
|
||||
- **AI Services:** Text generation, image generation, TTS (pluggable adapters)
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
pip install -e . # Install dependencies
|
||||
pip install -e ".[dev]" # With dev tools (pytest, ruff)
|
||||
uvicorn app.main:app --reload --port 8000 # Start dev server
|
||||
|
||||
# Celery worker (requires Redis)
|
||||
celery -A app.tasks worker --loglevel=info
|
||||
|
||||
# Database migrations
|
||||
alembic upgrade head # Run migrations
|
||||
alembic revision -m "message" --autogenerate # Generate new migration
|
||||
|
||||
# Linting
|
||||
ruff check app/ # Check code
|
||||
ruff check app/ --fix # Auto-fix
|
||||
|
||||
# Testing
|
||||
pytest # Run all tests
|
||||
pytest tests/test_auth.py -v # Single test file
|
||||
pytest -k "test_name" # Match pattern
|
||||
|
||||
# Frontend
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev # Start dev server (port 5173)
|
||||
npm run build # Type-check + build
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── main.py # FastAPI app entry, routes registration
|
||||
├── api/
|
||||
│ ├── auth.py # OAuth routes (GitHub/Google)
|
||||
│ ├── stories.py # Story CRUD and AI generation endpoints
|
||||
│ ├── profiles.py # User profile management
|
||||
│ ├── universes.py # Story universe/world management
|
||||
│ ├── reading_events.py # Reading progress tracking
|
||||
│ ├── push_configs.py # Push notification settings
|
||||
│ ├── admin_providers.py # Provider CRUD (admin)
|
||||
│ └── admin_reload.py # Hot-reload providers (admin)
|
||||
├── core/
|
||||
│ ├── config.py # Pydantic settings from env
|
||||
│ ├── deps.py # Dependency injection (auth, db session)
|
||||
│ ├── security.py # JWT token create/verify
|
||||
│ ├── prompts.py # AI prompt templates
|
||||
│ └── admin_auth.py # Basic Auth for admin routes
|
||||
├── db/
|
||||
│ ├── database.py # SQLAlchemy async engine + session
|
||||
│ ├── models.py # User, Story models
|
||||
│ └── admin_models.py # Provider model
|
||||
├── services/
|
||||
│ ├── adapters/ # Capability adapters (text, image, tts)
|
||||
│ ├── provider_router.py # Failover routing across providers
|
||||
│ ├── provider_cache.py # In-memory provider config cache
|
||||
│ ├── provider_metrics.py # Provider performance metrics
|
||||
│ └── achievement_extractor.py # Extract achievements from stories
|
||||
└── tasks/
|
||||
├── achievements.py # Celery task: achievement processing
|
||||
└── push_notifications.py # Celery task: push notifications
|
||||
|
||||
frontend/src/
|
||||
├── api/client.ts # Axios wrapper with auth interceptors
|
||||
├── stores/user.ts # Pinia user state
|
||||
├── router.ts # Vue Router config
|
||||
├── i18n.ts + locales/ # vue-i18n setup
|
||||
├── components/ # Reusable Vue components
|
||||
└── views/ # Page components
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
- **Async everywhere:** All database and API calls use async/await
|
||||
- **Dependency injection:** FastAPI `Depends()` for auth and db session
|
||||
- **JWT auth:** Stored in httpOnly cookie, validated via `get_current_user` dependency
|
||||
- **Provider routing:** `provider_router.py` tries providers in order, auto-failover on error
|
||||
- **Background tasks:** Celery workers handle achievements and push notifications
|
||||
- **Proxy in dev:** Vite proxies `/api`, `/auth`, `/admin` to backend (see `vite.config.ts`)
|
||||
|
||||
## Provider System
|
||||
|
||||
AI providers are configured via env vars (`TEXT_PROVIDERS`, `IMAGE_PROVIDERS`, `TTS_PROVIDERS`) as JSON arrays. The router tries each in order and fails over automatically.
|
||||
|
||||
Admin console (disabled by default): Set `ENABLE_ADMIN_CONSOLE=true` to enable `/admin/providers` CRUD endpoints with Basic Auth (`ADMIN_USERNAME`/`ADMIN_PASSWORD`).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
See `backend/.env.example` for required variables:
|
||||
|
||||
- `DATABASE_URL`, `SECRET_KEY` (required)
|
||||
- OAuth: `GITHUB_CLIENT_ID/SECRET`, `GOOGLE_CLIENT_ID/SECRET`
|
||||
- AI: `TEXT_API_KEY`, `TTS_API_BASE`, `TTS_API_KEY`, `IMAGE_API_KEY`
|
||||
- Providers: `TEXT_PROVIDERS`, `IMAGE_PROVIDERS`, `TTS_PROVIDERS` (JSON arrays)
|
||||
- Celery: `CELERY_BROKER_URL`, `CELERY_RESULT_BACKEND` (Redis URLs)
|
||||
- Admin: `ENABLE_ADMIN_CONSOLE`, `ADMIN_USERNAME`, `ADMIN_PASSWORD`
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Route | Description |
|
||||
| ------------------- | -------------------------- | --------------------------- |
|
||||
| GET | `/auth/{provider}/signin` | OAuth login |
|
||||
| GET | `/auth/session` | Get current user |
|
||||
| POST | `/api/generate` | Generate/enhance story |
|
||||
| POST | `/api/image/generate/{id}` | Generate cover image |
|
||||
| GET | `/api/audio/{id}` | Get TTS audio |
|
||||
| GET | `/api/stories` | List stories (paginated) |
|
||||
| GET/DELETE | `/api/stories/{id}` | Story CRUD |
|
||||
| CRUD | `/api/profiles` | User profiles |
|
||||
| CRUD | `/api/universes` | Story universes |
|
||||
| CRUD | `/api/reading-events` | Reading progress |
|
||||
| CRUD | `/api/push-configs` | Push notification settings |
|
||||
| GET/POST/PUT/DELETE | `/admin/providers` | Provider management (admin) |
|
||||
@@ -1,14 +0,0 @@
|
||||
# Code Review Report (2nd follow-up)
|
||||
|
||||
## What¡¯s fixed
|
||||
- Provider cache now loads on startup via lifespan (`app/main.py`), so DB providers are honored without manual reload.
|
||||
- Providers support DB-stored `api_key` precedence (`provider_router.py:77-104`) and Provider model added `api_key` column (`db/admin_models.py:25`).
|
||||
- Frontend uses `/api/generate/full` and propagates image-failure warning to detail via query flag; StoryDetail displays banner when image generation failed.
|
||||
- Tests added for full generation, provider failover, config-from-DB, and startup cache load.
|
||||
|
||||
## Remaining issue
|
||||
1) **Missing DB migration for new Provider.api_key column**
|
||||
- Files updated model (`backend/app/db/admin_models.py:25`) but `backend/alembic/versions/0001_init_providers_and_story_mode.py` lacks this column. Existing databases will not have `api_key`, causing runtime errors when accessing or inserting. Add an Alembic migration to add/drop `api_key` to `providers` table and update sample data if needed.
|
||||
|
||||
## Suggested action
|
||||
- Create and apply an Alembic migration adding `api_key` (String, nullable) to `providers`. After migration, verify admin CRUD works with the new field.
|
||||
@@ -1,89 +0,0 @@
|
||||
# HA 部署与验证 Runbook(Phase 3 MVP)
|
||||
|
||||
本文档对应 `docker-compose.ha.yml`,用于本地/测试环境验证高可用基础能力。
|
||||
|
||||
## 1. 启动方式
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.ha.yml up -d
|
||||
```
|
||||
|
||||
说明:
|
||||
- 基础业务服务仍来自 `docker-compose.yml`。
|
||||
- `docker-compose.ha.yml` 覆盖了 `db`、`redis`,并新增 `db-replica`、`postgres-backup`、`redis-replica`、`redis-sentinel-*`。
|
||||
|
||||
## 2. 核心环境变量建议
|
||||
|
||||
在 `backend/.env`(或 shell 环境)中至少配置:
|
||||
|
||||
```env
|
||||
# PostgreSQL
|
||||
POSTGRES_USER=dreamweaver
|
||||
POSTGRES_PASSWORD=dreamweaver_password
|
||||
POSTGRES_DB=dreamweaver_db
|
||||
POSTGRES_REPMGR_PASSWORD=repmgr_password
|
||||
|
||||
# Redis Sentinel
|
||||
REDIS_SENTINEL_ENABLED=true
|
||||
REDIS_SENTINEL_NODES=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
|
||||
REDIS_SENTINEL_MASTER_NAME=mymaster
|
||||
REDIS_SENTINEL_DB=0
|
||||
REDIS_SENTINEL_SOCKET_TIMEOUT=0.5
|
||||
|
||||
# 可选:若 Sentinel/Redis 设置了密码
|
||||
REDIS_SENTINEL_PASSWORD=
|
||||
|
||||
# 备份周期,默认 86400 秒(1 天)
|
||||
BACKUP_INTERVAL_SECONDS=86400
|
||||
```
|
||||
|
||||
## 3. 健康检查
|
||||
|
||||
### 3.1 PostgreSQL 主从
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.ha.yml ps
|
||||
docker exec -it dreamweaver_db_primary psql -U dreamweaver -d dreamweaver_db -c "select now();"
|
||||
docker exec -it dreamweaver_db_replica psql -U dreamweaver -d dreamweaver_db -c "select pg_is_in_recovery();"
|
||||
```
|
||||
|
||||
期望:
|
||||
- 主库可读写;
|
||||
- 从库 `pg_is_in_recovery()` 返回 `t`。
|
||||
|
||||
### 3.2 Redis Sentinel
|
||||
|
||||
```bash
|
||||
docker exec -it dreamweaver_redis_sentinel_1 redis-cli -p 26379 sentinel masters
|
||||
docker exec -it dreamweaver_redis_sentinel_1 redis-cli -p 26379 sentinel replicas mymaster
|
||||
```
|
||||
|
||||
期望:
|
||||
- `mymaster` 存在;
|
||||
- 至少 1 个 replica 被发现。
|
||||
|
||||
### 3.3 备份任务
|
||||
|
||||
```bash
|
||||
docker exec -it dreamweaver_postgres_backup sh -c "ls -lh /backups"
|
||||
```
|
||||
|
||||
期望:
|
||||
- `/backups` 下出现 `.dump` 文件;
|
||||
- 旧于 7 天的备份会被自动清理。
|
||||
|
||||
## 4. 故障切换演练(最小)
|
||||
|
||||
```bash
|
||||
# 模拟 Redis 主节点故障
|
||||
docker stop dreamweaver_redis_master
|
||||
|
||||
# 等待 Sentinel 选主后查看
|
||||
docker exec -it dreamweaver_redis_sentinel_1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
|
||||
```
|
||||
|
||||
提示:应用与 Celery 已支持 Sentinel 配置。若未启用 Sentinel,仍可回退到 `REDIS_URL` / `CELERY_BROKER_URL` / `CELERY_RESULT_BACKEND` 直连模式。
|
||||
|
||||
## 5. 当前已知限制(下一步)
|
||||
|
||||
- PostgreSQL 侧当前仅完成主从拓扑,读写分离(PgBouncer/路由)待后续迭代。
|
||||
@@ -1,147 +0,0 @@
|
||||
# 记忆系统开发指南 (Development Guide)
|
||||
|
||||
本文档详细说明了 PRD 中定义的记忆系统的技术实现细节。
|
||||
|
||||
## 1. 数据库架构变更 (Schema Changes)
|
||||
|
||||
目前的 `MemoryItem` 表结构尚可,但需要增强字段以支持丰富的情感和元数据。
|
||||
|
||||
### 1.1 `MemoryItem` 表优化
|
||||
建议使用 Alembic 进行迁移,增加以下字段或在 `value` JSON 中规范化以下结构:
|
||||
|
||||
```python
|
||||
# 建议在 models.py 中明确这些字段,或者严格定义 value 字段的 Schema
|
||||
class MemoryItem(Base):
|
||||
# ... 现有字段 ...
|
||||
|
||||
# 新增/规范化字段建议
|
||||
# value 字段的 JSON 结构规范:
|
||||
# {
|
||||
# "content": "小兔子战胜了大灰狼", # 记忆的核心文本
|
||||
# "keywords": ["勇敢", "森林"], # 用于检索的关键词
|
||||
# "emotion": "positive", # 情感倾向: positive/negative/neutral
|
||||
# "source_story_id": 123, # 来源故事 ID
|
||||
# "confidence": 0.85 # 记忆置信度 (如果是 AI 自动提取)
|
||||
# }
|
||||
```
|
||||
|
||||
### 1.2 `StoryUniverse` 表优化 (成就系统)
|
||||
目前的成就存储在 `achievements` JSON 字段中。为了支持更复杂的查询(如"获得勇气勋章的所有用户"),建议将其重构为独立关联表,或保持 JSON 但规范化结构。
|
||||
|
||||
**当前 JSON 结构规范**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "badge_courage_01",
|
||||
"type": "勇气",
|
||||
"name": "小小勇士",
|
||||
"description": "第一次在故事中独自面对困难",
|
||||
"icon_url": "badges/courage.png",
|
||||
"obtained_at": "2023-10-27T10:00:00Z",
|
||||
"source_story_id": 45
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心逻辑实现
|
||||
|
||||
### 2.1 记忆注入逻辑 (Prompt Engineering)
|
||||
|
||||
修改 `backend/app/api/stories.py` 中的 `_build_memory_context` 函数。
|
||||
|
||||
**目标**: 生成一段自然的、不仅是罗列数据的 Prompt。
|
||||
|
||||
**伪代码逻辑**:
|
||||
```python
|
||||
def format_memory_for_prompt(memories: list[MemoryItem]) -> str:
|
||||
"""
|
||||
将记忆项转换为自然语言 Prompt 片段。
|
||||
"""
|
||||
context_parts = []
|
||||
|
||||
# 1. 角色记忆
|
||||
chars = [m for m in memories if m.type == 'favorite_character']
|
||||
if chars:
|
||||
names = ", ".join([c.value['name'] for c in chars])
|
||||
context_parts.append(f"孩子特别喜欢的角色有:{names}。请尝试让他们客串出场。")
|
||||
|
||||
# 2. 近期情节
|
||||
recent_stories = [m for m in memories if m.type == 'recent_story'][:2]
|
||||
if recent_stories:
|
||||
for story in recent_stories:
|
||||
context_parts.append(f"最近发生过:{story.value['summary']}。可以在对话中不经意地提及此事。")
|
||||
|
||||
# 3. 避雷区 (负面记忆)
|
||||
scary = [m for m in memories if m.type == 'scary_element']
|
||||
if scary:
|
||||
items = ", ".join([s.value['keyword'] for s in scary])
|
||||
context_parts.append(f"【绝对禁止】不要出现以下让孩子害怕的元素:{items}。")
|
||||
|
||||
return "\n".join(context_parts)
|
||||
```
|
||||
|
||||
### 2.2 成就提取与通知流程
|
||||
|
||||
当前流程在 `app/tasks/achievements.py`。需要完善闭环。
|
||||
|
||||
**改进后的流程**:
|
||||
1. **Story Generation**: 故事生成成功,存入数据库。
|
||||
2. **Async Task**: 触发 Celery 任务 `extract_story_achievements`。
|
||||
3. **LLM Analysis**: 调用 Gemini 分析故事,提取成就。
|
||||
4. **Update DB**: 更新 `StoryUniverse.achievements`。
|
||||
5. **Notification (新增)**:
|
||||
* 创建一个 `Notification` 或 `UserMessage` 记录(需要新建表或使用 Redis Pub/Sub)。
|
||||
* 前端轮询或通过 SSE (Server-Sent Events) 接收通知:"🎉 恭喜!在这个故事里,小明获得了[诚实勋章]!"
|
||||
|
||||
### 2.3 记忆清理与衰减 (Maintenance)
|
||||
|
||||
需要一个后台定时任务(Cron Job),清理无效记忆,避免 Prompt 过长。
|
||||
|
||||
* **频率**: 每天一次。
|
||||
* **逻辑**:
|
||||
* 删除 `ttl_days` 已过期的记录。
|
||||
* 对 `recent_story` 类型的 `base_weight` 进行每日衰减 update(或者只在读取时计算,数据库存静态值,推荐读取时动态计算以减少写操作)。
|
||||
* 当 `MemoryItem` 总数超过 100 条时,触发"记忆总结"任务,将多条旧记忆合并为一条"长期印象" (Long-term Impression)。
|
||||
|
||||
---
|
||||
|
||||
## 3. API 接口规划
|
||||
|
||||
### 3.1 获取成长时间轴
|
||||
`GET /api/profiles/{id}/timeline`
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"date": "2023-10-01",
|
||||
"type": "milestone",
|
||||
"title": "初次相遇",
|
||||
"description": "创建了角色 [小明]"
|
||||
},
|
||||
{
|
||||
"date": "2023-10-05",
|
||||
"type": "story",
|
||||
"title": "小明与魔法树",
|
||||
"image_url": "..."
|
||||
},
|
||||
{
|
||||
"date": "2023-10-05",
|
||||
"type": "achievement",
|
||||
"badge": {
|
||||
"name": "好奇宝宝",
|
||||
"icon": "..."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 记忆反馈 (人工干预)
|
||||
`POST /api/memories/{id}/feedback`
|
||||
|
||||
允许家长手动删除或修正错误的记忆。
|
||||
* **Action**: `delete` | `reinforce` (强化,增加权重)
|
||||
@@ -1,246 +0,0 @@
|
||||
# Provider 系统开发文档
|
||||
|
||||
## 当前版本功能 (v0.2.0)
|
||||
|
||||
### 已完成功能
|
||||
|
||||
1. **CQTAI nano 图像适配器** (`app/services/adapters/image/cqtai.py`)
|
||||
- 异步生成 + 轮询获取结果
|
||||
- 支持 nano-banana / nano-banana-pro 模型
|
||||
- 支持多种分辨率和画面比例
|
||||
- 支持图生图 (filesUrl)
|
||||
|
||||
2. **密钥加密存储** (`app/services/secret_service.py`)
|
||||
- Fernet 对称加密,密钥从 SECRET_KEY 派生
|
||||
- Provider API Key 自动加密存储
|
||||
- 密钥管理 API (CRUD)
|
||||
|
||||
3. **指标收集系统** (`app/services/provider_metrics.py`)
|
||||
- 调用成功率、延迟、成本统计
|
||||
- 时间窗口聚合查询
|
||||
- 已集成到 provider_router
|
||||
|
||||
4. **熔断器功能** (`app/services/provider_metrics.py`)
|
||||
- 连续失败 3 次触发熔断
|
||||
- 60 秒后自动恢复尝试
|
||||
- 健康状态持久化到数据库
|
||||
|
||||
5. **管理后台前端** (`app/admin_app.py`)
|
||||
- 独立端口部署 (8001)
|
||||
- Vue 3 + Tailwind CSS 单页应用
|
||||
- Provider CRUD 管理
|
||||
- 密钥管理界面
|
||||
- Basic Auth 认证
|
||||
|
||||
### 配置说明
|
||||
|
||||
```bash
|
||||
# 启动主应用
|
||||
uvicorn app.main:app --port 8000
|
||||
|
||||
# 启动管理后台 (独立端口)
|
||||
uvicorn app.admin_app:app --port 8001
|
||||
```
|
||||
|
||||
环境变量:
|
||||
```
|
||||
CQTAI_API_KEY=your-cqtai-api-key
|
||||
ENABLE_ADMIN_CONSOLE=true
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=your-secure-password
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一版本优化计划 (v0.3.0)
|
||||
|
||||
### 高优先级
|
||||
|
||||
#### 1. 智能负载分流 (方案 B)
|
||||
**目标**: 主渠道压力大时自动分流到后备渠道
|
||||
|
||||
**实现方案**:
|
||||
- 监控指标: 并发数、响应延迟、错误率
|
||||
- 分流阈值配置:
|
||||
```python
|
||||
class LoadBalanceConfig:
|
||||
max_concurrent: int = 10 # 并发超过此值时分流
|
||||
max_latency_ms: int = 5000 # 延迟超过此值时分流
|
||||
max_error_rate: float = 0.1 # 错误率超过 10% 时分流
|
||||
```
|
||||
- 分流策略: 加权轮询,根据健康度动态调整权重
|
||||
|
||||
**涉及文件**:
|
||||
- `app/services/provider_router.py` - 添加负载均衡逻辑
|
||||
- `app/services/provider_metrics.py` - 添加并发计数器
|
||||
- `app/db/admin_models.py` - 添加 LoadBalanceConfig 模型
|
||||
|
||||
#### 2. Storybook 适配器
|
||||
**目标**: 生成可翻页的分页故事书
|
||||
|
||||
**实现方案**:
|
||||
- 参考 Gemini AI Story Generator 格式
|
||||
- 输出结构:
|
||||
```python
|
||||
class StorybookPage:
|
||||
page_number: int
|
||||
text: str
|
||||
image_prompt: str
|
||||
image_url: str | None
|
||||
|
||||
class Storybook:
|
||||
title: str
|
||||
pages: list[StorybookPage]
|
||||
cover_url: str | None
|
||||
```
|
||||
- 集成文本 + 图像生成流水线
|
||||
|
||||
**涉及文件**:
|
||||
- `app/services/adapters/storybook/` - 新建目录
|
||||
- `app/api/stories.py` - 添加 storybook 生成端点
|
||||
|
||||
### 中优先级
|
||||
|
||||
#### 3. 成本追踪系统
|
||||
**目标**: 记录实际消费,支持预算控制
|
||||
|
||||
**实现方案**:
|
||||
- 成本记录表:
|
||||
```python
|
||||
class CostRecord:
|
||||
user_id: str
|
||||
provider_id: str
|
||||
capability: str # text/image/tts
|
||||
estimated_cost: Decimal
|
||||
actual_cost: Decimal | None
|
||||
timestamp: datetime
|
||||
```
|
||||
- 预算配置:
|
||||
```python
|
||||
class BudgetConfig:
|
||||
user_id: str
|
||||
daily_limit: Decimal
|
||||
monthly_limit: Decimal
|
||||
alert_threshold: float = 0.8 # 80% 时告警
|
||||
```
|
||||
- 超预算处理: 拒绝请求 / 降级到低成本 provider
|
||||
|
||||
**涉及文件**:
|
||||
- `app/db/admin_models.py` - 添加 CostRecord, BudgetConfig
|
||||
- `app/services/cost_tracker.py` - 新建
|
||||
- `app/api/admin_providers.py` - 添加成本查询 API
|
||||
|
||||
#### 4. 指标可视化
|
||||
**目标**: 管理后台展示供应商指标图表
|
||||
|
||||
**实现方案**:
|
||||
- 添加指标查询 API:
|
||||
- GET /admin/metrics/summary - 汇总统计
|
||||
- GET /admin/metrics/timeline - 时间线数据
|
||||
- GET /admin/metrics/providers/{id} - 单个供应商详情
|
||||
- 前端使用 Chart.js 或 ECharts 展示
|
||||
|
||||
### 低优先级
|
||||
|
||||
#### 5. 多租户 Provider 配置
|
||||
**目标**: 每个租户可配置独立 provider 列表和 API Key
|
||||
|
||||
**实现方案**:
|
||||
- 租户配置表:
|
||||
```python
|
||||
class TenantProviderConfig:
|
||||
tenant_id: str
|
||||
provider_type: str
|
||||
provider_ids: list[str] # 按优先级排序
|
||||
api_key_override: str | None # 加密存储
|
||||
```
|
||||
- 路由时优先使用租户配置,回退到全局配置
|
||||
|
||||
#### 6. Provider 健康检查调度器
|
||||
**目标**: 定期主动检查 provider 健康状态
|
||||
|
||||
**实现方案**:
|
||||
- Celery Beat 定时任务
|
||||
- 每 5 分钟检查一次所有启用的 provider
|
||||
- 更新 ProviderHealth 表
|
||||
|
||||
#### 7. 适配器热加载
|
||||
**目标**: 支持运行时动态加载新适配器
|
||||
|
||||
**实现方案**:
|
||||
- 适配器插件目录: `app/services/adapters/plugins/`
|
||||
- 启动时扫描并注册
|
||||
- 提供 API 触发重新扫描
|
||||
|
||||
---
|
||||
|
||||
## API 变更记录
|
||||
|
||||
### v0.2.0 新增
|
||||
|
||||
| Method | Route | Description |
|
||||
|--------|-------|-------------|
|
||||
| GET | `/admin/secrets` | 列出所有密钥名称 |
|
||||
| POST | `/admin/secrets` | 创建/更新密钥 |
|
||||
| DELETE | `/admin/secrets/{name}` | 删除密钥 |
|
||||
| GET | `/admin/secrets/{name}/verify` | 验证密钥有效性 |
|
||||
|
||||
### 计划中 (v0.3.0)
|
||||
|
||||
| Method | Route | Description |
|
||||
|--------|-------|-------------|
|
||||
| GET | `/admin/metrics/summary` | 指标汇总 |
|
||||
| GET | `/admin/metrics/timeline` | 时间线数据 |
|
||||
| POST | `/api/storybook/generate` | 生成分页故事书 |
|
||||
| GET | `/admin/costs` | 成本统计 |
|
||||
| POST | `/admin/budgets` | 设置预算 |
|
||||
|
||||
---
|
||||
|
||||
## 适配器开发指南
|
||||
|
||||
### 添加新适配器
|
||||
|
||||
1. 创建适配器文件:
|
||||
```python
|
||||
# app/services/adapters/image/new_provider.py
|
||||
from app.services.adapters.base import AdapterConfig, BaseAdapter
|
||||
from app.services.adapters.registry import AdapterRegistry
|
||||
|
||||
@AdapterRegistry.register("image", "new_provider")
|
||||
class NewProviderAdapter(BaseAdapter[str]):
|
||||
adapter_type = "image"
|
||||
adapter_name = "new_provider"
|
||||
|
||||
async def execute(self, prompt: str, **kwargs) -> str:
|
||||
# 实现生成逻辑
|
||||
pass
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
# 实现健康检查
|
||||
pass
|
||||
|
||||
@property
|
||||
def estimated_cost(self) -> float:
|
||||
return 0.01 # USD
|
||||
```
|
||||
|
||||
2. 在 `__init__.py` 中导入:
|
||||
```python
|
||||
# app/services/adapters/__init__.py
|
||||
from app.services.adapters.image import new_provider as _new_provider # noqa: F401
|
||||
```
|
||||
|
||||
3. 添加配置:
|
||||
```python
|
||||
# app/core/config.py
|
||||
new_provider_api_key: str = ""
|
||||
|
||||
# app/services/provider_router.py
|
||||
API_KEY_MAP["new_provider"] = "new_provider_api_key"
|
||||
```
|
||||
|
||||
4. 更新 `.env.example`:
|
||||
```
|
||||
NEW_PROVIDER_API_KEY=
|
||||
```
|
||||
@@ -1,109 +0,0 @@
|
||||
# DreamWeaver 重构实施计划
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档基于对当前架构的深入分析,制定了从稳定性、可维护性到可扩展性的分阶段重构计划。
|
||||
|
||||
**目标**:
|
||||
- **短期**:解决单点故障风险,优化开发体验,清理关键技术债。
|
||||
- **中期**:提升系统高可用能力,增强监控与可观测性。
|
||||
- **长期**:架构演进,支持大规模并发与复杂业务场景。
|
||||
|
||||
---
|
||||
|
||||
## 2. 短期优化计划 (1-2周)
|
||||
|
||||
**重点**:消除即时风险,提升部署效率。
|
||||
|
||||
### 2.1 统一镜像构建 (High Priority)
|
||||
目前 `backend`, `backend-admin`, `worker`, `celery-beat` 重复构建 4 次,浪费资源且镜像版本可能不一致。
|
||||
|
||||
- **Action Items**:
|
||||
- [x] 修改 `backend/Dockerfile` 为通用基础镜像。
|
||||
- [x] 更新 `docker-compose.yml`,定义 `backend-base` 服务或使用 `image` 标签共享镜像。
|
||||
- [x] 确保所有 Python 服务共用同一构建产物,仅启动命令不同。
|
||||
|
||||
### 2.2 修复 Provider 缓存与限流 (High Priority)
|
||||
内存缓存 (`TTLCache`, `_latency_cache`) 在多进程/多实例下失效。
|
||||
|
||||
- **Action Items**:
|
||||
- [x] 引入 Redis 作为共享缓存后端。
|
||||
- [x] 重构 `_load_provider_cache`,将 Provider 配置缓存至 Redis。
|
||||
- [x] 重构 `stories.py` 中的限流逻辑,使用 `redis-cell` 或简单的 Redis 计数器替代 `TTLCache`。
|
||||
|
||||
### 2.3 拆分 `stories.py` (Medium Priority)
|
||||
`app/api/stories.py` 超过 600 行,包含 API 定义、业务逻辑、验证逻辑,维护困难。
|
||||
|
||||
- **Action Items**:
|
||||
- [x] 创建 `app/services/story_service.py`,迁移生成、润色、PDF生成等核心逻辑。
|
||||
- [x] 创建 `app/schemas/story_schemas.py`,迁移 Pydantic 模型(`GenerateRequest`, `StoryResponse` 等)。
|
||||
- [x] API 层 `stories.py` 仅保留路由定义和依赖注入,调用 Service 层。
|
||||
|
||||
---
|
||||
|
||||
## 3. 中期优化计划 (1-2月)
|
||||
|
||||
**重点**:高可用 (HA) 与系统韧性。
|
||||
|
||||
### 3.1 数据库高可用 (Critical)
|
||||
当前 PostgreSQL 为单点,且 Admin/User 混合使用。
|
||||
|
||||
- **Action Items**:
|
||||
- [ ] 部署 PostgreSQL 主从复制 (Master-Slave)。
|
||||
- [ ] 配置 `PgBouncer` 或 SQLAlchemy 读写分离,减轻主库压力。
|
||||
- [ ] 实施数据库自动备份策略 (如 `pg_dump` 定时上传 S3)。
|
||||
|
||||
### 3.2 消息队列高可用 (Critical)
|
||||
Redis 单点故障将导致 Celery 任务全盘停摆。
|
||||
|
||||
- **Action Items**:
|
||||
- [ ] 迁移至 Redis Sentinel 或 Redis Cluster 模式。
|
||||
- [ ] 更新 Celery 配置以支持 Sentinel/Cluster 连接串。
|
||||
|
||||
### 3.3 增强可观测性 (Important)
|
||||
目前仅有简单的日志,缺乏系统级指标。
|
||||
|
||||
- **Action Items**:
|
||||
- [ ] 集成 Prometheus Client,暴露 `/metrics` 端点。
|
||||
- [ ] 部署 Grafana + Prometheus,监控 API 延迟、QPS、Celery 队列积压情况。
|
||||
- [ ] 完善 `ProviderMetrics`,增加可视化大盘,实时监控 AI 供应商的成本与成功率。
|
||||
|
||||
### 3.4 Phase 3 最小可执行任务清单 (MVP)
|
||||
|
||||
目标:在不大改业务代码的前提下,于一个迭代内完成高可用基础设施闭环。
|
||||
|
||||
- [x] PostgreSQL 主从:新增 `docker-compose.ha.yml`,包含 1 主 1 从与健康检查。
|
||||
- [x] PostgreSQL 备份:新增每日备份任务(`pg_dump`)与 7 天保留策略。
|
||||
- [x] Redis Sentinel:新增 1 主 2 哨兵最小拓扑,并验证故障切换。
|
||||
- [x] Celery 连接:更新 Celery broker/result backend 配置,支持 Sentinel 连接串。
|
||||
- [x] 回归验证:执行一次故事生成 + 异步任务链路(worker/beat)冒烟测试。
|
||||
- [x] 运行手册:补充故障切换与恢复步骤文档(PostgreSQL/Redis/Celery)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 长期架构演进 (季度规划)
|
||||
|
||||
**重点**:业务解耦与规模化。
|
||||
|
||||
### 4.1 统一 API 网关
|
||||
- **当前**:前端直连后端端口,CORS 配置分散。
|
||||
- **演进**:引入 Traefik 或 Nginx 作为统一网关,管理路由、SSL、全局限流、统一鉴权。
|
||||
|
||||
### 4.2 前端工程合并
|
||||
- **当前**:User App 和 Admin Console 是完全独立的两个项目,但在组件和工具链上高度重复。
|
||||
- **演进**:使用一种 Monorepo 策略或基于路由的单一应用策略,共享组件库和类型定义,减少维护成本。
|
||||
|
||||
### 4.3 事件驱动架构完善
|
||||
- **当前**:部分业务逻辑耦合在 API 中。
|
||||
- **演进**:扩展事件总线,将“阅读记录”、“成就解锁”、“通知推送”等非核心链路完全异步化,通过 Domain Events 解耦。
|
||||
|
||||
---
|
||||
|
||||
## 5. 实施路线图
|
||||
|
||||
| 阶段 | 时间估算 | 关键里程碑 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Phase 1: 基础夯实** | Week 1-2 | Docker 构建优化上线,Redis 替代内存缓存。 |
|
||||
| **Phase 2: 代码重构** | Week 3-4 | `stories.py` 拆分完成,Service 层建立。 |
|
||||
| **Phase 3: 高可用建设** | Month 2 | 数据库与 Redis 实现主备/集群模式。 |
|
||||
| **Phase 4: 监控体系** | Month 2 | Grafana 监控大盘上线,关键指标报警配置完毕。 |
|
||||
@@ -1,52 +0,0 @@
|
||||
# `stories.py` 拆分分析 (Phase 2 准备)
|
||||
|
||||
## 当前职责
|
||||
|
||||
`app/api/stories.py` (591 行) 承担了以下职责:
|
||||
|
||||
| 职责 | 行数 | 描述 |
|
||||
|---|---|---|
|
||||
| Pydantic 模型 | ~50 行 | `GenerateRequest`, `StoryResponse`, `FullStoryResponse` 等 |
|
||||
| 验证逻辑 | ~40 行 | `_validate_profile_and_universe` |
|
||||
| 路由 + 业务 | ~300 行 | `generate_story`, `generate_story_full`, `generate_story_stream` |
|
||||
| 绘本逻辑 | ~170 行 | `generate_storybook_api` (含并行图片生成) |
|
||||
| 成就查询 | ~30 行 | `get_story_achievements` |
|
||||
|
||||
## 缺失端点
|
||||
|
||||
测试中引用但 **未实现** 的端点(这些应在拆分时一并补充):
|
||||
|
||||
- `GET /api/stories` — 故事列表 (分页)
|
||||
- `GET /api/stories/{id}` — 故事详情
|
||||
- `DELETE /api/stories/{id}` — 故事删除
|
||||
- `POST /api/image/generate/{id}` — 封面图片生成
|
||||
- `GET /api/audio/{id}` — 语音朗读
|
||||
|
||||
## 建议拆分结构
|
||||
|
||||
```
|
||||
app/
|
||||
├── schemas/
|
||||
│ └── story_schemas.py # [NEW] Pydantic 模型
|
||||
├── services/
|
||||
│ └── story_service.py # [NEW] 核心业务逻辑
|
||||
└── api/
|
||||
├── stories.py # [SLIM] 路由定义 + 依赖注入
|
||||
└── stories_storybook.py # [NEW] 绘本相关端点 (可选)
|
||||
```
|
||||
|
||||
### `story_schemas.py`
|
||||
- 迁移所有 Pydantic 模型
|
||||
- 包括 `GenerateRequest`, `StoryResponse`, `FullStoryResponse`, `StorybookRequest`, `StorybookResponse` 等
|
||||
|
||||
### `story_service.py`
|
||||
- `validate_profile_and_universe()` — 验证逻辑
|
||||
- `create_story()` — 故事入库
|
||||
- `generate_and_save_story()` — 生成 + 保存联合操作
|
||||
- `generate_storybook_with_images()` — 绘本并行图片生成
|
||||
- 补充: `list_stories()`, `get_story()`, `delete_story()`
|
||||
|
||||
### `stories.py` (瘦路由层)
|
||||
- 仅保留 `@router` 装饰器和依赖注入
|
||||
- 调用 service 层完成业务逻辑
|
||||
- 预计 150-200 行
|
||||
@@ -157,15 +157,12 @@ async function generateStory() {
|
||||
|
||||
storybookStore.setStorybook(response)
|
||||
close()
|
||||
router.push('/storybook/view')
|
||||
const storybookPath = response.id ? `/storybook/view/${response.id}` : '/storybook/view'
|
||||
router.push(storybookPath)
|
||||
} else {
|
||||
const result = await api.post<any>('/api/stories/generate/full', payload)
|
||||
const query: Record<string, string> = {}
|
||||
if (result.errors && Object.keys(result.errors).length > 0) {
|
||||
if (result.errors.image) query.imageError = '1'
|
||||
}
|
||||
close()
|
||||
router.push({ path: `/story/${result.id}`, query })
|
||||
router.push(`/story/${result.id}`)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : '生成失败'
|
||||
|
||||
@@ -44,7 +44,7 @@ const router = createRouter({
|
||||
component: () => import('./views/StoryDetail.vue'),
|
||||
},
|
||||
{
|
||||
path: '/storybook/view',
|
||||
path: '/storybook/view/:id?',
|
||||
name: 'storybook-viewer',
|
||||
component: () => import('./views/StorybookViewer.vue'),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user