models: dedupe endpoints by base_url on create (#266)

POST /api/model-endpoints always inserted a new row, so Settings -> Add
Models -> Scan for Servers re-added any endpoint a user had already
registered manually — once under its model name (from the earlier
manual add) and again under its host:port (auto-generated when scan
posts without a name). The success toast then misreported the result
as "added N new".

Look up an existing endpoint with the same base_url accessible to the
caller (shared or owned by them) before inserting. If found, return it
with `existing: true` so the client can tell the difference between
an actual add and a dedupe hit. Toast now reads, e.g.,
"Found 1 server with 1 model — 1 already added".

Tested: POSTing the same base_url three times (incl. trailing-slash
variation) returns the same id each time; only one row exists.
This commit is contained in:
Sirsyorrz
2026-06-01 15:22:06 +10:00
committed by GitHub
parent 5c142ec34a
commit 09acf955f1
2 changed files with 40 additions and 4 deletions
+28
View File
@@ -909,6 +909,34 @@ def setup_model_routes(model_discovery):
require_model_list = _truthy(require_models)
should_probe = require_model_list or not _truthy(skip_probe)
# 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.
from src.auth_helpers import get_current_user as _gcu_dedup
_caller = _gcu_dedup(request) or None
_db_dedup = SessionLocal()
try:
existing = (
_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()
)
if existing:
return {
"id": existing.id,
"name": existing.name,
"base_url": existing.base_url,
"models": json.loads(existing.cached_models) if existing.cached_models else [],
"online": True,
"status": "online",
"existing": True,
}
finally:
_db_dedup.close()
# Quick model list fetch (1s timeout — if endpoint is slow, it'll update on next refresh)
_probe_timeout = 3 if (":11434" in base_url or "ollama" in base_url.lower()) else 1
model_ids = _probe_endpoint(base_url, api_key.strip() or None, timeout=_probe_timeout) if should_probe else []