@adobe/spacecat-shared-utils 1.54.0 → 1.55.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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/llmo-config.js +2 -0
- package/src/schemas.js +58 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-utils-v1.55.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.54.0...@adobe/spacecat-shared-utils-v1.55.0) (2025-10-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add topics with nested prompts + make categories and prompts top level entities ([#999](https://github.com/adobe/spacecat-shared/issues/999)) ([1b43970](https://github.com/adobe/spacecat-shared/commit/1b43970912478361b2eb4eaf1e7f173e77ad80e9))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-utils-v1.54.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.53.0...@adobe/spacecat-shared-utils-v1.54.0) (2025-10-02)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/llmo-config.js
CHANGED
package/src/schemas.js
CHANGED
|
@@ -38,14 +38,33 @@ const nonEmptyString = z.string().min(1);
|
|
|
38
38
|
|
|
39
39
|
const region = z.string().length(2).regex(/^[a-z][a-z]$/i);
|
|
40
40
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
z.
|
|
45
|
-
])
|
|
41
|
+
const prompt = z.object({
|
|
42
|
+
prompt: nonEmptyString,
|
|
43
|
+
regions: z.array(region),
|
|
44
|
+
origin: z.union([z.literal('human'), z.literal('ai'), z.string()]),
|
|
45
|
+
source: z.union([z.literal('config'), z.literal('api'), z.string()]),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const entity = z.object({
|
|
49
|
+
type: nonEmptyString,
|
|
50
|
+
name: nonEmptyString,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const category = z.object({
|
|
54
|
+
name: nonEmptyString,
|
|
55
|
+
region: z.union([region, z.array(region)]),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const topic = z.object({
|
|
59
|
+
name: nonEmptyString,
|
|
60
|
+
prompts: z.array(prompt).min(1),
|
|
61
|
+
category: z.union([z.uuid(), nonEmptyString]),
|
|
62
|
+
});
|
|
46
63
|
|
|
47
64
|
export const llmoConfig = z.object({
|
|
48
65
|
entities: z.record(z.uuid(), entity),
|
|
66
|
+
categories: z.record(z.uuid(), category),
|
|
67
|
+
topics: z.record(z.uuid(), topic),
|
|
49
68
|
brands: z.object({
|
|
50
69
|
aliases: z.array(
|
|
51
70
|
z.object({
|
|
@@ -67,59 +86,68 @@ export const llmoConfig = z.object({
|
|
|
67
86
|
),
|
|
68
87
|
}),
|
|
69
88
|
}).superRefine((value, ctx) => {
|
|
70
|
-
const {
|
|
89
|
+
const {
|
|
90
|
+
categories, topics, brands, competitors,
|
|
91
|
+
} = value;
|
|
71
92
|
|
|
72
93
|
brands.aliases.forEach((alias, index) => {
|
|
73
|
-
|
|
74
|
-
ensureRegionCompatibility(
|
|
94
|
+
ensureCategoryExists(categories, ctx, alias.category, ['brands', 'aliases', index, 'category']);
|
|
95
|
+
ensureRegionCompatibility(categories, ctx, alias.category, alias.region, ['brands', 'aliases', index, 'region'], 'brand alias');
|
|
75
96
|
});
|
|
76
97
|
|
|
77
98
|
competitors.competitors.forEach((competitor, index) => {
|
|
78
|
-
|
|
79
|
-
ensureRegionCompatibility(
|
|
99
|
+
ensureCategoryExists(categories, ctx, competitor.category, ['competitors', 'competitors', index, 'category']);
|
|
100
|
+
ensureRegionCompatibility(categories, ctx, competitor.category, competitor.region, ['competitors', 'competitors', index, 'region'], 'competitor');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Validate topic prompts regions against their category
|
|
104
|
+
Object.entries(topics).forEach(([topicId, topicEntity]) => {
|
|
105
|
+
if (topicEntity.prompts && topicEntity.category) {
|
|
106
|
+
// If category is a UUID, validate against the referenced category entity
|
|
107
|
+
if (topicEntity.category.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)) {
|
|
108
|
+
topicEntity.prompts.forEach((promptItem, promptIndex) => {
|
|
109
|
+
ensureRegionCompatibility(
|
|
110
|
+
categories,
|
|
111
|
+
ctx,
|
|
112
|
+
topicEntity.category,
|
|
113
|
+
promptItem.regions,
|
|
114
|
+
['topics', topicId, 'prompts', promptIndex, 'regions'],
|
|
115
|
+
'topic prompt',
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
80
120
|
});
|
|
81
121
|
});
|
|
82
122
|
|
|
83
123
|
/**
|
|
84
|
-
* @param {LLMOConfig['
|
|
124
|
+
* @param {LLMOConfig['categories']} categories
|
|
85
125
|
* @param {z.RefinementCtx} ctx
|
|
86
126
|
* @param {string} id
|
|
87
|
-
* @param {string} expectedType
|
|
88
127
|
* @param {Array<number | string>} path
|
|
89
|
-
* @param {string} refLabel
|
|
90
128
|
*/
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
if (!entityValue) {
|
|
94
|
-
ctx.addIssue({
|
|
95
|
-
code: 'custom',
|
|
96
|
-
path,
|
|
97
|
-
message: `Unknown ${refLabel} entity: ${id}`,
|
|
98
|
-
});
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (entityValue.type !== expectedType) {
|
|
129
|
+
function ensureCategoryExists(categories, ctx, id, path) {
|
|
130
|
+
if (!categories[id]) {
|
|
103
131
|
ctx.addIssue({
|
|
104
132
|
code: 'custom',
|
|
105
133
|
path,
|
|
106
|
-
message: `
|
|
134
|
+
message: `Category ${id} does not exist`,
|
|
107
135
|
});
|
|
108
136
|
}
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
/**
|
|
112
|
-
* @param {LLMOConfig['
|
|
140
|
+
* @param {LLMOConfig['categories']} categories
|
|
113
141
|
* @param {z.RefinementCtx} ctx
|
|
114
142
|
* @param {string} categoryId
|
|
115
143
|
* @param {string | string[]} itemRegion
|
|
116
144
|
* @param {Array<number | string>} path
|
|
117
145
|
* @param {string} itemLabel
|
|
118
146
|
*/
|
|
119
|
-
function ensureRegionCompatibility(
|
|
120
|
-
const categoryEntity =
|
|
147
|
+
function ensureRegionCompatibility(categories, ctx, categoryId, itemRegion, path, itemLabel) {
|
|
148
|
+
const categoryEntity = categories[categoryId];
|
|
121
149
|
if (!categoryEntity) {
|
|
122
|
-
// Category validation is handled by
|
|
150
|
+
// Category validation is handled by ensureCategoryExists
|
|
123
151
|
return;
|
|
124
152
|
}
|
|
125
153
|
|