v1.0.114: source_document_id + raw_llm_response in spec_events — full Opus provenance

This commit is contained in:
2026-06-23 07:14:34 +04:00
parent b75b206bfe
commit bd45559b36
3 changed files with 50 additions and 18 deletions
+39 -13
View File
@@ -8,14 +8,28 @@
<cfset ops = structKeyExists(data, "ops") ? data.ops : []> <cfset ops = structKeyExists(data, "ops") ? data.ops : []>
<cfset supplementId = structKeyExists(data, "supplement_id") ? data.supplement_id : ""> <cfset supplementId = structKeyExists(data, "supplement_id") ? data.supplement_id : "">
<cfset promptId = structKeyExists(data, "prompt_id") AND len(data.prompt_id) ? data.prompt_id : ""> <cfset promptId = structKeyExists(data, "prompt_id") AND len(data.prompt_id) ? data.prompt_id : "">
<cfset documentId = structKeyExists(data, "document_id") AND len(data.document_id) ? data.document_id : "">
<cfset rawResponse = structKeyExists(data, "raw_llm_response") ? serializeJSON(data.raw_llm_response) : "">
<!--- ALTER TABLE: добавить prompt_version если ещё нет ---> <!--- ALTER TABLE: provenance-колонки (Opus round 2) --->
<cftry> <cftry>
<cfquery datasource="baza"> <cfquery datasource="baza">
ALTER TABLE spec_events ADD COLUMN IF NOT EXISTS prompt_version UUID ALTER TABLE spec_events ADD COLUMN IF NOT EXISTS prompt_version UUID
</cfquery> </cfquery>
<cfcatch></cfcatch> <cfcatch></cfcatch>
</cftry> </cftry>
<cftry>
<cfquery datasource="baza">
ALTER TABLE spec_events ADD COLUMN IF NOT EXISTS source_document_id UUID
</cfquery>
<cfcatch></cfcatch>
</cftry>
<cftry>
<cfquery datasource="baza">
ALTER TABLE spec_events ADD COLUMN IF NOT EXISTS raw_llm_response JSONB
</cfquery>
<cfcatch></cfcatch>
</cftry>
<cfset result = {ok: false, error: ""}> <cfset result = {ok: false, error: ""}>
@@ -53,7 +67,7 @@
<!--- full_replace: явные DELETE для аудита (массовый INSERT) ---> <!--- full_replace: явные DELETE для аудита (массовый INSERT) --->
<cfif url.mode EQ "full_replace"> <cfif url.mode EQ "full_replace">
<cfquery datasource="baza"> <cfquery datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status, prompt_version, source_document_id, raw_llm_response)
SELECT SELECT
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -62,7 +76,9 @@
name_hash, name_hash,
'full_replace', 'full_replace',
'applied', 'applied',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
FROM spec_current FROM spec_current
WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar"> WHERE contract_id = <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">
</cfquery> </cfquery>
@@ -111,7 +127,7 @@
<cfset ds = (structKeyExists(nr, 'date_start') AND len(nr.date_start)) ? nr.date_start : ''> <cfset ds = (structKeyExists(nr, 'date_start') AND len(nr.date_start)) ? nr.date_start : ''>
<cfquery name="evt" datasource="baza"> <cfquery name="evt" datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version, source_document_id, raw_llm_response)
VALUES ( VALUES (
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -120,7 +136,9 @@
<cfqueryparam value="#serializeJSON(nr)#" cfsqltype="cf_sql_varchar">::jsonb, <cfqueryparam value="#serializeJSON(nr)#" cfsqltype="cf_sql_varchar">::jsonb,
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
'applied', 'applied',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
) )
RETURNING id RETURNING id
</cfquery> </cfquery>
@@ -145,7 +163,7 @@
<cfif NOT structKeyExists(oldValues, th)> <cfif NOT structKeyExists(oldValues, th)>
<!--- target_hash не найден — создаём UNRESOLVED вместо падения ---> <!--- target_hash не найден — создаём UNRESOLVED вместо падения --->
<cfquery datasource="baza"> <cfquery datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version, source_document_id, raw_llm_response)
VALUES ( VALUES (
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -154,7 +172,9 @@
<cfqueryparam value="#serializeJSON(structKeyExists(op,'new_values') ? op.new_values : {})#" cfsqltype="cf_sql_varchar">::jsonb, <cfqueryparam value="#serializeJSON(structKeyExists(op,'new_values') ? op.new_values : {})#" cfsqltype="cf_sql_varchar">::jsonb,
<cfqueryparam value="UPDATE target_hash not found: #th#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="UPDATE target_hash not found: #th#" cfsqltype="cf_sql_varchar">,
'pending', 'pending',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
) )
</cfquery> </cfquery>
<cfset summary.unresolved++> <cfset summary.unresolved++>
@@ -167,7 +187,7 @@
<cfset newDate = structKeyExists(nv, 'date_start') AND len(nv.date_start) ? nv.date_start : old.date_start> <cfset newDate = structKeyExists(nv, 'date_start') AND len(nv.date_start) ? nv.date_start : old.date_start>
<cfquery name="evt" datasource="baza"> <cfquery name="evt" datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, new_values, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, new_values, comment, status, prompt_version, source_document_id, raw_llm_response)
VALUES ( VALUES (
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -177,7 +197,9 @@
<cfqueryparam value="#serializeJSON(nv)#" cfsqltype="cf_sql_varchar">::jsonb, <cfqueryparam value="#serializeJSON(nv)#" cfsqltype="cf_sql_varchar">::jsonb,
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
'applied', 'applied',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
) )
RETURNING id RETURNING id
</cfquery> </cfquery>
@@ -199,7 +221,7 @@
<cfelseif action EQ "DELETE"> <cfelseif action EQ "DELETE">
<cfset th = op.target_hash> <cfset th = op.target_hash>
<cfquery datasource="baza"> <cfquery datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, target_hash, comment, status, prompt_version, source_document_id, raw_llm_response)
VALUES ( VALUES (
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -208,7 +230,9 @@
<cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#th#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#structKeyExists(op,'comment') ? op.comment : ''#" cfsqltype="cf_sql_varchar">,
'applied', 'applied',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
) )
</cfquery> </cfquery>
<cfquery datasource="baza"> <cfquery datasource="baza">
@@ -220,7 +244,7 @@
<cfelseif action EQ "UNRESOLVED"> <cfelseif action EQ "UNRESOLVED">
<cfquery datasource="baza"> <cfquery datasource="baza">
INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version) INSERT INTO spec_events (contract_id, supplement_id, seq, action, new_values, comment, status, prompt_version, source_document_id, raw_llm_response)
VALUES ( VALUES (
<cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#url.contract_id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">, <cfqueryparam value="#supplementId#" cfsqltype="cf_sql_varchar">,
@@ -229,7 +253,9 @@
<cfqueryparam value="#serializeJSON(structKeyExists(op,'new_values') ? op.new_values : {})#" cfsqltype="cf_sql_varchar">::jsonb, <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">, <cfqueryparam value="#structKeyExists(op,'reason') ? op.reason : ''#" cfsqltype="cf_sql_varchar">,
'pending', 'pending',
<cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif> <cfif len(promptId)><cfqueryparam value="#promptId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(documentId)><cfqueryparam value="#documentId#" cfsqltype="cf_sql_varchar">::uuid<cfelse>NULL</cfif>,
<cfif len(rawResponse)><cfqueryparam value="#rawResponse#" cfsqltype="cf_sql_varchar">::jsonb<cfelse>NULL</cfif>
) )
</cfquery> </cfquery>
<cfset summary.unresolved++> <cfset summary.unresolved++>
+10 -4
View File
@@ -113,9 +113,9 @@ class Handler(BaseHTTPRequestHandler):
self._sse({"type": "error", "message": f"reset failed: HTTP {r.status_code}"}) self._sse({"type": "error", "message": f"reset failed: HTTP {r.status_code}"})
return return
# 1. Список supplements # 1. Список supplements (с document_id для provenance)
supps = self._lucee_query( supps = self._lucee_query(
f"SELECT s.id, s.type, d.filename FROM supplements s " f"SELECT s.id, s.type, d.filename, d.id as document_id FROM supplements s "
f"JOIN documents d ON s.document_id=d.id " f"JOIN documents d ON s.document_id=d.id "
f"WHERE s.contract_id='{cid}' AND d.elements_json IS NOT NULL ORDER BY s.created_at" f"WHERE s.contract_id='{cid}' AND d.elements_json IS NOT NULL ORDER BY s.created_at"
) )
@@ -234,10 +234,16 @@ class Handler(BaseHTTPRequestHandler):
self._sse({"type": "llm_done", "supplement_id": sid, "filename": s["filename"], self._sse({"type": "llm_done", "supplement_id": sid, "filename": s["filename"],
"ops_count": len(ops), "mode": mode, "time_s": elapsed}) "ops_count": len(ops), "mode": mode, "time_s": elapsed})
# Применить # Применить (с provenance: document_id, prompt_id, raw_llm_response)
apply_resp = httpx.post( apply_resp = httpx.post(
f"{LUCEE_URL}/apply_events.cfm?contract_id={cid}&mode={mode}", f"{LUCEE_URL}/apply_events.cfm?contract_id={cid}&mode={mode}",
json={"supplement_id": sid, "ops": ops, "prompt_id": prompt_id}, timeout=30 json={
"supplement_id": sid,
"document_id": s.get("document_id", ""),
"ops": ops,
"prompt_id": prompt_id,
"raw_llm_response": llm_result
}, timeout=30
) )
if apply_resp.status_code == 200: if apply_resp.status_code == 200:
ar = apply_resp.json() ar = apply_resp.json()
+1 -1
View File
@@ -75,7 +75,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 AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.113 — Lucee</span></span> <span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.114 — Lucee</span></span>
</div> </div>
<div class="content"> <div class="content">