@zodic/shared 0.0.200 → 0.0.202

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.
@@ -29,181 +29,100 @@ export class ConceptService {
29
29
  combinationString: string,
30
30
  override: boolean = false
31
31
  ): Promise<void> {
32
- console.log(
33
- `🚀 Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
34
- );
35
-
32
+ console.log(`🚀 Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`);
33
+
36
34
  const kvStore = this.context.kvConceptsStore();
37
35
  const kvFailuresStore = this.context.kvConceptFailuresStore();
38
36
  const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
39
37
  const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
40
-
38
+
41
39
  // ✅ Use Durable Object stub
42
40
  const id = this.context.env.CONCEPT_NAMES_DO.idFromName(conceptSlug);
43
41
  const stub = this.context.env.CONCEPT_NAMES_DO.get(id);
44
-
42
+
45
43
  console.log(`📡 Fetching existing KV data for ${conceptSlug}...`);
46
44
  const existingEN = await this.getKVConcept(kvKeyEN);
47
45
  const existingPT = await this.getKVConcept(kvKeyPT);
48
-
49
- // ✅ IMMEDIATE SKIP if already exists and override is false
46
+
50
47
  if (!override && existingEN.name && existingPT.name) {
51
48
  console.log(`⚡ Basic info already exists for ${conceptSlug}, skipping.`);
52
49
  return;
53
50
  }
54
-
51
+
55
52
  let attempts = 0;
56
53
  const maxAttempts = 3;
57
-
54
+
58
55
  while (attempts < maxAttempts) {
59
56
  let phase = 'generation';
60
57
  try {
61
58
  attempts++;
62
- console.log(
63
- `🔄 Attempt ${attempts} to generate basic info for ${conceptSlug}...`
64
- );
65
-
66
- // ✅ Fetch the latest name list from Durable Object
67
- console.log(
68
- `📡 Fetching names from Durable Object for ${conceptSlug}...`
69
- );
59
+ console.log(`🔄 Attempt ${attempts} to generate basic info for ${conceptSlug}...`);
60
+
70
61
  let allNamesEN: string[] = [];
71
62
  let allNamesPT: string[] = [];
72
63
  const response = await stub.fetch(`https://internal/names`);
73
-
64
+
74
65
  if (response.ok) {
75
- const data = (await response.json()) as {
76
- 'en-us': string[];
77
- 'pt-br': string[];
78
- };
66
+ const data = (await response.json()) as { 'en-us': string[]; 'pt-br': string[] };
79
67
  allNamesEN = data['en-us'] || [];
80
68
  allNamesPT = data['pt-br'] || [];
81
69
  }
82
-
70
+
83
71
  console.log(`✏️ Generating new name...`);
84
- const messages = this.context
85
- .buildLLMMessages()
86
- .generateConceptBasicInfo({
87
- combination: combinationString,
88
- conceptSlug,
89
- existingNames: allNamesEN.slice(-40),
90
- });
91
-
92
- let aiResponse = await this.context
93
- .api()
94
- .callTogether.single(messages, {});
72
+ const messages = this.context.buildLLMMessages().generateConceptBasicInfo({
73
+ combination: combinationString,
74
+ conceptSlug,
75
+ existingNames: allNamesEN.length > 100 ? allNamesEN.slice(-100) : allNamesEN,
76
+ });
77
+
78
+ let aiResponse = await this.context.api().callTogether.single(messages, {});
95
79
  if (!aiResponse) throw new Error(`❌ AI returned an empty response`);
96
-
80
+
97
81
  phase = 'cleaning';
98
82
  aiResponse = this.cleanAIResponse(aiResponse);
99
-
83
+
100
84
  phase = 'parsing';
101
- let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
102
- this.parseBasicInfoResponse(aiResponse, conceptSlug);
103
-
85
+ let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } = this.parseBasicInfoResponse(aiResponse, conceptSlug);
86
+
104
87
  console.log(`🎭 Generated names: EN - "${nameEN}", PT - "${namePT}"`);
105
-
106
- // ✅ **Check name length and retry if too long**
107
- const MAX_NAME_LENGTH = 44; // Adjust if needed
108
- if (
109
- nameEN.length > MAX_NAME_LENGTH ||
110
- namePT.length > MAX_NAME_LENGTH
111
- ) {
112
- console.warn(
113
- `⚠️ Name too long: "${nameEN}" | "${namePT}". Retrying...`
114
- );
115
-
116
- // ✅ **Register name in Durable Object as blocked**
117
- await stub.fetch(`https://internal/add-name`, {
118
- method: 'POST',
119
- body: JSON.stringify({ language: 'en-us', name: nameEN }),
120
- headers: { 'Content-Type': 'application/json' },
121
- });
122
-
123
- await stub.fetch(`https://internal/add-name`, {
124
- method: 'POST',
125
- body: JSON.stringify({ language: 'pt-br', name: namePT }),
126
- headers: { 'Content-Type': 'application/json' },
127
- });
128
-
129
- console.log(`✅ Added long name to DO blocklist, retrying...`);
88
+
89
+ // ✅ Name length check
90
+ const MAX_NAME_LENGTH = 44;
91
+ if (nameEN.length > MAX_NAME_LENGTH || namePT.length > MAX_NAME_LENGTH) {
92
+ console.warn(`⚠️ Name too long: "${nameEN}" | "${namePT}". Retrying...`);
93
+ allNamesEN.push(nameEN);
94
+ allNamesPT.push(namePT);
130
95
  continue;
131
96
  }
132
-
97
+
133
98
  // ✅ Check uniqueness before storing
134
99
  if (allNamesEN.includes(nameEN) || allNamesPT.includes(namePT)) {
135
- console.warn(
136
- `⚠️ Duplicate Name Detected: "${nameEN}" or "${namePT}"`
137
- );
138
-
139
- // ✅ **On third attempt, store the name anyway & register in kvFailures**
100
+ console.warn(`⚠️ Duplicate Name Detected: "${nameEN}" or "${namePT}"`);
140
101
  if (attempts >= maxAttempts) {
141
- console.log(
142
- `🚨 Max attempts reached. Storing name despite duplicate.`
143
- );
144
-
102
+ console.log(`🚨 Max attempts reached. Storing name despite duplicate.`);
145
103
  await kvFailuresStore.put(
146
104
  `failures:duplicates:${conceptSlug}:${combinationString}`,
147
- JSON.stringify({
148
- nameEN,
149
- namePT,
150
- attempts,
151
- conceptSlug,
152
- combinationString,
153
- timestamp: new Date().toISOString(),
154
- })
155
- );
156
-
157
- console.log(
158
- `✅ Stored duplicate name after max retries: ${nameEN}, ${namePT}`
105
+ JSON.stringify({ nameEN, namePT, attempts, conceptSlug, combinationString, timestamp: new Date().toISOString() })
159
106
  );
160
107
  break;
161
108
  }
162
-
163
- console.log('🔁 Retrying due to duplicate name...');
164
109
  continue;
165
110
  }
166
-
111
+
167
112
  console.log(`📝 Storing names in Durable Object...`);
168
- // **Immediately update Durable Object with the new name**
169
- await stub.fetch(`https://internal/add-name`, {
170
- method: 'POST',
171
- body: JSON.stringify({ language: 'en-us', name: nameEN }),
172
- headers: { 'Content-Type': 'application/json' },
173
- });
174
-
175
- await stub.fetch(`https://internal/add-name`, {
176
- method: 'POST',
177
- body: JSON.stringify({ language: 'pt-br', name: namePT }),
178
- headers: { 'Content-Type': 'application/json' },
179
- });
180
-
181
- // ✅ Store the generated basic info in KV
182
- Object.assign(existingEN, {
183
- name: nameEN,
184
- description: descriptionEN,
185
- poem: poemEN,
186
- status: 'idle',
187
- });
113
+ await stub.fetch(`https://internal/add-name`, { method: 'POST', body: JSON.stringify({ language: 'en-us', name: nameEN }), headers: { 'Content-Type': 'application/json' } });
114
+ await stub.fetch(`https://internal/add-name`, { method: 'POST', body: JSON.stringify({ language: 'pt-br', name: namePT }), headers: { 'Content-Type': 'application/json' } });
115
+
116
+ Object.assign(existingEN, { name: nameEN, description: descriptionEN, poem: poemEN, status: 'idle' });
188
117
  await kvStore.put(kvKeyEN, JSON.stringify(existingEN));
189
-
190
- Object.assign(existingPT, {
191
- name: namePT,
192
- description: descriptionPT,
193
- poem: poemPT,
194
- status: 'idle',
195
- });
118
+
119
+ Object.assign(existingPT, { name: namePT, description: descriptionPT, poem: poemPT, status: 'idle' });
196
120
  await kvStore.put(kvKeyPT, JSON.stringify(existingPT));
197
-
198
- console.log(
199
- `✅ Stored basic info for ${conceptSlug}, combination: ${combinationString}.`
200
- );
121
+
122
+ console.log(`✅ Stored basic info for ${conceptSlug}, combination: ${combinationString}.`);
201
123
  return;
202
124
  } catch (error) {
203
- console.error(
204
- `❌ Attempt ${attempts} failed at phase: ${phase}`,
205
- (error as Error).message
206
- );
125
+ console.error(`❌ Attempt ${attempts} failed at phase: ${phase}`, error);
207
126
  }
208
127
  }
