@rudra-js/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"tracking-input.js","sourceRoot":"","sources":["../src/tracking-input.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tracking-input.js","sourceRoot":"","sources":["../src/tracking-input.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rudra-js/core",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "The contracts and logic that turn one tracking payload into one renderable component specification",
5
5
  "keywords": [
6
6
  "llm",
@@ -17,7 +17,7 @@ import {
17
17
  placeableHeroSkus,
18
18
  reconcileSpec,
19
19
  } from './reconciliation.js';
20
- import { selectProducts, type ProductPick } from './product-selection.js';
20
+ import { selectProducts, type RankOrder, type ProductPick } from './product-selection.js';
21
21
  import { fitToShopper } from './fit-to-shopper.js';
22
22
  import { buildDigest, toCohortDigest, type SignalDigest } from './signal-digest.js';
23
23
  import {
@@ -113,6 +113,14 @@ export interface ComponentGeneratorOptions {
113
113
  * 'cohort'.
114
114
  */
115
115
  generation?: 'cohort' | 'per-shopper';
116
+ /**
117
+ * How the products are ordered. 'signals' scores each candidate from this
118
+ * shopper's signals. 'given' keeps the order you sent, for a shop whose own
119
+ * ranking is better than four weights. Either way the exclusions and the
120
+ * stock check still apply, and each product still carries a basis
121
+ * reconciliation can verify. Defaults to 'signals'.
122
+ */
123
+ rank?: RankOrder;
116
124
  /** Observability. Never allowed to break a render. */
117
125
  onEvent?: (event: GenerationEvent) => void;
118
126
  }
@@ -242,8 +250,10 @@ function fitCohortSpec(
242
250
  spec: GeneratedSpec,
243
251
  input: TrackingInput,
244
252
  digest: SignalDigest,
253
+ rank: RankOrder,
254
+ hostReasonSkus: Set<string>,
245
255
  ): GeneratedSpec {
246
- const picks = selectProducts(input, digest);
256
+ const picks = selectProducts(input, digest, { rank });
247
257
  // Blocks past the cap never render, so a set is not worth reserving for one.
248
258
  const blocks = spec.blocks.slice(0, MAX_BLOCKS);
249
259
 
@@ -256,26 +266,26 @@ function fitCohortSpec(
256
266
  }
257
267
  aboveBundle.push(block);
258
268
  }
259
- if (!hasBundleBlock) return fitToShopper(spec, picks, digest.maxItems);
269
+ if (!hasBundleBlock) return fitToShopper(spec, picks, digest.maxItems, hostReasonSkus);
260
270
 
261
271
  // Only the heroes above the bundle block are placed when it is reached, so
262
272
  // they are all the choice may account for.
263
273
  const chosen = bundleForShopper(input, digest, placeableHeroSkus(aboveBundle, input, digest));
264
- if (!chosen) return fitToShopper(spec, picks, digest.maxItems);
274
+ if (!chosen) return fitToShopper(spec, picks, digest.maxItems, hostReasonSkus);
265
275
 
266
276
  const spokenFor = new Set<string>(chosen.skus);
267
277
  for (const sku of placeableHeroSkus(blocks, input, digest)) spokenFor.add(sku);
268
278
 
269
279
  const roomLeft = digest.maxItems - spokenFor.size;
270
280
  // A set is worth showing, but not at the cost of an empty grid.
271
- if (roomLeft <= 0) return fitToShopper(spec, picks, digest.maxItems);
281
+ if (roomLeft <= 0) return fitToShopper(spec, picks, digest.maxItems, hostReasonSkus);
272
282
 
273
283
  const forGrid: ProductPick[] = [];
274
284
  for (const pick of picks) {
275
285
  if (!spokenFor.has(pick.product.sku)) forGrid.push(pick);
276
286
  }
277
287
 
278
- return fitToShopper(spec, forGrid, roomLeft);
288
+ return fitToShopper(spec, forGrid, roomLeft, hostReasonSkus);
279
289
  }
280
290
 
281
291
  /** Attaches the provenance the server owns. The model never supplies any of it. */
@@ -292,6 +302,7 @@ export function createComponentGenerator(
292
302
  const provider = options.provider ?? null;
293
303
  const cache = options.cache ?? createMemorySpecCache();
294
304
  const generation = options.generation ?? 'cohort';
305
+ const rank = options.rank ?? 'signals';
295
306
  const modelTimeoutMs = options.modelTimeoutMs ?? 1_500;
296
307
  const cacheTimeoutMs = options.cacheTimeoutMs ?? 50;
297
308
  const singleFlight = createSingleFlight<ModelCall>();
@@ -330,7 +341,7 @@ export function createComponentGenerator(
330
341
  degradedReason,
331
342
  });
332
343
 
333
- return withProvenance(buildFallbackSpec(input, digest), {
344
+ return withProvenance(buildFallbackSpec(input, digest, { rank }), {
334
345
  slot: digest.slot,
335
346
  source: 'fallback',
336
347
  generatedAt: finishedAt,
@@ -500,10 +511,15 @@ export function createComponentGenerator(
500
511
  // One place where anything is served, whichever side of the cache it came
501
512
  // from, and always against the facts of the shopper asking now.
502
513
  // A cohort spec names products chosen for whoever asked first.
514
+ // Only the cohort path writes a host reason into a spec, so in
515
+ // per-shopper mode this stays empty and every reason is screened.
516
+ const hostReasonSkus = new Set<string>();
503
517
  const served =
504
- generation === 'cohort' ? fitCohortSpec(answer.spec, input, digest) : answer.spec;
518
+ generation === 'cohort'
519
+ ? fitCohortSpec(answer.spec, input, digest, rank, hostReasonSkus)
520
+ : answer.spec;
505
521
 
506
- const reconciled = reconcileSpec(served, input, digest);
522
+ const reconciled = reconcileSpec(served, input, digest, hostReasonSkus);
507
523
  if (!reconciled.isUsable) {
508
524
  return buildDeterministic(input, digest, startedAt, key, 'unusable-on-serve', {
509
525
  calledModel,
@@ -92,7 +92,7 @@ export const productReferenceSchema = z.object({
92
92
  * true, but the prose asserting that reason must not render.
93
93
  */
94
94
  reason: z.string().nullable(),
95
- /** Short accent label, e.g. "Back in stock". Null when nothing warrants one. */
95
+ /** Short accent label, e.g. "Worth a look". Null when nothing warrants one. */
96
96
  badge: z.string().nullable(),
97
97
  emphasis: z.enum(EMPHASIS),
98
98
  });
@@ -1,6 +1,6 @@
1
1
  import type { GeneratedSpec, ProductReference } from './component-spec.js';
2
2
  import type { SignalDigest } from './signal-digest.js';
3
- import { selectProducts, type ProductPick } from './product-selection.js';
3
+ import { selectProducts, type SelectOptions, type ProductPick } from './product-selection.js';
4
4
  import type { TrackingInput } from './tracking-input.js';
5
5
 
6
6
  /**
@@ -57,8 +57,12 @@ function toProductReference(pick: ProductPick, index: number, total: number): Pr
57
57
  * the renderer treats as "render nothing" — an empty recommendation region is
58
58
  * worse than none.
59
59
  */
60
- export function buildFallbackSpec(input: TrackingInput, digest: SignalDigest): GeneratedSpec {
61
- const picks = selectProducts(input, digest).slice(0, digest.maxItems);
60
+ export function buildFallbackSpec(
61
+ input: TrackingInput,
62
+ digest: SignalDigest,
63
+ options: SelectOptions = {},
64
+ ): GeneratedSpec {
65
+ const picks = selectProducts(input, digest, options).slice(0, digest.maxItems);
62
66
  const { headline, subheadline } = headlineFor(digest);
63
67
  const items = picks.map((pick, index) => toProductReference(pick, index, picks.length));
64
68
 
@@ -7,6 +7,7 @@ export function fitToShopper(
7
7
  spec: GeneratedSpec,
8
8
  picks: readonly ProductPick[],
9
9
  maxItems: number,
10
+ hostReasonSkus?: Set<string>,
10
11
  ): GeneratedSpec {
11
12
  // Picks are ordered best first. Every slot takes the next one.
12
13
  let next = 0;
@@ -24,6 +25,7 @@ export function fitToShopper(
24
25
  for (const item of block.items) {
25
26
  if (next >= limit) break; // shrink, never pad
26
27
  const chosen = picks[next]!;
28
+ if (chosen.reasonFromHost) hostReasonSkus?.add(chosen.product.sku);
27
29
  items.push({
28
30
  sku: chosen.product.sku,
29
31
  basis: chosen.basis,
@@ -23,6 +23,8 @@ export interface ProductPick {
23
23
  basis: RecommendationBasis;
24
24
  /** How the basis reads to a shopper. */
25
25
  reason: string;
26
+ /** True when `reason` is the host's own sentence from the candidate. */
27
+ reasonFromHost: boolean;
26
28
  /** Unnormalised. Only the ordering is meaningful. */
27
29
  score: number;
28
30
  }
@@ -95,9 +97,6 @@ function basisFor(
95
97
  if (evidence.hasCart) {
96
98
  return { basis: 'complements_cart', reason: 'Goes with what is in your cart' };
97
99
  }
98
- if ((product.rating ?? 0) >= 4.5) {
99
- return { basis: 'popular', reason: 'Highly rated' };
100
- }
101
100
  return { basis: 'popular', reason: `Popular in ${product.category}` };
102
101
  }
103
102
 
@@ -107,7 +106,24 @@ function basisFor(
107
106
  * Ties break on SKU so the order is total: two runs over the same payload
108
107
  * produce the same list, which is what makes the control arm reproducible.
109
108
  */
110
- export function selectProducts(input: TrackingInput, digest: SignalDigest): ProductPick[] {
109
+ /** How the picks are ordered once the unplaceable ones are gone. */
110
+ export type RankOrder = 'signals' | 'given';
111
+
112
+ export interface SelectOptions {
113
+ /**
114
+ * `signals` scores each candidate and orders by that score. `given` keeps the
115
+ * order you sent, for a shop whose own ranking is better than four weights.
116
+ * Either way the exclusions and the stock check still apply, and every pick
117
+ * still carries a basis reconciliation can verify.
118
+ */
119
+ rank?: RankOrder;
120
+ }
121
+
122
+ export function selectProducts(
123
+ input: TrackingInput,
124
+ digest: SignalDigest,
125
+ options: SelectOptions = {},
126
+ ): ProductPick[] {
111
127
  const affinityByCategory = new Map(
112
128
  digest.categoryAffinity.map((affinity) => [affinity.category, affinity.score]),
113
129
  );
@@ -144,9 +160,17 @@ export function selectProducts(input: TrackingInput, digest: SignalDigest): Prod
144
160
  hasCart,
145
161
  });
146
162
 
147
- picks.push({ product, basis, reason, score });
163
+ picks.push({
164
+ product,
165
+ basis,
166
+ reason: product.reason ?? reason,
167
+ reasonFromHost: product.reason !== undefined,
168
+ score,
169
+ });
148
170
  }
149
171
 
172
+ if (options.rank === 'given') return picks;
173
+
150
174
  return picks.toSorted(
151
175
  (left, right) => right.score - left.score || left.product.sku.localeCompare(right.product.sku),
152
176
  );
@@ -255,12 +255,95 @@ const CLAIM_PATTERNS: { kind: string; patterns: RegExp[] }[] = [
255
255
  },
256
256
  ];
257
257
 
258
+ /**
259
+ * Letters that render as a Latin letter but are not one. A Cyrillic о in
260
+ * "in stоck" would otherwise carry a stock claim straight past the rule written
261
+ * for it, and the shopper would read the claim anyway.
262
+ */
263
+ const CONFUSABLES: Record<string, string> = {
264
+ а: 'a',
265
+ в: 'b',
266
+ е: 'e',
267
+ к: 'k',
268
+ м: 'm',
269
+ н: 'h',
270
+ о: 'o',
271
+ р: 'p',
272
+ с: 'c',
273
+ т: 't',
274
+ х: 'x',
275
+ у: 'y',
276
+ ѕ: 's',
277
+ і: 'i',
278
+ ј: 'j',
279
+ ԁ: 'd',
280
+ һ: 'h',
281
+ ӏ: 'l',
282
+ α: 'a',
283
+ ε: 'e',
284
+ ι: 'i',
285
+ κ: 'k',
286
+ ν: 'v',
287
+ ο: 'o',
288
+ ρ: 'p',
289
+ τ: 't',
290
+ υ: 'u',
291
+ χ: 'x',
292
+ ϲ: 'c',
293
+ };
294
+
295
+ /**
296
+ * Characters that render as nothing: format characters, default-ignorable code
297
+ * points and the controls. Cf and Default_Ignorable each hold characters the
298
+ * other does not.
299
+ *
300
+ * The same class @rudra-js/attested strips in its own `hidden.ts`. A second copy,
301
+ * because core carries no dependencies and is not about to take one — which means
302
+ * widening one and not the other is the mistake to watch for.
303
+ */
304
+ const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
305
+
306
+ /**
307
+ * The controls that do take room. Deleting these would read `Only 2\n3 left` as 23.
308
+ *
309
+ * No test pins this line, and none can: clamping collapses every run of whitespace
310
+ * before a claim is screened, so nothing here ever sees one. It is kept so the class
311
+ * reads the same in both copies.
312
+ */
313
+ const SPACING = /[\t\n\v\f\r\u0085]/;
314
+
315
+ /**
316
+ * Marks that hang on the character before them instead of taking a column of their
317
+ * own. Dropped after composition, so an accented e keeps its accent while an
318
+ * overline, which composes with nothing, cannot hide a word from its own rule.
319
+ */
320
+ const MARKS = /[\p{Mn}\p{Me}]/gu;
321
+
322
+ /**
323
+ * One shape for the text before the patterns read it, so a word that renders as
324
+ * "in stock" is read as "in stock" however it was spelled.
325
+ *
326
+ * Only the reading is normalised. What renders is what the model wrote.
327
+ */
328
+ function normaliseForClaims(text: string): string {
329
+ const lowered = text
330
+ .replace(INVISIBLE, (char) => (SPACING.test(char) ? char : ''))
331
+ .normalize('NFKC')
332
+ .toLowerCase()
333
+ .normalize('NFC')
334
+ .replace(MARKS, '');
335
+
336
+ let folded = '';
337
+ for (const char of lowered) folded += CONFUSABLES[char] ?? char;
338
+ return folded;
339
+ }
340
+
258
341
  /** Names the first forbidden claim the text makes, or null when it makes none. */
259
342
  function claimIn(text: string): string | null {
260
- const lower = text.toLowerCase();
343
+ const normalised = normaliseForClaims(text);
261
344
  for (const claim of CLAIM_PATTERNS) {
262
345
  for (const pattern of claim.patterns) {
263
- if (pattern.test(lower)) return claim.kind;
346
+ if (pattern.test(normalised)) return claim.kind;
264
347
  }
265
348
  }
266
349
  return null;
@@ -356,6 +439,7 @@ function reconcileItems(
356
439
  candidatesBySku: Map<string, Product>,
357
440
  digest: SignalDigest,
358
441
  tracker: PlacementTracker,
442
+ hostReasonSkus: ReadonlySet<string>,
359
443
  ): ProductReference[] {
360
444
  const kept: ProductReference[] = [];
361
445
 
@@ -375,15 +459,23 @@ function reconcileItems(
375
459
  const hasSupportedBasis = verifyBasis(item.basis, product, digest);
376
460
  if (!hasSupportedBasis) tracker.record(`unsupported-basis:${item.basis}:${item.sku}`);
377
461
 
462
+ const isHostReason =
463
+ item.reason !== null && hostReasonSkus.has(item.sku) && item.reason === product.reason;
464
+
378
465
  kept.push({
379
466
  sku: item.sku,
380
467
  basis: hasSupportedBasis ? item.basis : 'popular',
381
468
  // The prose exists to state the basis. If the basis did not hold, the
382
469
  // prose is a claim we just decided is untrue.
383
470
  reason: hasSupportedBasis
384
- ? screenClaim(clampNullable(item.reason, CLAMP.reason), `reason:${item.sku}`, tracker)
471
+ ? isHostReason
472
+ ? clampNullable(item.reason, CLAMP.reason)
473
+ : screenClaim(clampNullable(item.reason, CLAMP.reason), `reason:${item.sku}`, tracker)
385
474
  : null,
386
- badge: clampNullable(item.badge, CLAMP.badge),
475
+ // A badge is the shortest, loudest text on the card, and the schema's own
476
+ // example for it was "Back in stock" — a stock claim. It renders, so it is
477
+ // read for claims like every other sentence the model writes.
478
+ badge: screenClaim(clampNullable(item.badge, CLAMP.badge), `badge:${item.sku}`, tracker),
387
479
  emphasis: item.emphasis,
388
480
  });
389
481
  }
@@ -516,6 +608,7 @@ function reconcileBlock(
516
608
  digest: SignalDigest,
517
609
  bundles: readonly Bundle[],
518
610
  tracker: PlacementTracker,
611
+ hostReasonSkus: ReadonlySet<string>,
519
612
  ): Block | null {
520
613
  switch (block.kind) {
521
614
  case 'hero': {
@@ -552,7 +645,14 @@ function reconcileBlock(
552
645
  }
553
646
 
554
647
  case 'grid': {
555
- const items = reconcileItems(block.items, allowlist, candidatesBySku, digest, tracker);
648
+ const items = reconcileItems(
649
+ block.items,
650
+ allowlist,
651
+ candidatesBySku,
652
+ digest,
653
+ tracker,
654
+ hostReasonSkus,
655
+ );
556
656
  if (items.length === 0) {
557
657
  tracker.record('empty-block:grid');
558
658
  return null;
@@ -567,7 +667,14 @@ function reconcileBlock(
567
667
  }
568
668
 
569
669
  case 'carousel': {
570
- const items = reconcileItems(block.items, allowlist, candidatesBySku, digest, tracker);
670
+ const items = reconcileItems(
671
+ block.items,
672
+ allowlist,
673
+ candidatesBySku,
674
+ digest,
675
+ tracker,
676
+ hostReasonSkus,
677
+ );
571
678
  if (items.length === 0) {
572
679
  tracker.record('empty-block:carousel');
573
680
  return null;
@@ -643,6 +750,12 @@ export function reconcileSpec(
643
750
  generated: GeneratedSpec,
644
751
  input: TrackingInput,
645
752
  digest: SignalDigest,
753
+ /**
754
+ * SKUs whose reason this request wrote from the host's own candidate. Only
755
+ * `fitToShopper` fills it, so in `per-shopper` mode it is empty and every
756
+ * reason is the model's, including one that happens to read the same.
757
+ */
758
+ hostReasonSkus: ReadonlySet<string> = new Set(),
646
759
  ): ReconcileResult {
647
760
  const allowlist = buildAllowlist(input, digest);
648
761
  const candidatesBySku = new Map(input.candidates.map((product) => [product.sku, product]));
@@ -661,6 +774,7 @@ export function reconcileSpec(
661
774
  digest,
662
775
  input.bundles,
663
776
  tracker,
777
+ hostReasonSkus,
664
778
  );
665
779
  if (reconciled !== null) blocks.push(reconciled);
666
780
  }
@@ -44,6 +44,9 @@ export const FIELD_LIMITS = {
44
44
  bundles: 20,
45
45
  localeTag: 35,
46
46
  maxItems: 12,
47
+ // Matches CLAMP.reason in reconciliation: a host reason is rendered in the
48
+ // same place a model's is, so the same length has to hold.
49
+ reason: 120,
47
50
  } as const;
48
51
 
49
52
  /** Assigning this as an object key mutates the prototype instead of the object. */
@@ -95,6 +98,10 @@ export const productSchema = z.strictObject({
95
98
  // scheme to abuse.
96
99
  imageUrl: imageReference().optional(),
97
100
  rating: z.number().min(0).max(5).optional(),
101
+ // Your phrase for why this product is here, when your own ranking already has
102
+ // one. It renders as the item's reason and is not screened: these are your
103
+ // words about your product, like the title, so you stand behind them.
104
+ reason: z.string().min(1).max(FIELD_LIMITS.reason).optional(),
98
105
  isInStock: z.boolean().default(true),
99
106
  tags: z
100
107
  .array(z.string().min(1).max(FIELD_LIMITS.tag))