@syntrologie/adapt-product 2.8.0-canary.430 → 2.8.0-canary.432
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/dist/cdn.js +1 -1
- package/dist/{chunk-5CIX67HG.js → chunk-F6VOPJYW.js} +121 -11
- package/dist/chunk-F6VOPJYW.js.map +7 -0
- package/dist/runtime.js +1 -1
- package/dist/scoring/pool-selection.d.ts +33 -0
- package/dist/scoring/pool-selection.d.ts.map +1 -0
- package/dist/scoring/scored-template.d.ts +20 -2
- package/dist/scoring/scored-template.d.ts.map +1 -1
- package/dist/widgets/SyntroPdpLit.d.ts +2 -0
- package/dist/widgets/SyntroPdpLit.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5CIX67HG.js.map +0 -7
package/dist/cdn.js
CHANGED
|
@@ -3041,9 +3041,66 @@ if (!customElements.get("syntro-product-reco-carousel"))
|
|
|
3041
3041
|
// src/widgets/SyntroPdpLit.ts
|
|
3042
3042
|
import { html as html11, LitElement as LitElement11, render } from "lit";
|
|
3043
3043
|
|
|
3044
|
+
// src/scoring/pool-selection.ts
|
|
3045
|
+
var UNKNOWN_DECISION = "unknown";
|
|
3046
|
+
var DISPLAY_MAX = {
|
|
3047
|
+
qa: 5,
|
|
3048
|
+
"trending-news": 5,
|
|
3049
|
+
"peer-feed": 4
|
|
3050
|
+
};
|
|
3051
|
+
function tagsOf(item) {
|
|
3052
|
+
return Array.isArray(item.archetypes) ? item.archetypes : [];
|
|
3053
|
+
}
|
|
3054
|
+
function selectPoolItems(items, decision, max) {
|
|
3055
|
+
if (items.length <= max) return [...items];
|
|
3056
|
+
const sharp = decision && decision !== UNKNOWN_DECISION;
|
|
3057
|
+
if (sharp) {
|
|
3058
|
+
const matched = items.filter((i2) => tagsOf(i2).includes(decision));
|
|
3059
|
+
const general = items.filter((i2) => tagsOf(i2).length === 0);
|
|
3060
|
+
const rest = items.filter((i2) => !matched.includes(i2) && !general.includes(i2));
|
|
3061
|
+
return [...matched, ...general, ...rest].slice(0, max);
|
|
3062
|
+
}
|
|
3063
|
+
const picked = [];
|
|
3064
|
+
const covered = /* @__PURE__ */ new Set();
|
|
3065
|
+
for (const item of items) {
|
|
3066
|
+
const tags = tagsOf(item);
|
|
3067
|
+
if (tags.length === 0) continue;
|
|
3068
|
+
if (tags.some((t2) => covered.has(t2))) continue;
|
|
3069
|
+
picked.push(item);
|
|
3070
|
+
for (const t2 of tags) covered.add(t2);
|
|
3071
|
+
if (picked.length >= max) return picked.slice(0, max);
|
|
3072
|
+
}
|
|
3073
|
+
for (const item of items) {
|
|
3074
|
+
if (picked.length >= max) break;
|
|
3075
|
+
if (!picked.includes(item) && tagsOf(item).length === 0) picked.push(item);
|
|
3076
|
+
}
|
|
3077
|
+
for (const item of items) {
|
|
3078
|
+
if (picked.length >= max) break;
|
|
3079
|
+
if (!picked.includes(item)) picked.push(item);
|
|
3080
|
+
}
|
|
3081
|
+
return picked.slice(0, max);
|
|
3082
|
+
}
|
|
3083
|
+
function selectPresetData(kind, data, decision) {
|
|
3084
|
+
const max = DISPLAY_MAX[kind];
|
|
3085
|
+
if (!max || typeof data !== "object" || data === null) return data;
|
|
3086
|
+
const record = data;
|
|
3087
|
+
const listKey = kind === "peer-feed" ? "peers" : "items";
|
|
3088
|
+
const list = record[listKey];
|
|
3089
|
+
if (!Array.isArray(list)) return data;
|
|
3090
|
+
return { ...record, [listKey]: selectPoolItems(list, decision, max) };
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3044
3093
|
// src/scoring/scored-template.ts
|
|
3045
3094
|
async function resolveScoredTemplate(opts) {
|
|
3046
|
-
|
|
3095
|
+
let value;
|
|
3096
|
+
let decisionValue = "unknown";
|
|
3097
|
+
if (opts.decisionSpec && opts.scoreMulti) {
|
|
3098
|
+
const scored = await opts.scoreMulti([opts.scoreSpec, opts.decisionSpec]);
|
|
3099
|
+
value = scored[0] ?? "unknown";
|
|
3100
|
+
decisionValue = scored[1] ?? "unknown";
|
|
3101
|
+
} else {
|
|
3102
|
+
value = await opts.score(opts.scoreSpec);
|
|
3103
|
+
}
|
|
3047
3104
|
const variant = opts.presets[value] ?? opts.presets[opts.fallbackValue ?? "unknown"] ?? {};
|
|
3048
3105
|
const setSlots = opts.slotIds.filter((s4) => s4 in variant);
|
|
3049
3106
|
const unsetSlots = opts.slotIds.filter((s4) => !(s4 in variant));
|
|
@@ -3052,12 +3109,13 @@ async function resolveScoredTemplate(opts) {
|
|
|
3052
3109
|
opts.emitPlan(opts.buildPlan(pageOrder.map((slot) => ({ slot, preset: variant[slot] }))));
|
|
3053
3110
|
}
|
|
3054
3111
|
for (const slot of setSlots) {
|
|
3055
|
-
|
|
3112
|
+
const preset = opts.transformPreset ? opts.transformPreset(variant[slot], decisionValue) : variant[slot];
|
|
3113
|
+
await opts.emitMount(opts.buildMount(slot, preset));
|
|
3056
3114
|
}
|
|
3057
3115
|
if (unsetSlots.length > 0 && opts.composeUnset) {
|
|
3058
|
-
await opts.composeUnset(unsetSlots, value);
|
|
3116
|
+
await opts.composeUnset(unsetSlots, value, decisionValue);
|
|
3059
3117
|
}
|
|
3060
|
-
return { value, setSlots, unsetSlots };
|
|
3118
|
+
return { value, decisionValue, setSlots, unsetSlots };
|
|
3061
3119
|
}
|
|
3062
3120
|
|
|
3063
3121
|
// src/widgets/PdpSectionHeaderLit.ts
|
|
@@ -3628,16 +3686,28 @@ function _readPdpArchetypes() {
|
|
|
3628
3686
|
if (typeof window === "undefined") return void 0;
|
|
3629
3687
|
return window.__SYNTRO_PDP_ARCHETYPES__;
|
|
3630
3688
|
}
|
|
3689
|
+
function _readPdpDecisionArchetypes() {
|
|
3690
|
+
if (typeof window === "undefined") return void 0;
|
|
3691
|
+
return window.__SYNTRO_PDP_DECISION_ARCHETYPES__;
|
|
3692
|
+
}
|
|
3631
3693
|
async function composeModule(authedFetch, args) {
|
|
3632
3694
|
try {
|
|
3633
3695
|
const requestBody = {
|
|
3634
3696
|
product_name: args.productName,
|
|
3697
|
+
// Scopes the peer-feed's review-corpus fetch to this product.
|
|
3698
|
+
product_id: args.productId,
|
|
3635
3699
|
// `kind` flows from runtime slot-id parsing (`pdp-slot-<kind>`), so it's a
|
|
3636
3700
|
// plain string here; the backend validates it against the PdpModuleKind
|
|
3637
3701
|
// enum (422 on an unknown kind). The other fields ARE contract-checked.
|
|
3638
3702
|
kind: args.kind,
|
|
3639
3703
|
variant: args.variant,
|
|
3640
3704
|
archetype: { slug: args.archetype.slug, description: args.archetype.description },
|
|
3705
|
+
// Axis A — what's blocking the purchase. Null (unknown / undeclared)
|
|
3706
|
+
// degrades server-side to facet-only intent.
|
|
3707
|
+
decision_archetype: args.decisionArchetype,
|
|
3708
|
+
// Live per-visitor generation is always focused; the preset generator is
|
|
3709
|
+
// the only 'pool' caller.
|
|
3710
|
+
coverage: "focused",
|
|
3641
3711
|
behavior_summary: args.behaviorSummary,
|
|
3642
3712
|
// geo/attribution/device — the regional module needs the visitor's region.
|
|
3643
3713
|
signals: args.signals
|
|
@@ -3720,7 +3790,17 @@ function _sessionMetric(signals, key) {
|
|
|
3720
3790
|
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
3721
3791
|
}
|
|
3722
3792
|
function buildExplain(opts) {
|
|
3723
|
-
const {
|
|
3793
|
+
const {
|
|
3794
|
+
archetype,
|
|
3795
|
+
description,
|
|
3796
|
+
decisionArchetype,
|
|
3797
|
+
decisionDescription,
|
|
3798
|
+
summary,
|
|
3799
|
+
signals,
|
|
3800
|
+
latencyMs
|
|
3801
|
+
} = opts;
|
|
3802
|
+
const blockingQuestion = /Blocked on:\s*([\s\S]*?)\s*Resolved by:/.exec(decisionDescription)?.[1] ?? decisionDescription;
|
|
3803
|
+
const decisionKnown = !!decisionArchetype && decisionArchetype !== "unknown";
|
|
3724
3804
|
const signalsCount = _countSignals(signals);
|
|
3725
3805
|
const dims = _signalDimensions(signals);
|
|
3726
3806
|
const fingerprint = `0x${_fingerprint(signals)}`;
|
|
@@ -3762,7 +3842,7 @@ function buildExplain(opts) {
|
|
|
3762
3842
|
{
|
|
3763
3843
|
value: "2",
|
|
3764
3844
|
label: "AI models run live",
|
|
3765
|
-
hint: "
|
|
3845
|
+
hint: "A two-axis archetype classifier (what you do + what is blocking your decision) and a behavior-summary model, evaluated in real time on your signals."
|
|
3766
3846
|
},
|
|
3767
3847
|
{
|
|
3768
3848
|
value: latency,
|
|
@@ -3789,6 +3869,8 @@ function buildExplain(opts) {
|
|
|
3789
3869
|
return {
|
|
3790
3870
|
archetypeLabel: _titleCase(archetype),
|
|
3791
3871
|
archetypeDesc: description,
|
|
3872
|
+
decisionLabel: decisionKnown ? _titleCase(decisionArchetype) : null,
|
|
3873
|
+
decisionDesc: decisionKnown ? blockingQuestion : null,
|
|
3792
3874
|
summary,
|
|
3793
3875
|
metrics: hero,
|
|
3794
3876
|
dossier
|
|
@@ -4248,7 +4330,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4248
4330
|
const productPresets = _readPdpPresets()?.[this.productId];
|
|
4249
4331
|
const synos = _readSynOS();
|
|
4250
4332
|
if (productPresets && typeof synos?.score === "function") {
|
|
4251
|
-
void this._fireFromConfig(productPresets, synos.score, synos.authedFetch);
|
|
4333
|
+
void this._fireFromConfig(productPresets, synos.score, synos.authedFetch, synos.scoreMulti);
|
|
4252
4334
|
return;
|
|
4253
4335
|
}
|
|
4254
4336
|
console.warn(
|
|
@@ -4298,12 +4380,14 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4298
4380
|
}
|
|
4299
4381
|
}
|
|
4300
4382
|
/** Render from config: score the archetype → mount the preset set-values. */
|
|
4301
|
-
async _fireFromConfig(productPresets, score, authedFetch) {
|
|
4383
|
+
async _fireFromConfig(productPresets, score, authedFetch, scoreMulti) {
|
|
4302
4384
|
const startedAt = performance.now();
|
|
4303
4385
|
const gen = ++this._fireGeneration;
|
|
4304
4386
|
this._streamState = "streaming";
|
|
4305
4387
|
const archetypeValues = Object.keys(productPresets);
|
|
4306
4388
|
const archetypeDescriptions = _readPdpArchetypes() ?? {};
|
|
4389
|
+
const decisionDescriptions = _readPdpDecisionArchetypes() ?? {};
|
|
4390
|
+
const decisionValues = Object.keys(decisionDescriptions);
|
|
4307
4391
|
const signals = { ...gatherVisitorSignals(), chat_excerpt: readChatExcerpt() };
|
|
4308
4392
|
const summaryPromise = authedFetch ? score({ model_id: "summary", name: "behavior_summary", type: "string", signals }).catch(
|
|
4309
4393
|
() => "unknown"
|
|
@@ -4327,6 +4411,23 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4327
4411
|
),
|
|
4328
4412
|
signals
|
|
4329
4413
|
},
|
|
4414
|
+
// Axis A: scored together with the facet in ONE model call when the
|
|
4415
|
+
// host declares decision archetypes and the runtime exposes scoreMulti.
|
|
4416
|
+
decisionSpec: decisionValues.length > 0 && scoreMulti ? {
|
|
4417
|
+
model_id: "llm",
|
|
4418
|
+
name: "decision_archetype",
|
|
4419
|
+
type: "enum",
|
|
4420
|
+
values: decisionValues,
|
|
4421
|
+
value_descriptions: decisionDescriptions,
|
|
4422
|
+
signals
|
|
4423
|
+
} : void 0,
|
|
4424
|
+
scoreMulti,
|
|
4425
|
+
// Pool selection: the cached preset carries more tagged items than the
|
|
4426
|
+
// widget shows; the decision archetype picks which ones surface.
|
|
4427
|
+
transformPreset: (preset, decisionValue) => ({
|
|
4428
|
+
...preset,
|
|
4429
|
+
data: selectPresetData(preset.kind, preset.data, decisionValue)
|
|
4430
|
+
}),
|
|
4330
4431
|
presets: productPresets,
|
|
4331
4432
|
slotIds: SCORED_PDP_SLOTS,
|
|
4332
4433
|
orderSlots: _orderSlotsForPage,
|
|
@@ -4369,8 +4470,9 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4369
4470
|
// Live-generate the uncached slots (chart/regional) via /api/pdp/module.
|
|
4370
4471
|
// The angle = archetype description + behavior summary (no planner). The
|
|
4371
4472
|
// boxes are already reserved by the plan above, so these fill in place.
|
|
4372
|
-
composeUnset: authedFetch ? async (unsetSlots, archetypeValue) => {
|
|
4473
|
+
composeUnset: authedFetch ? async (unsetSlots, archetypeValue, decisionValue) => {
|
|
4373
4474
|
const description = archetypeDescriptions[archetypeValue] ?? "";
|
|
4475
|
+
const decisionArchetype = decisionValue && decisionValue !== "unknown" ? { slug: decisionValue, description: decisionDescriptions[decisionValue] ?? "" } : null;
|
|
4374
4476
|
const rawSummary2 = await summaryPromise;
|
|
4375
4477
|
const behaviorSummary = rawSummary2 === "unknown" ? "" : rawSummary2;
|
|
4376
4478
|
await Promise.all(
|
|
@@ -4378,9 +4480,11 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4378
4480
|
const kind = slot.replace("pdp-slot-", "");
|
|
4379
4481
|
const payload = await composeModule(authedFetch, {
|
|
4380
4482
|
productName: this.productName,
|
|
4483
|
+
productId: this.productId || null,
|
|
4381
4484
|
kind,
|
|
4382
4485
|
variant: null,
|
|
4383
4486
|
archetype: { slug: archetypeValue, description },
|
|
4487
|
+
decisionArchetype,
|
|
4384
4488
|
behaviorSummary,
|
|
4385
4489
|
signals
|
|
4386
4490
|
});
|
|
@@ -4400,6 +4504,8 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4400
4504
|
this._explain = buildExplain({
|
|
4401
4505
|
archetype: result.value,
|
|
4402
4506
|
description: archetypeDescriptions[result.value] ?? "",
|
|
4507
|
+
decisionArchetype: result.decisionValue,
|
|
4508
|
+
decisionDescription: decisionDescriptions[result.decisionValue] ?? "",
|
|
4403
4509
|
summary: rawSummary === "unknown" ? "" : rawSummary,
|
|
4404
4510
|
signals,
|
|
4405
4511
|
latencyMs: performance.now() - startedAt
|
|
@@ -4411,10 +4517,11 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4411
4517
|
}).catch(() => {
|
|
4412
4518
|
});
|
|
4413
4519
|
const archLabel = _titleCase(result.value);
|
|
4520
|
+
const decisionLabel = result.decisionValue && result.decisionValue !== "unknown" ? _titleCase(result.decisionValue) : null;
|
|
4414
4521
|
for (const slot of result.setSlots) {
|
|
4415
4522
|
this._setSlotReasoning(slot, {
|
|
4416
4523
|
source: "cached",
|
|
4417
|
-
text: `Chosen from the content library \u2014 matched to your \u201C${archLabel}\u201D archetype.`
|
|
4524
|
+
text: decisionLabel ? `Chosen from the content library \u2014 matched to your \u201C${archLabel}\u201D archetype, showing the items that answer your \u201C${decisionLabel}\u201D question first.` : `Chosen from the content library \u2014 matched to your \u201C${archLabel}\u201D archetype.`
|
|
4418
4525
|
});
|
|
4419
4526
|
}
|
|
4420
4527
|
this._track("syntro_pdp_done", {
|
|
@@ -4527,6 +4634,9 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement11 {
|
|
|
4527
4634
|
<div data-syntro-pdp-why-archetype>
|
|
4528
4635
|
Detected archetype: <strong>${e2.archetypeLabel}</strong>${e2.archetypeDesc ? html11` — ${e2.archetypeDesc}` : ""}
|
|
4529
4636
|
</div>
|
|
4637
|
+
${e2.decisionLabel ? html11`<div data-syntro-pdp-why-archetype>
|
|
4638
|
+
What's blocking the decision: <strong>${e2.decisionLabel}</strong>${e2.decisionDesc ? html11` — “${e2.decisionDesc}”` : ""}
|
|
4639
|
+
</div>` : ""}
|
|
4530
4640
|
${e2.summary ? html11`<div data-syntro-pdp-why-summary>"${e2.summary}"</div>` : ""}
|
|
4531
4641
|
<div data-syntro-pdp-why-metrics>
|
|
4532
4642
|
${e2.metrics.map(
|
|
@@ -9347,4 +9457,4 @@ export {
|
|
|
9347
9457
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
9348
9458
|
*)
|
|
9349
9459
|
*/
|
|
9350
|
-
//# sourceMappingURL=chunk-
|
|
9460
|
+
//# sourceMappingURL=chunk-F6VOPJYW.js.map
|