diff --git a/css/styles.css b/css/styles.css index dff0085..80e516a 100644 --- a/css/styles.css +++ b/css/styles.css @@ -367,3 +367,18 @@ html[lang="en"] .tooltip-icon:hover::after { align-self: flex-end; } } + +.loading-indicator { + text-align: center; + font-style: italic; + color: #0000FF; + padding: 10px; +} + +#hf-fetch-btn { + background-color: #0000FF; +} + +#hf-fetch-btn:hover { + background-color: #0000AA; +} diff --git a/index.html b/index.html index 97aa3b4..479b645 100644 --- a/index.html +++ b/index.html @@ -90,6 +90,14 @@ +
+ + + +
+ + +
diff --git a/js/app.js b/js/app.js index 05d55ff..b40ee9d 100644 --- a/js/app.js +++ b/js/app.js @@ -72,6 +72,10 @@ config_file_label: 'Config.json', config_file_ru: 'Загрузите config.json из модели. Автоматически заполнит поля и запустит расчет', config_file_en: 'Upload model config.json. Auto-fills fields and runs calculation', + hf_model_label: 'Model (HuggingFace)', + hf_model_ru: 'Введите имя модели', + hf_model_en: 'Please enter a model name', + hf_fetch_btn: 'Fetch', example_text: 'Example: context=8192, layers=32, kv_heads=32, head_size=128, model_size=7 GB, parallel=1', results_title: 'Results', k_cache: 'K cache:', @@ -187,6 +191,9 @@ const configFileInput = document.getElementById('config-file'); const resetBtn = document.getElementById('reset-btn'); const modelNameDisplay = document.getElementById('model-name-display'); + const hfModelInput = document.getElementById('hf-model'); + const hfFetchBtn = document.getElementById('hf-fetch-btn'); + const loadingIndicator = document.getElementById('loading-indicator'); // Initialize function init() { @@ -216,6 +223,11 @@ if (resetBtn) { resetBtn.addEventListener('click', handleReset); } + + // HF fetch button + if (hfFetchBtn) { + hfFetchBtn.addEventListener('click', handleHfFetch); + } } // Toggle language @@ -270,6 +282,12 @@ configTooltip.setAttribute('data-tooltip-en', translations[currentLang].config_file_en); } + // Update HF model field translation + const hfModelLabel = document.querySelector('#hf-model + label'); + if (hfModelLabel) { + hfModelLabel.textContent = translations[currentLang].hf_model_label; + } + // Re-evaluate errors if (window.configErrors && Object.keys(window.configErrors).length > 0) { showConfigErrors(); @@ -520,6 +538,7 @@ document.getElementById('model-size').value = ''; document.getElementById('parallel').value = '1'; document.getElementById('full-attention').value = ''; + hfModelInput.value = ''; // Reset quantization document.getElementById('k-type').value = 'KV'; @@ -535,6 +554,61 @@ clearAllTooltips(); } + // HuggingFace fetch function + async function handleHfFetch() { + const modelName = hfModelInput.value.trim(); + if (!modelName) { + showConfigError('hf-model', currentLang === 'ru' + ? 'Введите имя модели' + : 'Please enter a model name'); + return; + } + + // Show loading state + loadingIndicator.textContent = currentLang === 'ru' + ? 'Загрузка config из HuggingFace...' + : 'Fetching config from HuggingFace...'; + loadingIndicator.style.display = 'block'; + + const url = `https://huggingface.co/${modelName}/blob/main/config.json`; + + try { + const response = await fetch(url); + if (!response.ok) { + if (response.status === 404) { + showConfigError('hf-model', currentLang === 'ru' + ? 'Модель не найдена' + : 'Model not found'); + } else { + showConfigError('hf-model', currentLang === 'ru' + ? `Ошибка загрузки: ${response.status}` + : `Error loading: ${response.status}`); + } + loadingIndicator.style.display = 'none'; + return; + } + + const config = await response.json(); + + // Clear errors + window.configErrors = {}; + clearAllTooltips(); + + // Populate from config + populateFromConfig(config); + + // Hide loading and set focus + loadingIndicator.style.display = 'none'; + hfModelInput.focus(); + + } catch (err) { + loadingIndicator.style.display = 'none'; + showConfigError('hf-model', currentLang === 'ru' + ? `Не удалось загрузить config: ${err.message}` + : `Failed to load config: ${err.message}`); + } + } + // Show config error function showConfigError(field, message) { window.configErrors[field] = message;