Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@

from windup_ai_engine.ports import ProgressPort
from windup_framework.providers.render3d.tencent import (
CREDIT_PRICE_CNY,
CREDITS,
RIG_CREDITS,
)
Expand All @@ -69,7 +68,6 @@
MODEL3D_CREDITS = CREDITS["Normal"]
AUTORIG_CREDITS = RIG_CREDITS
BUILD_CREDITS = MODEL3D_CREDITS + AUTORIG_CREDITS
BUILD_CNY = round(BUILD_CREDITS * CREDIT_PRICE_CNY, 2)


class Render3DAssetState(str, Enum):
Expand Down Expand Up @@ -303,7 +301,7 @@ def ensure(self, outfit_key: str, master: bytes, progress: ProgressPort) -> byte
if not self._may_build_assets:
raise SpendNotAuthorized(
f"造型 {outfit_key!r} 的 3D 资产未就绪,而本实例未获准建(建一次 "
f"{BUILD_CREDITS} 积分,约 ¥{BUILD_CNY}:图生 3D {MODEL3D_CREDITS} + "
f"{BUILD_CREDITS} 积分:图生 3D {MODEL3D_CREDITS} + "
f"绑骨 {AUTORIG_CREDITS})。要现建请显式授权花钱,"
"或先把资产备好,或改走 video_i2v。"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from windup_app.server.orchestrator._fetch import FetchNotAllowed, fetch_own_media
from windup_app.server.orchestrator.render3d_assets import (
AUTORIG_CREDITS,
BUILD_CNY,
BUILD_CREDITS,
MODEL3D_CREDITS,
RAW_KEY_PREFIX,
Expand Down Expand Up @@ -183,7 +182,7 @@ def build(self, outfit_key: str, master_url: str) -> dict:
if not self._builder.may_build_assets:
raise SpendNotAuthorized(
f"本部署未开启建 3D 资产(需 WINDUP_RENDER3D_ALLOW_SPEND)。建一次 "
f"{BUILD_CREDITS} 积分,约 ¥{BUILD_CNY}。"
f"{BUILD_CREDITS} 积分。"
)
state = self._builder.state(outfit_key)
if state is not Render3DAssetState.ABSENT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

__all__ = [
"TencentModel3DProvider", "TencentAutoRigProvider", "TencentCosModelUploader",
"SpendNotAuthorizedError", "CREDITS", "CREDIT_PRICE_CNY", "PRESET_MOTIONS",
"SpendNotAuthorizedError", "CREDITS", "PRESET_MOTIONS",
"RIG_CREDITS", "MAX_IMAGE_BYTES", "VIEW_TYPES",
]

Expand All @@ -54,7 +54,6 @@

CREDITS = {"Normal": 20, "LowPoly": 25, "Geometry": 15, "Sketch": 25}
RIG_CREDITS = 10
CREDIT_PRICE_CNY = 0.12 # 后付费单价;预付费 0.09–0.1
MAX_IMAGE_BYTES = 6 * 10**6 # ImageBase64 上限
VIEW_TYPES = ("back", "left", "right") # 正面走主参数,不在这里

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这块我觉得可以统一走配置?
你看看是否可以将这些东西放在.env中?而不是在代码中写死?


Expand Down Expand Up @@ -266,10 +265,10 @@ def __init__(
self._poll = poll_interval
self._max_min = max_min

def quote(self, n_views: int = 1) -> tuple[int, float]:
def quote(self, n_views: int = 1) -> int:
"""返回 (积分, 预估元)。PBR、多视图各 +10 积分。纯计算,可在提交前随便调。"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 将 quote 文档同步为单个积分返回值

这里的签名和实现已改为返回 int,但 docstring 仍承诺返回 (积分, 预估元)。任何按实现文档调用或解包返回值的使用方都会得到错误指引;请把说明改成仅返回积分,并删除“预估元”。

credits = CREDITS[self._type] + (10 if self._pbr else 0) + (10 if n_views > 1 else 0)
return credits, round(credits * CREDIT_PRICE_CNY, 2)
return credits

def build_params(self, master: bytes,
extra_views: Mapping[str, bytes] | None = None) -> dict:
Expand Down Expand Up @@ -305,10 +304,10 @@ def image_to_3d(self, master: bytes, *, want: str = "GLB",
if want not in MODEL_FORMATS:
raise ArtifactFormatError(f"want 只能是 {MODEL_FORMATS},收到 {want!r}")
params = self.build_params(master, extra_views)
credits, cny = self.quote(1 + len(extra_views or {}))
credits = self.quote(1 + len(extra_views or {}))
if not self._allow_spend:
raise SpendNotAuthorizedError(
f"图生 3D 会消耗 {credits} 积分(后付费约 ¥{cny})。"
f"图生 3D 会消耗 {credits} 积分。"
"确认要花这笔钱后,用 TencentModel3DProvider(..., allow_spend=True) 构造。")

if self._ask_format:
Expand Down Expand Up @@ -392,8 +391,8 @@ def __init__(
def preset_motions(self) -> Mapping[str, PresetMotion]:
return PRESET_MOTIONS

def quote(self) -> tuple[int, float]:
return RIG_CREDITS, round(RIG_CREDITS * CREDIT_PRICE_CNY, 2)
def quote(self) -> int:
return RIG_CREDITS

def resolve_motion(self, motion: str | int | None) -> PresetMotion | None:
"""动作名 / 编号 → :class:`PresetMotion`。名字不认识就抛,不猜编号。"""
Expand Down Expand Up @@ -424,10 +423,10 @@ def rig(self, model: bytes, *, want: str = "GLB",
# 入口预检在**上传与提交之前**:三条硬约束违反了接口不报错,只默默出错结果。
check_model(model)

credits, cny = self.quote()
credits = self.quote()
if not self._allow_spend:
raise SpendNotAuthorizedError(
f"绑骨会消耗 {credits} 积分(后付费约 ¥{cny})。"
f"绑骨会消耗 {credits} 积分。"
"确认要花这笔钱后,用 TencentAutoRigProvider(..., allow_spend=True) 构造。")

url = self._upload(model, src_fmt)
Expand Down
16 changes: 10 additions & 6 deletions backend/tests/test_render3d_tencent.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ def test_model3d_refuses_to_spend_by_default(cloud):
p = TencentModel3DProvider(CREDS)
with pytest.raises(SpendNotAuthorizedError) as e:
p.image_to_3d(b"\x89PNG fake")
assert "20 积分" in str(e.value) and "¥2.4" in str(e.value) # 报价必须写在异常里
# 报价写在异常里,但只给积分 —— 人民币是我们付给供应商的成本,不是用户的价钱,
# 而本仓是公开仓。同一条判据见 test_render3d_asset_endpoints 里的出参断言。
assert "20 积分" in str(e.value)
assert "¥" not in str(e.value), "人民币成本不出现在任何对外文案里"
assert fake.calls == [] # 一个请求都没发出去


Expand Down Expand Up @@ -142,11 +145,12 @@ def test_unknown_motion_name_dies_before_the_spend_gate(cloud):


def test_quotes_are_pure_computation():
assert TencentModel3DProvider(CREDS).quote() == (20, 2.4)
assert TencentModel3DProvider(CREDS, generate_type="Geometry").quote() == (15, 1.8)
assert TencentModel3DProvider(CREDS, enable_pbr=True).quote() == (30, 3.6)
assert TencentModel3DProvider(CREDS).quote(n_views=3) == (30, 3.6)
assert TencentAutoRigProvider(Uploader(), CREDS).quote() == (10, 1.2)
"""报价只出积分。换算人民币要用供应商单价,那个数不该留在公开仓里。"""
assert TencentModel3DProvider(CREDS).quote() == 20
assert TencentModel3DProvider(CREDS, generate_type="Geometry").quote() == 15
assert TencentModel3DProvider(CREDS, enable_pbr=True).quote() == 30
assert TencentModel3DProvider(CREDS).quote(n_views=3) == 30
assert TencentAutoRigProvider(Uploader(), CREDS).quote() == 10


def test_unknown_generate_type_dies_at_construction():
Expand Down
Loading