TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { z } from 'zod';2import { getRouter } from '../router.js';3import type { CostContext, ModelProvider } from '../types.js';45export interface MatchCandidate {6 title: string;7 attributes?: Record<string, unknown>;8 identifiers?: Record<string, string>;9 grade?: { grader?: string | null; grade?: string | null } | null;10 source?: string;11}1213export const MatchVerdictSchema = z.object({14 sameAsset: z.boolean().describe('True if both describe the same canonical collectible (ignoring grade/condition)'),15 sameVariant: z.boolean().describe('True if grade/condition/edition also match'),16 confidence: z.number().min(0).max(1),17 blockingDifferences: z.array(z.string()).describe('Concrete attribute differences that prevent a match (set, number, edition, language, year…)'),18 rationale: z.string(),19});20export type MatchVerdict = z.infer<typeof MatchVerdictSchema>;2122/**23 * LLM verification for entity resolution (§112) — the last step after deterministic identifiers,24 * canonical keys and fuzzy matching have produced a candidate pair.25 */26export async function verifyEntityMatch(a: MatchCandidate, b: MatchCandidate, opts: { provider?: ModelProvider; cost?: CostContext } = {}) {27 const provider = opts.provider ?? getRouter();28 const res = await provider.extract('resolve', {29 schema: MatchVerdictSchema,30 system: 'You verify whether two collectible records refer to the same canonical asset. Be strict: different set, card number, edition (1st Edition vs Unlimited), language, year, reference number, colorway or size are different assets. Grade and condition differences make different variants of the same asset.',31 prompt: 'Compare record A and record B.',32 input: `Record A:\n${JSON.stringify(a, null, 2)}\n\nRecord B:\n${JSON.stringify(b, null, 2)}`,33 maxTokens: 800,34 effort: 'low',35 cost: { endpoint: 'verify_match', ...(opts.cost ?? {}) },36 });37 return res;38}39