Carry OCR and missing-source warnings into analysis readiness

This commit is contained in:
2026-09-06 09:45:33 +03:00
parent 49965a2688
commit f073438922
12 changed files with 75 additions and 21 deletions
+3 -3
View File
@@ -317,9 +317,9 @@ function kesikJsonOnar(s) {
}
/** Zinciri sırayla dener; ilk başarılı sonucu döndürür. */
export async function analyzeSpec({ text, fileName, tender, chain, systemOverride, mode, _singlePart = false }) {
export async function analyzeSpec({ text, fileName, tender, chain, systemOverride, mode, extraction, _singlePart = false }) {
if (!_singlePart && String(text || '').length > 24000) {
return analyzeInParts({text,fileName,tender,mode}, part => analyzeSpec({
return analyzeInParts({text,fileName,tender,mode,extraction}, part => analyzeSpec({
text:part.text,fileName:`${fileName || 'Doküman'} — bölüm ${part.index+1}`,
tender:tender ? {...tender,items:[]} : undefined,chain,mode,_singlePart:true,
systemOverride:(systemOverride || (mode === 'derin' ? DERIN_SYSTEM_PROMPT : SYSTEM_PROMPT)) + '\nBu metin büyük dokümanın bir bölümüdür. Yalnız bu bölümde görülen koşulları çıkar; bu bölümde bulunmayan şartın bütün dokümanda bulunmadığı sonucunu çıkarma.',
@@ -362,7 +362,7 @@ export async function analyzeSpec({ text, fileName, tender, chain, systemOverrid
raw = await callOpenAiStyle(p, route.key, route.model, userPrompt, ac.signal, sistem, maxTokens);
}
const parsed = extractJson(raw);
const result = qualifyAnalysis(parsed,{text,fileName,limit:derin ? 90000 : 60000,
const result = qualifyAnalysis(parsed,{text,fileName,extraction,limit:derin ? 90000 : 60000,
expectedItems:tender?.items?.length || 0,repaired:parsed._outputRepaired === true});
attempts.push({ id, ok: true, yol: route.yol });
return {
+1 -1
View File
@@ -56,7 +56,7 @@ export async function analyzeInParts(input,runPart) {
quantities.set(key,row.quantity);
}
for(const {part,out} of results) for(const issue of out.result.sourceReview?.issues || [])issues.push(`Bölüm ${part.index+1}: ${issue}`);
const result=qualifyAnalysis(merged,{text:input.text,limit:String(input.text).length,fileName:input.fileName,expectedItems:input.tender?.items?.length || 0});
const result=qualifyAnalysis(merged,{text:input.text,limit:String(input.text).length,fileName:input.fileName,extraction:input.extraction,expectedItems:input.tender?.items?.length || 0});
const ownIssues=[...result.sourceReview.issues,...issues];
result.sourceReview={...result.sourceReview,status:ownIssues.length?'partial':'review_required',issues:ownIssues,
analyzedCharacters:results.reduce((sum,{part})=>sum+part.end-part.start,0),
+5 -3
View File
@@ -1,8 +1,9 @@
import { extractionReview } from '../src/services/extractionReview.js';
import { verifyAnalysisCitations } from './analysis-citations.mjs';
import { validateAnalysisShape } from './analysis-schema.mjs';
import {createHash} from 'node:crypto';
import {extractBidValidity,extractBidType} from '../src/services/bidTerms.js';
export function qualifyAnalysis(result,{text='',limit=60000,fileName='',expectedItems=0,repaired=false}={}) {
export function qualifyAnalysis(result,{text='',limit=60000,fileName='',expectedItems=0,repaired=false,extraction}={}) {
if(!result || typeof result !== 'object' || Array.isArray(result)) throw new Error('Analiz nesne biçiminde değil.');
const clean={...result};delete clean._outputRepaired;
if(clean.citations != null && !Array.isArray(clean.citations)) throw new Error('Kaynak alıntıları liste biçiminde değil.');
@@ -11,7 +12,8 @@ export function qualifyAnalysis(result,{text='',limit=60000,fileName='',expected
if(clean[key] != null && !Array.isArray(clean[key])) throw new Error(`${key} liste biçiminde değil.`);
sections[key]=clean[key]?.length ? 'extracted' : 'not_extracted';
}
const source=String(text),issues=validateAnalysisShape(clean);
const reading=extractionReview(extraction);
const source=String(text),issues=[...validateAnalysisShape(clean),...reading.issues];
if(source.length>limit)issues.push('Kaynak metnin tamamı analiz edilmedi.');
if(repaired)issues.push('Analiz yanıtı kesildi; yalnız kurtarılabilen alanlar gösteriliyor.');
if(expectedItems>0 && (clean.itemsAndSpecs?.length || 0)<expectedItems)issues.push(`Kaynakta ${expectedItems} kalem var; analiz listesi daha kısa.`);
@@ -22,6 +24,6 @@ export function qualifyAnalysis(result,{text='',limit=60000,fileName='',expected
if(bidValidity.status==='conflict')issues.push('Kaynakta farklı teklif geçerlilik süreleri var; ilgili maddeler karşılaştırılmalı.');
const version=createHash('sha256').update(source).digest('hex');
return {...clean,procurementTerms:{bidValidity,bidType},sourceEvidence:citations.evidence,documentVersion:version,sourceReview:{status:issues.length?'partial':'review_required',
fileName,sourceCharacters:source.length,analyzedCharacters:Math.min(source.length,limit),
extraction:reading.sources,fileName,sourceCharacters:source.length,analyzedCharacters:Math.min(source.length,limit),
outputRepaired:repaired,sections,issues,unverifiedFindings:citations.missing}};
}
+1
View File
@@ -1294,6 +1294,7 @@ const server = http.createServer(async (req, res) => {
const out = await analyzeSpec({
text: body.text,
fileName: body.fileName,
extraction: body.extraction,
tender: body.tender,
chain: body.chain,
mode: body.mode === 'derin' ? 'derin' : undefined,