update: 更新伏笔管理删除和同步逻辑,避免重复引入
This commit is contained in:
+1
-1
@@ -114,7 +114,7 @@ logo.ico
|
|||||||
dist_embed/
|
dist_embed/
|
||||||
embed_build.py
|
embed_build.py
|
||||||
|
|
||||||
|
.roo/
|
||||||
data/
|
data/
|
||||||
docs/
|
docs/
|
||||||
data_old/
|
data_old/
|
||||||
|
|||||||
@@ -146,6 +146,25 @@ async def analyze_chapter(
|
|||||||
chapter.chapter_number
|
chapter.chapter_number
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 重新分析前,先清理该章节旧记忆(关系库 + 向量库)
|
||||||
|
old_memories_result = await db.execute(
|
||||||
|
select(StoryMemory).where(StoryMemory.chapter_id == chapter_id)
|
||||||
|
)
|
||||||
|
old_memories = old_memories_result.scalars().all()
|
||||||
|
for old_mem in old_memories:
|
||||||
|
await db.delete(old_mem)
|
||||||
|
await db.flush()
|
||||||
|
|
||||||
|
if user_id:
|
||||||
|
try:
|
||||||
|
await memory_service.delete_chapter_memories(
|
||||||
|
user_id=user_id,
|
||||||
|
project_id=project_id,
|
||||||
|
chapter_id=chapter_id
|
||||||
|
)
|
||||||
|
except Exception as vector_delete_error:
|
||||||
|
logger.warning(f"⚠️ 清理章节向量记忆失败(继续分析): {str(vector_delete_error)}")
|
||||||
|
|
||||||
# 保存记忆到数据库和向量库
|
# 保存记忆到数据库和向量库
|
||||||
saved_count = 0
|
saved_count = 0
|
||||||
for mem_data in memories_data:
|
for mem_data in memories_data:
|
||||||
@@ -178,6 +197,59 @@ async def analyze_chapter(
|
|||||||
|
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
|
entity_changes = {
|
||||||
|
"careers": {"updated_count": 0, "changes": []},
|
||||||
|
"character_states": {
|
||||||
|
"state_updated_count": 0,
|
||||||
|
"relationship_created_count": 0,
|
||||||
|
"relationship_updated_count": 0,
|
||||||
|
"org_updated_count": 0,
|
||||||
|
"changes": []
|
||||||
|
},
|
||||||
|
"organization_states": {"updated_count": 0, "changes": []}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 更新角色职业 / 角色状态关系 / 组织状态
|
||||||
|
if analysis_result.get('character_states'):
|
||||||
|
try:
|
||||||
|
from app.services.career_update_service import CareerUpdateService
|
||||||
|
career_update_result = await CareerUpdateService.update_careers_from_analysis(
|
||||||
|
db=db,
|
||||||
|
project_id=project_id,
|
||||||
|
character_states=analysis_result.get('character_states', []),
|
||||||
|
chapter_id=chapter_id,
|
||||||
|
chapter_number=chapter.chapter_number
|
||||||
|
)
|
||||||
|
entity_changes["careers"] = career_update_result
|
||||||
|
except Exception as career_error:
|
||||||
|
logger.error(f"⚠️ 更新角色职业失败(不影响分析结果): {str(career_error)}", exc_info=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from app.services.character_state_update_service import CharacterStateUpdateService
|
||||||
|
state_update_result = await CharacterStateUpdateService.update_from_analysis(
|
||||||
|
db=db,
|
||||||
|
project_id=project_id,
|
||||||
|
character_states=analysis_result.get('character_states', []),
|
||||||
|
chapter_id=chapter_id,
|
||||||
|
chapter_number=chapter.chapter_number
|
||||||
|
)
|
||||||
|
entity_changes["character_states"] = state_update_result
|
||||||
|
except Exception as state_error:
|
||||||
|
logger.error(f"⚠️ 更新角色状态、关系和组织成员失败(不影响分析结果): {str(state_error)}", exc_info=True)
|
||||||
|
|
||||||
|
if analysis_result.get('organization_states'):
|
||||||
|
try:
|
||||||
|
from app.services.character_state_update_service import CharacterStateUpdateService
|
||||||
|
org_state_result = await CharacterStateUpdateService.update_organization_states(
|
||||||
|
db=db,
|
||||||
|
project_id=project_id,
|
||||||
|
organization_states=analysis_result.get('organization_states', []),
|
||||||
|
chapter_number=chapter.chapter_number
|
||||||
|
)
|
||||||
|
entity_changes["organization_states"] = org_state_result
|
||||||
|
except Exception as org_state_error:
|
||||||
|
logger.error(f"⚠️ 更新组织自身状态失败(不影响分析结果): {str(org_state_error)}", exc_info=True)
|
||||||
|
|
||||||
# 【新增】自动更新伏笔状态
|
# 【新增】自动更新伏笔状态
|
||||||
foreshadow_stats = {"planted_count": 0, "resolved_count": 0, "created_count": 0}
|
foreshadow_stats = {"planted_count": 0, "resolved_count": 0, "created_count": 0}
|
||||||
analysis_foreshadows = analysis_result.get('foreshadows', [])
|
analysis_foreshadows = analysis_result.get('foreshadows', [])
|
||||||
@@ -202,7 +274,8 @@ async def analyze_chapter(
|
|||||||
"message": f"分析完成,提取了{saved_count}条记忆",
|
"message": f"分析完成,提取了{saved_count}条记忆",
|
||||||
"analysis": plot_analysis.to_dict(),
|
"analysis": plot_analysis.to_dict(),
|
||||||
"memories_count": saved_count,
|
"memories_count": saved_count,
|
||||||
"foreshadow_stats": foreshadow_stats
|
"foreshadow_stats": foreshadow_stats,
|
||||||
|
"entity_changes": entity_changes
|
||||||
}
|
}
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import hashlib
|
|||||||
from app.models.foreshadow import Foreshadow
|
from app.models.foreshadow import Foreshadow
|
||||||
from app.models.chapter import Chapter
|
from app.models.chapter import Chapter
|
||||||
from app.models.memory import PlotAnalysis, StoryMemory
|
from app.models.memory import PlotAnalysis, StoryMemory
|
||||||
|
from app.models.project import Project
|
||||||
|
from app.services.memory_service import memory_service
|
||||||
from app.schemas.foreshadow import (
|
from app.schemas.foreshadow import (
|
||||||
ForeshadowCreate, ForeshadowUpdate,
|
ForeshadowCreate, ForeshadowUpdate,
|
||||||
PlantForeshadowRequest, ResolveForeshadowRequest,
|
PlantForeshadowRequest, ResolveForeshadowRequest,
|
||||||
@@ -232,16 +234,134 @@ class ForeshadowService:
|
|||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
foreshadow_id: str
|
foreshadow_id: str
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""删除伏笔"""
|
"""删除伏笔,并同步清理关联记忆数据和历史分析引用"""
|
||||||
try:
|
try:
|
||||||
foreshadow = await self.get_foreshadow(db, foreshadow_id)
|
foreshadow = await self.get_foreshadow(db, foreshadow_id)
|
||||||
if not foreshadow:
|
if not foreshadow:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
project_result = await db.execute(
|
||||||
|
select(Project).where(Project.id == foreshadow.project_id)
|
||||||
|
)
|
||||||
|
project = project_result.scalar_one_or_none()
|
||||||
|
|
||||||
|
deleted_memory_rows = 0
|
||||||
|
deleted_vector_memories = 0
|
||||||
|
cleaned_analysis_refs = 0
|
||||||
|
cleaned_analysis_rows = 0
|
||||||
|
foreshadow_keywords = []
|
||||||
|
content_snippet = (foreshadow.content or "")[:50].strip()
|
||||||
|
|
||||||
|
if foreshadow.title and foreshadow.title.strip():
|
||||||
|
foreshadow_keywords.append(foreshadow.title.strip())
|
||||||
|
|
||||||
|
if content_snippet:
|
||||||
|
foreshadow_keywords.append(content_snippet)
|
||||||
|
|
||||||
|
memory_conditions = [
|
||||||
|
StoryMemory.project_id == foreshadow.project_id,
|
||||||
|
StoryMemory.memory_type == "foreshadow"
|
||||||
|
]
|
||||||
|
keyword_conditions = []
|
||||||
|
for keyword in foreshadow_keywords:
|
||||||
|
keyword_conditions.append(StoryMemory.content.contains(keyword))
|
||||||
|
keyword_conditions.append(StoryMemory.title.contains(keyword))
|
||||||
|
|
||||||
|
if keyword_conditions:
|
||||||
|
delete_memory_query = delete(StoryMemory).where(
|
||||||
|
and_(*memory_conditions, or_(*keyword_conditions))
|
||||||
|
)
|
||||||
|
delete_memory_result = await db.execute(delete_memory_query)
|
||||||
|
deleted_memory_rows = delete_memory_result.rowcount or 0
|
||||||
|
|
||||||
|
if project and project.user_id and foreshadow_keywords:
|
||||||
|
deleted_vector_memories = await memory_service.delete_foreshadow_memories(
|
||||||
|
user_id=project.user_id,
|
||||||
|
project_id=foreshadow.project_id,
|
||||||
|
foreshadow_keywords=foreshadow_keywords
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis_result = await db.execute(
|
||||||
|
select(PlotAnalysis).where(PlotAnalysis.project_id == foreshadow.project_id)
|
||||||
|
)
|
||||||
|
project_analyses = analysis_result.scalars().all()
|
||||||
|
|
||||||
|
for analysis in project_analyses:
|
||||||
|
analysis_foreshadows = analysis.foreshadows or []
|
||||||
|
if not analysis_foreshadows:
|
||||||
|
continue
|
||||||
|
|
||||||
|
original_count = len(analysis_foreshadows)
|
||||||
|
filtered_foreshadows = []
|
||||||
|
removed_count = 0
|
||||||
|
|
||||||
|
for item in analysis_foreshadows:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
filtered_foreshadows.append(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
should_remove = False
|
||||||
|
|
||||||
|
# 1. 清理历史回收引用
|
||||||
|
if item.get("reference_foreshadow_id") == foreshadow_id:
|
||||||
|
should_remove = True
|
||||||
|
|
||||||
|
# 2. 清理历史埋入记录,避免“手动同步分析伏笔”再次从 PlotAnalysis 重建已删除伏笔
|
||||||
|
if not should_remove and foreshadow.source_type == "analysis":
|
||||||
|
item_type = item.get("type")
|
||||||
|
item_content = (item.get("content") or "").strip()
|
||||||
|
item_title = (item.get("title") or "").strip()
|
||||||
|
item_source_memory_id = None
|
||||||
|
|
||||||
|
if item_type == "planted" and item_content and analysis.chapter_id == foreshadow.plant_chapter_id:
|
||||||
|
item_source_memory_id = generate_stable_foreshadow_id(
|
||||||
|
analysis.chapter_id,
|
||||||
|
item_content,
|
||||||
|
item_type
|
||||||
|
)
|
||||||
|
|
||||||
|
if foreshadow.source_memory_id and item_source_memory_id == foreshadow.source_memory_id:
|
||||||
|
should_remove = True
|
||||||
|
elif (
|
||||||
|
item_title
|
||||||
|
and foreshadow.title
|
||||||
|
and item_title == foreshadow.title.strip()
|
||||||
|
and content_snippet
|
||||||
|
and content_snippet in item_content
|
||||||
|
):
|
||||||
|
should_remove = True
|
||||||
|
|
||||||
|
if should_remove:
|
||||||
|
removed_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
filtered_foreshadows.append(item)
|
||||||
|
|
||||||
|
if removed_count > 0:
|
||||||
|
analysis.foreshadows = filtered_foreshadows
|
||||||
|
analysis.foreshadows_planted = sum(
|
||||||
|
1 for f in filtered_foreshadows
|
||||||
|
if isinstance(f, dict) and f.get('type') == 'planted'
|
||||||
|
)
|
||||||
|
analysis.foreshadows_resolved = sum(
|
||||||
|
1 for f in filtered_foreshadows
|
||||||
|
if isinstance(f, dict) and f.get('type') == 'resolved'
|
||||||
|
)
|
||||||
|
cleaned_analysis_refs += removed_count
|
||||||
|
cleaned_analysis_rows += 1
|
||||||
|
logger.info(
|
||||||
|
f"🧹 已清理章节分析 {analysis.chapter_id[:8]} 中 {removed_count} 条已删除伏笔历史记录 "
|
||||||
|
f"(原数量: {original_count}, 现数量: {len(filtered_foreshadows)})"
|
||||||
|
)
|
||||||
|
|
||||||
await db.delete(foreshadow)
|
await db.delete(foreshadow)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
logger.info(f"✅ 删除伏笔成功: {foreshadow.title}")
|
logger.info(
|
||||||
|
f"✅ 删除伏笔成功: {foreshadow.title} "
|
||||||
|
f"(关系记忆清理: {deleted_memory_rows}条, 向量记忆清理: {deleted_vector_memories}条, "
|
||||||
|
f"历史分析引用清理: {cleaned_analysis_refs}条/{cleaned_analysis_rows}个分析)"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1173,16 +1293,22 @@ class ForeshadowService:
|
|||||||
matched_by_content = False
|
matched_by_content = False
|
||||||
|
|
||||||
# 策略1: 优先使用 reference_id 精确匹配
|
# 策略1: 优先使用 reference_id 精确匹配
|
||||||
|
# 重要:如果分析结果里已经给出了 reference_foreshadow_id,
|
||||||
|
# 但该伏笔已被用户删除或已不属于当前项目,则直接跳过,
|
||||||
|
# 不再退回内容匹配,避免把“已删除伏笔”的旧分析结果错误同步到其他同名/近似伏笔上。
|
||||||
if reference_id:
|
if reference_id:
|
||||||
existing = await self.get_foreshadow(db, reference_id)
|
existing = await self.get_foreshadow(db, reference_id)
|
||||||
if existing and existing.project_id == project_id:
|
if existing and existing.project_id == project_id:
|
||||||
logger.info(f"🎯 通过ID精确匹配伏笔: {existing.title}")
|
logger.info(f"🎯 通过ID精确匹配伏笔: {existing.title}")
|
||||||
else:
|
else:
|
||||||
existing = None
|
existing = None
|
||||||
logger.warning(f"⚠️ 伏笔ID不存在或不属于该项目: {reference_id}")
|
logger.warning(f"⚠️ 伏笔ID不存在或不属于该项目,跳过本次回收同步: {reference_id}")
|
||||||
|
stats["skipped_resolve_count"] = stats.get("skipped_resolve_count", 0) + 1
|
||||||
|
stats["errors"].append(f"reference_foreshadow_id 无效或已删除: {reference_id}")
|
||||||
|
continue
|
||||||
|
|
||||||
# 策略2: 内容匹配备用机制(当没有reference_id或ID匹配失败时)
|
# 策略2: 内容匹配备用机制(仅在 analysis 未提供 reference_id 时启用)
|
||||||
if not existing and planted_foreshadows:
|
if not reference_id and not existing and planted_foreshadows:
|
||||||
matched = self._match_foreshadow_by_content(
|
matched = self._match_foreshadow_by_content(
|
||||||
fs_data, planted_foreshadows
|
fs_data, planted_foreshadows
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -723,6 +723,59 @@ class MemoryService:
|
|||||||
|
|
||||||
return "\n".join(lines) + "\n"
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
async def delete_foreshadow_memories(
|
||||||
|
self,
|
||||||
|
user_id: str,
|
||||||
|
project_id: str,
|
||||||
|
foreshadow_keywords: List[str]
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
根据伏笔关键词删除向量库中的相关伏笔记忆
|
||||||
|
|
||||||
|
说明:当前记忆系统未持久化 [reference_foreshadow_id](backend/app/services/prompt_service.py:1109) /
|
||||||
|
[foreshadow_id](backend/app/services/foreshadow_service.py:230) 映射,因此这里采用内容关键词匹配作为清理策略,
|
||||||
|
仅删除 [memory_type='foreshadow'](backend/app/models/memory.py:23) 的向量记忆。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: 用户ID
|
||||||
|
project_id: 项目ID
|
||||||
|
foreshadow_keywords: 伏笔关键词列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
实际删除数量
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
keywords = [kw.strip() for kw in foreshadow_keywords if kw and kw.strip()]
|
||||||
|
if not keywords:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
collection = self.get_collection(user_id, project_id)
|
||||||
|
results = collection.get(where={"memory_type": "foreshadow"})
|
||||||
|
|
||||||
|
ids_to_delete = []
|
||||||
|
documents = results.get('documents') or []
|
||||||
|
metadatas = results.get('metadatas') or []
|
||||||
|
result_ids = results.get('ids') or []
|
||||||
|
|
||||||
|
for index, memory_id in enumerate(result_ids):
|
||||||
|
document = documents[index] if index < len(documents) else ""
|
||||||
|
metadata = metadatas[index] if index < len(metadatas) else {}
|
||||||
|
title = str((metadata or {}).get('title', ''))
|
||||||
|
haystack = f"{title}\n{document}".lower()
|
||||||
|
|
||||||
|
if any(keyword.lower() in haystack for keyword in keywords):
|
||||||
|
ids_to_delete.append(memory_id)
|
||||||
|
|
||||||
|
if ids_to_delete:
|
||||||
|
collection.delete(ids=ids_to_delete)
|
||||||
|
logger.info(f"🗑️ 已删除项目{project_id[:8]}的{len(ids_to_delete)}条伏笔相关向量记忆")
|
||||||
|
|
||||||
|
return len(ids_to_delete)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ 删除伏笔相关向量记忆失败: {str(e)}")
|
||||||
|
return 0
|
||||||
|
|
||||||
async def delete_chapter_memories(
|
async def delete_chapter_memories(
|
||||||
self,
|
self,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
|
|||||||
@@ -333,7 +333,14 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
|||||||
const renderAnalysisResult = () => {
|
const renderAnalysisResult = () => {
|
||||||
if (!analysis) return null;
|
if (!analysis) return null;
|
||||||
|
|
||||||
const { analysis: analysis_data, memories } = analysis;
|
const { analysis: analysis_data, memories, entity_changes } = analysis;
|
||||||
|
const hasEntityChanges = Boolean(
|
||||||
|
entity_changes && (
|
||||||
|
(entity_changes.careers?.changes?.length || 0) > 0 ||
|
||||||
|
(entity_changes.character_states?.changes?.length || 0) > 0 ||
|
||||||
|
(entity_changes.organization_states?.changes?.length || 0) > 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
@@ -411,6 +418,71 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{hasEntityChanges && entity_changes && (
|
||||||
|
<Card title="实体联动更新" style={{ marginBottom: 16 }} size={isMobile ? 'small' : 'default'}>
|
||||||
|
<Row gutter={isMobile ? 8 : 16} style={{ marginBottom: 16 }}>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Statistic
|
||||||
|
title="职业更新"
|
||||||
|
value={entity_changes.careers?.updated_count || 0}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Statistic
|
||||||
|
title="角色状态/关系更新"
|
||||||
|
value={
|
||||||
|
(entity_changes.character_states?.state_updated_count || 0) +
|
||||||
|
(entity_changes.character_states?.relationship_created_count || 0) +
|
||||||
|
(entity_changes.character_states?.relationship_updated_count || 0) +
|
||||||
|
(entity_changes.character_states?.org_updated_count || 0)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={isMobile ? 24 : 8}>
|
||||||
|
<Statistic
|
||||||
|
title="组织状态更新"
|
||||||
|
value={entity_changes.organization_states?.updated_count || 0}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{entity_changes.careers?.changes?.length ? (
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<strong>职业变化:</strong>
|
||||||
|
<div style={{ marginTop: 8 }}>
|
||||||
|
{entity_changes.careers.changes.map((change, index) => (
|
||||||
|
<Tag key={`career-${index}`} color="blue" style={{ marginBottom: 8 }}>
|
||||||
|
{change}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{entity_changes.character_states?.changes?.length ? (
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<strong>角色/关系变化:</strong>
|
||||||
|
<List
|
||||||
|
size="small"
|
||||||
|
dataSource={entity_changes.character_states.changes}
|
||||||
|
renderItem={(item) => <List.Item>{item}</List.Item>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{entity_changes.organization_states?.changes?.length ? (
|
||||||
|
<div>
|
||||||
|
<strong>组织状态变化:</strong>
|
||||||
|
<List
|
||||||
|
size="small"
|
||||||
|
dataSource={entity_changes.organization_states.changes}
|
||||||
|
renderItem={(item) => <List.Item>{item}</List.Item>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{analysis_data.suggestions && analysis_data.suggestions.length > 0 && (
|
{analysis_data.suggestions && analysis_data.suggestions.length > 0 && (
|
||||||
<Card title={<><BulbOutlined /> 改进建议</>} size={isMobile ? 'small' : 'default'}>
|
<Card title={<><BulbOutlined /> 改进建议</>} size={isMobile ? 'small' : 'default'}>
|
||||||
<List
|
<List
|
||||||
|
|||||||
@@ -744,12 +744,26 @@ export interface StoryMemory {
|
|||||||
is_foreshadow: 0 | 1 | 2; // 0=普通, 1=已埋下, 2=已回收
|
is_foreshadow: 0 | 1 | 2; // 0=普通, 1=已埋下, 2=已回收
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EntityChangesSummaryItem {
|
||||||
|
updated_count?: number;
|
||||||
|
state_updated_count?: number;
|
||||||
|
relationship_created_count?: number;
|
||||||
|
relationship_updated_count?: number;
|
||||||
|
org_updated_count?: number;
|
||||||
|
changes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
// 章节分析结果响应 - 匹配后端API返回
|
// 章节分析结果响应 - 匹配后端API返回
|
||||||
export interface ChapterAnalysisResponse {
|
export interface ChapterAnalysisResponse {
|
||||||
chapter_id: string;
|
chapter_id: string;
|
||||||
analysis: AnalysisData; // 注意:后端返回的是analysis而不是analysis_data
|
analysis: AnalysisData; // 注意:后端返回的是analysis而不是analysis_data
|
||||||
memories: StoryMemory[];
|
memories: StoryMemory[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
|
entity_changes?: {
|
||||||
|
careers: EntityChangesSummaryItem;
|
||||||
|
character_states: EntityChangesSummaryItem;
|
||||||
|
organization_states: EntityChangesSummaryItem;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手动触发分析响应
|
// 手动触发分析响应
|
||||||
|
|||||||
Reference in New Issue
Block a user