@zodic/shared 0.0.210 β 0.0.212
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/app/services/ConceptService.ts +436 -368
- package/app/workflow/ConceptWorkflow.ts +11 -0
- package/package.json +1 -1
- package/utils/buildMessages.ts +38 -24
|
@@ -29,67 +29,80 @@ export class ConceptService {
|
|
|
29
29
|
combinationString: string,
|
|
30
30
|
override: boolean = false
|
|
31
31
|
): Promise<void> {
|
|
32
|
-
console.log(
|
|
33
|
-
|
|
32
|
+
console.log(
|
|
33
|
+
`π Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
34
|
+
);
|
|
35
|
+
|
|
34
36
|
const kvStore = this.context.kvConceptsStore();
|
|
35
37
|
const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
36
38
|
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
37
39
|
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
// β
Use Durable Object stub
|
|
40
42
|
const id = this.context.env.CONCEPT_NAMES_DO.idFromName(conceptSlug);
|
|
41
43
|
const stub = this.context.env.CONCEPT_NAMES_DO.get(id);
|
|
42
|
-
|
|
44
|
+
|
|
43
45
|
console.log(`π‘ Fetching existing KV data for ${conceptSlug}...`);
|
|
44
46
|
const existingEN = await this.getKVConcept(kvKeyEN);
|
|
45
47
|
const existingPT = await this.getKVConcept(kvKeyPT);
|
|
46
|
-
|
|
48
|
+
|
|
47
49
|
if (!override && existingEN.name && existingPT.name) {
|
|
48
50
|
console.log(`β‘ Basic info already exists for ${conceptSlug}, skipping.`);
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
let attempts = 0;
|
|
53
55
|
const maxAttempts = 3;
|
|
54
56
|
const MAX_NAME_LENGTH = 44;
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
while (attempts < maxAttempts) {
|
|
57
59
|
let phase = 'generation';
|
|
58
60
|
try {
|
|
59
61
|
attempts++;
|
|
60
|
-
console.log(
|
|
61
|
-
|
|
62
|
+
console.log(
|
|
63
|
+
`π Attempt ${attempts} to generate basic info for ${conceptSlug}...`
|
|
64
|
+
);
|
|
65
|
+
|
|
62
66
|
let allNamesEN: string[] = [];
|
|
63
67
|
let allNamesPT: string[] = [];
|
|
64
68
|
const response = await stub.fetch(`https://internal/names`);
|
|
65
|
-
|
|
69
|
+
|
|
66
70
|
if (response.ok) {
|
|
67
|
-
const data = (await response.json()) as {
|
|
71
|
+
const data = (await response.json()) as {
|
|
72
|
+
'en-us': string[];
|
|
73
|
+
'pt-br': string[];
|
|
74
|
+
};
|
|
68
75
|
allNamesEN = data['en-us'] || [];
|
|
69
76
|
allNamesPT = data['pt-br'] || [];
|
|
70
77
|
}
|
|
71
|
-
|
|
78
|
+
|
|
72
79
|
console.log(`βοΈ Generating new name...`);
|
|
73
|
-
const messages = this.context
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
const messages = this.context
|
|
81
|
+
.buildLLMMessages()
|
|
82
|
+
.generateConceptBasicInfo({
|
|
83
|
+
combination: combinationString,
|
|
84
|
+
conceptSlug,
|
|
85
|
+
existingNames:
|
|
86
|
+
allNamesEN.length > 100 ? allNamesEN.slice(-100) : allNamesEN,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
let aiResponse = await this.context
|
|
90
|
+
.api()
|
|
91
|
+
.callTogether.single(messages, {});
|
|
80
92
|
if (!aiResponse) throw new Error(`β AI returned an empty response`);
|
|
81
|
-
|
|
93
|
+
|
|
82
94
|
phase = 'cleaning';
|
|
83
95
|
aiResponse = this.cleanAIResponse(aiResponse);
|
|
84
96
|
|
|
85
|
-
console.log(
|
|
86
|
-
console.log(
|
|
87
|
-
|
|
97
|
+
console.log('!-- aiResponse -> ', aiResponse);
|
|
98
|
+
console.log('!-- aiResponse length -> ', aiResponse.length);
|
|
99
|
+
|
|
88
100
|
phase = 'parsing';
|
|
89
|
-
let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
|
|
90
|
-
|
|
101
|
+
let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
|
|
102
|
+
this.parseBasicInfoResponse(aiResponse, conceptSlug);
|
|
103
|
+
|
|
91
104
|
console.log(`π Generated names: EN - "${nameEN}", PT - "${namePT}"`);
|
|
92
|
-
|
|
105
|
+
|
|
93
106
|
// β
Forcefully trim the name to exactly 5 words if it's too long
|
|
94
107
|
const enforceWordLimit = (name: string): string => {
|
|
95
108
|
const words = name.split(/\s+/);
|
|
@@ -98,40 +111,66 @@ export class ConceptService {
|
|
|
98
111
|
}
|
|
99
112
|
return name;
|
|
100
113
|
};
|
|
101
|
-
|
|
114
|
+
|
|
102
115
|
nameEN = enforceWordLimit(nameEN);
|
|
103
116
|
namePT = enforceWordLimit(namePT);
|
|
104
|
-
|
|
117
|
+
|
|
105
118
|
console.log(`βοΈ Trimmed names: EN - "${nameEN}", PT - "${namePT}"`);
|
|
106
|
-
|
|
119
|
+
|
|
107
120
|
// β
Check uniqueness before storing
|
|
108
121
|
if (allNamesEN.includes(nameEN) || allNamesPT.includes(namePT)) {
|
|
109
|
-
console.warn(
|
|
122
|
+
console.warn(
|
|
123
|
+
`β οΈ Duplicate Name Detected: "${nameEN}" or "${namePT}"`
|
|
124
|
+
);
|
|
110
125
|
if (attempts >= maxAttempts) {
|
|
111
|
-
console.log(
|
|
126
|
+
console.log(
|
|
127
|
+
`π¨ Max attempts reached. Storing name despite duplicate.`
|
|
128
|
+
);
|
|
112
129
|
await kvFailuresStore.put(
|
|
113
130
|
`failures:duplicates:${conceptSlug}:${combinationString}`,
|
|
114
|
-
JSON.stringify({
|
|
131
|
+
JSON.stringify({
|
|
132
|
+
nameEN,
|
|
133
|
+
namePT,
|
|
134
|
+
attempts,
|
|
135
|
+
conceptSlug,
|
|
136
|
+
combinationString,
|
|
137
|
+
timestamp: new Date().toISOString(),
|
|
138
|
+
})
|
|
115
139
|
);
|
|
116
140
|
break;
|
|
117
141
|
}
|
|
118
142
|
continue;
|
|
119
143
|
}
|
|
120
|
-
|
|
144
|
+
|
|
121
145
|
console.log(`π Storing names in Durable Object...`);
|
|
122
146
|
// await stub.fetch(`https://internal/add-name`, { method: 'POST', body: JSON.stringify({ language: 'en-us', name: nameEN }), headers: { 'Content-Type': 'application/json' } });
|
|
123
147
|
// await stub.fetch(`https://internal/add-name`, { method: 'POST', body: JSON.stringify({ language: 'pt-br', name: namePT }), headers: { 'Content-Type': 'application/json' } });
|
|
124
|
-
|
|
125
|
-
Object.assign(existingEN, {
|
|
148
|
+
|
|
149
|
+
Object.assign(existingEN, {
|
|
150
|
+
name: nameEN,
|
|
151
|
+
description: descriptionEN,
|
|
152
|
+
poem: poemEN,
|
|
153
|
+
status: 'idle',
|
|
154
|
+
});
|
|
126
155
|
await kvStore.put(kvKeyEN, JSON.stringify(existingEN));
|
|
127
|
-
|
|
128
|
-
Object.assign(existingPT, {
|
|
156
|
+
|
|
157
|
+
Object.assign(existingPT, {
|
|
158
|
+
name: namePT,
|
|
159
|
+
description: descriptionPT,
|
|
160
|
+
poem: poemPT,
|
|
161
|
+
status: 'idle',
|
|
162
|
+
});
|
|
129
163
|
await kvStore.put(kvKeyPT, JSON.stringify(existingPT));
|
|
130
|
-
|
|
131
|
-
console.log(
|
|
164
|
+
|
|
165
|
+
console.log(
|
|
166
|
+
`β
Stored basic info for ${conceptSlug}, combination: ${combinationString}.`
|
|
167
|
+
);
|
|
132
168
|
return;
|
|
133
169
|
} catch (error) {
|
|
134
|
-
console.error(
|
|
170
|
+
console.error(
|
|
171
|
+
`β Attempt ${attempts} failed at phase: ${phase}`,
|
|
172
|
+
error
|
|
173
|
+
);
|
|
135
174
|
}
|
|
136
175
|
}
|
|
137
176
|
}
|
|
@@ -157,18 +196,22 @@ export class ConceptService {
|
|
|
157
196
|
descriptionPT: string;
|
|
158
197
|
poemPT: string[];
|
|
159
198
|
} {
|
|
160
|
-
console.log('π
|
|
199
|
+
console.log('π Received AI response for parsing:', response);
|
|
161
200
|
|
|
162
201
|
// β
More flexible regex to handle variations in formatting
|
|
202
|
+
console.log('π Attempting to match English section...');
|
|
163
203
|
const enMatch = response.match(
|
|
164
204
|
/EN:\s*(?:β’\s*)?Name:\s*(.+?)\s*(?:β’\s*)?Description:\s*([\s\S]+?)\s*(?:β’\s*)?Poetic Passage:\s*([\s\S]+?)\s*(?=PT:|$)/
|
|
165
205
|
);
|
|
206
|
+
|
|
207
|
+
console.log('π Attempting to match Portuguese section...');
|
|
166
208
|
const ptMatch = response.match(
|
|
167
209
|
/PT:\s*(?:β’\s*)?Nome:\s*(.+?)\s*(?:β’\s*)?DescriΓ§Γ£o:\s*([\s\S]+?)\s*(?:β’\s*)?Passagem PoΓ©tica:\s*([\s\S]+)/
|
|
168
210
|
);
|
|
169
211
|
|
|
170
212
|
if (!enMatch || !ptMatch) {
|
|
171
213
|
console.error('β Invalid basic info response format:', response);
|
|
214
|
+
console.log('β Regex match results:', { enMatch, ptMatch });
|
|
172
215
|
throw new Error('Invalid basic info response format');
|
|
173
216
|
}
|
|
174
217
|
|
|
@@ -176,14 +219,15 @@ export class ConceptService {
|
|
|
176
219
|
const cleanText = (text: string) => text.trim().replace(/\*/g, '');
|
|
177
220
|
|
|
178
221
|
// β
Parse and clean extracted content
|
|
222
|
+
console.log('βοΈ Extracting and cleaning English content...');
|
|
179
223
|
const nameEN = cleanText(enMatch[1]);
|
|
180
224
|
const descriptionEN = cleanText(enMatch[2]);
|
|
181
225
|
const poemEN = enMatch[3]
|
|
182
226
|
.trim()
|
|
183
227
|
.split(/\n+/)
|
|
184
228
|
.map((line) => cleanText(line));
|
|
185
|
-
|
|
186
229
|
|
|
230
|
+
console.log('βοΈ Extracting and cleaning Portuguese content...');
|
|
187
231
|
const namePT = cleanText(ptMatch[1]);
|
|
188
232
|
const descriptionPT = cleanText(ptMatch[2]);
|
|
189
233
|
const poemPT = ptMatch[3]
|
|
@@ -191,6 +235,15 @@ export class ConceptService {
|
|
|
191
235
|
.split(/\n+/)
|
|
192
236
|
.map((line) => cleanText(line));
|
|
193
237
|
|
|
238
|
+
console.log('π Extracted values before processing:', {
|
|
239
|
+
nameEN,
|
|
240
|
+
descriptionEN,
|
|
241
|
+
poemEN,
|
|
242
|
+
namePT,
|
|
243
|
+
descriptionPT,
|
|
244
|
+
poemPT,
|
|
245
|
+
});
|
|
246
|
+
|
|
194
247
|
// β
Determine appropriate replacement text based on conceptSlug
|
|
195
248
|
const conceptPlaceholder = `this ${
|
|
196
249
|
conceptSlug.charAt(0).toUpperCase() + conceptSlug.slice(1)
|
|
@@ -210,15 +263,18 @@ export class ConceptService {
|
|
|
210
263
|
};
|
|
211
264
|
|
|
212
265
|
// β
Ensure the concept name is not repeated in the description
|
|
266
|
+
console.log('π Replacing concept name in descriptions...');
|
|
213
267
|
const cleanedDescriptionEN = replaceConceptName(descriptionEN, nameEN);
|
|
214
268
|
const cleanedDescriptionPT = replaceConceptName(descriptionPT, namePT);
|
|
215
269
|
|
|
216
270
|
// β
If the name appears in the poem, trigger a retry
|
|
271
|
+
console.log('π Checking for concept name in poems...');
|
|
217
272
|
if (
|
|
218
273
|
poemEN.some((line) => line.includes(nameEN)) ||
|
|
219
274
|
poemPT.some((line) => line.includes(namePT))
|
|
220
275
|
) {
|
|
221
276
|
console.error('β Concept name detected in poem, triggering a retry.');
|
|
277
|
+
console.log('β Problematic lines:', { poemEN, poemPT });
|
|
222
278
|
throw new Error('Concept name found in poem, regenerating response.');
|
|
223
279
|
}
|
|
224
280
|
|
|
@@ -791,361 +847,319 @@ export class ConceptService {
|
|
|
791
847
|
};
|
|
792
848
|
}
|
|
793
849
|
|
|
794
|
-
async regenerateDuplicateNames(limit?: number) {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
async
|
|
929
|
-
conceptSlug: Concept,
|
|
930
|
-
limit?: number
|
|
931
|
-
) {
|
|
850
|
+
// async regenerateDuplicateNames(limit?: number) {
|
|
851
|
+
// console.log(`π Processing duplicate names for regeneration...`);
|
|
852
|
+
|
|
853
|
+
// const { duplicateEntries, usedNamesEN, usedNamesPT } =
|
|
854
|
+
// await this.findDuplicateNamesAndUsedNames(limit);
|
|
855
|
+
|
|
856
|
+
// if (duplicateEntries.length === 0) {
|
|
857
|
+
// console.log(`π No duplicates found. Nothing to regenerate.`);
|
|
858
|
+
// return { message: 'No duplicates detected.' };
|
|
859
|
+
// }
|
|
860
|
+
|
|
861
|
+
// for (const { name: oldNameEN, combinations } of duplicateEntries) {
|
|
862
|
+
// for (let i = 1; i < combinations.length; i++) {
|
|
863
|
+
// const [_, lang, conceptSlug, combinationString] =
|
|
864
|
+
// combinations[i].split(':');
|
|
865
|
+
|
|
866
|
+
// if (!lang || !conceptSlug || !combinationString) {
|
|
867
|
+
// console.warn(`β οΈ Invalid KV key format: ${combinations[i]}`);
|
|
868
|
+
// continue;
|
|
869
|
+
// }
|
|
870
|
+
|
|
871
|
+
// const kvKeyEN = buildConceptKVKey(
|
|
872
|
+
// 'en-us',
|
|
873
|
+
// conceptSlug as Concept,
|
|
874
|
+
// combinationString
|
|
875
|
+
// );
|
|
876
|
+
// const kvKeyPT = buildConceptKVKey(
|
|
877
|
+
// 'pt-br',
|
|
878
|
+
// conceptSlug as Concept,
|
|
879
|
+
// combinationString
|
|
880
|
+
// );
|
|
881
|
+
|
|
882
|
+
// const kvDataEN = (await this.context.env.KV_CONCEPTS.get(
|
|
883
|
+
// kvKeyEN,
|
|
884
|
+
// 'json'
|
|
885
|
+
// )) as {
|
|
886
|
+
// name: string;
|
|
887
|
+
// description: string;
|
|
888
|
+
// };
|
|
889
|
+
// const kvDataPT = (await this.context.env.KV_CONCEPTS.get(
|
|
890
|
+
// kvKeyPT,
|
|
891
|
+
// 'json'
|
|
892
|
+
// )) as {
|
|
893
|
+
// name: string;
|
|
894
|
+
// };
|
|
895
|
+
|
|
896
|
+
// console.log(
|
|
897
|
+
// `π Regenerating name for: ${kvKeyEN} (Old Name: "${oldNameEN}")`
|
|
898
|
+
// );
|
|
899
|
+
|
|
900
|
+
// let newNameEN: string | null = null;
|
|
901
|
+
// let newNamePT: string | null = null;
|
|
902
|
+
// let attempts = 0;
|
|
903
|
+
// const maxAttempts = 3;
|
|
904
|
+
|
|
905
|
+
// while (attempts < maxAttempts) {
|
|
906
|
+
// attempts++;
|
|
907
|
+
|
|
908
|
+
// const messages = this.context
|
|
909
|
+
// .buildLLMMessages()
|
|
910
|
+
// .regenerateConceptName({
|
|
911
|
+
// oldNameEN: kvDataEN.name,
|
|
912
|
+
// description: kvDataEN.description,
|
|
913
|
+
// usedNamesEN: Array.from(usedNamesEN), // β
Ensure array format
|
|
914
|
+
// });
|
|
915
|
+
|
|
916
|
+
// const aiResponse = await this.context
|
|
917
|
+
// .api()
|
|
918
|
+
// .callTogether.single(messages, {});
|
|
919
|
+
// if (!aiResponse) {
|
|
920
|
+
// console.warn(
|
|
921
|
+
// `β οΈ AI failed to generate a new name for ${kvKeyEN}, skipping.`
|
|
922
|
+
// );
|
|
923
|
+
// break;
|
|
924
|
+
// }
|
|
925
|
+
|
|
926
|
+
// // β
Parse AI response to extract both EN and PT names
|
|
927
|
+
// const [generatedEN, generatedPT] = aiResponse
|
|
928
|
+
// .split('\n')
|
|
929
|
+
// .map((s) => s.trim());
|
|
930
|
+
|
|
931
|
+
// // β
Validate that the generated names are not duplicates
|
|
932
|
+
// if (!usedNamesEN.has(generatedEN) && !usedNamesPT.has(generatedPT)) {
|
|
933
|
+
// newNameEN = generatedEN;
|
|
934
|
+
// newNamePT = generatedPT;
|
|
935
|
+
// break; // β
Found unique names, exit retry loop
|
|
936
|
+
// }
|
|
937
|
+
|
|
938
|
+
// console.warn(
|
|
939
|
+
// `β οΈ AI suggested a duplicate name: EN - "${generatedEN}", PT - "${generatedPT}". Retrying... (Attempt ${attempts}/${maxAttempts})`
|
|
940
|
+
// );
|
|
941
|
+
// }
|
|
942
|
+
|
|
943
|
+
// if (!newNameEN || !newNamePT) {
|
|
944
|
+
// console.error(
|
|
945
|
+
// `π¨ Failed to generate a unique name for ${kvKeyEN} after ${maxAttempts} attempts.`
|
|
946
|
+
// );
|
|
947
|
+
// continue;
|
|
948
|
+
// }
|
|
949
|
+
|
|
950
|
+
// console.log(
|
|
951
|
+
// `β
Storing new names for ${kvKeyEN}: EN - "${newNameEN}", PT - "${newNamePT}"`
|
|
952
|
+
// );
|
|
953
|
+
|
|
954
|
+
// // β
Update KV with new names (EN)
|
|
955
|
+
// Object.assign(kvDataEN, { name: newNameEN });
|
|
956
|
+
// await this.context.env.KV_CONCEPTS.put(
|
|
957
|
+
// kvKeyEN,
|
|
958
|
+
// JSON.stringify(kvDataEN)
|
|
959
|
+
// );
|
|
960
|
+
|
|
961
|
+
// // β
Update KV with new names (PT)
|
|
962
|
+
// Object.assign(kvDataPT, { name: newNamePT });
|
|
963
|
+
// await this.context.env.KV_CONCEPTS.put(
|
|
964
|
+
// kvKeyPT,
|
|
965
|
+
// JSON.stringify(kvDataPT)
|
|
966
|
+
// );
|
|
967
|
+
|
|
968
|
+
// // β
Add new names to the used names sets to prevent future reuse
|
|
969
|
+
// usedNamesEN.add(newNameEN);
|
|
970
|
+
// usedNamesPT.add(newNamePT);
|
|
971
|
+
|
|
972
|
+
// // β
Stop if limit is reached
|
|
973
|
+
// if (limit && --limit <= 0) {
|
|
974
|
+
// console.log(`π― Limit reached, stopping further processing.`);
|
|
975
|
+
// return { message: 'Regeneration limit reached.' };
|
|
976
|
+
// }
|
|
977
|
+
// }
|
|
978
|
+
// }
|
|
979
|
+
|
|
980
|
+
// console.log(`π Duplicate names regenerated successfully.`);
|
|
981
|
+
// return { message: 'Duplicate names processed and regenerated.' };
|
|
982
|
+
// }
|
|
983
|
+
|
|
984
|
+
async findDuplicateNamesForConcept(conceptSlug: Concept) {
|
|
932
985
|
console.log(
|
|
933
|
-
`π
|
|
986
|
+
`π Fetching duplicate names from KV_CONCEPT_CACHE for ${conceptSlug}...`
|
|
934
987
|
);
|
|
935
988
|
|
|
936
|
-
const
|
|
937
|
-
let cursor: string | undefined = undefined;
|
|
938
|
-
let nameUsageMapEN: Record<string, string[]> = {}; // { enName: [combination1, combination2] }
|
|
939
|
-
let usedNamesEN = new Set<string>(); // β
All used EN names
|
|
940
|
-
let usedNamesPT = new Set<string>(); // β
All used PT names
|
|
941
|
-
let totalKeysScanned = 0;
|
|
942
|
-
|
|
943
|
-
do {
|
|
944
|
-
const response: KVNamespaceListResult<KVConcept> = (await kvStore.list({
|
|
945
|
-
prefix: `concepts:en-us:${conceptSlug}:`, // β
Fetch only keys for this concept
|
|
946
|
-
cursor,
|
|
947
|
-
})) as KVNamespaceListResult<KVConcept, string>;
|
|
948
|
-
|
|
949
|
-
const {
|
|
950
|
-
keys,
|
|
951
|
-
cursor: newCursor,
|
|
952
|
-
}: { keys: KVNamespaceListKey<KVConcept>[]; cursor?: string } = response;
|
|
953
|
-
|
|
954
|
-
totalKeysScanned += keys.length;
|
|
955
|
-
|
|
956
|
-
for (const key of keys) {
|
|
957
|
-
const kvDataEN = (await kvStore.get(key.name, 'json')) as {
|
|
958
|
-
name: string;
|
|
959
|
-
};
|
|
960
|
-
const kvKeyPT = key.name.replace(':en-us:', ':pt-br:'); // β
Get corresponding PT key
|
|
961
|
-
const kvDataPT = (await kvStore.get(kvKeyPT, 'json')) as {
|
|
962
|
-
name: string;
|
|
963
|
-
};
|
|
989
|
+
const kvCacheStore = this.context.env.KV_CONCEPT_CACHE;
|
|
964
990
|
|
|
965
|
-
|
|
966
|
-
|
|
991
|
+
// β
Fetch the cached concept name mappings
|
|
992
|
+
const nameMap: Record<string, string[]> =
|
|
993
|
+
(await kvCacheStore.get(
|
|
994
|
+
`cache:concepts:en-us:${conceptSlug}:0`,
|
|
995
|
+
'json'
|
|
996
|
+
)) || {};
|
|
967
997
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
nameUsageMapEN[kvDataEN.name].push(key.name); // Store the key for that name
|
|
972
|
-
}
|
|
998
|
+
let duplicateKeys: string[] = [];
|
|
999
|
+
let usedNamesEN = new Set<string>();
|
|
1000
|
+
let usedNamesPT = new Set<string>();
|
|
973
1001
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1002
|
+
for (const [name, keys] of Object.entries(nameMap)) {
|
|
1003
|
+
if (keys.length > 1) {
|
|
1004
|
+
duplicateKeys.push(...keys.slice(1)); // β
Store all but the first occurrence
|
|
977
1005
|
}
|
|
1006
|
+
usedNamesEN.add(name);
|
|
1007
|
+
}
|
|
978
1008
|
|
|
979
|
-
|
|
980
|
-
|
|
1009
|
+
// β
Fetch PT-BR version from cache
|
|
1010
|
+
const nameMapPT: Record<string, string[]> =
|
|
1011
|
+
(await kvCacheStore.get(
|
|
1012
|
+
`cache:concepts:pt-br:${conceptSlug}:0`,
|
|
1013
|
+
'json'
|
|
1014
|
+
)) || {};
|
|
981
1015
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
.filter(([_, combinations]) => combinations.length > 1)
|
|
985
|
-
.map(([name, combinations]) => ({
|
|
986
|
-
name,
|
|
987
|
-
combinations,
|
|
988
|
-
}));
|
|
989
|
-
|
|
990
|
-
// β
If limit is set, trim duplicates list
|
|
991
|
-
if (limit && duplicateENNames.length > limit) {
|
|
992
|
-
duplicateENNames = duplicateENNames.slice(0, limit);
|
|
1016
|
+
for (const name of Object.keys(nameMapPT)) {
|
|
1017
|
+
usedNamesPT.add(name);
|
|
993
1018
|
}
|
|
994
1019
|
|
|
995
1020
|
console.log(
|
|
996
|
-
`β
Found ${
|
|
1021
|
+
`β
Found ${duplicateKeys.length} duplicate keys for ${conceptSlug}.`
|
|
997
1022
|
);
|
|
998
1023
|
|
|
999
1024
|
return {
|
|
1000
|
-
|
|
1001
|
-
usedNamesEN, // β
Set of all
|
|
1002
|
-
usedNamesPT, // β
Set of all
|
|
1025
|
+
duplicateKeys, // β
List of keys that need new names
|
|
1026
|
+
usedNamesEN, // β
Set of all used EN names
|
|
1027
|
+
usedNamesPT, // β
Set of all used PT names
|
|
1003
1028
|
};
|
|
1004
1029
|
}
|
|
1005
1030
|
|
|
1006
|
-
async regenerateDuplicateNamesForConcept(
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
);
|
|
1031
|
+
async regenerateDuplicateNamesForConcept(conceptSlug: Concept) {
|
|
1032
|
+
console.log(`π [START] Regenerating duplicate names for concept: ${conceptSlug}`);
|
|
1033
|
+
|
|
1034
|
+
const kvCacheStore = this.context.env.KV_CONCEPT_CACHE;
|
|
1035
|
+
const kvStore = this.context.env.KV_CONCEPTS;
|
|
1036
|
+
|
|
1037
|
+
// β
Get duplicate keys and used names
|
|
1038
|
+
const { duplicateKeys, usedNamesEN, usedNamesPT } =
|
|
1039
|
+
await this.findDuplicateNamesForConcept(conceptSlug);
|
|
1040
|
+
|
|
1041
|
+
if (duplicateKeys.length === 0) {
|
|
1042
|
+
console.log(`π No duplicates found for ${conceptSlug}.`);
|
|
1019
1043
|
return { message: `No duplicates detected for ${conceptSlug}.` };
|
|
1020
1044
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1045
|
+
|
|
1046
|
+
let updatedKeys: string[] = [];
|
|
1047
|
+
let recentDuplicates = new Set<string>(); // β
Track newly generated names dynamically
|
|
1048
|
+
|
|
1049
|
+
console.log(`π Found ${duplicateKeys.length} duplicate keys to process.`);
|
|
1050
|
+
|
|
1051
|
+
for (const kvKeyEN of duplicateKeys) {
|
|
1052
|
+
const kvKeyPT = kvKeyEN.replace(':en-us:', ':pt-br:');
|
|
1053
|
+
|
|
1054
|
+
const kvDataEN = (await kvStore.get(kvKeyEN, 'json')) as {
|
|
1055
|
+
name: string;
|
|
1056
|
+
description: string;
|
|
1057
|
+
};
|
|
1058
|
+
const kvDataPT = (await kvStore.get(kvKeyPT, 'json')) as { name: string };
|
|
1059
|
+
|
|
1060
|
+
if (!kvDataEN || !kvDataEN.name) {
|
|
1061
|
+
console.warn(`β οΈ Missing data for key: ${kvKeyEN}, skipping.`);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
console.log(`π Processing duplicate: ${kvKeyEN} (Old Name: "${kvDataEN.name}")`);
|
|
1066
|
+
|
|
1067
|
+
let newNameEN: string | null = null;
|
|
1068
|
+
let newNamePT: string | null = null;
|
|
1069
|
+
let attempts = 0;
|
|
1070
|
+
const maxAttempts = 3;
|
|
1071
|
+
|
|
1072
|
+
while (attempts < maxAttempts) {
|
|
1073
|
+
attempts++;
|
|
1074
|
+
|
|
1075
|
+
console.log(`π‘ Attempt ${attempts}/${maxAttempts} - Requesting AI for new name...`);
|
|
1076
|
+
|
|
1077
|
+
// β
Generate a new name, passing dynamically tracked duplicates
|
|
1078
|
+
const messages = this.context.buildLLMMessages().regenerateConceptName({
|
|
1079
|
+
conceptSlug,
|
|
1080
|
+
oldNameEN: kvDataEN.name,
|
|
1081
|
+
description: kvDataEN.description,
|
|
1082
|
+
recentNames: Array.from(recentDuplicates), // β
Prevent reusing recent names
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
const aiResponse = await this.context.api().callTogether.single(messages, {});
|
|
1086
|
+
|
|
1087
|
+
if (!aiResponse) {
|
|
1088
|
+
console.warn(`β οΈ AI failed to generate a new name for ${kvKeyEN}, skipping.`);
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// β
Parse AI response
|
|
1093
|
+
const [generatedEN, generatedPT] = aiResponse.split('\n').map((s) => s.trim());
|
|
1094
|
+
|
|
1095
|
+
console.log(`π¨ AI Response: EN - "${generatedEN}", PT - "${generatedPT}"`);
|
|
1096
|
+
|
|
1097
|
+
// β
Check if the name already exists
|
|
1027
1098
|
if (
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
concept !== conceptSlug
|
|
1099
|
+
usedNamesEN.has(generatedEN) ||
|
|
1100
|
+
usedNamesPT.has(generatedPT) ||
|
|
1101
|
+
recentDuplicates.has(generatedEN)
|
|
1032
1102
|
) {
|
|
1033
|
-
console.warn(`β οΈ Invalid KV key format: ${combinations[i]}`);
|
|
1034
|
-
continue;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
const kvKeyEN = buildConceptKVKey(
|
|
1038
|
-
'en-us',
|
|
1039
|
-
concept as Concept,
|
|
1040
|
-
combinationString
|
|
1041
|
-
);
|
|
1042
|
-
const kvKeyPT = buildConceptKVKey(
|
|
1043
|
-
'pt-br',
|
|
1044
|
-
concept as Concept,
|
|
1045
|
-
combinationString
|
|
1046
|
-
);
|
|
1047
|
-
|
|
1048
|
-
const kvDataEN = (await this.context.env.KV_CONCEPTS.get(
|
|
1049
|
-
kvKeyEN,
|
|
1050
|
-
'json'
|
|
1051
|
-
)) as {
|
|
1052
|
-
name: string;
|
|
1053
|
-
description: string;
|
|
1054
|
-
};
|
|
1055
|
-
const kvDataPT = (await this.context.env.KV_CONCEPTS.get(
|
|
1056
|
-
kvKeyPT,
|
|
1057
|
-
'json'
|
|
1058
|
-
)) as {
|
|
1059
|
-
name: string;
|
|
1060
|
-
};
|
|
1061
|
-
|
|
1062
|
-
console.log(
|
|
1063
|
-
`π Regenerating name for: ${kvKeyEN} (Old Name: "${oldNameEN}")`
|
|
1064
|
-
);
|
|
1065
|
-
|
|
1066
|
-
let newNameEN: string | null = null;
|
|
1067
|
-
let newNamePT: string | null = null;
|
|
1068
|
-
let attempts = 0;
|
|
1069
|
-
const maxAttempts = 3;
|
|
1070
|
-
|
|
1071
|
-
while (attempts < maxAttempts) {
|
|
1072
|
-
attempts++;
|
|
1073
|
-
|
|
1074
|
-
const messages = this.context
|
|
1075
|
-
.buildLLMMessages()
|
|
1076
|
-
.regenerateConceptName({
|
|
1077
|
-
oldNameEN: kvDataEN.name,
|
|
1078
|
-
description: kvDataEN.description,
|
|
1079
|
-
usedNamesEN: Array.from(usedNamesEN), // β
Ensure array format
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
const aiResponse = await this.context
|
|
1083
|
-
.api()
|
|
1084
|
-
.callTogether.single(messages, {});
|
|
1085
|
-
if (!aiResponse) {
|
|
1086
|
-
console.warn(
|
|
1087
|
-
`β οΈ AI failed to generate a new name for ${kvKeyEN}, skipping.`
|
|
1088
|
-
);
|
|
1089
|
-
break;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
// β
Parse AI response to extract both EN and PT names
|
|
1093
|
-
const [generatedEN, generatedPT] = aiResponse
|
|
1094
|
-
.split('\n')
|
|
1095
|
-
.map((s) => s.trim());
|
|
1096
|
-
|
|
1097
|
-
// β
Validate that the generated names are not duplicates
|
|
1098
|
-
if (!usedNamesEN.has(generatedEN) && !usedNamesPT.has(generatedPT)) {
|
|
1099
|
-
newNameEN = generatedEN;
|
|
1100
|
-
newNamePT = generatedPT;
|
|
1101
|
-
break; // β
Found unique names, exit retry loop
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
1103
|
console.warn(
|
|
1105
|
-
`β οΈ
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
if (!newNameEN || !newNamePT) {
|
|
1110
|
-
console.error(
|
|
1111
|
-
`π¨ Failed to generate a unique name for ${kvKeyEN} after ${maxAttempts} attempts.`
|
|
1104
|
+
`β οΈ Duplicate detected: EN - "${generatedEN}", PT - "${generatedPT}". Retrying...`
|
|
1112
1105
|
);
|
|
1106
|
+
recentDuplicates.add(generatedEN);
|
|
1107
|
+
recentDuplicates.add(generatedPT);
|
|
1113
1108
|
continue;
|
|
1114
1109
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
// β
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1110
|
+
|
|
1111
|
+
newNameEN = generatedEN;
|
|
1112
|
+
newNamePT = generatedPT;
|
|
1113
|
+
recentDuplicates.add(newNameEN);
|
|
1114
|
+
recentDuplicates.add(newNamePT);
|
|
1115
|
+
break; // β
Found unique names, exit retry loop
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (!newNameEN || !newNamePT) {
|
|
1119
|
+
console.error(`π¨ Failed to generate a unique name for ${kvKeyEN} after ${maxAttempts} attempts.`);
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
console.log(`β
Storing new names: EN - "${newNameEN}", PT - "${newNamePT}"`);
|
|
1124
|
+
|
|
1125
|
+
// β
Update KV with new names (EN)
|
|
1126
|
+
Object.assign(kvDataEN, { name: newNameEN });
|
|
1127
|
+
await kvStore.put(kvKeyEN, JSON.stringify(kvDataEN));
|
|
1128
|
+
|
|
1129
|
+
// β
Update KV with new names (PT)
|
|
1130
|
+
Object.assign(kvDataPT, { name: newNamePT });
|
|
1131
|
+
await kvStore.put(kvKeyPT, JSON.stringify(kvDataPT));
|
|
1132
|
+
|
|
1133
|
+
// β
Add new names to used names sets
|
|
1134
|
+
usedNamesEN.add(newNameEN);
|
|
1135
|
+
usedNamesPT.add(newNamePT);
|
|
1136
|
+
|
|
1137
|
+
// β
Collect successfully processed keys for cache update
|
|
1138
|
+
updatedKeys.push(kvKeyEN);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// β
Remove successfully updated keys from duplicate cache
|
|
1142
|
+
if (updatedKeys.length > 0) {
|
|
1143
|
+
let duplicateKeysList: string[] =
|
|
1144
|
+
(await kvCacheStore.get(`cache:duplicates:${conceptSlug}`, 'json')) || [];
|
|
1145
|
+
|
|
1146
|
+
console.log(`ποΈ Removing ${updatedKeys.length} resolved duplicates from cache.`);
|
|
1147
|
+
|
|
1148
|
+
// β
Remove processed keys
|
|
1149
|
+
duplicateKeysList = duplicateKeysList.filter((key) => !updatedKeys.includes(key));
|
|
1150
|
+
|
|
1151
|
+
if (duplicateKeysList.length === 0) {
|
|
1152
|
+
console.log(`β
All duplicates resolved for ${conceptSlug}, deleting cache entry.`);
|
|
1153
|
+
await kvCacheStore.delete(`cache:duplicates:${conceptSlug}`);
|
|
1154
|
+
} else {
|
|
1155
|
+
await kvCacheStore.put(
|
|
1156
|
+
`cache:duplicates:${conceptSlug}`,
|
|
1157
|
+
JSON.stringify(duplicateKeysList)
|
|
1132
1158
|
);
|
|
1133
|
-
|
|
1134
|
-
// β
Add new names to the used names sets to prevent future reuse
|
|
1135
|
-
usedNamesEN.add(newNameEN);
|
|
1136
|
-
usedNamesPT.add(newNamePT);
|
|
1137
|
-
|
|
1138
|
-
// β
Stop if limit is reached
|
|
1139
|
-
if (limit && --limit <= 0) {
|
|
1140
|
-
console.log(`π― Limit reached, stopping further processing.`);
|
|
1141
|
-
return { message: `Regeneration limit reached for ${conceptSlug}.` };
|
|
1142
|
-
}
|
|
1143
1159
|
}
|
|
1144
1160
|
}
|
|
1145
|
-
|
|
1146
|
-
console.log(
|
|
1147
|
-
`π Duplicate names regenerated successfully for concept: ${conceptSlug}.`
|
|
1148
|
-
);
|
|
1161
|
+
|
|
1162
|
+
console.log(`π [COMPLETE] Duplicate names regenerated successfully for concept: ${conceptSlug}.`);
|
|
1149
1163
|
return {
|
|
1150
1164
|
message: `Duplicate names processed and regenerated for ${conceptSlug}.`,
|
|
1151
1165
|
};
|
|
@@ -1207,4 +1221,58 @@ export class ConceptService {
|
|
|
1207
1221
|
message: `Cached ${totalKeysScanned} EN names for ${conceptSlug} in ${batchIndex} batches.`,
|
|
1208
1222
|
};
|
|
1209
1223
|
}
|
|
1224
|
+
|
|
1225
|
+
async cacheDuplicateConcepts(conceptSlug: Concept) {
|
|
1226
|
+
console.log(`π Identifying duplicate entries for concept: ${conceptSlug}`);
|
|
1227
|
+
|
|
1228
|
+
const kvCacheStore = this.context.env.KV_CONCEPT_CACHE;
|
|
1229
|
+
let cursor: string | undefined = undefined;
|
|
1230
|
+
let batchIndex = 0;
|
|
1231
|
+
let duplicateKeys: string[] = [];
|
|
1232
|
+
|
|
1233
|
+
do {
|
|
1234
|
+
const batchKey = `cache:concepts:en-us:${conceptSlug}:${batchIndex}`;
|
|
1235
|
+
const batchData = await kvCacheStore.get<{ [name: string]: string[] }>(
|
|
1236
|
+
batchKey,
|
|
1237
|
+
'json'
|
|
1238
|
+
);
|
|
1239
|
+
|
|
1240
|
+
if (!batchData) {
|
|
1241
|
+
console.log(`β οΈ No data found for batch: ${batchKey}, stopping.`);
|
|
1242
|
+
break;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
console.log(
|
|
1246
|
+
`π¦ Processing batch ${batchIndex} with ${
|
|
1247
|
+
Object.keys(batchData).length
|
|
1248
|
+
} names`
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1251
|
+
for (const [name, keys] of Object.entries(batchData)) {
|
|
1252
|
+
if (keys.length > 1) {
|
|
1253
|
+
// Collect duplicate entries (all but the first one)
|
|
1254
|
+
duplicateKeys.push(...keys.slice(1));
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
batchIndex++;
|
|
1259
|
+
} while (true);
|
|
1260
|
+
|
|
1261
|
+
if (duplicateKeys.length === 0) {
|
|
1262
|
+
console.log(`β
No duplicates found for ${conceptSlug}.`);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
console.log(
|
|
1267
|
+
`π¨ Found ${duplicateKeys.length} duplicate entries for ${conceptSlug}`
|
|
1268
|
+
);
|
|
1269
|
+
|
|
1270
|
+
// Store the duplicate keys list in KV
|
|
1271
|
+
await kvCacheStore.put(
|
|
1272
|
+
`cache:duplicates:${conceptSlug}`,
|
|
1273
|
+
JSON.stringify(duplicateKeys)
|
|
1274
|
+
);
|
|
1275
|
+
|
|
1276
|
+
console.log(`β
Stored duplicate keys for ${conceptSlug} in cache.`);
|
|
1277
|
+
}
|
|
1210
1278
|
}
|
|
@@ -66,6 +66,17 @@ export class ConceptWorkflow {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
async processDuplicateNames(conceptSlug: Concept) {
|
|
70
|
+
console.log(`π Initiating duplicate name resolution for ${conceptSlug}`);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await this.conceptService.regenerateDuplicateNamesForConcept(conceptSlug);
|
|
74
|
+
console.log(`β
Duplicate names resolved for ${conceptSlug}`);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(`β Error resolving duplicates for ${conceptSlug}:`, error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
async processBatch(
|
|
70
81
|
conceptSlug: Concept,
|
|
71
82
|
combinations: string[],
|
package/package.json
CHANGED
package/utils/buildMessages.ts
CHANGED
|
@@ -183,42 +183,56 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
183
183
|
regenerateConceptName: ({
|
|
184
184
|
oldNameEN,
|
|
185
185
|
description,
|
|
186
|
-
|
|
186
|
+
conceptSlug,
|
|
187
|
+
recentNames = [],
|
|
187
188
|
}: {
|
|
188
189
|
oldNameEN: string;
|
|
189
190
|
description: string;
|
|
190
|
-
|
|
191
|
+
conceptSlug: string;
|
|
192
|
+
recentNames?: string[];
|
|
191
193
|
}): ChatMessages => [
|
|
192
194
|
{
|
|
193
195
|
role: 'system',
|
|
194
196
|
content: `
|
|
195
|
-
You are an expert in
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
You are an expert in mystical and symbolic naming. Your task is to generate a new, unique name for a given concept while ensuring it aligns with its description.
|
|
198
|
+
|
|
199
|
+
Guidelines:
|
|
200
|
+
β’ The name must have exactly six words.
|
|
201
|
+
β’ It must start with "The ${conceptSlug} of" in English and "O ${conceptSlug} de" in Portuguese.
|
|
202
|
+
β’ The last three words should be distinct, meaningful, and thematically relevant.
|
|
203
|
+
β’ It should reflect the conceptβs core essence such as protection, transformation, or resilience.
|
|
204
|
+
β’ Avoid generic, redundant, or overly abstract terms.
|
|
205
|
+
β’ Ensure it does not resemble the previous name.
|
|
206
|
+
β’ The name must not match any of the recently generated names.
|
|
207
|
+
|
|
208
|
+
Input:
|
|
209
|
+
β’ Concept Name: ${conceptSlug}
|
|
210
|
+
β’ Previous Name Already Used: ${oldNameEN}
|
|
211
|
+
β’ Recently Generated Names: ${
|
|
212
|
+
recentNames.length > 0 ? recentNames.join(', ') : 'None'
|
|
213
|
+
}
|
|
214
|
+
β’ Description: ${description}
|
|
215
|
+
|
|
216
|
+
Output Format:
|
|
217
|
+
|
|
218
|
+
EN:
|
|
219
|
+
β’ New Name: The ${conceptSlug} of [New Symbolic Concept]
|
|
220
|
+
|
|
221
|
+
PT:
|
|
222
|
+
β’ Novo Nome: O ${conceptSlug} de [Novo Conceito SimbΓ³lico]
|
|
223
|
+
|
|
224
|
+
No explanations or additional text. Return only the names.
|
|
210
225
|
`,
|
|
211
226
|
},
|
|
212
227
|
{
|
|
213
228
|
role: 'user',
|
|
214
229
|
content: `
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Also, provide a natural Portuguese translation of the new name.
|
|
230
|
+
β’ Concept Name: ${conceptSlug}
|
|
231
|
+
β’ Previous Name Already Used: ${oldNameEN}
|
|
232
|
+
β’ Recently Generated Names: ${
|
|
233
|
+
recentNames.length > 0 ? recentNames.join(', ') : 'None'
|
|
234
|
+
}
|
|
235
|
+
β’ Description: ${description}
|
|
222
236
|
`,
|
|
223
237
|
},
|
|
224
238
|
],
|