sync active training with UI if running
This commit is contained in:
@@ -2054,6 +2054,52 @@ class RealTrainingAdapter:
|
||||
'duration_seconds': session.duration_seconds,
|
||||
'error': session.error
|
||||
}
|
||||
|
||||
def get_active_training_session(self) -> Optional[Dict]:
|
||||
"""
|
||||
Get currently active training session (if any)
|
||||
|
||||
This allows the UI to resume tracking training progress after page reload
|
||||
|
||||
Returns:
|
||||
Dict with training info if active session exists, None otherwise
|
||||
"""
|
||||
# Find any session with 'running' status
|
||||
for training_id, session in self.training_sessions.items():
|
||||
if session.status == 'running':
|
||||
return {
|
||||
'training_id': training_id,
|
||||
'status': session.status,
|
||||
'model_name': session.model_name,
|
||||
'test_cases_count': session.test_cases_count,
|
||||
'current_epoch': session.current_epoch,
|
||||
'total_epochs': session.total_epochs,
|
||||
'current_loss': session.current_loss,
|
||||
'start_time': session.start_time
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def get_all_training_sessions(self) -> List[Dict]:
|
||||
"""
|
||||
Get all training sessions (for debugging/monitoring)
|
||||
|
||||
Returns:
|
||||
List of all training session summaries
|
||||
"""
|
||||
sessions = []
|
||||
for training_id, session in self.training_sessions.items():
|
||||
sessions.append({
|
||||
'training_id': training_id,
|
||||
'status': session.status,
|
||||
'model_name': session.model_name,
|
||||
'current_epoch': session.current_epoch,
|
||||
'total_epochs': session.total_epochs,
|
||||
'start_time': session.start_time,
|
||||
'duration_seconds': session.duration_seconds
|
||||
})
|
||||
|
||||
return sessions
|
||||
|
||||
|
||||
# Real-time inference support
|
||||
|
||||
@@ -1241,6 +1241,48 @@ class AnnotationDashboard:
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/active-training', methods=['GET'])
|
||||
def get_active_training():
|
||||
"""
|
||||
Get currently active training session (if any)
|
||||
Allows UI to resume tracking after page reload or across multiple clients
|
||||
"""
|
||||
try:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'active': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
active_session = self.training_adapter.get_active_training_session()
|
||||
|
||||
if active_session:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'active': True,
|
||||
'session': active_session
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'active': False
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting active training: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'active': False,
|
||||
'error': {
|
||||
'code': 'ACTIVE_TRAINING_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
# Live Training API Endpoints
|
||||
@self.server.route('/api/live-training/start', methods=['POST'])
|
||||
def start_live_training():
|
||||
|
||||
@@ -92,6 +92,16 @@
|
||||
// Load initial data (may call renderAnnotationsList which needs deleteAnnotation)
|
||||
loadInitialData();
|
||||
|
||||
// Load available models for training panel
|
||||
if (typeof loadAvailableModels === 'function') {
|
||||
loadAvailableModels();
|
||||
}
|
||||
|
||||
// Check for active training session (resume tracking after page reload)
|
||||
if (typeof checkActiveTraining === 'function') {
|
||||
checkActiveTraining();
|
||||
}
|
||||
|
||||
// Setup keyboard shortcuts
|
||||
setupKeyboardShortcuts();
|
||||
});
|
||||
|
||||
@@ -109,6 +109,30 @@
|
||||
// Track model states
|
||||
let modelStates = [];
|
||||
let selectedModel = null;
|
||||
let activeTrainingId = null; // Track active training session
|
||||
|
||||
function checkActiveTraining() {
|
||||
/**
|
||||
* Check if there's an active training session on page load
|
||||
* This allows resuming progress tracking after page reload
|
||||
*/
|
||||
fetch('/api/active-training')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.active && data.session) {
|
||||
console.log('Active training session found:', data.session);
|
||||
// Resume tracking
|
||||
activeTrainingId = data.session.training_id;
|
||||
showTrainingStatus();
|
||||
pollTrainingProgress(activeTrainingId);
|
||||
} else {
|
||||
console.log('No active training session');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error checking active training:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function loadAvailableModels() {
|
||||
fetch('/api/available-models')
|
||||
@@ -290,11 +314,16 @@
|
||||
startTraining(modelName, annotationIds);
|
||||
});
|
||||
|
||||
function startTraining(modelName, annotationIds) {
|
||||
// Show training status
|
||||
function showTrainingStatus() {
|
||||
// Show training status UI
|
||||
document.getElementById('training-status').style.display = 'block';
|
||||
document.getElementById('training-results').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = true;
|
||||
}
|
||||
|
||||
function startTraining(modelName, annotationIds) {
|
||||
// Show training status
|
||||
showTrainingStatus();
|
||||
|
||||
// Reset progress
|
||||
document.getElementById('training-progress-bar').style.width = '0%';
|
||||
@@ -313,18 +342,22 @@
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Store active training ID for persistence across reloads
|
||||
activeTrainingId = data.training_id;
|
||||
// Start polling for training progress
|
||||
pollTrainingProgress(data.training_id);
|
||||
} else {
|
||||
showError('Failed to start training: ' + data.error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
activeTrainingId = null;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
activeTrainingId = null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -350,9 +383,11 @@
|
||||
// Check if complete
|
||||
if (progress.status === 'completed') {
|
||||
clearInterval(pollInterval);
|
||||
activeTrainingId = null; // Clear active training
|
||||
showTrainingResults(progress);
|
||||
} else if (progress.status === 'failed') {
|
||||
clearInterval(pollInterval);
|
||||
activeTrainingId = null; // Clear active training
|
||||
showError('Training failed: ' + progress.error);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
@@ -361,6 +396,7 @@
|
||||
})
|
||||
.catch(error => {
|
||||
clearInterval(pollInterval);
|
||||
// Don't clear activeTrainingId on network error - training might still be running
|
||||
showError('Failed to get training progress: ' + error.message);
|
||||
document.getElementById('training-status').style.display = 'none';
|
||||
document.getElementById('train-model-btn').disabled = false;
|
||||
|
||||
Reference in New Issue
Block a user