Files
life-echo/api/app/tasks/story_title_tasks.py

135 lines
5.0 KiB
Python
Raw Normal View History

"""Async story title refinement after new story create (placeholder first)."""
import time
from celery import shared_task
from app.core.db import get_sync_db
from app.core.llm_gateway import LlmGateway, LlmUseCase
from app.core.logging import get_logger
logger = get_logger(__name__)
@shared_task(bind=True, max_retries=2, default_retry_delay=15)
def generate_story_title_after_create(
self,
story_id: str,
chapter_category: str,
oral_scope: str,
user_id: str,
):
"""Replace placeholder title with LLM title when body is long enough."""
from app.agents.chat.prompts_profile import format_user_profile_context
from app.agents.memoir.narrative_agent import NarrativeAgent
from app.features.memoir.state_service import get_or_create_state_sync
from app.features.memoir.story_pipeline_sync import (
_maybe_generate_title,
_placeholder_title,
_slot_snippets_for_narrative,
)
from app.features.story.models import Story
from app.features.user.models import User
t0 = time.perf_counter()
logger.info(
"event=story_title_task_start story_id={} user_id={} chapter_category={} "
"msg=故事标题精修任务开始",
story_id,
user_id,
chapter_category,
)
try:
with get_sync_db() as db:
st = db.get(Story, story_id)
if not st or str(st.user_id) != str(user_id):
ms = (time.perf_counter() - t0) * 1000
logger.info(
"event=story_title_task_skip story_id={} reason=not_found duration_ms={:.1f} "
"msg=标题精修跳过(故事不存在或无权限)",
story_id,
ms,
)
return {"status": "skip_not_found"}
expected_ph = _placeholder_title(chapter_category)
if (st.title or "").strip() and (st.title or "").strip() != expected_ph:
ms = (time.perf_counter() - t0) * 1000
logger.info(
"event=story_title_task_skip story_id={} reason=user_modified duration_ms={:.1f} "
"msg=标题精修跳过(用户已改标题)",
story_id,
ms,
)
return {"status": "skip_user_modified"}
llm = LlmGateway().langchain_llm_for(LlmUseCase("story_title"))
if not llm:
ms = (time.perf_counter() - t0) * 1000
logger.info(
"event=story_title_task_skip story_id={} reason=no_llm duration_ms={:.1f} "
"msg=标题精修跳过(无 LLM",
story_id,
ms,
)
return {"status": "skip_no_llm"}
user_obj = db.get(User, user_id)
user_profile = ""
birth_year = None
if user_obj:
birth_year = user_obj.birth_year
user_profile = format_user_profile_context(
birth_year=user_obj.birth_year,
birth_place=user_obj.birth_place,
grew_up_place=user_obj.grew_up_place,
occupation=user_obj.occupation,
)
state = get_or_create_state_sync(user_id, db)
slot_snippets = _slot_snippets_for_narrative(
state=state,
chapter_category=chapter_category,
user_id=user_id,
)
md = (st.canonical_markdown or "").strip()
new_title = _maybe_generate_title(
NarrativeAgent(),
chapter_category=chapter_category,
md=md,
slot_snippets=slot_snippets,
user_profile=user_profile,
user_birth_year=birth_year,
llm=llm,
oral_scope=oral_scope or "",
)
if not new_title.strip() or new_title.strip() == expected_ph:
ms = (time.perf_counter() - t0) * 1000
logger.info(
"event=story_title_task_skip story_id={} reason=placeholder duration_ms={:.1f} "
"msg=标题精修跳过(仍为占位)",
story_id,
ms,
)
return {"status": "skip_placeholder"}
st.title = new_title
db.commit()
ms = (time.perf_counter() - t0) * 1000
logger.info(
"event=story_title_task_done story_id={} user_id={} duration_ms={:.1f} "
"msg=故事标题精修完成",
story_id,
user_id,
ms,
)
return {"status": "ok", "title": new_title}
except Exception as exc:
ms = (time.perf_counter() - t0) * 1000
logger.warning(
"event=generate_story_title_after_create_failed story_id={} duration_ms={:.1f} err={} "
"msg=故事标题精修失败",
story_id,
ms,
exc,
)
raise self.retry(exc=exc) from exc