# Swapper Service - Cabinet Integration Guide **Version:** 1.0.0 **Last Updated:** 2025-11-22 **Status:** ✅ Ready for Integration --- ## Overview This document describes how to integrate Swapper Service status and metrics into Node #1 and Node #2 admin consoles (cabinet interfaces). --- ## API Endpoints for Cabinets ### 1. GET /api/cabinet/swapper/status Get complete Swapper Service status for cabinet display. **Response:** ```json { "service": "swapper-service", "status": "healthy", "mode": "single-active", "active_model": { "name": "deepseek-r1-70b", "uptime_hours": 1.5, "request_count": 42, "loaded_at": "2025-11-22T10:30:00" }, "total_models": 8, "available_models": ["deepseek-r1-70b", "qwen2.5-coder-32b", ...], "loaded_models": ["deepseek-r1-70b"], "models": [ { "name": "deepseek-r1-70b", "ollama_name": "deepseek-r1:70b", "type": "llm", "size_gb": 42, "priority": "high", "status": "loaded", "is_active": true, "uptime_hours": 1.5, "request_count": 42, "total_uptime_seconds": 5400.0 } ], "timestamp": "2025-11-22T12:00:00" } ``` ### 2. GET /api/cabinet/swapper/models Get detailed information about all models. **Response:** ```json { "models": [ { "name": "deepseek-r1-70b", "ollama_name": "deepseek-r1:70b", "type": "llm", "size_gb": 42, "priority": "high", "status": "loaded", "is_active": true, "can_load": false, "can_unload": true, "uptime_hours": 1.5, "request_count": 42, "total_uptime_seconds": 5400.0, "loaded_at": "2025-11-22T10:30:00" } ], "total": 8, "active_count": 1, "timestamp": "2025-11-22T12:00:00" } ``` ### 3. GET /api/cabinet/swapper/metrics/summary Get summary metrics for dashboard. **Response:** ```json { "summary": { "total_models": 8, "active_models": 1, "available_models": 8, "total_uptime_hours": 24.5, "total_requests": 150 }, "most_used_model": { "name": "deepseek-r1-70b", "uptime_hours": 12.3, "request_count": 85 }, "active_model": { "name": "deepseek-r1-70b", "uptime_hours": 1.5 }, "timestamp": "2025-11-22T12:00:00" } ``` --- ## Frontend Integration Examples ### React Component Example ```tsx import React, { useEffect, useState } from 'react'; interface SwapperStatus { service: string; status: string; mode: string; active_model: { name: string; uptime_hours: number; request_count: number; loaded_at: string; } | null; total_models: number; models: Array<{ name: string; type: string; size_gb: number; status: string; is_active: boolean; uptime_hours: number; }>; } export const SwapperStatusCard: React.FC = () => { const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchStatus = async () => { try { const response = await fetch('http://localhost:8890/api/cabinet/swapper/status'); const data = await response.json(); setStatus(data); } catch (error) { console.error('Error fetching Swapper status:', error); } finally { setLoading(false); } }; fetchStatus(); const interval = setInterval(fetchStatus, 30000); // Update every 30 seconds return () => clearInterval(interval); }, []); if (loading) return
Loading...
; if (!status) return
Error loading status
; return (

Swapper Service

Status: {status.status}

Mode: {status.mode}

Total Models: {status.total_models}

{status.active_model && (

Active Model

Name: {status.active_model.name}

Uptime: {status.active_model.uptime_hours.toFixed(2)} hours

Requests: {status.active_model.request_count}

)}

Available Models

{status.models.map((model) => ( ))}
Name Type Size (GB) Status Uptime (hours)
{model.name} {model.type} {model.size_gb} {model.status} {model.uptime_hours.toFixed(2)}
); }; ``` ### Vue Component Example ```vue ``` --- ## Dashboard Widgets ### Widget 1: Active Model Display ```tsx {activeModel ? (

{activeModel.name}

Uptime: {activeModel.uptime_hours.toFixed(2)} hours

Requests: {activeModel.request_count}

Loaded: {new Date(activeModel.loaded_at).toLocaleString()}

) : (

No model loaded

)}
``` ### Widget 2: Model List with Actions ```tsx {models.map(model => ( ))}
Name Status Uptime Actions
{model.name} {model.is_active ? 'Active' : model.status} {model.uptime_hours.toFixed(2)}h {model.can_load && ( )} {model.can_unload && ( )}
``` ### Widget 3: Metrics Summary ```tsx
{mostUsedModel && (

Most Used Model

{mostUsedModel.name}

{mostUsedModel.uptime_hours.toFixed(2)} hours

)}
``` --- ## Integration Steps ### Step 1: Add API Client Create a service to fetch Swapper data: ```typescript // services/swapperService.ts export const swapperService = { async getStatus() { const response = await fetch('http://localhost:8890/api/cabinet/swapper/status'); return response.json(); }, async getModels() { const response = await fetch('http://localhost:8890/api/cabinet/swapper/models'); return response.json(); }, async getMetricsSummary() { const response = await fetch('http://localhost:8890/api/cabinet/swapper/metrics/summary'); return response.json(); }, async loadModel(modelName: string) { const response = await fetch( `http://localhost:8890/models/${modelName}/load`, { method: 'POST' } ); return response.json(); }, async unloadModel(modelName: string) { const response = await fetch( `http://localhost:8890/models/${modelName}/unload`, { method: 'POST' } ); return response.json(); } }; ``` ### Step 2: Add to Admin Console Add Swapper section to admin console sidebar: ```typescript // Admin Console Sidebar const menuItems = [ { id: 'overview', label: 'Overview', icon: 'dashboard' }, { id: 'members', label: 'Members & Roles', icon: 'users' }, { id: 'agents', label: 'Agents', icon: 'robot' }, { id: 'swapper', label: 'Swapper Service', icon: 'swap' }, // Add this { id: 'settings', label: 'Settings', icon: 'settings' }, ]; ``` ### Step 3: Create Swapper Page ```tsx // pages/SwapperPage.tsx export const SwapperPage: React.FC = () => { return (
); }; ``` --- ## Node-Specific Configuration ### Node #1 (Production Server) ```typescript const SWAPPER_URL = 'http://swapper-service:8890'; // Internal Docker network ``` ### Node #2 (MacBook Development) ```typescript const SWAPPER_URL = 'http://localhost:8890'; // Local development ``` --- ## Error Handling ```typescript try { const status = await swapperService.getStatus(); // Handle success } catch (error) { if (error.status === 503) { // Service unavailable showError('Swapper Service is not available'); } else if (error.status === 404) { // Model not found showError('Model not found'); } else { // Generic error showError('Error loading Swapper status'); } } ``` --- ## Real-time Updates Use polling or WebSocket for real-time updates: ```typescript // Polling example useEffect(() => { const interval = setInterval(async () => { const status = await swapperService.getStatus(); setStatus(status); }, 30000); // Update every 30 seconds return () => clearInterval(interval); }, []); ``` --- ## Styling Recommendations ```css .swapper-status-card { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .active-model { background: #e8f5e9; padding: 15px; border-radius: 4px; margin: 15px 0; } .models-list table { width: 100%; border-collapse: collapse; } .models-list tr.active { background: #fff3e0; } .status-badge { padding: 4px 8px; border-radius: 4px; font-size: 12px; } .status-badge.loaded { background: #4caf50; color: white; } .status-badge.unloaded { background: #9e9e9e; color: white; } ``` --- ## Testing ```typescript // Test Swapper API integration describe('Swapper Service Integration', () => { it('should fetch status', async () => { const status = await swapperService.getStatus(); expect(status).toHaveProperty('service', 'swapper-service'); expect(status).toHaveProperty('status', 'healthy'); }); it('should load model', async () => { const result = await swapperService.loadModel('deepseek-r1-70b'); expect(result.status).toBe('success'); }); }); ``` --- **Last Updated:** 2025-11-22 **Maintained by:** Ivan Tytar & DAARION Team **Status:** ✅ Ready for Integration