Files
contractor/extractor.cfm
T

183 lines
9.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<cfsetting showdebugoutput="no" enablecfoutputonly="true">
<cfheader name="Content-Type" value="application/json; charset=utf-8">
<cfparam name="url.supplement_id" default="">
<cfset result = {ok: false, error: ""}>
<cftry>
<cfif NOT len(url.supplement_id)>
<cfset result.error = "supplement_id required">
<cfelse>
<!--- Читаем supplement + document --->
<cfquery name="sup" datasource="baza">
SELECT s.id, s.contract_id, d.elements_json, d.parsed_text
FROM supplements s
JOIN documents d ON s.document_id = d.id
WHERE s.id = <cfqueryparam value="#url.supplement_id#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfif sup.recordCount EQ 0>
<cfset result.error = "supplement not found">
<cfelse>
<!--- textify: elements → текст --->
<cfset elements = deserializeJSON(sup.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 parsedText = arrayToList(textLines, chr(10))>
<!--- Промпт --->
<cfset prompt = 'Ты — анализатор договоров. Ниже текст спецификации услуг из договора облачного провайдера.
Найди ВСЕ таблицы со списком услуг. Извлеки каждую строку в JSON-массив.
Для каждой строки верни:
- row_num: номер по порядку (целое число)
- name: полное наименование услуги (весь текст из ячейки)
- price: цена за единицу в рублях (число, из колонки "Цена")
- qty: количество/объём (число, из колонки "Объем")
- sum: итоговая сумма (число, из колонки "Сумма")
- date_start: дата начала оказания (строка YYYY-MM-DD)
ПРАВИЛА:
1. Пропускай итоговые строки ("Итого...") и строки с подписями.
2. Если ячейка пустая — ставь null.
3. Если не можешь определить значение — ставь null и добавь в unresolved.
4. Верни ТОЛЬКО JSON, без пояснений.
Пример ответа:
{"rows":[{"row_num":1,"name":"Аренда стойко-места","price":213905.44,"qty":3,"sum":641716.32,"date_start":"2026-04-01"}],"unresolved":[]}
Текст документа:
---
#parsedText#
---'>
<!--- Вызов LLM --->
<cfset apiKey = "sk-ucI5YvOticoOQ9Kuj5K9mQ">
<cfset llmPayload = {
"model": "gpt-oss-120b",
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 8000,
"temperature": 0.1
}>
<cfhttp url="https://api.aillm.ru/v1/chat/completions" method="POST" timeout="120">
<cfhttpparam type="header" name="Authorization" value="Bearer #apiKey#">
<cfhttpparam type="header" name="Content-Type" value="application/json">
<cfhttpparam type="body" value="#serializeJSON(llmPayload)#">
</cfhttp>
<cfif cfhttp.statusCode EQ 200 OR left(cfhttp.statusCode, 3) EQ "200">
<cfset llmResp = deserializeJSON(cfhttp.fileContent)>
<cfset llmText = llmResp.choices[1].message.content>
<!--- Вытаскиваем JSON из ответа --->
<cfset jsonText = llmText>
<cfset p1 = find("```json", jsonText)>
<cfif p1 GT 0>
<cfset jsonText = mid(jsonText, p1 + 7, len(jsonText))>
<cfset p2 = find("```", jsonText)>
<cfif p2 GT 0>
<cfset jsonText = left(jsonText, p2 - 1)>
</cfif>
<cfelse>
<cfset p1 = find("{", jsonText)>
<cfset p2 = find("}", reverse(jsonText))>
<cfif p1 GT 0 AND p2 GT 0>
<cfset jsonText = mid(jsonText, p1, len(jsonText) - p2 - p1 + 2)>
</cfif>
</cfif>
<cfset jsonText = trim(jsonText)>
<cftry>
<cfset extracted = deserializeJSON(jsonText)>
<cfset rows = []>
<cfif structKeyExists(extracted, "rows") AND isArray(extracted.rows)>
<cfset rows = extracted.rows>
</cfif>
<cfset unresolved = []>
<cfif structKeyExists(extracted, "unresolved") AND isArray(extracted.unresolved)>
<cfset unresolved = extracted.unresolved>
</cfif>
<!--- Сохраняем spec_rows --->
<cfset savedRows = []>
<cfloop array="#rows#" index="row">
<cftry>
<cfset rn = structKeyExists(row, 'row_num') ? row.row_num : 0>
<cfset nm = structKeyExists(row, 'name') ? row.name : ''>
<cfset pr = (structKeyExists(row, 'price') AND len(row.price)) ? row.price : ''>
<cfset qt = (structKeyExists(row, 'qty') AND len(row.qty)) ? row.qty : ''>
<cfset sm = (structKeyExists(row, 'sum') AND len(row.sum)) ? row.sum : ''>
<cfset ds = (structKeyExists(row, 'date_start') AND len(row.date_start)) ? row.date_start : ''>
<cfquery datasource="baza">
INSERT INTO spec_rows (supplement_id, row_num, name, price, qty, sum, date_start)
VALUES (
<cfqueryparam value="#sup.id#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#rn#" cfsqltype="cf_sql_integer">,
<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#">
)
</cfquery>
<cfset arrayAppend(savedRows, row)>
<cfcatch>
<cfset arrayAppend(unresolved, {row: row, error: cfcatch.message})>
</cfcatch>
</cftry>
</cfloop>
<cfset result = {
ok: true,
supplement_id: sup.id,
contract_id: sup.contract_id,
rows_saved: arrayLen(savedRows),
unresolved: arrayLen(unresolved),
model: structKeyExists(llmResp, "model") ? llmResp.model : "gpt-oss-120b"
}>
<cfcatch>
<cfset result = {ok: false, error: "JSON parse: " & cfcatch.message, llm_text: left(llmText, 500)}>
</cfcatch>
</cftry>
<cfelse>
<cfset result = {ok: false, error: "LLM HTTP #cfhttp.statusCode#: #left(cfhttp.fileContent, 500)#"}>
</cfif>
</cfif>
</cfif>
<cfcatch>
<cfset result = {ok: false, error: cfcatch.message, detail: cfcatch.detail}>
</cfcatch>
</cftry>
<cfoutput>#serializeJSON(result)#</cfoutput>