Spaces:
Running
Running
import { RemoteCompute } from './RemoteCompute.svelte'; | |
import type { Position3D } from '$lib/types/positionable.js'; | |
import { generateName } from '$lib/utils/generateName.js'; | |
import { positionManager } from '$lib/utils/positionManager.js'; | |
import { | |
rootGet, | |
healthCheckHealthGet, | |
listSessionsSessionsGet, | |
createSessionSessionsPost, | |
startInferenceSessionsSessionIdStartPost, | |
stopInferenceSessionsSessionIdStopPost, | |
deleteSessionSessionsSessionIdDelete | |
} from '@robothub/inference-server-client'; | |
import { settings } from '$lib/runes/settings.svelte'; | |
import type { | |
CreateSessionRequest, | |
CreateSessionResponse | |
} from '@robothub/inference-server-client'; | |
export interface AISessionConfig { | |
sessionId: string; | |
policyPath: string; | |
cameraNames: string[]; | |
transportServerUrl: string; | |
workspaceId?: string; | |
} | |
export interface AISessionResponse { | |
workspace_id: string; | |
camera_room_ids: Record<string, string>; | |
joint_input_room_id: string; | |
joint_output_room_id: string; | |
} | |
export interface AISessionStatus { | |
session_id: string; | |
status: 'initializing' | 'ready' | 'running' | 'stopped'; | |
policy_path: string; | |
camera_names: string[]; | |
workspace_id: string; | |
rooms: { | |
workspace_id: string; | |
camera_room_ids: Record<string, string>; | |
joint_input_room_id: string; | |
joint_output_room_id: string; | |
}; | |
stats: { | |
inference_count: number; | |
commands_sent: number; | |
joints_received: number; | |
images_received: Record<string, number>; | |
errors: number; | |
actions_in_queue: number; | |
}; | |
inference_stats?: { | |
inference_count: number; | |
total_inference_time: number; | |
average_inference_time: number; | |
average_fps: number; | |
is_loaded: boolean; | |
device: string; | |
}; | |
error_message?: string; | |
} | |
export class RemoteComputeManager { | |
private _computes = $state<RemoteCompute[]>([]); | |
constructor() { | |
// No client initialization needed anymore | |
} | |
// Reactive getters | |
get computes(): RemoteCompute[] { | |
return this._computes; | |
} | |
get computeCount(): number { | |
return this._computes.length; | |
} | |
get runningComputes(): RemoteCompute[] { | |
return this._computes.filter(compute => compute.status === 'running'); | |
} | |
/** | |
* Create a new AI compute instance | |
*/ | |
createCompute(id?: string, name?: string, position?: Position3D): RemoteCompute { | |
const computeId = id || generateName(); | |
// Check if compute already exists | |
if (this._computes.find(c => c.id === computeId)) { | |
throw new Error(`Compute with ID ${computeId} already exists`); | |
} | |
// Create compute instance | |
const compute = new RemoteCompute(computeId, name); | |
// Set position (from position manager if not provided) | |
compute.position = position || positionManager.getNextPosition(); | |
// Add to reactive array | |
this._computes.push(compute); | |
console.log(`Created compute ${computeId} at position (${compute.position.x.toFixed(1)}, ${compute.position.y.toFixed(1)}, ${compute.position.z.toFixed(1)}). Total computes: ${this._computes.length}`); | |
return compute; | |
} | |
/** | |
* Get compute by ID | |
*/ | |
getCompute(id: string): RemoteCompute | undefined { | |
return this._computes.find(c => c.id === id); | |
} | |
/** | |
* Remove a compute instance | |
*/ | |
async removeCompute(id: string): Promise<void> { | |
const computeIndex = this._computes.findIndex(c => c.id === id); | |
if (computeIndex === -1) return; | |
const compute = this._computes[computeIndex]; | |
// Clean up compute resources | |
await this.stopSession(id); | |
await this.deleteSession(id); | |
// Remove from reactive array | |
this._computes.splice(computeIndex, 1); | |
console.log(`Removed compute ${id}. Remaining computes: ${this._computes.length}`); | |
} | |
/** | |
* Create an Inference Session | |
*/ | |
async createSession(computeId: string, config: AISessionConfig): Promise<{ success: boolean; error?: string; data?: AISessionResponse }> { | |
const compute = this.getCompute(computeId); | |
if (!compute) { | |
return { success: false, error: `Compute ${computeId} not found` }; | |
} | |
try { | |
const request: CreateSessionRequest = { | |
session_id: config.sessionId, | |
policy_path: config.policyPath, | |
camera_names: config.cameraNames, | |
transport_server_url: config.transportServerUrl, | |
workspace_id: config.workspaceId || undefined, | |
}; | |
const response = await createSessionSessionsPost({ | |
body: request, | |
baseUrl: settings.inferenceServerUrl | |
}); | |
if (!response.data) { | |
throw new Error('Failed to create session - no data returned'); | |
} | |
const data: CreateSessionResponse = response.data; | |
// Update compute with session info | |
compute.sessionId = config.sessionId; | |
compute.status = 'ready'; | |
compute.sessionConfig = config; | |
compute.sessionData = { | |
workspace_id: data.workspace_id, | |
camera_room_ids: data.camera_room_ids, | |
joint_input_room_id: data.joint_input_room_id, | |
joint_output_room_id: data.joint_output_room_id | |
}; | |
return { success: true, data: compute.sessionData }; | |
} catch (error) { | |
console.error(`Failed to create session for compute ${computeId}:`, error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Start inference for a session | |
*/ | |
async startSession(computeId: string): Promise<{ success: boolean; error?: string }> { | |
const compute = this.getCompute(computeId); | |
if (!compute || !compute.sessionId) { | |
return { success: false, error: 'No session to start' }; | |
} | |
try { | |
await startInferenceSessionsSessionIdStartPost({ | |
path: { session_id: compute.sessionId }, | |
baseUrl: settings.inferenceServerUrl | |
}); | |
compute.status = 'running'; | |
return { success: true }; | |
} catch (error) { | |
console.error(`Failed to start session for compute ${computeId}:`, error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Stop inference for a session | |
*/ | |
async stopSession(computeId: string): Promise<{ success: boolean; error?: string }> { | |
const compute = this.getCompute(computeId); | |
if (!compute || !compute.sessionId) { | |
return { success: false, error: 'No session to stop' }; | |
} | |
try { | |
await stopInferenceSessionsSessionIdStopPost({ | |
path: { session_id: compute.sessionId }, | |
baseUrl: settings.inferenceServerUrl | |
}); | |
compute.status = 'stopped'; | |
return { success: true }; | |
} catch (error) { | |
console.error(`Failed to stop session for compute ${computeId}:`, error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Delete a session | |
*/ | |
async deleteSession(computeId: string): Promise<{ success: boolean; error?: string }> { | |
const compute = this.getCompute(computeId); | |
if (!compute || !compute.sessionId) { | |
return { success: true }; // Already deleted | |
} | |
try { | |
await deleteSessionSessionsSessionIdDelete({ | |
path: { session_id: compute.sessionId }, | |
baseUrl: settings.inferenceServerUrl | |
}); | |
// Reset compute session info | |
compute.sessionId = null; | |
compute.status = 'disconnected'; | |
compute.sessionConfig = null; | |
compute.sessionData = null; | |
return { success: true }; | |
} catch (error) { | |
console.error(`Failed to delete session for compute ${computeId}:`, error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Get session status | |
*/ | |
async getSessionStatus(computeId: string): Promise<{ success: boolean; data?: AISessionStatus; error?: string }> { | |
const compute = this.getCompute(computeId); | |
if (!compute || !compute.sessionId) { | |
return { success: false, error: 'No session found' }; | |
} | |
try { | |
// Get all sessions and find the one we want | |
const response = await listSessionsSessionsGet({ | |
baseUrl: settings.inferenceServerUrl | |
}); | |
if (!response.data) { | |
throw new Error('Failed to get sessions list'); | |
} | |
const session = response.data.find(s => s.session_id === compute.sessionId); | |
if (!session) { | |
throw new Error(`Session ${compute.sessionId} not found`); | |
} | |
// Update compute status | |
compute.status = session.status as 'initializing' | 'ready' | 'running' | 'stopped'; | |
// Convert to AISessionStatus format | |
const sessionStatus: AISessionStatus = { | |
session_id: session.session_id, | |
status: session.status as 'initializing' | 'ready' | 'running' | 'stopped', | |
policy_path: session.policy_path, | |
camera_names: session.camera_names, | |
workspace_id: session.workspace_id, | |
rooms: session.rooms as any, | |
stats: session.stats as any, | |
inference_stats: session.inference_stats as any, | |
error_message: session.error_message || undefined | |
}; | |
return { success: true, data: sessionStatus }; | |
} catch (error) { | |
console.error(`Failed to get session status for compute ${computeId}:`, error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Check AI server health | |
*/ | |
async checkServerHealth(): Promise<{ success: boolean; data?: any; error?: string }> { | |
try { | |
const healthResponse = await rootGet({ | |
baseUrl: settings.inferenceServerUrl | |
}); | |
if (!healthResponse.data) { | |
return { success: false, error: 'Server is not healthy' }; | |
} | |
// Get detailed health info | |
const detailedHealthResponse = await healthCheckHealthGet({ | |
baseUrl: settings.inferenceServerUrl | |
}); | |
return { success: true, data: detailedHealthResponse.data }; | |
} catch (error) { | |
console.error('Failed to check AI server health:', error); | |
return { | |
success: false, | |
error: error instanceof Error ? error.message : String(error) | |
}; | |
} | |
} | |
/** | |
* Clean up all computes | |
*/ | |
async destroy(): Promise<void> { | |
const cleanupPromises = this._computes.map(async (compute) => { | |
await this.stopSession(compute.id); | |
await this.deleteSession(compute.id); | |
}); | |
await Promise.allSettled(cleanupPromises); | |
this._computes.length = 0; | |
} | |
} | |
// Global compute manager instance | |
export const remoteComputeManager = new RemoteComputeManager(); |