Phase 3: apply_events.cfm + process.cfm v2 + кнопка v2 (v1.0.74)

This commit is contained in:
2026-06-20 10:31:46 +04:00
parent 7f84c86633
commit 4e20b7760e
4 changed files with 429 additions and 1 deletions
+158
View File
@@ -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>