@zodic/shared 0.0.137 → 0.0.139

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.
@@ -16,64 +16,83 @@ export class ConceptService {
16
16
  /**
17
17
  * Generate basic info for a concept: name, description, and poem.
18
18
  */
19
- async generateBasicInfo(
20
- language: Languages,
21
- conceptSlug: Concept,
22
- combinationString: string
23
- ): Promise<void> {
24
- const kvKey = buildConceptKVKey(language, conceptSlug, combinationString);
25
- console.log(
26
- `Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, language: ${language}`
27
- );
28
-
19
+ async generateBasicInfo(conceptSlug: Concept, combinationString: string): Promise<void> {
20
+ console.log(`🚀 Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}`);
21
+
22
+ // ✅ Build the messages to request content
29
23
  const messages = this.context.buildLLMMessages().generateConceptBasicInfo({
30
24
  combination: combinationString,
31
25
  conceptSlug,
32
26
  });
33
-
27
+
28
+ // ✅ Call ChatGPT API
34
29
  const response = await this.context.api().callChatGPT.single(messages, {});
35
30
  if (!response) {
36
- throw new Error(
37
- `Failed to generate basic info for concept: ${conceptSlug}`
38
- );
31
+ throw new Error(`❌ Failed to generate basic info for concept: ${conceptSlug}`);
39
32
  }
40
-
41
- const { name, description, poem } = this.parseBasicInfoResponse(response);
42
-
43
- const concept = await this.getKVConcept(kvKey);
44
- Object.assign(concept, { name, description, poem, status: 'idle' });
45
-
46
- await this.context.kvConceptsStore().put(kvKey, JSON.stringify(concept));
47
- console.log(
48
- `Basic info stored for concept: ${conceptSlug}, combination: ${combinationString}, language: ${language}`
49
- );
33
+
34
+ // Parse response for both languages
35
+ const { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } = this.parseBasicInfoResponse(response);
36
+
37
+ // Store in KV for both languages
38
+ const kvStore = this.context.kvConceptsStore();
39
+
40
+ // 🌍 English version
41
+ const kvKeyEN = buildConceptKVKey("en-us", conceptSlug, combinationString);
42
+ const conceptEN = await this.getKVConcept(kvKeyEN);
43
+ Object.assign(conceptEN, { name: nameEN, description: descriptionEN, poem: poemEN, status: "idle" });
44
+ await kvStore.put(kvKeyEN, JSON.stringify(conceptEN));
45
+
46
+ // 🇧🇷 Portuguese version
47
+ const kvKeyPT = buildConceptKVKey("pt-br", conceptSlug, combinationString);
48
+ const conceptPT = await this.getKVConcept(kvKeyPT);
49
+ Object.assign(conceptPT, { name: namePT, description: descriptionPT, poem: poemPT, status: "idle" });
50
+ await kvStore.put(kvKeyPT, JSON.stringify(conceptPT));
51
+
52
+ console.log(`✅ Basic info stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`);
50
53
  }
51
54
 
52
55
  private parseBasicInfoResponse(response: string): {
53
- name: string;
54
- description: string;
55
- poem: string;
56
+ nameEN: string;
57
+ descriptionEN: string;
58
+ poemEN: string;
59
+ namePT: string;
60
+ descriptionPT: string;
61
+ poemPT: string;
56
62
  } {
57
- console.log('📌 Parsing basic info response from ChatGPT:', response);
58
-
59
- const nameMatch = response.match(/1\.\s*Name:\s*(.+)/);
60
- const descriptionMatch = response.match(
61
- /2\.\s*Description:\s*([\s\S]+?)\s*3\./
62
- );
63
- const poemMatch = response.match(/3\.\s*Poem\/Quote:\s*([\s\S]+)/);
64
-
65
- if (!nameMatch || !descriptionMatch || !poemMatch) {
66
- console.error('❌ Invalid basic info response format:', response);
67
- throw new Error('Invalid basic info response format');
63
+ console.log("📌 Parsing basic info response from ChatGPT:", response);
64
+
65
+ const nameENMatch = response.match(/EN:\s*•\s*Name:\s*(.+)/);
66
+ const descriptionENMatch = response.match(/EN:\s*•\s*Description:\s*([\s\S]+?)\s*•\s*Poetic Passage:/);
67
+ const poemENMatch = response.match(/EN:\s*•\s*Poetic Passage:\s*([\s\S]+?)\s*PT:/);
68
+
69
+ const namePTMatch = response.match(/PT:\s*•\s*Nome:\s*(.+)/);
70
+ const descriptionPTMatch = response.match(/PT:\s*•\s*Descrição:\s*([\s\S]+?)\s*•\s*Passagem Poética:/);
71
+ const poemPTMatch = response.match(/PT:\s*•\s*Passagem Poética:\s*([\s\S]+)/);
72
+
73
+ if (!nameENMatch || !descriptionENMatch || !poemENMatch || !namePTMatch || !descriptionPTMatch || !poemPTMatch) {
74
+ console.error("❌ Invalid basic info response format:", response);
75
+ throw new Error("Invalid basic info response format");
68
76
  }
69
-
70
- const name = nameMatch[1].trim();
71
- const description = descriptionMatch[1].trim();
72
- const poem = poemMatch[1].trim();
73
-
74
- console.log('✅ Parsed basic info:', { name, description, poem });
75
-
76
- return { name, description, poem };
77
+
78
+ const nameEN = nameENMatch[1].trim();
79
+ const descriptionEN = descriptionENMatch[1].trim();
80
+ const poemEN = poemENMatch[1].trim();
81
+
82
+ const namePT = namePTMatch[1].trim();
83
+ const descriptionPT = descriptionPTMatch[1].trim();
84
+ const poemPT = poemPTMatch[1].trim();
85
+
86
+ console.log("✅ Parsed basic info:", {
87
+ nameEN,
88
+ descriptionEN,
89
+ poemEN,
90
+ namePT,
91
+ descriptionPT,
92
+ poemPT,
93
+ });
94
+
95
+ return { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT };
77
96
  }
78
97
 
79
98
  /**
@@ -19,7 +19,6 @@ export class ConceptWorkflow {
19
19
  switch (phase) {
20
20
  case 'basic-info':
21
21
  await this.conceptService.generateBasicInfo(
22
- 'en-us',
23
22
  conceptSlug,
24
23
  combinationString
25
24
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.137",
3
+ "version": "0.0.139",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -99,7 +99,7 @@ export const buildLLMMessages = (env: BackendBindings) => ({
99
99
  conceptSlug,
100
100
  }: {
101
101
  combination: string;
102
- conceptSlug: Concept;
102
+ conceptSlug: Concept;
103
103
  }): ChatMessages => {
104
104
  const zodiacCombination = mapConceptToPlanets(conceptSlug, combination);
105
105
 
@@ -111,8 +111,7 @@ export const buildLLMMessages = (env: BackendBindings) => ({
111
111
  {
112
112
  role: 'user',
113
113
  content: `
114
- Combination:
115
- ${zodiacCombination}
114
+ Combination: ${zodiacCombination}
116
115
  `,
117
116
  },
118
117
  ];
@@ -122,7 +121,7 @@ export const buildLLMMessages = (env: BackendBindings) => ({
122
121
  conceptSlug,
123
122
  }: {
124
123
  combination: string;
125
- conceptSlug: Concept;
124
+ conceptSlug: Concept;
126
125
  }): ChatMessages => [
127
126
  {
128
127
  role: 'system',
@@ -145,24 +144,28 @@ export const buildLLMMessages = (env: BackendBindings) => ({
145
144
  name: string;
146
145
  description: string;
147
146
  poem: string;
148
- conceptSlug: Concept;
147
+ conceptSlug: Concept;
149
148
  combination: string;
150
- }): ChatMessages => [
151
- {
152
- role: 'system',
153
- content: conceptPrompts(env)['content'][conceptSlug],
154
- },
155
- {
156
- role: 'user',
157
- content: `
149
+ }): ChatMessages => {
150
+ const zodiacCombination = mapConceptToPlanets(conceptSlug, combination);
151
+
152
+ return [
153
+ {
154
+ role: 'system',
155
+ content: conceptPrompts(env)['content'][conceptSlug],
156
+ },
157
+ {
158
+ role: 'user',
159
+ content: `
158
160
  Concept Details:
159
- - Combination: ${combination}
161
+ - Combination: ${zodiacCombination}
160
162
  - Name: ${name}
161
163
  - Description: ${description}
162
164
  - Poem: ${poem}
163
165
 
164
166
  Generate additional content to elaborate on this concept for frontend display.
165
167
  `,
166
- },
167
- ],
168
+ },
169
+ ];
170
+ },
168
171
  });
@@ -52,14 +52,36 @@ export const conceptPrompts: (
52
52
  // }
53
53
 
54
54
  export const PROMPT_GENERATE_CROWN = `
55
- You are a skilled creative writer specializing in astrology and symbolic storytelling. Your task is to craft a name, description, and poetic quote for the Crown concept. The Crown reflects core identity, representing self-awareness, inner strength, and personal truth, as influenced by a given zodiac sign combination.
56
-
57
- For the given combination:
58
- 1. Name: Keep it sober and descriptive, focusing on the Crown’s symbolic purpose. Avoid ornate or fantastical phrasing. It should start with "The Crown of...".
59
- 2. Description: 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.
60
- 3. Poem/Quote: Provide a short rhyming poetic paragraph or couplet (1-2 sentences) that encapsulates the Crown’s essence. Use rhyme to evoke inspiration and aspiration. Don't mention the Crown itself, the quote should be about what it represents.
61
-
62
- Do not mention zodiac signs or planets explicitly in the content. Ensure the output is insightful, symbolic, and emotionally resonant.
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
+
57
+ 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
+ 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
+ 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.
60
+ 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 destiny, wisdom, and transformation.
61
+ • Do not translate the poem directly—let each version flourish naturally in its own language.
62
+ Ensure the passage flows rhythmically, evoking emotion and imagery.
63
+ • Avoid mentioning the Crown itself or using the word “crown.”
64
+
65
+ Rules & Formatting:
66
+ • Do not mention zodiac signs or planets explicitly.
67
+ • Ensure both descriptions are structurally and emotionally equivalent.
68
+ • Write the poem freely in each language to maximize impact, avoiding direct translation.
69
+ • Use rich, symbolic language while keeping it accessible and inspiring.
70
+ • Use around 120-140 words to create the description
71
+
72
+ Response Format:
73
+
74
+ EN:
75
+ • Name: The Crown of [Symbolic Concept]
76
+ • Description: [Concise and grounded explanation - around 120-140 words]
77
+ • Poetic Passage:
78
+ [4 poetic lines uniquely written in English]
79
+
80
+ PT:
81
+ • Nome: A Coroa de [Conceito Simbólico]
82
+ • Descrição: [Explicação concisa e bem fundamentada - em torno de 120-140 palavras]
83
+ • Passagem Poética:
84
+ [4 poetic lines uniquely written in Portuguese]
63
85
  `;
64
86
 
65
87
  export const PROMPT_GENERATE_SCEPTER = `
@@ -27,5 +27,8 @@ export const mapConceptToPlanets = (
27
27
  );
28
28
  }
29
29
 
30
- return signs.map((sign, index) => `- ${planets[index]}: ${sign}`).join('\n');
30
+ // Format planets and signs properly
31
+ return signs
32
+ .map((sign, index) => `${planets[index]} in ${sign}`)
33
+ .join(', ');
31
34
  };
@@ -1,3 +1,43 @@
1
1
  export function normalizeName(name: string): string {
2
- return name.toLowerCase().replace(/\s+/g, '-');
2
+ const englishName = TRANSLATION_MAP[name] || name;
3
+
4
+ return englishName.toLowerCase().replace(/\s+/g, '-');
3
5
  }
6
+
7
+ const TRANSLATION_MAP: Record<string, string> = {
8
+ // Planets
9
+ Sol: 'Sun',
10
+ Lua: 'Moon',
11
+ Mercúrio: 'Mercury',
12
+ Vênus: 'Venus',
13
+ Marte: 'Mars',
14
+ Júpiter: 'Jupiter',
15
+ Saturno: 'Saturn',
16
+ Urano: 'Uranus',
17
+ Netuno: 'Neptune',
18
+ Plutão: 'Pluto',
19
+ Ascendente: 'Ascendant',
20
+ MC: 'Midheaven',
21
+ Nodo: 'Node',
22
+
23
+ // Signs
24
+ Áries: 'Aries',
25
+ Touro: 'Taurus',
26
+ Gémeos: 'Gemini',
27
+ Câncer: 'Cancer',
28
+ Leão: 'Leo',
29
+ Virgem: 'Virgo',
30
+ Libra: 'Libra',
31
+ Escorpião: 'Scorpio',
32
+ Sagitário: 'Sagittarius',
33
+ Capricórnio: 'Capricorn',
34
+ Aquário: 'Aquarius',
35
+ Peixes: 'Pisces',
36
+
37
+ // Aspect Types
38
+ Conjunção: 'Conjunction',
39
+ Oposição: 'Opposition',
40
+ Quadratura: 'Square',
41
+ Sextil: 'Sextile',
42
+ Trígono: 'Trine',
43
+ };