会员积分改版V1
This commit is contained in:
@@ -19,7 +19,6 @@ from app.enums.common import (
|
||||
)
|
||||
from app.enums.credit_record import CreditRecordBillingScene, CreditRecordChargeKind, CreditRecordOwnerType
|
||||
from app.enums.generation_task import ChatGenerationTaskStatus
|
||||
from app.enums.llm_billing import LlmBillingConfigKey
|
||||
from app.enums.shot_replicate import (
|
||||
ShotSegmentReplicateStatusEnum,
|
||||
ShotSplitStatusEnum,
|
||||
@@ -51,13 +50,14 @@ from app.services.generation.pipeline.db_lock_service import (
|
||||
from app.services.generation.task_factory_service import create_chat_generation_task_for_module
|
||||
from app.services.llm_billing import (
|
||||
LlmBillingContext,
|
||||
ensure_hold_exists,
|
||||
ensure_pre_deducted,
|
||||
log_provider_failure,
|
||||
log_provider_start,
|
||||
log_provider_success,
|
||||
release_on_failure,
|
||||
settle_success,
|
||||
start_hold,
|
||||
mark_business_success,
|
||||
pre_deduct,
|
||||
record_provider_exception,
|
||||
finalize_llm_business_failure,
|
||||
)
|
||||
from app.services.hot_opening_video_prompt_service import (
|
||||
build_final_video_prompt,
|
||||
@@ -369,8 +369,7 @@ def build_v2_video_prompt_billing_context(
|
||||
source_step_id=str(step_id),
|
||||
source_step_code=VIDEO_PROMPT_OPTIMIZE,
|
||||
related_id=str(step_id),
|
||||
hold_config_key=LlmBillingConfigKey.HOLD_MODULE_VIDEO_PROMPT.value,
|
||||
description_prefix=f"{display_name}视频提词优化",
|
||||
description_prefix=f"{display_name}视频提词优化",
|
||||
trace_id=f"module-v2-video-prompt:{step_id}",
|
||||
)
|
||||
|
||||
@@ -433,7 +432,7 @@ async def create_hot_opening_project_v2(
|
||||
video_config=video_config,
|
||||
target_platform=req.target_platform or "抖音",
|
||||
)
|
||||
await start_hold(
|
||||
await pre_deduct(
|
||||
db,
|
||||
build_v2_video_prompt_billing_context(
|
||||
user_id=str(current_user.id),
|
||||
@@ -600,7 +599,7 @@ async def create_shot_replicate_project_v2(
|
||||
urls=[req.material_image_url],
|
||||
allow_common_migrate=True,
|
||||
)
|
||||
await start_hold(
|
||||
await pre_deduct(
|
||||
db,
|
||||
build_v2_video_prompt_billing_context(
|
||||
user_id=str(current_user.id),
|
||||
@@ -724,7 +723,7 @@ async def rebuild_video_prompt_step_v2(
|
||||
project.final_video_cover_url = None
|
||||
project.completed_at = None
|
||||
project.error_message = None
|
||||
await start_hold(
|
||||
await pre_deduct(
|
||||
db,
|
||||
build_v2_video_prompt_billing_context(
|
||||
user_id=str(project.user_id),
|
||||
@@ -799,19 +798,16 @@ async def mark_video_prompt_dispatch_failed_v2(
|
||||
message=error_message,
|
||||
detail={"dispatch_compensated": True},
|
||||
)
|
||||
await release_on_failure(
|
||||
db,
|
||||
build_v2_video_prompt_billing_context(
|
||||
user_id=str(project.user_id),
|
||||
project_id=str(project.id),
|
||||
step_id=str(step.id),
|
||||
step_version=int(step.version or 1),
|
||||
module=config.module,
|
||||
display_name=config.display_name,
|
||||
),
|
||||
error=error_message,
|
||||
billing_context = build_v2_video_prompt_billing_context(
|
||||
user_id=str(project.user_id),
|
||||
project_id=str(project.id),
|
||||
step_id=str(step.id),
|
||||
step_version=int(step.version or 1),
|
||||
module=config.module,
|
||||
display_name=config.display_name,
|
||||
)
|
||||
await db.commit()
|
||||
await finalize_llm_business_failure(billing_context, error=error_message)
|
||||
|
||||
|
||||
async def run_video_prompt_optimize_v2(
|
||||
@@ -888,14 +884,13 @@ async def run_video_prompt_optimize_v2(
|
||||
source_step_id=project_snapshot["step_id"],
|
||||
source_step_code=VIDEO_PROMPT_OPTIMIZE,
|
||||
related_id=project_snapshot["step_id"],
|
||||
hold_config_key=LlmBillingConfigKey.HOLD_MODULE_VIDEO_PROMPT.value,
|
||||
description_prefix=f"{config.display_name}视频提词优化",
|
||||
trace_id=f"module-v2-video-prompt:{project_snapshot['step_id']}",
|
||||
)
|
||||
hold_validation = await ensure_hold_exists(db, llm_billing_context)
|
||||
if not hold_validation.can_execute:
|
||||
pre_deduct_validation = await ensure_pre_deducted(db, llm_billing_context)
|
||||
if not pre_deduct_validation.can_execute:
|
||||
step.status = ModuleStepStatusEnum.FAILED.value
|
||||
step.error_message = f"LLM账务状态异常({hold_validation.state.value}),已终止任务"
|
||||
step.error_message = f"LLM账务状态异常({pre_deduct_validation.state.value}),已终止任务"
|
||||
step.completed_at = utc_now()
|
||||
project.status = ModuleProjectStatusEnum.FAILED.value
|
||||
project.error_message = step.error_message
|
||||
@@ -906,7 +901,7 @@ async def run_video_prompt_optimize_v2(
|
||||
|
||||
provider_succeeded = False
|
||||
usage: dict[str, Any] = {}
|
||||
log_provider_start(llm_billing_context, detail={"prompt_type": "video", "flow_version": "v2"})
|
||||
await log_provider_start(db, llm_billing_context, detail={"prompt_type": "video", "flow_version": "v2"})
|
||||
prompt_schema, final_prompt, usage = await optimize_hot_opening_video_prompt(
|
||||
db,
|
||||
user_id=project_snapshot["user_id"],
|
||||
@@ -921,9 +916,11 @@ async def run_video_prompt_optimize_v2(
|
||||
module=project_snapshot["module"],
|
||||
project_id=project_snapshot["project_id"],
|
||||
step_id=project_snapshot["step_id"],
|
||||
fixed_model_config_id=llm_billing_context.model_config_id,
|
||||
fixed_model_snapshot=llm_billing_context.model_parameters_snapshot,
|
||||
)
|
||||
provider_succeeded = True
|
||||
log_provider_success(llm_billing_context, usage=usage)
|
||||
await log_provider_success(db, llm_billing_context, usage=usage)
|
||||
|
||||
if execution_guard is not None:
|
||||
await execution_guard()
|
||||
@@ -945,24 +942,20 @@ async def run_video_prompt_optimize_v2(
|
||||
row = locked.first()
|
||||
if not row:
|
||||
await db.rollback()
|
||||
# Provider 已成功,即使业务对象被异常移除,也必须按真实 usage 完成幂等结算。
|
||||
await settle_success(
|
||||
db,
|
||||
await log_provider_failure(db, llm_billing_context, error="业务对象已失效,供应商结果无法落库")
|
||||
await finalize_llm_business_failure(
|
||||
llm_billing_context,
|
||||
usage=usage,
|
||||
description=f"{config.display_name}-视频提词优化(业务对象失效结算)",
|
||||
error="业务对象已失效,供应商结果无法落库",
|
||||
)
|
||||
await db.commit()
|
||||
return None
|
||||
project, step = row
|
||||
if int(step.version) != expected_version or step.input_json != expected_input or step.status != ModuleStepStatusEnum.PROCESSING.value:
|
||||
await settle_success(
|
||||
db,
|
||||
await db.rollback()
|
||||
await log_provider_failure(db, llm_billing_context, error="业务步骤版本已失效,供应商结果被丢弃")
|
||||
await finalize_llm_business_failure(
|
||||
llm_billing_context,
|
||||
usage=usage,
|
||||
description=f"{config.display_name}-视频提词优化(失效结果结算)",
|
||||
error="业务步骤版本已失效,供应商结果被丢弃",
|
||||
)
|
||||
await db.commit()
|
||||
log_module_event_file(
|
||||
module=project_snapshot["module"],
|
||||
event_type=ModuleEventTypeEnum.STALE_STEP_RESULT_DISCARDED.value,
|
||||
@@ -973,7 +966,7 @@ async def run_video_prompt_optimize_v2(
|
||||
detail={"expected_version": expected_version},
|
||||
)
|
||||
return None
|
||||
billing = await settle_success(
|
||||
billing = await mark_business_success(
|
||||
db,
|
||||
llm_billing_context,
|
||||
usage=usage,
|
||||
@@ -1018,33 +1011,24 @@ async def run_video_prompt_optimize_v2(
|
||||
)
|
||||
await db.commit()
|
||||
return step
|
||||
except DatabaseRowLockBusy:
|
||||
except DatabaseRowLockBusy as exc:
|
||||
await db.rollback()
|
||||
if locals().get("provider_succeeded", False):
|
||||
await settle_success(
|
||||
db,
|
||||
if "llm_billing_context" in locals() and locals().get("provider_succeeded", False):
|
||||
await log_provider_failure(db, llm_billing_context, error="业务行锁失败,供应商结果无法落库")
|
||||
await finalize_llm_business_failure(
|
||||
llm_billing_context,
|
||||
usage=locals().get("usage") or {},
|
||||
description=f"{config.display_name}-视频提词优化(行锁失败结算)",
|
||||
error="业务行锁失败,供应商结果无法落库",
|
||||
)
|
||||
await db.commit()
|
||||
return None
|
||||
raise
|
||||
raise exc
|
||||
except Exception as exc:
|
||||
await db.rollback()
|
||||
if "llm_billing_context" in locals() and not locals().get("provider_succeeded", False):
|
||||
log_provider_failure(llm_billing_context, error=str(exc))
|
||||
if "llm_billing_context" in locals():
|
||||
if locals().get("provider_succeeded", False):
|
||||
await log_provider_failure(db, llm_billing_context, error=str(exc))
|
||||
else:
|
||||
provider_succeeded, usage = await record_provider_exception(db, llm_billing_context, exc)
|
||||
try:
|
||||
if "llm_billing_context" in locals():
|
||||
if locals().get("provider_succeeded", False):
|
||||
await settle_success(
|
||||
db,
|
||||
llm_billing_context,
|
||||
usage=locals().get("usage") or {},
|
||||
description=f"{config.display_name}-视频提词优化(本地失败结算)",
|
||||
)
|
||||
else:
|
||||
await release_on_failure(db, llm_billing_context, error=str(exc))
|
||||
if execution_guard is not None:
|
||||
await execution_guard()
|
||||
result = await execute_with_lock_timeout(
|
||||
@@ -1070,9 +1054,9 @@ async def run_video_prompt_optimize_v2(
|
||||
step.completed_at = utc_now()
|
||||
project.status = ModuleProjectStatusEnum.FAILED.value
|
||||
project.error_message = str(exc) if str(exc) else type(exc).__name__
|
||||
# provider 已成功时 settle_success 已在当前事务写入 RELEASE/CHARGE;
|
||||
# 即使业务步骤已不存在或已不是 processing,也必须提交账务结算。
|
||||
await db.commit()
|
||||
if "llm_billing_context" in locals():
|
||||
await finalize_llm_business_failure(llm_billing_context, error=str(exc))
|
||||
log_module_error(
|
||||
module=row[0].module if row else "module_generation_v2",
|
||||
event_type=ModuleEventTypeEnum.VIDEO_PROMPT_FAILED.value,
|
||||
@@ -1092,8 +1076,11 @@ async def run_video_prompt_optimize_v2(
|
||||
detail={"origin_error": str(exc), "provider_succeeded": locals().get("provider_succeeded", False)},
|
||||
exc=mark_exc,
|
||||
)
|
||||
if locals().get("provider_succeeded", False):
|
||||
raise
|
||||
try:
|
||||
if "llm_billing_context" in locals():
|
||||
await finalize_llm_business_failure(llm_billing_context, error=str(exc))
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user