diff --git a/routstr/core/admin.py b/routstr/core/admin.py index fa2381b1..32526956 100644 --- a/routstr/core/admin.py +++ b/routstr/core/admin.py @@ -1513,437 +1513,6 @@ class ModelUpdate(BaseModel): enabled: bool = True -class ModelIdentifier(BaseModel): - model_id: str - provider_id: int | None = None - - -class ModelGetRequest(BaseModel): - model_id: str - provider_id: int | None = None - - -class ModelDeleteRequest(BaseModel): - model_id: str - provider_id: int | None = None - - -class ModelUpdateRequest(BaseModel): - model_id: str - provider_id: int | None = None - name: str - description: str - created: int - context_length: int - architecture: dict[str, object] - pricing: dict[str, object] - per_request_limits: dict[str, object] | None = None - top_provider: dict[str, object] | None = None - enabled: bool = True - - -class ModelCreateRequest(BaseModel): - model_id: str - provider_id: int | None = None - name: str - description: str - created: int - context_length: int - architecture: dict[str, object] - pricing: dict[str, object] - per_request_limits: dict[str, object] | None = None - top_provider: dict[str, object] | None = None - enabled: bool = True - - -@admin_router.get("/api/models", dependencies=[Depends(require_admin_api)]) -async def get_all_models() -> list[dict[str, object]]: - async with create_session() as session: - models = await list_models(session=session, include_disabled=True) - return [m.dict() for m in models] - - -@admin_router.get( - "/api/models/{model_id:path}", dependencies=[Depends(require_admin_api)] -) -async def get_model(model_id: str) -> dict[str, object]: - async with create_session() as session: - result = await session.exec( - select(ModelRow).where( - ModelRow.id == model_id, ModelRow.upstream_provider_id.is_(None) - ) - ) - row = result.first() - if not row: - raise HTTPException(status_code=404, detail="Model not found") - return _row_to_model(row, apply_provider_fee=False).dict() - - -@admin_router.post("/api/models", dependencies=[Depends(require_admin_api)]) -async def create_model(payload: ModelCreate) -> dict[str, object]: - async with create_session() as session: - exists = await session.get(ModelRow, (payload.id, None)) - if exists: - raise HTTPException( - status_code=409, detail="Model with this ID already exists" - ) - - row = ModelRow( - id=payload.id, - name=payload.name, - description=payload.description, - created=int(payload.created), - context_length=int(payload.context_length), - architecture=json.dumps(payload.architecture), - pricing=json.dumps(payload.pricing), - sats_pricing=None, - per_request_limits=( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ), - top_provider=( - json.dumps(payload.top_provider) if payload.top_provider else None - ), - upstream_provider_id=None, - enabled=payload.enabled, - ) - session.add(row) - await session.commit() - await session.refresh(row) - - await refresh_model_maps() - return _row_to_model(row, apply_provider_fee=False).dict() - - -@admin_router.patch( - "/api/models/{model_id:path}", dependencies=[Depends(require_admin_api)] -) -async def update_model(model_id: str, payload: ModelUpdate) -> dict[str, object]: - if payload.id != model_id: - raise HTTPException(status_code=400, detail="Path id does not match payload id") - - async with create_session() as session: - row = await session.get(ModelRow, (model_id, None)) - if not row: - raise HTTPException(status_code=404, detail="Model not found") - - row.name = payload.name - row.description = payload.description - row.created = int(payload.created) - row.context_length = int(payload.context_length) - row.architecture = json.dumps(payload.architecture) - row.pricing = json.dumps(payload.pricing) - row.sats_pricing = None - row.per_request_limits = ( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ) - row.top_provider = ( - json.dumps(payload.top_provider) if payload.top_provider else None - ) - row.enabled = payload.enabled - - session.add(row) - await session.commit() - await session.refresh(row) - - await refresh_model_maps() - return _row_to_model(row, apply_provider_fee=False).dict() - - -@admin_router.put( - "/api/models/{model_id:path}", dependencies=[Depends(require_admin_api)] -) -async def update_model_put(model_id: str, payload: ModelUpdate) -> dict[str, object]: - return await update_model(model_id, payload) - - -@admin_router.delete( - "/api/models/{model_id:path}", dependencies=[Depends(require_admin_api)] -) -async def delete_model(model_id: str) -> dict[str, object]: - async with create_session() as session: - row = await session.get(ModelRow, (model_id, None)) - if not row: - raise HTTPException(status_code=404, detail="Model not found") - await session.delete(row) - await session.commit() - await refresh_model_maps() - return {"ok": True, "deleted_id": model_id} - - -@admin_router.delete("/api/models", dependencies=[Depends(require_admin_api)]) -async def delete_all_models() -> dict[str, object]: - async with create_session() as session: - result = await session.exec( - select(ModelRow).where(ModelRow.upstream_provider_id.is_(None)) - ) - rows = result.all() - for row in rows: - await session.delete(row) - await session.commit() - await refresh_model_maps() - return {"ok": True, "deleted": len(rows)} - - -class BatchModelsRequest(BaseModel): - models: list[ModelCreate] - - -@admin_router.post("/api/models/batch", dependencies=[Depends(require_admin_api)]) -async def batch_add_models(payload: BatchModelsRequest) -> dict[str, object]: - async with create_session() as session: - created_models = [] - for model_data in payload.models: - exists = await session.get(ModelRow, (model_data.id, None)) - if exists: - continue - - row = ModelRow( - id=model_data.id, - name=model_data.name, - description=model_data.description, - created=int(model_data.created), - context_length=int(model_data.context_length), - architecture=json.dumps(model_data.architecture), - pricing=json.dumps(model_data.pricing), - sats_pricing=None, - per_request_limits=( - json.dumps(model_data.per_request_limits) - if model_data.per_request_limits is not None - else None - ), - top_provider=( - json.dumps(model_data.top_provider) - if model_data.top_provider - else None - ), - upstream_provider_id=None, - enabled=model_data.enabled, - ) - session.add(row) - created_models.append(model_data.id) - - await session.commit() - - await refresh_model_maps() - return {"ok": True, "created": len(created_models), "model_ids": created_models} - - -@admin_router.post("/api/models/get", dependencies=[Depends(require_admin_api)]) -async def get_model_by_id(payload: ModelGetRequest) -> dict[str, object]: - print(payload) - from ..upstream import _instantiate_provider - - async with create_session() as session: - if payload.provider_id is None: - result = await session.exec( - select(ModelRow).where( - ModelRow.id == payload.model_id, - ModelRow.upstream_provider_id.is_(None), - ) - ) - row = result.first() - if not row: - raise HTTPException(status_code=404, detail="Model not found") - return _row_to_model(row, apply_provider_fee=False).dict() - else: - provider = await session.get(UpstreamProviderRow, payload.provider_id) - if not provider: - raise HTTPException(status_code=404, detail="Provider not found") - - row = await session.get(ModelRow, (payload.model_id, payload.provider_id)) - if row: - return _row_to_model( - row, apply_provider_fee=True, provider_fee=provider.provider_fee - ).dict() - - upstream_instance = _instantiate_provider(provider) - if upstream_instance: - try: - remote_models = await upstream_instance.fetch_models() - for model in remote_models: - if model.id == payload.model_id: - model_with_fee = upstream_instance._apply_provider_fee_to_model(model) - return model_with_fee.dict() - except Exception as e: - logger.error( - f"Failed to fetch models from {provider.provider_type}: {e}" - ) - - raise HTTPException( - status_code=404, detail="Model not found for this provider" - ) - - -@admin_router.post("/api/models/create", dependencies=[Depends(require_admin_api)]) -async def create_model_v2(payload: ModelCreateRequest) -> dict[str, object]: - async with create_session() as session: - if payload.provider_id is None: - exists = await session.get(ModelRow, (payload.model_id, None)) - if exists: - raise HTTPException( - status_code=409, detail="Model with this ID already exists" - ) - - row = ModelRow( - id=payload.model_id, - name=payload.name, - description=payload.description, - created=int(payload.created), - context_length=int(payload.context_length), - architecture=json.dumps(payload.architecture), - pricing=json.dumps(payload.pricing), - sats_pricing=None, - per_request_limits=( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ), - top_provider=( - json.dumps(payload.top_provider) if payload.top_provider else None - ), - upstream_provider_id=None, - enabled=payload.enabled, - ) - session.add(row) - await session.commit() - await session.refresh(row) - await refresh_model_maps() - return _row_to_model(row, apply_provider_fee=False).dict() - else: - provider = await session.get(UpstreamProviderRow, payload.provider_id) - if not provider: - raise HTTPException(status_code=404, detail="Provider not found") - - exists = await session.get(ModelRow, (payload.model_id, payload.provider_id)) - if exists: - raise HTTPException( - status_code=409, - detail="Model with this ID already exists for this provider", - ) - - row = ModelRow( - id=payload.model_id, - name=payload.name, - description=payload.description, - created=int(payload.created), - context_length=int(payload.context_length), - architecture=json.dumps(payload.architecture), - pricing=json.dumps(payload.pricing), - sats_pricing=None, - per_request_limits=( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ), - top_provider=( - json.dumps(payload.top_provider) if payload.top_provider else None - ), - upstream_provider_id=payload.provider_id, - enabled=payload.enabled, - ) - session.add(row) - await session.commit() - await session.refresh(row) - await refresh_model_maps() - return _row_to_model( - row, apply_provider_fee=True, provider_fee=provider.provider_fee - ).dict() - - -@admin_router.post("/api/models/update", dependencies=[Depends(require_admin_api)]) -async def update_model_v2(payload: ModelUpdateRequest) -> dict[str, object]: - async with create_session() as session: - if payload.provider_id is None: - row = await session.get(ModelRow, (payload.model_id, None)) - if not row: - raise HTTPException(status_code=404, detail="Model not found") - - row.name = payload.name - row.description = payload.description - row.created = int(payload.created) - row.context_length = int(payload.context_length) - row.architecture = json.dumps(payload.architecture) - row.pricing = json.dumps(payload.pricing) - row.sats_pricing = None - row.per_request_limits = ( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ) - row.top_provider = ( - json.dumps(payload.top_provider) if payload.top_provider else None - ) - row.enabled = payload.enabled - - session.add(row) - await session.commit() - await session.refresh(row) - await refresh_model_maps() - return _row_to_model(row, apply_provider_fee=False).dict() - else: - provider = await session.get(UpstreamProviderRow, payload.provider_id) - if not provider: - raise HTTPException(status_code=404, detail="Provider not found") - - row = await session.get(ModelRow, (payload.model_id, payload.provider_id)) - if not row: - raise HTTPException( - status_code=404, detail="Model not found for this provider" - ) - - row.name = payload.name - row.description = payload.description - row.created = int(payload.created) - row.context_length = int(payload.context_length) - row.architecture = json.dumps(payload.architecture) - row.pricing = json.dumps(payload.pricing) - row.sats_pricing = None - row.per_request_limits = ( - json.dumps(payload.per_request_limits) - if payload.per_request_limits is not None - else None - ) - row.top_provider = ( - json.dumps(payload.top_provider) if payload.top_provider else None - ) - row.enabled = payload.enabled - - session.add(row) - await session.commit() - await session.refresh(row) - await refresh_model_maps() - return _row_to_model( - row, apply_provider_fee=True, provider_fee=provider.provider_fee - ).dict() - - -@admin_router.post("/api/models/delete", dependencies=[Depends(require_admin_api)]) -async def delete_model_v2(payload: ModelDeleteRequest) -> dict[str, object]: - async with create_session() as session: - if payload.provider_id is None: - row = await session.get(ModelRow, (payload.model_id, None)) - if not row: - raise HTTPException(status_code=404, detail="Model not found") - await session.delete(row) - await session.commit() - await refresh_model_maps() - return {"ok": True, "deleted_id": payload.model_id} - else: - row = await session.get(ModelRow, (payload.model_id, payload.provider_id)) - if not row: - raise HTTPException( - status_code=404, detail="Model not found for this provider" - ) - await session.delete(row) - await session.commit() - await refresh_model_maps() - return {"ok": True, "deleted_id": payload.model_id} - - @admin_router.get("/models", response_class=HTMLResponse) async def admin_models(request: Request) -> str: if is_admin_authenticated(request):