RobotHub-Frontend / src /lib /elements /compute /RemoteComputeManager.svelte.ts
blanchon's picture
Update
3cdf7b9
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();