/* ai.js — AI/LLM функции: проверка, генерация, объяснение, ассистент */ // PYTHON_STDLIB — стандартная библиотека Python (не требуют pip install). var PYTHON_STDLIB = new Set([ 'os','sys','json','time','re','math','io','collections','itertools','functools', 'datetime','pathlib','urllib','http','threading','asyncio','logging','random', 'string','struct','hashlib','hmac','base64','uuid','copy','abc','typing', 'dataclasses','contextlib','enum','warnings','traceback','inspect','importlib', 'subprocess','socket','ssl','email','html','xml','csv','sqlite3','unittest', 'gc','weakref','array','queue','heapq','bisect','decimal','fractions', 'statistics','textwrap','difflib','shutil','glob','fnmatch','tempfile', 'zipfile','tarfile','gzip','bz2','lzma','pickle','shelve','codecs', 'unicodedata','ast','dis','types','builtins','operator','numbers','cmath', 'pprint','reprlib','platform','signal','mmap','ctypes','multiprocessing', 'concurrent','select','selectors','errno','atexit','sched','calendar', 'locale','gettext','argparse','getopt','shlex','configparser','tokenize', 'runpy','pkgutil','site','sysconfig','distutils','zipimport','abc','io', '__future__','_thread','threading','contextvars','netrc','plistlib', 'html','xml','http','urllib','email','mailbox','mimetypes','encodings', 'codecs','unicodedata','readline','rlcompleter','curses','idlelib','tkinter', ]); // detectExternalPythonImports — возвращает список модулей, которых нет в stdlib. function detectExternalPythonImports(code) { var external = []; var seen = {}; var patterns = [/^import\s+([\w]+)/gm, /^from\s+([\w]+)/gm]; for (var pi = 0; pi < patterns.length; pi++) { var m; while ((m = patterns[pi].exec(code)) !== null) { var mod = m[1]; if (!PYTHON_STDLIB.has(mod) && !seen[mod]) { seen[mod] = true; external.push(mod); } } } return external; } function llmGeneratedWarning(lang) { var text = 'Сделано LLM. Не доверяй, проверяй!'; switch (lang) { case 'php': return '// ' + text; case 'nodejs': case 'go': return '// ' + text; case 'ruby': case 'python': default: return '# ' + text; } } function addLLMWarning(code, lang) { var body = String(code || '').replace(/^\s+/, ''); var warning = llmGeneratedWarning(lang); if (!body) return warning; if (body.indexOf(warning) === 0) return body; if (lang === 'php' && body.indexOf(' 0 && !depsVal) { resultText += '\n\n⚠️ Внешние библиотеки: ' + external.join(', ') + '\nДобавьте их в поле "Зависимости (requirements.txt)" или убедитесь что они уже есть в Python-окружении.'; if (data.ok) { resEl.style.background = '#3a2a00'; resEl.style.color = '#ffa'; } } } resEl.textContent = resultText; } catch (e) { resEl.style.background = '#3a2a00'; resEl.style.color = '#ffa'; resEl.textContent = 'Ошибка: ' + e.message; } finally { btn.disabled = false; btn.textContent = '🔍 Проверить синтаксис линтером'; } } var _assistantOpen = true; function toggleAssistant() { _assistantOpen = !_assistantOpen; document.getElementById('assistant-body').style.display = _assistantOpen ? 'flex' : 'none'; document.getElementById('assistant-toggle-icon').textContent = _assistantOpen ? '▲' : '▼'; } async function askAssistant() { var inp = document.getElementById('assistant-input'); var msgs = document.getElementById('assistant-messages'); var q = inp.value.trim(); if (!q) return; inp.value = ''; msgs.textContent += '\n\n👤 ' + q + '\n⏳ ...'; msgs.scrollTop = msgs.scrollHeight; try { var r = await fetch('/console/api/ai/ask', { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeaders() }, body: JSON.stringify({ mode: 'chat', question: q }) }); var d = await r.json(); msgs.textContent = msgs.textContent.replace('⏳ ...', '🤖 ' + (d.answer || d.error || 'Нет ответа')); } catch (e) { msgs.textContent = msgs.textContent.replace('⏳ ...', '❌ Ошибка: ' + e.message); } msgs.scrollTop = msgs.scrollHeight; } function showGenPrompt(prefix) { prefix = prefix || 'cc'; var el = document.getElementById(prefix + '-gen-prompt'); el.style.display = 'flex'; document.getElementById(prefix + '-gen-desc').focus(); } async function aiGenerate(prefix) { prefix = prefix || 'cc'; var name = (document.getElementById(prefix + '-name').value || '').trim() || 'my-func'; var lang = document.getElementById(prefix + '-lang').value || 'python'; var desc = (document.getElementById(prefix + '-gen-desc').value || '').trim(); if (!desc) { document.getElementById(prefix + '-gen-desc').focus(); return; } var btn = document.getElementById(prefix + '-gen-btn'); var resEl = document.getElementById(prefix + '-ai-result'); btn.disabled = true; btn.textContent = '⏳ Генерирую...'; resEl.style.display = 'block'; resEl.style.background = 'var(--bg-alt)'; resEl.style.color = 'var(--fg)'; resEl.textContent = 'Запрашиваю у LLM...'; try { var d = await requestJSON('/console/api/ai/ask', 'POST', { mode: 'codegen', language: lang, name: name, description: desc }); var code = (d.answer || '').replace(/^```[\w]*\n?/, '').replace(/\n?```$/, ''); document.getElementById(prefix + '-code').value = addLLMWarning(code, lang); document.getElementById(prefix + '-gen-prompt').style.display = 'none'; resEl.style.background = '#1a3a1a'; resEl.style.color = '#8f8'; resEl.textContent = 'Код сгенерирован и вставлен.'; } catch (e) { resEl.style.background = '#3a2a00'; resEl.style.color = '#ffa'; resEl.textContent = 'Ошибка: ' + e.message; } finally { btn.disabled = false; btn.textContent = '✨ Сгенерировать код LLM'; } } async function aiExplain(codeId, langId, resultId) { var code = (document.getElementById(codeId).value || '').trim(); var lang = (document.getElementById(langId) ? document.getElementById(langId).value : '') || 'python'; var resEl = document.getElementById(resultId); var btnId = codeId === 'c-code' ? 'c-exp-btn' : 'e-exp-btn'; var btn = document.getElementById(btnId); if (!code) { resEl.style.display = 'block'; resEl.style.background = 'var(--bg-alt)'; resEl.textContent = 'Введите код.'; return; } btn.disabled = true; btn.textContent = '⏳ Объясняю...'; resEl.style.display = 'block'; resEl.style.background = 'var(--bg-alt)'; resEl.style.color = 'var(--fg)'; resEl.textContent = 'Запрашиваю у LLM...'; try { var d = await requestJSON('/console/api/ai/ask', 'POST', { mode: 'explain', language: lang, code: code }); resEl.style.background = '#1a2a3a'; resEl.style.color = '#8cf'; resEl.textContent = d.answer || '(пустой ответ)'; } catch (e) { resEl.style.background = '#3a2a00'; resEl.style.color = '#ffa'; resEl.textContent = 'Ошибка: ' + e.message; } finally { btn.disabled = false; btn.textContent = '📖 LLM: Что делает?'; } }