Phase 3: apply_events.cfm + process.cfm v2 + кнопка v2 (v1.0.74)
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
<cfsetting showdebugoutput="no" enablecfoutputonly="true">
|
||||
<cfheader name="Content-Type" value="application/json; charset=utf-8">
|
||||
|
||||
<cfparam name="url.contract_id" default="">
|
||||
<cfparam name="url.mode" default="partial">
|
||||
<cfset body = toString(getHttpRequestData().content)>
|
||||
<cfset data = deserializeJSON(body)>
|
||||
<cfset ops = structKeyExists(data, "ops") ? data.ops : []>
|
||||
<cfset supplementId = structKeyExists(data, "supplement_id") ? data.supplement_id : "">
|
||||
|
||||
<cfset result = {ok: false, error: ""}>
|
||||
|
||||
<cftry>
|
||||
<cfif NOT len(url.contract_id)>
|
||||
<cfset result.error = "contract_id required">
|
||||
<cfelseif NOT len(supplementId)>
|
||||
<cfset result.error = "supplement_id required (JSON body)">
|
||||
<cfelse>
|
||||
<cftransaction>
|
||||
<cfset seq = 0>
|
||||
<cfquery name="sq" datasource="baza">
|
||||
SELECT COALESCE(MAX(seq), 0) as m FROM spec_events
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
<cfset seq = sq.m>
|
||||
|
||||
<!--- full_replace: явные DELETE для аудита --->
|
||||
<cfif url.mode EQ "full_replace">
|
||||
<cfquery name="curRows" datasource="baza">
|
||||
SELECT name_hash, name, price, qty, sum, date_start
|
||||
FROM spec_current
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
<cfloop query="curRows">
|
||||
<cfset seq++>
|
||||
<cfquery datasource="baza">
|
||||
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#seq#" cfsqltype="cf_sql_integer">,
|
||||
'DELETE',
|
||||
<cfqueryparam value="#curRows.name_hash#" cfsqltype="cf_sql_varchar">,
|
||||
'full_replace',
|
||||
'applied'
|
||||
)
|
||||
</cfquery>
|
||||
</cfloop>
|
||||
<cfquery datasource="baza">
|
||||
DELETE FROM spec_current WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
</cfif>
|
||||
|
||||
<!--- Применить операции --->
|
||||
<cfset summary = {added: 0, updated: 0, deleted: 0, unresolved: 0}>
|
||||
|
||||
<cfloop array="#ops#" index="op">
|
||||
<cfset seq++>
|
||||
<cfset action = op.action>
|
||||
|
||||
<cfif action EQ "ADD">
|
||||
<cfset nr = op.new_row>
|
||||
<cfset nm = structKeyExists(nr, 'name') ? nr.name : ''>
|
||||
<cfset pr = (structKeyExists(nr, 'price') AND len(nr.price)) ? nr.price : ''>
|
||||
<cfset qt = (structKeyExists(nr, 'qty') AND len(nr.qty)) ? nr.qty : ''>
|
||||
<cfset sm = (structKeyExists(nr, 'sum') AND len(nr.sum)) ? nr.sum : ''>
|
||||
<cfset ds = (structKeyExists(nr, 'date_start') AND len(nr.date_start)) ? nr.date_start : ''>
|
||||
|
||||
<cfquery name="evt" datasource="baza">
|
||||
INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#seq#" cfsqltype="cf_sql_integer">,
|
||||
'ADD',
|
||||
<cfqueryparam value="#serializeJSON(nr)#" cfsqltype="cf_sql_varchar">::jsonb,
|
||||
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
|
||||
'applied'
|
||||
)
|
||||
RETURNING id
|
||||
</cfquery>
|
||||
|
||||
<cfquery datasource="baza">
|
||||
INSERT INTO spec_current (contract_id, name_hash, name, price, qty, sum, date_start, last_event_id)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
md5(lower(trim(<cfqueryparam value="#nm#" cfsqltype="cf_sql_varchar">)) || coalesce(<cfqueryparam value="#ds#" cfsqltype="cf_sql_varchar">, '')),
|
||||
<cfqueryparam value="#nm#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#pr#" cfsqltype="cf_sql_float" null="#len(pr) EQ 0#">,
|
||||
<cfqueryparam value="#qt#" cfsqltype="cf_sql_float" null="#len(qt) EQ 0#">,
|
||||
<cfqueryparam value="#sm#" cfsqltype="cf_sql_float" null="#len(sm) EQ 0#">,
|
||||
<cfqueryparam value="#ds#" cfsqltype="cf_sql_varchar" null="#len(ds) EQ 0#">,
|
||||
<cfqueryparam value="#evt.id#" cfsqltype="cf_sql_varchar">
|
||||
)
|
||||
</cfquery>
|
||||
<cfset summary.added++>
|
||||
|
||||
<cfelseif action EQ "UPDATE">
|
||||
<cfset th = op.target_hash>
|
||||
<cfquery name="old" datasource="baza">
|
||||
SELECT price, qty, sum, date_start FROM spec_current
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
AND name_hash = <cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
<cfif old.recordCount EQ 0>
|
||||
<cfthrow message="UPDATE target not found: #th#">
|
||||
</cfif>
|
||||
<cfset nv = structKeyExists(op, 'new_values') ? op.new_values : {}>
|
||||
<cfset newPrice = structKeyExists(nv, 'price') AND len(nv.price) ? nv.price : old.price>
|
||||
<cfset newQty = structKeyExists(nv, 'qty') AND len(nv.qty) ? nv.qty : old.qty>
|
||||
<cfset newSum = structKeyExists(nv, 'sum') AND len(nv.sum) ? nv.sum : old.sum>
|
||||
<cfset newDate = structKeyExists(nv, 'date_start') AND len(nv.date_start) ? nv.date_start : old.date_start>
|
||||
|
||||
<cfquery name="evt" datasource="baza">
|
||||
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, new_values, comment, status)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#seq#" cfsqltype="cf_sql_integer">,
|
||||
'UPDATE',
|
||||
<cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#serializeJSON(nv)#" cfsqltype="cf_sql_varchar">::jsonb,
|
||||
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
|
||||
'applied'
|
||||
)
|
||||
RETURNING id
|
||||
</cfquery>
|
||||
|
||||
<cfquery datasource="baza">
|
||||
UPDATE spec_current SET
|
||||
price = <cfqueryparam value="#newPrice#" cfsqltype="cf_sql_float" null="#NOT len(newPrice)#">,
|
||||
qty = <cfqueryparam value="#newQty#" cfsqltype="cf_sql_float" null="#NOT len(newQty)#">,
|
||||
sum = <cfqueryparam value="#newSum#" cfsqltype="cf_sql_float" null="#NOT len(newSum)#">,
|
||||
date_start = <cfqueryparam value="#newDate#" cfsqltype="cf_sql_varchar" null="#NOT len(newDate)#">,
|
||||
last_event_id = <cfqueryparam value="#evt.id#" cfsqltype="cf_sql_varchar">,
|
||||
updated_at = now()
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
AND name_hash = <cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
<cfset summary.updated++>
|
||||
|
||||
<cfelseif action EQ "DELETE">
|
||||
<cfset th = op.target_hash>
|
||||
<cfquery datasource="baza">
|
||||
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#seq#" cfsqltype="cf_sql_integer">,
|
||||
'DELETE',
|
||||
<cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
|
||||
'applied'
|
||||
)
|
||||
</cfquery>
|
||||
<cfquery datasource="baza">
|
||||
DELETE FROM spec_current
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
AND name_hash = <cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">
|
||||
</cfquery>
|
||||
<cfset summary.deleted++>
|
||||
|
||||
<cfelseif action EQ "UNRESOLVED">
|
||||
<cfquery datasource="baza">
|
||||
INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status)
|
||||
VALUES (
|
||||
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
|
||||
<cfqueryparam value="#seq#" cfsqltype="cf_sql_integer">,
|
||||
'UNRESOLVED',
|
||||
<cfqueryparam value="#serializeJSON(structKeyExists(op,'new_values') ? op.new_values : {})#" cfsqltype="cf_sql_varchar">::jsonb,
|
||||
<cfqueryparam value="#structKeyExists(op,'reason') ? op.reason : ''#" cfsqltype="cf_sql_varchar">,
|
||||
'pending'
|
||||
)
|
||||
</cfquery>
|
||||
<cfset summary.unresolved++>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
|
||||
<cfset result = {ok: true, contract_id: url.contract_id, supplement_id: supplementId, summary: summary}>
|
||||
</cftransaction>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfset result = {ok: false, error: cfcatch.message, detail: cfcatch.detail}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
|
||||
<cfoutput>#serializeJSON(result)#</cfoutput>
|
||||
@@ -61,7 +61,7 @@
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<img src="/nubes-logo.svg" alt="Nubes">
|
||||
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.72 — Lucee</span></span>
|
||||
<span class="title">Сверка договоров — LLM <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.74 — Lucee</span></span>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
@@ -90,6 +90,10 @@
|
||||
<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить (LLM)
|
||||
</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>
|
||||
|
||||
<details style="margin-top:8px;font-size:12px;">
|
||||
<summary style="cursor:pointer;color:var(--muted);">⚙ Промпт LLM</summary>
|
||||
<textarea id="promptEditor" style="width:100%;height:200px;font-family:monospace;font-size:11px;border:1px solid var(--brand-gray);border-radius:6px;padding:8px;margin-top:4px;"></textarea>
|
||||
@@ -319,9 +323,81 @@ parseBtn.addEventListener('click', async function() {
|
||||
var llmBtn = document.getElementById('llmBtn');
|
||||
llmBtn.style.display = 'flex';
|
||||
llmBtn.disabled = false;
|
||||
var llmBtnV2 = document.getElementById('llmBtnV2');
|
||||
llmBtnV2.style.display = 'flex';
|
||||
llmBtnV2.disabled = false;
|
||||
lucide.createIcons();
|
||||
});
|
||||
|
||||
// ── LLM v2: Event Sourcing ─────────────────────────────────
|
||||
document.getElementById('llmBtnV2').addEventListener('click', function() {
|
||||
if (!contractId) return;
|
||||
var btn = this;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner"></span> LLM v2...';
|
||||
|
||||
var diffCard = document.getElementById('diffCard');
|
||||
var diffBody = document.getElementById('diffBody');
|
||||
var diffStatus = document.getElementById('diffStatus');
|
||||
diffCard.style.display = 'block';
|
||||
diffBody.innerHTML = '';
|
||||
diffStatus.textContent = '⏳ 0.0с';
|
||||
|
||||
var llmStart = Date.now();
|
||||
var timerInterval = setInterval(function() {
|
||||
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
||||
}, 200);
|
||||
|
||||
var es = new EventSource('/process.cfm?contract_id=' + contractId + '&v=2');
|
||||
|
||||
es.onmessage = function(e) {
|
||||
var d = JSON.parse(e.data);
|
||||
|
||||
if (d.type === 'extract_start') {
|
||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--muted);margin-top:2px;">⏳ ' + d.filename + ' → LLM...</div>';
|
||||
}
|
||||
else if (d.type === 'llm_done') {
|
||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--green);">✓ ' + d.filename + ': ' + d.ops_count + ' операций, ' + d.mode + ' (' + d.time_s + 'с)</div>';
|
||||
}
|
||||
else if (d.type === 'applied') {
|
||||
var s = d.summary || {};
|
||||
diffBody.innerHTML += '<div style="font-size:12px;margin-left:8px;"> +' + (s.added||0) + ' ~' + (s.updated||0) + ' -' + (s.deleted||0) + ' ?' + (s.unresolved||0) + '</div>';
|
||||
}
|
||||
else if (d.type === 'extract_error' || d.type === 'apply_error') {
|
||||
diffBody.innerHTML += '<div style="font-size:12px;color:var(--destructive);">✗ ' + d.filename + ': ' + d.error + '</div>';
|
||||
}
|
||||
else if (d.type === 'done') {
|
||||
clearInterval(timerInterval);
|
||||
es.close();
|
||||
diffStatus.textContent = '✓ ' + d.total_time_s + 'с';
|
||||
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
|
||||
if (d.total_unresolved > 0) {
|
||||
diffBody.innerHTML += '<div style="margin-top:8px;color:#eab308;">⚠ ' + d.total_unresolved + ' неразрешённых строк — <a href="/resolve.cfm?contract_id=' + contractId + '">разобрать</a></div>';
|
||||
}
|
||||
document.getElementById('chatCard').style.display = 'block';
|
||||
lucide.createIcons();
|
||||
}
|
||||
else if (d.type === 'error') {
|
||||
clearInterval(timerInterval);
|
||||
es.close();
|
||||
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.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
es.onerror = function() {
|
||||
clearInterval(timerInterval);
|
||||
es.close();
|
||||
if (diffBody.innerHTML === '') {
|
||||
diffBody.innerHTML = '<div style="color:var(--destructive);">✗ Ошибка соединения</div>';
|
||||
}
|
||||
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Сравнить v2';
|
||||
btn.disabled = false;
|
||||
lucide.createIcons();
|
||||
};
|
||||
});
|
||||
|
||||
// ── Обновить supplement_id/type для radio ──────────────────
|
||||
async function refreshSupps() {
|
||||
if (!contractId) { console.log('refreshSupps: no contractId'); return; }
|
||||
|
||||
@@ -6,11 +6,17 @@
|
||||
<cfheader name="Access-Control-Allow-Origin" value="*">
|
||||
|
||||
<cfparam name="url.contract_id" default="">
|
||||
<cfparam name="url.v" default="1">
|
||||
|
||||
<cfoutput>: ok
|
||||
|
||||
</cfoutput><cfflush>
|
||||
|
||||
<cfif url.v EQ "2">
|
||||
<cfinclude template="process_v2.cfm">
|
||||
<cfabort>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<cfif NOT len(url.contract_id)>
|
||||
<cfoutput>data: #serializeJSON({type:"error",message:"contract_id required"})#
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
<!--- process_v2.cfm — v2 pipeline: LLM-интерпретация ДС → apply_events --->
|
||||
<cftry>
|
||||
<cfquery name="supps" datasource="baza">
|
||||
SELECT s.id as supp_id, s.type, d.id as doc_id, d.filename, d.elements_json
|
||||
FROM supplements s
|
||||
JOIN documents d ON s.document_id = d.id
|
||||
WHERE s.contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
AND d.elements_json IS NOT NULL
|
||||
ORDER BY s.created_at
|
||||
</cfquery>
|
||||
|
||||
<cfif supps.recordCount EQ 0>
|
||||
<cfoutput>data: #serializeJSON({type:"error",message:"Нет распарсенных файлов"})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
<cfabort>
|
||||
</cfif>
|
||||
|
||||
<cfset totalUnresolved = 0>
|
||||
<cfset llmStart = getTickCount()>
|
||||
|
||||
<cfloop query="supps">
|
||||
<cfset sid = supps.supp_id>
|
||||
|
||||
<!--- Текущая спецификация из spec_current --->
|
||||
<cfquery name="curSpec" datasource="baza">
|
||||
SELECT name_hash, name, price, qty, sum, date_start
|
||||
FROM spec_current
|
||||
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
|
||||
ORDER BY name
|
||||
</cfquery>
|
||||
<cfset currentSpec = []>
|
||||
<cfloop query="curSpec">
|
||||
<cfset arrayAppend(currentSpec, {
|
||||
hash: curSpec.name_hash,
|
||||
name: curSpec.name,
|
||||
price: curSpec.price,
|
||||
qty: curSpec.qty,
|
||||
sum: curSpec.sum,
|
||||
date_start: curSpec.date_start
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<!--- textify elements_json → doc_text --->
|
||||
<cfset elements = deserializeJSON(supps.elements_json)>
|
||||
<cfset textLines = []>
|
||||
<cfloop array="#elements#" index="el">
|
||||
<cfif el.type EQ "paragraph">
|
||||
<cfset prefix = len(el.style) ? "[#el.style#] " : "">
|
||||
<cfset arrayAppend(textLines, prefix & el.text)>
|
||||
<cfelseif el.type EQ "table">
|
||||
<cfset rows = el.rows>
|
||||
<cfif arrayLen(rows) GT 0>
|
||||
<cfset ncols = arrayLen(rows[1])>
|
||||
<cfset arrayAppend(textLines, "--- Таблица (" & arrayLen(rows) & "×" & ncols & ") ---")>
|
||||
<cfloop array="#rows#" index="row">
|
||||
<cfset cells = []>
|
||||
<cfloop from="1" to="#ncols#" index="ci">
|
||||
<cfset val = "">
|
||||
<cfif ci LTE arrayLen(row)>
|
||||
<cfset val = replace(replace(toString(row[ci]), chr(10), " ", "ALL"), "|", "\|", "ALL")>
|
||||
</cfif>
|
||||
<cfset arrayAppend(cells, val)>
|
||||
</cfloop>
|
||||
<cfset arrayAppend(textLines, "| " & arrayToList(cells, " | ") & " |")>
|
||||
</cfloop>
|
||||
<cfset arrayAppend(textLines, "")>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<cfset docText = arrayToList(textLines, chr(10))>
|
||||
|
||||
<!--- SSE: начинаем LLM --->
|
||||
<cfoutput>data: #serializeJSON({type:"extract_start",supplement_id:sid,filename:supps.filename})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
|
||||
<!--- Вызов ВМ /llm-ops --->
|
||||
<cfset t0 = getTickCount()>
|
||||
<cfset llmPayload = {
|
||||
contract_id: url.contract_id,
|
||||
supplement_id: sid,
|
||||
current_spec: currentSpec,
|
||||
doc_text: docText
|
||||
}>
|
||||
|
||||
<cfhttp url="https://contracts.kube5s.ru/llm-ops" method="POST" timeout="130">
|
||||
<cfhttpparam type="header" name="Content-Type" value="application/json">
|
||||
<cfhttpparam type="body" value="#serializeJSON(llmPayload)#">
|
||||
</cfhttp>
|
||||
|
||||
<cfset elapsed = int((getTickCount() - t0) / 1000 * 10 + 0.5) / 10>
|
||||
|
||||
<cfif NOT (cfhttp.statusCode EQ 200 OR left(cfhttp.statusCode, 3) EQ "200")>
|
||||
<cfoutput>data: #serializeJSON({type:"extract_error",supplement_id:sid,filename:supps.filename,error:"LLM HTTP #cfhttp.statusCode#",time_s:elapsed})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
<cfcontinue>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<cfset llmResult = deserializeJSON(cfhttp.fileContent)>
|
||||
<cfset ops = structKeyExists(llmResult, "ops") ? llmResult.ops : []>
|
||||
<cfset mode = structKeyExists(llmResult, "mode") ? llmResult.mode : "partial">
|
||||
|
||||
<cfoutput>data: #serializeJSON({type:"llm_done",supplement_id:sid,filename:supps.filename,ops_count:arrayLen(ops),mode:mode,time_s:elapsed})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
|
||||
<!--- Применить операции --->
|
||||
<cfset applyPayload = {
|
||||
supplement_id: sid,
|
||||
ops: ops
|
||||
}>
|
||||
|
||||
<cfhttp url="https://contractor.luceek8s.dev.nubes.ru/apply_events.cfm?contract_id=#url.contract_id#&mode=#mode#" method="POST" timeout="30">
|
||||
<cfhttpparam type="header" name="Content-Type" value="application/json">
|
||||
<cfhttpparam type="body" value="#serializeJSON(applyPayload)#">
|
||||
</cfhttp>
|
||||
|
||||
<cfif cfhttp.statusCode EQ 200 OR left(cfhttp.statusCode, 3) EQ "200">
|
||||
<cfset applyResult = deserializeJSON(cfhttp.fileContent)>
|
||||
<cfif structKeyExists(applyResult, "summary")>
|
||||
<cfset totalUnresolved += applyResult.summary.unresolved>
|
||||
<cfoutput>data: #serializeJSON({type:"applied",supplement_id:sid,summary:applyResult.summary})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
<cfelse>
|
||||
<cfoutput>data: #serializeJSON({type:"apply_error",supplement_id:sid,error:structKeyExists(applyResult,'error')?applyResult.error:'unknown'})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<cfoutput>data: #serializeJSON({type:"apply_error",supplement_id:sid,error:"apply HTTP #cfhttp.statusCode#"})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
</cfif>
|
||||
|
||||
<cfcatch>
|
||||
<cfoutput>data: #serializeJSON({type:"extract_error",supplement_id:sid,filename:supps.filename,error:"JSON parse: " & cfcatch.message,time_s:elapsed})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cfloop>
|
||||
|
||||
<!--- Финальное событие --->
|
||||
<cfset totalTime = int((getTickCount() - llmStart) / 1000 * 10 + 0.5) / 10>
|
||||
<cfoutput>data: #serializeJSON({type:"done",total_time_s:totalTime,total_unresolved:totalUnresolved})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
|
||||
<cfcatch>
|
||||
<cfoutput>data: #serializeJSON({type:"error",message:cfcatch.message,detail:cfcatch.detail})#
|
||||
|
||||
</cfoutput><cfflush>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
Reference in New Issue
Block a user