← back to Rentv 2026
harden(llm-extract): Cody gate — anchor num() at the LEADING number + a >=100k 'already a full dollar value' guard, fixing a CATASTROPHE my first pass introduced ('51800000 (51.8M)' concatenated digit runs then scaled → $5e15). Also covers mln/bn abbrevs; anchoring rejects 'lot 5b'. 17/17 verified
b2b4b8c88d39b306b2ee7a250efeb55865625ce3 · 2026-08-06 12:28:05 -0700 · Steve
Files touched
M scripts/lib/llm-extract.mjs
Diff
commit b2b4b8c88d39b306b2ee7a250efeb55865625ce3
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Aug 6 12:28:05 2026 -0700
harden(llm-extract): Cody gate — anchor num() at the LEADING number + a >=100k 'already a full dollar value' guard, fixing a CATASTROPHE my first pass introduced ('51800000 (51.8M)' concatenated digit runs then scaled → $5e15). Also covers mln/bn abbrevs; anchoring rejects 'lot 5b'. 17/17 verified
---
scripts/lib/llm-extract.mjs | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/scripts/lib/llm-extract.mjs b/scripts/lib/llm-extract.mjs
index 847ce8c2..97b2c044 100644
--- a/scripts/lib/llm-extract.mjs
+++ b/scripts/lib/llm-extract.mjs
@@ -50,18 +50,22 @@ export async function llmExtract(title, story, { endpoint = OLLAMA_ENDPOINTS[0],
try { obj = JSON.parse(j.response); } catch { return null; }
if (!obj || typeof obj !== 'object') return null;
// normalize
- // Magnitude-aware: the prompt asks for a plain integer, but if the model returns "51.8 million" /
- // "$1.2 billion" / "$51.8M" / "985k" the old digits-only strip signed those as 52 / 1 / 52 / 985.
- // A compliant integer (no magnitude word/suffix) is unchanged; only a magnitude string gets scaled.
+ // Magnitude-aware, anchored at the LEADING number so a chatty value can't concatenate digit runs.
+ // The prompt asks for a plain integer, but a model may return "51.8 million" / "$1.2 billion" / "$51.8M"
+ // / "985k" / "1.2 bn" (scale those) OR annotate a real integer like "51800000 (51.8M)" (do NOT scale —
+ // the base is already a full dollar value, so the >=100k guard returns it as-is; scaling would give $5e15).
const num = (v) => {
if (v == null) return null;
- const s = String(v).toLowerCase();
- let mult = 1;
- if (/billion|[\d.]\s*b\b/.test(s)) mult = 1e9;
- else if (/million|[\d.]\s*m\b/.test(s)) mult = 1e6;
- else if (/thousand|[\d.]\s*k\b/.test(s)) mult = 1e3;
- const n = parseFloat(s.replace(/[^0-9.]/g, '')) * mult;
- return isFinite(n) && n > 0 ? Math.round(n) : null;
+ const s = String(v).toLowerCase().trim();
+ if (!s || /^(null|n\/a|none|unknown)$/i.test(s)) return null;
+ const m = s.match(/^\$?\s*([\d,]+(?:\.\d+)?)\s*(billion|bil\b|bn\b|b\b|million|mln\b|m\b|thousand|k\b)?/);
+ if (!m) return null;
+ const base = parseFloat(m[1].replace(/,/g, ''));
+ if (!isFinite(base) || base <= 0) return null;
+ if (base >= 100000) return Math.round(base); // already a full dollar value → never scale
+ const sfx = (m[2] || '').trim();
+ const mult = /^(billion|bil|bn|b)$/.test(sfx) ? 1e9 : /^(million|mln|m)$/.test(sfx) ? 1e6 : /^(thousand|k)$/.test(sfx) ? 1e3 : 1;
+ return Math.round(base * mult);
};
const amount = num(obj.amount);
const clean = (v) => { const s = v == null ? '' : String(v).trim(); return s && !/^(null|n\/a|none|unknown)$/i.test(s) ? s : null; };
← 869dd436 auto-data-snapshot: 2026-08-06T12:25:51 (1 data files) — pac
·
back to Rentv 2026
·
feat(services): rank big multi-location firms first (solicit 427a4cf2 →