v1.0.89: убрал v1, оставил только Event Sourcing как Сравнить
This commit is contained in:
@@ -71,7 +71,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="/nubes-logo.svg" alt="Nubes">
|
<img src="/nubes-logo.svg" alt="Nubes">
|
||||||
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.88 — Lucee</span></span>
|
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.89 — Lucee</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -92,12 +92,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" id="llmBtn" style="width:100%;justify-content:center;margin-top:8px;display:none;">
|
<button class="btn btn-primary" id="llmBtn" style="width:100%;justify-content:center;margin-top:8px;display:none;background:var(--green);border-color:var(--green);">
|
||||||
<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить (LLM)
|
<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" id="llmBtnV2" style="width:100%;justify-content:center;margin-top:8px;display:none;background:var(--green);border-color:var(--green);">
|
|
||||||
<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить v2 (Event Sourcing)
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<details style="margin-top:8px;font-size:12px;">
|
<details style="margin-top:8px;font-size:12px;">
|
||||||
@@ -269,9 +265,6 @@ fileInput.addEventListener('change', async function() {
|
|||||||
var llmBtn = document.getElementById('llmBtn');
|
var llmBtn = document.getElementById('llmBtn');
|
||||||
llmBtn.style.display = 'flex';
|
llmBtn.style.display = 'flex';
|
||||||
llmBtn.disabled = false;
|
llmBtn.disabled = false;
|
||||||
var llmBtnV2 = document.getElementById('llmBtnV2');
|
|
||||||
llmBtnV2.style.display = 'flex';
|
|
||||||
llmBtnV2.disabled = false;
|
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -325,12 +318,12 @@ function uploadFile(file, onProgress) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── LLM v2: Event Sourcing ─────────────────────────────────
|
// ── LLM: Сравнить (Event Sourcing) ────────────────────────
|
||||||
document.getElementById('llmBtnV2').addEventListener('click', function() {
|
document.getElementById('llmBtn').addEventListener('click', function() {
|
||||||
if (!contractId) return;
|
if (!contractId) return;
|
||||||
var btn = this;
|
var btn = this;
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.innerHTML = '<span class="spinner"></span> LLM v2...';
|
btn.innerHTML = '<span class="spinner"></span> Сравнение...';
|
||||||
|
|
||||||
var diffCard = document.getElementById('diffCard');
|
var diffCard = document.getElementById('diffCard');
|
||||||
var diffBody = document.getElementById('diffBody');
|
var diffBody = document.getElementById('diffBody');
|
||||||
@@ -377,7 +370,7 @@ document.getElementById('llmBtnV2').addEventListener('click', function() {
|
|||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
es.close();
|
es.close();
|
||||||
diffBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + d.message + '</div>';
|
diffBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + d.message + '</div>';
|
||||||
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить v2';
|
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить';
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -425,106 +418,6 @@ function setInitial(suppId) {
|
|||||||
}
|
}
|
||||||
window.setInitial = setInitial;
|
window.setInitial = setInitial;
|
||||||
|
|
||||||
// ── LLM: Сравнить ────────────────────────────────────────────
|
|
||||||
document.getElementById('llmBtn').addEventListener('click', async function() {
|
|
||||||
if (!contractId) return;
|
|
||||||
var btn = this;
|
|
||||||
btn.disabled = true;
|
|
||||||
btn.innerHTML = '<span class="spinner"></span> LLM-анализ...';
|
|
||||||
|
|
||||||
// Сохранить промпт на сервер (ДОЖДАТЬСЯ)
|
|
||||||
var p = promptEditor.value.trim() || DEFAULT_PROMPT;
|
|
||||||
if (contractId) {
|
|
||||||
await fetch('/prompt.cfm?contract_id=' + contractId, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
body: JSON.stringify({prompt: p})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var diffCard = document.getElementById('diffCard');
|
|
||||||
var diffBody = document.getElementById('diffBody');
|
|
||||||
var diffStatus = document.getElementById('diffStatus');
|
|
||||||
diffCard.style.display = 'block';
|
|
||||||
diffBody.innerHTML = '⏳ LLM-извлечение...';
|
|
||||||
diffStatus.textContent = '⏳ 0.0с';
|
|
||||||
|
|
||||||
var llmStart = Date.now();
|
|
||||||
var timerInterval = setInterval(function() {
|
|
||||||
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
// 1. Extract for each supplement
|
|
||||||
try {
|
|
||||||
var resp = await fetch('/api.cfm?action=query&sql=' + encodeURIComponent("SELECT id, type FROM supplements WHERE contract_id='" + contractId + "' ORDER BY created_at"));
|
|
||||||
var data = await resp.json();
|
|
||||||
if (data.OK && data.ROWS) {
|
|
||||||
for (var i = 0; i < data.ROWS.length; i++) {
|
|
||||||
try {
|
|
||||||
var er = await fetch('/extractor.cfm?supplement_id=' + data.ROWS[i].id);
|
|
||||||
var ed = await er.json();
|
|
||||||
if (ed.OK) {
|
|
||||||
var info = (ed.SKIPPED ? '⏭ ' : '✓ ') + (data.ROWS[i].type || '') + ': ' + ed.ROWS_SAVED + ' строк';
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;color:' + (ed.SKIPPED ? 'var(--muted)' : 'var(--green)') + ';">' + info + '</div>';
|
|
||||||
} else {
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--destructive);">✗ ' + (ed.ERROR||'') + '</div>';
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// 2. Differ
|
|
||||||
diffBody.innerHTML += '<div style="margin-top:8px;font-weight:600;">📊 Сравнение:</div>';
|
|
||||||
try {
|
|
||||||
var resp = await fetch('/differ.cfm?contract_id=' + contractId);
|
|
||||||
var data = await resp.json();
|
|
||||||
if (data.OK && data.ALL_CHANGES) {
|
|
||||||
var all = data.ALL_CHANGES;
|
|
||||||
diffBody.innerHTML = '';
|
|
||||||
all.forEach(function(ch) {
|
|
||||||
var s = ch.SUMMARY || {};
|
|
||||||
diffBody.innerHTML += '<div style="font-weight:600;margin-top:12px;">📄 Допник (' + ch.TYPE + ')</div>';
|
|
||||||
diffBody.innerHTML += '<div style="font-size:12px;margin-left:8px;"><span style="color:var(--green);">+' + (s.ADDED||0) + '</span> <span style="color:#eab308;">~' + (s.CHANGED||0) + '</span> <span style="color:var(--destructive);">-' + (s.DELETED||0) + '</span></div>';
|
|
||||||
diffStatus.textContent = 'изм:' + (s.CHANGED||0) + ' +' + (s.ADDED||0) + ' -' + (s.DELETED||0);
|
|
||||||
|
|
||||||
var changes = ch.CHANGES || [];
|
|
||||||
if (changes.length === 0) {
|
|
||||||
diffBody.innerHTML += '<div style="color:var(--muted);font-size:12px;">Нет изменений</div>';
|
|
||||||
} else {
|
|
||||||
var tbl = '<div class="table-wrap" style="margin-top:4px;"><table><thead><tr><th>№</th><th>Услуга</th><th>Цена</th><th>Объём</th><th>Сумма</th><th>Начало</th></tr></thead><tbody>';
|
|
||||||
changes.forEach(function(c) {
|
|
||||||
var cls = c.CHANGE_TYPE === 'added' ? 'diff-added' : c.CHANGE_TYPE === 'deleted' ? 'diff-deleted' : c.CHANGE_TYPE === 'changed' ? 'diff-changed' : '';
|
|
||||||
var vals = c.NEW_VALUES && Object.keys(c.NEW_VALUES).length > 0 ? c.NEW_VALUES : (c.OLD_VALUES || {});
|
|
||||||
tbl += '<tr class="' + cls + '"><td>' + (c.ROW_NUM||'') + '</td><td>' + (vals.NAME||'') + '</td><td class="num-cell">' + _dc(c,'PRICE') + '</td><td class="num-cell">' + _dc(c,'QTY') + '</td><td class="num-cell">' + _dc(c,'SUM') + '</td><td>' + (vals.DATE_START||'') + '</td></tr>';
|
|
||||||
});
|
|
||||||
tbl += '</tbody></table></div>';
|
|
||||||
diffBody.innerHTML += tbl;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
diffBody.innerHTML += '<div style="color:var(--destructive);">✗ ' + (data.ERROR||'') + '</div>';
|
|
||||||
}
|
|
||||||
} catch(e) { diffBody.innerHTML += '<div style="color:var(--destructive);">✗ ' + e.message + '</div>'; }
|
|
||||||
|
|
||||||
clearInterval(timerInterval);
|
|
||||||
diffStatus.textContent = '✓ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
|
||||||
|
|
||||||
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
|
|
||||||
document.getElementById('chatCard').style.display = 'block';
|
|
||||||
lucide.createIcons();
|
|
||||||
});
|
|
||||||
|
|
||||||
function _dc(c, field) {
|
|
||||||
if (c.CHANGE_TYPE === 'changed' && c.OLD_VALUES && c.NEW_VALUES) {
|
|
||||||
var ov = (c.OLD_VALUES||{})[field], nv = (c.NEW_VALUES||{})[field];
|
|
||||||
if (ov !== nv && ov !== undefined)
|
|
||||||
return '<span class="diff-field-old">' + (ov||'—') + '</span> → <span class="diff-field-new">' + (nv||'—') + '</span>';
|
|
||||||
}
|
|
||||||
var v = ((c.NEW_VALUES || c.OLD_VALUES || {})[field]);
|
|
||||||
return v != null ? v : '—';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Чат ──────────────────────────────────────────────────────
|
// ── Чат ──────────────────────────────────────────────────────
|
||||||
document.getElementById('chatSend').addEventListener('click', function() {
|
document.getElementById('chatSend').addEventListener('click', function() {
|
||||||
var input = document.getElementById('chatInput');
|
var input = document.getElementById('chatInput');
|
||||||
|
|||||||
Reference in New Issue
Block a user