@zodic/shared 0.0.155 → 0.0.157

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.
@@ -28,22 +28,23 @@ export class ConceptService {
28
28
  console.log(
29
29
  `🚀 Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
30
30
  );
31
-
31
+
32
32
  const kvStore = this.context.kvConceptsStore();
33
33
  const kvFailuresStore = this.context.kvConceptFailuresStore();
34
34
  const kvNamesStore = this.context.kvConceptNamesStore(); // ✅ New KV for storing names
35
35
  const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
36
36
  const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
37
37
  const failureKey = `failures:basic-info:${conceptSlug}:${combinationString}`;
38
-
39
- const namesKeyEN = `${conceptSlug}:en-us`; // ✅ Key for English names
40
- const namesKeyPT = `${conceptSlug}:pt-br`; // ✅ Key for Portuguese names
41
-
38
+
39
+ const namesKeyEN = `${conceptSlug}:en-us`;
40
+ const namesKeyPT = `${conceptSlug}:pt-br`;
41
+ const reportKey = `report:${conceptSlug}`; // ✅ New tracking key
42
+
42
43
  // ✅ Check if data already exists
43
44
  if (!override) {
44
45
  const existingEN = await this.getKVConcept(kvKeyEN);
45
46
  const existingPT = await this.getKVConcept(kvKeyPT);
46
-
47
+
47
48
  if (
48
49
  existingEN.name &&
49
50
  existingEN.description &&
@@ -60,16 +61,16 @@ export class ConceptService {
60
61
  return;
61
62
  }
62
63
  }
63
-
64
+
64
65
  let attempts = 0;
65
66
  const maxAttempts = 3;
66
-
67
+
67
68
  while (attempts < maxAttempts) {
68
69
  let phase = 'generation';
69
70
  try {
70
71
  attempts++;
71
72
  console.log(`🔄 Attempt ${attempts} to generate basic info...`);
72
-
73
+
73
74
  // ✅ Fetch the full name lists
74
75
  let allNamesEN = JSON.parse(
75
76
  (await kvNamesStore.get(namesKeyEN)) || '[]'
@@ -77,11 +78,11 @@ export class ConceptService {
77
78
  let allNamesPT = JSON.parse(
78
79
  (await kvNamesStore.get(namesKeyPT)) || '[]'
79
80
  ) as string[];
80
-
81
+
81
82
  // ✅ Get only the last 50 names for AI context
82
83
  const recentNamesEN = allNamesEN.slice(-50);
83
84
  const recentNamesPT = allNamesPT.slice(-50);
84
-
85
+
85
86
  // ✅ Build the messages to request content, ensuring uniqueness
86
87
  const messages = this.context
87
88
  .buildLLMMessages()
@@ -90,22 +91,22 @@ export class ConceptService {
90
91
  conceptSlug,
91
92
  existingNames: recentNamesEN, // 🔥 Pass only recent names
92
93
  });
93
-
94
+
94
95
  // ✅ Call ChatGPT API
95
96
  let response = await this.context.api().callTogether.single(messages, {});
96
97
  if (!response) {
97
98
  throw new Error(`❌ AI returned an empty response`);
98
99
  }
99
-
100
+
100
101
  phase = 'cleaning';
101
- response = this.cleanAIResponse(response); // ✅ Early Cleaning Step
102
-
102
+ response = this.cleanAIResponse(response);
103
+
103
104
  phase = 'parsing';
104
-
105
+
105
106
  // ✅ Parse response for both languages
106
107
  let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
107
- this.parseBasicInfoResponse(response);
108
-
108
+ this.parseBasicInfoResponse(response, conceptSlug);
109
+
109
110
  // 🔥 Check if generated English name is unique
110
111
  if (allNamesEN.includes(nameEN)) {
111
112
  console.warn(`⚠️ Duplicate Crown Name Detected: "${nameEN}"`);
@@ -117,7 +118,7 @@ export class ConceptService {
117
118
  console.log('🔁 Retrying due to duplicate English name...');
118
119
  continue;
119
120
  }
120
-
121
+
121
122
  // 🔥 Check if generated Portuguese name is unique
122
123
  if (allNamesPT.includes(namePT)) {
123
124
  console.warn(`⚠️ Duplicate Portuguese Name Detected: "${namePT}"`);
@@ -129,15 +130,15 @@ export class ConceptService {
129
130
  console.log('🔁 Retrying due to duplicate Portuguese name...');
130
131
  continue;
131
132
  }
132
-
133
+
133
134
  // ✅ Append new names to the full list
134
135
  allNamesEN.push(nameEN);
135
136
  allNamesPT.push(namePT);
136
-
137
+
137
138
  // ✅ Store updated name lists in KV
138
139
  await kvNamesStore.put(namesKeyEN, JSON.stringify(allNamesEN));
139
140
  await kvNamesStore.put(namesKeyPT, JSON.stringify(allNamesPT));
140
-
141
+
141
142
  // 🌍 English version
142
143
  const conceptEN = await this.getKVConcept(kvKeyEN);
143
144
  Object.assign(conceptEN, {
@@ -147,7 +148,7 @@ export class ConceptService {
147
148
  status: 'idle',
148
149
  });
149
150
  await kvStore.put(kvKeyEN, JSON.stringify(conceptEN));
150
-
151
+
151
152
  // 🇧🇷 Portuguese version
152
153
  const conceptPT = await this.getKVConcept(kvKeyPT);
153
154
  Object.assign(conceptPT, {
@@ -157,30 +158,43 @@ export class ConceptService {
157
158
  status: 'idle',
158
159
  });
159
160
  await kvStore.put(kvKeyPT, JSON.stringify(conceptPT));
160
-
161
+
161
162
  console.log(
162
163
  `✅ Basic info stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`
163
164
  );
165
+
166
+ // ✅ Increment report tracking successful inserts
167
+ let currentCount = parseInt(
168
+ (await kvNamesStore.get(reportKey)) || '0',
169
+ 10
170
+ );
171
+ currentCount += 1;
172
+ await kvNamesStore.put(reportKey, currentCount.toString());
173
+
174
+ console.log(
175
+ `📊 Updated progress for ${conceptSlug}: ${currentCount} successful inserts.`
176
+ );
177
+
164
178
  return; // ✅ Exit loop if successful
165
179
  } catch (error) {
166
180
  console.error(
167
181
  `❌ Attempt ${attempts} failed at phase: ${phase}`,
168
182
  (error as Error).message
169
183
  );
170
-
184
+
171
185
  // ✅ Store failure details in KV for manual review
172
186
  await kvFailuresStore.put(
173
187
  failureKey,
174
188
  JSON.stringify({
175
189
  error: (error as Error).message,
176
190
  attempt: attempts,
177
- phase, // ✅ Identify if failure occurred in "generation", "cleaning", or "parsing"
191
+ phase,
178
192
  conceptSlug,
179
193
  combinationString,
180
194
  timestamp: new Date().toISOString(),
181
195
  })
182
196
  );
183
-
197
+
184
198
  if (attempts >= maxAttempts) {
185
199
  console.error(
186
200
  `🚨 All ${maxAttempts} attempts failed during ${phase}. Logged failure.`
@@ -189,7 +203,7 @@ export class ConceptService {
189
203
  `Failed to generate basic info after ${maxAttempts} attempts`
190
204
  );
191
205
  }
192
-
206
+
193
207
  console.log('🔁 Retrying...');
194
208
  await new Promise((resolve) => setTimeout(resolve, 500)); // ⏳ Small delay before retrying
195
209
  }
@@ -206,7 +220,10 @@ export class ConceptService {
206
220
  .trim();
207
221
  }
208
222
 
209
- private parseBasicInfoResponse(response: string): {
223
+ private parseBasicInfoResponse(
224
+ response: string,
225
+ conceptSlug: Concept
226
+ ): {
210
227
  nameEN: string;
211
228
  descriptionEN: string;
212
229
  poemEN: string[];
@@ -237,25 +254,55 @@ export class ConceptService {
237
254
  const poemEN = enMatch[3]
238
255
  .trim()
239
256
  .split(/\n+/)
240
- .map((line) => cleanText(line)); // ✅ Split into array & clean lines
257
+ .map((line) => cleanText(line));
241
258
 
242
259
  const namePT = cleanText(ptMatch[1]);
243
260
  const descriptionPT = cleanText(ptMatch[2]);
244
261
  const poemPT = ptMatch[3]
245
262
  .trim()
246
263
  .split(/\n+/)
247
- .map((line) => cleanText(line)); // ✅ Split into array & clean lines
264
+ .map((line) => cleanText(line));
265
+
266
+ // ✅ Determine appropriate replacement text based on conceptSlug
267
+ const conceptPlaceholder = `this ${conceptSlug.charAt(0).toUpperCase() + conceptSlug.slice(1)}`;
268
+ const conceptPlaceholderCapitalized =
269
+ `This ${conceptSlug.charAt(0).toUpperCase() + conceptSlug.slice(1)}`;
270
+
271
+ // ✅ Function to replace occurrences of the name in a text while handling capitalization
272
+ const replaceConceptName = (text: string, conceptName: string) => {
273
+ return text.replace(
274
+ new RegExp(`(?<=\\.\\s*|^)${conceptName}`, 'g'),
275
+ conceptPlaceholderCapitalized
276
+ ).replace(new RegExp(conceptName, 'g'), conceptPlaceholder);
277
+ };
278
+
279
+ // ✅ Ensure the concept name is not repeated in the description
280
+ const cleanedDescriptionEN = replaceConceptName(descriptionEN, nameEN);
281
+ const cleanedDescriptionPT = replaceConceptName(descriptionPT, namePT);
282
+
283
+ // ✅ If the name appears in the poem, trigger a retry
284
+ if (poemEN.some((line) => line.includes(nameEN)) || poemPT.some((line) => line.includes(namePT))) {
285
+ console.error('❌ Concept name detected in poem, triggering a retry.');
286
+ throw new Error('Concept name found in poem, regenerating response.');
287
+ }
248
288
 
249
289
  console.log('✅ Successfully parsed and cleaned basic info:', {
250
290
  nameEN,
251
- descriptionEN,
291
+ descriptionEN: cleanedDescriptionEN,
252
292
  poemEN,
253
293
  namePT,
254
- descriptionPT,
294
+ descriptionPT: cleanedDescriptionPT,
255
295
  poemPT,
256
296
  });
257
297
 
258
- return { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT };
298
+ return {
299
+ nameEN,
300
+ descriptionEN: cleanedDescriptionEN,
301
+ poemEN,
302
+ namePT,
303
+ descriptionPT: cleanedDescriptionPT,
304
+ poemPT,
305
+ };
259
306
  }
260
307
 
261
308
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.155",
3
+ "version": "0.0.157",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -66,6 +66,7 @@ For the given combination, generate the following in both languages, ensuring th
66
66
 
67
67
  Rules & Formatting:
68
68
  • Do not mention zodiac signs or planets explicitly.
69
+ • Do not mention the Crown’s name in the description or poem.
69
70
  • Ensure both descriptions are structurally and emotionally equivalent.
70
71
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
71
72
  • Use rich, symbolic language while keeping it accessible and inspiring.
@@ -99,22 +100,16 @@ For the given combination, generate the following in both languages, ensuring th
99
100
 
100
101
  3. Poetic Passage (Passagem Poética): Create two separate and independent poetic passages—one in English, one in Portuguese. Each should reflect the same essence but be uniquely crafted to best suit the flow and artistic qualities of its respective language. The passage should be long and evocative, 4 poetic lines, ensuring it deeply resonates and immerses the reader in a sense of confidence, power, and determination.
101
102
 
102
-
103
103
  • Do not translate the poem directly—let each version flourish naturally in its own language.
104
-
105
104
  • Ensure the passage flows rhythmically, evoking strength and vision.
106
-
107
105
  • Avoid mentioning the Scepter itself or using the word “scepter.”
108
106
 
109
-
110
107
  Rules & Formatting:
111
108
 
112
109
  • Do not mention zodiac signs or planets explicitly.
113
-
110
+ • Do not mention the Scepter’s name in the description or poem.
114
111
  • Ensure both descriptions are structurally and emotionally equivalent.
115
-
116
112
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
117
-
118
113
  • Use rich, symbolic language while keeping it accessible and inspiring.
119
114
 
120
115
  Response Format:
@@ -147,20 +142,16 @@ For the given combination, generate the following in both languages, ensuring th
147
142
  3. Poetic Passage (Passagem Poética): Create two separate and independent poetic passages—one in English, one in Portuguese. Each should reflect the same essence but be uniquely crafted to best suit the flow and artistic qualities of its respective language. The passage should be long and evocative, 4 poetic lines, ensuring it deeply resonates and immerses the reader in a sense of longing, connection, and destiny.
148
143
 
149
144
  • Do not translate the poem directly—let each version flourish naturally in its own language.
150
-
151
145
  • Ensure the passage flows rhythmically, evoking deep emotion and intimacy.
152
-
153
146
  • Avoid mentioning the Ring itself or using the word “ring.”
154
147
 
155
148
 
156
149
  Rules & Formatting:
157
150
 
158
151
  • Do not mention zodiac signs or planets explicitly.
159
-
152
+ • Do not mention the Ring’s name in the description or poem.
160
153
  • Ensure both descriptions are structurally and emotionally equivalent.
161
-
162
154
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
163
-
164
155
  • Use rich, symbolic language while keeping it accessible and inspiring.
165
156
 
166
157
  Response Format:
@@ -192,19 +183,15 @@ For the given combination, generate the following in both languages, ensuring th
192
183
  3. Poetic Passage (Passagem Poética): Create two separate and independent poetic passages—one in English, one in Portuguese. Each should reflect the same essence but be uniquely crafted to best suit the flow and artistic qualities of its respective language. The passage should be long and evocative, 4 poetic lines, ensuring it deeply resonates and immerses the reader in a sense of renewal, strength, and inner harmony.
193
184
 
194
185
  • Do not translate the poem directly—let each version flourish naturally in its own language.
195
-
196
186
  • Ensure the passage flows rhythmically, evoking protection, resilience, and healing.
197
-
198
187
  • Avoid mentioning the Amulet itself or using the word “amulet.”
199
188
 
200
189
  Rules & Formatting:
201
190
 
202
191
  • Do not mention zodiac signs or planets explicitly.
203
-
192
+ • Do not mention the Amulet’s name in the description or poem.
204
193
  • Ensure both descriptions are structurally and emotionally equivalent.
205
-
206
194
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
207
-
208
195
  • Use rich, symbolic language while keeping it accessible and inspiring.
209
196
 
210
197
  Response Format:
@@ -236,14 +223,13 @@ For the given combination, generate the following in both languages, ensuring th
236
223
  3. Poetic Passage (Passagem Poética): Create two separate and independent poetic passages—one in English, one in Portuguese. Each should reflect the same essence but be uniquely crafted to best suit the flow and artistic qualities of its respective language. The passage should be long and evocative, 4 poetic lines, ensuring it deeply resonates and immerses the reader in a sense of mystery, guidance, and inner awakening.
237
224
 
238
225
  • Do not translate the poem directly—let each version flourish naturally in its own language.
239
-
240
226
  • Ensure the passage flows rhythmically, evoking transformation and introspection.
241
-
242
227
  • Avoid mentioning the Lantern itself or using the word “lantern.”
243
228
 
244
229
  Rules & Formatting:
245
230
 
246
231
  • Do not mention zodiac signs or planets explicitly.
232
+ • Do not mention the Lantern’s name in the description or poem.
247
233
  • Ensure both descriptions are structurally and emotionally equivalent.
248
234
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
249
235
  • Use rich, symbolic language while keeping it accessible and inspiring.
@@ -277,19 +263,15 @@ For the given combination, generate the following in both languages, ensuring th
277
263
  3. Poetic Passage (Passagem Poética): Create two separate and independent poetic passages—one in English, one in Portuguese. Each should reflect the same essence but be uniquely crafted to best suit the flow and artistic qualities of its respective language. The passage should be long and evocative, 4 poetic lines, ensuring it deeply resonates and immerses the reader in a sense of aspiration, vision, and transcendence.
278
264
 
279
265
  • Do not translate the poem directly—let each version flourish naturally in its own language.
280
-
281
266
  • Ensure the passage flows rhythmically, evoking inspiration and a higher calling.
282
-
283
267
  • Avoid mentioning the Orb itself or using the word “orb.”
284
268
 
285
269
  Rules & Formatting:
286
270
 
287
271
  • Do not mention zodiac signs or planets explicitly.
288
-
272
+ • Do not mention the Orb’s name in the description or poem.
289
273
  • Ensure both descriptions are structurally and emotionally equivalent.
290
-
291
274
  • Write the poem freely in each language to maximize impact, avoiding direct translation.
292
-
293
275
  • Use rich, symbolic language while keeping it accessible and inspiring.
294
276
 
295
277
  Response Format: