/
RobinDenisSh
/
Shvarts.D_Lab6
Обзор
Документация
Войти
/
RobinDenisSh
/
Shvarts.D_Lab6
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Lab6.py
587 строк
21 KB
RobinDenisSh
create: Lab6.py
14 май 2026, 08:01
Верифицирован
14 май 2026, 08:01
9c92b26
Код
Авторство
О чём код?
+<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8" /> +<meta name="viewport" content="width=device-width, initial-scale=1.0" /> +<title>Prompt Engineering Tool</title> +<style> + :root { + --bg: #0f1115; + --panel: #161922; + --panel-2: #1c1f2a; + --text: #e6e8ef; + --muted: #9aa2b2; + --accent: #58a6ff; + --accent-2: #3fb950; + --danger: #f85149; + --border: #2a2e3a; + --input-bg: #11131a; + --chip: #21262d; + --font: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; + } + * { box-sizing: border-box; } + html, body { height: 100%; margin: 0; } + body { + background: var(--bg); + color: var(--text); + font-family: var(--font); + font-size: 14px; + line-height: 1.45; + } + button { + font-family: inherit; + cursor: pointer; + border: 1px solid var(--border); + background: var(--panel-2); + color: var(--text); + padding: 6px 10px; + border-radius: 6px; + font-size: 12px; + } + button:hover { background: #22263a; } + button.primary { background: var(--accent); color: #fff; border-color: var(--accent); } + button.primary:hover { background: #4b8ede; } + button.danger { background: var(--danger); color: #fff; border-color: var(--danger); } + button.danger:hover { background: #e03b30; } + input, textarea, select { + font-family: inherit; + background: var(--input-bg); + color: var(--text); + border: 1px solid var(--border); + border-radius: 6px; + padding: 8px 10px; + font-size: 13px; + outline: none; + width: 100%; + } + input:focus, textarea:focus, select:focus { border-color: var(--accent); } + textarea { resize: vertical; min-height: 80px; } + label { display: block; color: var(--muted); font-size: 11px; margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.05em; } + + .app { display: flex; height: 100vh; overflow: hidden; } + + /* Left: Versions */ + .versions { + width: 260px; + min-width: 220px; + background: var(--panel); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + } + .versions header { + padding: 12px 14px; + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: space-between; + } + .versions header h2 { margin: 0; font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } + .versions .list { flex: 1; overflow-y: auto; padding: 8px; } + .version-item { + background: var(--panel-2); + border: 1px solid var(--border); + border-radius: 8px; + padding: 10px; + margin-bottom: 8px; + } + .version-item .name { font-weight: 600; font-size: 12px; } + .version-item .meta { color: var(--muted); font-size: 11px; margin-top: 2px; } + .version-item .actions { display: flex; gap: 6px; margin-top: 8px; } + .version-item .actions button { flex: 1; } + .empty { color: var(--muted); padding: 12px; text-align: center; font-size: 12px; } + + /* Center: Editor */ + .editor-pane { + flex: 1; + min-width: 320px; + display: flex; + flex-direction: column; + background: var(--bg); + } + .editor-pane header { + padding: 12px 14px; + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: space-between; + } + .editor-pane header h2 { margin: 0; font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } + .syntax-bar { + padding: 10px 14px; + border-bottom: 1px solid var(--border); + background: var(--panel); + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + } + .syntax-bar .label { color: var(--muted); font-size: 11px; } + .chip { + background: var(--chip); + border: 1px solid var(--border); + color: var(--accent); + padding: 2px 8px; + border-radius: 999px; + font-size: 11px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; + } + .editor-wrap { flex: 1; display: flex; flex-direction: column; padding: 10px 14px; gap: 10px; } + .editor-wrap textarea { flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 13px; line-height: 1.6; } + + /* Right: Sandbox */ + .sandbox { + width: 420px; + min-width: 340px; + background: var(--panel); + border-left: 1px solid var(--border); + display: flex; + flex-direction: column; + } + .sandbox header { + padding: 12px 14px; + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: space-between; + } + .sandbox header h2 { margin: 0; font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } + .sandbox-body { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 12px; } + .field { display: flex; flex-direction: column; } + .field-inline { display: flex; gap: 8px; align-items: flex-end; } + .field-inline .field { flex: 1; } + .answer-box { + background: var(--bg); + border: 1px solid var(--border); + border-radius: 8px; + padding: 10px; + min-height: 120px; + white-space: pre-wrap; + word-break: break-word; + font-size: 13px; + line-height: 1.5; + } + .vars-form { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } + .var-row { display: contents; } + .var-row input { background: var(--input-bg); } + .muted { color: var(--muted); } + .hint { font-size: 11px; color: var(--muted); } + .row-actions { display: flex; gap: 8px; } + + /* Modal for version name */ + .modal-overlay { + position: fixed; inset: 0; background: rgba(0,0,0,0.6); + display: none; align-items: center; justify-content: center; z-index: 100; + } + .modal-overlay.open { display: flex; } + .modal { + background: var(--panel); + border: 1px solid var(--border); + border-radius: 10px; + padding: 16px; + width: 420px; + max-width: 90vw; + } + .modal h3 { margin: 0 0 10px; font-size: 14px; } + .modal .actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; } + + @media (max-width: 1100px) { + .sandbox { width: 360px; } + .versions { width: 220px; } + } +</style> +</head> +<body> +<div class="app"> + <!-- Versions Sidebar --> + <aside class="versions"> + <header> + <h2>Versions</h2> + <button class="primary" id="btnSaveVersion">Save Version</button> + </header> + <div class="list" id="versionList"> + <div class="empty">No versions saved yet.</div> + </div> + </aside> + + <!-- Editor --> + <section class="editor-pane"> + <header> + <h2>Prompt Editor</h2> + <span class="hint" id="draftStatus">Draft auto-saved</span> + </header> + <div class="syntax-bar" id="syntaxBar"> + <span class="label">Variables:</span> + <span class="hint">Type <code>{{variable_name}}</code> in the editor</span> + </div> + <div class="editor-wrap"> + <textarea id="promptEditor" placeholder="Write your prompt template here... Example: Hello {{user_name}}, answer as {{persona}}."></textarea> + </div> + </section> + + <!-- Sandbox --> + <aside class="sandbox"> + <header> + <h2>Sandbox</h2> + <span class="hint" id="runStatus"></span> + </header> + <div class="sandbox-body"> + <div class="field-inline"> + <div class="field"> + <label>Model</label> + <select id="modelSelect"> + <option value="mock">Mock / Local</option> + <option value="openai:gpt-4o">OpenAI GPT-4o</option> + <option value="openai:gpt-4o-mini">OpenAI GPT-4o Mini</option> + <option value="anthropic:claude-3-opus-20240229">Claude 3 Opus</option> + <option value="anthropic:claude-3-sonnet-20240229">Claude 3 Sonnet</option> + <option value="anthropic:claude-3-haiku-20240307">Claude 3 Haiku</option> + </select> + </div> + <div class="field"> + <label>API Key</label> + <input id="apiKey" type="password" placeholder="sk-... or sk-ant-..." /> + </div> + </div> + + <div class="field"> + <label>Inject Variables</label> + <div class="hint">Values for <code>{{var}}</code> placeholders in your prompt</div> + <div class="vars-form" id="varsForm"> + <div class="var-row"> + <input class="var-key" placeholder="key" /> + <input class="var-val" placeholder="value" /> + </div> + </div> + <div class="row-actions" style="margin-top:6px;"> + <button id="btnAddVar">+ Add variable</button> + <button id="btnClearVars">Clear all</button> + </div> + </div> + + <div class="field"> + <label>User Input (Question)</label> + <textarea id="userInput" placeholder="Enter a test question here..."></textarea> + </div> + + <div class="field"> + <div class="row-actions" style="justify-content: space-between; align-items: center;"> + <label style="margin:0">Answer</label> + <button class="primary" id="btnRun">Run Test</button> + </div> + <div class="answer-box" id="answerBox">Run a test to see the answer here.</div> + </div> + </div> + </aside> +</div> + +<!-- Save version modal --> +<div class="modal-overlay" id="modalOverlay"> + <div class="modal"> + <h3>Save Version</h3> + <div class="field"> + <label>Version Name</label> + <input id="versionName" placeholder="e.g., v1.0" /> + </div> + <div class="actions"> + <button id="btnCancelVersion">Cancel</button> + <button class="primary" id="btnConfirmVersion">Save</button> + </div> + </div> +</div> + +<script> +(function() { + const LS_VERSIONS = 'pet_versions'; + const LS_DRAFT = 'pet_draft'; + + const promptEditor = document.getElementById('promptEditor'); + const syntaxBar = document.getElementById('syntaxBar'); + const versionList = document.getElementById('versionList'); + const draftStatus = document.getElementById('draftStatus'); + const btnSaveVersion = document.getElementById('btnSaveVersion'); + const modalOverlay = document.getElementById('modalOverlay'); + const versionNameInput = document.getElementById('versionName'); + const btnCancelVersion = document.getElementById('btnCancelVersion'); + const btnConfirmVersion = document.getElementById('btnConfirmVersion'); + const modelSelect = document.getElementById('modelSelect'); + const apiKeyInput = document.getElementById('apiKey'); + const varsForm = document.getElementById('varsForm'); + const btnAddVar = document.getElementById('btnAddVar'); + const btnClearVars = document.getElementById('btnClearVars'); + const userInput = document.getElementById('userInput'); + const btnRun = document.getElementById('btnRun'); + const answerBox = document.getElementById('answerBox'); + const runStatus = document.getElementById('runStatus'); + + let versions = []; + + function loadStorage() { + try { + const v = localStorage.getItem(LS_VERSIONS); + if (v) versions = JSON.parse(v); + } catch (e) { versions = []; } + const d = localStorage.getItem(LS_DRAFT); + if (d !== null) promptEditor.value = d; + renderVersions(); + updateSyntaxBar(); + } + + function saveVersions() { + localStorage.setItem(LS_VERSIONS, JSON.stringify(versions)); + } + + function saveDraft() { + localStorage.setItem(LS_DRAFT, promptEditor.value); + draftStatus.textContent = 'Draft auto-saved ' + new Date().toLocaleTimeString(); + } + + function extractVars(text) { + const re = /\{\{(\s*\w+\s*)\}\}/g; + const found = new Set(); + let m; + while ((m = re.exec(text)) !== null) { + found.add(m[1].trim()); + } + return Array.from(found); + } + + function updateSyntaxBar() { + const vars = extractVars(promptEditor.value); + const base = document.createElement('div'); + base.className = 'syntax-bar'; + const label = document.createElement('span'); + label.className = 'label'; + label.textContent = 'Variables:'; + base.appendChild(label); + if (vars.length === 0) { + const hint = document.createElement('span'); + hint.className = 'hint'; + hint.innerHTML = 'Type <code>{{variable_name}}</code> in the editor'; + base.appendChild(hint); + } else { + vars.forEach(v => { + const chip = document.createElement('span'); + chip.className = 'chip'; + chip.textContent = '{{' + v + '}}'; + base.appendChild(chip); + }); + } + syntaxBar.replaceWith(base); + // rebind global reference + Object.defineProperty(window, 'syntaxBar', { value: base, configurable: true }); + // Actually replace the reference used in this closure + // But we can't redefine const. Let's just keep using the original node by replacing children. + // Simpler: just mutate children of the original node + } + + // Simpler: just mutate children of the original node without replaceWith + function updateSyntaxBarSimple() { + const vars = extractVars(promptEditor.value); + syntaxBar.innerHTML = ''; + const label = document.createElement('span'); + label.className = 'label'; + label.textContent = 'Variables:'; + syntaxBar.appendChild(label); + if (vars.length === 0) { + const hint = document.createElement('span'); + hint.className = 'hint'; + hint.innerHTML = 'Type <code>{{variable_name}}</code> in the editor'; + syntaxBar.appendChild(hint); + } else { + vars.forEach(v => { + const chip = document.createElement('span'); + chip.className = 'chip'; + chip.textContent = '{{' + v + '}}'; + syntaxBar.appendChild(chip); + }); + } + } + + function renderVersions() { + versionList.innerHTML = ''; + if (versions.length === 0) { + versionList.innerHTML = '<div class="empty">No versions saved yet.</div>'; + return; + } + // Newest first + const list = versions.slice().reverse(); + list.forEach((v, idx) => { + const realIndex = versions.length - 1 - idx; + const el = document.createElement('div'); + el.className = 'version-item'; + const name = document.createElement('div'); + name.className = 'name'; + name.textContent = v.name || 'Untitled'; + const meta = document.createElement('div'); + meta.className = 'meta'; + const d = new Date(v.timestamp); + meta.textContent = d.toLocaleString(); + const actions = document.createElement('div'); + actions.className = 'actions'; + const btnRollback = document.createElement('button'); + btnRollback.textContent = 'Rollback'; + btnRollback.onclick = () => rollback(realIndex); + const btnDelete = document.createElement('button'); + btnDelete.className = 'danger'; + btnDelete.textContent = 'Delete'; + btnDelete.onclick = () => { + if (confirm('Delete version "' + (v.name || 'Untitled') + '"?')) { + versions.splice(realIndex, 1); + saveVersions(); + renderVersions(); + } + }; + actions.appendChild(btnRollback); + actions.appendChild(btnDelete); + el.appendChild(name); + el.appendChild(meta); + el.appendChild(actions); + versionList.appendChild(el); + }); + } + + function rollback(index) { + const v = versions[index]; + if (!v) return; + promptEditor.value = v.content; + saveDraft(); + updateSyntaxBarSimple(); + draftStatus.textContent = 'Rolled back to ' + (v.name || 'version') + ' — draft saved'; + } + + function openModal() { versionNameInput.value = 'v' + (versions.length + 1) + '.0'; modalOverlay.classList.add('open'); versionNameInput.focus(); } + function closeModal() { modalOverlay.classList.remove('open'); } + + btnSaveVersion.addEventListener('click', openModal); + btnCancelVersion.addEventListener('click', closeModal); + btnConfirmVersion.addEventListener('click', () => { + const name = versionNameInput.value.trim(); + if (!name) { alert('Enter a version name'); return; } + versions.push({ id: crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + Math.random(), name, content: promptEditor.value, timestamp: Date.now() }); + saveVersions(); + renderVersions(); + closeModal(); + }); + + promptEditor.addEventListener('input', () => { saveDraft(); updateSyntaxBarSimple(); }); + + // Variable rows + function addVarRow(key, val) { + const row = document.createElement('div'); + row.className = 'var-row'; + const k = document.createElement('input'); + k.className = 'var-key'; + k.placeholder = 'key'; + if (key !== undefined) k.value = key; + const v = document.createElement('input'); + v.className = 'var-val'; + v.placeholder = 'value'; + if (val !== undefined) v.value = val; + row.appendChild(k); + row.appendChild(v); + varsForm.appendChild(row); + } + btnAddVar.addEventListener('click', () => addVarRow()); + btnClearVars.addEventListener('click', () => { varsForm.innerHTML = ''; addVarRow(); }); + + function getInjectedVars() { + const rows = varsForm.querySelectorAll('.var-row'); + const vars = {}; + rows.forEach(r => { + const k = r.querySelector('.var-key').value.trim(); + const v = r.querySelector('.var-val').value; + if (k) vars[k] = v; + }); + return vars; + } + + function replaceVars(text, vars) { + return text.replace(/\{\{(\s*\w+\s*)\}\}/g, (m, name) => { + const key = name.trim(); + return vars.hasOwnProperty(key) ? vars[key] : m; + }); + } + + async function runTest() { + const promptTemplate = promptEditor.value; + const userQuestion = userInput.value.trim(); + if (!promptTemplate.trim() && !userQuestion) { + answerBox.textContent = 'Enter a prompt template or a user question first.'; + return; + } + const vars = getInjectedVars(); + const systemPrompt = replaceVars(promptTemplate, vars); + const modelVal = modelSelect.value; + const apiKey = apiKeyInput.value.trim(); + + runStatus.textContent = 'Running...'; + answerBox.textContent = ''; + btnRun.disabled = true; + + try { + if (modelVal === 'mock' || !apiKey) { + // Simulate delay + await new Promise(r => setTimeout(r, 600)); + let mock = 'This is a mock response.\n\nSystem prompt used:\n' + (systemPrompt || '(empty)'); + if (userQuestion) mock += '\n\nUser question:\n' + userQuestion; + mock += '\n\nVariables injected: ' + JSON.stringify(vars, null, 2); + answerBox.textContent = mock; + } else if (modelVal.startsWith('openai:')) { + const model = modelVal.split(':')[1]; + const messages = []; + if (systemPrompt.trim()) messages.push({ role: 'system', content: systemPrompt }); + messages.push({ role: 'user', content: userQuestion || 'Hello' }); + const res = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + apiKey }, + body: JSON.stringify({ model, messages, temperature: 0.7 }) + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error?.message || 'OpenAI request failed'); + answerBox.textContent = data.choices?.[0]?.message?.content || '(no content)'; + } else if (modelVal.startsWith('anthropic:')) { + const model = modelVal.split(':')[1]; + const body = { + model, + max_tokens: 1024, + messages: [{ role: 'user', content: userQuestion || 'Hello' }] + }; + if (systemPrompt.trim()) body.system = systemPrompt; + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01' + }, + body: JSON.stringify(body) + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error?.message || 'Anthropic request failed'); + const contentBlocks = data.content || []; + const text = contentBlocks.filter(c => c.type === 'text').map(c => c.text).join(''); + answerBox.textContent = text || '(no content)'; + } else { + answerBox.textContent = 'Unknown model.'; + } + } catch (err) { + answerBox.textContent = 'Error: ' + err.message; + } finally { + btnRun.disabled = false; + runStatus.textContent = ''; + } + } + + btnRun.addEventListener('click', runTest); + + // Init + addVarRow('user_name', 'Alice'); + loadStorage(); + updateSyntaxBarSimple(); +})(); +</script> +</body> +</html> ```