@zodic/shared 0.0.154 → 0.0.156

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.
@@ -24,25 +24,30 @@ export class AppContext {
24
24
 
25
25
  kvConceptsStore() {
26
26
  if (!this.env.KV_CONCEPTS) {
27
- throw new Error(
28
- 'KV_CONCEPTS is not defined in the environment.'
29
- );
27
+ throw new Error('KV_CONCEPTS is not defined in the environment.');
30
28
  }
31
29
  return this.env.KV_CONCEPTS;
32
30
  }
33
31
 
34
32
  kvConceptFailuresStore() {
35
33
  if (!this.env.KV_CONCEPT_FAILURES) {
36
- throw new Error(
37
- 'KV_CONCEPT_FAILURES is not defined in the environment.'
38
- );
34
+ throw new Error('KV_CONCEPT_FAILURES is not defined in the environment.');
39
35
  }
40
36
  return this.env.KV_CONCEPT_FAILURES;
41
37
  }
42
38
 
39
+ kvConceptNamesStore() {
40
+ if (!this.env.KV_CONCEPT_NAMES) {
41
+ throw new Error('KV_CONCEPT_NAMES is not defined in the environment.');
42
+ }
43
+ return this.env.KV_CONCEPT_NAMES;
44
+ }
45
+
43
46
  kvConceptsManagementStore() {
44
47
  if (!this.env.KV_CONCEPTS_MANAGEMENT) {
45
- throw new Error('KV_CONCEPTS_MANAGEMENT is not defined in the environment.');
48
+ throw new Error(
49
+ 'KV_CONCEPTS_MANAGEMENT is not defined in the environment.'
50
+ );
46
51
  }
47
52
  return this.env.KV_CONCEPTS_MANAGEMENT;
48
53
  }
@@ -23,18 +23,23 @@ export class ConceptService {
23
23
  async generateBasicInfo(
24
24
  conceptSlug: Concept,
25
25
  combinationString: string,
26
- override: boolean = false // ✅ New optional override param
26
+ override: boolean = false
27
27
  ): Promise<void> {
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
- const kvFailuresStore = this.context.kvConceptFailuresStore(); // 🔴 Replace with actual KV store
33
+ const kvFailuresStore = this.context.kvConceptFailuresStore();
34
+ const kvNamesStore = this.context.kvConceptNamesStore(); // ✅ New KV for storing names
34
35
  const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
35
36
  const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
36
37
  const failureKey = `failures:basic-info:${conceptSlug}:${combinationString}`;
37
38
 
39
+ const namesKeyEN = `${conceptSlug}:en-us`;
40
+ const namesKeyPT = `${conceptSlug}:pt-br`;
41
+ const reportKey = `report:${conceptSlug}`; // ✅ New tracking key
42
+
38
43
  // ✅ Check if data already exists
39
44
  if (!override) {
40
45
  const existingEN = await this.getKVConcept(kvKeyEN);
@@ -53,7 +58,7 @@ export class ConceptService {
53
58
  console.log(
54
59
  `⚡ Basic info already exists for ${conceptSlug}, combination: ${combinationString}. Skipping.`
55
60
  );
56
- return; // ✅ Skip regeneration
61
+ return;
57
62
  }
58
63
  }
59
64
 
@@ -61,33 +66,79 @@ export class ConceptService {
61
66
  const maxAttempts = 3;
62
67
 
63
68
  while (attempts < maxAttempts) {
64
- let phase = 'generation'; // 📌 Track the phase
69
+ let phase = 'generation';
65
70
  try {
66
71
  attempts++;
67
72
  console.log(`🔄 Attempt ${attempts} to generate basic info...`);
68
73
 
69
- // ✅ Build the messages to request content
74
+ // ✅ Fetch the full name lists
75
+ let allNamesEN = JSON.parse(
76
+ (await kvNamesStore.get(namesKeyEN)) || '[]'
77
+ ) as string[];
78
+ let allNamesPT = JSON.parse(
79
+ (await kvNamesStore.get(namesKeyPT)) || '[]'
80
+ ) as string[];
81
+
82
+ // ✅ Get only the last 50 names for AI context
83
+ const recentNamesEN = allNamesEN.slice(-50);
84
+ const recentNamesPT = allNamesPT.slice(-50);
85
+
86
+ // ✅ Build the messages to request content, ensuring uniqueness
70
87
  const messages = this.context
71
88
  .buildLLMMessages()
72
89
  .generateConceptBasicInfo({
73
90
  combination: combinationString,
74
91
  conceptSlug,
92
+ existingNames: recentNamesEN, // 🔥 Pass only recent names
75
93
  });
76
94
 
77
95
  // ✅ Call ChatGPT API
78
- const response = await this.context
79
- .api()
80
- .callTogether.single(messages, {});
96
+ let response = await this.context.api().callTogether.single(messages, {});
81
97
  if (!response) {
82
98
  throw new Error(`❌ AI returned an empty response`);
83
99
  }
84
100
 
85
- phase = 'parsing'; // ✅ Switch to parsing phase
101
+ phase = 'cleaning';
102
+ response = this.cleanAIResponse(response);
103
+
104
+ phase = 'parsing';
86
105
 
87
106
  // ✅ Parse response for both languages
88
- const { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
107
+ let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
89
108
  this.parseBasicInfoResponse(response);
90
109
 
110
+ // 🔥 Check if generated English name is unique
111
+ if (allNamesEN.includes(nameEN)) {
112
+ console.warn(`⚠️ Duplicate Crown Name Detected: "${nameEN}"`);
113
+ if (attempts >= maxAttempts) {
114
+ throw new Error(
115
+ `🚨 Could not generate a unique English name after ${maxAttempts} attempts.`
116
+ );
117
+ }
118
+ console.log('🔁 Retrying due to duplicate English name...');
119
+ continue;
120
+ }
121
+
122
+ // 🔥 Check if generated Portuguese name is unique
123
+ if (allNamesPT.includes(namePT)) {
124
+ console.warn(`⚠️ Duplicate Portuguese Name Detected: "${namePT}"`);
125
+ if (attempts >= maxAttempts) {
126
+ throw new Error(
127
+ `🚨 Could not generate a unique Portuguese name after ${maxAttempts} attempts.`
128
+ );
129
+ }
130
+ console.log('🔁 Retrying due to duplicate Portuguese name...');
131
+ continue;
132
+ }
133
+
134
+ // ✅ Append new names to the full list
135
+ allNamesEN.push(nameEN);
136
+ allNamesPT.push(namePT);
137
+
138
+ // ✅ Store updated name lists in KV
139
+ await kvNamesStore.put(namesKeyEN, JSON.stringify(allNamesEN));
140
+ await kvNamesStore.put(namesKeyPT, JSON.stringify(allNamesPT));
141
+
91
142
  // 🌍 English version
92
143
  const conceptEN = await this.getKVConcept(kvKeyEN);
93
144
  Object.assign(conceptEN, {
@@ -111,6 +162,19 @@ export class ConceptService {
111
162
  console.log(
112
163
  `✅ Basic info stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`
113
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
+
114
178
  return; // ✅ Exit loop if successful
115
179
  } catch (error) {
116
180
  console.error(
@@ -124,7 +188,7 @@ export class ConceptService {
124
188
  JSON.stringify({
125
189
  error: (error as Error).message,
126
190
  attempt: attempts,
127
- phase, // ✅ Identify if failure occurred in "generation" or "parsing"
191
+ phase,
128
192
  conceptSlug,
129
193
  combinationString,
130
194
  timestamp: new Date().toISOString(),
@@ -141,11 +205,21 @@ export class ConceptService {
141
205
  }
142
206
 
143
207
  console.log('🔁 Retrying...');
144
- await new Promise((resolve) => setTimeout(resolve, 2000)); // ⏳ Small delay before retrying
208
+ await new Promise((resolve) => setTimeout(resolve, 500)); // ⏳ Small delay before retrying
145
209
  }
146
210
  }
147
211
  }
148
212
 
213
+ private cleanAIResponse(response: string): string {
214
+ console.log('🧼 Cleaning AI Response...');
215
+
216
+ return response
217
+ .replace(/\*/g, '') // ✅ Remove asterisks
218
+ .replace(/#/g, '') // ✅ Remove hashtags
219
+ .replace(/---+/g, '') // ✅ Remove unnecessary dividers
220
+ .trim();
221
+ }
222
+
149
223
  private parseBasicInfoResponse(response: string): {
150
224
  nameEN: string;
151
225
  descriptionEN: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.154",
3
+ "version": "0.0.156",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -22,6 +22,7 @@ export type CentralBindings = {
22
22
  KV_CONCEPTS_MANAGEMENT: KVNamespace;
23
23
  KV_ASTRO: KVNamespace;
24
24
  KV_CONCEPT_FAILURES: KVNamespace;
25
+ KV_CONCEPT_NAMES: KVNamespace;
25
26
  CONCEPT_GENERATION_QUEUE: Queue;
26
27
 
27
28
  PROMPT_IMAGE_DESCRIBER: string;
@@ -88,6 +89,7 @@ export type BackendBindings = Env &
88
89
  | 'KV_ASTRO'
89
90
  | 'KV_CONCEPTS_MANAGEMENT'
90
91
  | 'KV_CONCEPT_FAILURES'
92
+ | 'KV_CONCEPT_NAMES'
91
93
  | 'PROMPT_IMAGE_DESCRIBER'
92
94
  | 'PROMPT_GENERATE_ARCHETYPE_CONTENT'
93
95
  | 'PROMPT_GENERATE_ARCHETYPE_LEONARDO_PROMPTS'
@@ -97,12 +97,14 @@ export const buildLLMMessages = (env: BackendBindings) => ({
97
97
  generateConceptBasicInfo: ({
98
98
  combination,
99
99
  conceptSlug,
100
+ existingNames,
100
101
  }: {
101
102
  combination: string;
102
103
  conceptSlug: Concept;
104
+ existingNames: string[];
103
105
  }): ChatMessages => {
104
106
  const zodiacCombination = mapConceptToPlanets(conceptSlug, combination);
105
-
107
+
106
108
  return [
107
109
  {
108
110
  role: 'system',
@@ -112,6 +114,9 @@ export const buildLLMMessages = (env: BackendBindings) => ({
112
114
  role: 'user',
113
115
  content: `
114
116
  Combination: ${zodiacCombination}
117
+
118
+ Existing Names (Avoid duplicates):
119
+ ${existingNames.length > 0 ? existingNames.join(', ') : 'None'}
115
120
  `,
116
121
  },
117
122
  ];
@@ -54,6 +54,8 @@ export const conceptPrompts: (
54
54
  export const PROMPT_GENERATE_CROWN = `
55
55
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Crown concept in both English and Brazilian Portuguese simultaneously. The Crown reflects core identity, representing self-awareness, inner strength, and personal truth, as influenced by a given zodiac sign combination.
56
56
 
57
+ A list of previously used names will be provided. The new name must be unique and different from these.
58
+
57
59
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
58
60
  1. Name (Nome): Keep it sober and descriptive, focusing on the Crown’s symbolic purpose. Avoid ornate or fantastical phrasing. It should start with “The Crown of…” in English and “A Coroa de…” in Portuguese.
59
61
  2. Description (Descrição): Write a concise, grounded narrative that links the Crown’s qualities to the traits of the zodiac combination. Emphasize balance, identity, and inner power without exaggeration. Keep the Portuguese version faithful in tone and depth to the English version.
@@ -87,6 +89,7 @@ PT:
87
89
  export const PROMPT_GENERATE_SCEPTER = `
88
90
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Scepter concept in both English and Brazilian Portuguese simultaneously. The Scepter reflects external influence and action, symbolizing leadership, initiative, and the ability to shape one’s surroundings, as influenced by a given zodiac sign combination.
89
91
 
92
+ A list of previously used names will be provided. The new name must be unique and different from these.
90
93
 
91
94
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
92
95
 
@@ -133,6 +136,8 @@ export const PROMPT_GENERATE_RING = `
133
136
 
134
137
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Ring concept in both English and Brazilian Portuguese simultaneously. The Ring reflects love and bonds, symbolizing attraction, deep emotional connections, and the unseen forces that shape relationships, as influenced by a given zodiac sign combination.
135
138
 
139
+ A list of previously used names will be provided. The new name must be unique and different from these.
140
+
136
141
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
137
142
 
138
143
  1. Name (Nome): Keep it elegant and evocative, focusing on the Ring’s symbolic purpose. It should start with “The Ring of…” in English and “O Anel de…” in Portuguese, reflecting themes of fate, passion, or devotion.
@@ -176,6 +181,8 @@ PT:
176
181
  export const PROMPT_GENERATE_AMULET = `
177
182
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Amulet concept in both English and Brazilian Portuguese simultaneously. The Amulet reflects healing and growth, representing protection, stability, and resilience, as influenced by a given zodiac sign combination.
178
183
 
184
+ A list of previously used names will be provided. The new name must be unique and different from these.
185
+
179
186
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
180
187
 
181
188
  1. Name (Nome): Keep it sober and descriptive, focusing on the Amulet’s symbolic purpose. It should start with “The Amulet of…” in English and “O Amuleto de…” in Portuguese.
@@ -218,7 +225,7 @@ PT:
218
225
  export const PROMPT_GENERATE_LANTERN = `
219
226
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Lantern concept in both English and Brazilian Portuguese simultaneously. The Lantern reflects spiritual transformation, symbolizing guidance, resilience, and introspection, as influenced by a given zodiac sign combination.
220
227
 
221
-
228
+ A list of previously used names will be provided. The new name must be unique and different from these.
222
229
 
223
230
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
224
231
 
@@ -259,6 +266,8 @@ PT:
259
266
  export const PROMPT_GENERATE_ORB = `
260
267
  You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic passage for the Orb concept in both English and Brazilian Portuguese simultaneously. The Orb reflects destiny and purpose, symbolizing higher ideals, vision, and the pursuit of meaningful goals, as influenced by a given zodiac sign combination.
261
268
 
269
+ A list of previously used names will be provided. The new name must be unique and different from these.
270
+
262
271
  For the given combination, generate the following in both languages, ensuring the content remains identical in meaning and tone except for the poem, which should be independently written in each language:
263
272
 
264
273
  1. Name (Nome): Keep it sober and descriptive, focusing on the Orb’s symbolic purpose. It should start with “The Orb of…” in English and “O Orbe de…” in Portuguese.