This commit is contained in:
2026-07-16 10:47:38 -05:00
parent 92e1f3235a
commit 18f8f97a43
2 changed files with 38 additions and 4 deletions

View File

@@ -107,7 +107,7 @@ services:
- -fa
- "on"
- -c
- "40960"
- "65536"
- --parallel
- "1"
# KV-Cache quantisieren — 32GB VRAM, 35B-A3B + mmproj + Bildkontext:

View File

@@ -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. */
function normDate(raw: string | null): string | null {
const c = cleanStr(raw);
let c = cleanStr(raw);
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})$/);
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})$/);
@@ -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
- 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.
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):
- 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.
@@ -426,12 +428,41 @@ async function callVision(api: string, images: string[], timeoutMs: number): Pro
// 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 {
const base: BuyerRecord = {
...det,
_parser: "vision",
_vision_model: model,
_vision_ts: new Date().toISOString(),
name_from_filename: nameFromFilename(det.file_name),
_vision_error: undefined,
};
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_raw: dp.raw,
// 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,
_ca_page: vis.ca_page,
_notes_page: vis.notes_page,