fix(platform): 端点协议感知治理(404根因)——端点落库带protocol标签;裸域退化拦截(同host已有带路径端点则跳过);账号端点绑定自检:活跃账号未绑本协议端点自动补绑并报account_notes

This commit is contained in:
yumoqing 2026-09-05 23:08:15 +08:00
parent ac52a61e0b
commit d6228c59c6

View File

@ -499,7 +499,13 @@ async def _h_apply_llm_config(sor, params, ctx):
from appPublic.uniqueID import getID
# 1. 供应商:按名称复用
# 1. 供应商按名称复用。端点带协议标签2026-09-05 404 实测教训):
# 同一供应商可同时有 openai_compat(compatible-mode/v1) 与原生异步(api/v1)
# 两类端点,账号绑定/运行时选择必须按协议匹配,否则原生模型被拼到
# compatible-mode 前缀下 → 404。另拦截「裸域退化」提取 LLM 偶发把
# base_url 提成 https://host丢了 /api/v1与已有带路径端点同 host
# 时该裸域无意义,跳过不入目录。
from urllib.parse import urlparse as _urlparse
recs = await sor.sqlExe(
"SELECT id, endpoints FROM llm_vendor WHERE name=${n}$", {"n": vendor_name})
await sor.sqlExe("COMMIT", {})
@ -512,29 +518,55 @@ async def _h_apply_llm_config(sor, params, ctx):
eps_old = []
merged = list(eps_old)
added_eps = []
skipped_eps = []
for ep in endpoints:
bu = (ep.get("base_url") or "").rstrip("/")
if bu and bu not in [(e.get("base_url") or "").rstrip("/") for e in merged]:
merged.append({"base_url": bu, "region": ep.get("region") or "domestic",
"timeout": int(_f(ep.get("timeout"), 60))})
added_eps.append(bu)
# 2026-09-05供应商表不再有 protocol——请求形态由模型挂的适配模板决定
if not bu:
continue
# 同 base_url 已存在:只补协议标签(旧端点无 protocol 时回填)
hit = None
for e in merged:
if (e.get("base_url") or "").rstrip("/") == bu:
hit = e
break
if hit is not None:
if protocol and not hit.get("protocol"):
hit["protocol"] = protocol
continue
# 裸域退化拦截:同 host 已有带路径端点 → 裸域跳过
pu = _urlparse(bu)
if not pu.path.strip("/"):
same_host = any(_urlparse((e.get("base_url") or "")).netloc == pu.netloc
for e in merged)
if same_host:
skipped_eps.append("%s(裸域退化:同 host 已有带路径端点)" % bu)
continue
merged.append({"base_url": bu, "region": ep.get("region") or "domestic",
"timeout": int(_f(ep.get("timeout"), 60)),
"protocol": protocol})
added_eps.append(bu)
await sor.sqlExe(
"UPDATE llm_vendor SET endpoints=${e}$, updated_at=NOW() WHERE id=${i}$",
{"e": json.dumps(merged, ensure_ascii=False), "i": vendor_id})
await sor.sqlExe("COMMIT", {})
vendor_action = "复用供应商 %s(新增端点 %d 个)" % (vendor_id, len(added_eps))
vendor_action = "复用供应商 %s(新增端点 %d%s" % (
vendor_id, len(added_eps),
(",跳过 %s" % "".join(skipped_eps)) if skipped_eps else "")
vendor_endpoints = merged
else:
vendor_id = getID()
eps_norm = [{"base_url": (ep.get("base_url") or "").rstrip("/"),
"region": ep.get("region") or "domestic",
"timeout": int(_f(ep.get("timeout"), 60))} for ep in endpoints]
"timeout": int(_f(ep.get("timeout"), 60)),
"protocol": protocol} for ep in endpoints
if (ep.get("base_url") or "").strip()]
await sor.C("llm_vendor", {
"id": vendor_id, "name": vendor_name,
"endpoints": json.dumps(eps_norm, ensure_ascii=False),
"description": spec.get("doc_notes", "") or "",
"status": "active", "org_id": ctx.get("org_id", "") or "0"})
vendor_action = "新建供应商 %s%s" % (vendor_id, vendor_name)
vendor_endpoints = eps_norm
# 2. 适配模板:按 (协议×能力) 复用;旧骨架模板(硬编码 xxx_file/__from_doc__
# 2026-09-05 实测 t2v 因它渲染崩)视为不可用,按文档示例自愈重建
@ -649,6 +681,32 @@ async def _h_apply_llm_config(sor, params, ctx):
if protocol not in RUNTIME_PROTOCOLS:
rt_note = ("⚠️ 协议「%s」的适配模板已存档,但当前运行时调用链仅支持 %s——"
"该供应商模型暂不可被产线直接调用" % (protocol, "/".join(RUNTIME_PROTOCOLS)))
# 4. 账号端点绑定自检2026-09-05 404 教训):账号只绑 compatible-mode
# 端点时,原生协议模型运行时会被选到错误端点 → 404。自动补绑本协议
# 端点到该供应商全部活跃账号(不删已有绑定),如实报告。
account_notes = []
proto_idx = [i for i, e in enumerate(vendor_endpoints or [])
if (e.get("protocol") or "") == protocol]
if proto_idx:
recs = await sor.sqlExe(
"SELECT id, name, endpoint_ids FROM llm_account "
"WHERE vendor_id=${v}$ AND status='active'", {"v": vendor_id})
await sor.sqlExe("COMMIT", {})
for acc in recs or []:
try:
bound = [int(b) for b in json.loads(getattr(acc, "endpoint_ids", "") or "[]")]
except Exception:
bound = []
if any(i in proto_idx for i in bound):
continue # 已绑本协议端点,可用
newb = sorted(set(bound) | set(proto_idx))
await sor.sqlExe(
"UPDATE llm_account SET endpoint_ids=${e}$, updated_at=NOW() WHERE id=${i}$",
{"e": json.dumps(newb), "i": getattr(acc, "id", "")})
await sor.sqlExe("COMMIT", {})
account_notes.append("账号「%s」原绑定端点 %s 不含协议 %s 的端点(运行时会 404"
"已自动补绑为 %s" % (getattr(acc, "name", ""), bound,
protocol, newb))
audit_note = await _audit_media_convention(sor, list(profile_ids.values()))
return json.dumps({
"vendor": vendor_action, "profiles": profile_ids,
@ -656,6 +714,7 @@ async def _h_apply_llm_config(sor, params, ctx):
"vendor_conflicts": conflicts,
"template_errors": tpl_errors,
"template_notes": tpl_notes,
"account_notes": account_notes,
"runtime_note": rt_note,
"media_audit": audit_note,
}, ensure_ascii=False)