diff --git a/pipeline_llm/gateway.py b/pipeline_llm/gateway.py index c1a6d2c..b0114d5 100644 --- a/pipeline_llm/gateway.py +++ b/pipeline_llm/gateway.py @@ -260,8 +260,19 @@ def _filter_by_pref(candidates_with_ep, pref: str): return matched + rest +async def _model_protocol(sor, model: dict) -> str: + """模型适配模板的协议(2026-09-05 端点协议感知选择用)。查不到返回空串。""" + pid = (model.get('profile_id') or '').strip() + if not pid: + return '' + recs = await sor.sqlExe( + "SELECT protocol FROM llm_api_profile WHERE id=${i}$ LIMIT 1", {"i": pid}) + await sor.sqlExe("COMMIT", {}) + return (getattr(recs[0], 'protocol', '') or '').strip() if recs else '' + + async def _pick_candidate(sor, model: dict, pref: str): - """构建候选并选一个:区域偏好过滤 → 冷却避让 → 余额加权。 + """构建候选并选一个:协议过滤 → 区域偏好过滤 → 冷却避让 → 余额加权。 返回 (ok, result):ok 时 result=(account_dict, endpoint_dict);否则 result=错误信息。 """ @@ -274,6 +285,27 @@ async def _pick_candidate(sor, model: dict, pref: str): accounts = await _account_candidates(sor, model) if not accounts: return False, '模型「%s」的供应商下无启用账号(或账号余额状态异常)' % model.get('name', '') + # 协议感知(2026-09-05 404 教训):模型模板协议与端点标签匹配才可用。 + # 端点无标签(历史数据)视为通用——同供应商混布 compatible-mode/api/v1 + # 两类端点时,无标签过滤会把原生异步模型拼到 openai_compat 前缀下 404。 + mproto = await _model_protocol(sor, model) + if mproto: + tagged = [(i, e) for i, e in enumerate(endpoints) if (e.get('protocol') or '').strip()] + if tagged: + matched = {i for i, e in tagged if (e.get('protocol') or '').strip() == mproto} + untagged = {i for i, e in enumerate(endpoints) + if not (e.get('protocol') or '').strip()} + usable = matched | untagged + if not matched: + return False, ('供应商端点均为其他协议(模型模板协议 %s,端点协议 %s)——' + '原生异步(openai_compat 之外)与兼容模式端点 base_url 不同,' + '混用必 404。请在供应商端点目录添加协议 %s 的端点' + % (mproto, sorted({(e.get('protocol') or '') for _, e in tagged}), + mproto)) + else: + usable = set(range(len(endpoints))) + else: + usable = set(range(len(endpoints))) # 展开 (账号, 端点) 对:账号选用的端点下标 pairs = [] for acc in accounts: @@ -285,10 +317,11 @@ async def _pick_candidate(sor, model: dict, pref: str): i = int(i) except (TypeError, ValueError): continue - if 0 <= i < len(endpoints): + if i in usable: pairs.append((acc, endpoints[i], i)) if not pairs: - return False, '所有账号都未选用端点(模型治理→供应商账号→选用端点),请配置' + return False, ('所有账号都未选用可用端点(模型治理→供应商账号→选用端点)。' + '模型模板协议 %s——请把该协议端点加入账号的选用端点' % (mproto or '未知')) pairs = _filter_by_pref(pairs, pref) if not pairs: return False, ('端点偏好为「仅 %s」,但没有可用的该区域端点。按合规要求不跨端点降级——'