209
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.200",
3
+ "version": "0.0.202",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -7,7 +7,7 @@ export const conceptPrompts: (
7
7
  crown: PROMPT_GENERATE_CROWN,
8
8
  scepter: PROMPT_GENERATE_SCEPTER,
9
9
  ring: PROMPT_GENERATE_RING,
10
- amulet: PROMPT_GENERATE_AMULET,
10
+ amulet: PROMPT_GENERATE_AMULET_BASIC_INFO,
11
11
  lantern: PROMPT_GENERATE_LANTERN,
12
12
  orb: PROMPT_GENERATE_ORB,
13
13
  },
@@ -176,7 +176,7 @@ A list of previously used names will be provided. The new name must be unique an
176
176
 
177
177
  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:
178
178
 
179
- 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.
179
+ 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. It must not have more than 5 words.
180
180
 
181
181
  2. Description (Descrição): Write a concise, grounded narrative that links the Amulet’s qualities to the traits of the zodiac combination. Emphasize protection, nurturing energy, and personal growth. Keep the Portuguese version faithful in tone and depth to the English version.
182
182
 
@@ -209,6 +209,54 @@ PT:
209
209
  • Passagem Poética:[4 poetic lines uniquely written in Portuguese]
210
210
  `;
211
211
 
212
+ export const PROMPT_GENERATE_AMULET_BASIC_INFO = `
213
+ 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.
214
+
215
+ A list of previously used names will be provided. The new name must be unique and different from these.
216
+
217
+ 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:
218
+
219
+ 1. Name (Nome)
220
+ - The name must contain exactly 5 words.
221
+ - It must start with "The Amulet of…" in English and "O Amuleto de…" in Portuguese.
222
+ - Choose words that emphasize protection, stability, healing, and resilience.
223
+ - Keep the name sober, symbolic, and not overly long or ornate.
224
+ - Avoid redundancy and ensure each word adds value.
225
+
226
+ 2. Description (Descrição)
227
+ - Write a concise and grounded narrative that links the Amulet’s qualities to the traits of the zodiac combination.
228
+ - Emphasize protection, nurturing energy, and personal growth.
229
+ - Keep the Portuguese version faithful in tone and depth to the English version.
230
+
231
+ 3. Poetic Passage (Passagem Poética)
232
+ - Create two separate and independent poetic passages—one in English, one in Portuguese.
233
+ - The passage should be 4 poetic lines long.
234
+ - It must evoke protection, resilience, and healing.
235
+ - Do not translate the poem directly—let each version flourish naturally in its own language.
236
+ - The passage should flow rhythmically and be immersive.
237
+ - Do not mention the Amulet directly in the poem.
238
+
239
+ Rules and Restrictions:
240
+ - Do not mention zodiac signs or planets.
241
+ - Do not include the Amulet’s name in the description or poem.
242
+ - Ensure both descriptions are structurally and emotionally equivalent.
243
+ - The poem must be free-flowing and symbolic, avoiding direct translation.
244
+
245
+ Response Format:
246
+
247
+ EN:
248
+ • Name: The Amulet of [Symbolic Concept]
249
+ • Description: [Concise and grounded explanation]
250
+ • Poetic Passage:
251
+ [4 poetic lines uniquely written in English]
252
+
253
+ PT:
254
+ • Nome: O Amuleto de [Conceito Simbólico]
255
+ • Descrição: [Explicação concisa e bem fundamentada]
256
+ • Passagem Poética:
257
+ [4 poetic lines uniquely written in Portuguese]
258
+ `;
259
+
212
260
  export const PROMPT_GENERATE_LANTERN = `
213
261
  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.
214
262