Files
life-echo/api/app/features/story/service.py

240 lines
7.8 KiB
Python
Raw Normal View History

重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
"""
StoryService Story 层业务逻辑
- 创建 story版本evidence 关联
- 不直接依赖 agent orchestrator 调用
- story 正文生成后提取 primary image intent 并落库
"""
from datetime import datetime, timezone
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.logging import get_logger
2026-03-20 15:15:35 +08:00
from app.features.memoir import repo as memoir_repo
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
from app.features.story.image_intent_extractor import extract_primary_image_intent
from app.features.story.repo import (
count_story_versions,
create_story,
create_story_evidence_link,
create_story_image_intent,
create_story_version,
delete_story_image_intents_by_story,
get_stories_for_user,
get_story_by_id,
get_story_image_intent_by_story,
)
logger = get_logger(__name__)
async def _extract_and_store_image_intent(
db,
*,
story,
version,
markdown: str,
) -> None:
"""
markdown 提取 primary intent
仅移除 pending/failed避免删掉正在 processing 的旧任务行同版本则原地更新行以幂等
"""
await delete_story_image_intents_by_story(
db, story.id, statuses=["pending", "failed"]
)
result = extract_primary_image_intent(
markdown,
title=story.title or "",
stage=story.stage,
summary=story.summary,
people_refs=story.people_refs or [],
place_refs=story.place_refs or [],
time_start=story.time_start,
time_end=story.time_end,
)
existing = await get_story_image_intent_by_story(db, story.id, role="primary")
now = datetime.now(timezone.utc)
if existing and existing.story_version_id == version.id:
st = (existing.status or "").strip()
if st in ("processing", "completed"):
return
existing.caption = result.caption
existing.prompt_brief = result.prompt_brief
existing.style_profile = result.style_profile
existing.status = "pending"
existing.error = None
existing.asset_id = None
existing.updated_at = now
return
if existing and existing.story_version_id != version.id:
# 复用同一主键行,避免删行导致进行中的 Celery 任务找不到 intent
existing.story_version_id = version.id
existing.caption = result.caption
existing.prompt_brief = result.prompt_brief
existing.style_profile = result.style_profile
existing.status = "pending"
existing.error = None
existing.asset_id = None
existing.updated_at = now
return
await create_story_image_intent(
db,
story_id=story.id,
story_version_id=version.id,
caption=result.caption,
prompt_brief=result.prompt_brief,
style_profile=result.style_profile,
)
class StoryService:
def __init__(self, db: AsyncSession):
self._db = db
async def create_story(
self,
user_id: str,
title: str,
*,
stage: str | None = None,
story_type: str | None = None,
summary: str | None = None,
canonical_markdown: str | None = None,
) -> str:
"""Create story, commit, return story_id."""
story = await create_story(
self._db,
user_id=user_id,
title=title,
stage=stage,
story_type=story_type,
summary=summary,
canonical_markdown=canonical_markdown or "",
)
await self._db.flush()
if canonical_markdown:
version = await create_story_version(
self._db,
story_id=story.id,
version_no=1,
markdown_snapshot=canonical_markdown,
actor_type="ai",
source_type="generate",
)
await self._db.flush()
story.current_version_id = version.id
await _extract_and_store_image_intent(
self._db,
story=story,
version=version,
markdown=canonical_markdown,
)
2026-03-20 15:15:35 +08:00
if canonical_markdown:
await memoir_repo.mark_chapters_dirty_for_story(self._db, story.id)
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
await self._db.commit()
if canonical_markdown:
2026-03-20 15:15:35 +08:00
from app.tasks.chapter_compose_tasks import recompose_chapters_for_story
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
from app.tasks.story_image_tasks import generate_story_image
try:
generate_story_image.delay(story.id)
except Exception as exc:
logger.warning("派发 generate_story_image 失败: %s", exc)
2026-03-20 15:15:35 +08:00
try:
recompose_chapters_for_story.delay(story.id)
except Exception as exc:
logger.warning("派发 recompose_chapters_for_story 失败: %s", exc)
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
return story.id
async def append_version(
self,
story_id: str,
markdown_snapshot: str,
*,
actor_type: str = "ai",
source_type: str = "generate",
change_summary: str | None = None,
prompt_meta: dict | None = None,
) -> str:
"""Append new version, update canonical_markdown, return version_id."""
story = await get_story_by_id(self._db, story_id)
if not story:
raise ValueError(f"Story {story_id} not found")
parent_id = story.current_version_id
version_no = (await count_story_versions(self._db, story_id)) + 1
version = await create_story_version(
self._db,
story_id=story_id,
version_no=version_no,
markdown_snapshot=markdown_snapshot,
actor_type=actor_type,
source_type=source_type,
parent_version_id=parent_id,
prompt_meta=prompt_meta,
)
version.change_summary = change_summary
story.current_version_id = version.id
story.canonical_markdown = markdown_snapshot
await _extract_and_store_image_intent(
self._db,
story=story,
version=version,
markdown=markdown_snapshot,
)
2026-03-20 15:15:35 +08:00
await memoir_repo.mark_chapters_dirty_for_story(self._db, story_id)
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
await self._db.commit()
2026-03-20 15:15:35 +08:00
from app.tasks.chapter_compose_tasks import recompose_chapters_for_story
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
from app.tasks.story_image_tasks import generate_story_image
try:
generate_story_image.delay(story_id)
except Exception as exc:
logger.warning("派发 generate_story_image 失败: %s", exc)
2026-03-20 15:15:35 +08:00
try:
recompose_chapters_for_story.delay(story_id)
except Exception as exc:
logger.warning("派发 recompose_chapters_for_story 失败: %s", exc)
重构回忆录为 story-first / markdown-first 架构并整合图片意图与前端 UI 修复 本次 squash merge 将 codex-story-first-image-intent 的整体改动合入 development,核心内容包括: 1. 后端数据与迁移:新增 stories、story_versions、story_image_intents、chapter_cover_intents、assets 等模型与 Alembic 迁移,建立 story-first、markdown-first、asset-first 的主数据链路。 2. 生成与任务链:引入 StoryBuilderOrchestrator、ChapterComposerOrchestrator、story_image_tasks、chapter_cover_tasks,图片生成从正文占位符改为结构化 intent -> asset -> markdown 回填。 3. 并发与一致性:为 story/chapter intent 增加 claim_token、claimed_at、attempt_count,采用数据库原子 claim 为主、Redis 锁为辅,避免重复生成、锁误删和 processing 卡死。 4. Memoir 读写路径:章节 canonical_markdown 成为正文真源,列表/详情接口补齐 markdown、cover_asset、word_count 等字段,PDF 与 asset 解析链路同步升级。 5. Memory / Retrieval:扩展 transcript ingest、chunking、evidence 检索与 story 聚合基础设施,为后续 story-first RAG 与多 agent 编排提供底座。 6. App 端体验:章节页继续走 MarkdownRenderer 阅读链,同时吸收 fix3-19 的跨平台 UI glitch 修复;更新对话页、首页、文案资源与章节列表映射逻辑。 7. 测试与文档:补充 asset resolver、story image task、章节封面派发、markdown 映射等回归测试,并加入图片占位符退役设计文档。
2026-03-20 10:30:07 +08:00
return version.id
async def link_evidence(
self,
story_id: str,
evidence_type: str,
evidence_id: str,
*,
role: str = "primary",
weight: float | None = None,
) -> None:
"""Add evidence link. Caller must ensure story exists."""
await create_story_evidence_link(
self._db,
story_id=story_id,
evidence_type=evidence_type,
evidence_id=evidence_id,
role=role,
weight=weight,
)
await self._db.commit()
async def get_stories(
self, user_id: str, *, status: str | None = "active"
) -> list[dict]:
"""List stories for user."""
stories = await get_stories_for_user(self._db, user_id, status=status)
return [
{
"id": s.id,
"title": s.title,
"stage": s.stage,
"story_type": s.story_type,
"summary": s.summary,
"canonical_markdown": s.canonical_markdown,
"status": s.status,
"created_at": s.created_at.isoformat() if s.created_at else None,
}
for s in stories
]