update:更新智能引入角色/组织sse推送逻辑,优化进度展示
This commit is contained in:
+143
-237
@@ -75,6 +75,48 @@ async def verify_project_access(project_id: str, user_id: str, db: AsyncSession)
|
||||
return project
|
||||
|
||||
|
||||
def _build_chapters_brief(outlines: List[Outline], max_recent: int = 20) -> str:
|
||||
"""构建章节概览字符串"""
|
||||
target = outlines[-max_recent:] if len(outlines) > max_recent else outlines
|
||||
return "\n".join([f"第{o.order_index}章《{o.title}》" for o in target])
|
||||
|
||||
|
||||
def _build_characters_info(characters: List[Character]) -> str:
|
||||
"""构建角色信息字符串"""
|
||||
return "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
|
||||
|
||||
async def _get_existing_organizations(project_id: str, db: AsyncSession) -> List[dict]:
|
||||
"""获取项目现有组织列表"""
|
||||
from app.models.relationship import Organization
|
||||
|
||||
organizations_result = await db.execute(
|
||||
select(Character, Organization)
|
||||
.join(Organization, Character.id == Organization.character_id)
|
||||
.where(
|
||||
Character.project_id == project_id,
|
||||
Character.is_organization == True
|
||||
)
|
||||
)
|
||||
organizations_raw = organizations_result.all()
|
||||
return [
|
||||
{
|
||||
"id": org.id,
|
||||
"name": char.name,
|
||||
"organization_type": char.organization_type,
|
||||
"organization_purpose": char.organization_purpose,
|
||||
"power_level": org.power_level,
|
||||
"location": org.location,
|
||||
"motto": org.motto
|
||||
}
|
||||
for char, org in organizations_raw
|
||||
]
|
||||
|
||||
|
||||
@router.post("", response_model=OutlineResponse, summary="创建大纲")
|
||||
async def create_outline(
|
||||
outline: OutlineCreate,
|
||||
@@ -145,26 +187,8 @@ async def get_project_outlines(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""获取指定项目的所有大纲(路径参数版本)"""
|
||||
# 验证用户权限
|
||||
user_id = getattr(request.state, 'user_id', None)
|
||||
await verify_project_access(project_id, user_id, db)
|
||||
|
||||
# 获取总数
|
||||
count_result = await db.execute(
|
||||
select(func.count(Outline.id)).where(Outline.project_id == project_id)
|
||||
)
|
||||
total = count_result.scalar_one()
|
||||
|
||||
# 获取大纲列表
|
||||
result = await db.execute(
|
||||
select(Outline)
|
||||
.where(Outline.project_id == project_id)
|
||||
.order_by(Outline.order_index)
|
||||
)
|
||||
outlines = result.scalars().all()
|
||||
|
||||
return OutlineListResponse(total=total, items=outlines)
|
||||
"""获取指定项目的所有大纲(路径参数版本,兼容旧API)"""
|
||||
return await get_outlines(project_id, request, db)
|
||||
|
||||
|
||||
@router.get("/{outline_id}", response_model=OutlineResponse, summary="获取大纲详情")
|
||||
@@ -406,18 +430,7 @@ async def predict_characters(
|
||||
characters = characters_result.scalars().all()
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief = _build_chapters_brief(existing_outlines)
|
||||
|
||||
# 调用自动角色服务进行预测
|
||||
from app.services.auto_character_service import get_auto_character_service
|
||||
@@ -479,7 +492,6 @@ async def predict_organizations(
|
||||
|
||||
用于组织确认机制的第一步:在生成大纲前预测组织需求
|
||||
"""
|
||||
from app.schemas.outline import OrganizationPredictionResponse, PredictedOrganization
|
||||
from app.models.relationship import Organization
|
||||
|
||||
# 验证用户权限
|
||||
@@ -510,40 +522,10 @@ async def predict_organizations(
|
||||
characters = characters_result.scalars().all()
|
||||
|
||||
# 获取现有组织
|
||||
organizations_result = await db.execute(
|
||||
select(Character, Organization)
|
||||
.join(Organization, Character.id == Organization.character_id)
|
||||
.where(
|
||||
Character.project_id == request_data.project_id,
|
||||
Character.is_organization == True
|
||||
)
|
||||
)
|
||||
organizations_raw = organizations_result.all()
|
||||
existing_organizations = []
|
||||
for char, org in organizations_raw:
|
||||
existing_organizations.append({
|
||||
"id": org.id,
|
||||
"name": char.name,
|
||||
"organization_type": char.organization_type,
|
||||
"organization_purpose": char.organization_purpose,
|
||||
"power_level": org.power_level,
|
||||
"location": org.location,
|
||||
"motto": org.motto
|
||||
})
|
||||
existing_organizations = await _get_existing_organizations(request_data.project_id, db)
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief = _build_chapters_brief(existing_outlines)
|
||||
|
||||
# 调用自动组织服务进行预测
|
||||
from app.services.auto_organization_service import get_auto_organization_service
|
||||
@@ -613,11 +595,7 @@ async def _generate_new_outline(
|
||||
select(Character).where(Character.project_id == project.id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
# 🔍 MCP工具增强:收集情节设计参考资料(优化版)
|
||||
mcp_reference_materials = ""
|
||||
@@ -894,11 +872,7 @@ async def _continue_outline(
|
||||
select(Character).where(Character.project_id == project.id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
# 情节阶段指导
|
||||
stage_instructions = {
|
||||
@@ -978,11 +952,7 @@ async def _continue_outline(
|
||||
logger.info(f"ℹ️ 【确认模式】所有角色均已存在,无需创建")
|
||||
|
||||
# 更新角色信息(供后续大纲生成使用)
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"⚠️ 【确认模式】创建确认角色失败: {e}", exc_info=True)
|
||||
@@ -992,18 +962,7 @@ async def _continue_outline(
|
||||
from app.services.auto_character_service import get_auto_character_service
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief_for_analysis = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief_for_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief_for_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
|
||||
|
||||
auto_char_service = get_auto_character_service(user_ai_service)
|
||||
|
||||
@@ -1075,11 +1034,7 @@ async def _continue_outline(
|
||||
|
||||
# 更新角色信息(供后续大纲生成使用)
|
||||
characters.extend(auto_result["new_characters"])
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
else:
|
||||
logger.info(f"✅ 【直接创建模式】AI判断无需引入新角色,继续生成大纲")
|
||||
|
||||
@@ -1091,29 +1046,8 @@ async def _continue_outline(
|
||||
|
||||
# 🏛️ 【组织引入】在生成大纲前预测并创建组织
|
||||
if request.enable_auto_organizations:
|
||||
from app.models.relationship import Organization
|
||||
|
||||
# 获取现有组织
|
||||
organizations_result = await db.execute(
|
||||
select(Character, Organization)
|
||||
.join(Organization, Character.id == Organization.character_id)
|
||||
.where(
|
||||
Character.project_id == project.id,
|
||||
Character.is_organization == True
|
||||
)
|
||||
)
|
||||
organizations_raw = organizations_result.all()
|
||||
existing_organizations = []
|
||||
for char, org in organizations_raw:
|
||||
existing_organizations.append({
|
||||
"id": org.id,
|
||||
"name": char.name,
|
||||
"organization_type": char.organization_type,
|
||||
"organization_purpose": char.organization_purpose,
|
||||
"power_level": org.power_level,
|
||||
"location": org.location,
|
||||
"motto": org.motto
|
||||
})
|
||||
existing_organizations = await _get_existing_organizations(project.id, db)
|
||||
|
||||
# 检查是否有用户确认的组织列表
|
||||
if request.confirmed_organizations:
|
||||
@@ -1177,11 +1111,7 @@ async def _continue_outline(
|
||||
await db.commit()
|
||||
|
||||
# 更新角色信息(供后续大纲生成使用)
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
logger.info(f"✅ 【确认模式】成功创建 {len(request.confirmed_organizations)} 个用户确认的组织")
|
||||
|
||||
@@ -1193,18 +1123,7 @@ async def _continue_outline(
|
||||
from app.services.auto_organization_service import get_auto_organization_service
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief_for_org_analysis = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief_for_org_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief_for_org_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
|
||||
|
||||
auto_org_service = get_auto_organization_service(user_ai_service)
|
||||
|
||||
@@ -1281,11 +1200,7 @@ async def _continue_outline(
|
||||
org_char = org_item.get("character")
|
||||
if org_char:
|
||||
characters.append(org_char)
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
else:
|
||||
logger.info(f"✅ 【直接创建模式】AI判断无需引入新组织,继续生成大纲")
|
||||
|
||||
@@ -1711,11 +1626,7 @@ async def new_outline_generator(
|
||||
select(Character).where(Character.project_id == project_id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
# 🔍 MCP工具增强:收集情节设计参考资料(优化版)
|
||||
mcp_reference_materials = ""
|
||||
@@ -2049,11 +1960,7 @@ async def continue_outline_generator(
|
||||
select(Character).where(Character.project_id == project_id)
|
||||
)
|
||||
characters = characters_result.scalars().all()
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
|
||||
# 分批配置
|
||||
batch_size = 5
|
||||
@@ -2195,35 +2102,19 @@ async def continue_outline_generator(
|
||||
from app.services.auto_character_service import get_auto_character_service
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief_for_analysis = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief_for_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief_for_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
|
||||
|
||||
auto_char_service = get_auto_character_service(user_ai_service)
|
||||
|
||||
if require_confirmation:
|
||||
# 🔮 预测模式:仅预测角色,不自动创建,需要用户确认
|
||||
yield await SSEResponse.send_progress(
|
||||
"🔮 【预测模式】AI分析角色需求...",
|
||||
11
|
||||
"🔮 【预测模式】开始分析角色需求...",
|
||||
12
|
||||
)
|
||||
|
||||
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新角色")
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"🤖 【预测模式】AI智能预测新角色...",
|
||||
15
|
||||
)
|
||||
|
||||
# 进度消息不使用回调,因为在async generator中无法嵌套yield
|
||||
auto_result = await auto_char_service.analyze_and_create_characters(
|
||||
project_id=project_id,
|
||||
outline_content="", # 预测模式不需要大纲内容
|
||||
@@ -2239,6 +2130,11 @@ async def continue_outline_generator(
|
||||
preview_only=True # ✅ 仅预测不创建
|
||||
)
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【预测模式】角色需求分析完成",
|
||||
18
|
||||
)
|
||||
|
||||
# 检查是否需要新角色
|
||||
if auto_result.get("needs_new_characters") and auto_result.get("predicted_characters"):
|
||||
predicted_count = len(auto_result["predicted_characters"])
|
||||
@@ -2266,13 +2162,21 @@ async def continue_outline_generator(
|
||||
else:
|
||||
# 🚀 直接创建模式:预测后自动创建,无需用户确认
|
||||
yield await SSEResponse.send_progress(
|
||||
"🚀 【直接创建模式】检测并自动创建新角色(无需确认)...",
|
||||
13
|
||||
"🚀 【直接创建模式】开始分析并创建角色...",
|
||||
14
|
||||
)
|
||||
|
||||
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新角色")
|
||||
|
||||
auto_result = await auto_char_service.analyze_and_create_characters(
|
||||
# 使用队列桥接回调和generator
|
||||
import asyncio
|
||||
progress_queue = asyncio.Queue()
|
||||
|
||||
async def char_progress_callback(message):
|
||||
await progress_queue.put(message)
|
||||
|
||||
# 启动服务任务
|
||||
char_task = asyncio.create_task(
|
||||
auto_char_service.analyze_and_create_characters(
|
||||
project_id=project_id,
|
||||
outline_content="",
|
||||
existing_characters=list(characters),
|
||||
@@ -2284,7 +2188,27 @@ async def continue_outline_generator(
|
||||
chapter_count=total_chapters_to_generate,
|
||||
plot_stage=data.get("plot_stage", "development"),
|
||||
story_direction=data.get("story_direction", "自然延续"),
|
||||
preview_only=False # ✅ 直接创建角色
|
||||
preview_only=False,
|
||||
progress_callback=char_progress_callback
|
||||
)
|
||||
)
|
||||
|
||||
# 在等待任务完成的同时,消费队列中的进度消息
|
||||
char_progress_base = 14
|
||||
while not char_task.done():
|
||||
try:
|
||||
message = await asyncio.wait_for(progress_queue.get(), timeout=0.1)
|
||||
char_progress_base = min(char_progress_base + 1, 17)
|
||||
yield await SSEResponse.send_progress(message, char_progress_base)
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
# 获取结果
|
||||
auto_result = await char_task
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【直接创建模式】角色分析和创建完成",
|
||||
18
|
||||
)
|
||||
|
||||
# 如果创建了新角色,更新角色列表
|
||||
@@ -2302,11 +2226,7 @@ async def continue_outline_generator(
|
||||
|
||||
# 更新角色信息(供后续大纲生成使用)
|
||||
characters.extend(auto_result["new_characters"])
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
else:
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【直接创建模式】无需引入新角色,继续生成大纲",
|
||||
@@ -2329,29 +2249,8 @@ async def continue_outline_generator(
|
||||
# confirmed_organizations = data.get("confirmed_organizations")
|
||||
|
||||
if enable_auto_organizations:
|
||||
from app.models.relationship import Organization
|
||||
|
||||
# 获取现有组织
|
||||
organizations_result = await db.execute(
|
||||
select(Character, Organization)
|
||||
.join(Organization, Character.id == Organization.character_id)
|
||||
.where(
|
||||
Character.project_id == project_id,
|
||||
Character.is_organization == True
|
||||
)
|
||||
)
|
||||
organizations_raw = organizations_result.all()
|
||||
existing_organizations = []
|
||||
for char, org in organizations_raw:
|
||||
existing_organizations.append({
|
||||
"id": org.id,
|
||||
"name": char.name,
|
||||
"organization_type": char.organization_type,
|
||||
"organization_purpose": char.organization_purpose,
|
||||
"power_level": org.power_level,
|
||||
"location": org.location,
|
||||
"motto": org.motto
|
||||
})
|
||||
existing_organizations = await _get_existing_organizations(project_id, db)
|
||||
|
||||
# 检查是否有用户确认的组织列表
|
||||
if confirmed_organizations:
|
||||
@@ -2465,34 +2364,17 @@ async def continue_outline_generator(
|
||||
from app.services.auto_organization_service import get_auto_organization_service
|
||||
|
||||
# 构建已有章节概览
|
||||
all_chapters_brief_for_org_analysis = ""
|
||||
if len(existing_outlines) > 20:
|
||||
recent_20 = existing_outlines[-20:]
|
||||
all_chapters_brief_for_org_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in recent_20
|
||||
])
|
||||
else:
|
||||
all_chapters_brief_for_org_analysis = "\n".join([
|
||||
f"第{o.order_index}章《{o.title}》"
|
||||
for o in existing_outlines
|
||||
])
|
||||
all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
|
||||
|
||||
auto_org_service = get_auto_organization_service(user_ai_service)
|
||||
|
||||
if require_org_confirmation:
|
||||
# 🔮 预测模式:仅预测组织,不自动创建,需要用户确认
|
||||
yield await SSEResponse.send_progress(
|
||||
"🔮 【预测模式】AI分析组织需求...",
|
||||
21
|
||||
)
|
||||
|
||||
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"🤖 【预测模式】AI智能预测新组织...",
|
||||
"🔮 【预测模式】开始分析组织需求...",
|
||||
22
|
||||
)
|
||||
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
|
||||
|
||||
auto_result = await auto_org_service.analyze_and_create_organizations(
|
||||
project_id=project_id,
|
||||
@@ -2510,6 +2392,11 @@ async def continue_outline_generator(
|
||||
preview_only=True # ✅ 仅预测不创建
|
||||
)
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【预测模式】组织需求分析完成",
|
||||
28
|
||||
)
|
||||
|
||||
# 检查是否需要新组织
|
||||
if auto_result.get("needs_new_organizations") and auto_result.get("predicted_organizations"):
|
||||
predicted_count = len(auto_result["predicted_organizations"])
|
||||
@@ -2537,18 +2424,21 @@ async def continue_outline_generator(
|
||||
else:
|
||||
# 🚀 直接创建模式:预测后自动创建,无需用户确认
|
||||
yield await SSEResponse.send_progress(
|
||||
"🚀 【直接创建模式】AI分析组织需求...",
|
||||
23
|
||||
"🚀 【直接创建模式】开始分析并创建组织...",
|
||||
24
|
||||
)
|
||||
|
||||
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新组织")
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"🤖 【直接创建模式】AI预测并生成组织详情...",
|
||||
25
|
||||
)
|
||||
# 使用队列桥接回调和generator
|
||||
import asyncio
|
||||
org_progress_queue = asyncio.Queue()
|
||||
|
||||
auto_result = await auto_org_service.analyze_and_create_organizations(
|
||||
async def org_progress_callback(message):
|
||||
await org_progress_queue.put(message)
|
||||
|
||||
# 启动服务任务
|
||||
org_task = asyncio.create_task(
|
||||
auto_org_service.analyze_and_create_organizations(
|
||||
project_id=project_id,
|
||||
outline_content="",
|
||||
existing_characters=list(characters),
|
||||
@@ -2561,7 +2451,27 @@ async def continue_outline_generator(
|
||||
chapter_count=total_chapters_to_generate,
|
||||
plot_stage=data.get("plot_stage", "development"),
|
||||
story_direction=data.get("story_direction", "自然延续"),
|
||||
preview_only=False # ✅ 直接创建组织
|
||||
preview_only=False,
|
||||
progress_callback=org_progress_callback
|
||||
)
|
||||
)
|
||||
|
||||
# 在等待任务完成的同时,消费队列中的进度消息
|
||||
org_progress_base = 24
|
||||
while not org_task.done():
|
||||
try:
|
||||
message = await asyncio.wait_for(org_progress_queue.get(), timeout=0.1)
|
||||
org_progress_base = min(org_progress_base + 1, 27)
|
||||
yield await SSEResponse.send_progress(message, org_progress_base)
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
# 获取结果
|
||||
auto_result = await org_task
|
||||
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【直接创建模式】组织分析和创建完成",
|
||||
28
|
||||
)
|
||||
|
||||
# 如果创建了新组织,更新角色列表
|
||||
@@ -2587,11 +2497,7 @@ async def continue_outline_generator(
|
||||
org_char = org_item.get("character")
|
||||
if org_char:
|
||||
characters.append(org_char)
|
||||
characters_info = "\n".join([
|
||||
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
|
||||
f"{char.personality[:100] if char.personality else '暂无描述'}"
|
||||
for char in characters
|
||||
])
|
||||
characters_info = _build_characters_info(characters)
|
||||
else:
|
||||
yield await SSEResponse.send_progress(
|
||||
"✅ 【直接创建模式】无需引入新组织,继续生成大纲",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""自动角色引入服务 - 在续写大纲时根据剧情推进自动引入新角色"""
|
||||
from typing import List, Dict, Any, Optional
|
||||
from typing import List, Dict, Any, Optional, Callable, Awaitable
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
import json
|
||||
@@ -33,7 +33,8 @@ class AutoCharacterService:
|
||||
chapter_count: int = 3,
|
||||
plot_stage: str = "发展",
|
||||
story_direction: str = "继续推进主线剧情",
|
||||
preview_only: bool = False
|
||||
preview_only: bool = False,
|
||||
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
预测性分析并创建需要的新角色(方案A:先角色后大纲)
|
||||
@@ -136,6 +137,9 @@ class AutoCharacterService:
|
||||
logger.info(f" [{idx+1}/{len(character_specs)}] 生成角色规格: {spec_name}")
|
||||
logger.debug(f" 角色规格内容: {json.dumps(spec, ensure_ascii=False)}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"🎨 [{idx+1}/{len(character_specs)}] 生成角色详情: {spec_name}")
|
||||
|
||||
# 生成角色详细信息
|
||||
character_data = await self._generate_character_details(
|
||||
spec=spec,
|
||||
@@ -148,6 +152,9 @@ class AutoCharacterService:
|
||||
|
||||
logger.debug(f" AI生成的角色数据: {json.dumps(character_data, ensure_ascii=False)[:200]}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"💾 [{idx+1}/{len(character_specs)}] 保存角色: {character_data.get('name', spec_name)}")
|
||||
|
||||
# 创建角色记录
|
||||
character = await self._create_character_record(
|
||||
project_id=project_id,
|
||||
@@ -158,6 +165,9 @@ class AutoCharacterService:
|
||||
new_characters.append(character)
|
||||
logger.info(f" ✅ 创建新角色: {character.name} ({character.role_type}), ID: {character.id}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"✅ [{idx+1}/{len(character_specs)}] 角色创建成功: {character.name}")
|
||||
|
||||
# 建立关系(兼容两种字段名)
|
||||
relationships_data = character_data.get("relationships") or character_data.get("relationships_array", [])
|
||||
logger.info(f" 🔍 检查关系数据:")
|
||||
@@ -170,6 +180,9 @@ class AutoCharacterService:
|
||||
logger.info(f" 🔗 开始创建 {len(relationships_data)} 条关系...")
|
||||
for idx, rel in enumerate(relationships_data):
|
||||
logger.info(f" [{idx+1}] {rel.get('target_character_name')} - {rel.get('relationship_type')}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"🔗 [{idx+1}/{len(character_specs)}] 建立 {len(relationships_data)} 个关系")
|
||||
else:
|
||||
logger.warning(f" ⚠️ AI返回的角色数据中没有关系信息!")
|
||||
logger.warning(f" 完整的character_data keys: {list(character_data.keys())}")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""自动组织引入服务 - 在续写大纲时根据剧情推进自动引入新组织"""
|
||||
from typing import List, Dict, Any, Optional
|
||||
from typing import List, Dict, Any, Optional, Callable, Awaitable
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
import json
|
||||
@@ -34,7 +34,8 @@ class AutoOrganizationService:
|
||||
chapter_count: int = 3,
|
||||
plot_stage: str = "发展",
|
||||
story_direction: str = "继续推进主线剧情",
|
||||
preview_only: bool = False
|
||||
preview_only: bool = False,
|
||||
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
预测性分析并创建需要的新组织
|
||||
@@ -86,6 +87,9 @@ class AutoOrganizationService:
|
||||
existing_chars_summary = self._build_character_summary(existing_characters)
|
||||
|
||||
# 3. AI预测性分析是否需要新组织
|
||||
if progress_callback:
|
||||
await progress_callback("🤖 AI分析组织需求...")
|
||||
|
||||
analysis_result = await self._analyze_organization_needs(
|
||||
project=project,
|
||||
outline_content=outline_content,
|
||||
@@ -101,6 +105,9 @@ class AutoOrganizationService:
|
||||
story_direction=story_direction
|
||||
)
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback("✅ 组织需求分析完成")
|
||||
|
||||
# 4. 判断是否需要创建组织
|
||||
if not analysis_result or not analysis_result.get("needs_new_organizations"):
|
||||
logger.info("✅ AI判断:当前剧情不需要引入新组织")
|
||||
@@ -141,6 +148,9 @@ class AutoOrganizationService:
|
||||
logger.info(f" [{idx+1}/{len(organization_specs)}] 生成组织规格: {spec_name}")
|
||||
logger.debug(f" 组织规格内容: {json.dumps(spec, ensure_ascii=False)}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"🏛️ [{idx+1}/{len(organization_specs)}] 生成组织详情: {spec_name}")
|
||||
|
||||
# 生成组织详细信息
|
||||
organization_data = await self._generate_organization_details(
|
||||
spec=spec,
|
||||
@@ -154,6 +164,9 @@ class AutoOrganizationService:
|
||||
|
||||
logger.debug(f" AI生成的组织数据: {json.dumps(organization_data, ensure_ascii=False)[:200]}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"💾 [{idx+1}/{len(organization_specs)}] 保存组织: {organization_data.get('name', spec_name)}")
|
||||
|
||||
# 创建组织记录(先创建Character记录,再创建Organization记录)
|
||||
character, organization = await self._create_organization_record(
|
||||
project_id=project_id,
|
||||
@@ -167,10 +180,17 @@ class AutoOrganizationService:
|
||||
})
|
||||
logger.info(f" ✅ 创建新组织: {character.name}, ID: {organization.id}")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"✅ [{idx+1}/{len(organization_specs)}] 组织创建成功: {character.name}")
|
||||
|
||||
# 建立成员关系
|
||||
members_data = organization_data.get("initial_members", [])
|
||||
if members_data:
|
||||
logger.info(f" 🔗 开始创建 {len(members_data)} 个成员关系...")
|
||||
|
||||
if progress_callback:
|
||||
await progress_callback(f"🔗 [{idx+1}/{len(organization_specs)}] 建立 {len(members_data)} 个成员关系")
|
||||
|
||||
members = await self._create_member_relationships(
|
||||
organization=organization,
|
||||
member_specs=members_data,
|
||||
|
||||
Reference in New Issue
Block a user