- Add OPN-based equivalence matcher robot (7,245 manufacturer-confirmed matches, confidence=1.0) - Add spec-based equivalence matcher robot (683 matches, confidence=0.85) - Matches by form_factor + speed_gbps + reach_tier + wavelength ±10nm - Safety cap: skip FX products matching >30 competitors (too generic) - Daily schedule: 04:30 UTC via pg-boss - SQL migrations 116 (OPN) + 117 (spec) with tip_extract_wavelength_nm() + tip_reach_tier() helpers - Fix tenGtek.ts: add 3 missing 400G categories (QSFP-DD, QSFP112) — closes pricing gap - Generate tip-llm-pricing-v1.jsonl: 80 DB-grounded QA pairs (pricing, equivalences, 400G) - Rebuild TIP_LLM training pool: 11,999 pairs (+127 vs prev), deployed to Erik - FX product equivalence coverage: 88.1% (959/1089)
131 lines
4.9 KiB
TypeScript
131 lines
4.9 KiB
TypeScript
/**
|
|
* OPN-Based Equivalence Matcher
|
|
*
|
|
* Uses the manufacturer-provided compatibility matrix (fx_compatibilities)
|
|
* to create high-confidence equivalences between Flexoptix products and
|
|
* their exact OEM counterparts in competitor catalogs.
|
|
*
|
|
* "OPN" = OEM Part Number — the actual part number the customer buys from
|
|
* the original manufacturer (e.g. Cisco QSFP-100G-LR4-S).
|
|
*
|
|
* Match quality:
|
|
* - confidence = 1.0 (manufacturer-confirmed)
|
|
* - match_mode = 'opn'
|
|
* - status = 'auto_approved' (same as deterministic spec match)
|
|
*
|
|
* Strategy:
|
|
* - Only processes FX products whose fx_compatibilities was updated recently
|
|
* (detail_synced_at > last_opn_run OR last_opn_run IS NULL)
|
|
* - Skips pairs that already have ANY status (approved, auto_approved, rejected)
|
|
* - Case-insensitive part_number match on the competitor side
|
|
* - Minimum OPN length = 4 chars (skips empty or trivially short entries)
|
|
* - Excludes MSA Standard and Flexoptix self-references
|
|
*/
|
|
|
|
import { pool } from "../utils/db";
|
|
|
|
export interface OPNMatcherResult {
|
|
inserted: number;
|
|
fxProductsScanned: number;
|
|
candidatePairs: number;
|
|
skippedExisting: number;
|
|
}
|
|
|
|
// ── Queries ────────────────────────────────────────────────────────────────
|
|
|
|
const INSERT_OPN_MATCHES = `
|
|
INSERT INTO transceiver_equivalences (
|
|
flexoptix_id,
|
|
competitor_id,
|
|
confidence,
|
|
status,
|
|
match_basis,
|
|
match_notes,
|
|
created_at,
|
|
updated_at
|
|
)
|
|
SELECT DISTINCT
|
|
fx.id AS flexoptix_id,
|
|
comp.id AS competitor_id,
|
|
1.0 AS confidence,
|
|
'auto_approved' AS status,
|
|
ARRAY['opn'] AS match_basis,
|
|
'Manufacturer-confirmed: FX compatibility matrix lists ' ||
|
|
COALESCE(compat->>'compatible_to_vendor', '?') || ' OPN ' ||
|
|
COALESCE(compat->>'original_part_number', '?') AS match_notes,
|
|
NOW() AS created_at,
|
|
NOW() AS updated_at
|
|
FROM transceivers fx
|
|
JOIN vendors vfx ON vfx.id = fx.vendor_id AND UPPER(vfx.name) LIKE '%FLEXOPTIX%'
|
|
CROSS JOIN LATERAL jsonb_array_elements(fx.fx_compatibilities) AS compat
|
|
JOIN transceivers comp
|
|
ON UPPER(comp.part_number) = UPPER(compat->>'original_part_number')
|
|
JOIN vendors vcomp ON vcomp.id = comp.vendor_id AND vcomp.is_competitor = true
|
|
WHERE fx.fx_compatibilities IS NOT NULL
|
|
AND compat->>'original_part_number' IS NOT NULL
|
|
AND length(trim(compat->>'original_part_number')) >= 4
|
|
AND compat->>'compatible_to_vendor' NOT IN ('MSA Standard (Default)', 'Flexoptix')
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM transceiver_equivalences e
|
|
WHERE e.flexoptix_id = fx.id
|
|
AND e.competitor_id = comp.id
|
|
)
|
|
ON CONFLICT DO NOTHING
|
|
`;
|
|
|
|
const COUNT_FX_WITH_COMPAT = `
|
|
SELECT COUNT(*) AS cnt
|
|
FROM transceivers t
|
|
JOIN vendors v ON v.id = t.vendor_id AND UPPER(v.name) LIKE '%FLEXOPTIX%'
|
|
WHERE t.fx_compatibilities IS NOT NULL
|
|
`;
|
|
|
|
const COUNT_CANDIDATE_PAIRS = `
|
|
SELECT COUNT(DISTINCT (fx.id, comp.id)) AS cnt
|
|
FROM transceivers fx
|
|
JOIN vendors vfx ON vfx.id = fx.vendor_id AND UPPER(vfx.name) LIKE '%FLEXOPTIX%'
|
|
CROSS JOIN LATERAL jsonb_array_elements(fx.fx_compatibilities) AS compat
|
|
JOIN transceivers comp
|
|
ON UPPER(comp.part_number) = UPPER(compat->>'original_part_number')
|
|
JOIN vendors vcomp ON vcomp.id = comp.vendor_id AND vcomp.is_competitor = true
|
|
WHERE fx.fx_compatibilities IS NOT NULL
|
|
AND compat->>'original_part_number' IS NOT NULL
|
|
AND length(trim(compat->>'original_part_number')) >= 4
|
|
AND compat->>'compatible_to_vendor' NOT IN ('MSA Standard (Default)', 'Flexoptix')
|
|
`;
|
|
|
|
// ── Main export ────────────────────────────────────────────────────────────
|
|
|
|
export async function runOPNMatcher(): Promise<OPNMatcherResult> {
|
|
const ts = () => new Date().toISOString();
|
|
console.log(`[${ts()}] OPN Matcher starting`);
|
|
|
|
// Count FX products with compatibility data
|
|
const fxRes = await pool.query<{ cnt: string }>(COUNT_FX_WITH_COMPAT);
|
|
const fxProductsScanned = parseInt(fxRes.rows[0].cnt, 10);
|
|
|
|
// Count candidate pairs (informational)
|
|
const candRes = await pool.query<{ cnt: string }>(COUNT_CANDIDATE_PAIRS);
|
|
const candidatePairs = parseInt(candRes.rows[0].cnt, 10);
|
|
|
|
console.log(`[${ts()}] OPN Matcher: ${fxProductsScanned} FX products, ${candidatePairs} candidate pairs`);
|
|
|
|
// Insert new OPN-based equivalences
|
|
const insertRes = await pool.query(INSERT_OPN_MATCHES);
|
|
const inserted = insertRes.rowCount ?? 0;
|
|
const skippedExisting = candidatePairs - inserted;
|
|
|
|
console.log(
|
|
`[${ts()}] OPN Matcher done: ${inserted} new equivalences inserted ` +
|
|
`(${skippedExisting} pairs already existed)`,
|
|
);
|
|
|
|
return {
|
|
inserted,
|
|
fxProductsScanned,
|
|
candidatePairs,
|
|
skippedExisting,
|
|
};
|
|
}
|