GT AI OS Community v2.0.33 - Add NVIDIA NIM and Nemotron agents

- Updated python_coding_microproject.csv to use NVIDIA NIM Kimi K2
- Updated kali_linux_shell_simulator.csv to use NVIDIA NIM Kimi K2
  - Made more general-purpose (flexible targets, expanded tools)
- Added nemotron-mini-agent.csv for fast local inference via Ollama
- Added nemotron-agent.csv for advanced reasoning via Ollama
- Added wiki page: Projects for NVIDIA NIMs and Nemotron
This commit is contained in:
HackWeasel
2025-12-12 17:47:14 -05:00
commit 310491a557
750 changed files with 232701 additions and 0 deletions

View File

@@ -0,0 +1,362 @@
"""
Tenant Model Management API for GT 2.0 Admin Control Panel
Provides endpoints for managing which models are available to which tenants,
with tenant-specific permissions and rate limits.
"""
from typing import Dict, Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel, Field
import logging
from app.core.database import get_db
from app.services.model_management_service import get_model_management_service
from app.models.tenant_model_config import TenantModelConfig
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/tenants", tags=["Tenant Model Management"])
# Request/Response Models
class TenantModelAssignRequest(BaseModel):
model_id: str = Field(..., description="Model ID to assign")
rate_limits: Optional[Dict[str, Any]] = Field(None, description="Custom rate limits")
capabilities: Optional[Dict[str, Any]] = Field(None, description="Tenant-specific capabilities")
usage_constraints: Optional[Dict[str, Any]] = Field(None, description="Usage restrictions")
priority: int = Field(1, ge=1, le=10, description="Priority level (1-10)")
model_config = {"protected_namespaces": ()}
class TenantModelUpdateRequest(BaseModel):
is_enabled: Optional[bool] = Field(None, description="Enable/disable model for tenant")
rate_limits: Optional[Dict[str, Any]] = Field(None, description="Updated rate limits")
tenant_capabilities: Optional[Dict[str, Any]] = Field(None, description="Updated capabilities")
usage_constraints: Optional[Dict[str, Any]] = Field(None, description="Updated usage restrictions")
priority: Optional[int] = Field(None, ge=1, le=10, description="Updated priority level")
class ModelAccessCheckRequest(BaseModel):
user_capabilities: Optional[List[str]] = Field(None, description="User capabilities")
user_id: Optional[str] = Field(None, description="User identifier")
class TenantModelResponse(BaseModel):
id: int
tenant_id: int
model_id: str
is_enabled: bool
tenant_capabilities: Dict[str, Any]
rate_limits: Dict[str, Any]
usage_constraints: Dict[str, Any]
priority: int
created_at: str
updated_at: str
class ModelWithTenantConfigResponse(BaseModel):
model_id: str
name: str
provider: str
model_type: str
endpoint: str
tenant_config: TenantModelResponse
@router.post("/{tenant_id}/models", response_model=TenantModelResponse)
async def assign_model_to_tenant(
tenant_id: int,
request: TenantModelAssignRequest,
db: AsyncSession = Depends(get_db)
):
"""Assign a model to a tenant with specific configuration"""
try:
service = get_model_management_service(db)
tenant_model_config = await service.assign_model_to_tenant(
tenant_id=tenant_id,
model_id=request.model_id,
rate_limits=request.rate_limits,
capabilities=request.capabilities,
usage_constraints=request.usage_constraints,
priority=request.priority
)
return TenantModelResponse(**tenant_model_config.to_dict())
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Error assigning model to tenant: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.delete("/{tenant_id}/models/{model_id:path}")
async def remove_model_from_tenant(
tenant_id: int,
model_id: str,
db: AsyncSession = Depends(get_db)
):
"""Remove model access from a tenant"""
try:
service = get_model_management_service(db)
success = await service.remove_model_from_tenant(tenant_id, model_id)
if not success:
raise HTTPException(status_code=404, detail="Model assignment not found")
return {"message": f"Model {model_id} removed from tenant {tenant_id}"}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error removing model from tenant: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.patch("/{tenant_id}/models/{model_id:path}", response_model=TenantModelResponse)
async def update_tenant_model_config(
tenant_id: int,
model_id: str,
request: TenantModelUpdateRequest,
db: AsyncSession = Depends(get_db)
):
"""Update tenant-specific model configuration"""
try:
service = get_model_management_service(db)
# Convert request to dict, excluding None values
updates = {k: v for k, v in request.dict().items() if v is not None}
tenant_model_config = await service.update_tenant_model_config(
tenant_id=tenant_id,
model_id=model_id,
updates=updates
)
if not tenant_model_config:
raise HTTPException(status_code=404, detail="Tenant model configuration not found")
return TenantModelResponse(**tenant_model_config.to_dict())
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating tenant model config: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/{tenant_id}/models", response_model=List[ModelWithTenantConfigResponse])
async def get_tenant_models(
tenant_id: int,
enabled_only: bool = Query(False, description="Only return enabled models"),
db: AsyncSession = Depends(get_db)
):
"""Get all models available to a tenant"""
try:
service = get_model_management_service(db)
models = await service.get_tenant_models(
tenant_id=tenant_id,
enabled_only=enabled_only
)
# Format response
response_models = []
for model in models:
tenant_config = model.pop("tenant_config")
response_models.append({
**model,
"tenant_config": TenantModelResponse(**tenant_config)
})
return response_models
except Exception as e:
logger.error(f"Error getting tenant models: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.post("/{tenant_id}/models/{model_id}/check-access")
async def check_tenant_model_access(
tenant_id: int,
model_id: str,
request: ModelAccessCheckRequest,
db: AsyncSession = Depends(get_db)
):
"""Check if a tenant/user can access a specific model"""
try:
service = get_model_management_service(db)
access_info = await service.check_tenant_model_access(
tenant_id=tenant_id,
model_id=model_id,
user_capabilities=request.user_capabilities,
user_id=request.user_id
)
return access_info
except Exception as e:
logger.error(f"Error checking tenant model access: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/{tenant_id}/models/stats")
async def get_tenant_model_stats(
tenant_id: int,
db: AsyncSession = Depends(get_db)
):
"""Get statistics about models for a tenant"""
try:
service = get_model_management_service(db)
stats = await service.get_tenant_model_stats(tenant_id)
return stats
except Exception as e:
logger.error(f"Error getting tenant model stats: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Additional endpoints for model-centric views
@router.get("/models/{model_id:path}/tenants")
async def get_model_tenants(
model_id: str,
db: AsyncSession = Depends(get_db)
):
"""Get all tenants that have access to a model"""
try:
service = get_model_management_service(db)
tenants = await service.get_model_tenants(model_id)
return {
"model_id": model_id,
"tenants": tenants,
"total_tenants": len(tenants)
}
except Exception as e:
logger.error(f"Error getting model tenants: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Global tenant model configuration endpoints
@router.get("/all")
async def get_all_tenant_model_configs(
db: AsyncSession = Depends(get_db)
):
"""Get all tenant model configurations with joined tenant and model data"""
try:
service = get_model_management_service(db)
# This would need to be implemented in the service
configs = await service.get_all_tenant_model_configs()
return configs
except Exception as e:
logger.error(f"Error getting all tenant model configs: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Bulk operations
@router.post("/{tenant_id}/models/bulk-assign")
async def bulk_assign_models_to_tenant(
tenant_id: int,
model_ids: List[str],
default_config: Optional[TenantModelAssignRequest] = None,
db: AsyncSession = Depends(get_db)
):
"""Assign multiple models to a tenant with the same configuration"""
try:
service = get_model_management_service(db)
results = []
errors = []
for model_id in model_ids:
try:
config = default_config if default_config else TenantModelAssignRequest(model_id=model_id)
tenant_model_config = await service.assign_model_to_tenant(
tenant_id=tenant_id,
model_id=model_id,
rate_limits=config.rate_limits,
capabilities=config.capabilities,
usage_constraints=config.usage_constraints,
priority=config.priority
)
results.append({
"model_id": model_id,
"status": "success",
"config": tenant_model_config.to_dict()
})
except Exception as e:
errors.append({
"model_id": model_id,
"status": "error",
"error": str(e)
})
return {
"tenant_id": tenant_id,
"total_requested": len(model_ids),
"successful": len(results),
"failed": len(errors),
"results": results,
"errors": errors
}
except Exception as e:
logger.error(f"Error bulk assigning models: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.delete("/{tenant_id}/models/bulk-remove")
async def bulk_remove_models_from_tenant(
tenant_id: int,
model_ids: List[str],
db: AsyncSession = Depends(get_db)
):
"""Remove multiple models from a tenant"""
try:
service = get_model_management_service(db)
results = []
for model_id in model_ids:
try:
success = await service.remove_model_from_tenant(tenant_id, model_id)
results.append({
"model_id": model_id,
"status": "success" if success else "not_found",
"removed": success
})
except Exception as e:
results.append({
"model_id": model_id,
"status": "error",
"error": str(e)
})
successful = sum(1 for r in results if r["status"] == "success")
return {
"tenant_id": tenant_id,
"total_requested": len(model_ids),
"successful": successful,
"results": results
}
except Exception as e:
logger.error(f"Error bulk removing models: {e}")
raise HTTPException(status_code=500, detail=str(e))