fixes
This commit is contained in:
@@ -107,7 +107,7 @@ services:
|
|||||||
- -fa
|
- -fa
|
||||||
- "on"
|
- "on"
|
||||||
- -c
|
- -c
|
||||||
- "40960"
|
- "65536"
|
||||||
- --parallel
|
- --parallel
|
||||||
- "1"
|
- "1"
|
||||||
# KV-Cache quantisieren — 32GB VRAM, 35B-A3B + mmproj + Bildkontext:
|
# KV-Cache quantisieren — 32GB VRAM, 35B-A3B + mmproj + Bildkontext:
|
||||||
|
|||||||
@@ -184,8 +184,10 @@ function isoDate(y: number, mo: number, d: number): string | null {
|
|||||||
|
|
||||||
/** US-Formate: 6/25/26, 06-25-2026, "June 25, 2026" → YYYY-MM-DD. */
|
/** US-Formate: 6/25/26, 06-25-2026, "June 25, 2026" → YYYY-MM-DD. */
|
||||||
function normDate(raw: string | null): string | null {
|
function normDate(raw: string | null): string | null {
|
||||||
const c = cleanStr(raw);
|
let c = cleanStr(raw);
|
||||||
if (!c) return null;
|
if (!c) return null;
|
||||||
|
// Leerzeichen um Trenner entfernen: "09 / 13 / 2021" -> "09/13/2021"
|
||||||
|
c = c.replace(/\s*([\/\-.])\s*/g, "$1").trim();
|
||||||
let m = c.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{2}|\d{4})$/);
|
let m = c.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{2}|\d{4})$/);
|
||||||
if (m) return isoDate(parseInt(m[3], 10), parseInt(m[1], 10), parseInt(m[2], 10));
|
if (m) return isoDate(parseInt(m[3], 10), parseInt(m[1], 10), parseInt(m[2], 10));
|
||||||
m = c.match(/^([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(\d{2}|\d{4})$/);
|
m = c.match(/^([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(\d{2}|\d{4})$/);
|
||||||
@@ -344,7 +346,7 @@ STEP 3 — Transcribe VERBATIM (exactly as written, do not normalize or expand a
|
|||||||
- total_purchase_price: "Total Purchase Price" as written
|
- total_purchase_price: "Total Purchase Price" as written
|
||||||
- down_payment_raw: "Down Payment" as written (e.g. "$350,000", "1.5M", "TBD")
|
- down_payment_raw: "Down Payment" as written (e.g. "$350,000", "1.5M", "TBD")
|
||||||
6. If doc_type is "ca_only": transcribe from the CA page — the prospective buyer's printed/signed name into prospective_buyer, plus address/phone/email if present. Everything else null.
|
6. If doc_type is "ca_only": transcribe from the CA page — the prospective buyer's printed/signed name into prospective_buyer, plus address/phone/email if present. Everything else null.
|
||||||
7. date_of_introduction_raw: the date next to the buyer's signature on the CA page, verbatim (e.g. "6/25/26"), else null.
|
7. date_of_introduction_raw: the "Date of Introduction" on the CA page (usually near the buyer's signature), transcribed EXACTLY as written including any spaces or separators (e.g. "09 / 13 / 2021", "6/25/26", "Sept 13 2021"). If a date is visible anywhere labeled "Date of Introduction", always return it verbatim — never leave it null just because the format looks unusual. Only null if truly no such date is present.
|
||||||
8. From the NOTES page (only if notes_page is set):
|
8. From the NOTES page (only if notes_page is set):
|
||||||
- notes_business_raw: the business name(s) written in the "Business Interested In" TABLE of the notes page, verbatim (join multiple with a comma). Take ONLY the table entries — do NOT include the free-text "Notes" area at the bottom of the page. If the table is empty, null.
|
- notes_business_raw: the business name(s) written in the "Business Interested In" TABLE of the notes page, verbatim (join multiple with a comma). Take ONLY the table entries — do NOT include the free-text "Notes" area at the bottom of the page. If the table is empty, null.
|
||||||
The notes page and the info sheet are INDEPENDENT sources; never copy content from one into the other's field.
|
The notes page and the info sheet are INDEPENDENT sources; never copy content from one into the other's field.
|
||||||
@@ -426,12 +428,41 @@ async function callVision(api: string, images: string[], timeoutMs: number): Pro
|
|||||||
// Merge: deterministischer Datensatz + Vision-Rohwerte → Zielstruktur
|
// Merge: deterministischer Datensatz + Vision-Rohwerte → Zielstruktur
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zieht den Namen deterministisch aus dem Dateinamen (verlaessliche Quelle,
|
||||||
|
* unabhaengig von der Vision-Transkription). Schema: "Nachname, Vorname <datum> [Zusatz].pdf".
|
||||||
|
* Gibt "Nachname, Vorname" zurueck, oder null wenn nicht erkennbar.
|
||||||
|
* Beeinflusst die Bilderkennung NICHT — reine String-Operation.
|
||||||
|
*/
|
||||||
|
function nameFromFilename(fileName: string): string | null {
|
||||||
|
let s = fileName.replace(/\.pdf$/i, "");
|
||||||
|
// Klammer-Zusaetze wie "(Gordon Greve)" / "(JF Lehman)" entfernen
|
||||||
|
s = s.replace(/\([^)]*\)/g, " ").replace(/\s+/g, " ").trim();
|
||||||
|
// Schema ist "Nachname, Vorname ...". Nimm den Nachnamen (vor dem Komma)
|
||||||
|
// und aus dem Rest nur das erste Wort als Vorname - alles Weitere
|
||||||
|
// (Zweitnamen, Datum, Notes/Oilfield-Zusaetze) faellt weg.
|
||||||
|
const comma = s.indexOf(",");
|
||||||
|
if (comma < 0) {
|
||||||
|
// kein Komma: erstes Wort als Ganzes nehmen, ohne Datum/Zusatz
|
||||||
|
const first = s.replace(/\b\d{4,8}\b.*$/, "").trim();
|
||||||
|
return first.length >= 2 ? first : null;
|
||||||
|
}
|
||||||
|
const last = s.slice(0, comma).trim();
|
||||||
|
const rest = s.slice(comma + 1).trim();
|
||||||
|
// erstes Token des Rests = Vorname (stoppt vor Datum/Zusatzwort)
|
||||||
|
const firstName = (rest.match(/^[A-Za-zÀ-ÿ.'-]+/) || [""])[0];
|
||||||
|
if (!last || !firstName) return null;
|
||||||
|
return `${last}, ${firstName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function mergeRecord(det: BuyerRecord, vis: VisionRaw, model: string): BuyerRecord {
|
function mergeRecord(det: BuyerRecord, vis: VisionRaw, model: string): BuyerRecord {
|
||||||
const base: BuyerRecord = {
|
const base: BuyerRecord = {
|
||||||
...det,
|
...det,
|
||||||
_parser: "vision",
|
_parser: "vision",
|
||||||
_vision_model: model,
|
_vision_model: model,
|
||||||
_vision_ts: new Date().toISOString(),
|
_vision_ts: new Date().toISOString(),
|
||||||
|
name_from_filename: nameFromFilename(det.file_name),
|
||||||
_vision_error: undefined,
|
_vision_error: undefined,
|
||||||
};
|
};
|
||||||
delete (base as Record<string, unknown>)["_vision_error"];
|
delete (base as Record<string, unknown>)["_vision_error"];
|
||||||
@@ -464,7 +495,10 @@ function mergeRecord(det: BuyerRecord, vis: VisionRaw, model: string): BuyerReco
|
|||||||
down_payment: dp.value,
|
down_payment: dp.value,
|
||||||
down_payment_raw: dp.raw,
|
down_payment_raw: dp.raw,
|
||||||
// Vision-Datum bevorzugt; Fallback: deterministischer Wert (z.B. aus Dateinamen)
|
// Vision-Datum bevorzugt; Fallback: deterministischer Wert (z.B. aus Dateinamen)
|
||||||
date_of_introduction: normDate(vis.date_of_introduction_raw) ?? (det.date_of_introduction as string | null) ?? null,
|
// Datum: ISO wenn parsebar, sonst ROHWERT behalten (Mitarbeiter korrigiert
|
||||||
|
// spaeter). Nie verwerfen, nur weil das Format ungewohnt ist.
|
||||||
|
date_of_introduction: normDate(vis.date_of_introduction_raw) ?? cleanStr(vis.date_of_introduction_raw) ?? (det.date_of_introduction as string | null) ?? null,
|
||||||
|
date_of_introduction_raw: cleanStr(vis.date_of_introduction_raw),
|
||||||
_info_page: vis.info_page,
|
_info_page: vis.info_page,
|
||||||
_ca_page: vis.ca_page,
|
_ca_page: vis.ca_page,
|
||||||
_notes_page: vis.notes_page,
|
_notes_page: vis.notes_page,
|
||||||
|
|||||||
Reference in New Issue
Block a user