@rudra-js/core 0.1.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/LICENSE +21 -0
- package/README.md +189 -0
- package/dist/component-generator.d.ts +84 -0
- package/dist/component-generator.d.ts.map +1 -0
- package/dist/component-generator.js +331 -0
- package/dist/component-generator.js.map +1 -0
- package/dist/component-spec.d.ts +426 -0
- package/dist/component-spec.d.ts.map +1 -0
- package/dist/component-spec.js +170 -0
- package/dist/component-spec.js.map +1 -0
- package/dist/fallback-component.d.ts +11 -0
- package/dist/fallback-component.d.ts.map +1 -0
- package/dist/fallback-component.js +69 -0
- package/dist/fallback-component.js.map +1 -0
- package/dist/fit-to-shopper.d.ts +4 -0
- package/dist/fit-to-shopper.d.ts.map +1 -0
- package/dist/fit-to-shopper.js +36 -0
- package/dist/fit-to-shopper.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/model-prompt.d.ts +29 -0
- package/dist/model-prompt.d.ts.map +1 -0
- package/dist/model-prompt.js +227 -0
- package/dist/model-prompt.js.map +1 -0
- package/dist/product-selection.d.ts +33 -0
- package/dist/product-selection.d.ts.map +1 -0
- package/dist/product-selection.js +102 -0
- package/dist/product-selection.js.map +1 -0
- package/dist/provider.d.ts +80 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +23 -0
- package/dist/provider.js.map +1 -0
- package/dist/reconciliation.d.ts +49 -0
- package/dist/reconciliation.d.ts.map +1 -0
- package/dist/reconciliation.js +564 -0
- package/dist/reconciliation.js.map +1 -0
- package/dist/signal-digest.d.ts +66 -0
- package/dist/signal-digest.d.ts.map +1 -0
- package/dist/signal-digest.js +224 -0
- package/dist/signal-digest.js.map +1 -0
- package/dist/spec-cache.d.ts +88 -0
- package/dist/spec-cache.d.ts.map +1 -0
- package/dist/spec-cache.js +152 -0
- package/dist/spec-cache.js.map +1 -0
- package/dist/tracking-input.d.ts +258 -0
- package/dist/tracking-input.d.ts.map +1 -0
- package/dist/tracking-input.js +241 -0
- package/dist/tracking-input.js.map +1 -0
- package/package.json +60 -0
- package/src/component-generator.ts +521 -0
- package/src/component-spec.ts +243 -0
- package/src/fallback-component.ts +77 -0
- package/src/fit-to-shopper.ts +45 -0
- package/src/index.ts +102 -0
- package/src/model-prompt.ts +258 -0
- package/src/product-selection.ts +153 -0
- package/src/provider.ts +98 -0
- package/src/reconciliation.ts +675 -0
- package/src/signal-digest.ts +335 -0
- package/src/spec-cache.ts +223 -0
- package/src/tracking-input.ts +300 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The component specification — the only thing a language model is ever allowed
|
|
4
|
+
* to return.
|
|
5
|
+
*
|
|
6
|
+
* The model chooses layout, ordering, emphasis, copy, the recommendation
|
|
7
|
+
* strategy behind each pick, and which of the host's candidate products to
|
|
8
|
+
* surface. It never returns markup, code, URLs, prices,
|
|
9
|
+
* product titles, or images. Every field is an enum, a bounded number, a SKU
|
|
10
|
+
* reference resolved against the candidate set, or free text that is clamped
|
|
11
|
+
* and escaped before it renders. That is what makes generated output safe to
|
|
12
|
+
* put in a server-rendered response.
|
|
13
|
+
*
|
|
14
|
+
* Two constraints shape this file, and both are easy to undo by accident:
|
|
15
|
+
*
|
|
16
|
+
* 1. It doubles as a provider structured-output schema, so it avoids
|
|
17
|
+
* recursion, string length bounds, and numeric ranges. Providers reject
|
|
18
|
+
* schemas that use them in strict mode. Bounds are enforced afterwards, in
|
|
19
|
+
* reconciliation, where a violation can be repaired rather than fatal.
|
|
20
|
+
* 2. Optionality is `.nullable()` rather than `.optional()`, because strict
|
|
21
|
+
* structured outputs require every declared property to be present. "No
|
|
22
|
+
* badge" has to be expressible as `null`, not as a missing key.
|
|
23
|
+
*
|
|
24
|
+
* A test asserts both by converting this schema to JSON Schema, so neither is
|
|
25
|
+
* left to a comment nobody reads.
|
|
26
|
+
*
|
|
27
|
+
* Note the asymmetry with `tracking-input`, which rejects unknown fields: these
|
|
28
|
+
* schemas strip them. The inputs differ in kind. An unrecognised key in a host
|
|
29
|
+
* payload is a typo, and silently dropping it costs the shopper their history,
|
|
30
|
+
* so it must be loud. An unrecognised key from a model is drift, and discarding
|
|
31
|
+
* it is exactly right — rejecting the whole spec over one stray key would cost
|
|
32
|
+
* the shopper the component. Either way the renderer only ever sees the fields
|
|
33
|
+
* declared here, which is the property that matters.
|
|
34
|
+
*/
|
|
35
|
+
/** Overall voice of the component. */
|
|
36
|
+
export declare const TONES: readonly ['neutral', 'enthusiastic', 'urgent', 'editorial'];
|
|
37
|
+
/** What a banner is claiming. */
|
|
38
|
+
export declare const BANNER_TONES: readonly ['info', 'promo', 'urgency', 'restock'];
|
|
39
|
+
/** How prominently a single product is placed. */
|
|
40
|
+
export declare const EMPHASIS: readonly ['normal', 'featured'];
|
|
41
|
+
/**
|
|
42
|
+
* Why a product was chosen — the recommendation strategy behind the pick.
|
|
43
|
+
*
|
|
44
|
+
* This is a closed set rather than free text because every value here is a
|
|
45
|
+
* factual claim about the shopper, and a claim the server can check. "Because
|
|
46
|
+
* you viewed this" written as prose is unverifiable: nothing downstream can
|
|
47
|
+
* tell whether the shopper viewed anything. Declared as `most_viewed`,
|
|
48
|
+
* reconciliation can look it up in the digest and drop the claim if it is
|
|
49
|
+
* false.
|
|
50
|
+
*
|
|
51
|
+
* Every value is deliberately checkable against data already in hand — the
|
|
52
|
+
* signal digest or the host's own catalog. A basis that needs data the
|
|
53
|
+
* framework never receives (`new_arrival`, `back_in_stock`, `trending_today`)
|
|
54
|
+
* is not in this list, because it could only ever be taken on trust.
|
|
55
|
+
*/
|
|
56
|
+
export declare const RECOMMENDATION_BASES: readonly [
|
|
57
|
+
/** Same category as the product being viewed. */
|
|
58
|
+
'similar_to_current',
|
|
59
|
+
/** The shopper has viewed this product. */
|
|
60
|
+
'most_viewed',
|
|
61
|
+
/** Goes with something already in the cart. */
|
|
62
|
+
'complements_cart',
|
|
63
|
+
/** Goes with something the shopper has bought. */
|
|
64
|
+
'complements_purchase',
|
|
65
|
+
/** In a category the shopper's signals favour. */
|
|
66
|
+
'liked_category',
|
|
67
|
+
/** No claim about this shopper at all — the safe default. */
|
|
68
|
+
'popular'];
|
|
69
|
+
/** A reference to one product from the candidate set, and why it was chosen. */
|
|
70
|
+
export declare const productReferenceSchema: z.ZodObject<{
|
|
71
|
+
sku: z.ZodString;
|
|
72
|
+
basis: z.ZodEnum<{
|
|
73
|
+
complements_cart: "complements_cart";
|
|
74
|
+
complements_purchase: "complements_purchase";
|
|
75
|
+
liked_category: "liked_category";
|
|
76
|
+
most_viewed: "most_viewed";
|
|
77
|
+
popular: "popular";
|
|
78
|
+
similar_to_current: "similar_to_current";
|
|
79
|
+
}>;
|
|
80
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
81
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
82
|
+
emphasis: z.ZodEnum<{
|
|
83
|
+
featured: "featured";
|
|
84
|
+
normal: "normal";
|
|
85
|
+
}>;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
export type ProductReference = z.infer<typeof productReferenceSchema>;
|
|
88
|
+
export type RecommendationBasis = (typeof RECOMMENDATION_BASES)[number];
|
|
89
|
+
/** One large featured statement, optionally anchored to a single product. */
|
|
90
|
+
declare const heroBlockSchema: z.ZodObject<{
|
|
91
|
+
kind: z.ZodLiteral<"hero">;
|
|
92
|
+
headline: z.ZodString;
|
|
93
|
+
body: z.ZodNullable<z.ZodString>;
|
|
94
|
+
sku: z.ZodNullable<z.ZodString>;
|
|
95
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
96
|
+
}, z.core.$strip>;
|
|
97
|
+
/** The general-purpose choice when several products are comparably relevant. */
|
|
98
|
+
declare const gridBlockSchema: z.ZodObject<{
|
|
99
|
+
kind: z.ZodLiteral<"grid">;
|
|
100
|
+
title: z.ZodNullable<z.ZodString>;
|
|
101
|
+
columns: z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>;
|
|
102
|
+
items: z.ZodArray<z.ZodObject<{
|
|
103
|
+
sku: z.ZodString;
|
|
104
|
+
basis: z.ZodEnum<{
|
|
105
|
+
complements_cart: "complements_cart";
|
|
106
|
+
complements_purchase: "complements_purchase";
|
|
107
|
+
liked_category: "liked_category";
|
|
108
|
+
most_viewed: "most_viewed";
|
|
109
|
+
popular: "popular";
|
|
110
|
+
similar_to_current: "similar_to_current";
|
|
111
|
+
}>;
|
|
112
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
113
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
114
|
+
emphasis: z.ZodEnum<{
|
|
115
|
+
featured: "featured";
|
|
116
|
+
normal: "normal";
|
|
117
|
+
}>;
|
|
118
|
+
}, z.core.$strip>>;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
/** A horizontally scanned row, for when order implies a ranking. */
|
|
121
|
+
declare const carouselBlockSchema: z.ZodObject<{
|
|
122
|
+
kind: z.ZodLiteral<"carousel">;
|
|
123
|
+
title: z.ZodNullable<z.ZodString>;
|
|
124
|
+
items: z.ZodArray<z.ZodObject<{
|
|
125
|
+
sku: z.ZodString;
|
|
126
|
+
basis: z.ZodEnum<{
|
|
127
|
+
complements_cart: "complements_cart";
|
|
128
|
+
complements_purchase: "complements_purchase";
|
|
129
|
+
liked_category: "liked_category";
|
|
130
|
+
most_viewed: "most_viewed";
|
|
131
|
+
popular: "popular";
|
|
132
|
+
similar_to_current: "similar_to_current";
|
|
133
|
+
}>;
|
|
134
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
135
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
136
|
+
emphasis: z.ZodEnum<{
|
|
137
|
+
featured: "featured";
|
|
138
|
+
normal: "normal";
|
|
139
|
+
}>;
|
|
140
|
+
}, z.core.$strip>>;
|
|
141
|
+
}, z.core.$strip>;
|
|
142
|
+
/** A single line of merchandising copy. */
|
|
143
|
+
declare const bannerBlockSchema: z.ZodObject<{
|
|
144
|
+
kind: z.ZodLiteral<"banner">;
|
|
145
|
+
tone: z.ZodEnum<{
|
|
146
|
+
info: "info";
|
|
147
|
+
promo: "promo";
|
|
148
|
+
restock: "restock";
|
|
149
|
+
urgency: "urgency";
|
|
150
|
+
}>;
|
|
151
|
+
text: z.ZodString;
|
|
152
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
153
|
+
}, z.core.$strip>;
|
|
154
|
+
/** Short editorial prose, for explaining the theme of a selection. */
|
|
155
|
+
declare const copyBlockSchema: z.ZodObject<{
|
|
156
|
+
kind: z.ZodLiteral<"copy">;
|
|
157
|
+
title: z.ZodNullable<z.ZodString>;
|
|
158
|
+
body: z.ZodString;
|
|
159
|
+
}, z.core.$strip>;
|
|
160
|
+
/** A set the shop sells together. The shop picks which one; this only asks for it. */
|
|
161
|
+
declare const bundleBlockSchema: z.ZodObject<{
|
|
162
|
+
kind: z.ZodLiteral<"bundle">;
|
|
163
|
+
title: z.ZodNullable<z.ZodString>;
|
|
164
|
+
body: z.ZodNullable<z.ZodString>;
|
|
165
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
166
|
+
bundleId: z.ZodNullable<z.ZodString>;
|
|
167
|
+
}, z.core.$strip>;
|
|
168
|
+
/** Blocks do not nest. The vocabulary is closed on purpose. */
|
|
169
|
+
export declare const blockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
170
|
+
kind: z.ZodLiteral<"hero">;
|
|
171
|
+
headline: z.ZodString;
|
|
172
|
+
body: z.ZodNullable<z.ZodString>;
|
|
173
|
+
sku: z.ZodNullable<z.ZodString>;
|
|
174
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
175
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
176
|
+
kind: z.ZodLiteral<"grid">;
|
|
177
|
+
title: z.ZodNullable<z.ZodString>;
|
|
178
|
+
columns: z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>;
|
|
179
|
+
items: z.ZodArray<z.ZodObject<{
|
|
180
|
+
sku: z.ZodString;
|
|
181
|
+
basis: z.ZodEnum<{
|
|
182
|
+
complements_cart: "complements_cart";
|
|
183
|
+
complements_purchase: "complements_purchase";
|
|
184
|
+
liked_category: "liked_category";
|
|
185
|
+
most_viewed: "most_viewed";
|
|
186
|
+
popular: "popular";
|
|
187
|
+
similar_to_current: "similar_to_current";
|
|
188
|
+
}>;
|
|
189
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
190
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
191
|
+
emphasis: z.ZodEnum<{
|
|
192
|
+
featured: "featured";
|
|
193
|
+
normal: "normal";
|
|
194
|
+
}>;
|
|
195
|
+
}, z.core.$strip>>;
|
|
196
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
197
|
+
kind: z.ZodLiteral<"carousel">;
|
|
198
|
+
title: z.ZodNullable<z.ZodString>;
|
|
199
|
+
items: z.ZodArray<z.ZodObject<{
|
|
200
|
+
sku: z.ZodString;
|
|
201
|
+
basis: z.ZodEnum<{
|
|
202
|
+
complements_cart: "complements_cart";
|
|
203
|
+
complements_purchase: "complements_purchase";
|
|
204
|
+
liked_category: "liked_category";
|
|
205
|
+
most_viewed: "most_viewed";
|
|
206
|
+
popular: "popular";
|
|
207
|
+
similar_to_current: "similar_to_current";
|
|
208
|
+
}>;
|
|
209
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
210
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
211
|
+
emphasis: z.ZodEnum<{
|
|
212
|
+
featured: "featured";
|
|
213
|
+
normal: "normal";
|
|
214
|
+
}>;
|
|
215
|
+
}, z.core.$strip>>;
|
|
216
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
217
|
+
kind: z.ZodLiteral<"banner">;
|
|
218
|
+
tone: z.ZodEnum<{
|
|
219
|
+
info: "info";
|
|
220
|
+
promo: "promo";
|
|
221
|
+
restock: "restock";
|
|
222
|
+
urgency: "urgency";
|
|
223
|
+
}>;
|
|
224
|
+
text: z.ZodString;
|
|
225
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
226
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
227
|
+
kind: z.ZodLiteral<"copy">;
|
|
228
|
+
title: z.ZodNullable<z.ZodString>;
|
|
229
|
+
body: z.ZodString;
|
|
230
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
231
|
+
kind: z.ZodLiteral<"bundle">;
|
|
232
|
+
title: z.ZodNullable<z.ZodString>;
|
|
233
|
+
body: z.ZodNullable<z.ZodString>;
|
|
234
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
235
|
+
bundleId: z.ZodNullable<z.ZodString>;
|
|
236
|
+
}, z.core.$strip>], "kind">;
|
|
237
|
+
export type Block = z.infer<typeof blockSchema>;
|
|
238
|
+
export type BlockKind = Block['kind'];
|
|
239
|
+
export type HeroBlock = z.infer<typeof heroBlockSchema>;
|
|
240
|
+
export type GridBlock = z.infer<typeof gridBlockSchema>;
|
|
241
|
+
export type CarouselBlock = z.infer<typeof carouselBlockSchema>;
|
|
242
|
+
export type BannerBlock = z.infer<typeof bannerBlockSchema>;
|
|
243
|
+
export type CopyBlock = z.infer<typeof copyBlockSchema>;
|
|
244
|
+
export type BundleBlock = z.infer<typeof bundleBlockSchema>;
|
|
245
|
+
/**
|
|
246
|
+
* Exactly what the model must return. Provenance — version, slot, latency, which
|
|
247
|
+
* provider answered — is added by the server afterwards, and is deliberately not
|
|
248
|
+
* part of the model's burden.
|
|
249
|
+
*/
|
|
250
|
+
export declare const generatedSpecSchema: z.ZodObject<{
|
|
251
|
+
tone: z.ZodEnum<{
|
|
252
|
+
editorial: "editorial";
|
|
253
|
+
enthusiastic: "enthusiastic";
|
|
254
|
+
neutral: "neutral";
|
|
255
|
+
urgent: "urgent";
|
|
256
|
+
}>;
|
|
257
|
+
headline: z.ZodString;
|
|
258
|
+
subheadline: z.ZodNullable<z.ZodString>;
|
|
259
|
+
blocks: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
260
|
+
kind: z.ZodLiteral<"hero">;
|
|
261
|
+
headline: z.ZodString;
|
|
262
|
+
body: z.ZodNullable<z.ZodString>;
|
|
263
|
+
sku: z.ZodNullable<z.ZodString>;
|
|
264
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
265
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
266
|
+
kind: z.ZodLiteral<"grid">;
|
|
267
|
+
title: z.ZodNullable<z.ZodString>;
|
|
268
|
+
columns: z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>;
|
|
269
|
+
items: z.ZodArray<z.ZodObject<{
|
|
270
|
+
sku: z.ZodString;
|
|
271
|
+
basis: z.ZodEnum<{
|
|
272
|
+
complements_cart: "complements_cart";
|
|
273
|
+
complements_purchase: "complements_purchase";
|
|
274
|
+
liked_category: "liked_category";
|
|
275
|
+
most_viewed: "most_viewed";
|
|
276
|
+
popular: "popular";
|
|
277
|
+
similar_to_current: "similar_to_current";
|
|
278
|
+
}>;
|
|
279
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
280
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
281
|
+
emphasis: z.ZodEnum<{
|
|
282
|
+
featured: "featured";
|
|
283
|
+
normal: "normal";
|
|
284
|
+
}>;
|
|
285
|
+
}, z.core.$strip>>;
|
|
286
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
287
|
+
kind: z.ZodLiteral<"carousel">;
|
|
288
|
+
title: z.ZodNullable<z.ZodString>;
|
|
289
|
+
items: z.ZodArray<z.ZodObject<{
|
|
290
|
+
sku: z.ZodString;
|
|
291
|
+
basis: z.ZodEnum<{
|
|
292
|
+
complements_cart: "complements_cart";
|
|
293
|
+
complements_purchase: "complements_purchase";
|
|
294
|
+
liked_category: "liked_category";
|
|
295
|
+
most_viewed: "most_viewed";
|
|
296
|
+
popular: "popular";
|
|
297
|
+
similar_to_current: "similar_to_current";
|
|
298
|
+
}>;
|
|
299
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
300
|
+
badge: z.ZodNullable<z.ZodString>;
|
|
301
|
+
emphasis: z.ZodEnum<{
|
|
302
|
+
featured: "featured";
|
|
303
|
+
normal: "normal";
|
|
304
|
+
}>;
|
|
305
|
+
}, z.core.$strip>>;
|
|
306
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
307
|
+
kind: z.ZodLiteral<"banner">;
|
|
308
|
+
tone: z.ZodEnum<{
|
|
309
|
+
info: "info";
|
|
310
|
+
promo: "promo";
|
|
311
|
+
restock: "restock";
|
|
312
|
+
urgency: "urgency";
|
|
313
|
+
}>;
|
|
314
|
+
text: z.ZodString;
|
|
315
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
316
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
317
|
+
kind: z.ZodLiteral<"copy">;
|
|
318
|
+
title: z.ZodNullable<z.ZodString>;
|
|
319
|
+
body: z.ZodString;
|
|
320
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
321
|
+
kind: z.ZodLiteral<"bundle">;
|
|
322
|
+
title: z.ZodNullable<z.ZodString>;
|
|
323
|
+
body: z.ZodNullable<z.ZodString>;
|
|
324
|
+
ctaLabel: z.ZodNullable<z.ZodString>;
|
|
325
|
+
bundleId: z.ZodNullable<z.ZodString>;
|
|
326
|
+
}, z.core.$strip>], "kind">>;
|
|
327
|
+
rationale: z.ZodString;
|
|
328
|
+
}, z.core.$strip>;
|
|
329
|
+
export type GeneratedSpec = z.infer<typeof generatedSpecSchema>;
|
|
330
|
+
/** How a spec came to exist. Surfaced for benchmarking and observability. */
|
|
331
|
+
export type SpecSource = 'llm' | 'cache' | 'fallback';
|
|
332
|
+
/**
|
|
333
|
+
* Why a component is not what the model would have produced.
|
|
334
|
+
*
|
|
335
|
+
* A closed set, like `SpecSource`: it is rendered into pages as
|
|
336
|
+
* `data-rudra-degraded` and counted in dashboards, so a host needs to know what
|
|
337
|
+
* it can receive without reading the generator.
|
|
338
|
+
*/
|
|
339
|
+
export type DegradedReason =
|
|
340
|
+
/** No provider was configured, so nothing was ever asked. */
|
|
341
|
+
'no-provider'
|
|
342
|
+
/** The provider errored, or threw before it reached the vendor. */
|
|
343
|
+
| 'provider-error'
|
|
344
|
+
/** The deadline fired before an answer arrived. */
|
|
345
|
+
| 'timeout'
|
|
346
|
+
/** An answer came back that did not satisfy the schema. */
|
|
347
|
+
| 'invalid-generation'
|
|
348
|
+
/** A usable answer reconciled down to nothing for this shopper. */
|
|
349
|
+
| 'unusable-on-serve'
|
|
350
|
+
/** The caller asked for the deterministic component on purpose. */
|
|
351
|
+
| 'requested';
|
|
352
|
+
export declare const SPEC_VERSION: '1';
|
|
353
|
+
/** A generated spec plus the provenance the server owns. This is what renders. */
|
|
354
|
+
export interface ComponentSpec extends GeneratedSpec {
|
|
355
|
+
specVersion: typeof SPEC_VERSION;
|
|
356
|
+
slot: string;
|
|
357
|
+
source: SpecSource;
|
|
358
|
+
/** Epoch milliseconds at which the underlying generation completed. */
|
|
359
|
+
generatedAt: number;
|
|
360
|
+
/** Wall-clock milliseconds spent producing it, including any cache lookup. */
|
|
361
|
+
latencyMs: number;
|
|
362
|
+
/** Provider name, or null when no model was involved. */
|
|
363
|
+
provider: string | null;
|
|
364
|
+
/** Model identifier, or null when no model was involved. */
|
|
365
|
+
model: string | null;
|
|
366
|
+
/**
|
|
367
|
+
* Why the deterministic component is showing instead of a generated one.
|
|
368
|
+
* Present on every fallback, including the ones where no model was involved
|
|
369
|
+
* at all — no provider configured, or the caller asked for it directly.
|
|
370
|
+
*/
|
|
371
|
+
degradedReason?: DegradedReason;
|
|
372
|
+
}
|
|
373
|
+
/** Throws a `ZodError` if the value is not a well-formed generated spec. */
|
|
374
|
+
export declare function parseGeneratedSpec(value: unknown): GeneratedSpec;
|
|
375
|
+
/** Non-throwing variant, for validating untrusted model output. */
|
|
376
|
+
export declare function safeParseGeneratedSpec(value: unknown): z.ZodSafeParseResult<{
|
|
377
|
+
tone: "editorial" | "enthusiastic" | "neutral" | "urgent";
|
|
378
|
+
headline: string;
|
|
379
|
+
subheadline: string | null;
|
|
380
|
+
blocks: ({
|
|
381
|
+
kind: "hero";
|
|
382
|
+
headline: string;
|
|
383
|
+
body: string | null;
|
|
384
|
+
sku: string | null;
|
|
385
|
+
ctaLabel: string | null;
|
|
386
|
+
} | {
|
|
387
|
+
kind: "grid";
|
|
388
|
+
title: string | null;
|
|
389
|
+
columns: 2 | 3 | 4;
|
|
390
|
+
items: {
|
|
391
|
+
sku: string;
|
|
392
|
+
basis: "complements_cart" | "complements_purchase" | "liked_category" | "most_viewed" | "popular" | "similar_to_current";
|
|
393
|
+
reason: string | null;
|
|
394
|
+
badge: string | null;
|
|
395
|
+
emphasis: "featured" | "normal";
|
|
396
|
+
}[];
|
|
397
|
+
} | {
|
|
398
|
+
kind: "carousel";
|
|
399
|
+
title: string | null;
|
|
400
|
+
items: {
|
|
401
|
+
sku: string;
|
|
402
|
+
basis: "complements_cart" | "complements_purchase" | "liked_category" | "most_viewed" | "popular" | "similar_to_current";
|
|
403
|
+
reason: string | null;
|
|
404
|
+
badge: string | null;
|
|
405
|
+
emphasis: "featured" | "normal";
|
|
406
|
+
}[];
|
|
407
|
+
} | {
|
|
408
|
+
kind: "banner";
|
|
409
|
+
tone: "info" | "promo" | "restock" | "urgency";
|
|
410
|
+
text: string;
|
|
411
|
+
ctaLabel: string | null;
|
|
412
|
+
} | {
|
|
413
|
+
kind: "copy";
|
|
414
|
+
title: string | null;
|
|
415
|
+
body: string;
|
|
416
|
+
} | {
|
|
417
|
+
kind: "bundle";
|
|
418
|
+
title: string | null;
|
|
419
|
+
body: string | null;
|
|
420
|
+
ctaLabel: string | null;
|
|
421
|
+
bundleId: string | null;
|
|
422
|
+
})[];
|
|
423
|
+
rationale: string;
|
|
424
|
+
}>;
|
|
425
|
+
export {};
|
|
426
|
+
//# sourceMappingURL=component-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-spec.d.ts","sourceRoot":"","sources":["../src/component-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The component specification — the only thing a language model is ever allowed
|
|
4
|
+
* to return.
|
|
5
|
+
*
|
|
6
|
+
* The model chooses layout, ordering, emphasis, copy, the recommendation
|
|
7
|
+
* strategy behind each pick, and which of the host's candidate products to
|
|
8
|
+
* surface. It never returns markup, code, URLs, prices,
|
|
9
|
+
* product titles, or images. Every field is an enum, a bounded number, a SKU
|
|
10
|
+
* reference resolved against the candidate set, or free text that is clamped
|
|
11
|
+
* and escaped before it renders. That is what makes generated output safe to
|
|
12
|
+
* put in a server-rendered response.
|
|
13
|
+
*
|
|
14
|
+
* Two constraints shape this file, and both are easy to undo by accident:
|
|
15
|
+
*
|
|
16
|
+
* 1. It doubles as a provider structured-output schema, so it avoids
|
|
17
|
+
* recursion, string length bounds, and numeric ranges. Providers reject
|
|
18
|
+
* schemas that use them in strict mode. Bounds are enforced afterwards, in
|
|
19
|
+
* reconciliation, where a violation can be repaired rather than fatal.
|
|
20
|
+
* 2. Optionality is `.nullable()` rather than `.optional()`, because strict
|
|
21
|
+
* structured outputs require every declared property to be present. "No
|
|
22
|
+
* badge" has to be expressible as `null`, not as a missing key.
|
|
23
|
+
*
|
|
24
|
+
* A test asserts both by converting this schema to JSON Schema, so neither is
|
|
25
|
+
* left to a comment nobody reads.
|
|
26
|
+
*
|
|
27
|
+
* Note the asymmetry with `tracking-input`, which rejects unknown fields: these
|
|
28
|
+
* schemas strip them. The inputs differ in kind. An unrecognised key in a host
|
|
29
|
+
* payload is a typo, and silently dropping it costs the shopper their history,
|
|
30
|
+
* so it must be loud. An unrecognised key from a model is drift, and discarding
|
|
31
|
+
* it is exactly right — rejecting the whole spec over one stray key would cost
|
|
32
|
+
* the shopper the component. Either way the renderer only ever sees the fields
|
|
33
|
+
* declared here, which is the property that matters.
|
|
34
|
+
*/
|
|
35
|
+
/** Overall voice of the component. */
|
|
36
|
+
export const TONES = ['neutral', 'enthusiastic', 'urgent', 'editorial'];
|
|
37
|
+
/** What a banner is claiming. */
|
|
38
|
+
export const BANNER_TONES = ['info', 'promo', 'urgency', 'restock'];
|
|
39
|
+
/** How prominently a single product is placed. */
|
|
40
|
+
export const EMPHASIS = ['normal', 'featured'];
|
|
41
|
+
/**
|
|
42
|
+
* Why a product was chosen — the recommendation strategy behind the pick.
|
|
43
|
+
*
|
|
44
|
+
* This is a closed set rather than free text because every value here is a
|
|
45
|
+
* factual claim about the shopper, and a claim the server can check. "Because
|
|
46
|
+
* you viewed this" written as prose is unverifiable: nothing downstream can
|
|
47
|
+
* tell whether the shopper viewed anything. Declared as `most_viewed`,
|
|
48
|
+
* reconciliation can look it up in the digest and drop the claim if it is
|
|
49
|
+
* false.
|
|
50
|
+
*
|
|
51
|
+
* Every value is deliberately checkable against data already in hand — the
|
|
52
|
+
* signal digest or the host's own catalog. A basis that needs data the
|
|
53
|
+
* framework never receives (`new_arrival`, `back_in_stock`, `trending_today`)
|
|
54
|
+
* is not in this list, because it could only ever be taken on trust.
|
|
55
|
+
*/
|
|
56
|
+
export const RECOMMENDATION_BASES = [
|
|
57
|
+
/** Same category as the product being viewed. */
|
|
58
|
+
'similar_to_current',
|
|
59
|
+
/** The shopper has viewed this product. */
|
|
60
|
+
'most_viewed',
|
|
61
|
+
/** Goes with something already in the cart. */
|
|
62
|
+
'complements_cart',
|
|
63
|
+
/** Goes with something the shopper has bought. */
|
|
64
|
+
'complements_purchase',
|
|
65
|
+
/** In a category the shopper's signals favour. */
|
|
66
|
+
'liked_category',
|
|
67
|
+
/** No claim about this shopper at all — the safe default. */
|
|
68
|
+
'popular',
|
|
69
|
+
];
|
|
70
|
+
/** A reference to one product from the candidate set, and why it was chosen. */
|
|
71
|
+
export const productReferenceSchema = z.object({
|
|
72
|
+
/** Must be a SKU the host supplied in `TrackingInput.candidates`. */
|
|
73
|
+
sku: z.string(),
|
|
74
|
+
/**
|
|
75
|
+
* The strategy behind this pick. Reconciliation checks it against the
|
|
76
|
+
* shopper's signals, so the model cannot assert a relationship that is not
|
|
77
|
+
* there.
|
|
78
|
+
*/
|
|
79
|
+
basis: z.enum(RECOMMENDATION_BASES),
|
|
80
|
+
/**
|
|
81
|
+
* How the basis is phrased for the shopper, e.g. "Pairs with the boots you
|
|
82
|
+
* bought". Free text, but it has to be consistent with `basis`, which is not.
|
|
83
|
+
*
|
|
84
|
+
* Nullable because reconciliation clears it when it cannot verify the basis:
|
|
85
|
+
* a pick may still be worth showing when the stated reason for it is not
|
|
86
|
+
* true, but the prose asserting that reason must not render.
|
|
87
|
+
*/
|
|
88
|
+
reason: z.string().nullable(),
|
|
89
|
+
/** Short accent label, e.g. "Back in stock". Null when nothing warrants one. */
|
|
90
|
+
badge: z.string().nullable(),
|
|
91
|
+
emphasis: z.enum(EMPHASIS),
|
|
92
|
+
});
|
|
93
|
+
/** One large featured statement, optionally anchored to a single product. */
|
|
94
|
+
const heroBlockSchema = z.object({
|
|
95
|
+
kind: z.literal('hero'),
|
|
96
|
+
headline: z.string(),
|
|
97
|
+
body: z.string().nullable(),
|
|
98
|
+
sku: z.string().nullable(),
|
|
99
|
+
ctaLabel: z.string().nullable(),
|
|
100
|
+
});
|
|
101
|
+
/** The general-purpose choice when several products are comparably relevant. */
|
|
102
|
+
const gridBlockSchema = z.object({
|
|
103
|
+
kind: z.literal('grid'),
|
|
104
|
+
title: z.string().nullable(),
|
|
105
|
+
columns: z.union([z.literal(2), z.literal(3), z.literal(4)]),
|
|
106
|
+
items: z.array(productReferenceSchema),
|
|
107
|
+
});
|
|
108
|
+
/** A horizontally scanned row, for when order implies a ranking. */
|
|
109
|
+
const carouselBlockSchema = z.object({
|
|
110
|
+
kind: z.literal('carousel'),
|
|
111
|
+
title: z.string().nullable(),
|
|
112
|
+
items: z.array(productReferenceSchema),
|
|
113
|
+
});
|
|
114
|
+
/** A single line of merchandising copy. */
|
|
115
|
+
const bannerBlockSchema = z.object({
|
|
116
|
+
kind: z.literal('banner'),
|
|
117
|
+
tone: z.enum(BANNER_TONES),
|
|
118
|
+
text: z.string(),
|
|
119
|
+
ctaLabel: z.string().nullable(),
|
|
120
|
+
});
|
|
121
|
+
/** Short editorial prose, for explaining the theme of a selection. */
|
|
122
|
+
const copyBlockSchema = z.object({
|
|
123
|
+
kind: z.literal('copy'),
|
|
124
|
+
title: z.string().nullable(),
|
|
125
|
+
body: z.string(),
|
|
126
|
+
});
|
|
127
|
+
/** A set the shop sells together. The shop picks which one; this only asks for it. */
|
|
128
|
+
const bundleBlockSchema = z.object({
|
|
129
|
+
kind: z.literal('bundle'),
|
|
130
|
+
title: z.string().nullable(),
|
|
131
|
+
body: z.string().nullable(),
|
|
132
|
+
ctaLabel: z.string().nullable(),
|
|
133
|
+
// Filled in per request. Whatever the model puts here is thrown away.
|
|
134
|
+
bundleId: z.string().nullable(),
|
|
135
|
+
});
|
|
136
|
+
/** Blocks do not nest. The vocabulary is closed on purpose. */
|
|
137
|
+
export const blockSchema = z.discriminatedUnion('kind', [
|
|
138
|
+
heroBlockSchema,
|
|
139
|
+
gridBlockSchema,
|
|
140
|
+
carouselBlockSchema,
|
|
141
|
+
bannerBlockSchema,
|
|
142
|
+
copyBlockSchema,
|
|
143
|
+
bundleBlockSchema,
|
|
144
|
+
]);
|
|
145
|
+
/**
|
|
146
|
+
* Exactly what the model must return. Provenance — version, slot, latency, which
|
|
147
|
+
* provider answered — is added by the server afterwards, and is deliberately not
|
|
148
|
+
* part of the model's burden.
|
|
149
|
+
*/
|
|
150
|
+
export const generatedSpecSchema = z.object({
|
|
151
|
+
tone: z.enum(TONES),
|
|
152
|
+
headline: z.string(),
|
|
153
|
+
subheadline: z.string().nullable(),
|
|
154
|
+
blocks: z.array(blockSchema),
|
|
155
|
+
/**
|
|
156
|
+
* One sentence on why this arrangement was chosen. For engineers reading
|
|
157
|
+
* generation logs, not for shoppers; it is not rendered by default.
|
|
158
|
+
*/
|
|
159
|
+
rationale: z.string(),
|
|
160
|
+
});
|
|
161
|
+
export const SPEC_VERSION = '1';
|
|
162
|
+
/** Throws a `ZodError` if the value is not a well-formed generated spec. */
|
|
163
|
+
export function parseGeneratedSpec(value) {
|
|
164
|
+
return generatedSpecSchema.parse(value);
|
|
165
|
+
}
|
|
166
|
+
/** Non-throwing variant, for validating untrusted model output. */
|
|
167
|
+
export function safeParseGeneratedSpec(value) {
|
|
168
|
+
return generatedSpecSchema.safeParse(value);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=component-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-spec.js","sourceRoot":"","sources":["../src/component-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GeneratedSpec } from './component-spec.js';
|
|
2
|
+
import type { SignalDigest } from './signal-digest.js';
|
|
3
|
+
import type { TrackingInput } from './tracking-input.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds a renderable spec from signals alone. Never throws. Returns a spec with
|
|
6
|
+
* no blocks only when there is genuinely nothing in stock left to show, which
|
|
7
|
+
* the renderer treats as "render nothing" — an empty recommendation region is
|
|
8
|
+
* worse than none.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildFallbackSpec(input: TrackingInput, digest: SignalDigest): GeneratedSpec;
|
|
11
|
+
//# sourceMappingURL=fallback-component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback-component.d.ts","sourceRoot":"","sources":["../src/fallback-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkDzD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,aAAa,CAiB3F"}
|