@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.
package/app/base/AppContext.ts
CHANGED
|
@@ -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(
|
|
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
|
|
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();
|
|
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;
|
|
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';
|
|
69
|
+
let phase = 'generation';
|
|
65
70
|
try {
|
|
66
71
|
attempts++;
|
|
67
72
|
console.log(`🔄 Attempt ${attempts} to generate basic info...`);
|
|
68
73
|
|
|
69
|
-
// ✅
|
|
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
|
-
|
|
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 = '
|
|
101
|
+
phase = 'cleaning';
|
|
102
|
+
response = this.cleanAIResponse(response);
|
|
103
|
+
|
|
104
|
+
phase = 'parsing';
|
|
86
105
|
|
|
87
106
|
// ✅ Parse response for both languages
|
|
88
|
-
|
|
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,
|
|
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,
|
|
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
|
@@ -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'
|
package/utils/buildMessages.ts
CHANGED
|
@@ -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
|
];
|
package/utils/conceptPrompts.ts
CHANGED
|
@@ -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.
|