undefined / model_converter.html
eric164's picture
Titel:
e97deb0 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ModelMuse - AI Model Converter</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<style>
.dropzone {
border: 2px dashed #9CA3AF;
transition: all 0.3s ease;
}
.dropzone-active {
border-color: #8B5CF6;
background-color: #F3E8FF;
}
.progress-bar {
transition: width 0.3s ease;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.pulse {
animation: pulse 2s infinite;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-5xl">
<header class="mb-8 text-center">
<h1 class="text-4xl font-bold text-purple-700 mb-2">ModelMuse</h1>
<p class="text-lg text-gray-600">Your AI Alchemy Wizard for converting Stable Diffusion models to Amuse-compatible ONNX/Olive</p>
</header>
<div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
<div class="bg-gradient-to-r from-purple-600 to-indigo-600 p-4 text-white">
<h2 class="text-xl font-bold">Model Converter</h2>
</div>
<div class="p-6">
<!-- Model Input Section -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-800 mb-3">Input Model</h3>
<div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer mb-4">
<div class="flex flex-col items-center justify-center">
<i data-feather="upload-cloud" class="w-12 h-12 text-gray-400 mb-3"></i>
<p class="text-gray-600">Drag & drop your model file here (.ckpt, .safetensors)</p>
<p class="text-sm text-gray-500 mt-1">or</p>
<button id="browse-btn" class="mt-2 bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors">
Browse Files
</button>
</div>
<input type="file" id="file-input" class="hidden" accept=".ckpt,.safetensors">
</div>
<div id="selected-files" class="hidden">
<h4 class="text-sm font-medium text-gray-700 mb-2">Selected Files:</h4>
<div id="file-list" class="space-y-2"></div>
</div>
</div>
<!-- Conversion Options -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-800 mb-3">Conversion Options</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Target Hardware</label>
<select class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-purple-500 focus:border-purple-500">
<option value="directml">DirectML (Windows Default)</option>
<option value="cuda">CUDA (NVIDIA GPU)</option>
<option value="cpu">CPU (OpenVINO)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Precision</label>
<select class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-purple-500 focus:border-purple-500">
<option value="fp16">FP16 (Recommended)</option>
<option value="fp32">FP32 (Most Compatible)</option>
<option value="int8">INT8 (Quantized, Experimental)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Output Directory</label>
<div class="flex">
<input type="text" id="output-dir" class="flex-grow border border-gray-300 rounded-l-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500" placeholder="Select output folder" readonly>
<button id="output-dir-btn" class="bg-purple-600 text-white px-4 py-2 rounded-r-lg hover:bg-purple-700 transition-colors">
<i data-feather="folder"></i>
</button>
</div>
</div>
<div class="flex items-end">
<button id="advanced-btn" class="text-purple-600 hover:text-purple-800 flex items-center">
<i data-feather="settings" class="w-4 h-4 mr-2"></i>
Advanced Options
</button>
</div>
</div>
</div>
<!-- License Warning -->
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-8">
<div class="flex">
<div class="flex-shrink-0">
<i data-feather="alert-triangle" class="w-5 h-5 text-yellow-400"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800">License Compliance</h3>
<div class="mt-2 text-sm text-yellow-700">
<p>You may only convert models for which you have the appropriate rights. Please check the model license before proceeding.</p>
</div>
<div class="mt-3">
<div class="flex items-center">
<input id="license-checkbox" type="checkbox" class="h-4 w-4 text-purple-600 focus:ring-purple-500 border-gray-300 rounded">
<label for="license-checkbox" class="ml-2 block text-sm text-gray-700">
I confirm I have the rights to convert this model
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex justify-between">
<button id="cli-btn" class="flex items-center text-gray-600 hover:text-gray-800">
<i data-feather="terminal" class="w-4 h-4 mr-2"></i>
CLI Command Builder
</button>
<button id="convert-btn" class="bg-gradient-to-r from-purple-600 to-indigo-600 text-white px-6 py-3 rounded-lg font-medium hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed" disabled>
Convert Model
</button>
</div>
</div>
</div>
<!-- Progress Section -->
<div id="progress-section" class="bg-white rounded-xl shadow-lg overflow-hidden hidden mb-8">
<div class="bg-gradient-to-r from-purple-600 to-indigo-600 p-4 text-white">
<h2 class="text-xl font-bold">Conversion Progress</h2>
</div>
<div class="p-6">
<div class="mb-4">
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-700">Processing</span>
<span id="progress-percent" class="text-sm font-medium text-gray-700">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progress-bar" class="progress-bar bg-purple-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
<div id="progress-steps" class="space-y-3">
<!-- Steps will be added dynamically -->
</div>
<div id="logs-container" class="mt-4 bg-gray-50 p-3 rounded-lg max-h-48 overflow-y-auto hidden">
<pre id="conversion-logs" class="text-xs font-mono text-gray-700"></pre>
</div>
</div>
</div>
<!-- Results Section -->
<div id="results-section" class="bg-white rounded-xl shadow-lg overflow-hidden hidden">
<div class="bg-gradient-to-r from-green-600 to-emerald-600 p-4 text-white">
<h2 class="text-xl font-bold">Conversion Complete</h2>
</div>
<div class="p-6">
<div class="flex items-center mb-4">
<div class="bg-green-100 p-3 rounded-full mr-4">
<i data-feather="check-circle" class="w-6 h-6 text-green-600"></i>
</div>
<div>
<h3 class="text-lg font-medium text-gray-800">Successfully converted model</h3>
<p class="text-sm text-gray-600">Your model is now ready for use with Amuse</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div class="border border-gray-200 rounded-lg p-4">
<h4 class="text-sm font-medium text-gray-700 mb-2">Output Files</h4>
<ul id="output-files" class="text-sm text-gray-600 space-y-1">
<li class="flex items-center"><i data-feather="file" class="w-4 h-4 mr-2 text-purple-500"></i> model.onnx</li>
<li class="flex items-center"><i data-feather="file" class="w-4 h-4 mr-2 text-purple-500"></i> metadata.json</li>
<li class="flex items-center"><i data-feather="file" class="w-4 h-4 mr-2 text-purple-500"></i> olive_config.json</li>
</ul>
</div>
<div class="border border-gray-200 rounded-lg p-4">
<h4 class="text-sm font-medium text-gray-700 mb-2">Model Info</h4>
<dl class="text-sm text-gray-600 space-y-1">
<div class="flex justify-between">
<dt>Original Model:</dt>
<dd id="original-model" class="font-medium">model.safetensors</dd>
</div>
<div class="flex justify-between">
<dt>Format:</dt>
<dd id="output-format" class="font-medium">ONNX + Olive</dd>
</div>
<div class="flex justify-between">
<dt>Precision:</dt>
<dd id="output-precision" class="font-medium">FP16</dd>
</div>
<div class="flex justify-between">
<dt>Target:</dt>
<dd id="output-target" class="font-medium">DirectML</dd>
</div>
</dl>
</div>
</div>
<div class="flex justify-end space-x-3">
<button id="open-folder-btn" class="flex items-center border border-gray-300 px-4 py-2 rounded-lg text-gray-700 hover:bg-gray-50">
<i data-feather="folder" class="w-4 h-4 mr-2"></i>
Open Output Folder
</button>
<button id="create-installer-btn" class="flex items-center bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700">
<i data-feather="package" class="w-4 h-4 mr-2"></i>
Create Windows Installer
</button>
</div>
</div>
</div>
<!-- CLI Modal -->
<div id="cli-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
<div class="border-b border-gray-200 px-6 py-4">
<h3 class="text-lg font-semibold text-gray-800">CLI Command Builder</h3>
</div>
<div class="p-6">
<p class="text-sm text-gray-600 mb-4">Use this command to run the conversion from command line:</p>
<div class="bg-gray-800 rounded-lg p-4 mb-4">
<pre id="cli-command" class="text-green-400 font-mono text-sm overflow-x-auto">modelmuse --input model.safetensors --output ./output --target directml --precision fp16</pre>
</div>
<p class="text-xs text-gray-500 mb-4">Note: You can also run batch conversions by specifying multiple input files or directories.</p>
<div class="flex justify-end">
<button id="copy-cli-btn" class="bg-purple-600 text-white px-4 py-2 rounded-lg mr-3 hover:bg-purple-700">
Copy Command
</button>
<button id="close-cli-btn" class="border border-gray-300 px-4 py-2 rounded-lg text-gray-700 hover:bg-gray-50">
Close
</button>
</div>
</div>
</div>
</div>
</div>
<script>
feather.replace();
// DOM Elements
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('file-input');
const browseBtn = document.getElementById('browse-btn');
const selectedFilesDiv = document.getElementById('selected-files');
const fileList = document.getElementById('file-list');
const licenseCheckbox = document.getElementById('license-checkbox');
const convertBtn = document.getElementById('convert-btn');
const progressSection = document.getElementById('progress-section');
const resultsSection = document.getElementById('results-section');
const cliBtn = document.getElementById('cli-btn');
const cliModal = document.getElementById('cli-modal');
const closeCliBtn = document.getElementById('close-cli-btn');
const copyCliBtn = document.getElementById('copy-cli-btn');
// Event Listeners
browseBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
dropzone.classList.add('dropzone-active');
});
['dragleave', 'dragend'].forEach(type => {
dropzone.addEventListener(type, () => {
dropzone.classList.remove('dropzone-active');
});
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dropzone-active');
if (e.dataTransfer.files.length) {
handleFiles(e.dataTransfer.files);
}
});
licenseCheckbox.addEventListener('change', () => {
convertBtn.disabled = !licenseCheckbox.checked;
});
convertBtn.addEventListener('click', () => {
startConversion();
});
cliBtn.addEventListener('click', () => {
cliModal.classList.remove('hidden');
});
closeCliBtn.addEventListener('click', () => {
cliModal.classList.add('hidden');
});
copyCliBtn.addEventListener('click', () => {
const cliCommand = document.getElementById('cli-command').textContent;
navigator.clipboard.writeText(cliCommand).then(() => {
copyCliBtn.textContent = 'Copied!';
setTimeout(() => {
copyCliBtn.textContent = 'Copy Command';
}, 2000);
});
});
// Functions
function handleFiles(files) {
fileList.innerHTML = '';
for (const file of files) {
const fileItem = document.createElement('div');
fileItem.className = 'flex items-center justify-between p-2 bg-gray-50 rounded-lg';
fileItem.innerHTML = `
<div class="flex items-center">
<i data-feather="file" class="w-4 h-4 mr-2 text-purple-500"></i>
<span class="text-sm">${file.name}</span>
</div>
<span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
`;
fileList.appendChild(fileItem);
}
selectedFilesDiv.classList.remove('hidden');
feather.replace();
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function startConversion() {
progressSection.classList.remove('hidden');
// Simulate conversion progress
const progressBar = document.getElementById('progress-bar');
const progressPercent = document.getElementById('progress-percent');
const progressSteps = document.getElementById('progress-steps');
const steps = [
{ name: 'Detecting model type', icon: 'search' },
{ name: 'Loading model weights', icon: 'loader' },
{ name: 'Exporting to ONNX', icon: 'download' },
{ name: 'Running Olive optimization', icon: 'cpu' },
{ name: 'Validating outputs', icon: 'check-circle' },
{ name: 'Saving artifacts', icon: 'save' }
];
progressSteps.innerHTML = steps.map(step => `
<div class="flex items-center">
<div class="flex-shrink-0 mr-3">
<i data-feather="${step.icon}" class="w-4 h-4 text-purple-500"></i>
</div>
<div class="flex-grow">
<p class="text-sm font-medium text-gray-700">${step.name}</p>
<div class="w-full bg-gray-200 rounded-full h-1.5 mt-1">
<div class="bg-purple-600 h-1.5 rounded-full progress-step" style="width: 0%"></div>
</div>
</div>
</div>
`).join('');
feather.replace();
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 5;
if (progress > 100) progress = 100;
progressBar.style.width = `${progress}%`;
progressPercent.textContent = `${Math.floor(progress)}%`;
const stepProgresses = document.querySelectorAll('.progress-step');
steps.forEach((_, i) => {
const stepProgress = Math.min(100, Math.max(0, (progress - (i * (100 / steps.length))) * steps.length));
if (stepProgresses[i]) {
stepProgresses[i].style.width = `${stepProgress}%`;
}
});
if (progress === 100) {
clearInterval(interval);
setTimeout(() => {
progressSection.classList.add('hidden');
resultsSection.classList.remove('hidden');
}, 500);
}
}, 300);
}
</script>
</body>
</html>