|
|
@@ -3,8 +3,8 @@
|
|
|
业务模型(用户 2026-06-08 确认):
|
|
|
对一个承接视频(LandingVideo)执行 3 种策略召回素材,合并去重,按 score 取 top N。
|
|
|
|
|
|
-接口:POST https://api-internal.piaoquantv.com/videoVector/recallTest/matchByText
|
|
|
-鉴权:不需要(内部 API)
|
|
|
+接口:POST https://tp-open.piaoquantv.com/contentPlatform/videoVector/recallTest/matchByText
|
|
|
+鉴权:复用 contentPlatform 动态登录 token
|
|
|
|
|
|
本质(用户 2026-06-08 确认):**多路召回 → 排序 → 去重**
|
|
|
|
|
|
@@ -30,28 +30,68 @@ from typing import Iterable, List, Optional
|
|
|
|
|
|
import httpx
|
|
|
|
|
|
-from tools.video_recall import LandingVideo
|
|
|
+from tools.video_recall import (
|
|
|
+ LandingVideo,
|
|
|
+ _build_auth_headers,
|
|
|
+ _is_not_login_error,
|
|
|
+ get_piaoquantv_token,
|
|
|
+ refresh_piaoquantv_token,
|
|
|
+)
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
VECTOR_BASE = os.getenv(
|
|
|
"VECTOR_BASE_URL",
|
|
|
- "https://api-internal.piaoquantv.com/videoVector",
|
|
|
+ "https://tp-open.piaoquantv.com/contentPlatform/videoVector",
|
|
|
)
|
|
|
VECTOR_MATCH_BY_TEXT = f"{VECTOR_BASE}/recallTest/matchByText"
|
|
|
VECTOR_BATCH_BY_TEXT = f"{VECTOR_BASE}/recallTest/batchByText"
|
|
|
VECTOR_ALL_CONFIG_CODES = f"{VECTOR_BASE}/videoSearch/getAllConfigCodes"
|
|
|
|
|
|
|
|
|
+def _request_vector(
|
|
|
+ method: str,
|
|
|
+ url: str,
|
|
|
+ *,
|
|
|
+ json_body: Optional[dict] = None,
|
|
|
+ timeout: int = 30,
|
|
|
+) -> dict:
|
|
|
+ """调用 growth-manager vector 转发接口,token 失效时刷新并重试一次。"""
|
|
|
+ token = get_piaoquantv_token()
|
|
|
+
|
|
|
+ def _do_request(current_token: str):
|
|
|
+ return httpx.request(
|
|
|
+ method,
|
|
|
+ url,
|
|
|
+ json=json_body,
|
|
|
+ headers=_build_auth_headers(current_token),
|
|
|
+ timeout=timeout,
|
|
|
+ )
|
|
|
+
|
|
|
+ resp = _do_request(token)
|
|
|
+ if resp.status_code == 401:
|
|
|
+ logger.warning("[material_recall] vector 转发接口返回 401,刷新 token 并重试")
|
|
|
+ token = refresh_piaoquantv_token()
|
|
|
+ resp = _do_request(token)
|
|
|
+
|
|
|
+ resp.raise_for_status()
|
|
|
+ data = resp.json()
|
|
|
+ if _is_not_login_error(data):
|
|
|
+ logger.warning("[material_recall] vector 转发接口返回 Not_LOGIN(1000),刷新 token 并重试")
|
|
|
+ token = refresh_piaoquantv_token()
|
|
|
+ resp = _do_request(token)
|
|
|
+ resp.raise_for_status()
|
|
|
+ data = resp.json()
|
|
|
+ return data
|
|
|
+
|
|
|
+
|
|
|
def fetch_all_config_codes() -> dict[str, str]:
|
|
|
"""动态获取 vector 服务支持的全部 configCode → 中文名。
|
|
|
|
|
|
返回 {configCode: 中文名}。
|
|
|
召回前先调一次,避免硬编码列表过时。
|
|
|
"""
|
|
|
- resp = httpx.get(VECTOR_ALL_CONFIG_CODES, timeout=15)
|
|
|
- resp.raise_for_status()
|
|
|
- data = resp.json()
|
|
|
+ data = _request_vector("GET", VECTOR_ALL_CONFIG_CODES, timeout=15)
|
|
|
if data.get("code") not in (0, 200):
|
|
|
raise RuntimeError(
|
|
|
f"getAllConfigCodes 失败:code={data.get('code')} msg={data.get('msg')}"
|
|
|
@@ -196,14 +236,12 @@ def _call_match_by_text(
|
|
|
"[material_recall] matchByText q=%r configCode=%s topN=%d",
|
|
|
query_text[:40], config_code, material_top_n,
|
|
|
)
|
|
|
- resp = httpx.post(
|
|
|
+ data = _request_vector(
|
|
|
+ "POST",
|
|
|
VECTOR_MATCH_BY_TEXT,
|
|
|
- json=body,
|
|
|
- headers={"content-type": "application/json", "accept": "application/json"},
|
|
|
+ json_body=body,
|
|
|
timeout=timeout,
|
|
|
)
|
|
|
- resp.raise_for_status()
|
|
|
- data = resp.json()
|
|
|
|
|
|
code = data.get("code")
|
|
|
# CommonResponse 可能用 0 或 200 表示成功;以 data 字段为准
|
|
|
@@ -327,14 +365,12 @@ def _call_batch_by_text(
|
|
|
"[material_recall] batchByText q=%r configCodes=%d displayK=%d simT=%.2f wCtr=%.2f",
|
|
|
query_text[:40], len(config_codes), display_k, sim_threshold, w_ctr,
|
|
|
)
|
|
|
- resp = httpx.post(
|
|
|
+ data = _request_vector(
|
|
|
+ "POST",
|
|
|
VECTOR_BATCH_BY_TEXT,
|
|
|
- json=body,
|
|
|
- headers={"content-type": "application/json", "accept": "application/json"},
|
|
|
+ json_body=body,
|
|
|
timeout=timeout,
|
|
|
)
|
|
|
- resp.raise_for_status()
|
|
|
- data = resp.json()
|
|
|
if data.get("code") not in (0, 200, "0", "200"):
|
|
|
raise RuntimeError(
|
|
|
f"batchByText 失败:code={data.get('code')} msg={data.get('msg') or data.get('message')}"
|