refactor(ui): step9 - extract ai.js (llmGeneratedWarning, addLLMWarning, aiCheck, aiGenerate, aiExplain, askAssistant, toggleAssistant, showGenPrompt)
This commit is contained in:
+1
-216
@@ -20,6 +20,7 @@
|
||||
<script src="js/triggers.js"></script>
|
||||
<script src="js/modals.js"></script>
|
||||
<script src="js/functions.js"></script>
|
||||
<script src="js/ai.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -416,131 +417,6 @@
|
||||
currentInvoke: null
|
||||
};
|
||||
|
||||
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('<?php') === 0) {
|
||||
var rest = body.slice(5);
|
||||
rest = rest.replace(/^\r?\n/, '');
|
||||
return '<?php\n' + warning + '\n' + rest;
|
||||
}
|
||||
return warning + '\n' + body;
|
||||
}
|
||||
|
||||
async function reloadAll() {
|
||||
try {
|
||||
const [envs, pkgs, fns, http, times] = await Promise.all([
|
||||
getJSON(API_BASE + '/environments'),
|
||||
getJSON(API_BASE + '/packages'),
|
||||
getJSON(API_BASE + '/functions'),
|
||||
getJSON(API_BASE + '/httptriggers'),
|
||||
getJSON(API_BASE + '/timetriggers')
|
||||
]);
|
||||
|
||||
S.envs = envs || [];
|
||||
S.fns = fns || [];
|
||||
S.httpTriggers = http || [];
|
||||
S.timeTriggers = times || [];
|
||||
|
||||
setText('env-count', envs.length || 0);
|
||||
setText('pkg-count', pkgs.length || 0);
|
||||
setText('fn-count', fns.length || 0);
|
||||
setText('http-count', http.length || 0);
|
||||
setText('cron-count', new Set((times || []).map(function (t) {
|
||||
return t && t.spec && t.spec.functionref && t.spec.functionref.name;
|
||||
}).filter(Boolean)).size);
|
||||
|
||||
const rows = (fns || []).map(function (f) {
|
||||
const spec = f.spec || {};
|
||||
const meta = f.metadata || {};
|
||||
const ann = meta.annotations || {};
|
||||
const env = (spec.environment && spec.environment.name) || '-';
|
||||
const pkg = (spec.package && spec.package.packageref && spec.package.packageref.name) || '-';
|
||||
const name = (f.metadata && f.metadata.name) || '-';
|
||||
const trig = httpTriggerByFn(name) || {};
|
||||
const timeTrig = timeTriggerByFn(name) || {};
|
||||
const route = (trig.spec && trig.spec.relativeurl) || '-';
|
||||
const methods = (trig.spec && trig.spec.methods) || [];
|
||||
const chips = methods.map(function (m) { return '<span class="chip">' + h(m) + '</span>'; }).join('');
|
||||
const cron = (timeTrig.spec && timeTrig.spec.cron) || '';
|
||||
const cronCell = cron ? '<span class="chip">' + h(cron) + '</span>' : '<span class="mono" style="color:var(--text-secondary)">—</span>';
|
||||
const createdAt = ann['fission-console/created-at'] || meta.creationTimestamp || '-';
|
||||
const updatedAt = ann['fission-console/updated-at'] || createdAt;
|
||||
var isGo = /go[-_]env/.test(env);
|
||||
var isTf = /^tf-/.test(name);
|
||||
var tfBadge = (isGo || isTf) ? '<span class="chip" style="background:#555;color:#ffa" title="Управляется Terraform. Изменения могут быть перезаписаны при terraform apply.">TF</span> ' : '';
|
||||
var actions = tfBadge +
|
||||
'<button class="btn ghost" onclick="openEdit(\'' + h(name) + '\')">\u0420\u0435\u0434.</button> ' +
|
||||
'<button class="btn ghost" onclick="openInvoke(\'' + h(name) + '\')">\u0412\u044b\u0437\u043e\u0432</button> ' +
|
||||
'<button class="btn danger" onclick="removeFn(\'' + h(name) + '\')">\u0423\u0434\u0430\u043b\u0438\u0442\u044c</button>';
|
||||
return '<tr>' +
|
||||
'<td class="mono">' + h(name) + '</td>' +
|
||||
'<td>' + h(env) + '</td>' +
|
||||
'<td class="mono">' + h(pkg) + '</td>' +
|
||||
'<td>' + timestampCell(createdAt) + '</td>' +
|
||||
'<td>' + timestampCell(updatedAt) + '</td>' +
|
||||
'<td class="mono">' + h(route) + '</td>' +
|
||||
'<td>' + chips + '</td>' +
|
||||
'<td>' + cronCell + '</td>' +
|
||||
'<td class="nowrap">' + actions + '</td>' +
|
||||
'</tr>';
|
||||
}).join('');
|
||||
|
||||
document.getElementById('fn-rows').innerHTML = rows || '<tr><td colspan="9">\u041d\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0439</td></tr>';
|
||||
if (!rows) {
|
||||
document.getElementById('fn-rows').innerHTML = '<tr><td colspan="9">\u041d\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0439</td></tr>';
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('fn-rows').innerHTML = '<tr><td colspan="9">Load error: ' + e.message + '</td></tr>';
|
||||
showStatus('\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438: ' + e.message, 'err');
|
||||
}
|
||||
}
|
||||
|
||||
async function aiCheck(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);
|
||||
if (!code) { resEl.style.display = 'block'; resEl.style.background = 'var(--bg-alt)'; resEl.textContent = 'Введите код для проверки.'; return; }
|
||||
var btnId = codeId === 'c-code' ? 'c-ai-btn' : 'e-ai-btn';
|
||||
var btn = document.getElementById(btnId);
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Проверяю...';
|
||||
resEl.style.display = 'block';
|
||||
resEl.style.background = 'var(--bg-alt)';
|
||||
resEl.style.color = 'var(--fg)';
|
||||
resEl.textContent = 'Проверяю код линтером...';
|
||||
try {
|
||||
var data = await requestJSON(API_BASE + '/ai/check', 'POST', { language: lang, code: code });
|
||||
resEl.style.background = data.ok ? '#1a3a1a' : '#3a1a1a';
|
||||
resEl.style.color = data.ok ? '#8f8' : '#f88';
|
||||
resEl.textContent = data.result || '(пустой ответ)';
|
||||
} catch (e) {
|
||||
resEl.style.background = '#3a2a00';
|
||||
resEl.style.color = '#ffa';
|
||||
resEl.textContent = 'Ошибка: ' + e.message;
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '🔍 Проверить синтаксис линтером';
|
||||
}
|
||||
}
|
||||
|
||||
var _initPolling = false;
|
||||
var _initTickBusy = false;
|
||||
var _initStartedAt = 0;
|
||||
@@ -758,97 +634,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// ai/ask feature
|
||||
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() {
|
||||
var el = document.getElementById('c-gen-prompt');
|
||||
el.style.display = 'flex';
|
||||
document.getElementById('c-gen-desc').focus();
|
||||
}
|
||||
async function aiGenerate() {
|
||||
var name = (document.getElementById('c-name').value || '').trim() || 'my-func';
|
||||
var lang = document.getElementById('c-lang').value || 'python';
|
||||
var desc = (document.getElementById('c-gen-desc').value || '').trim();
|
||||
if (!desc) { document.getElementById('c-gen-desc').focus(); return; }
|
||||
var btn = document.getElementById('c-gen-btn');
|
||||
var resEl = document.getElementById('c-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('c-code').value = addLLMWarning(code, lang);
|
||||
document.getElementById('c-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: Что делает?';
|
||||
}
|
||||
}
|
||||
// end ai/ask feature
|
||||
</script>
|
||||
<!-- end ai/ask feature -->
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/* ai.js — AI/LLM функции: проверка, генерация, объяснение, ассистент */
|
||||
|
||||
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('<?php') === 0) {
|
||||
var rest = body.slice(5);
|
||||
rest = rest.replace(/^\r?\n/, '');
|
||||
return '<?php\n' + warning + '\n' + rest;
|
||||
}
|
||||
return warning + '\n' + body;
|
||||
}
|
||||
|
||||
async function aiCheck(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);
|
||||
if (!code) { resEl.style.display = 'block'; resEl.style.background = 'var(--bg-alt)'; resEl.textContent = 'Введите код для проверки.'; return; }
|
||||
var btnId = codeId === 'c-code' ? 'c-ai-btn' : 'e-ai-btn';
|
||||
var btn = document.getElementById(btnId);
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Проверяю...';
|
||||
resEl.style.display = 'block';
|
||||
resEl.style.background = 'var(--bg-alt)';
|
||||
resEl.style.color = 'var(--fg)';
|
||||
resEl.textContent = 'Проверяю код линтером...';
|
||||
try {
|
||||
var data = await requestJSON(API_BASE + '/ai/check', 'POST', { language: lang, code: code });
|
||||
resEl.style.background = data.ok ? '#1a3a1a' : '#3a1a1a';
|
||||
resEl.style.color = data.ok ? '#8f8' : '#f88';
|
||||
resEl.textContent = data.result || '(пустой ответ)';
|
||||
} 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() {
|
||||
var el = document.getElementById('c-gen-prompt');
|
||||
el.style.display = 'flex';
|
||||
document.getElementById('c-gen-desc').focus();
|
||||
}
|
||||
|
||||
async function aiGenerate() {
|
||||
var name = (document.getElementById('c-name').value || '').trim() || 'my-func';
|
||||
var lang = document.getElementById('c-lang').value || 'python';
|
||||
var desc = (document.getElementById('c-gen-desc').value || '').trim();
|
||||
if (!desc) { document.getElementById('c-gen-desc').focus(); return; }
|
||||
var btn = document.getElementById('c-gen-btn');
|
||||
var resEl = document.getElementById('c-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('c-code').value = addLLMWarning(code, lang);
|
||||
document.getElementById('c-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: Что делает?';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user