Files
mlflow-dashboard/static/app.js
2026-02-19 17:21:51 +09:00

119 lines
4.3 KiB
JavaScript

const API_BASE = window.location.origin;
function getTrackingUri() {
return document.getElementById('trackingUri').value.trim() || null;
}
function formatTime(ts) {
if (!ts) return '';
const d = new Date(ts);
return d.toLocaleDateString('ko-KR') + ' ' + d.toLocaleTimeString('ko-KR', {hour: '2-digit', minute: '2-digit'});
}
async function loadExperiments() {
const container = document.getElementById('content');
container.innerHTML = '<div class="loading">Loading...</div>';
const uri = getTrackingUri();
const params = uri ? '?tracking_uri=' + encodeURIComponent(uri) : '';
try {
const res = await fetch(API_BASE + '/api/experiments' + params);
if (!res.ok) throw new Error('Failed: ' + res.status);
const experiments = await res.json();
if (experiments.length === 0) {
container.innerHTML = '<div class="loading">No experiments found.</div>';
return;
}
container.innerHTML = '';
experiments.forEach(function(exp) {
const card = document.createElement('div');
card.className = 'exp-card';
card.innerHTML =
'<div class="exp-header" onclick="toggleExp(this, \'' + exp.experiment_id + '\')">' +
'<span class="exp-arrow">&#9654;</span>' +
'<span class="exp-name">' + exp.name + '</span>' +
'<span class="exp-badge">' + exp.run_count + ' runs</span>' +
'</div>' +
'<div class="run-list" id="runs-' + exp.experiment_id + '">' +
'<div class="loading">Loading runs...</div>' +
'</div>';
container.appendChild(card);
});
} catch (e) {
container.innerHTML = '<div class="error">Connection failed: ' + e.message + '</div>';
}
}
async function toggleExp(header, expId) {
const arrow = header.querySelector('.exp-arrow');
const runList = document.getElementById('runs-' + expId);
if (runList.classList.contains('open')) {
runList.classList.remove('open');
arrow.classList.remove('open');
return;
}
arrow.classList.add('open');
runList.classList.add('open');
runList.innerHTML = '<div class="loading">Loading runs...</div>';
const uri = getTrackingUri();
const params = uri ? '?tracking_uri=' + encodeURIComponent(uri) : '';
try {
const res = await fetch(API_BASE + '/api/experiments/' + expId + '/runs' + params);
const runs = await res.json();
if (runs.length === 0) {
runList.innerHTML = '<div class="run-row" style="color:#888;">No runs</div>';
return;
}
runList.innerHTML = '';
runs.forEach(function(run) {
const row = document.createElement('div');
row.className = 'run-row';
row.innerHTML =
'<span class="run-name">' + (run.run_name || run.run_id.substring(0, 8)) + '</span>' +
'<span class="status ' + run.status + '">' + run.status + '</span>' +
'<span class="run-time">' + formatTime(run.start_time) + '</span>' +
'<div class="btn-group">' +
'<button class="btn btn-view" onclick="viewRun(\'' + run.run_id + '\')">View</button>' +
'<button class="btn btn-train" onclick="trainRun(\'' + run.run_id + '\')">Train</button>' +
'<button class="btn btn-serve" onclick="serveRun(\'' + run.run_id + '\')">Serve</button>' +
'</div>';
runList.appendChild(row);
});
} catch (e) {
runList.innerHTML = '<div class="error">Failed to load runs</div>';
}
}
async function viewRun(runId) {
const uri = getTrackingUri();
const params = uri ? '?tracking_uri=' + encodeURIComponent(uri) : '';
try {
const res = await fetch(API_BASE + '/api/runs/' + runId + '/mlflow-link' + params);
const data = await res.json();
window.open(data.url, '_blank');
} catch (e) {
alert('Failed to get MLflow link');
}
}
function trainRun(runId) {
alert('Train is not implemented yet.');
}
function serveRun(runId) {
alert('Serve: model_uri required. Use Swagger UI (/docs) for now.');
}
document.addEventListener('DOMContentLoaded', function() {
loadExperiments();
});