Files
video-gen/video-gen-api/app/services/llm_billing/context.py
T
2026-07-24 09:18:05 +08:00

114 lines
3.1 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from app.enums.credit_record import CreditRecordChargeKind
from app.enums.llm_billing import LlmBillingLedgerState
from app.services.generation.billing_service import build_credit_biz_key
class LlmBillingConfigurationError(RuntimeError):
"""LLM 统一账务配置无效,必须在调用模型前终止。"""
class LlmBillingStateError(RuntimeError):
"""当前 attempt 的账务流水状态不允许继续执行。"""
@dataclass(slots=True, frozen=True)
class LlmBillingPolicy:
enabled: bool
hold_credits: float
config_key: str | None = None
source_key: str | None = None
valid: bool = True
error: str | None = None
@property
def bypassed(self) -> bool:
return not self.enabled
@dataclass(slots=True)
class LlmBillingContext:
user_id: str
owner_type: str
owner_id: str
attempt_no: int
charge_kind: str = CreditRecordChargeKind.TEXT_PROMPT.value
billing_scene: str | None = None
source_module: str | None = None
source_project_id: str | None = None
source_step_id: str | None = None
source_step_code: str | None = None
related_id: str | None = None
hold_credits: float | None = None
hold_config_key: str | None = None
description_prefix: str = "LLM"
trace_id: str | None = None
request_id: str | None = None
celery_task_id: str | None = None
provider: str | None = None
model_name: str | None = None
token_usage_id: str | None = None
@property
def hold_biz_key(self) -> str:
return build_credit_biz_key(
owner_type=self.owner_type,
owner_id=self.owner_id,
attempt_no=self.attempt_no,
charge_kind=self.charge_kind,
action="hold",
)
@property
def hold_release_biz_key(self) -> str:
return build_credit_biz_key(
owner_type=self.owner_type,
owner_id=self.owner_id,
attempt_no=self.attempt_no,
charge_kind=self.charge_kind,
action="hold_release",
)
@property
def charge_biz_key(self) -> str:
return build_credit_biz_key(
owner_type=self.owner_type,
owner_id=self.owner_id,
attempt_no=self.attempt_no,
charge_kind=self.charge_kind,
action="charge",
)
@property
def ledger_biz_keys(self) -> tuple[str, str, str]:
return self.hold_biz_key, self.hold_release_biz_key, self.charge_biz_key
@dataclass(slots=True, frozen=True)
class LlmHoldResult:
amount: float
state: LlmBillingLedgerState
created: bool = False
record_id: str | None = None
reason: str | None = None
@property
def bypassed(self) -> bool:
return self.state == LlmBillingLedgerState.BILLING_BYPASSED
@dataclass(slots=True, frozen=True)
class LlmHoldValidation:
can_execute: bool
amount: float
state: LlmBillingLedgerState
reason: str | None = None
hold_record_id: str | None = None
@property
def bypassed(self) -> bool:
return self.state == LlmBillingLedgerState.BILLING_BYPASSED