update:1.更新项目版本

This commit is contained in:
xiamuceer
2025-11-25 19:35:38 +08:00
parent d014383b69
commit 096c2ebbd1
4 changed files with 211 additions and 3 deletions
+200 -1
View File
@@ -1062,7 +1062,6 @@ async def outline_generator(
logger.info("大纲生成事务已回滚(异常)")
yield await SSEResponse.send_error(f"生成失败: {str(e)}")
@router.post("/outline", summary="流式生成完整大纲")
async def generate_outline_stream(
data: Dict[str, Any],
@@ -1074,3 +1073,203 @@ async def generate_outline_stream(
"""
return create_sse_response(outline_generator(data, db, user_ai_service))
async def world_building_regenerate_generator(
project_id: str,
data: Dict[str, Any],
db: AsyncSession,
user_ai_service: AIService
) -> AsyncGenerator[str, None]:
"""世界观重新生成流式生成器"""
db_committed = False
try:
yield await SSEResponse.send_progress("开始重新生成世界观...", 10)
# 获取项目信息
result = await db.execute(
select(Project).where(Project.id == project_id)
)
project = result.scalar_one_or_none()
if not project:
yield await SSEResponse.send_error("项目不存在", 404)
return
# 提取参数
provider = data.get("provider")
model = data.get("model")
enable_mcp = data.get("enable_mcp", True)
user_id = data.get("user_id")
# 获取基础提示词
yield await SSEResponse.send_progress("准备AI提示词...", 15)
base_prompt = prompt_service.get_world_building_prompt(
title=project.title,
theme=project.theme or "未设定",
genre=project.genre or "通用"
)
# MCP工具增强:收集参考资料
reference_materials = ""
if enable_mcp and user_id:
try:
from app.services.mcp_tool_service import mcp_tool_service
available_tools = await mcp_tool_service.get_user_enabled_tools(
user_id=user_id,
db_session=db
)
if available_tools:
yield await SSEResponse.send_progress("🔍 尝试使用MCP工具收集参考资料...", 18)
planning_prompt = f"""你正在为小说《{project.title}》重新设计世界观。
【小说信息】
- 题材:{project.genre}
- 主题:{project.theme}
- 简介:{project.description or '未设定'}
【任务】
请使用可用工具搜索相关背景资料,帮助构建更真实、更有深度的世界观设定。
你可以查询:
1. 历史背景(如果是历史题材)
2. 地理环境和文化特征
3. 相关领域的专业知识
4. 类似作品的设定参考
请查询最关键的1个问题(不要超过1个)。"""
planning_result = await user_ai_service.generate_text_with_mcp(
prompt=planning_prompt,
user_id=user_id,
db_session=db,
enable_mcp=True,
max_tool_rounds=1,
tool_choice="auto",
provider=None,
model=None
)
if planning_result.get("tool_calls_made", 0) > 0:
yield await SSEResponse.send_progress(
f"✅ MCP工具调用成功({planning_result['tool_calls_made']}次)",
25
)
reference_materials = planning_result.get("content", "")
else:
logger.debug("MCP工具可用但AI未选择使用")
else:
logger.debug(f"用户 {user_id} 未启用MCP工具,跳过MCP增强")
except Exception as e:
logger.warning(f"MCP工具调用失败(降级处理): {e}")
yield await SSEResponse.send_progress("⚠️ MCP工具暂时不可用,使用基础模式", 25)
# 构建增强提示词
if reference_materials:
enhanced_prompt = f"""{base_prompt}
【参考资料】
以下是通过MCP工具收集的真实背景资料,请参考这些信息构建更真实的世界观:
{reference_materials}
请结合上述资料,生成符合历史/现实的世界观设定。"""
final_prompt = enhanced_prompt
yield await SSEResponse.send_progress("💡 已整合参考资料,开始生成世界观...", 30)
else:
final_prompt = base_prompt
yield await SSEResponse.send_progress("正在调用AI生成...", 30)
# 流式生成世界观
accumulated_text = ""
chunk_count = 0
async for chunk in user_ai_service.generate_text_stream(
prompt=final_prompt,
provider=provider,
model=model
):
chunk_count += 1
accumulated_text += chunk
yield await SSEResponse.send_chunk(chunk)
if chunk_count % 5 == 0:
progress = min(30 + (chunk_count // 5), 70)
yield await SSEResponse.send_progress(f"生成中... ({len(accumulated_text)}字符)", progress)
if chunk_count % 20 == 0:
yield await SSEResponse.send_heartbeat()
# 解析结果
yield await SSEResponse.send_progress("解析AI返回结果...", 80)
world_data = {}
try:
cleaned_text = accumulated_text.strip()
if cleaned_text.startswith('```json'):
cleaned_text = cleaned_text[7:].lstrip('\n\r')
elif cleaned_text.startswith('```'):
cleaned_text = cleaned_text[3:].lstrip('\n\r')
if cleaned_text.endswith('```'):
cleaned_text = cleaned_text[:-3].rstrip('\n\r')
cleaned_text = cleaned_text.strip()
world_data = json.loads(cleaned_text)
except json.JSONDecodeError as e:
logger.error(f"世界构建JSON解析失败: {e}")
world_data = {
"time_period": "AI返回格式错误,请重试",
"location": "AI返回格式错误,请重试",
"atmosphere": "AI返回格式错误,请重试",
"rules": "AI返回格式错误,请重试"
}
# 不保存到数据库,仅返回生成结果供用户预览
yield await SSEResponse.send_progress("生成完成,等待用户确认...", 90)
# 发送最终结果(不包含project_id,表示未保存)
yield await SSEResponse.send_result({
"time_period": world_data.get("time_period"),
"location": world_data.get("location"),
"atmosphere": world_data.get("atmosphere"),
"rules": world_data.get("rules")
})
yield await SSEResponse.send_progress("完成!", 100, "success")
yield await SSEResponse.send_done()
except GeneratorExit:
logger.warning("世界观重新生成器被提前关闭")
if not db_committed and db.in_transaction():
await db.rollback()
logger.info("世界观重新生成事务已回滚(GeneratorExit")
except Exception as e:
logger.error(f"世界观重新生成失败: {str(e)}")
if not db_committed and db.in_transaction():
await db.rollback()
logger.info("世界观重新生成事务已回滚(异常)")
yield await SSEResponse.send_error(f"生成失败: {str(e)}")
@router.post("/world-building/{project_id}/regenerate", summary="流式重新生成世界观")
async def regenerate_world_building_stream(
project_id: str,
request: Request,
data: Dict[str, Any],
db: AsyncSession = Depends(get_db),
user_ai_service: AIService = Depends(get_user_ai_service)
):
"""
使用SSE流式重新生成世界观,避免超时
前端使用EventSource接收实时进度和结果
"""
# 从中间件注入user_id到data中
if hasattr(request.state, 'user_id'):
data['user_id'] = request.state.user_id
return create_sse_response(world_building_regenerate_generator(project_id, data, db, user_ai_service))
+1 -1
View File
@@ -10,7 +10,7 @@ class Project(Base):
__tablename__ = "projects"
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = Column(String(36), nullable=False, index=True, comment="用户ID")
user_id = Column(String(100), nullable=False, index=True, comment="用户ID")
title = Column(String(200), nullable=False, comment="项目标题")
description = Column(Text, comment="项目简介")
theme = Column(Text, comment="主题")
+9
View File
@@ -0,0 +1,9 @@
-- 修复 projects 表中 user_id 字段长度不足的问题
-- 将 user_id 从 VARCHAR(36) 扩展到 VARCHAR(100)
ALTER TABLE projects ALTER COLUMN user_id TYPE VARCHAR(100);
-- 验证修改
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_name = 'projects' AND column_name = 'user_id';