120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from fastapi import HTTPException
|
|
|
|
from app.enums.celery_queue import CeleryQueue
|
|
from app.enums.common import ModuleEventTypeEnum
|
|
from app.services.module_async_recovery_service import (
|
|
TASK_MODULE_V2_VIDEO_PROMPT,
|
|
register_module_step_task,
|
|
)
|
|
from app.services.module_generation_log_service import log_module_error, log_module_event_file
|
|
from app.services.llm_billing import (
|
|
LlmBillingContext,
|
|
log_celery_dispatch_failure,
|
|
log_celery_dispatch_start,
|
|
log_celery_dispatch_success,
|
|
)
|
|
from app.services.module_generation_v2.config import VIDEO_PROMPT_OPTIMIZE, ModuleGenerationV2Config
|
|
from app.tasks.celery_app import celery_app
|
|
from app.tasks.module_generation_v2_tasks import start_video_prompt_optimize_v2
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VideoPromptDispatchResult:
|
|
registry_success: bool
|
|
celery_success: bool
|
|
registry_error: str | None = None
|
|
celery_error: str | None = None
|
|
|
|
@property
|
|
def recoverable(self) -> bool:
|
|
return self.registry_success or self.celery_success
|
|
|
|
|
|
def ensure_v2_celery_enabled() -> None:
|
|
if celery_app is None:
|
|
raise HTTPException(status_code=503, detail="Celery未启用")
|
|
|
|
|
|
async def dispatch_video_prompt_v2(
|
|
*,
|
|
config: ModuleGenerationV2Config,
|
|
project_id: str,
|
|
step_id: str,
|
|
billing_context: LlmBillingContext,
|
|
) -> VideoPromptDispatchResult:
|
|
"""注册并投递 V2 视频提词任务。
|
|
|
|
Redis 注册成功但 Celery 直投失败时,由周期恢复任务补投;Celery 成功但
|
|
Redis 注册失败时任务仍可正常执行。只有两个通道都失败时由 API 补偿落库为失败。
|
|
"""
|
|
log_celery_dispatch_start(billing_context)
|
|
registry_error: Exception | None = None
|
|
try:
|
|
await register_module_step_task(
|
|
module=config.module,
|
|
project_id=project_id,
|
|
step_id=step_id,
|
|
step_code=VIDEO_PROMPT_OPTIMIZE,
|
|
task_name=TASK_MODULE_V2_VIDEO_PROMPT,
|
|
)
|
|
except Exception as exc:
|
|
registry_error = exc
|
|
log_module_error(
|
|
module=config.module,
|
|
event_type=ModuleEventTypeEnum.V2_VIDEO_PROMPT_REGISTRY_FAILED.value,
|
|
project_id=project_id,
|
|
step_id=step_id,
|
|
message="V2 视频提词 Redis 活跃注册失败,将继续尝试 Celery 直投",
|
|
exc=exc,
|
|
)
|
|
|
|
celery_error: Exception | None = None
|
|
try:
|
|
start_video_prompt_optimize_v2.apply_async(
|
|
args=[project_id, step_id],
|
|
queue=CeleryQueue.GEN_CHATAPI_CREATE.value,
|
|
countdown=0,
|
|
task_id=f"module-v2-video-prompt:{step_id}",
|
|
)
|
|
except Exception as exc:
|
|
celery_error = exc
|
|
log_module_error(
|
|
module=config.module,
|
|
event_type=ModuleEventTypeEnum.V2_VIDEO_PROMPT_DISPATCH_FAILED.value,
|
|
project_id=project_id,
|
|
step_id=step_id,
|
|
message="V2 视频提词 Celery 投递失败",
|
|
detail={"redis_registry_available": registry_error is None},
|
|
exc=exc,
|
|
)
|
|
|
|
result = VideoPromptDispatchResult(
|
|
registry_success=registry_error is None,
|
|
celery_success=celery_error is None,
|
|
registry_error=str(registry_error) if registry_error else None,
|
|
celery_error=str(celery_error) if celery_error else None,
|
|
)
|
|
if result.celery_success:
|
|
log_celery_dispatch_success(billing_context)
|
|
log_module_event_file(
|
|
module=config.module,
|
|
event_type=ModuleEventTypeEnum.V2_VIDEO_PROMPT_DISPATCHED.value,
|
|
project_id=project_id,
|
|
step_id=step_id,
|
|
message="V2 视频提词任务已投递",
|
|
detail={
|
|
"queue": CeleryQueue.GEN_CHATAPI_CREATE.value,
|
|
"redis_registry_available": result.registry_success,
|
|
},
|
|
)
|
|
if not result.celery_success:
|
|
log_celery_dispatch_failure(
|
|
billing_context,
|
|
error=result.celery_error or "Celery direct dispatch failed; waiting for registry recovery",
|
|
)
|
|
return result
|