mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/ - 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层 - 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构 - 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations) - 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image - 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
10
_deprecated_py_src/services/billing/rule_defs/__init__.py
Normal file
10
_deprecated_py_src/services/billing/rule_defs/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
Billing rule definitions (config-file mode).
|
||||
|
||||
Each module should export:
|
||||
- TEMPLATES: list[CodeBillingRuleTemplate]
|
||||
|
||||
Design goal:
|
||||
- Add a new billing mode by adding a new file here.
|
||||
- No central registry edits required.
|
||||
"""
|
||||
195
_deprecated_py_src/services/billing/rule_defs/universal.py
Normal file
195
_deprecated_py_src/services/billing/rule_defs/universal.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Universal billing template.
|
||||
|
||||
This is the single unified billing template for all task types.
|
||||
Formula: total = (input_cost + output_cost + cache_creation_cost + cache_read_cost) + request_cost + video_cost
|
||||
|
||||
Each component can be 0 if not applicable for the specific task type.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from src.services.billing.default_rules import DefaultBillingRuleGenerator, VirtualBillingRule
|
||||
from src.services.billing.rule_templates import CodeBillingRuleTemplate, RuleTemplateContext
|
||||
|
||||
|
||||
def _get_nested(obj: object | None, path: str) -> object | None:
|
||||
if not isinstance(obj, dict):
|
||||
return None
|
||||
cur: object = obj
|
||||
for part in (path or "").split("."):
|
||||
if not part:
|
||||
continue
|
||||
if not isinstance(cur, dict):
|
||||
return None
|
||||
cur = cur.get(part) # type: ignore[assignment]
|
||||
return cur
|
||||
|
||||
|
||||
def _as_float(v: object | None) -> float | None:
|
||||
try:
|
||||
if v is None:
|
||||
return None
|
||||
if isinstance(v, bool):
|
||||
return None
|
||||
return float(v)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
_WXH_PATTERN = re.compile(r"^(\d+)x(\d+)$")
|
||||
|
||||
|
||||
def _normalize_resolution_key(raw: str) -> str:
|
||||
"""
|
||||
Normalize resolution key:
|
||||
- lowercase, remove spaces, × → x
|
||||
- For WxH format, sort dimensions so smaller comes first (1080x720 → 720x1080)
|
||||
"""
|
||||
k = (raw or "").strip().lower().replace(" ", "").replace("×", "x")
|
||||
match = _WXH_PATTERN.match(k)
|
||||
if match:
|
||||
a, b = int(match.group(1)), int(match.group(2))
|
||||
k = f"{a}x{b}" if a <= b else f"{b}x{a}"
|
||||
return k
|
||||
|
||||
|
||||
def _effective_unit_price(ctx: RuleTemplateContext) -> float:
|
||||
"""Get video price per second from config."""
|
||||
if ctx.model is not None:
|
||||
v = _as_float(
|
||||
_get_nested(getattr(ctx.model, "config", None), "billing.video.price_per_second")
|
||||
)
|
||||
if v is not None:
|
||||
return v
|
||||
v = _as_float(
|
||||
_get_nested(getattr(ctx.global_model, "config", None), "billing.video.price_per_second")
|
||||
)
|
||||
if v is not None:
|
||||
return v
|
||||
return 0.0
|
||||
|
||||
|
||||
def _effective_resolution_price_per_second(ctx: RuleTemplateContext) -> dict[str, float]:
|
||||
"""
|
||||
Resolution (or size) -> price_per_second.
|
||||
"""
|
||||
for conf in (
|
||||
getattr(ctx.model, "config", None) if ctx.model is not None else None,
|
||||
getattr(ctx.global_model, "config", None),
|
||||
):
|
||||
raw = _get_nested(conf, "billing.video.price_per_second_by_resolution")
|
||||
if not isinstance(raw, dict):
|
||||
continue
|
||||
out: dict[str, float] = {}
|
||||
for k, v in raw.items():
|
||||
fk = _normalize_resolution_key(str(k))
|
||||
fv = _as_float(v)
|
||||
if not fk:
|
||||
continue
|
||||
if fv is None:
|
||||
continue
|
||||
out[fk] = fv
|
||||
if out:
|
||||
return out
|
||||
|
||||
# Backward-compat: resolution multipliers
|
||||
base = _effective_unit_price(ctx)
|
||||
if base and base > 0:
|
||||
for conf in (
|
||||
getattr(ctx.model, "config", None) if ctx.model is not None else None,
|
||||
getattr(ctx.global_model, "config", None),
|
||||
):
|
||||
raw = _get_nested(conf, "billing.video.resolution_multipliers")
|
||||
if not isinstance(raw, dict):
|
||||
continue
|
||||
out2: dict[str, float] = {}
|
||||
for k, v in raw.items():
|
||||
fk = _normalize_resolution_key(str(k))
|
||||
mv = _as_float(v)
|
||||
if not fk:
|
||||
continue
|
||||
if mv is None:
|
||||
continue
|
||||
out2[fk] = float(base) * float(mv)
|
||||
if out2:
|
||||
return out2
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def build_universal(ctx: RuleTemplateContext) -> VirtualBillingRule:
|
||||
"""
|
||||
Build the universal billing rule.
|
||||
|
||||
Formula:
|
||||
total = (input_cost + output_cost + cache_creation_cost + cache_read_cost) + request_cost + video_cost
|
||||
|
||||
Each component defaults to 0 if not configured or not applicable.
|
||||
"""
|
||||
# Base rule: token + per-request
|
||||
base = DefaultBillingRuleGenerator.generate_for_model(
|
||||
global_model=ctx.global_model,
|
||||
model=ctx.model,
|
||||
task_type=ctx.task_type,
|
||||
)
|
||||
|
||||
unit_price = _effective_unit_price(ctx)
|
||||
resolution_price_map = _effective_resolution_price_per_second(ctx)
|
||||
|
||||
variables = dict(base.variables or {})
|
||||
|
||||
dimension_mappings = dict(base.dimension_mappings or {})
|
||||
|
||||
# Video duration dimension
|
||||
dimension_mappings["duration_seconds"] = {
|
||||
"source": "dimension",
|
||||
"key": "duration_seconds",
|
||||
"required": False,
|
||||
"allow_zero": True,
|
||||
"default": 0,
|
||||
}
|
||||
|
||||
# Video price per second (resolved from resolution map or fallback to unit price)
|
||||
dimension_mappings["video_price_per_second"] = {
|
||||
"source": "matrix",
|
||||
"key": "video_resolution_key",
|
||||
"required": False,
|
||||
"default": unit_price,
|
||||
"map": resolution_price_map,
|
||||
}
|
||||
|
||||
# Video cost component
|
||||
dimension_mappings["video_cost"] = {
|
||||
"source": "computed",
|
||||
"required": False,
|
||||
"default": 0,
|
||||
"expression": "duration_seconds * video_price_per_second",
|
||||
}
|
||||
|
||||
# Universal formula: token costs + request cost + video cost
|
||||
# base.expression = "input_cost + output_cost + cache_creation_cost + cache_read_cost + request_cost"
|
||||
expression = f"({base.expression}) + video_cost"
|
||||
|
||||
return VirtualBillingRule(
|
||||
id="__default__",
|
||||
name="Universal Billing Rule",
|
||||
task_type=ctx.task_type,
|
||||
expression=expression,
|
||||
variables=variables,
|
||||
dimension_mappings=dimension_mappings,
|
||||
is_virtual=True,
|
||||
)
|
||||
|
||||
|
||||
TEMPLATES = [
|
||||
CodeBillingRuleTemplate(
|
||||
name="universal",
|
||||
description="Universal billing: (input + output + cache) + request + video. All components default to 0 if not applicable.",
|
||||
task_types={"chat", "cli", "video", "image", "audio"},
|
||||
priority=100, # Highest priority - used for all task types
|
||||
build=build_universal,
|
||||
)
|
||||
]
|
||||
Reference in New Issue
Block a user