装配 /v1/models 响应,返回 (状态码, body dict)。协议锁定(修评审 P2-2): - relay/openai-custom 回源成功 → (200, {data:[…含 supports_tools…]})。 - 回源 HTTPError → (上游同状态码, {error_kind:"upstream", upstream_status, message}), 绝不吞成 200+静态(否则掩盖坏 key)。builtin 兜底交 Rust 命令决定。 - 网络异常 → (502, {error_kind:"netw
()
| 384 | |
| 385 | |
| 386 | def build_models_response(): |
| 387 | """装配 /v1/models 响应,返回 (状态码, body dict)。协议锁定(修评审 P2-2): |
| 388 | - relay/openai-custom 回源成功 → (200, {data:[…含 supports_tools…]})。 |
| 389 | - 回源 HTTPError → (上游同状态码, {error_kind:"upstream", upstream_status, message}), |
| 390 | 绝不吞成 200+静态(否则掩盖坏 key)。builtin 兜底交 Rust 命令决定。 |
| 391 | - 网络异常 → (502, {error_kind:"network", upstream_status:None, message})。 |
| 392 | - 非 relay(无 models_url,deepseek/qwen)→ (200, {静态选择器列表}),行为不变。""" |
| 393 | if PROV.get("models_url"): |
| 394 | if RELAY_FORCE_MODEL: |
| 395 | # force(Science 常驻代理):只返回一个壳,Science 主列表显示真实模型名。 |
| 396 | # 出站由 resolve_model 的 force 分支覆盖,无需 model_map。app 的 fetch_models |
| 397 | # 不设 RELAY_FORCE_MODEL,故仍走下面回源拿真实 id 供用户选(两个消费者切分)。 |
| 398 | shell = [{"type": "model", "id": "claude-opus-4-8", |
| 399 | "display_name": RELAY_FORCE_MODEL, "supports_tools": None, |
| 400 | "created_at": "2026-01-01T00:00:00Z"}] |
| 401 | log(f"GET /v1/models -> {PROV_NAME}(force 借壳): {RELAY_FORCE_MODEL}") |
| 402 | return 200, {"data": shell, "has_more": False, |
| 403 | "first_id": "claude-opus-4-8", "last_id": "claude-opus-4-8"} |
| 404 | try: |
| 405 | data = fetch_relay_models() |
| 406 | log(f"GET /v1/models -> {PROV_NAME}(回源): {len(data)} 个模型") |
| 407 | return 200, {"data": data, "has_more": False, |
| 408 | "first_id": data[0]["id"] if data else None, |
| 409 | "last_id": data[-1]["id"] if data else None} |
| 410 | except urllib.error.HTTPError as e: |
| 411 | detail = "" |
| 412 | try: |
| 413 | detail = e.read().decode("utf-8", "replace")[:200] |
| 414 | except Exception: |
| 415 | pass |
| 416 | log(f"GET /v1/models -> {PROV_NAME} 回源 HTTP {e.code}(保留状态码,不回静态)") |
| 417 | return e.code, {"error_kind": "upstream", "upstream_status": e.code, |
| 418 | "message": f"upstream {e.code}: {detail}"} |
| 419 | except Exception as e: |
| 420 | log(f"GET /v1/models -> {PROV_NAME} 回源网络异常,本地回 502: {e}") |
| 421 | return 502, {"error_kind": "network", "upstream_status": None, "message": str(e)} |
| 422 | # 非 relay:静态选择器列表(deepseek/qwen)。 |
| 423 | data = [{"type": "model", "id": mid, "display_name": disp, "supports_tools": None, |
| 424 | "created_at": "2026-01-01T00:00:00Z"} for mid, disp in PROV["models"]] |
| 425 | return 200, {"data": data, "has_more": False, |
| 426 | "first_id": data[0]["id"] if data else None, |
| 427 | "last_id": data[-1]["id"] if data else None} |
| 428 | |
| 429 | |
| 430 | # ---------- Anthropic -> OpenAI 翻译(qwen 路径) ---------- |
no test coverage detected