fix(models): allow same endpoint URL with different keys (#2758)

* fix(models): allow same endpoint URL with different keys

* fix(models): show endpoint key fingerprints
This commit is contained in:
Ocean Bennett
2026-06-05 15:12:14 -04:00
committed by GitHub
parent 08e543d1ff
commit 5911b8c0dc
3 changed files with 120 additions and 7 deletions
+35 -6
View File
@@ -5,6 +5,7 @@ import re
import uuid
import json
import socket
import hashlib
import time as _time
import logging
import httpx
@@ -850,6 +851,14 @@ def _visible_models(cached_models, hidden_models, pinned_models=None):
return [m for m in merged if m not in hidden]
def _api_key_fingerprint(api_key: Optional[str]) -> str:
"""Stable, non-secret label for distinguishing same-URL credentials."""
key = (api_key or "").strip()
if not key:
return ""
return hashlib.sha256(key.encode("utf-8")).hexdigest()[:8]
def setup_model_routes(model_discovery):
router = APIRouter(prefix="/api")
@@ -1397,6 +1406,7 @@ def setup_model_routes(model_discovery):
"name": r.name,
"base_url": r.base_url,
"has_key": bool(r.api_key),
"api_key_fingerprint": _api_key_fingerprint(r.api_key),
"is_enabled": r.is_enabled,
"models": visible,
"pinned_models": pinned,
@@ -1463,21 +1473,34 @@ def setup_model_routes(model_discovery):
)
explicit_timeout = _explicit_model_list_timeout(base_url, requested_kind, refresh_timeout)
# Dedupe: if an endpoint with the same base_url already exists and
# is reachable by the caller (shared or owned by them), return it
# instead of creating a duplicate row. Fixes "Scan for Servers"
# re-adding manually-added endpoints under their host:port name.
# Dedupe: if an endpoint with the same base_url and compatible
# credentials already exists and is reachable by the caller (shared or
# owned by them), return it instead of creating a duplicate row. Keep
# same-url/different-key rows distinct so users can group the same
# provider URL under multiple credentials.
from src.auth_helpers import get_current_user as _gcu_dedup
_caller = _gcu_dedup(request) or None
_incoming_api_key = api_key.strip()
_db_dedup = SessionLocal()
try:
existing = (
_same_url_rows = (
_db_dedup.query(ModelEndpoint)
.filter(ModelEndpoint.base_url == base_url)
.filter((ModelEndpoint.owner.is_(None)) | (ModelEndpoint.owner == _caller))
.order_by(ModelEndpoint.owner.desc()) # prefer owned over shared
.first()
.all()
)
existing = None
_empty_key_existing = None
for _candidate in _same_url_rows:
_candidate_key = (getattr(_candidate, "api_key", None) or "").strip()
if _candidate_key == _incoming_api_key:
existing = _candidate
break
if _incoming_api_key and not _candidate_key and _empty_key_existing is None:
_empty_key_existing = _candidate
if existing is None and _incoming_api_key and _empty_key_existing is not None:
existing = _empty_key_existing
if existing:
changed = False
# Persist any incoming pinned IDs onto the existing row. An
@@ -1526,6 +1549,8 @@ def setup_model_routes(model_discovery):
"id": existing.id,
"name": existing.name,
"base_url": existing.base_url,
"has_key": bool(existing.api_key),
"api_key_fingerprint": _api_key_fingerprint(existing.api_key),
"models": _visible_models(
existing_models,
getattr(existing, "hidden_models", None),
@@ -1599,6 +1624,8 @@ def setup_model_routes(model_discovery):
"id": ep_id,
"name": name.strip(),
"base_url": base_url,
"has_key": bool(api_key.strip()),
"api_key_fingerprint": _api_key_fingerprint(api_key),
"models": _merge_model_ids(model_ids, _pinned),
"pinned_models": _pinned,
"online": bool(model_ids) or bool(_pinned) or bool(ping.get("reachable")),
@@ -1948,6 +1975,8 @@ def setup_model_routes(model_discovery):
"name": ep.name,
"model_type": ep.model_type,
"base_url": ep.base_url,
"has_key": bool(ep.api_key),
"api_key_fingerprint": _api_key_fingerprint(ep.api_key),
"pinned_models": _normalize_model_ids(getattr(ep, "pinned_models", None)),
"endpoint_kind": getattr(ep, "endpoint_kind", None) or "auto",
"model_refresh_mode": getattr(ep, "model_refresh_mode", None) or "auto",