91 lines
2.1 KiB
Python
91 lines
2.1 KiB
Python
from __future__ import annotations
|
||
|
||
from typing import Any, Iterable, Optional
|
||
|
||
|
||
def normalize_int_id_list(mixed_ids: Iterable[Any]) -> list[int]:
|
||
"""
|
||
将混合类型的 id 列表归一化为 int 列表。
|
||
|
||
规则:
|
||
- int/可转 int 的 str -> int
|
||
- 其他(None/空字符串/不可解析)忽略
|
||
"""
|
||
|
||
out: list[int] = []
|
||
for x in mixed_ids or []:
|
||
if x is None:
|
||
continue
|
||
if isinstance(x, bool):
|
||
# 避免 True/False 被当作 1/0
|
||
continue
|
||
try:
|
||
s = str(x).strip()
|
||
if s == "":
|
||
continue
|
||
out.append(int(s))
|
||
except Exception:
|
||
continue
|
||
return out
|
||
|
||
|
||
def merge_exclude_ids(
|
||
*,
|
||
already_recommended_ids: Iterable[Any],
|
||
touched_or_viewed_ids: Iterable[Any],
|
||
extra_exclude_content_ids: Optional[Iterable[int]] = None,
|
||
) -> list[int]:
|
||
"""
|
||
合并并去重排除 id(保持首次出现顺序)。
|
||
"""
|
||
|
||
merged = list(normalize_int_id_list(list(already_recommended_ids or []) + list(touched_or_viewed_ids or [])))
|
||
if extra_exclude_content_ids:
|
||
merged += [int(x) for x in extra_exclude_content_ids if x is not None]
|
||
|
||
seen: set[int] = set()
|
||
out: list[int] = []
|
||
for cid in merged:
|
||
if cid in seen:
|
||
continue
|
||
seen.add(cid)
|
||
out.append(cid)
|
||
return out
|
||
|
||
|
||
def normalize_or_default_locale(locale: Optional[str]) -> str:
|
||
"""
|
||
locale 防御式归一化:
|
||
- 未传/空 -> 默认 "en"
|
||
- 其他 -> 原样返回,由下游 normalize_locale 做严格校验
|
||
"""
|
||
|
||
if locale is None:
|
||
return "en"
|
||
raw = str(locale).strip()
|
||
return raw or "en"
|
||
|
||
|
||
def clamp_personalization_power(power: Any, *, fallback_level: int) -> float:
|
||
"""
|
||
按回退层级对 personalization_power 做防御式约束。
|
||
|
||
- L0:不改
|
||
- L1:<= 0.5
|
||
- L2/L3:= 0
|
||
"""
|
||
|
||
try:
|
||
p = float(power)
|
||
except Exception:
|
||
p = 0.0
|
||
if p != p:
|
||
p = 0.0
|
||
|
||
if int(fallback_level) >= 2:
|
||
return 0.0
|
||
if int(fallback_level) >= 1:
|
||
return min(p, 0.5)
|
||
return p
|
||
|