update:更新智能引入角色/组织sse推送逻辑,优化进度展示

This commit is contained in:
xiamuceer
2026-01-07 14:25:40 +08:00
parent 609426fd7d
commit dff2834c4f
3 changed files with 202 additions and 263 deletions
+143 -237
View File
@@ -75,6 +75,48 @@ async def verify_project_access(project_id: str, user_id: str, db: AsyncSession)
return project 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="创建大纲") @router.post("", response_model=OutlineResponse, summary="创建大纲")
async def create_outline( async def create_outline(
outline: OutlineCreate, outline: OutlineCreate,
@@ -145,26 +187,8 @@ async def get_project_outlines(
request: Request, request: Request,
db: AsyncSession = Depends(get_db) db: AsyncSession = Depends(get_db)
): ):
"""获取指定项目的所有大纲(路径参数版本)""" """获取指定项目的所有大纲(路径参数版本,兼容旧API"""
# 验证用户权限 return await get_outlines(project_id, request, 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)
@router.get("/{outline_id}", response_model=OutlineResponse, summary="获取大纲详情") @router.get("/{outline_id}", response_model=OutlineResponse, summary="获取大纲详情")
@@ -406,18 +430,7 @@ async def predict_characters(
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief = "" all_chapters_brief = _build_chapters_brief(existing_outlines)
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
])
# 调用自动角色服务进行预测 # 调用自动角色服务进行预测
from app.services.auto_character_service import get_auto_character_service 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 from app.models.relationship import Organization
# 验证用户权限 # 验证用户权限
@@ -510,40 +522,10 @@ async def predict_organizations(
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
# 获取现有组织 # 获取现有组织
organizations_result = await db.execute( existing_organizations = await _get_existing_organizations(request_data.project_id, db)
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
})
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief = "" all_chapters_brief = _build_chapters_brief(existing_outlines)
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
])
# 调用自动组织服务进行预测 # 调用自动组织服务进行预测
from app.services.auto_organization_service import get_auto_organization_service 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) select(Character).where(Character.project_id == project.id)
) )
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
# 🔍 MCP工具增强:收集情节设计参考资料(优化版) # 🔍 MCP工具增强:收集情节设计参考资料(优化版)
mcp_reference_materials = "" mcp_reference_materials = ""
@@ -894,11 +872,7 @@ async def _continue_outline(
select(Character).where(Character.project_id == project.id) select(Character).where(Character.project_id == project.id)
) )
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
# 情节阶段指导 # 情节阶段指导
stage_instructions = { stage_instructions = {
@@ -978,11 +952,7 @@ async def _continue_outline(
logger.info(f"ℹ️ 【确认模式】所有角色均已存在,无需创建") logger.info(f"ℹ️ 【确认模式】所有角色均已存在,无需创建")
# 更新角色信息(供后续大纲生成使用) # 更新角色信息(供后续大纲生成使用)
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
except Exception as e: except Exception as e:
logger.error(f"⚠️ 【确认模式】创建确认角色失败: {e}", exc_info=True) 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 from app.services.auto_character_service import get_auto_character_service
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief_for_analysis = "" all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
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
])
auto_char_service = get_auto_character_service(user_ai_service) 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.extend(auto_result["new_characters"])
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
else: else:
logger.info(f"✅ 【直接创建模式】AI判断无需引入新角色,继续生成大纲") logger.info(f"✅ 【直接创建模式】AI判断无需引入新角色,继续生成大纲")
@@ -1091,29 +1046,8 @@ async def _continue_outline(
# 🏛️ 【组织引入】在生成大纲前预测并创建组织 # 🏛️ 【组织引入】在生成大纲前预测并创建组织
if request.enable_auto_organizations: if request.enable_auto_organizations:
from app.models.relationship import Organization
# 获取现有组织 # 获取现有组织
organizations_result = await db.execute( existing_organizations = await _get_existing_organizations(project.id, db)
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
})
# 检查是否有用户确认的组织列表 # 检查是否有用户确认的组织列表
if request.confirmed_organizations: if request.confirmed_organizations:
@@ -1177,11 +1111,7 @@ async def _continue_outline(
await db.commit() await db.commit()
# 更新角色信息(供后续大纲生成使用) # 更新角色信息(供后续大纲生成使用)
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
logger.info(f"✅ 【确认模式】成功创建 {len(request.confirmed_organizations)} 个用户确认的组织") 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 from app.services.auto_organization_service import get_auto_organization_service
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief_for_org_analysis = "" all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
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
])
auto_org_service = get_auto_organization_service(user_ai_service) auto_org_service = get_auto_organization_service(user_ai_service)
@@ -1281,11 +1200,7 @@ async def _continue_outline(
org_char = org_item.get("character") org_char = org_item.get("character")
if org_char: if org_char:
characters.append(org_char) characters.append(org_char)
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
else: else:
logger.info(f"✅ 【直接创建模式】AI判断无需引入新组织,继续生成大纲") logger.info(f"✅ 【直接创建模式】AI判断无需引入新组织,继续生成大纲")
@@ -1711,11 +1626,7 @@ async def new_outline_generator(
select(Character).where(Character.project_id == project_id) select(Character).where(Character.project_id == project_id)
) )
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
# 🔍 MCP工具增强:收集情节设计参考资料(优化版) # 🔍 MCP工具增强:收集情节设计参考资料(优化版)
mcp_reference_materials = "" mcp_reference_materials = ""
@@ -2049,11 +1960,7 @@ async def continue_outline_generator(
select(Character).where(Character.project_id == project_id) select(Character).where(Character.project_id == project_id)
) )
characters = characters_result.scalars().all() characters = characters_result.scalars().all()
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
# 分批配置 # 分批配置
batch_size = 5 batch_size = 5
@@ -2195,35 +2102,19 @@ async def continue_outline_generator(
from app.services.auto_character_service import get_auto_character_service from app.services.auto_character_service import get_auto_character_service
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief_for_analysis = "" all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
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
])
auto_char_service = get_auto_character_service(user_ai_service) auto_char_service = get_auto_character_service(user_ai_service)
if require_confirmation: if require_confirmation:
# 🔮 预测模式:仅预测角色,不自动创建,需要用户确认 # 🔮 预测模式:仅预测角色,不自动创建,需要用户确认
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"🔮 【预测模式】AI分析角色需求...", "🔮 【预测模式】开始分析角色需求...",
11 12
) )
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新角色") logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新角色")
yield await SSEResponse.send_progress( # 进度消息不使用回调,因为在async generator中无法嵌套yield
"🤖 【预测模式】AI智能预测新角色...",
15
)
auto_result = await auto_char_service.analyze_and_create_characters( auto_result = await auto_char_service.analyze_and_create_characters(
project_id=project_id, project_id=project_id,
outline_content="", # 预测模式不需要大纲内容 outline_content="", # 预测模式不需要大纲内容
@@ -2239,6 +2130,11 @@ async def continue_outline_generator(
preview_only=True # ✅ 仅预测不创建 preview_only=True # ✅ 仅预测不创建
) )
yield await SSEResponse.send_progress(
"✅ 【预测模式】角色需求分析完成",
18
)
# 检查是否需要新角色 # 检查是否需要新角色
if auto_result.get("needs_new_characters") and auto_result.get("predicted_characters"): if auto_result.get("needs_new_characters") and auto_result.get("predicted_characters"):
predicted_count = len(auto_result["predicted_characters"]) predicted_count = len(auto_result["predicted_characters"])
@@ -2266,13 +2162,21 @@ async def continue_outline_generator(
else: else:
# 🚀 直接创建模式:预测后自动创建,无需用户确认 # 🚀 直接创建模式:预测后自动创建,无需用户确认
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"🚀 【直接创建模式】检测并自动创建角色(无需确认)...", "🚀 【直接创建模式】开始分析并创建角色...",
13 14
) )
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新角色") 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, project_id=project_id,
outline_content="", outline_content="",
existing_characters=list(characters), existing_characters=list(characters),
@@ -2284,7 +2188,27 @@ async def continue_outline_generator(
chapter_count=total_chapters_to_generate, chapter_count=total_chapters_to_generate,
plot_stage=data.get("plot_stage", "development"), plot_stage=data.get("plot_stage", "development"),
story_direction=data.get("story_direction", "自然延续"), 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.extend(auto_result["new_characters"])
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
else: else:
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"✅ 【直接创建模式】无需引入新角色,继续生成大纲", "✅ 【直接创建模式】无需引入新角色,继续生成大纲",
@@ -2329,29 +2249,8 @@ async def continue_outline_generator(
# confirmed_organizations = data.get("confirmed_organizations") # confirmed_organizations = data.get("confirmed_organizations")
if enable_auto_organizations: if enable_auto_organizations:
from app.models.relationship import Organization
# 获取现有组织 # 获取现有组织
organizations_result = await db.execute( existing_organizations = await _get_existing_organizations(project_id, db)
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
})
# 检查是否有用户确认的组织列表 # 检查是否有用户确认的组织列表
if confirmed_organizations: if confirmed_organizations:
@@ -2465,34 +2364,17 @@ async def continue_outline_generator(
from app.services.auto_organization_service import get_auto_organization_service from app.services.auto_organization_service import get_auto_organization_service
# 构建已有章节概览 # 构建已有章节概览
all_chapters_brief_for_org_analysis = "" all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
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
])
auto_org_service = get_auto_organization_service(user_ai_service) auto_org_service = get_auto_organization_service(user_ai_service)
if require_org_confirmation: if require_org_confirmation:
# 🔮 预测模式:仅预测组织,不自动创建,需要用户确认 # 🔮 预测模式:仅预测组织,不自动创建,需要用户确认
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"🔮 【预测模式】AI分析组织需求...", "🔮 【预测模式】开始分析组织需求...",
21
)
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
yield await SSEResponse.send_progress(
"🤖 【预测模式】AI智能预测新组织...",
22 22
) )
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
auto_result = await auto_org_service.analyze_and_create_organizations( auto_result = await auto_org_service.analyze_and_create_organizations(
project_id=project_id, project_id=project_id,
@@ -2510,6 +2392,11 @@ async def continue_outline_generator(
preview_only=True # ✅ 仅预测不创建 preview_only=True # ✅ 仅预测不创建
) )
yield await SSEResponse.send_progress(
"✅ 【预测模式】组织需求分析完成",
28
)
# 检查是否需要新组织 # 检查是否需要新组织
if auto_result.get("needs_new_organizations") and auto_result.get("predicted_organizations"): if auto_result.get("needs_new_organizations") and auto_result.get("predicted_organizations"):
predicted_count = len(auto_result["predicted_organizations"]) predicted_count = len(auto_result["predicted_organizations"])
@@ -2537,18 +2424,21 @@ async def continue_outline_generator(
else: else:
# 🚀 直接创建模式:预测后自动创建,无需用户确认 # 🚀 直接创建模式:预测后自动创建,无需用户确认
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"🚀 【直接创建模式】AI分析组织需求...", "🚀 【直接创建模式】开始分析并创建组织...",
23 24
) )
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新组织") logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新组织")
yield await SSEResponse.send_progress( # 使用队列桥接回调和generator
"🤖 【直接创建模式】AI预测并生成组织详情...", import asyncio
25 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, project_id=project_id,
outline_content="", outline_content="",
existing_characters=list(characters), existing_characters=list(characters),
@@ -2561,7 +2451,27 @@ async def continue_outline_generator(
chapter_count=total_chapters_to_generate, chapter_count=total_chapters_to_generate,
plot_stage=data.get("plot_stage", "development"), plot_stage=data.get("plot_stage", "development"),
story_direction=data.get("story_direction", "自然延续"), 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") org_char = org_item.get("character")
if org_char: if org_char:
characters.append(org_char) characters.append(org_char)
characters_info = "\n".join([ characters_info = _build_characters_info(characters)
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
else: else:
yield await SSEResponse.send_progress( yield await SSEResponse.send_progress(
"✅ 【直接创建模式】无需引入新组织,继续生成大纲", "✅ 【直接创建模式】无需引入新组织,继续生成大纲",
+15 -2
View File
@@ -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.ext.asyncio import AsyncSession
from sqlalchemy import select from sqlalchemy import select
import json import json
@@ -33,7 +33,8 @@ class AutoCharacterService:
chapter_count: int = 3, chapter_count: int = 3,
plot_stage: str = "发展", plot_stage: str = "发展",
story_direction: str = "继续推进主线剧情", story_direction: str = "继续推进主线剧情",
preview_only: bool = False preview_only: bool = False,
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
预测性分析并创建需要的新角色(方案A:先角色后大纲) 预测性分析并创建需要的新角色(方案A:先角色后大纲)
@@ -136,6 +137,9 @@ class AutoCharacterService:
logger.info(f" [{idx+1}/{len(character_specs)}] 生成角色规格: {spec_name}") logger.info(f" [{idx+1}/{len(character_specs)}] 生成角色规格: {spec_name}")
logger.debug(f" 角色规格内容: {json.dumps(spec, ensure_ascii=False)}") 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( character_data = await self._generate_character_details(
spec=spec, spec=spec,
@@ -148,6 +152,9 @@ class AutoCharacterService:
logger.debug(f" AI生成的角色数据: {json.dumps(character_data, ensure_ascii=False)[:200]}") 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( character = await self._create_character_record(
project_id=project_id, project_id=project_id,
@@ -158,6 +165,9 @@ class AutoCharacterService:
new_characters.append(character) new_characters.append(character)
logger.info(f" ✅ 创建新角色: {character.name} ({character.role_type}), ID: {character.id}") 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", []) relationships_data = character_data.get("relationships") or character_data.get("relationships_array", [])
logger.info(f" 🔍 检查关系数据:") logger.info(f" 🔍 检查关系数据:")
@@ -170,6 +180,9 @@ class AutoCharacterService:
logger.info(f" 🔗 开始创建 {len(relationships_data)} 条关系...") logger.info(f" 🔗 开始创建 {len(relationships_data)} 条关系...")
for idx, rel in enumerate(relationships_data): for idx, rel in enumerate(relationships_data):
logger.info(f" [{idx+1}] {rel.get('target_character_name')} - {rel.get('relationship_type')}") 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: else:
logger.warning(f" ⚠️ AI返回的角色数据中没有关系信息!") logger.warning(f" ⚠️ AI返回的角色数据中没有关系信息!")
logger.warning(f" 完整的character_data keys: {list(character_data.keys())}") 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.ext.asyncio import AsyncSession
from sqlalchemy import select from sqlalchemy import select
import json import json
@@ -34,7 +34,8 @@ class AutoOrganizationService:
chapter_count: int = 3, chapter_count: int = 3,
plot_stage: str = "发展", plot_stage: str = "发展",
story_direction: str = "继续推进主线剧情", story_direction: str = "继续推进主线剧情",
preview_only: bool = False preview_only: bool = False,
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
预测性分析并创建需要的新组织 预测性分析并创建需要的新组织
@@ -86,6 +87,9 @@ class AutoOrganizationService:
existing_chars_summary = self._build_character_summary(existing_characters) existing_chars_summary = self._build_character_summary(existing_characters)
# 3. AI预测性分析是否需要新组织 # 3. AI预测性分析是否需要新组织
if progress_callback:
await progress_callback("🤖 AI分析组织需求...")
analysis_result = await self._analyze_organization_needs( analysis_result = await self._analyze_organization_needs(
project=project, project=project,
outline_content=outline_content, outline_content=outline_content,
@@ -101,6 +105,9 @@ class AutoOrganizationService:
story_direction=story_direction story_direction=story_direction
) )
if progress_callback:
await progress_callback("✅ 组织需求分析完成")
# 4. 判断是否需要创建组织 # 4. 判断是否需要创建组织
if not analysis_result or not analysis_result.get("needs_new_organizations"): if not analysis_result or not analysis_result.get("needs_new_organizations"):
logger.info("✅ AI判断:当前剧情不需要引入新组织") logger.info("✅ AI判断:当前剧情不需要引入新组织")
@@ -141,6 +148,9 @@ class AutoOrganizationService:
logger.info(f" [{idx+1}/{len(organization_specs)}] 生成组织规格: {spec_name}") logger.info(f" [{idx+1}/{len(organization_specs)}] 生成组织规格: {spec_name}")
logger.debug(f" 组织规格内容: {json.dumps(spec, ensure_ascii=False)}") 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( organization_data = await self._generate_organization_details(
spec=spec, spec=spec,
@@ -154,6 +164,9 @@ class AutoOrganizationService:
logger.debug(f" AI生成的组织数据: {json.dumps(organization_data, ensure_ascii=False)[:200]}") 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记录)
character, organization = await self._create_organization_record( character, organization = await self._create_organization_record(
project_id=project_id, project_id=project_id,
@@ -167,10 +180,17 @@ class AutoOrganizationService:
}) })
logger.info(f" ✅ 创建新组织: {character.name}, ID: {organization.id}") 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", []) members_data = organization_data.get("initial_members", [])
if members_data: if members_data:
logger.info(f" 🔗 开始创建 {len(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( members = await self._create_member_relationships(
organization=organization, organization=organization,
member_specs=members_data, member_specs=members_data,