From d95c09e00c3f4d9b1a50e97d123a1f002b20a28b Mon Sep 17 00:00:00 2001 From: 9qeklajc Date: Sun, 19 Apr 2026 15:21:23 +0200 Subject: [PATCH 1/2] fix test model connection --- routstr/payment/models.py | 72 ++++++++++++++++++++++++++- ui/components/api-endpoint-tester.tsx | 6 ++- ui/components/model-tester.tsx | 6 ++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/routstr/payment/models.py b/routstr/payment/models.py index 95d7ad89..afed3238 100644 --- a/routstr/payment/models.py +++ b/routstr/payment/models.py @@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends from pydantic.v1 import BaseModel from sqlmodel.ext.asyncio.session import AsyncSession -from ..core.db import ModelRow, get_session +from ..core.db import ModelRow, UpstreamProviderRow, get_session from ..core.logging import get_logger from ..core.settings import settings from .price import sats_usd_price @@ -405,6 +405,76 @@ async def update_sats_pricing() -> None: logger.error(f"Error updating sats pricing: {e}") +class ModelTestRequest(BaseModel): + model_id: str + endpoint_type: str + request_data: dict + + +@models_router.post("/api/models/test") +async def test_model( + payload: ModelTestRequest, + session: AsyncSession = Depends(get_session), +) -> dict: + """Test a model by sending a request through its configured upstream provider.""" + from sqlmodel import select + + result = await session.execute( + select(ModelRow).where(ModelRow.id == payload.model_id) + ) + model_row = result.scalars().first() + + if not model_row: + return { + "success": False, + "error": f"Model '{payload.model_id}' not found in database", + "status_code": 404, + } + + provider = await session.get(UpstreamProviderRow, model_row.upstream_provider_id) + if not provider: + return { + "success": False, + "error": "Upstream provider not found", + "status_code": 404, + } + + base_url = provider.base_url.rstrip("/") + if payload.endpoint_type == "chat-completions": + url = f"{base_url}/chat/completions" + else: + url = f"{base_url}/{payload.endpoint_type}" + + actual_model_id = model_row.forwarded_model_id or model_row.id + request_data = dict(payload.request_data) + request_data["model"] = actual_model_id + + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {provider.api_key}", + } + + try: + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.post(url, json=request_data, headers=headers) + try: + response_data = response.json() + except Exception: + response_data = {"raw": response.text} + + return { + "success": response.status_code < 400, + "data": response_data, + "status_code": response.status_code, + } + except Exception as e: + return { + "success": False, + "error": str(e), + "status_code": 500, + } + + @models_router.get("/v1/models") @models_router.get("/v1/models/", include_in_schema=False) @models_router.get("/models") diff --git a/ui/components/api-endpoint-tester.tsx b/ui/components/api-endpoint-tester.tsx index a77b4248..dbb91266 100644 --- a/ui/components/api-endpoint-tester.tsx +++ b/ui/components/api-endpoint-tester.tsx @@ -471,7 +471,11 @@ export function ApiEndpointTester({ models }: ApiEndpointTesterProps) { testEndpointMutation.mutate(requestData); }; - const enabledModels = models.filter((model) => model.isEnabled); + const enabledModels = Array.from( + new Map( + models.filter((model) => model.isEnabled).map((m) => [m.id, m]) + ).values() + ); const credentials = selectedModel ? getModelCredentials(selectedModel) : null; const endpointUrl = credentials ? buildEndpointUrl( diff --git a/ui/components/model-tester.tsx b/ui/components/model-tester.tsx index dee500a7..3ecd43b3 100644 --- a/ui/components/model-tester.tsx +++ b/ui/components/model-tester.tsx @@ -197,7 +197,11 @@ export function ModelTester({ models }: ModelTesterProps) { testModelMutation.mutate(request); }; - const enabledModels = models.filter((model) => model.isEnabled); + const enabledModels = Array.from( + new Map( + models.filter((model) => model.isEnabled).map((m) => [m.id, m]) + ).values() + ); const credentials = selectedModel ? getModelCredentials(selectedModel) : null; return ( From 3681fc8aabef8789ea40c577f10241f3b953fc76 Mon Sep 17 00:00:00 2001 From: 9qeklajc Date: Sun, 19 Apr 2026 15:22:47 +0200 Subject: [PATCH 2/2] no advanced testing for now --- ui/components/models-page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/components/models-page.tsx b/ui/components/models-page.tsx index f8505d7f..bff3a379 100644 --- a/ui/components/models-page.tsx +++ b/ui/components/models-page.tsx @@ -124,12 +124,14 @@ export function ModelsPage() { > Basic Testing + {/* API Endpoints + */}