@rudra-js/core 0.6.0 → 0.7.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.
- package/README.md +13 -12
- package/dist/claim-screening.d.ts +0 -23
- package/dist/claim-screening.d.ts.map +1 -1
- package/dist/claim-screening.js +0 -69
- package/dist/claim-screening.js.map +1 -1
- package/dist/component-generator.d.ts +0 -70
- package/dist/component-generator.d.ts.map +1 -1
- package/dist/component-generator.js +10 -52
- package/dist/component-generator.js.map +1 -1
- package/dist/component-spec.d.ts +2 -110
- package/dist/component-spec.d.ts.map +1 -1
- package/dist/component-spec.js +0 -78
- package/dist/component-spec.js.map +1 -1
- package/dist/fallback-component.d.ts +0 -9
- package/dist/fallback-component.d.ts.map +1 -1
- package/dist/fallback-component.js +0 -11
- package/dist/fallback-component.js.map +1 -1
- package/dist/fit-to-shopper.d.ts.map +1 -1
- package/dist/fit-to-shopper.js +1 -11
- package/dist/fit-to-shopper.js.map +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +1 -1
- package/dist/model-prompt.d.ts +0 -29
- package/dist/model-prompt.d.ts.map +1 -1
- package/dist/model-prompt.js +0 -79
- package/dist/model-prompt.js.map +1 -1
- package/dist/product-selection.d.ts +0 -23
- package/dist/product-selection.d.ts.map +1 -1
- package/dist/product-selection.js +0 -19
- package/dist/product-selection.js.map +1 -1
- package/dist/provider.d.ts +0 -48
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +0 -5
- package/dist/provider.js.map +1 -1
- package/dist/reconciliation.d.ts +1 -44
- package/dist/reconciliation.d.ts.map +1 -1
- package/dist/reconciliation.js +2 -109
- package/dist/reconciliation.js.map +1 -1
- package/dist/signal-digest.d.ts +0 -27
- package/dist/signal-digest.d.ts.map +1 -1
- package/dist/signal-digest.js +1 -50
- package/dist/signal-digest.js.map +1 -1
- package/dist/spec-cache.d.ts +6 -80
- package/dist/spec-cache.d.ts.map +1 -1
- package/dist/spec-cache.js +1 -54
- package/dist/spec-cache.js.map +1 -1
- package/dist/tracking-input.d.ts +0 -49
- package/dist/tracking-input.d.ts.map +1 -1
- package/dist/tracking-input.js +0 -102
- package/dist/tracking-input.js.map +1 -1
- package/package.json +7 -6
- package/src/claim-screening.ts +17 -69
- package/src/component-generator.ts +24 -133
- package/src/component-spec.ts +15 -103
- package/src/fallback-component.ts +0 -11
- package/src/fit-to-shopper.ts +3 -12
- package/src/index.ts +0 -8
- package/src/model-prompt.ts +1 -82
- package/src/product-selection.ts +5 -43
- package/src/provider.ts +4 -53
- package/src/reconciliation.ts +12 -112
- package/src/signal-digest.ts +5 -64
- package/src/spec-cache.ts +8 -98
- package/src/tracking-input.ts +11 -108
package/README.md
CHANGED
|
@@ -6,20 +6,21 @@ component specification.
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
npm install @rudra-js/core @rudra-js/attested
|
|
9
|
+
npm install @rudra-js/core @rudra-js/attested
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
`zod` is a dependency, so you don't need it yourself. The public API of this
|
|
13
|
+
package _is_ zod schemas, and an app on any zod 4 shares one copy with it. An
|
|
14
|
+
app on zod 3 gets its own copy alongside ours, and the two don't mix: build on
|
|
15
|
+
the exported schemas with their own methods (`productSchema.array()`) rather
|
|
16
|
+
than your zod's (`z.array(productSchema)`), and read `error.issues` rather than
|
|
17
|
+
checking `instanceof ZodError`. Whichever copy core ends up with writes the tool
|
|
18
|
+
schema we send the model, so its wording shifts a little between releases:
|
|
19
|
+
before 4.5, a nullable field is written as `anyOf` rather than a type array. It
|
|
20
|
+
means the same thing either way.
|
|
21
|
+
|
|
22
|
+
`@rudra-js/attested` is a peer dependency, even though none of its types cross
|
|
23
|
+
this package's public surface and you never have to import it. It's a peer so
|
|
23
24
|
there is exactly one denylist in your tree. As a real dependency, an app that
|
|
24
25
|
pins its own copy gets two — we checked, and npm 10.9.4 installs the app's
|
|
25
26
|
version at the top and quietly nests ours under `@rudra-js/core` — and the two
|
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
import type { Product, TrackingInput } from './tracking-input.js';
|
|
2
|
-
/**
|
|
3
|
-
* Wording attested bans that this project has already ruled is not a claim. An allowance
|
|
4
|
-
* forgives a banned phrase sitting inside it, so an entry ending on a preposition — "extra
|
|
5
|
-
* clearance for" — forgives whatever follows it. Exported so a test can hold the list
|
|
6
|
-
* against attested's own.
|
|
7
|
-
*/
|
|
8
2
|
export declare const ALLOWED_PHRASES: string[];
|
|
9
|
-
/**
|
|
10
|
-
* The numbers the shop published about one product: its category and tags, only the strings
|
|
11
|
-
* carrying a digit. Exported so a test can pin what never reaches attested.
|
|
12
|
-
*/
|
|
13
3
|
export declare function productFacts(product: Product): string[];
|
|
14
4
|
export interface HostFacts {
|
|
15
5
|
pooled: string[];
|
|
16
6
|
bySku: Map<string, string[]>;
|
|
17
7
|
}
|
|
18
|
-
/**
|
|
19
|
-
* The shop's own numbers for this request, for attested to check numerals against.
|
|
20
|
-
*
|
|
21
|
-
* `offeredCandidates` rather than `input.candidates` — out of stock or past the prompt's cap
|
|
22
|
-
* means a product the model never saw. A title and a rating it is shown but told never to
|
|
23
|
-
* repeat, so a numeral out of one is one it was told not to write. The browsed category comes
|
|
24
|
-
* from the request rather than the catalogue, so a host passing a URL segment through would
|
|
25
|
-
* hand this list to whoever types the URL.
|
|
26
|
-
*
|
|
27
|
-
* `pooled` is the soft part of the check: one candidate's "40 litre" backs "take 40 off"
|
|
28
|
-
* written about another. A `reason` and a `badge` read `bySku`, because they sit under a
|
|
29
|
-
* named product — off the pooled list, a badge reading "Only 2" stood on a tent's tag.
|
|
30
|
-
*/
|
|
31
8
|
export declare function hostFacts(input: TrackingInput): HostFacts;
|
|
32
9
|
export declare function claimIn(text: string): string | null;
|
|
33
10
|
//# sourceMappingURL=claim-screening.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-screening.d.ts","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"claim-screening.d.ts","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA2IlE,eAAO,MAAM,eAAe,UAAkE,CAAC;AAc/F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAOvD;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAWzD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnD"}
|
package/dist/claim-screening.js
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
import { offeredCandidates } from './model-prompt.js';
|
|
2
2
|
const CLAIM_PATTERNS = [
|
|
3
3
|
{
|
|
4
|
-
// Customers scoring the product. "rated for winter use", "rated to -10C"
|
|
5
|
-
// and "an IPX7 water rating" are about what the product can take.
|
|
6
4
|
kind: 'rating',
|
|
7
5
|
patterns: [
|
|
8
6
|
/\breviews?\b|\breviewed\b/,
|
|
9
7
|
/\b(?:\d+(?:\.\d+)?|three|four|five)[\s-]?stars?\b/,
|
|
10
8
|
/\bstars?[\s-]?ratings?\b/,
|
|
11
|
-
// A rating somebody gave it, rather than one it was built to.
|
|
12
9
|
/\b(?:customer|shopper|buyer|user|average|overall)[\s-]ratings?\b/,
|
|
13
|
-
// A score out of five is written as a decimal. A spec is "-5C" or "20,000mm".
|
|
14
10
|
/\bratings?\s+of\s+[0-5]\.\d\b/,
|
|
15
|
-
// The same score with the word "rating" nowhere near it: "4.8 out of 5".
|
|
16
11
|
/\b[0-5](?:\.\d)?\s+out of\s+(?:5|five)\b/,
|
|
17
12
|
/\b(?:highly|top|best|well|poorly|five|four)[\s-]rated\b/,
|
|
18
|
-
// "rated 4.8" is a score. "rated 3 season" is what the tent is built for.
|
|
19
13
|
/\brated\s+(?:[0-5]\.\d|(?:[0-5]|three|four|five)\s+(?:stars?|out of))\b/,
|
|
20
|
-
// How many other people bought or liked it is a customer claim too.
|
|
21
14
|
/\bbest[\s-]?sell(?:er|ers|ing)\b/,
|
|
22
|
-
// The words a model reaches for when "best" is banned. Each branch carries its own
|
|
23
|
-
// \b — one in front of the group demands a word character before the #, never there.
|
|
24
15
|
/(?:\bnumber one|\bno\.? ?1|#\s?1)[\s-]?sell(?:er|ers|ing)\b/,
|
|
25
16
|
/\bloved by (?:thousands|hundreds|millions|\d)/,
|
|
26
17
|
],
|
|
@@ -28,32 +19,21 @@ const CLAIM_PATTERNS = [
|
|
|
28
19
|
{
|
|
29
20
|
kind: 'price',
|
|
30
21
|
patterns: [
|
|
31
|
-
// No digit beside it: asking for one let "$thirty-nine and it is yours" through.
|
|
32
22
|
/[$£€¥₹]/,
|
|
33
23
|
/\b\d+(?:\.\d+)?\s?(?:usd|eur|gbp|dollars?|pounds?|euros?)\b/,
|
|
34
|
-
// The same spelled out. "pounds" is left off on purpose — a pack weighs two of those.
|
|
35
24
|
/\bdollars?\b|\beuros?\b/,
|
|
36
|
-
// The same codes on the other side of the number: "USD 20", "EUR 5.99".
|
|
37
25
|
/\b(?:usd|eur|gbp)\s?\d/,
|
|
38
|
-
// The krona is spelled out rather than drawn, so it needs a number beside it.
|
|
39
26
|
/\bkr\s?\d|\d\s?kr\b/,
|
|
40
27
|
/\bpric(?:e|es|ed|ing)\b/,
|
|
41
|
-
// A before-and-after is a price claim even with no currency on it.
|
|
42
28
|
/\bwas\s+[$£€¥]?\s?\d[\d,.]*\s*[,;–—-]?\s*now\s+[$£€¥]?\s?\d/,
|
|
43
|
-
// "does not feel cheap" is about quality. Only the comparison is money.
|
|
44
29
|
/\bcheap(?:er|est)\b/,
|
|
45
30
|
/\baffordable\b|\bbargain\b/,
|
|
46
|
-
// "at no cost to comfort" is not money. "costs less" is.
|
|
47
31
|
/\bcosts?\s+(?:less|more|only|just|about|around|[$£€¥]?\d)/,
|
|
48
32
|
/\blow(?:er)?[\s-]cost\b/,
|
|
49
|
-
// "saves weight" and "saves on weight" are both about grams.
|
|
50
33
|
/\bsaves?\s+(?:you\s+)?(?:money|cash|[$£€¥]\s?\d)/,
|
|
51
34
|
],
|
|
52
35
|
},
|
|
53
36
|
{
|
|
54
|
-
// A percentage in outdoor copy is a specification far more often than it is
|
|
55
|
-
// money off, so it only counts beside a money word: "20% off", not
|
|
56
|
-
// "100% recycled".
|
|
57
37
|
kind: 'discount',
|
|
58
38
|
patterns: [
|
|
59
39
|
/\d+(?:\.\d+)?\s?(?:%|percent)\s*(?:off\b|discount|reduction|less\b)/,
|
|
@@ -61,19 +41,12 @@ const CLAIM_PATTERNS = [
|
|
|
61
41
|
/\bdiscount(?:s|ed)?\b/,
|
|
62
42
|
/\bsale\b|\bmarked down\b|\bdeal of\b/,
|
|
63
43
|
/\bhalf[\s-]?(?:price|off)\b/,
|
|
64
|
-
// Money off with no percent sign anywhere on it: "save 20 off".
|
|
65
44
|
/\bsaves?\s+\d[\d.,]*\s+off\b/,
|
|
66
|
-
// "extra clearance for thick socks" is room inside the shoe.
|
|
67
45
|
/\bclearance\s+(?:sale|price|event|deal)\b|\bon clearance\b/,
|
|
68
|
-
// "reduced weight" and "reduced to 900g" are specifications. Only a
|
|
69
|
-
// reduced price, or a reduction with a date on it, is money off.
|
|
70
46
|
/\bprice reduced\b|\breduced price\b|\breduced by \d+\s?(?:%|percent)|\breduced\s+(?:this|next|last)\s+week\b/,
|
|
71
47
|
],
|
|
72
48
|
},
|
|
73
49
|
{
|
|
74
|
-
// When it turns up, not what turns up. "ships in a recycled box" and
|
|
75
|
-
// "arrives flat-packed" describe the thing, so the verb alone is not enough
|
|
76
|
-
// — it needs a day or a date beside it.
|
|
77
50
|
kind: 'delivery',
|
|
78
51
|
patterns: [
|
|
79
52
|
/\bdelivery\b|\bshipping\b/,
|
|
@@ -89,13 +62,11 @@ const CLAIM_PATTERNS = [
|
|
|
89
62
|
/\b(?:in|out of|low on) stock\b/,
|
|
90
63
|
/\blimited stock\b/,
|
|
91
64
|
/\brestocked?\b|\bsold out\b/,
|
|
92
|
-
// "the last few miles" is a distance, so a count needs "left" after it.
|
|
93
65
|
/\b(?:only\s+)?(?:\d+|a few|a handful|a couple|one|few)\s+(?:left|remain(?:s|ing)?)\b/,
|
|
94
66
|
/\bselling fast\b|\b(?:almost|nearly) gone\b|\bwhile stocks last\b/,
|
|
95
67
|
],
|
|
96
68
|
},
|
|
97
69
|
];
|
|
98
|
-
/** Letters that render as a Latin letter but are not one — a Cyrillic о in "in stоck". */
|
|
99
70
|
const CONFUSABLES = {
|
|
100
71
|
а: 'a',
|
|
101
72
|
в: 'b',
|
|
@@ -127,21 +98,9 @@ const CONFUSABLES = {
|
|
|
127
98
|
χ: 'x',
|
|
128
99
|
ϲ: 'c',
|
|
129
100
|
};
|
|
130
|
-
/**
|
|
131
|
-
* Characters that render as nothing: format characters, default-ignorable code points and the
|
|
132
|
-
* controls. Cf and Default_Ignorable each hold characters the other does not.
|
|
133
|
-
* A second copy of the class in attested's `hidden.ts`, pinned to it by a test.
|
|
134
|
-
*/
|
|
135
101
|
const INVISIBLE = /[\p{Cf}\p{Default_Ignorable_Code_Point}\p{Cc}]/gu;
|
|
136
|
-
/** The controls that do take room. Deleting these would read `Only 2\n3 left` as 23. */
|
|
137
102
|
const SPACING = /[\t\n\v\f\r\u0085]/;
|
|
138
|
-
/**
|
|
139
|
-
* Marks that hang on the character before them instead of taking a column of their own.
|
|
140
|
-
* Dropped after composition, so an accented e keeps its accent while an overline,
|
|
141
|
-
* which composes with nothing, cannot hide a word from its own rule.
|
|
142
|
-
*/
|
|
143
103
|
const MARKS = /[\p{Mn}\p{Me}]/gu;
|
|
144
|
-
/** Only the reading is normalised. What renders is what the model wrote. */
|
|
145
104
|
function normaliseForClaims(text) {
|
|
146
105
|
const lowered = text
|
|
147
106
|
.replace(INVISIBLE, (char) => (SPACING.test(char) ? char : ''))
|
|
@@ -154,31 +113,16 @@ function normaliseForClaims(text) {
|
|
|
154
113
|
folded += CONFUSABLES[char] ?? char;
|
|
155
114
|
return folded;
|
|
156
115
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Wording attested bans that this project has already ruled is not a claim. An allowance
|
|
159
|
-
* forgives a banned phrase sitting inside it, so an entry ending on a preposition — "extra
|
|
160
|
-
* clearance for" — forgives whatever follows it. Exported so a test can hold the list
|
|
161
|
-
* against attested's own.
|
|
162
|
-
*/
|
|
163
116
|
export const ALLOWED_PHRASES = ['does not feel cheap', "doesn't feel cheap", 'last few miles'];
|
|
164
|
-
/** Only a string with one of these in it can stand behind a numeral. */
|
|
165
117
|
const DIGIT = /\p{Nd}/u;
|
|
166
|
-
/** A fact whose whole content is a number in exponent notation. attested lays those out in place. */
|
|
167
118
|
const BARE_EXPONENT = /^[+-]?\d+(?:\.\d+)?[eE]([+-]?\d+)$/;
|
|
168
|
-
/** Wide enough for every finite JavaScript number, and for anything a shop sells. */
|
|
169
119
|
const MAX_EXPONENT = 1000;
|
|
170
|
-
// `1e2000000000` is twelve characters and lays out into a run long enough to end the process.
|
|
171
|
-
// attested caps that itself, but it is a peer dependency and a host's copy of it may be older.
|
|
172
120
|
function canLayOut(fact) {
|
|
173
121
|
const match = BARE_EXPONENT.exec(fact.trim());
|
|
174
122
|
if (match === null)
|
|
175
123
|
return true;
|
|
176
124
|
return Math.abs(Number(match[1] ?? '')) <= MAX_EXPONENT;
|
|
177
125
|
}
|
|
178
|
-
/**
|
|
179
|
-
* The numbers the shop published about one product: its category and tags, only the strings
|
|
180
|
-
* carrying a digit. Exported so a test can pin what never reaches attested.
|
|
181
|
-
*/
|
|
182
126
|
export function productFacts(product) {
|
|
183
127
|
const facts = [];
|
|
184
128
|
if (DIGIT.test(product.category) && canLayOut(product.category))
|
|
@@ -189,19 +133,6 @@ export function productFacts(product) {
|
|
|
189
133
|
}
|
|
190
134
|
return facts;
|
|
191
135
|
}
|
|
192
|
-
/**
|
|
193
|
-
* The shop's own numbers for this request, for attested to check numerals against.
|
|
194
|
-
*
|
|
195
|
-
* `offeredCandidates` rather than `input.candidates` — out of stock or past the prompt's cap
|
|
196
|
-
* means a product the model never saw. A title and a rating it is shown but told never to
|
|
197
|
-
* repeat, so a numeral out of one is one it was told not to write. The browsed category comes
|
|
198
|
-
* from the request rather than the catalogue, so a host passing a URL segment through would
|
|
199
|
-
* hand this list to whoever types the URL.
|
|
200
|
-
*
|
|
201
|
-
* `pooled` is the soft part of the check: one candidate's "40 litre" backs "take 40 off"
|
|
202
|
-
* written about another. A `reason` and a `badge` read `bySku`, because they sit under a
|
|
203
|
-
* named product — off the pooled list, a badge reading "Only 2" stood on a tent's tag.
|
|
204
|
-
*/
|
|
205
136
|
export function hostFacts(input) {
|
|
206
137
|
const pooled = new Set();
|
|
207
138
|
const bySku = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-screening.js","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,cAAc,GAA2C;IAC7D;QACE,
|
|
1
|
+
{"version":3,"file":"claim-screening.js","sourceRoot":"","sources":["../src/claim-screening.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,51 +3,11 @@ import type { ComponentProvider, TokenUsage } from './provider.js';
|
|
|
3
3
|
import { type RankOrder } from './product-selection.js';
|
|
4
4
|
import { type SpecCache } from './spec-cache.js';
|
|
5
5
|
import { type TrackingInputDraft } from './tracking-input.js';
|
|
6
|
-
/**
|
|
7
|
-
* Turns one tracking payload into one renderable component.
|
|
8
|
-
*
|
|
9
|
-
* Everything else in this package is a piece of that sentence; this module is
|
|
10
|
-
* the order they go in. It is deliberately the only place that knows the whole
|
|
11
|
-
* sequence, and it is written as a straight line so the sequence is readable:
|
|
12
|
-
*
|
|
13
|
-
* validate → digest → cache → generate → reconcile → render
|
|
14
|
-
*
|
|
15
|
-
* The single promise it makes to a caller is that `generate` always returns
|
|
16
|
-
* something renderable. A model that is slow, refusing, erroring, rate-limited
|
|
17
|
-
* or simply not configured produces the deterministic component instead. The
|
|
18
|
-
* only way it rejects is a malformed payload, which is a caller bug and should
|
|
19
|
-
* be loud.
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Reported exactly once per call to `generate`, whatever happened.
|
|
23
|
-
*
|
|
24
|
-
* One flat shape rather than a variant per outcome, because the numbers the
|
|
25
|
-
* evaluation needs are ratios over all calls — hit rate, fallback share, model
|
|
26
|
-
* calls and cost per thousand views. A variant that some callers do not emit
|
|
27
|
-
* makes every one of those ratios wrong by however many it skipped, which is
|
|
28
|
-
* what happened when requests that joined an in-flight generation reported
|
|
29
|
-
* nothing at all.
|
|
30
|
-
*/
|
|
31
6
|
export interface GenerationEvent {
|
|
32
|
-
/** Null when no key was computed, which means no provider was configured. */
|
|
33
7
|
key: string | null;
|
|
34
8
|
source: SpecSource;
|
|
35
|
-
/** Wall-clock milliseconds for the whole call. */
|
|
36
9
|
elapsedMs: number;
|
|
37
|
-
/**
|
|
38
|
-
* True for the caller that sent the request, on every outcome — including a
|
|
39
|
-
* call that timed out, errored or came back unparseable. Requests that joined
|
|
40
|
-
* an in-flight generation share its answer and its usage figures, so cost
|
|
41
|
-
* must be summed over this flag rather than over every event.
|
|
42
|
-
*
|
|
43
|
-
* It counts requests sent, which is an upper bound on requests billed: an
|
|
44
|
-
* adapter that throws before it reaches the vendor looks the same from here
|
|
45
|
-
* as one that throws after. An upper bound is the useful direction — the
|
|
46
|
-
* calls that produce nothing are the ones worth seeing, and reporting them as
|
|
47
|
-
* no call at all hides them completely.
|
|
48
|
-
*/
|
|
49
10
|
calledModel: boolean;
|
|
50
|
-
/** What reconciliation removed. Absent when no spec was reconciled. */
|
|
51
11
|
violations?: string[];
|
|
52
12
|
usage?: TokenUsage;
|
|
53
13
|
degradedReason?: DegradedReason;
|
|
@@ -55,46 +15,16 @@ export interface GenerationEvent {
|
|
|
55
15
|
cache?: 'hit' | 'miss' | 'error' | 'timeout';
|
|
56
16
|
}
|
|
57
17
|
export interface ComponentGeneratorOptions {
|
|
58
|
-
/**
|
|
59
|
-
* Omit to run without a model. That is a supported configuration rather than
|
|
60
|
-
* a stub: it is the control arm of the benchmark, and the right setting for
|
|
61
|
-
* anyone who has not yet decided on a provider.
|
|
62
|
-
*/
|
|
63
18
|
provider?: ComponentProvider | null;
|
|
64
|
-
/** Defaults to an in-process cache. Pass `createNullSpecCache()` to disable. */
|
|
65
19
|
cache?: SpecCache;
|
|
66
|
-
/**
|
|
67
|
-
* How long the model gets. Past this the deterministic component renders and
|
|
68
|
-
* the request is aborted. Defaults to 1500ms.
|
|
69
|
-
*/
|
|
70
20
|
modelTimeoutMs?: number;
|
|
71
|
-
/**
|
|
72
|
-
* How long the cache gets. The shipped caches cannot exceed it, but the store
|
|
73
|
-
* is a port a host implements — a hung Redis read on the render path would
|
|
74
|
-
* hold the page open, which is exactly what this module exists to prevent.
|
|
75
|
-
*/
|
|
76
21
|
cacheTimeoutMs?: number;
|
|
77
|
-
/**
|
|
78
|
-
* 'cohort' shares one generated component between shoppers who look alike and
|
|
79
|
-
* fills in each shopper's own products. 'per-shopper' generates for the
|
|
80
|
-
* individual, which is what the benchmark compares against. Defaults to
|
|
81
|
-
* 'cohort'.
|
|
82
|
-
*/
|
|
83
22
|
generation?: 'cohort' | 'per-shopper';
|
|
84
|
-
/**
|
|
85
|
-
* How the products are ordered. 'signals' scores each candidate from this
|
|
86
|
-
* shopper's signals. 'given' keeps the order you sent, for a shop whose own
|
|
87
|
-
* ranking is better than four weights. Either way the exclusions and the
|
|
88
|
-
* stock check still apply, and each product still carries a basis
|
|
89
|
-
* reconciliation can verify. Defaults to 'signals'.
|
|
90
|
-
*/
|
|
91
23
|
rank?: RankOrder;
|
|
92
|
-
/** Observability. Never allowed to break a render. */
|
|
93
24
|
onEvent?: (event: GenerationEvent) => void;
|
|
94
25
|
}
|
|
95
26
|
export interface ComponentGenerator {
|
|
96
27
|
generate(input: TrackingInputDraft): Promise<ComponentSpec>;
|
|
97
|
-
/** The deterministic component, without consulting a model or a cache. */
|
|
98
28
|
generateDeterministic(input: TrackingInputDraft): ComponentSpec;
|
|
99
29
|
}
|
|
100
30
|
export declare class TimeoutError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-generator.d.ts","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAkB,KAAK,SAAS,EAAoB,MAAM,wBAAwB,CAAC;AAG1F,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B
|
|
1
|
+
{"version":3,"file":"component-generator.d.ts","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAkB,KAAK,SAAS,EAAoB,MAAM,wBAAwB,CAAC;AAG1F,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,OAAO,CAAC;IAErB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEpC,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,UAAU,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IAEtC,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE5D,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAAC;CACjE;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAG9C;CACF;AAkGD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,kBAAkB,CA2LpB"}
|
|
@@ -14,12 +14,6 @@ export class TimeoutError extends Error {
|
|
|
14
14
|
this.name = 'TimeoutError';
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Once the deadline has fired the caller is told so, whatever the race actually
|
|
19
|
-
* settled with: a provider honouring its half of the contract rejects from
|
|
20
|
-
* inside the `abort()` below, so its rejection reaches the race first and the
|
|
21
|
-
* deadline's own never wins.
|
|
22
|
-
*/
|
|
23
17
|
async function withinBudget(label, milliseconds, start) {
|
|
24
18
|
const controller = new AbortController();
|
|
25
19
|
let timer;
|
|
@@ -55,19 +49,10 @@ function createSingleFlight() {
|
|
|
55
49
|
},
|
|
56
50
|
};
|
|
57
51
|
}
|
|
58
|
-
/** A cache entry is no more trustworthy than model output, so it is parsed too. */
|
|
59
52
|
const cachedSpecSchema = z.object({
|
|
60
53
|
spec: generatedSpecSchema,
|
|
61
54
|
generatedAt: z.number(),
|
|
62
55
|
});
|
|
63
|
-
/**
|
|
64
|
-
* The arithmetic has to hold whatever order the model put the blocks in, so it
|
|
65
|
-
* is written as one sum over the whole spec rather than as a running budget:
|
|
66
|
-
* the grid gets `maxItems` minus every distinct product the set and the heroes
|
|
67
|
-
* will place. Reconciliation then reaches the same set this did — it can only
|
|
68
|
-
* ever have more placed than the pre-choice assumed, and never one of the set's
|
|
69
|
-
* own products.
|
|
70
|
-
*/
|
|
71
56
|
function fitCohortSpec(spec, input, digest, rank) {
|
|
72
57
|
const picks = selectProducts(input, digest, { rank });
|
|
73
58
|
const blocks = capBlocks(spec.blocks);
|
|
@@ -82,8 +67,6 @@ function fitCohortSpec(spec, input, digest, rank) {
|
|
|
82
67
|
}
|
|
83
68
|
if (!hasBundleBlock)
|
|
84
69
|
return fitToShopper(spec, picks, digest.maxItems);
|
|
85
|
-
// Only the heroes above the bundle block are placed when it is reached, so
|
|
86
|
-
// they are all the choice may account for.
|
|
87
70
|
const chosen = bundleForShopper(input, digest, placeableHeroSkus(aboveBundle, input));
|
|
88
71
|
if (!chosen)
|
|
89
72
|
return fitToShopper(spec, picks, digest.maxItems);
|
|
@@ -91,7 +74,6 @@ function fitCohortSpec(spec, input, digest, rank) {
|
|
|
91
74
|
for (const sku of placeableHeroSkus(blocks, input))
|
|
92
75
|
spokenFor.add(sku);
|
|
93
76
|
const roomLeft = digest.maxItems - spokenFor.size;
|
|
94
|
-
// A set is worth showing, but not at the cost of an empty grid.
|
|
95
77
|
if (roomLeft <= 0)
|
|
96
78
|
return fitToShopper(spec, picks, digest.maxItems);
|
|
97
79
|
const forGrid = [];
|
|
@@ -101,9 +83,6 @@ function fitCohortSpec(spec, input, digest, rank) {
|
|
|
101
83
|
}
|
|
102
84
|
return fitToShopper(spec, forGrid, roomLeft);
|
|
103
85
|
}
|
|
104
|
-
function withProvenance(spec, provenance) {
|
|
105
|
-
return { ...spec, specVersion: SPEC_VERSION, ...provenance };
|
|
106
|
-
}
|
|
107
86
|
export function createComponentGenerator(options = {}) {
|
|
108
87
|
const provider = options.provider ?? null;
|
|
109
88
|
const cache = options.cache ?? createMemorySpecCache();
|
|
@@ -118,9 +97,7 @@ export function createComponentGenerator(options = {}) {
|
|
|
118
97
|
try {
|
|
119
98
|
options.onEvent(event);
|
|
120
99
|
}
|
|
121
|
-
catch {
|
|
122
|
-
// A broken metrics hook must not take down a page.
|
|
123
|
-
}
|
|
100
|
+
catch { }
|
|
124
101
|
};
|
|
125
102
|
const buildDeterministic = (input, digest, startedAt, key, degradedReason, modelCall = {
|
|
126
103
|
calledModel: false,
|
|
@@ -133,7 +110,9 @@ export function createComponentGenerator(options = {}) {
|
|
|
133
110
|
...modelCall,
|
|
134
111
|
degradedReason,
|
|
135
112
|
});
|
|
136
|
-
return
|
|
113
|
+
return {
|
|
114
|
+
...buildFallbackSpec(input, digest, { rank }),
|
|
115
|
+
specVersion: SPEC_VERSION,
|
|
137
116
|
slot: digest.slot,
|
|
138
117
|
source: 'fallback',
|
|
139
118
|
generatedAt: finishedAt,
|
|
@@ -141,7 +120,7 @@ export function createComponentGenerator(options = {}) {
|
|
|
141
120
|
provider: null,
|
|
142
121
|
model: null,
|
|
143
122
|
degradedReason,
|
|
144
|
-
}
|
|
123
|
+
};
|
|
145
124
|
};
|
|
146
125
|
const readCache = async (key) => {
|
|
147
126
|
try {
|
|
@@ -150,28 +129,17 @@ export function createComponentGenerator(options = {}) {
|
|
|
150
129
|
return parsed.success ? { outcome: 'hit', entry: parsed.data } : { outcome: 'miss' };
|
|
151
130
|
}
|
|
152
131
|
catch (error) {
|
|
153
|
-
// A store that is down or slow degrades to generating, not to an error page.
|
|
154
132
|
return { outcome: error instanceof TimeoutError ? 'timeout' : 'error' };
|
|
155
133
|
}
|
|
156
134
|
};
|
|
157
|
-
/** The `Promise.resolve` wrapper catches a store that throws synchronously rather than rejecting. */
|
|
158
135
|
const storeInBackground = (key, cached) => {
|
|
159
136
|
void Promise.resolve()
|
|
160
137
|
.then(() => cache.set(key, cached))
|
|
161
|
-
.catch(() => {
|
|
162
|
-
// A store that cannot be written is not a reason to fail a render.
|
|
163
|
-
});
|
|
138
|
+
.catch(() => { });
|
|
164
139
|
};
|
|
165
|
-
/**
|
|
166
|
-
* Deliberately does not decide whether the answer is usable. That depends on
|
|
167
|
-
* the asking shopper's live facts — stock, dislikes, what is in their basket
|
|
168
|
-
* — and none of those are in the cache key, so a verdict reached here would
|
|
169
|
-
* be handed to every request that joined this one.
|
|
170
|
-
*/
|
|
171
140
|
const askModel = async (active, input, promptDigest) => {
|
|
172
141
|
const { system, user } = buildPrompt(input, promptDigest);
|
|
173
142
|
const result = await withinBudget('generation', modelTimeoutMs, (signal) => active.generate({ system, user, schema: generatedSpecSchema, signal }));
|
|
174
|
-
// Providers return parsed objects, but the shape is still model output.
|
|
175
143
|
const parsed = generatedSpecSchema.safeParse(result.spec);
|
|
176
144
|
return {
|
|
177
145
|
spec: parsed.success ? parsed.data : null,
|
|
@@ -186,8 +154,6 @@ export function createComponentGenerator(options = {}) {
|
|
|
186
154
|
},
|
|
187
155
|
async generate(draft) {
|
|
188
156
|
const startedAt = Date.now();
|
|
189
|
-
// Deliberately unguarded: an invalid payload is a caller bug, not a
|
|
190
|
-
// degraded render.
|
|
191
157
|
const input = parseTrackingInput(draft);
|
|
192
158
|
const digest = buildDigest(input);
|
|
193
159
|
if (!provider) {
|
|
@@ -202,15 +168,12 @@ export function createComponentGenerator(options = {}) {
|
|
|
202
168
|
const cached = read.entry;
|
|
203
169
|
let calledModel = false;
|
|
204
170
|
let answer;
|
|
205
|
-
// When the model produced this, not when it was served.
|
|
206
171
|
let generatedAt;
|
|
207
172
|
if (cached) {
|
|
208
173
|
answer = { spec: cached.spec };
|
|
209
174
|
generatedAt = cached.generatedAt;
|
|
210
175
|
}
|
|
211
176
|
else {
|
|
212
|
-
// Asked before joining, because by the time the shared promise settles
|
|
213
|
-
// the entry is gone and there is no way to tell a leader from a follower.
|
|
214
177
|
calledModel = !singleFlight.isRunning(key);
|
|
215
178
|
let call;
|
|
216
179
|
try {
|
|
@@ -233,16 +196,9 @@ export function createComponentGenerator(options = {}) {
|
|
|
233
196
|
}
|
|
234
197
|
answer = { spec: call.spec, ...(call.usage ? { usage: call.usage } : {}) };
|
|
235
198
|
generatedAt = Date.now();
|
|
236
|
-
// Stored unreconciled on purpose, and stored even when it is unusable
|
|
237
|
-
// for this shopper. Reconciliation narrows a spec to one shopper's live
|
|
238
|
-
// facts, and those move independently of the key — a product can sell
|
|
239
|
-
// out and come back without the candidate list changing.
|
|
240
199
|
if (calledModel)
|
|
241
200
|
storeInBackground(key, { spec: answer.spec, generatedAt });
|
|
242
201
|
}
|
|
243
|
-
// A cohort spec names products chosen for whoever asked first, and every
|
|
244
|
-
// reason under one of them is written here rather than by the model. In
|
|
245
|
-
// per-shopper mode this stays empty and every reason is screened.
|
|
246
202
|
const { spec: served, ourReasons } = generation === 'cohort'
|
|
247
203
|
? fitCohortSpec(answer.spec, input, digest, rank)
|
|
248
204
|
: { spec: answer.spec, ourReasons: new Map() };
|
|
@@ -266,14 +222,16 @@ export function createComponentGenerator(options = {}) {
|
|
|
266
222
|
violations: reconciled.violations,
|
|
267
223
|
...(answer.usage ? { usage: answer.usage } : {}),
|
|
268
224
|
});
|
|
269
|
-
return
|
|
225
|
+
return {
|
|
226
|
+
...reconciled.spec,
|
|
227
|
+
specVersion: SPEC_VERSION,
|
|
270
228
|
slot: digest.slot,
|
|
271
229
|
source,
|
|
272
230
|
generatedAt,
|
|
273
231
|
latencyMs: finishedAt - startedAt,
|
|
274
232
|
provider: provider.name,
|
|
275
233
|
model: provider.model,
|
|
276
|
-
}
|
|
234
|
+
};
|
|
277
235
|
},
|
|
278
236
|
};
|
|
279
237
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-generator.js","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,mBAAmB,GAMpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,cAAc,EAAoC,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAmB,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAqB,MAAM,oBAAoB,CAAC;AACpF,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,YAAY,GAGb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,GAGnB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"component-generator.js","sourceRoot":"","sources":["../src/component-generator.ts"],"names":[],"mappings":"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"}
|