my-site/script.js
Dauren777 871147b688 feat: working backend with model + API config UI
- server.py: FastAPI serves model (best_generator.pt) + static files
- /api/generate endpoint with temperature/top_k controls
- /api/health endpoint for status
- Frontend auto-detects backend, shows connection status
- API URL configurable from UI (persisted in localStorage)
- NaN/inf safety in generation
- Clean output (strips BOS/EOS/PAD/UNK tokens)
2026-06-20 00:03:52 +00:00

184 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// API URL: ?api=... в URL, или localStorage, или auto-detect (same host :8000)
function getApiUrl() {
const qs = new URLSearchParams(window.location.search).get('api');
if (qs) return qs.replace(/\/+$/, '');
const saved = localStorage.getItem('cbgen_api_url');
if (saved) return saved.replace(/\/+$/, '');
// Auto-detect: same hostname, port 8000
return `${location.protocol}//${location.hostname}:8000`;
}
let API_URL = getApiUrl();
const promptInput = document.getElementById('promptInput');
const tempSlider = document.getElementById('tempSlider');
const tempVal = document.getElementById('tempVal');
const topkSlider = document.getElementById('topkSlider');
const topkVal = document.getElementById('topkVal');
const generateBtn = document.getElementById('generateBtn');
const generate5Btn = document.getElementById('generate5Btn');
const outputArea = document.getElementById('outputArea');
const statusMsg = document.getElementById('statusMsg');
const apiInput = document.getElementById('apiUrlInput');
const apiStatus = document.getElementById('apiStatus');
let backendOnline = false;
async function checkBackend() {
try {
const res = await fetch(`${API_URL}/api/health`, { signal: AbortSignal.timeout(3000) });
if (res.ok) {
const data = await res.json();
backendOnline = data.model_loaded === true;
if (backendOnline && data.using_dummy_vocab) {
apiStatus.textContent = '🟡 Модель загружена (без vocab.pt)';
apiStatus.className = 'api-status warn';
} else if (backendOnline) {
apiStatus.textContent = '🟢 Модель загружена';
apiStatus.className = 'api-status ok';
} else {
apiStatus.textContent = '🟡 Сервер работает, модель не загружена';
apiStatus.className = 'api-status warn';
}
} else {
backendOnline = false;
apiStatus.textContent = '🔴 Сервер отвечает с ошибкой';
apiStatus.className = 'api-status err';
}
} catch {
backendOnline = false;
apiStatus.textContent = '🔴 Бэкенд недоступен — используются шаблоны';
apiStatus.className = 'api-status err';
}
}
if (apiInput) {
apiInput.value = API_URL;
apiInput.addEventListener('change', () => {
API_URL = apiInput.value.replace(/\/+$/, '');
localStorage.setItem('cbgen_api_url', API_URL);
checkBackend();
});
}
checkBackend();
setInterval(checkBackend, 15000);
tempSlider.addEventListener('input', () => { tempVal.textContent = tempSlider.value; });
topkSlider.addEventListener('input', () => { topkVal.textContent = topkSlider.value; });
generateBtn.addEventListener('click', () => generate(1));
generate5Btn.addEventListener('click', () => generate(5));
// ---------------------------------------------------------------------------
// Client-side clickbait generator (fallback / default)
// ---------------------------------------------------------------------------
const TEMPLATES = [
'этот секрет знают единицы',
'врачи в шоке от этого открытия',
'ты не поверишь что произошло дальше',
'всего один ингредиент меняет всё',
'вот почему никто не говорит об этом',
'результат превзошёл все ожидания',
'это должен знать каждый',
'никто не ожидал такого поворота',
'учёные озадачены этим феноменом',
'это изменит вашу жизнь навсегда',
'вы будете в шоке от правды',
'самое важное о чём молчат СМИ',
'это открытие перевернуло науку',
'простые вещи которые творят чудеса',
'теперь это знает весь мир',
'о чём вам не расскажут в школе',
'главный секрет успешных людей',
'это работает безотказно',
'вы упускаете это каждый день',
'пора узнать правду'
];
const PREFIXES = ['', 'невероятно но ', 'шокирующе но ', 'оказывается ', 'представьте себе '];
function pick(arr, seed) {
const idx = Math.abs(seed * 7 + seed * seed * 31) % arr.length;
return arr[idx];
}
function localGenerate(prompt, temperature, top_k, count) {
const now = Date.now();
const results = [];
for (let i = 0; i < count; i++) {
const seed = now + i * 137 + Math.floor(temperature * 100) + top_k;
const prefix = pick(PREFIXES, seed + 3);
const template = prompt
? `${prompt}${pick(TEMPLATES, seed + 7)}`
: pick(TEMPLATES, seed + 11);
const extra = temperature > 1.3
? (pick(['невероятно', 'шокирующе', 'фантастически'], seed + 19) + ' ')
: '';
results.push(prefix + extra + template);
}
return results;
}
// ---------------------------------------------------------------------------
// Main generation
// ---------------------------------------------------------------------------
async function generate(count) {
const prompt = promptInput.value.trim();
const temperature = parseFloat(tempSlider.value);
const top_k = parseInt(topkSlider.value);
outputArea.innerHTML = '';
statusMsg.textContent = '⚡ Генерируем...';
statusMsg.className = 'status-msg';
let texts;
let usedBackend = false;
try {
const res = await fetch(`${API_URL}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt, temperature, top_k, num_samples: count })
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
texts = data.texts;
usedBackend = true;
} catch {
texts = localGenerate(prompt, temperature, top_k, count);
usedBackend = false;
}
statusMsg.textContent = usedBackend ? '✅ Модель (PyTorch)' : '⚡ Локальные шаблоны (бэкенд недоступен)';
statusMsg.className = 'status-msg';
texts.forEach((text, i) => {
const div = document.createElement('div');
div.className = 'gen-output-item';
div.style.animationDelay = `${i * 0.08}s`;
const label = count > 1 ? `<span class="gen-badge">#${i + 1}</span>` : '';
div.innerHTML = `${label}${text}...😱`;
outputArea.appendChild(div);
});
// Regenerate button
const reBtn = document.createElement('button');
reBtn.className = 'btn btn-secondary btn-regenerate';
reBtn.textContent = '🔄 Сгенерировать ещё';
reBtn.addEventListener('click', () => generate(count));
outputArea.appendChild(reBtn);
}
// Example tabs
document.querySelectorAll('.ex-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.ex-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.ex-content').forEach(c => c.classList.remove('active'));
tab.classList.add('active');
document.getElementById('ex-' + tab.dataset.tab).classList.add('active');
});
});